summaryrefslogtreecommitdiffstats
path: root/qemu/roms/openbios/kernel/forth.c
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/roms/openbios/kernel/forth.c')
-rw-r--r--qemu/roms/openbios/kernel/forth.c1966
1 files changed, 1966 insertions, 0 deletions
diff --git a/qemu/roms/openbios/kernel/forth.c b/qemu/roms/openbios/kernel/forth.c
new file mode 100644
index 000000000..61dd70d31
--- /dev/null
+++ b/qemu/roms/openbios/kernel/forth.c
@@ -0,0 +1,1966 @@
+/* tag: C implementation of all forth primitives,
+ * internal words, inner interpreter and such
+ *
+ * Copyright (C) 2003 Patrick Mauritz, Stefan Reinauer
+ *
+ * See the file "COPYING" for further information about
+ * the copyright and warranty status of this work.
+ */
+
+#include "config.h"
+#include "sysinclude.h"
+#include "kernel/stack.h"
+#include "kernel/kernel.h"
+#include "dict.h"
+
+/*
+ * cross platform abstraction
+ */
+
+#include "cross.h"
+
+#ifndef FCOMPILER
+#include "libc/vsprintf.h"
+#else
+#include <stdarg.h>
+#endif
+
+/*
+ * execution works as follows:
+ * - PC is pushed on return stack
+ * - PC is set to new CFA
+ * - address pointed by CFA is executed by CPU
+ */
+
+typedef void forth_word(void);
+
+static forth_word * const words[];
+ucell PC;
+volatile int interruptforth = 0;
+
+#define DEBUG_MODE_NONE 0
+#define DEBUG_MODE_STEP 1
+#define DEBUG_MODE_TRACE 2
+#define DEBUG_MODE_STEPUP 3
+
+#define DEBUG_BANNER "\nStepper keys: <space>/<enter> Up Down Trace Rstack Forth\n"
+
+/* Empty linked list of debug xts */
+struct debug_xt {
+ ucell xt_docol;
+ ucell xt_semis;
+ int mode;
+ struct debug_xt *next;
+};
+
+static struct debug_xt debug_xt_eol = { (ucell)0, (ucell)0, 0, NULL};
+static struct debug_xt *debug_xt_list = &debug_xt_eol;
+
+/* Static buffer for xt name */
+char xtname[MAXNFALEN];
+
+#ifndef FCOMPILER
+/* instead of pointing to an explicit 0 variable we
+ * point behind the pointer.
+ */
+static ucell t[] = { 0, 0, 0, 0 };
+static ucell *trampoline = t;
+
+/*
+ * Code Field Address (CFA) definitions (DOCOL and the like)
+ */
+
+void forth_init(void)
+{
+ init_trampoline(trampoline);
+}
+#endif
+
+#ifndef CONFIG_DEBUG_INTERPRETER
+#define dbg_interp_printk( a... ) do { } while(0)
+#else
+#define dbg_interp_printk( a... ) printk( a )
+#endif
+
+#ifndef CONFIG_DEBUG_INTERNAL
+#define dbg_internal_printk( a... ) do { } while(0)
+#else
+#define dbg_internal_printk( a... ) printk( a )
+#endif
+
+
+void init_trampoline(ucell *tramp)
+{
+ tramp[0] = DOCOL;
+ tramp[1] = 0;
+ tramp[2] = target_ucell(pointer2cell(tramp) + 3 * sizeof(ucell));
+ tramp[3] = 0;
+}
+
+static inline void processxt(ucell xt)
+{
+ void (*tokenp) (void);
+
+ dbg_interp_printk("processxt: pc=%x, xt=%x\n", PC, xt);
+ tokenp = words[xt];
+ tokenp();
+}
+
+static void docol(void)
+{ /* DOCOL */
+ PUSHR(PC);
+ PC = read_ucell(cell2pointer(PC));
+
+ dbg_interp_printk("docol: %s\n", cell2pointer( lfa2nfa(PC - sizeof(cell)) ));
+}
+
+static void semis(void)
+{
+ PC = POPR();
+}
+
+static inline void next(void)
+{
+ PC += sizeof(ucell);
+
+ dbg_interp_printk("next: PC is now %x\n", PC);
+ processxt(read_ucell(cell2pointer(read_ucell(cell2pointer(PC)))));
+}
+
+static inline void next_dbg(void);
+
+int enterforth(xt_t xt)
+{
+ ucell *_cfa = (ucell*)cell2pointer(xt);
+ cell tmp;
+
+ if (read_ucell(_cfa) != DOCOL) {
+ trampoline[1] = target_ucell(xt);
+ _cfa = trampoline;
+ }
+
+ if (rstackcnt < 0) {
+ rstackcnt = 0;
+ }
+
+ tmp = rstackcnt;
+ interruptforth = FORTH_INTSTAT_CLR;
+
+ PUSHR(PC);
+ PC = pointer2cell(_cfa);
+
+ while (rstackcnt > tmp && !(interruptforth & FORTH_INTSTAT_STOP)) {
+ if (debug_xt_list->next == NULL) {
+ while (rstackcnt > tmp && !interruptforth) {
+ dbg_interp_printk("enterforth: NEXT\n");
+ next();
+ }
+ } else {
+ while (rstackcnt > tmp && !interruptforth) {
+ dbg_interp_printk("enterforth: NEXT_DBG\n");
+ next_dbg();
+ }
+ }
+
+ /* Always clear the debug mode change flag */
+ interruptforth = interruptforth & (~FORTH_INTSTAT_DBG);
+ }
+
+#if 0
+ /* return true if we took an exception. The caller should normally
+ * handle exceptions by returning immediately since the throw
+ * is supposed to abort the execution of this C-code too.
+ */
+
+ if (rstackcnt != tmp) {
+ printk("EXCEPTION DETECTED!\n");
+ }
+#endif
+ return rstackcnt != tmp;
+}
+
+/* called inline thus a slightly different behaviour */
+static void lit(void)
+{ /* LIT */
+ PC += sizeof(cell);
+ PUSH(read_ucell(cell2pointer(PC)));
+ dbg_interp_printk("lit: %x\n", read_ucell(cell2pointer(PC)));
+}
+
+static void docon(void)
+{ /* DOCON */
+ ucell tmp = read_ucell(cell2pointer(read_ucell(cell2pointer(PC)) + sizeof(ucell)));
+ PUSH(tmp);
+ dbg_interp_printk("docon: PC=%x, value=%x\n", PC, tmp);
+}
+
+static void dovar(void)
+{ /* DOVAR */
+ ucell tmp = read_ucell(cell2pointer(PC)) + sizeof(ucell);
+ PUSH(tmp); /* returns address to variable */
+ dbg_interp_printk("dovar: PC: %x, %x\n", PC, tmp);
+}
+
+static void dobranch(void)
+{ /* unconditional branch */
+ PC += sizeof(cell);
+ PC += read_cell(cell2pointer(PC));
+}
+
+static void docbranch(void)
+{ /* conditional branch */
+ PC += sizeof(cell);
+ if (POP()) {
+ dbg_internal_printk(" ?branch: end loop\n");
+ } else {
+ dbg_internal_printk(" ?branch: follow branch\n");
+ PC += read_cell(cell2pointer(PC));
+ }
+}
+
+
+static void execute(void)
+{ /* EXECUTE */
+ ucell address = POP();
+ dbg_interp_printk("execute: %x\n", address);
+
+ PUSHR(PC);
+ trampoline[1] = target_ucell(address);
+ PC = pointer2cell(trampoline);
+}
+
+/*
+ * call ( ... function-ptr -- ??? )
+ */
+static void call(void)
+{
+#ifdef FCOMPILER
+ printk("Sorry. Usage of Forth2C binding is forbidden during bootstrap.\n");
+ exit(1);
+#else
+ void (*funcptr) (void);
+ funcptr=(void *)cell2pointer(POP());
+ dbg_interp_printk("call: %x", funcptr);
+ funcptr();
+#endif
+}
+
+/*
+ * sys-debug ( errno -- )
+ */
+
+static void sysdebug(void)
+{
+#ifdef FCOMPILER
+ cell errorno=POP();
+ exception(errorno);
+#else
+ (void) POP();
+#endif
+}
+
+static void dodoes(void)
+{ /* DODOES */
+ ucell data = read_ucell(cell2pointer(PC)) + (2 * sizeof(ucell));
+ ucell word = read_ucell(cell2pointer(read_ucell(cell2pointer(PC)) + sizeof(ucell)));
+
+ dbg_interp_printk("DODOES data=%x word=%x\n", data, word);
+
+ PUSH(data);
+ PUSH(word);
+
+ execute();
+}
+
+static void dodefer(void)
+{
+ docol();
+}
+
+static void dodo(void)
+{
+ cell startval, endval;
+ startval = POP();
+ endval = POP();
+
+ PUSHR(endval);
+ PUSHR(startval);
+}
+
+static void doisdo(void)
+{
+ cell startval, endval, offset;
+
+ startval = POP();
+ endval = POP();
+
+ PC += sizeof(cell);
+
+ if (startval == endval) {
+ offset = read_cell(cell2pointer(PC));
+ PC += offset;
+ } else {
+ PUSHR(endval);
+ PUSHR(startval);
+ }
+}
+
+static void doloop(void)
+{
+ cell offset, startval, endval;
+
+ startval = POPR() + 1;
+ endval = POPR();
+
+ PC += sizeof(cell);
+
+ if (startval < endval) {
+ offset = read_cell(cell2pointer(PC));
+ PC += offset;
+ PUSHR(endval);
+ PUSHR(startval);
+ }
+
+}
+
+static void doplusloop(void)
+{
+ ucell high, low;
+ cell increment, startval, endval, offset;
+
+ increment = POP();
+
+ startval = POPR();
+ endval = POPR();
+
+ low = (ucell) startval;
+ startval += increment;
+
+ PC += sizeof(cell);
+
+ if (increment >= 0) {
+ high = (ucell) startval;
+ } else {
+ high = low;
+ low = (ucell) startval;
+ }
+
+ if (endval - (low + 1) >= high - low) {
+ offset = read_cell(cell2pointer(PC));
+ PC += offset;
+
+ PUSHR(endval);
+ PUSHR(startval);
+ }
+}
+
+/*
+ * instance handling CFAs
+ */
+#ifndef FCOMPILER
+static ucell get_myself(void)
+{
+ static ucell *myselfptr = NULL;
+ if (myselfptr == NULL) {
+ myselfptr = (ucell*)cell2pointer(findword("my-self")) + 1;
+ }
+ ucell *myself = (ucell*)cell2pointer(*myselfptr);
+ return (myself != NULL) ? *myself : 0;
+}
+
+static void doivar(void)
+{
+ ucell r, *p = (ucell *)(*(ucell *) cell2pointer(PC) + sizeof(ucell));
+ ucell ibase = get_myself();
+
+ dbg_interp_printk("ivar, offset: %d size: %d (ibase %d)\n", p[0], p[1], ibase );
+
+ r = ibase ? ibase + p[0] : pointer2cell(&p[2]);
+ PUSH( r );
+}
+
+static void doival(void)
+{
+ ucell r, *p = (ucell *)(*(ucell *) cell2pointer(PC) + sizeof(ucell));
+ ucell ibase = get_myself();
+
+ dbg_interp_printk("ivar, offset: %d size: %d\n", p[0], p[1] );
+
+ r = ibase ? ibase + p[0] : pointer2cell(&p[2]);
+ PUSH( *(ucell *)cell2pointer(r) );
+}
+
+static void doidefer(void)
+{
+ ucell *p = (ucell *)(*(ucell *) cell2pointer(PC) + sizeof(ucell));
+ ucell ibase = get_myself();
+
+ dbg_interp_printk("doidefer, offset: %d size: %d\n", p[0], p[1] );
+
+ PUSHR(PC);
+ PC = ibase ? ibase + p[0] : pointer2cell(&p[2]);
+ PC -= sizeof(ucell);
+}
+#else
+static void noinstances(void)
+{
+ printk("Opening devices is not supported during bootstrap. Sorry.\n");
+ exit(1);
+}
+#define doivar noinstances
+#define doival noinstances
+#define doidefer noinstances
+#endif
+
+/*
+ * $include / $encode-file
+ */
+#ifdef FCOMPILER
+static void
+string_relay(void (*func)(const char *))
+{
+ int len = POP();
+ char *name, *p = (char*)cell2pointer(POP());
+ name = malloc(len + 1);
+ memcpy(name, p, len);
+ name[len] = 0;
+ (*func)(name);
+ free(name);
+}
+#else
+#define string_relay(dummy) do { DROP(); DROP(); } while(0)
+#endif
+
+static void
+do_include(void)
+{
+ string_relay(&include_file);
+}
+
+static void
+do_encode_file( void )
+{
+ string_relay(&encode_file);
+}
+
+/*
+ * Debug support functions
+ */
+
+static
+int printf_console(const char *fmt, ...)
+{
+ cell tmp;
+
+ char buf[512];
+ va_list args;
+ int i;
+
+ va_start(args, fmt);
+ i = vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+
+ /* Push to the Forth interpreter for console output */
+ tmp = rstackcnt;
+
+ PUSH(pointer2cell(buf));
+ PUSH((int)strlen(buf));
+ trampoline[1] = findword("type");
+
+ PUSHR(PC);
+ PC = pointer2cell(trampoline);
+
+ while (rstackcnt > tmp) {
+ dbg_interp_printk("printf_console: NEXT\n");
+ next();
+ }
+
+ return i;
+}
+
+static
+int getchar_console(void)
+{
+ cell tmp;
+
+ /* Push to the Forth interpreter for console output */
+ tmp = rstackcnt;
+
+ trampoline[1] = findword("key");
+
+ PUSHR(PC);
+ PC = pointer2cell(trampoline);
+
+ while (rstackcnt > tmp) {
+ dbg_interp_printk("getchar_console: NEXT\n");
+ next();
+ }
+
+ return POP();
+}
+
+static void
+display_dbg_dstack(void)
+{
+ /* Display dstack contents between parentheses */
+ int i;
+
+ if (dstackcnt == 0) {
+ printf_console(" ( Empty ) ");
+ return;
+ } else {
+ printf_console(" ( ");
+ for (i = 1; i <= dstackcnt; i++) {
+ if (i != 1) {
+ printf_console(" ");
+ }
+ printf_console("%" FMT_CELL_x, dstack[i]);
+ }
+ printf_console(" ) ");
+ }
+}
+
+static void
+display_dbg_rstack(void)
+{
+ /* Display rstack contents between parentheses */
+ int i;
+
+ if (rstackcnt == 0) {
+ printf_console(" ( Empty ) ");
+ return;
+ } else {
+ printf_console("\nR: ( ");
+ for (i = 1; i <= rstackcnt; i++) {
+ if (i != 1) {
+ printf_console(" ");
+ }
+ printf_console("%" FMT_CELL_x, rstack[i]);
+ }
+ printf_console(" ) \n");
+ }
+}
+
+static int
+add_debug_xt(ucell xt)
+{
+ struct debug_xt *debug_xt_item;
+
+ /* If the xt CFA isn't DOCOL then issue a warning and do nothing */
+ if (read_ucell(cell2pointer(xt)) != DOCOL) {
+ printf_console("\nprimitive words cannot be debugged\n");
+ return 0;
+ }
+
+ /* If this xt is already in the list, do nothing but indicate success */
+ for (debug_xt_item = debug_xt_list; debug_xt_item->next != NULL;
+ debug_xt_item = debug_xt_item->next)
+ if (debug_xt_item->xt_docol == xt) {
+ return 1;
+ }
+
+ /* We already have the CFA (PC) indicating the starting cell of
+ the word, however we also need the ending cell too (we cannot
+ rely on the rstack as it can be arbitrarily changed by a forth
+ word). Hence the use of findsemis() */
+
+ /* Otherwise add to the head of the linked list */
+ debug_xt_item = malloc(sizeof(struct debug_xt));
+ debug_xt_item->xt_docol = xt;
+ debug_xt_item->xt_semis = findsemis(xt);
+ debug_xt_item->mode = DEBUG_MODE_NONE;
+ debug_xt_item->next = debug_xt_list;
+ debug_xt_list = debug_xt_item;
+
+ /* Indicate debug mode change */
+ interruptforth |= FORTH_INTSTAT_DBG;
+
+ /* Success */
+ return 1;
+}
+
+static void
+del_debug_xt(ucell xt)
+{
+ struct debug_xt *debug_xt_item, *tmp_xt_item;
+
+ /* Handle the case where the xt is at the head of the list */
+ if (debug_xt_list->xt_docol == xt) {
+ tmp_xt_item = debug_xt_list;
+ debug_xt_list = debug_xt_list->next;
+ free(tmp_xt_item);
+
+ return;
+ }
+
+ /* Otherwise find this xt in the linked list and remove it */
+ for (debug_xt_item = debug_xt_list; debug_xt_item->next != NULL;
+ debug_xt_item = debug_xt_item->next) {
+ if (debug_xt_item->next->xt_docol == xt) {
+ tmp_xt_item = debug_xt_item->next;
+ debug_xt_item->next = debug_xt_item->next->next;
+ free(tmp_xt_item);
+ }
+ }
+
+ /* If the list is now empty, indicate debug mode change */
+ if (debug_xt_list->next == NULL) {
+ interruptforth |= FORTH_INTSTAT_DBG;
+ }
+}
+
+static void
+do_source_dbg(struct debug_xt *debug_xt_item)
+{
+ /* Forth source debugger implementation */
+ char k, done = 0;
+
+ /* Display current dstack */
+ display_dbg_dstack();
+ printf_console("\n");
+
+ fstrncpy(xtname, lfa2nfa(read_ucell(cell2pointer(PC)) - sizeof(cell)), MAXNFALEN);
+ printf_console("%p: %s ", cell2pointer(PC), xtname);
+
+ /* If in trace mode, we just carry on */
+ if (debug_xt_item->mode == DEBUG_MODE_TRACE) {
+ return;
+ }
+
+ /* Otherwise in step mode, prompt for a keypress */
+ k = getchar_console();
+
+ /* Only proceed if done is true */
+ while (!done) {
+ switch (k) {
+
+ case ' ':
+ case '\n':
+ /* Perform a single step */
+ done = 1;
+ break;
+
+ case 'u':
+ case 'U':
+ /* Up - unmark current word for debug, mark its caller for
+ * debugging and finish executing current word */
+
+ /* Since this word could alter the rstack during its execution,
+ * we only know the caller when (semis) is called for this xt.
+ * Hence we mark the xt as a special DEBUG_MODE_STEPUP which
+ * means we run as normal, but schedule the xt for deletion
+ * at its corresponding (semis) word when we know the rstack
+ * will be set to its final parent value */
+ debug_xt_item->mode = DEBUG_MODE_STEPUP;
+ done = 1;
+ break;
+
+ case 'd':
+ case 'D':
+ /* Down - mark current word for debug and step into it */
+ done = add_debug_xt(read_ucell(cell2pointer(PC)));
+ if (!done) {
+ k = getchar_console();
+ }
+ break;
+
+ case 't':
+ case 'T':
+ /* Trace mode */
+ debug_xt_item->mode = DEBUG_MODE_TRACE;
+ done = 1;
+ break;
+
+ case 'r':
+ case 'R':
+ /* Display rstack */
+ display_dbg_rstack();
+ done = 0;
+ k = getchar_console();
+ break;
+
+ case 'f':
+ case 'F':
+ /* Start subordinate Forth interpreter */
+ PUSHR(PC - sizeof(cell));
+ PC = findword("outer-interpreter") + sizeof(ucell);
+
+ /* Save rstack position for when we return */
+ dbgrstackcnt = rstackcnt;
+ done = 1;
+ break;
+
+ default:
+ /* Display debug banner */
+ printf_console(DEBUG_BANNER);
+ k = getchar_console();
+ }
+ }
+}
+
+static void docol_dbg(void)
+{ /* DOCOL */
+ struct debug_xt *debug_xt_item;
+
+ PUSHR(PC);
+ PC = read_ucell(cell2pointer(PC));
+
+ /* If current xt is in our debug xt list, display word name */
+ debug_xt_item = debug_xt_list;
+ while (debug_xt_item->next) {
+ if (debug_xt_item->xt_docol == PC) {
+ fstrncpy(xtname, lfa2nfa(PC - sizeof(cell)), MAXNFALEN);
+ printf_console("\n: %s ", xtname);
+
+ /* Step mode is the default */
+ debug_xt_item->mode = DEBUG_MODE_STEP;
+ }
+
+ debug_xt_item = debug_xt_item->next;
+ }
+
+ dbg_interp_printk("docol_dbg: %s\n", cell2pointer(lfa2nfa(PC - sizeof(cell))));
+}
+
+static void semis_dbg(void)
+{
+ struct debug_xt *debug_xt_item, *debug_xt_up = NULL;
+
+ /* If current semis is in our debug xt list, disable debug mode */
+ debug_xt_item = debug_xt_list;
+ while (debug_xt_item->next) {
+ if (debug_xt_item->xt_semis == PC) {
+ if (debug_xt_item->mode != DEBUG_MODE_STEPUP) {
+ /* Handle the normal case */
+ fstrncpy(xtname, lfa2nfa(debug_xt_item->xt_docol - sizeof(cell)), MAXNFALEN);
+ printf_console("\n[ Finished %s ] ", xtname);
+
+ /* Reset to step mode in case we were in trace mode */
+ debug_xt_item->mode = DEBUG_MODE_STEP;
+ } else {
+ /* This word requires execution of the debugger "Up"
+ * semantics. However we can't do this here since we
+ * are iterating through the debug list, and we need
+ * to change it. So we do it afterwards.
+ */
+ debug_xt_up = debug_xt_item;
+ }
+ }
+
+ debug_xt_item = debug_xt_item->next;
+ }
+
+ /* Execute debugger "Up" semantics if required */
+ if (debug_xt_up) {
+ /* Only add the parent word if it is not within the trampoline */
+ if (rstack[rstackcnt] != (cell)pointer2cell(&trampoline[1])) {
+ del_debug_xt(debug_xt_up->xt_docol);
+ add_debug_xt(findxtfromcell(rstack[rstackcnt]));
+
+ fstrncpy(xtname, lfa2nfa(findxtfromcell(rstack[rstackcnt]) - sizeof(cell)), MAXNFALEN);
+ printf_console("\n[ Up to %s ] ", xtname);
+ } else {
+ fstrncpy(xtname, lfa2nfa(findxtfromcell(debug_xt_up->xt_docol) - sizeof(cell)), MAXNFALEN);
+ printf_console("\n[ Finished %s (Unable to go up, hit trampoline) ] ", xtname);
+
+ del_debug_xt(debug_xt_up->xt_docol);
+ }
+
+ debug_xt_up = NULL;
+ }
+
+ PC = POPR();
+}
+
+static inline void next_dbg(void)
+{
+ struct debug_xt *debug_xt_item;
+ void (*tokenp) (void);
+
+ PC += sizeof(ucell);
+
+ /* If the PC lies within a debug range, run the source debugger */
+ debug_xt_item = debug_xt_list;
+ while (debug_xt_item->next) {
+ if (PC >= debug_xt_item->xt_docol && PC <= debug_xt_item->xt_semis &&
+ debug_xt_item->mode != DEBUG_MODE_STEPUP) {
+ do_source_dbg(debug_xt_item);
+ }
+
+ debug_xt_item = debug_xt_item->next;
+ }
+
+ dbg_interp_printk("next_dbg: PC is now %x\n", PC);
+
+ /* Intercept DOCOL and SEMIS and redirect to debug versions */
+ if (read_ucell(cell2pointer(read_ucell(cell2pointer(PC)))) == DOCOL) {
+ tokenp = docol_dbg;
+ tokenp();
+ } else if (read_ucell(cell2pointer(read_ucell(cell2pointer(PC)))) == DOSEMIS) {
+ tokenp = semis_dbg;
+ tokenp();
+ } else {
+ /* Otherwise process as normal */
+ processxt(read_ucell(cell2pointer(read_ucell(cell2pointer(PC)))));
+ }
+}
+
+static void
+do_debug_xt(void)
+{
+ ucell xt = POP();
+
+ /* Add to the debug list */
+ if (add_debug_xt(xt)) {
+ /* Display debug banner */
+ printf_console(DEBUG_BANNER);
+
+ /* Indicate change to debug mode */
+ interruptforth |= FORTH_INTSTAT_DBG;
+ }
+}
+
+static void
+do_debug_off(void)
+{
+ /* Empty the debug xt linked list */
+ while (debug_xt_list->next != NULL) {
+ del_debug_xt(debug_xt_list->xt_docol);
+ }
+}
+
+/*
+ * Forth primitives needed to set up
+ * all the words described in IEEE1275-1994.
+ */
+
+/*
+ * dup ( x -- x x )
+ */
+
+static void fdup(void)
+{
+ const cell tmp = GETTOS();
+ PUSH(tmp);
+}
+
+
+/*
+ * 2dup ( x1 x2 -- x1 x2 x1 x2 )
+ */
+
+static void twodup(void)
+{
+ cell tmp = GETITEM(1);
+ PUSH(tmp);
+ tmp = GETITEM(1);
+ PUSH(tmp);
+}
+
+
+/*
+ * ?dup ( x -- 0 | x x )
+ */
+
+static void isdup(void)
+{
+ const cell tmp = GETTOS();
+ if (tmp)
+ PUSH(tmp);
+}
+
+
+/*
+ * over ( x y -- x y x )
+ */
+
+static void over(void)
+{
+ const cell tmp = GETITEM(1);
+ PUSH(tmp);
+}
+
+
+/*
+ * 2over ( x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2 )
+ */
+
+static void twoover(void)
+{
+ const cell tmp = GETITEM(3);
+ const cell tmp2 = GETITEM(2);
+ PUSH(tmp);
+ PUSH(tmp2);
+}
+
+/*
+ * pick ( xu ... x1 x0 u -- xu ... x1 x0 xu )
+ */
+
+static void pick(void)
+{
+ const cell u = POP();
+ if (dstackcnt >= u) {
+ ucell tmp = dstack[dstackcnt - u];
+ PUSH(tmp);
+ } else {
+ /* underrun */
+ }
+}
+
+
+/*
+ * drop ( x -- )
+ */
+
+static void drop(void)
+{
+ POP();
+}
+
+/*
+ * 2drop ( x1 x2 -- )
+ */
+
+static void twodrop(void)
+{
+ POP();
+ POP();
+}
+
+
+/*
+ * nip ( x1 x2 -- x2 )
+ */
+
+static void nip(void)
+{
+ const cell tmp = POP();
+ POP();
+ PUSH(tmp);
+}
+
+
+/*
+ * roll ( xu ... x1 x0 u -- xu-1... x1 x0 xu )
+ */
+
+static void roll(void)
+{
+ const cell u = POP();
+ if (dstackcnt >= u) {
+ int i;
+ const cell xu = dstack[dstackcnt - u];
+ for (i = dstackcnt - u; i < dstackcnt; i++) {
+ dstack[i] = dstack[i + 1];
+ }
+ dstack[dstackcnt] = xu;
+ } else {
+ /* Stack underrun */
+ }
+}
+
+
+/*
+ * rot ( x1 x2 x3 -- x2 x3 x1 )
+ */
+
+static void rot(void)
+{
+ const cell tmp = POP();
+ const cell tmp2 = POP();
+ const cell tmp3 = POP();
+ PUSH(tmp2);
+ PUSH(tmp);
+ PUSH(tmp3);
+}
+
+
+/*
+ * -rot ( x1 x2 x3 -- x3 x1 x2 )
+ */
+
+static void minusrot(void)
+{
+ const cell tmp = POP();
+ const cell tmp2 = POP();
+ const cell tmp3 = POP();
+ PUSH(tmp);
+ PUSH(tmp3);
+ PUSH(tmp2);
+}
+
+
+/*
+ * swap ( x1 x2 -- x2 x1 )
+ */
+
+static void swap(void)
+{
+ const cell tmp = POP();
+ const cell tmp2 = POP();
+ PUSH(tmp);
+ PUSH(tmp2);
+}
+
+
+/*
+ * 2swap ( x1 x2 x3 x4 -- x3 x4 x1 x2 )
+ */
+
+static void twoswap(void)
+{
+ const cell tmp = POP();
+ const cell tmp2 = POP();
+ const cell tmp3 = POP();
+ const cell tmp4 = POP();
+ PUSH(tmp2);
+ PUSH(tmp);
+ PUSH(tmp4);
+ PUSH(tmp3);
+}
+
+
+/*
+ * >r ( x -- ) (R: -- x )
+ */
+
+static void tor(void)
+{
+ ucell tmp = POP();
+#ifdef CONFIG_DEBUG_RSTACK
+ printk(" >R: %x\n", tmp);
+#endif
+ PUSHR(tmp);
+}
+
+
+/*
+ * r> ( -- x ) (R: x -- )
+ */
+
+static void rto(void)
+{
+ ucell tmp = POPR();
+#ifdef CONFIG_DEBUG_RSTACK
+ printk(" R>: %x\n", tmp);
+#endif
+ PUSH(tmp);
+}
+
+
+/*
+ * r@ ( -- x ) (R: x -- x )
+ */
+
+static void rfetch(void)
+{
+ PUSH(GETTORS());
+}
+
+
+/*
+ * depth ( -- u )
+ */
+
+static void depth(void)
+{
+ const cell tmp = dstackcnt;
+ PUSH(tmp);
+}
+
+
+/*
+ * depth! ( ... u -- x1 x2 .. xu )
+ */
+
+static void depthwrite(void)
+{
+ ucell tmp = POP();
+ dstackcnt = tmp;
+}
+
+
+/*
+ * rdepth ( -- u )
+ */
+
+static void rdepth(void)
+{
+ const cell tmp = rstackcnt;
+ PUSH(tmp);
+}
+
+
+/*
+ * rdepth! ( u -- ) ( R: ... -- x1 x2 .. xu )
+ */
+
+static void rdepthwrite(void)
+{
+ ucell tmp = POP();
+ rstackcnt = tmp;
+}
+
+
+/*
+ * + ( nu1 nu2 -- sum )
+ */
+
+static void plus(void)
+{
+ cell tmp = POP() + POP();
+ PUSH(tmp);
+}
+
+
+/*
+ * - ( nu1 nu2 -- diff )
+ */
+
+static void minus(void)
+{
+ const cell nu2 = POP();
+ const cell nu1 = POP();
+ PUSH(nu1 - nu2);
+}
+
+
+/*
+ * * ( nu1 nu2 -- prod )
+ */
+
+static void mult(void)
+{
+ const cell nu2 = POP();
+ const cell nu1 = POP();
+ PUSH(nu1 * nu2);
+}
+
+
+/*
+ * u* ( u1 u2 -- prod )
+ */
+
+static void umult(void)
+{
+ const ucell tmp = (ucell) POP() * (ucell) POP();
+ PUSH(tmp);
+}
+
+
+/*
+ * mu/mod ( n1 n2 -- rem quot.l quot.h )
+ */
+
+static void mudivmod(void)
+{
+ const ucell b = POP();
+ const ducell a = DPOP();
+#ifdef NEED_FAKE_INT128_T
+ if (a.hi != 0) {
+ fprintf(stderr, "mudivmod called (0x%016llx %016llx / 0x%016llx)\n",
+ a.hi, a.lo, b);
+ exit(-1);
+ } else {
+ ducell c;
+
+ PUSH(a.lo % b);
+ c.hi = 0;
+ c.lo = a.lo / b;
+ DPUSH(c);
+ }
+#else
+ PUSH(a % b);
+ DPUSH(a / b);
+#endif
+}
+
+
+/*
+ * abs ( n -- u )
+ */
+
+static void forthabs(void)
+{
+ const cell tmp = GETTOS();
+ if (tmp < 0) {
+ POP();
+ PUSH(-tmp);
+ }
+}
+
+
+/*
+ * negate ( n1 -- n2 )
+ */
+
+static void negate(void)
+{
+ const cell tmp = POP();
+ PUSH(-tmp);
+}
+
+
+/*
+ * max ( n1 n2 -- n1|n2 )
+ */
+
+static void max(void)
+{
+ const cell tmp = POP();
+ const cell tmp2 = POP();
+ PUSH((tmp > tmp2) ? tmp : tmp2);
+}
+
+
+/*
+ * min ( n1 n2 -- n1|n2 )
+ */
+
+static void min(void)
+{
+ const cell tmp = POP();
+ const cell tmp2 = POP();
+ PUSH((tmp < tmp2) ? tmp : tmp2);
+}
+
+
+/*
+ * lshift ( x1 u -- x2 )
+ */
+
+static void lshift(void)
+{
+ const ucell u = POP();
+ const ucell x1 = POP();
+ PUSH(x1 << u);
+}
+
+
+/*
+ * rshift ( x1 u -- x2 )
+ */
+
+static void rshift(void)
+{
+ const ucell u = POP();
+ const ucell x1 = POP();
+ PUSH(x1 >> u);
+}
+
+
+/*
+ * >>a ( x1 u -- x2 ) ??
+ */
+
+static void rshifta(void)
+{
+ const cell u = POP();
+ const cell x1 = POP();
+ PUSH(x1 >> u);
+}
+
+
+/*
+ * and ( x1 x2 -- x3 )
+ */
+
+static void and(void)
+{
+ const cell x1 = POP();
+ const cell x2 = POP();
+ PUSH(x1 & x2);
+}
+
+
+/*
+ * or ( x1 x2 -- x3 )
+ */
+
+static void or(void)
+{
+ const cell x1 = POP();
+ const cell x2 = POP();
+ PUSH(x1 | x2);
+}
+
+
+/*
+ * xor ( x1 x2 -- x3 )
+ */
+
+static void xor(void)
+{
+ const cell x1 = POP();
+ const cell x2 = POP();
+ PUSH(x1 ^ x2);
+}
+
+
+/*
+ * invert ( x1 -- x2 )
+ */
+
+static void invert(void)
+{
+ const cell x1 = POP();
+ PUSH(x1 ^ -1);
+}
+
+
+/*
+ * d+ ( d1 d2 -- d.sum )
+ */
+
+static void dplus(void)
+{
+ const dcell d2 = DPOP();
+ const dcell d1 = DPOP();
+#ifdef NEED_FAKE_INT128_T
+ ducell c;
+
+ if (d1.hi != 0 || d2.hi != 0) {
+ fprintf(stderr, "dplus called (0x%016llx %016llx + 0x%016llx %016llx)\n",
+ d1.hi, d1.lo, d2.hi, d2.lo);
+ exit(-1);
+ }
+ c.hi = 0;
+ c.lo = d1.lo + d2.lo;
+ DPUSH(c);
+#else
+ DPUSH(d1 + d2);
+#endif
+}
+
+
+/*
+ * d- ( d1 d2 -- d.diff )
+ */
+
+static void dminus(void)
+{
+ const dcell d2 = DPOP();
+ const dcell d1 = DPOP();
+#ifdef NEED_FAKE_INT128_T
+ ducell c;
+
+ if (d1.hi != 0 || d2.hi != 0) {
+ fprintf(stderr, "dminus called (0x%016llx %016llx + 0x%016llx %016llx)\n",
+ d1.hi, d1.lo, d2.hi, d2.lo);
+ exit(-1);
+ }
+ c.hi = 0;
+ c.lo = d1.lo - d2.lo;
+ DPUSH(c);
+#else
+ DPUSH(d1 - d2);
+#endif
+}
+
+
+/*
+ * m* ( ?? -- )
+ */
+
+static void mmult(void)
+{
+ const cell u2 = POP();
+ const cell u1 = POP();
+#ifdef NEED_FAKE_INT128_T
+ ducell c;
+
+ if (0) { // XXX How to detect overflow?
+ fprintf(stderr, "mmult called (%016llx * 0x%016llx)\n", u1, u2);
+ exit(-1);
+ }
+ c.hi = 0;
+ c.lo = u1 * u2;
+ DPUSH(c);
+#else
+ DPUSH((dcell) u1 * u2);
+#endif
+}
+
+
+/*
+ * um* ( u1 u2 -- d.prod )
+ */
+
+static void ummult(void)
+{
+ const ucell u2 = POP();
+ const ucell u1 = POP();
+#ifdef NEED_FAKE_INT128_T
+ ducell c;
+
+ if (0) { // XXX How to detect overflow?
+ fprintf(stderr, "ummult called (%016llx * 0x%016llx)\n", u1, u2);
+ exit(-1);
+ }
+ c.hi = 0;
+ c.lo = u1 * u2;
+ DPUSH(c);
+#else
+ DPUSH((ducell) u1 * u2);
+#endif
+}
+
+
+/*
+ * @ ( a-addr -- x )
+ */
+
+static void fetch(void)
+{
+ const ucell *aaddr = (ucell *)cell2pointer(POP());
+ PUSH(read_ucell(aaddr));
+}
+
+
+/*
+ * c@ ( addr -- byte )
+ */
+
+static void cfetch(void)
+{
+ const u8 *aaddr = (u8 *)cell2pointer(POP());
+ PUSH(read_byte(aaddr));
+}
+
+
+/*
+ * w@ ( waddr -- w )
+ */
+
+static void wfetch(void)
+{
+ const u16 *aaddr = (u16 *)cell2pointer(POP());
+ PUSH(read_word(aaddr));
+}
+
+
+/*
+ * l@ ( qaddr -- quad )
+ */
+
+static void lfetch(void)
+{
+ const u32 *aaddr = (u32 *)cell2pointer(POP());
+ PUSH(read_long(aaddr));
+}
+
+
+/*
+ * ! ( x a-addr -- )
+ */
+
+static void store(void)
+{
+ const ucell *aaddr = (ucell *)cell2pointer(POP());
+ const ucell x = POP();
+#ifdef CONFIG_DEBUG_INTERNAL
+ printk("!: %lx : %lx -> %lx\n", aaddr, read_ucell(aaddr), x);
+#endif
+ write_ucell(aaddr,x);
+}
+
+
+/*
+ * +! ( nu a-addr -- )
+ */
+
+static void plusstore(void)
+{
+ const ucell *aaddr = (ucell *)cell2pointer(POP());
+ const cell nu = POP();
+ write_cell(aaddr,read_cell(aaddr)+nu);
+}
+
+
+/*
+ * c! ( byte addr -- )
+ */
+
+static void cstore(void)
+{
+ const u8 *aaddr = (u8 *)cell2pointer(POP());
+ const ucell byte = POP();
+#ifdef CONFIG_DEBUG_INTERNAL
+ printk("c!: %x = %x\n", aaddr, byte);
+#endif
+ write_byte(aaddr, byte);
+}
+
+
+/*
+ * w! ( w waddr -- )
+ */
+
+static void wstore(void)
+{
+ const u16 *aaddr = (u16 *)cell2pointer(POP());
+ const u16 word = POP();
+ write_word(aaddr, word);
+}
+
+
+/*
+ * l! ( quad qaddr -- )
+ */
+
+static void lstore(void)
+{
+ const u32 *aaddr = (u32 *)cell2pointer(POP());
+ const u32 longval = POP();
+ write_long(aaddr, longval);
+}
+
+
+/*
+ * = ( x1 x2 -- equal? )
+ */
+
+static void equals(void)
+{
+ cell tmp = (POP() == POP());
+ PUSH(-tmp);
+}
+
+
+/*
+ * > ( n1 n2 -- greater? )
+ */
+
+static void greater(void)
+{
+ cell tmp = ((cell) POP() < (cell) POP());
+ PUSH(-tmp);
+}
+
+
+/*
+ * < ( n1 n2 -- less? )
+ */
+
+static void less(void)
+{
+ cell tmp = ((cell) POP() > (cell) POP());
+ PUSH(-tmp);
+}
+
+
+/*
+ * u> ( u1 u2 -- unsigned-greater? )
+ */
+
+static void ugreater(void)
+{
+ cell tmp = ((ucell) POP() < (ucell) POP());
+ PUSH(-tmp);
+}
+
+
+/*
+ * u< ( u1 u2 -- unsigned-less? )
+ */
+
+static void uless(void)
+{
+ cell tmp = ((ucell) POP() > (ucell) POP());
+ PUSH(-tmp);
+}
+
+
+/*
+ * sp@ ( -- stack-pointer )
+ */
+
+static void spfetch(void)
+{
+ // FIXME this can only work if the stack pointer
+ // is within range.
+ ucell tmp = pointer2cell(&(dstack[dstackcnt]));
+ PUSH(tmp);
+}
+
+
+/*
+ * move ( src-addr dest-addr len -- )
+ */
+
+static void fmove(void)
+{
+ ucell count = POP();
+ void *dest = (void *)cell2pointer(POP());
+ const void *src = (const void *)cell2pointer(POP());
+ memmove(dest, src, count);
+}
+
+
+/*
+ * fill ( addr len byte -- )
+ */
+
+static void ffill(void)
+{
+ ucell value = POP();
+ ucell count = POP();
+ void *src = (void *)cell2pointer(POP());
+ memset(src, value, count);
+}
+
+
+/*
+ * unaligned-w@ ( addr -- w )
+ */
+
+static void unalignedwordread(void)
+{
+ const unsigned char *addr = (const unsigned char *) cell2pointer(POP());
+ PUSH(unaligned_read_word(addr));
+}
+
+
+/*
+ * unaligned-w! ( w addr -- )
+ */
+
+static void unalignedwordwrite(void)
+{
+ const unsigned char *addr = (const unsigned char *) cell2pointer(POP());
+ u16 w = POP();
+ unaligned_write_word(addr, w);
+}
+
+
+/*
+ * unaligned-l@ ( addr -- quad )
+ */
+
+static void unalignedlongread(void)
+{
+ const unsigned char *addr = (const unsigned char *) cell2pointer(POP());
+ PUSH(unaligned_read_long(addr));
+}
+
+
+/*
+ * unaligned-l! ( quad addr -- )
+ */
+
+static void unalignedlongwrite(void)
+{
+ unsigned char *addr = (unsigned char *) cell2pointer(POP());
+ u32 l = POP();
+ unaligned_write_long(addr, l);
+}
+
+/*
+ * here ( -- dictionary-pointer )
+ */
+
+static void here(void)
+{
+ PUSH(pointer2cell(dict) + dicthead);
+#ifdef CONFIG_DEBUG_INTERNAL
+ printk("here: %x\n", pointer2cell(dict) + dicthead);
+#endif
+}
+
+/*
+ * here! ( new-dict-pointer -- )
+ */
+
+static void herewrite(void)
+{
+ ucell tmp = POP(); /* converted pointer */
+ dicthead = tmp - pointer2cell(dict);
+#ifdef CONFIG_DEBUG_INTERNAL
+ printk("here!: new value: %x\n", tmp);
+#endif
+
+ if (dictlimit && dicthead >= dictlimit) {
+ printk("Dictionary space overflow:"
+ " dicthead=" FMT_ucellx
+ " dictlimit=" FMT_ucellx
+ "\n",
+ dicthead, dictlimit);
+ }
+}
+
+
+/*
+ * emit ( char -- )
+ */
+
+static void emit(void)
+{
+ cell tmp = POP();
+#ifndef FCOMPILER
+ putchar(tmp);
+#else
+ put_outputbyte(tmp);
+#endif
+}
+
+
+/*
+ * key? ( -- pressed? )
+ */
+
+static void iskey(void)
+{
+ PUSH((cell) availchar());
+}
+
+
+/*
+ * key ( -- char )
+ */
+
+static void key(void)
+{
+ while (!availchar());
+#ifdef FCOMPILER
+ PUSH(get_inputbyte());
+#else
+ PUSH(getchar());
+#endif
+}
+
+
+/*
+ * ioc@ ( reg -- val )
+ */
+
+static void iocfetch(void)
+{
+#ifndef FCOMPILER
+ cell reg = POP();
+ PUSH(inb(reg));
+#else
+ (void)POP();
+ PUSH(0);
+#endif
+}
+
+
+/*
+ * iow@ ( reg -- val )
+ */
+
+static void iowfetch(void)
+{
+#ifndef FCOMPILER
+ cell reg = POP();
+ PUSH(inw(reg));
+#else
+ (void)POP();
+ PUSH(0);
+#endif
+}
+
+/*
+ * iol@ ( reg -- val )
+ */
+
+static void iolfetch(void)
+{
+#ifndef FCOMPILER
+ cell reg = POP();
+ PUSH(inl(reg));
+#else
+ (void)POP();
+ PUSH(0);
+#endif
+}
+
+
+/*
+ * ioc! ( val reg -- )
+ */
+
+static void iocstore(void)
+{
+#ifndef FCOMPILER
+ cell reg = POP();
+ cell val = POP();
+
+ outb(val, reg);
+#else
+ (void)POP();
+ (void)POP();
+#endif
+}
+
+
+/*
+ * iow! ( val reg -- )
+ */
+
+static void iowstore(void)
+{
+#ifndef FCOMPILER
+ cell reg = POP();
+ cell val = POP();
+
+ outw(val, reg);
+#else
+ (void)POP();
+ (void)POP();
+#endif
+}
+
+
+/*
+ * iol! ( val reg -- )
+ */
+
+static void iolstore(void)
+{
+#ifndef FCOMPILER
+ ucell reg = POP();
+ ucell val = POP();
+
+ outl(val, reg);
+#else
+ (void)POP();
+ (void)POP();
+#endif
+}
+
+/*
+ * i ( -- i )
+ */
+
+static void loop_i(void)
+{
+ PUSH(rstack[rstackcnt]);
+}
+
+/*
+ * j ( -- i )
+ */
+
+static void loop_j(void)
+{
+ PUSH(rstack[rstackcnt - 2]);
+}
+
+/* words[] is a function array of all native code functions used by
+ * the dictionary, i.e. CFAs and primitives.
+ * Any change here needs a matching change in the primitive word's
+ * name list that is kept for bootstrapping in kernel/bootstrap.c
+ *
+ * NOTE: THIS LIST SHALL NOT CHANGE (EXCEPT MANDATORY ADDITIONS AT
+ * THE END). ANY OTHER CHANGE WILL BREAK COMPATIBILITY TO OLDER
+ * BINARY DICTIONARIES.
+ */
+static forth_word * const words[] = {
+ /*
+ * CFAs and special words
+ */
+ semis,
+ docol,
+ lit,
+ docon,
+ dovar,
+ dodefer,
+ dodoes,
+ dodo,
+ doisdo,
+ doloop,
+ doplusloop,
+ doival,
+ doivar,
+ doidefer,
+
+ /*
+ * primitives
+ */
+ fdup, /* dup */
+ twodup, /* 2dup */
+ isdup, /* ?dup */
+ over, /* over */
+ twoover, /* 2over */
+ pick, /* pick */
+ drop, /* drop */
+ twodrop, /* 2drop */
+ nip, /* nip */
+ roll, /* roll */
+ rot, /* rot */
+ minusrot, /* -rot */
+ swap, /* swap */
+ twoswap, /* 2swap */
+ tor, /* >r */
+ rto, /* r> */
+ rfetch, /* r@ */
+ depth, /* depth */
+ depthwrite, /* depth! */
+ rdepth, /* rdepth */
+ rdepthwrite, /* rdepth! */
+ plus, /* + */
+ minus, /* - */
+ mult, /* * */
+ umult, /* u* */
+ mudivmod, /* mu/mod */
+ forthabs, /* abs */
+ negate, /* negate */
+ max, /* max */
+ min, /* min */
+ lshift, /* lshift */
+ rshift, /* rshift */
+ rshifta, /* >>a */
+ and, /* and */
+ or, /* or */
+ xor, /* xor */
+ invert, /* invert */
+ dplus, /* d+ */
+ dminus, /* d- */
+ mmult, /* m* */
+ ummult, /* um* */
+ fetch, /* @ */
+ cfetch, /* c@ */
+ wfetch, /* w@ */
+ lfetch, /* l@ */
+ store, /* ! */
+ plusstore, /* +! */
+ cstore, /* c! */
+ wstore, /* w! */
+ lstore, /* l! */
+ equals, /* = */
+ greater, /* > */
+ less, /* < */
+ ugreater, /* u> */
+ uless, /* u< */
+ spfetch, /* sp@ */
+ fmove, /* move */
+ ffill, /* fill */
+ emit, /* emit */
+ iskey, /* key? */
+ key, /* key */
+ execute, /* execute */
+ here, /* here */
+ herewrite, /* here! */
+ dobranch, /* dobranch */
+ docbranch, /* do?branch */
+ unalignedwordread, /* unaligned-w@ */
+ unalignedwordwrite, /* unaligned-w! */
+ unalignedlongread, /* unaligned-l@ */
+ unalignedlongwrite, /* unaligned-l! */
+ iocfetch, /* ioc@ */
+ iowfetch, /* iow@ */
+ iolfetch, /* iol@ */
+ iocstore, /* ioc! */
+ iowstore, /* iow! */
+ iolstore, /* iol! */
+ loop_i, /* i */
+ loop_j, /* j */
+ call, /* call */
+ sysdebug, /* sys-debug */
+ do_include, /* $include */
+ do_encode_file, /* $encode-file */
+ do_debug_xt, /* (debug */
+ do_debug_off, /* (debug-off) */
+};