Trying to build a forth runtime in C
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
forth/include/fh_runtime.h

281 lines
6.9 KiB

/**
* 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;
};
static inline void instr_init(struct fh_instruction_s *instr, enum fh_instruction_kind kind, uint32_t data)
{
instr->kind = kind;
instr->data = 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_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 */
#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 MAGICADDR_DICTFIRST 0xFFFFFFFFULL
#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;
/** 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;
};
#define HEAP_END (HEAP_SIZE - WORDBUF_SIZE - INPUT_BUFFER_SIZE)
#define WORDBUF_ADDR HEAP_END
#define INPUTBUF_ADDR (HEAP_END + WORDBUF_SIZE)
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
);
/* if the return address is this, we should drop back to interactive mode */
// SFR and magic addresses are "negative"
#define MAGICADDR_EXEC_INTERACTIVE 0xFFFFFFFFULL
#define MAGICADDR_BASE 0xFFFFBA5EULL
#define MAGICADDR_HERE 0xFFFF4E7EULL
#define MAGICADDR_INPTR 0xFFFFF111ULL
#define MAGICADDR_UNRESOLVED 0xFFFFFBADULL
/** Get a value rounded up to multiple of word size */
#define WORDALIGNED(var) (((var) + 3) & ~3)
_Static_assert(WORDALIGNED(0) == 0, "word align");
_Static_assert(WORDALIGNED(1) == 4, "word align");
_Static_assert(WORDALIGNED(2) == 4, "word align");
_Static_assert(WORDALIGNED(3) == 4, "word align");
_Static_assert(WORDALIGNED(4) == 4, "word align");
_Static_assert(WORDALIGNED(5) == 8, "word align");
_Static_assert(WORDALIGNED(1023) == 1024, "word align");
_Static_assert(WORDALIGNED(1024) == 1024, "word align");
#define TRY(x) \
do { \
if (FH_OK != (rv = (x))) return rv; \
} while (0)
/**
* 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);
#endif //FORTH_FH_RUNTIME_H