diff --git a/include/fh_mem.h b/include/fh_mem.h index 2b25f18..e7e5506 100644 --- a/include/fh_mem.h +++ b/include/fh_mem.h @@ -7,6 +7,8 @@ #ifndef FORTH_FH_MEM_H #define FORTH_FH_MEM_H +void fh_align(struct fh_thread_s *fh); + void fh_setbase(struct fh_thread_s *fh, uint32_t base); enum fh_error fh_fetch(struct fh_thread_s *fh, uint32_t addr, uint32_t *dst); @@ -26,16 +28,8 @@ void fh_heap_write(struct fh_thread_s *fh, uint32_t addr, const void *src, uint3 enum fh_error fh_heap_put(struct fh_thread_s *fh, const void *src, uint32_t len); void fh_heap_copy(struct fh_thread_s *fh, uint32_t addr, uint32_t srcaddr, uint32_t len); -static inline char *fh_str_at(struct fh_thread_s *fh, uint32_t addr) { - return (char *) &fh->heap[addr]; -} - -static inline struct fh_instruction_s *fh_instr_at(struct fh_thread_s *fh, uint32_t addr) { - return (void *) &fh->heap[addr]; -} - -static inline struct fh_word_s *fh_word_at(struct fh_thread_s *fh, uint32_t addr) { - return (struct fh_word_s *) &fh->heap[addr]; -} +char *fh_str_at(struct fh_thread_s *fh, uint32_t addr); +struct fh_instruction_s *fh_instr_at(struct fh_thread_s *fh, uint32_t addr); +struct fh_word_s *fh_word_at(struct fh_thread_s *fh, uint32_t addr); #endif //FORTH_FH_MEM_H diff --git a/include/fh_runtime.h b/include/fh_runtime.h index 931e3c7..f80d893 100644 --- a/include/fh_runtime.h +++ b/include/fh_runtime.h @@ -94,11 +94,17 @@ enum fh_substate { FH_SUBSTATE_MAX, }; +/** Indicates that this is a built-in instruction and not a word call */ +#define WORDFLAG_BUILTIN 0x01 +/** Indicates that this instruction should always be treated as interpreted */ +#define WORDFLAG_IMMEDIATE 0x02 + /** Word struct as they are stored in the dictionary */ struct fh_word_s { - uint32_t index; + /** Linked list pointer to previous word */ + uint32_t previous; /** Word name */ - char name[MAX_NAME_LEN]; + char name[MAX_NAME_LEN]; // XXX this wastes RAM! /** * Handler function. * Builtin functions use pre-defined native handlers. @@ -106,17 +112,14 @@ struct fh_word_s { * bytecode at 'start' address of the compile-memory area. */ word_exec_t handler; - /** Indicates that this is a built-in instruction and not a word call */ - bool builtin; - /** Indicates that this instruction should always be treated as interpreted */ - bool immediate; + uint32_t flags; /** Start address in case of user words, or param for builtins */ - union { - uint32_t start; - uint32_t param; - }; + uint32_t param; }; +#define MAGICADDR_DICTFIRST 0xFFFFFFFFULL +#define DICTWORD_SIZE sizeof(struct fh_word_s) + /** * Forth runtime instance - state variables and memory areas. * @@ -124,6 +127,8 @@ struct fh_word_s { * to a shared pointer if multi-threading and synchronization is added. */ struct fh_thread_s { + // TODO stacks could live on heap too and share space with some other structures + /** Data stack */ uint32_t data_stack[DATA_STACK_DEPTH]; size_t data_stack_top; @@ -140,10 +145,8 @@ struct fh_thread_s { /** Pointer into the compile buffer for execution */ uint32_t execptr; - - /** Word dict */ - struct fh_word_s dict[DICT_SIZE]; - uint32_t dict_top; + /** Address of the last dict word */ + uint32_t dict_last; /** Forth state */ enum fh_state state; @@ -193,6 +196,6 @@ enum fh_error fh_handle_ascii_word( size_t wordlen ); -enum fh_error fh_handle_word(struct fh_thread_s *fh, const struct fh_word_s *w); +enum fh_error fh_handle_word(struct fh_thread_s *fh, uint32_t addr); #endif //FORTH_FH_RUNTIME_H diff --git a/src/fh_builtins.c b/src/fh_builtins.c index 147a648..673df8a 100644 --- a/src/fh_builtins.c +++ b/src/fh_builtins.c @@ -359,17 +359,19 @@ static enum fh_error w_slash_mod(struct fh_thread_s *fh, const struct fh_word_s static enum fh_error w_colon(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; + enum fh_error rv; ENSURE_STATE(FH_STATE_INTERPRET); fh_setstate(fh, FH_STATE_COMPILE, FH_SUBSTATE_COLON_NAME); - if (fh->dict_top >= DICT_SIZE) { - return FH_ERR_DICT_FULL; - } - struct fh_word_s *new_word = &fh->dict[fh->dict_top]; - new_word->index = fh->dict_top; - new_word->start = fh->here; + uint32_t ptr; + TRY(fh_heap_reserve(fh, DICTWORD_SIZE, &ptr)); + + struct fh_word_s *new_word = fh_word_at(fh, ptr); + new_word->previous = fh->dict_last; + new_word->param = fh->here; new_word->handler = w_user_word; + fh->dict_last = ptr; return FH_OK; } @@ -427,24 +429,6 @@ static enum fh_error w_semicolon(struct fh_thread_s *fh, const struct fh_word_s /* Return to interpret state */ fh_setstate(fh, FH_STATE_INTERPRET, 0); - - struct fh_word_s *new_word = &fh->dict[fh->dict_top]; - - /* Now, check if a word with this name already exists. The new one should be used. */ - struct fh_word_s *old_word = &fh->dict[0]; - while (old_word->handler && old_word != new_word) { - if (0 == strncasecmp(new_word->name, old_word->name, MAX_NAME_LEN)) { - // We can't move the new definition because of RECURSE already using its address. - // Instead, redirect and wipe the old name. - // Note that this leaks compile memory! - old_word->start = new_word->start; - old_word->name[0] = 0; - break; - } - old_word++; - } - - fh->dict_top++; return FH_OK; } @@ -453,12 +437,12 @@ static enum fh_error w_immediate(struct fh_thread_s *fh, const struct fh_word_s (void) w; enum fh_error rv; - if (fh->dict_top == 0) { + if (fh->dict_last == 0) { LOGE("Dict is empty, cannot modify previous word!"); return FH_ERR_INVALID_STATE; } - fh->dict[fh->dict_top - 1].immediate = 1; + fh_word_at(fh, fh->dict_last)->flags |= WORDFLAG_IMMEDIATE; return FH_OK; } @@ -471,7 +455,7 @@ static enum fh_error w_recurse(struct fh_thread_s *fh, const struct fh_word_s *w ENSURE_STATE(FH_STATE_COMPILE); - instr_init(&instr, FH_INSTR_WORD, fh->dict_top); + instr_init(&instr, FH_INSTR_WORD, fh->dict_last); TRY(fh_heap_put(fh, &instr, INSTR_SIZE)); return FH_OK; @@ -1157,6 +1141,19 @@ enum fh_error register_builtin_words(struct fh_thread_s *fh) const struct name_and_handler builtins[] = { {"", w_error_word0, 1, 0}, + /* Weird meta stuff */ + {"immediate", w_immediate, 1, 0}, + {"postpone", w_postpone, 1, 0}, + {"[", w_leftbracket, 1, 0}, + {"]", w_rightbracket, 1, 0}, + {"literal", w_literal, 1, 0}, + /* Runtime stats */ + {"depth", w_depth, 0, 0}, + {"unused", w_unused, 0, 0}, + /* Debug tools & system */ + {"reset", w_reset, 1, 0}, + {"see", w_see, 0, 0}, + {"bye", w_bye, 0, 0}, /* Strings & Chars */ {"s\"", w_s_quote, 1, 0}, {".\"", w_dot_quote, 1, 0}, @@ -1263,33 +1260,20 @@ enum fh_error register_builtin_words(struct fh_thread_s *fh) {";", w_semicolon, 1, 0}, {"\\", w_backslash, 1, 0}, // line comment {"(", w_paren, 1, 0}, // enclosed comment - /* Weird meta stuff */ - {"immediate", w_immediate, 1, 0}, - {"postpone", w_postpone, 1, 0}, - {"[", w_leftbracket, 1, 0}, - {"]", w_rightbracket, 1, 0}, - {"literal", w_literal, 1, 0}, - /* Runtime stats */ - {"depth", w_depth, 0, 0}, - {"unused", w_unused, 0, 0}, - /* Debug tools & system */ - {"reset", w_reset, 1, 0}, - {"see", w_see, 0, 0}, - {"bye", w_bye, 0, 0}, { /* end marker */ } }; + LOG("Adding builtin words"); + // foreach struct fh_word_s w; const struct name_and_handler *p = builtins; enum fh_error rv; while (p->handler) { strcpy(w.name, p->name); - w.index = fh->dict_top; w.handler = p->handler; - w.builtin = 1; - w.immediate = p->immediate; + w.flags = WORDFLAG_BUILTIN | (p->immediate ? WORDFLAG_IMMEDIATE : 0); w.param = p->param; rv = fh_add_word(&w, fh); if (rv != FH_OK) { diff --git a/src/fh_mem.c b/src/fh_mem.c index 02a1476..7cd0777 100644 --- a/src/fh_mem.c +++ b/src/fh_mem.c @@ -5,6 +5,11 @@ #include "fh_runtime.h" #include "fh_mem.h" +void fh_align(struct fh_thread_s *fh) +{ + fh->here = WORDALIGNED(fh->here); +} + void fh_setbase(struct fh_thread_s *fh, uint32_t base) { LOG("BASE = %d", base); fh->base = base; @@ -105,6 +110,9 @@ enum fh_error fh_heap_reserve( *addr = p; } + // Erase the region. This is out of abundance of caution, not really needed if it was erased initially. Maybe. + memset(&fh->heap[p], 0, len); + fh->here = WORDALIGNED(p + len); return FH_OK; @@ -120,7 +128,7 @@ void fh_heap_write(struct fh_thread_s *fh, uint32_t addr, const void *src, uint3 enum fh_error fh_heap_put(struct fh_thread_s *fh, const void *src, uint32_t len) { enum fh_error rv; - uint32_t addr; + uint32_t addr = 0; TRY(fh_heap_reserve(fh, len, &addr)); fh_heap_write(fh, addr, src, len); return FH_OK; @@ -131,3 +139,25 @@ void fh_heap_copy(struct fh_thread_s *fh, uint32_t addr, uint32_t srcaddr, uint3 { memcpy(&fh->heap[addr], &fh->heap[srcaddr], len); } + + +char *fh_str_at(struct fh_thread_s *fh, uint32_t addr) { + if (addr >= HEAP_SIZE) { + LOGE("fh_str_at out of bounds!"); + } + return (char *) &fh->heap[addr]; +} + +struct fh_instruction_s *fh_instr_at(struct fh_thread_s *fh, uint32_t addr) { + if (addr >= HEAP_SIZE) { + LOGE("fh_instr_at out of bounds!"); + } + return (void *) &fh->heap[addr]; +} + +struct fh_word_s *fh_word_at(struct fh_thread_s *fh, uint32_t addr) { + if (addr >= HEAP_SIZE) { + LOGE("fh_word_at out of bounds!"); + } + return (struct fh_word_s *) &fh->heap[addr]; +} diff --git a/src/fh_runtime.c b/src/fh_runtime.c index db662f1..c090523 100644 --- a/src/fh_runtime.c +++ b/src/fh_runtime.c @@ -37,10 +37,19 @@ static const char *substatenames[FH_SUBSTATE_MAX] = { /** Add a word to the dictionary. */ enum fh_error fh_add_word(const struct fh_word_s *w, struct fh_thread_s *fh) { - if (fh->dict_top == DICT_SIZE) { - return FH_ERR_DICT_FULL; - } - memcpy(&fh->dict[fh->dict_top++], w, sizeof(struct fh_word_s)); + enum fh_error rv; + + fh_align(fh); + uint32_t ptr = fh->here; + + TRY(fh_heap_put(fh, w, DICTWORD_SIZE)); + + //LOG("Added word \"%s\" at 0x%08x", w->name, ptr); + + // thread it onto the linked list + fh_word_at(fh, ptr)->previous = fh->dict_last; + fh->dict_last = ptr; + return FH_OK; } @@ -83,7 +92,7 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0) LOG("Run user word: %s", w->name); TRY(rs_push(fh, fh->execptr)); - fh->execptr = w->start; + fh->execptr = w->param; instr:; if (fh->state == FH_STATE_QUIT) { @@ -108,8 +117,8 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0) case FH_INSTR_POSTPONED_WORD: if (fh->state == FH_STATE_COMPILE) { - w2 = &fh->dict[instr->data]; - if (w2->immediate) { + w2 = fh_word_at(fh, instr->data); + if (w2->flags & WORDFLAG_IMMEDIATE) { LOG("Call immediate postponed word: %s", w2->name); TRY(w2->handler(fh, w2)); } else { @@ -124,8 +133,8 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0) goto instr; case FH_INSTR_WORD: - w2 = &fh->dict[instr->data]; - if (w2->builtin) { + w2 = fh_word_at(fh, instr->data); + if (w2->flags & WORDFLAG_BUILTIN) { LOG("Exec: builtin-word \"%s\"", w2->name); w2->handler(fh, w2); if (fh->substate == FH_SUBSTATE_EXIT) { @@ -139,7 +148,7 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0) goto instr; } else { LOG("Exec: user-word %s (CALL)", w2->name); - w = &fh->dict[instr->data]; + w = fh_word_at(fh, instr->data); goto call; } @@ -202,6 +211,7 @@ enum fh_error fh_init(struct fh_thread_s *fh) /* Make sure we have a clean state */ memset(fh, 0, sizeof(struct fh_thread_s)); + fh->dict_last = MAGICADDR_DICTFIRST; TRY(register_builtin_words(fh)); fh->execptr = MAGICADDR_INTERACTIVE; @@ -248,13 +258,14 @@ static enum fh_error fh_handle_quoted_string( return FH_OK; } -enum fh_error fh_handle_word(struct fh_thread_s *fh, const struct fh_word_s *w) +enum fh_error fh_handle_word(struct fh_thread_s *fh, uint32_t addr) { struct fh_instruction_s instr; enum fh_error rv; - if (fh->state == FH_STATE_COMPILE && !w->immediate) { + struct fh_word_s *w = fh_word_at(fh, addr); + if (fh->state == FH_STATE_COMPILE && 0 == (w->flags & WORDFLAG_IMMEDIATE)) { LOG("Compile word call: %s", w->name); - instr_init(&instr, FH_INSTR_WORD, w->index); + instr_init(&instr, FH_INSTR_WORD, addr); TRY(fh_heap_put(fh, &instr, INSTR_SIZE)); } else { /* interpret or immediate in compiled code */ @@ -264,16 +275,18 @@ enum fh_error fh_handle_word(struct fh_thread_s *fh, const struct fh_word_s *w) return FH_OK; } -static struct fh_word_s *find_word(struct fh_thread_s *fh, const char *name, const size_t wordlen) +static struct fh_word_s *find_word(struct fh_thread_s *fh, const char *name, const size_t wordlen, uint32_t *addr_out) { - struct fh_word_s *w = &fh->dict[0]; - uint32_t cnt = 0; - while (w->handler) { + uint32_t addr = fh->dict_last; + while (addr != MAGICADDR_DICTFIRST) { + struct fh_word_s *w = fh_word_at(fh, addr); if (0 == strncasecmp(name, w->name, wordlen) && w->name[wordlen] == 0) { + if (addr_out) { + *addr_out = addr; + } return w; } - w++; - cnt++; + addr = w->previous; } return NULL; } @@ -292,9 +305,10 @@ enum fh_error fh_handle_ascii_word( /* First, try if it's a known word */ - struct fh_word_s *w = find_word(fh, name, wordlen); - if (w) {// word found! - TRY(fh_handle_word(fh, w)); + uint32_t wadr = MAGICADDR_UNRESOLVED; + find_word(fh, name, wordlen, &wadr); + if (wadr != MAGICADDR_UNRESOLVED) { + TRY(fh_handle_word(fh, wadr)); return FH_OK; } @@ -316,7 +330,7 @@ enum fh_error fh_handle_ascii_word( long v = strtol(name, &endptr, base); // XXX if base is 0, this will use auto-detection if (errno != 0 || (endptr - name) != wordlen) { - LOGE("Unknown word and fail to parse as number: %.*s", (int) wordlen, name); + LOGE("Unknown word and fail to parse as number: \"%.*s\"", (int) wordlen, name); return FH_ERR_UNKNOWN_WORD; } @@ -337,7 +351,7 @@ enum fh_error fh_handle_ascii_word( static void show_word(struct fh_thread_s *fh, const struct fh_word_s *w) { if (w->handler == w_user_word) { - uint32_t execptr = w->start; + uint32_t execptr = w->param; instr:; // make sure it's aligned @@ -355,7 +369,7 @@ static void show_word(struct fh_thread_s *fh, const struct fh_word_s *w) goto instr; case FH_INSTR_WORD: - w2 = &fh->dict[instr->data]; + w2 = fh_word_at(fh, instr->data); if (w2->name[0]) { FHPRINT("Call(%s)\n", w2->name); } else { @@ -364,7 +378,7 @@ static void show_word(struct fh_thread_s *fh, const struct fh_word_s *w) goto instr; case FH_INSTR_POSTPONED_WORD: - w2 = &fh->dict[instr->data]; + w2 = fh_word_at(fh, instr->data); if (w2->name[0]) { FHPRINT("Postpone(%s)\n", w2->name); } else { @@ -410,7 +424,7 @@ static enum fh_error fh_see_word( const size_t wordlen ) { - struct fh_word_s *w = find_word(fh, name, wordlen); + struct fh_word_s *w = find_word(fh, name, wordlen, NULL); if (!w) { return FH_ERR_UNKNOWN_WORD; } @@ -425,7 +439,8 @@ static enum fh_error fh_postpone_word( const size_t wordlen ) { - struct fh_word_s *w = find_word(fh, name, wordlen); + uint32_t wadr; + struct fh_word_s *w = find_word(fh, name, wordlen, &wadr); if (!w) { return FH_ERR_UNKNOWN_WORD; } @@ -433,7 +448,7 @@ static enum fh_error fh_postpone_word( enum fh_error rv; struct fh_instruction_s instr; LOG("Postpone %s", w->name); - instr_init(&instr, FH_INSTR_POSTPONED_WORD, w->index); + instr_init(&instr, FH_INSTR_POSTPONED_WORD, wadr); TRY(fh_heap_put(fh, &instr, INSTR_SIZE)); return FH_OK; @@ -491,7 +506,7 @@ enum fh_error fh_process_line(struct fh_thread_s *fh, const char *linebuf) case FH_SUBSTATE_COLON_NAME: /* new word's name is found */ LOG("New word name = \"%.*s\"", (int) length, rp); - strncpy(fh->dict[fh->dict_top].name, rp, length); + strncpy(fh_word_at(fh, fh->dict_last)->name, rp, length); fh_setsubstate(fh, FH_SUBSTATE_NONE); break; case FH_SUBSTATE_SEE_NAME: diff --git a/src/main.c b/src/main.c index 5e8d3ed..f8bd195 100644 --- a/src/main.c +++ b/src/main.c @@ -10,14 +10,6 @@ int main(int argc, char *argv[]) { - enum fh_error rv; - struct fh_thread_s fh; - rv = fh_init(&fh); - if (rv != FH_OK) { - LOGE("Error in forth init: %s", fherr_name(rv)); - return 1; - } - fh_globals.verbose = false; fh_globals.interactive = isatty(STDIN_FILENO); @@ -49,6 +41,14 @@ int main(int argc, char *argv[]) } } + enum fh_error rv; + struct fh_thread_s fh; + rv = fh_init(&fh); + if (rv != FH_OK) { + LOGE("Error in forth init: %s", fherr_name(rv)); + return 1; + } + const char *prompt = "> "; /* process input line by line */