diff --git a/.gitignore b/.gitignore index 113722a..d7d093a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ cmake-* *.bak .idea/ a.out -./forth +forth diff --git a/forth b/forth deleted file mode 100755 index ce9449a..0000000 Binary files a/forth and /dev/null differ diff --git a/include/fh_config.h b/include/fh_config.h index 68ff300..c24b488 100644 --- a/include/fh_config.h +++ b/include/fh_config.h @@ -16,4 +16,6 @@ #define HEAP_SIZE (1024*1024) #define MAXLINE 65535 +#define CELL 4 + #endif //FORTH_FH_CONFIG_H diff --git a/include/fh_error.h b/include/fh_error.h index 0564437..c1c947e 100644 --- a/include/fh_error.h +++ b/include/fh_error.h @@ -23,6 +23,8 @@ enum fh_error { FH_ERR_INVALID_STATE, FH_ERR_INTERNAL, FH_ERR_UNKNOWN_WORD, + FH_ERR_ILLEGAL_FETCH, + FH_ERR_DIV_BY_ZERO, FH_ERR_MAX, }; diff --git a/include/fh_globals.h b/include/fh_globals.h index 17c8683..8f21dfc 100644 --- a/include/fh_globals.h +++ b/include/fh_globals.h @@ -7,6 +7,8 @@ #ifndef FORTH_FH_GLOBALS_H #define FORTH_FH_GLOBALS_H +#include + /** Forth runtime global state */ struct fh_global_s { /** Verbose logging enabled */ diff --git a/include/fh_mem.h b/include/fh_mem.h index 096a569..52e129f 100644 --- a/include/fh_mem.h +++ b/include/fh_mem.h @@ -7,6 +7,14 @@ #ifndef FORTH_FH_MEM_H #define FORTH_FH_MEM_H +enum fh_error fh_fetch(struct fh_thread_s *fh, uint32_t addr, uint32_t *dst); + +enum fh_error fh_fetch_char(struct fh_thread_s *fh, uint32_t addr, char *dst); + +enum fh_error fh_store(struct fh_thread_s *fh, uint32_t addr, uint32_t val); + +enum fh_error fh_store_char(struct fh_thread_s *fh, uint32_t addr, char val); + enum fh_error fh_heap_reserve( struct fh_thread_s *fh, size_t len, diff --git a/include/fh_runtime.h b/include/fh_runtime.h index 3db86f6..0ded4e7 100644 --- a/include/fh_runtime.h +++ b/include/fh_runtime.h @@ -19,7 +19,7 @@ struct fh_instruction_s; struct fh_thread_s; /** Word handler typedef */ -typedef enum fh_error (*word_exec_t)(struct fh_thread_s *fh); +typedef enum fh_error (*word_exec_t)(struct fh_thread_s *fh, const struct fh_word_s *w); /** Bytecode instruction type marker */ enum fb_instruction_kind { @@ -62,6 +62,7 @@ _Static_assert(sizeof(struct fh_instruction_s) % 4 == 0, "Instruction struct is enum fh_state { FH_STATE_INTERPRET = 0, FH_STATE_COMPILE, + FH_STATE_QUIT, FH_STATE_SHUTDOWN, FH_STATE_MAX, }; @@ -93,8 +94,11 @@ struct fh_word_s { /** Indicates that this instruction should always be treated as interpreted, * in practice this is only used for `;` */ bool immediate; - /** Start address in case of user words */ - uint32_t start; + /** Start address in case of user words, or param for builtins */ + union { + uint32_t start; + uint32_t param; + }; }; /** @@ -139,9 +143,8 @@ struct fh_thread_s { /** Forth sub-state */ enum fh_substate substate; - /** Word currently being executed - a pointer is placed here - * before calling the handler */ - struct fh_word_s *exec_word; + /** The numeric base register */ + uint32_t base; }; enum fh_error fh_add_word(const struct fh_word_s *w, struct fh_thread_s *fh); @@ -149,10 +152,13 @@ enum fh_error fh_add_word(const struct fh_word_s *w, struct fh_thread_s *fh); void fh_setstate(struct fh_thread_s *fh, enum fh_state state, enum fh_substate substate); void fh_setsubstate(struct fh_thread_s *fh, enum fh_substate substate); -enum fh_error w_user_word(struct fh_thread_s *fh); +enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w); /* if the return address is this, we should drop back to interactive mode */ + +// SFR and magic addresses are "negative" #define MAGICADDR_INTERACTIVE 0xFFFFFFFFULL +#define MAGICADDR_BASE 0xFFFBA5EFULL /** 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 b56314b..6148a59 100644 --- a/src/fh_builtins.c +++ b/src/fh_builtins.c @@ -8,8 +8,11 @@ #include "fh_stack.h" #include "fh_mem.h" -static enum fh_error w_add(struct fh_thread_s *fh) +#define TOBOOL(a) (a == 0 ? 0 : 0xFFFFFFFF) + +static enum fh_error w_plus(struct fh_thread_s *fh, const struct fh_word_s *w) { + (void) w; enum fh_error rv; uint32_t a = 0, b = 0; TRY(ds_pop(fh, &a)); @@ -18,8 +21,9 @@ static enum fh_error w_add(struct fh_thread_s *fh) return FH_OK; } -static enum fh_error w_sub(struct fh_thread_s *fh) +static enum fh_error w_minus(struct fh_thread_s *fh, const struct fh_word_s *w) { + (void) w; enum fh_error rv; uint32_t a = 0, b = 0; TRY(ds_pop(fh, &a)); @@ -28,8 +32,9 @@ static enum fh_error w_sub(struct fh_thread_s *fh) return FH_OK; } -static enum fh_error w_mul(struct fh_thread_s *fh) +static enum fh_error w_star(struct fh_thread_s *fh, const struct fh_word_s *w) { + (void) w; enum fh_error rv; uint32_t a = 0, b = 0; TRY(ds_pop(fh, &a)); @@ -38,8 +43,176 @@ static enum fh_error w_mul(struct fh_thread_s *fh) return FH_OK; } -static enum fh_error w_colon(struct fh_thread_s *fh) +static enum fh_error w_zero_less(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t a = 0; + TRY(ds_pop(fh, &a)); + TRY(ds_push(fh, TOBOOL(a < 0))); + return FH_OK; +} + +static enum fh_error w_zero_greater(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t a = 0; + TRY(ds_pop(fh, &a)); + TRY(ds_push(fh, TOBOOL(a > 0))); + return FH_OK; +} + +static enum fh_error w_zero_equals(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t a = 0; + TRY(ds_pop(fh, &a)); + TRY(ds_push(fh, TOBOOL(a == 0))); + return FH_OK; +} + +static enum fh_error w_zero_not_equals(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t a = 0; + TRY(ds_pop(fh, &a)); + TRY(ds_push(fh, TOBOOL(a != 0))); + return FH_OK; +} + + +static enum fh_error w_less(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t a = 0, b = 0; + TRY(ds_pop(fh, &b)); + TRY(ds_pop(fh, &a)); + TRY(ds_push(fh, TOBOOL(a < b))); + return FH_OK; +} + +static enum fh_error w_greater(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t a = 0, b = 0; + TRY(ds_pop(fh, &b)); + TRY(ds_pop(fh, &a)); + TRY(ds_push(fh, TOBOOL(a > b))); + return FH_OK; +} + +static enum fh_error w_equals(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t a = 0, b = 0; + TRY(ds_pop(fh, &b)); + TRY(ds_pop(fh, &a)); + TRY(ds_push(fh, TOBOOL(a == b))); + return FH_OK; +} + +static enum fh_error w_not_equals(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t a = 0, b = 0; + TRY(ds_pop(fh, &b)); + TRY(ds_pop(fh, &a)); + TRY(ds_push(fh, TOBOOL(a != b))); + return FH_OK; +} + +static enum fh_error wp_add(struct fh_thread_s *fh, const struct fh_word_s *w) { + enum fh_error rv; + uint32_t a = 0; + TRY(ds_pop(fh, &a)); + TRY(ds_push(fh, a + w->param)); + return FH_OK; +} + +static enum fh_error wp_mul(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + enum fh_error rv; + uint32_t a = 0; + TRY(ds_pop(fh, &a)); + TRY(ds_push(fh, a * w->param)); + return FH_OK; +} + +static enum fh_error wp_div(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + enum fh_error rv; + uint32_t a = 0; + TRY(ds_pop(fh, &a)); + TRY(ds_push(fh, a * w->param)); + return FH_OK; +} + +static enum fh_error w_star_slash(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t a = 0, b = 0, c = 0; + TRY(ds_pop(fh, &c)); + TRY(ds_pop(fh, &b)); + TRY(ds_pop(fh, &a)); + + if (c == 0) { + return FH_ERR_DIV_BY_ZERO; + } + + uint64_t v = ((uint64_t) a * (uint64_t) b) / (uint64_t) c; + + TRY(ds_push(fh, (uint32_t) v)); + return FH_OK; +} + +static enum fh_error w_slash(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t a = 0, b = 0; + TRY(ds_pop(fh, &b)); + TRY(ds_pop(fh, &a)); + + if (b == 0) { + return FH_ERR_DIV_BY_ZERO; + } + + TRY(ds_push(fh, a / b)); + return FH_OK; +} + +static enum fh_error w_slash_mod(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t a = 0, b = 0; + TRY(ds_pop(fh, &b)); + TRY(ds_pop(fh, &a)); + + if (b == 0) { + return FH_ERR_DIV_BY_ZERO; + } + + uint32_t rem = a % b; + uint32_t div = a / b; + + TRY(ds_push(fh, rem)); + TRY(ds_push(fh, div)); + return FH_OK; +} + +static enum fh_error w_colon(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; if (fh->state != FH_STATE_INTERPRET) { return FH_ERR_INVALID_STATE; } @@ -54,8 +227,9 @@ static enum fh_error w_colon(struct fh_thread_s *fh) return FH_OK; } -static enum fh_error w_semicolon(struct fh_thread_s *fh) +static enum fh_error w_semicolon(struct fh_thread_s *fh, const struct fh_word_s *w) { + (void) w; enum fh_error rv; struct fh_instruction_s instr; @@ -73,8 +247,9 @@ static enum fh_error w_semicolon(struct fh_thread_s *fh) return FH_OK; } -static enum fh_error w_dup(struct fh_thread_s *fh) +static enum fh_error w_dup(struct fh_thread_s *fh, const struct fh_word_s *w) { + (void) w; enum fh_error rv; uint32_t a = 0; TRY(ds_peek(fh, &a)); @@ -82,30 +257,76 @@ static enum fh_error w_dup(struct fh_thread_s *fh) return FH_OK; } -static enum fh_error w_drop(struct fh_thread_s *fh) +static enum fh_error w_two_dup(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t a = 0; + uint32_t b = 0; + TRY(ds_peek_n(fh, &a, 0)); + TRY(ds_peek_n(fh, &b, 1)); + TRY(ds_push(fh, b)); + TRY(ds_push(fh, a)); + return FH_OK; +} + +static enum fh_error w_drop(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t a = 0; + TRY(ds_pop(fh, &a)); + return FH_OK; +} + +static enum fh_error w_two_drop(struct fh_thread_s *fh, const struct fh_word_s *w) { + (void) w; enum fh_error rv; uint32_t a = 0; TRY(ds_pop(fh, &a)); + TRY(ds_pop(fh, &a)); return FH_OK; } -static enum fh_error w_swap(struct fh_thread_s *fh) +static enum fh_error w_swap(struct fh_thread_s *fh, const struct fh_word_s *w) { + (void) w; enum fh_error rv; TRY(ds_roll(fh, 1)); return FH_OK; } -static enum fh_error w_rot(struct fh_thread_s *fh) +static enum fh_error w_two_swap(struct fh_thread_s *fh, const struct fh_word_s *w) { + (void) w; + enum fh_error rv; + if (fh->data_stack_top < 4) { + LOG("DS two-swap UNDERFLOW"); + return FH_ERR_DS_UNDERFLOW; + } + + uint32_t n = fh->data_stack_top - 4; + uint32_t a = fh->data_stack[n]; + uint32_t b = fh->data_stack[n + 1]; + fh->data_stack[n] = fh->data_stack[n + 2]; + fh->data_stack[n + 1] = fh->data_stack[n + 3]; + fh->data_stack[n + 2] = a; + fh->data_stack[n + 3] = b; + return FH_OK; +} + +static enum fh_error w_rot(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; enum fh_error rv; TRY(ds_roll(fh, 2)); return FH_OK; } -static enum fh_error w_over(struct fh_thread_s *fh) +static enum fh_error w_over(struct fh_thread_s *fh, const struct fh_word_s *w) { + (void) w; enum fh_error rv; uint32_t a = 0; TRY(ds_peek_n(fh, &a, 1)); @@ -113,8 +334,22 @@ static enum fh_error w_over(struct fh_thread_s *fh) return FH_OK; } -static enum fh_error w_tuck(struct fh_thread_s *fh) +static enum fh_error w_two_over(struct fh_thread_s *fh, const struct fh_word_s *w) { + (void) w; + enum fh_error rv; + uint32_t a = 0; + uint32_t b = 0; + TRY(ds_peek_n(fh, &a, 2)); + TRY(ds_peek_n(fh, &b, 3)); + TRY(ds_push(fh, b)); + TRY(ds_push(fh, a)); + return FH_OK; +} + +static enum fh_error w_tuck(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; enum fh_error rv; uint32_t a = 0; uint32_t b = 0; @@ -126,8 +361,9 @@ static enum fh_error w_tuck(struct fh_thread_s *fh) return FH_OK; } -static enum fh_error w_pick(struct fh_thread_s *fh) +static enum fh_error w_pick(struct fh_thread_s *fh, const struct fh_word_s *w) { + (void) w; enum fh_error rv; uint32_t nth = 0; uint32_t a = 0; @@ -137,8 +373,9 @@ static enum fh_error w_pick(struct fh_thread_s *fh) return FH_OK; } -static enum fh_error w_roll(struct fh_thread_s *fh) +static enum fh_error w_roll(struct fh_thread_s *fh, const struct fh_word_s *w) { + (void) w; enum fh_error rv; uint32_t n = 0; TRY(ds_pop(fh, &n)); @@ -146,8 +383,78 @@ static enum fh_error w_roll(struct fh_thread_s *fh) return FH_OK; } -static enum fh_error w_dot(struct fh_thread_s *fh) +static enum fh_error w_to_r(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t a; + TRY(ds_pop(fh, &a)); + TRY(rs_push(fh, a)); + return FH_OK; +} + +static enum fh_error w_two_to_r(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t a; + uint32_t b; + TRY(ds_pop(fh, &a)); + TRY(ds_pop(fh, &b)); + TRY(rs_push(fh, b)); + TRY(rs_push(fh, a)); + return FH_OK; +} + +static enum fh_error w_two_r_from(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t a; + uint32_t b; + TRY(rs_pop(fh, &a)); + TRY(rs_pop(fh, &b)); + TRY(ds_push(fh, b)); + TRY(ds_push(fh, a)); + return FH_OK; +} + +static enum fh_error w_two_r_fetch(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t a; + uint32_t b; + TRY(rs_peek_n(fh, &a, 0)); + TRY(rs_peek_n(fh, &b, 1)); + TRY(ds_push(fh, b)); + TRY(ds_push(fh, a)); + return FH_OK; +} + +static enum fh_error w_r_from(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t a; + TRY(rs_pop(fh, &a)); + TRY(ds_push(fh, a)); + return FH_OK; +} + +static enum fh_error w_r_fetch(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t a; + TRY(rs_peek(fh, &a)); + TRY(ds_push(fh, a)); + return FH_OK; +} + +static enum fh_error w_dot(struct fh_thread_s *fh, const struct fh_word_s *w) { + (void) w; enum fh_error rv; uint32_t a = 0; TRY(ds_pop(fh, &a)); @@ -156,8 +463,9 @@ static enum fh_error w_dot(struct fh_thread_s *fh) return FH_OK; } -static enum fh_error w_type(struct fh_thread_s *fh) +static enum fh_error w_type(struct fh_thread_s *fh, const struct fh_word_s *w) { + (void) w; enum fh_error rv; uint32_t count = 0, addr = 0; TRY(ds_pop(fh, &count)); @@ -167,59 +475,167 @@ static enum fh_error w_type(struct fh_thread_s *fh) return FH_OK; } -static enum fh_error w_cr(struct fh_thread_s *fh) +static enum fh_error w_cr(struct fh_thread_s *fh, const struct fh_word_s *w) { + (void) w; (void) fh; FHPRINT("\n"); return FH_OK; } -static enum fh_error w_space(struct fh_thread_s *fh) +static enum fh_error w_space(struct fh_thread_s *fh, const struct fh_word_s *w) { + (void) w; (void) fh; FHPRINT(" "); return FH_OK; } -static enum fh_error w_dump(struct fh_thread_s *fh) +static enum fh_error w_dump(struct fh_thread_s *fh, const struct fh_word_s *w) { + (void) w; (void) fh; + + FHPRINT("DS "); for (int i = 0; i < fh->data_stack_top; i++) { FHPRINT("%d ", fh->data_stack[i]); } + + FHPRINT("\nRS "); + for (int i = 0; i < fh->return_stack_top; i++) { + FHPRINT("%d ", fh->return_stack[i]); + } + + FHPRINT("\n"); return FH_OK; } -static enum fh_error w_s_quote(struct fh_thread_s *fh) +static enum fh_error w_abort(struct fh_thread_s *fh, const struct fh_word_s *w) { + (void) w; + + fh->data_stack_top = 0; + fh->return_stack_top = 0; + fh->state = FH_STATE_QUIT; + + return FH_OK; +} + +static enum fh_error w_quit(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + + fh->return_stack_top = 0; + fh->state = FH_STATE_QUIT; + + return FH_OK; +} + +static enum fh_error w_s_quote(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; fh_setsubstate(fh, FH_SUBSTATE_SQUOTE); return FH_OK; } -static enum fh_error w_dot_quote(struct fh_thread_s *fh) +static enum fh_error w_dot_quote(struct fh_thread_s *fh, const struct fh_word_s *w) { + (void) w; fh_setsubstate(fh, FH_SUBSTATE_DOTQUOTE); return FH_OK; } -static enum fh_error w_backslash(struct fh_thread_s *fh) +static enum fh_error w_backslash(struct fh_thread_s *fh, const struct fh_word_s *w) { + (void) w; fh_setsubstate(fh, FH_SUBSTATE_LINECOMMENT); return FH_OK; } -static enum fh_error w_paren(struct fh_thread_s *fh) +static enum fh_error w_paren(struct fh_thread_s *fh, const struct fh_word_s *w) { + (void) w; fh_setsubstate(fh, FH_SUBSTATE_PARENCOMMENT); return FH_OK; } -static enum fh_error w_bye(struct fh_thread_s *fh) +static enum fh_error w_bye(struct fh_thread_s *fh, const struct fh_word_s *w) { + (void) w; fh_setstate(fh, FH_STATE_SHUTDOWN, 0); return FH_OK; } +static enum fh_error wp_setbase(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + fh->base = w->param; + return FH_OK; +} + +static enum fh_error w_base(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + TRY(ds_push(fh, MAGICADDR_BASE)); + return FH_OK; +} + +static enum fh_error w_fetch(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t addr = 0; + TRY(ds_pop(fh, &addr)); + uint32_t val = 0; + TRY(fh_fetch(fh, addr, &val)); + TRY(ds_push(fh, val)); + return FH_OK; +} + +static enum fh_error w_store(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t addr = 0; + TRY(ds_pop(fh, &addr)); + uint32_t val = 0; + TRY(ds_pop(fh, &val)); + + TRY(fh_store(fh, addr, val)); + return FH_OK; +} + +static enum fh_error w_two_store(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t addr = 0; + TRY(ds_pop(fh, &addr)); + uint32_t a = 0, b = 0; + TRY(ds_pop(fh, &a)); + TRY(ds_pop(fh, &b)); + + TRY(fh_store(fh, addr, a)); + TRY(fh_store(fh, addr + CELL, b)); + return FH_OK; +} + +static enum fh_error w_two_fetch(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t addr = 0; + TRY(ds_pop(fh, &addr)); + uint32_t a = 0, b = 0; + TRY(fh_fetch(fh, addr, &a)); + TRY(fh_fetch(fh, addr + CELL, &b)); + TRY(ds_push(fh, b)); + TRY(ds_push(fh, a)); + return FH_OK; +} + /** Add pointers to built-in word handlers to a runtime struct */ enum fh_error register_builtin_words(struct fh_thread_s *fh) { @@ -227,37 +643,79 @@ enum fh_error register_builtin_words(struct fh_thread_s *fh) const char *name; word_exec_t handler; bool immediate; + uint32_t param; }; const struct name_and_handler builtins[] = { - {"s\"", w_s_quote, 1}, - {".\"", w_dot_quote, 1}, + {"s\"", w_s_quote, 1, 0}, + {".\"", w_dot_quote, 1, 0}, /* Compiler control words */ - {"bye", w_bye, 0}, + {"bye", w_bye, 0, 0}, + /* Pointers */ + {"@", w_fetch, 0, 0}, + {"!", w_store, 0, 0}, + {"2!", w_two_store, 0, 0}, + {"2@", w_two_fetch, 0, 0}, /* Arithmetics */ - {"+", w_add, 0}, - {"-", w_sub, 0}, - {"*", w_mul, 0}, + {"dec", wp_setbase, 0, 10}, + {"hex", wp_setbase, 0, 16}, + {"base", w_base, 0, 0}, + {"+", w_plus, 0, 0}, + {"-", w_minus, 0, 0}, + {"*", w_star, 0, 0}, + {"*/", w_star_slash, 0, 0}, + {"/", w_slash, 0, 0}, + {"/mod", w_slash_mod, 0, 0}, + {"0<", w_zero_less, 0, 0}, + {"0=", w_zero_equals, 0, 0}, + {"0<>", w_zero_not_equals, 0, 0}, + {"0>", w_zero_greater, 0, 0}, + {"<", w_less, 0, 0}, + {"=", w_equals, 0, 0}, + {"<>", w_not_equals, 0, 0}, + {">", w_greater, 0, 0}, + {"1+", wp_add, 0, 1}, + {"1-", wp_add, 0, -1}, + {"2+", wp_add, 0, 2}, + {"2-", wp_add, 0, -2}, + {"2*", wp_mul, 0, 2}, + {"2/", wp_div, 0, 2}, /* Stack manip */ - {"dup", w_dup, 0}, - {"drop", w_drop, 0}, - {"swap", w_swap, 0}, - {"rot", w_rot, 0}, - {"over", w_over, 0}, - {"tuck", w_tuck, 0}, - {"pick", w_pick, 0}, - {"roll", w_roll, 0}, + {"drop", w_drop, 0, 0}, + {"dup", w_dup, 0, 0}, + {"over", w_over, 0, 0}, + {"swap", w_swap, 0, 0}, + {"rot", w_rot, 0, 0}, + {"tuck", w_tuck, 0, 0}, + {"pick", w_pick, 0, 0}, + {"roll", w_roll, 0, 0}, + /* Double wide stack manip */ + {"2drop", w_two_drop, 0, 0}, + {"2dup", w_two_dup, 0, 0}, + {"2over", w_two_over, 0, 0}, + {"2swap", w_two_swap, 0, 0}, +// /* Return stack manip */ + {">r", w_to_r, 0, 0}, + {"r>", w_r_from, 0, 0}, + {"r@", w_r_fetch, 0, 0}, +// /* Double wide return stack manip */ + {"2>r", w_two_to_r, 0, 0}, + {"2r>", w_two_r_from, 0, 0}, + {"2r@", w_two_r_fetch, 0, 0}, /* Printing */ - {".", w_dot, 0}, - {"type", w_type, 0}, - {"cr", w_cr, 0}, - {"space", w_space, 0}, - {"dump", w_dump, 0}, - /* Control words */ - {":", w_colon, 0}, - {";", w_semicolon, 1}, - {"\\", w_backslash, 1}, // line comment - {"(", w_paren, 1}, // enclosed comment + {".", w_dot, 0, 0}, + {"type", w_type, 0, 0}, + {"cr", w_cr, 0, 0}, + {"space", w_space, 0, 0}, + {"dump", w_dump, 0, 0}, + /* Control flow */ + {"abort", w_abort, 0, 0}, + {"quit", w_quit, 0, 0}, + /* Syntax */ + {":", w_colon, 0, 0}, + {";", w_semicolon, 1, 0}, + {"\\", w_backslash, 1, 0}, // line comment + {"(", w_paren, 1, 0}, // enclosed comment { /* end marker */ } }; @@ -270,6 +728,7 @@ enum fh_error register_builtin_words(struct fh_thread_s *fh) w.handler = p->handler; w.builtin = 1; w.immediate = p->immediate; + w.param = p->param; rv = fh_add_word(&w, fh); if (rv != FH_OK) { return rv; diff --git a/src/fh_error.c b/src/fh_error.c index fddbf3b..f0d7ae3 100644 --- a/src/fh_error.c +++ b/src/fh_error.c @@ -1,7 +1,7 @@ #include "fh_error.h" /** Error names */ -static const char *errornames[] = { +static const char *errornames[FH_ERR_MAX] = { [FH_OK] = "OK", [FH_ERR_CS_OVERFLOW] = "CS_OVERFLOW", [FH_ERR_DS_OVERFLOW] = "DS_OVERFLOW", @@ -16,6 +16,8 @@ static const char *errornames[] = { [FH_ERR_INVALID_STATE] = "INVALID_STATE", [FH_ERR_INTERNAL] = "INTERNAL", [FH_ERR_UNKNOWN_WORD] = "UNKNOWN_WORD", + [FH_ERR_ILLEGAL_FETCH] = "ILLEGAL_FETCH", + [FH_ERR_DIV_BY_ZERO] = "DIV_BY_ZERO", }; /** Get error name from code, returns Unknown if not defined */ diff --git a/src/fh_mem.c b/src/fh_mem.c index bca4561..d33bd23 100644 --- a/src/fh_mem.c +++ b/src/fh_mem.c @@ -1,9 +1,82 @@ #include +#include "fh_print.h" #include "fh_error.h" #include "fh_runtime.h" #include "fh_mem.h" +enum fh_error fh_fetch(struct fh_thread_s *fh, uint32_t addr, uint32_t *dst) +{ + switch (addr) { + case MAGICADDR_BASE: + *dst = fh->base; + break; + // TODO more magic + + default: + if (addr & 3) { + LOGE("Address 0x%08x is not aligned!", addr); + return FH_ERR_ILLEGAL_FETCH; + } + if (addr < HEAP_SIZE - 4) { + *dst = *((uint32_t*)&fh->heap[addr]); + } else { + LOGE("Address 0x%08x too high!", addr); + return FH_ERR_ILLEGAL_FETCH; + } + } + + return FH_OK; +} + +enum fh_error fh_fetch_char(struct fh_thread_s *fh, uint32_t addr, char *dst) +{ + if (addr < HEAP_SIZE - 4) { + *dst = (char) fh->heap[addr]; + } else { + LOGE("Address 0x%08x too high!", addr); + return FH_ERR_ILLEGAL_FETCH; + } + + return FH_OK; +} + +enum fh_error fh_store(struct fh_thread_s *fh, uint32_t addr, uint32_t val) +{ + switch (addr) { + case MAGICADDR_BASE: + fh->base = val; + break; + // TODO more magic + + default: + if (addr & 3) { + LOGE("Address 0x%08x is not aligned!", addr); + return FH_ERR_ILLEGAL_FETCH; + } + if (addr <= HEAP_SIZE - 4) { + *((uint32_t*)&fh->heap[addr]) = val; + } else { + LOGE("Address 0x%08x too high!", addr); + return FH_ERR_ILLEGAL_FETCH; + } + } + + return FH_OK; +} + +enum fh_error fh_store_char(struct fh_thread_s *fh, uint32_t addr, char val) +{ + if (addr < HEAP_SIZE) { + fh->heap[addr] = val; + } else { + LOGE("Address 0x%08x too high!", addr); + return FH_ERR_ILLEGAL_FETCH; + } + + return FH_OK; +} + /** Allocate a heap region, e.g. for a string. The address is stored to `addr` */ enum fh_error fh_heap_reserve( struct fh_thread_s *fh, diff --git a/src/fh_runtime.c b/src/fh_runtime.c index 15465e3..bd35291 100644 --- a/src/fh_runtime.c +++ b/src/fh_runtime.c @@ -13,14 +13,14 @@ struct fh_global_s fh_globals = {}; /** State names */ -static const char *statenames[] = { +static const char *statenames[FH_STATE_MAX] = { [FH_STATE_INTERPRET] = "INTERPRET", [FH_STATE_COMPILE] = "COMPILE", [FH_STATE_SHUTDOWN] = "SHUTDOWN", }; /** Sub-state names */ -static const char *substatenames[] = { +static const char *substatenames[FH_SUBSTATE_MAX] = { [FH_SUBSTATE_NONE] = "NONE", [FH_SUBSTATE_COLONNAME] = "COLONNAME", [FH_SUBSTATE_SQUOTE] = "SQUOTE", @@ -65,15 +65,15 @@ void fh_setsubstate(struct fh_thread_s *fh, enum fh_substate substate) } /** Execute a user word */ -enum fh_error w_user_word(struct fh_thread_s *fh) +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: - w = fh->exec_word; if (!w) { return FH_ERR_INTERNAL; } LOG("Run user word: %s", w->name); @@ -82,6 +82,11 @@ enum fh_error w_user_word(struct fh_thread_s *fh) fh->execptr = w->start; instr:; + if (fh->state == FH_STATE_QUIT) { + /* abort or quit was called, return to interactive mode */ + fh_setstate(fh, FH_STATE_INTERPRET, FH_SUBSTATE_NONE); + return FH_OK; + } // make sure it's aligned fh->execptr = WORDALIGNED(fh->execptr); const struct fh_instruction_s *instr = (const struct fh_instruction_s *) &fh->compile[fh->execptr]; @@ -128,11 +133,11 @@ enum fh_error w_user_word(struct fh_thread_s *fh) w2 = &fh->dict[instr->data]; if (w2->builtin) { LOG("Exec: builtin-word %s", w2->name); - w2->handler(fh); + w2->handler(fh, w2); goto instr; } else { LOG("Exec: user-word %s (CALL)", w2->name); - fh->exec_word = &fh->dict[instr->data]; + w = &fh->dict[instr->data]; goto call; } } @@ -154,6 +159,7 @@ enum fh_error fh_init(struct fh_thread_s *fh) TRY(register_builtin_words(fh)); fh->execptr = MAGICADDR_INTERACTIVE; + fh->base = 10; return FH_OK; } @@ -229,8 +235,7 @@ static enum fh_error fh_handle_word( } else { /* interpret */ LOG("Interpret word: %s", w->name); - fh->exec_word = w; - TRY(w->handler(fh)); + TRY(w->handler(fh, w)); } return FH_OK; } @@ -241,7 +246,7 @@ static enum fh_error fh_handle_word( /* word not found, try parsing as number */ errno = 0; char *endptr; - long v = strtol(start, &endptr, 0); + long v = strtol(start, &endptr, (int) fh->base); // XXX if base is 0, this will use auto-detection if (errno != 0 || endptr == start) { LOGE("Unknown word and fail to parse as number: %.*s", (int) len, start); return FH_ERR_UNKNOWN_WORD; diff --git a/src/main.c b/src/main.c index e376bc4..d6278e0 100644 --- a/src/main.c +++ b/src/main.c @@ -49,9 +49,12 @@ int main(int argc, char *argv[]) } } + const char *prompt = "> "; + /* process input line by line */ int linecnt = 0; char linebuf[MAXLINE]; + FHPRINT("%s", prompt); while (fh.state != FH_STATE_SHUTDOWN && fgets(linebuf, MAXLINE, infile)) { linecnt++; @@ -76,6 +79,8 @@ int main(int argc, char *argv[]) /* reset state */ fh_setstate(&fh, FH_STATE_INTERPRET, FH_SUBSTATE_NONE); } + + FHPRINT("%s", prompt); } // Show resource usage