|
|
|
/**
|
|
|
|
* Forth runtime internals
|
|
|
|
*
|
|
|
|
* Created on 2021/11/13.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef FORTH_FH_RUNTIME_H
|
|
|
|
#define FORTH_FH_RUNTIME_H
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
|
|
|
#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
|