/** * Forth runtime internals * * Created on 2021/11/13. */ #ifndef FORTH_FH_RUNTIME_H #define FORTH_FH_RUNTIME_H #include #include #include #include #include "fh_config.h" struct fh_word_s; struct fh_instruction_s; struct fh_thread_s; /** Word handler typedef */ typedef enum fh_error (*word_exec_t)(struct fh_thread_s *fh, const struct fh_word_s *w); /** Bytecode instruction type marker */ enum fh_instruction_kind { /* Data = word pointer (dict index) */ FH_INSTR_WORD, /* Data = numeric value to push onto the data stack */ FH_INSTR_NUMBER, /** End of a user defined word, pop address and jump back */ FH_INSTR_ENDWORD, /** This is the `s"` instruction, the length (u32) and string data immediately follow */ FH_INSTR_ALLOCSTR, /** This is the `."` instruction, same format as above. */ FH_INSTR_TYPESTR, /* Unconditional jump */ FH_INSTR_JUMP, /* Jump if zero */ FH_INSTR_JUMPZERO, /* Loop exit */ FH_INSTR_LEAVE, /* DO loop initializer */ FH_INSTR_DO, /* value TO var */ FH_INSTR_TO, /* ?DO short-circuiting loop */ FH_INSTR_DO_QUESTION, /* Loop end instr */ FH_INSTR_LOOP, /* Loop end instr with custom step */ FH_INSTR_LOOP_PLUS, /* Postponed word */ FH_INSTR_POSTPONED_WORD, FH_INSTR_MAX, }; const char *instr_name(enum fh_instruction_kind kind); /** One instruction in bytecode */ struct fh_instruction_s { /** What is the meaning of data? */ enum fh_instruction_kind kind; /** Data word */ uint32_t data; }; #define INSTR_SIZE (sizeof(struct fh_instruction_s)) _Static_assert(sizeof(struct fh_instruction_s) % 4 == 0, "Instruction struct is aligned"); /** Forth runtime major state */ enum fh_state { /** Interactive interpret mode */ FH_STATE_INTERPRET = 0, /** Compiling */ FH_STATE_COMPILE, /** Quit from RUN to interpret */ FH_STATE_QUIT, /** Shutting down the runtime */ FH_STATE_SHUTDOWN, FH_STATE_MAX, }; /** Forth runtime minor state */ enum fh_substate { FH_SUBSTATE_NONE = 0, FH_SUBSTATE_PAREN_COMMENT, FH_SUBSTATE_LINE_COMMENT, FH_SUBSTATE_EXIT, FH_SUBSTATE_SKIP_IF, FH_SUBSTATE_MAX, }; extern const char *substatenames[FH_SUBSTATE_MAX]; /** Marks a dictionary entry that is a word */ #define WORDFLAG_WORD 0x01 /** Indicates that this is a built-in instruction and not a word call */ #define WORDFLAG_BUILTIN 0x02 /** Indicates that this instruction should always be treated as interpreted */ #define WORDFLAG_IMMEDIATE 0x04 /** Variable or value stored in the dictionary */ #define WORDFLAG_VARIABLE 0x08 /** Constant with a value assigned */ #define WORDFLAG_CONSTANT 0x10 /** Something CREATEd. When executed, put the data field address on stack */ #define WORDFLAG_CREATED 0x20 /** Word struct as they are stored in the dictionary */ struct fh_word_s { /** * Start address in case of user words, or param for builtins. * Param must be the first for convenience in variable code! */ uint32_t param; /** * Handler function. * Builtin functions use pre-defined native handlers. * User words use a shared handler that executes compiled * bytecode at 'start' address of the compile-memory area. */ word_exec_t handler; /** Word flags, using WORDFLAG_ defines */ uint32_t flags; /** Linked list pointer to previous word */ uint32_t previous; /** Word name */ char name[MAX_NAME_LEN]; // XXX this wastes RAM! }; #define DICTWORD_SIZE sizeof(struct fh_word_s) /** * Forth runtime instance - state variables and memory areas. * * Some memory areas, such as the dict or heap, could be moved * 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; size_t data_stack_hwm; /** Return stack */ uint32_t return_stack[RETURN_STACK_DEPTH]; size_t return_stack_top; size_t return_stack_hwm; /** Data buffer used for everything */ uint8_t heap[HEAP_SIZE]; size_t here; /** Pointer into the compile buffer for execution */ uint32_t execptr; /** Address of the last dict word */ uint32_t dict_last; /** Pictured numeric output buffer write cursor (used for prepend) */ uint32_t pictnumptr; /** Input buffer parse position */ uint32_t inputptr; /** Input buffer total content size */ uint32_t inputlen; /** Forth state */ enum fh_state state; /** Forth sub-state */ enum fh_substate substate; /** The numeric base register */ uint32_t base; /** Loop variable I */ uint32_t loop_i; /** Loop variable J */ uint32_t loop_j; /** Nesting level of [if] */ uint32_t parse_if_level; bool executing_compiled; }; enum fh_error fh_loop_nest(struct fh_thread_s *fh, uint32_t indexvalue); enum fh_error fh_loop_unnest(struct fh_thread_s *fh); 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, const struct fh_word_s *w); enum fh_error fh_postpone_word( struct fh_thread_s *fh, const char *name, size_t wordlen ); /** Show disassembly of a dictionary word */ enum fh_error fh_see_word( struct fh_thread_s *fh, const char *name, size_t wordlen ); /** * Execute a dictionary word from a definition stored at the given address * @param fh * @param addr * @return */ enum fh_error fh_handle_word(struct fh_thread_s *fh, uint32_t addr); /** * Find a word in the dict * * @param fh * @param name - name, may be NUL terminated * @param wordlen - length, use 0 for strlen * @param addr_out the word address is output here, if given * @return success */ enum fh_error fh_find_word(struct fh_thread_s *fh, const char *name, size_t wordlen, uint32_t *addr_out); enum fh_error fh_init(struct fh_thread_s *fh); enum fh_error fh_process_line(struct fh_thread_s *fh, const char *linebuf, size_t len); static inline uint32_t word_addr(struct fh_thread_s *fh, const struct fh_word_s *w) { return (uint32_t) ((void*)w - (void*)&fh->heap[0]); } #endif //FORTH_FH_RUNTIME_H