diff --git a/include/fh_runtime.h b/include/fh_runtime.h index 6370992..9dcfd2c 100644 --- a/include/fh_runtime.h +++ b/include/fh_runtime.h @@ -28,16 +28,21 @@ enum fb_instruction_kind { /* Data = numeric value to push onto the data stack */ FH_INSTR_NUMBER, -}; -/** Bytecode word indices that are not in the dict, have special effect */ -enum compiler_word { /** End of a user defined word, pop address and jump back */ - CPLWORD_ENDWORD = DICT_SIZE + 1, + FH_INSTR_ENDWORD, + /** This is the `s"` instruction, the length (u32) and string data immediately follow */ - CPLWORD_ALLOCSTR, + FH_INSTR_ALLOCSTR, + /** This is the `."` instruction, same format as above. */ - CPLWORD_TYPESTR, + FH_INSTR_TYPESTR, + + /* Unconditional jump */ + FH_INSTR_JUMP, + + /* Jump if zero */ + FH_INSTR_JUMPZERO, }; /** One instruction in bytecode */ @@ -160,7 +165,8 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w); // SFR and magic addresses are "negative" #define MAGICADDR_INTERACTIVE 0xFFFFFFFFULL -#define MAGICADDR_BASE 0xFFFBA5EFULL +#define MAGICADDR_BASE 0xFFFFBA5EULL +#define MAGICADDR_UNRESOLVED 0xFFFFFBADULL /** Get a value rounded up to multiple of word size */ #define WORDALIGNED(var) (((var) + 3) & ~3) diff --git a/src/fh_builtins.c b/src/fh_builtins.c index 6540163..57cd73f 100644 --- a/src/fh_builtins.c +++ b/src/fh_builtins.c @@ -1,5 +1,6 @@ #include #include +#include "forth.h" // for fh_init #include "fh_runtime.h" #include "fh_config.h" #include "fh_error.h" @@ -310,7 +311,7 @@ static enum fh_error w_semicolon(struct fh_thread_s *fh, const struct fh_word_s ENSURE_STATE(FH_STATE_COMPILE); - instr_init(&instr, FH_INSTR_WORD, CPLWORD_ENDWORD); + instr_init(&instr, FH_INSTR_ENDWORD, 0); TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); /* Return to interpret state */ @@ -700,6 +701,64 @@ static enum fh_error w_bye(struct fh_thread_s *fh, const struct fh_word_s *w) return FH_OK; } +static enum fh_error w_if(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + struct fh_instruction_s instr; + + ENSURE_STATE(FH_STATE_COMPILE); + + TRY(cs_push(fh, fh->compile_top)); + instr_init(&instr, FH_INSTR_JUMPZERO, MAGICADDR_UNRESOLVED); + TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); + return FH_OK; +} + +static enum fh_error w_else(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + struct fh_instruction_s instr; + + ENSURE_STATE(FH_STATE_COMPILE); + + uint32_t ifaddr = 0; + TRY(cs_pop(fh, &ifaddr)); + struct fh_instruction_s *if_instr = (void*) &fh->compile[ifaddr]; + if (if_instr->data != MAGICADDR_UNRESOLVED) { + LOGE("IF-ELSE control stack corruption"); + return FH_ERR_INTERNAL; + } + + if_instr->data = fh->compile_top + INSTR_SIZE; + + TRY(cs_push(fh, fh->compile_top)); + instr_init(&instr, FH_INSTR_JUMP, MAGICADDR_UNRESOLVED); + TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); + return FH_OK; +} + +static enum fh_error w_then(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + struct fh_instruction_s instr; + + ENSURE_STATE(FH_STATE_COMPILE); + + uint32_t ifaddr = 0; + TRY(cs_pop(fh, &ifaddr)); + struct fh_instruction_s *if_instr = (void*) &fh->compile[ifaddr]; + if (if_instr->data != MAGICADDR_UNRESOLVED) { + LOGE("IF-ELSE control stack corruption"); + return FH_ERR_INTERNAL; + } + + if_instr->data = fh->compile_top; + return FH_OK; +} + static enum fh_error wp_setbase(struct fh_thread_s *fh, const struct fh_word_s *w) { fh_setbase(fh, w->param); @@ -749,16 +808,7 @@ static enum fh_error w_reset(struct fh_thread_s *fh, const struct fh_word_s *w) ENSURE_STATE(FH_STATE_INTERPRET); - fh->data_stack_top = 0; - fh->return_stack_top = 0; - fh->control_stack_top = 0; - fh->data_stack_hwm = 0; - fh->return_stack_hwm = 0; - fh->control_stack_hwm = 0; - fh->heap_top = 0; - fh->dict_top = 0; - - TRY(ds_push(fh, w->param)); + fh_init(fh); return FH_OK; } @@ -909,6 +959,9 @@ enum fh_error register_builtin_words(struct fh_thread_s *fh) {"abort", w_abort, 0, 0}, {"quit", w_quit, 0, 0}, {"exit", w_exit, 0, 0}, + {"if", w_if, 1, 0}, + {"else", w_else, 1, 0}, + {"then", w_then, 1, 0}, /* Syntax */ {":", w_colon, 0, 0}, {";", w_semicolon, 1, 0}, diff --git a/src/fh_mem.c b/src/fh_mem.c index 6a1a7db..e2b4b76 100644 --- a/src/fh_mem.c +++ b/src/fh_mem.c @@ -89,7 +89,7 @@ enum fh_error fh_heap_reserve( uint32_t *addr ) { - uint32_t p = WORDALIGNED(fh->heap_top); // FIXME this shouldn't be needed + uint32_t p = fh->heap_top; if (p + len > HEAP_SIZE) { return FH_ERR_HEAP_FULL; @@ -131,7 +131,7 @@ enum fh_error fh_compile_reserve( uint32_t *addr ) { - uint32_t p = WORDALIGNED(fh->compile_top); // FIXME this shouldn't be needed + uint32_t p = fh->compile_top; // FIXME this shouldn't be needed if (p + len > COMPILED_BUFFER_SIZE) { return FH_ERR_HEAP_FULL; diff --git a/src/fh_runtime.c b/src/fh_runtime.c index ecc16cb..1eb133c 100644 --- a/src/fh_runtime.c +++ b/src/fh_runtime.c @@ -72,7 +72,6 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0) enum fh_error rv; const struct fh_word_s *w; const struct fh_word_s *w2; - uint32_t wn; w = w0; call: @@ -95,6 +94,7 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0) fh->execptr += INSTR_SIZE; uint32_t strl; + uint32_t val; uint32_t addr = 0; switch (instr->kind) { case FH_INSTR_NUMBER: @@ -102,54 +102,60 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0) goto instr; case FH_INSTR_WORD: - wn = instr->data; - switch (wn) { - /* special case for strings stored in compile memory */ - case CPLWORD_ALLOCSTR: - case CPLWORD_TYPESTR: - strl = *((uint32_t *) &fh->compile[fh->execptr]); - fh->execptr += 4; // advance past the length - if (wn == CPLWORD_ALLOCSTR) { - TRY(fh_heap_reserve(fh, strl, &addr)); - fh_heap_copy_from_compile(fh, addr, fh->execptr, strl); - LOG("Exec: alloc-str \"%.*s\"", strl, &fh->heap[addr]); - TRY(ds_push(fh, addr)); - TRY(ds_push(fh, strl)); - } else { - LOG("Exec: type-str \"%.*s\"", strl, &fh->compile[fh->execptr]); - FHPRINT("%.*s", (int) strl, &fh->compile[fh->execptr]); - } - fh->execptr += strl; - goto instr; - - case CPLWORD_ENDWORD: - LOG("Exec: word-end (RETURN)"); + w2 = &fh->dict[instr->data]; + if (w2->builtin) { + LOG("Exec: builtin-word %s", w2->name); + w2->handler(fh, w2); + if (fh->substate == FH_SUBSTATE_EXIT) { + fh_setsubstate(fh, 0); + LOG("Exec: early return"); TRY(rs_pop(fh, &fh->execptr)); if (fh->execptr == MAGICADDR_INTERACTIVE) { goto end; } - goto instr; - - default: - w2 = &fh->dict[instr->data]; - if (w2->builtin) { - LOG("Exec: builtin-word %s", w2->name); - w2->handler(fh, w2); - if (fh->substate == FH_SUBSTATE_EXIT) { - fh_setsubstate(fh, 0); - LOG("Exec: early return"); - TRY(rs_pop(fh, &fh->execptr)); - if (fh->execptr == MAGICADDR_INTERACTIVE) { - goto end; - } - } - goto instr; - } else { - LOG("Exec: user-word %s (CALL)", w2->name); - w = &fh->dict[instr->data]; - goto call; - } + } + goto instr; + } else { + LOG("Exec: user-word %s (CALL)", w2->name); + w = &fh->dict[instr->data]; + goto call; + } + + case FH_INSTR_JUMPZERO: + TRY(ds_pop(fh, &val)); + if (0 == val) { + fh->execptr = instr->data; + } + goto instr; + + case FH_INSTR_JUMP: + fh->execptr = instr->data; + goto instr; + + /* special case for strings stored in compile memory */ + case FH_INSTR_ALLOCSTR: + case FH_INSTR_TYPESTR: + strl = instr->data; + if (instr->kind == FH_INSTR_ALLOCSTR) { + TRY(fh_heap_reserve(fh, strl, &addr)); + fh_heap_copy_from_compile(fh, addr, fh->execptr, strl); + LOG("Exec: alloc-str \"%.*s\"", strl, &fh->heap[addr]); + TRY(ds_push(fh, addr)); + TRY(ds_push(fh, strl)); + } else { + LOG("Exec: type-str \"%.*s\"", strl, &fh->compile[fh->execptr]); + FHPRINT("%.*s", (int) strl, &fh->compile[fh->execptr]); } + fh->execptr += strl; + goto instr; + + case FH_INSTR_ENDWORD: + LOG("Exec: word-end (RETURN)"); + TRY(rs_pop(fh, &fh->execptr)); + if (fh->execptr == MAGICADDR_INTERACTIVE) { + goto end; + } + goto instr; } end: @@ -201,18 +207,12 @@ static enum fh_error fh_handle_quoted_string( LOG("Compile a string"); /* compile */ if (fh->substate == FH_SUBSTATE_SQUOTE) { - instr_init(&instr, FH_INSTR_WORD, CPLWORD_ALLOCSTR); + instr_init(&instr, FH_INSTR_ALLOCSTR, len); } else { - instr_init(&instr, FH_INSTR_WORD, CPLWORD_TYPESTR); + instr_init(&instr, FH_INSTR_TYPESTR, len); } - uint32_t len32 = len; - /* string is encoded as a special compiler command, the size, - * and then the string, all 4-byte aligned. */ TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); - - TRY(fh_compile_reserve(fh, len + 4, &addr)); - fh_compile_write(fh, addr, &len32, 4); - fh_compile_write(fh, addr + 4, start, len); + TRY(fh_compile_put(fh, start, len)); } return FH_OK; } @@ -289,13 +289,12 @@ static enum fh_error fh_handle_word( static void show_word(struct fh_thread_s *fh, const struct fh_word_s *w) { if (w->handler == w_user_word) { - FHPRINT("addr 0x%08x ", w->start); - uint32_t execptr = w->start; instr:; // make sure it's aligned execptr = WORDALIGNED(execptr); + FHPRINT("0x%08x: ", execptr); const struct fh_instruction_s *instr = (const struct fh_instruction_s *) &fh->compile[execptr]; execptr += INSTR_SIZE; @@ -304,39 +303,39 @@ static void show_word(struct fh_thread_s *fh, const struct fh_word_s *w) { const struct fh_word_s *w2; switch (instr->kind) { case FH_INSTR_NUMBER: - FHPRINT("Value(%d) ", instr->data); + FHPRINT("Number(%d)\n", instr->data); goto instr; case FH_INSTR_WORD: wn = instr->data; - switch (wn) { - /* special case for strings stored in compile memory */ - case CPLWORD_ALLOCSTR: - case CPLWORD_TYPESTR: - strl = *((uint32_t *) &fh->compile[execptr]); - execptr += 4; // advance past the length - if (wn == CPLWORD_ALLOCSTR) { - FHPRINT("AllocStr(\"%.*s\") ", strl, &fh->compile[execptr]); - execptr += strl; - } else { - FHPRINT("PrintStr(\"%.*s\") ", strl, &fh->compile[execptr]); - execptr += strl; - } - goto instr; - - case CPLWORD_ENDWORD: - FHPRINT("END"); - return; - - default: - w2 = &fh->dict[instr->data]; - if (w2->name[0]) { - FHPRINT("Call(%s) ", w2->name); - } else { - FHPRINT("Call(0x%08x) ", instr->data); - } - goto instr; + w2 = &fh->dict[instr->data]; + FHPRINT("Call(%s, 0x%08x)\n", w2->name, instr->data); + goto instr; + + case FH_INSTR_JUMPZERO: + FHPRINT("JumpIfZero(0x%08x)\n", instr->data); + goto instr; + + case FH_INSTR_JUMP: + FHPRINT("Jump(0x%08x)\n", instr->data); + goto instr; + + /* special case for strings stored in compile memory */ + case FH_INSTR_ALLOCSTR: + case FH_INSTR_TYPESTR: + strl = instr->data; + if (instr->kind == FH_INSTR_ALLOCSTR) { + FHPRINT("AllocStr(\"%.*s\")\n", strl, &fh->compile[execptr]); + execptr += strl; + } else { + FHPRINT("PrintStr(\"%.*s\")\n", strl, &fh->compile[execptr]); + execptr += strl; } + goto instr; + + case FH_INSTR_ENDWORD: + FHPRINT("END\n"); + return; } } else {