/******************************************************************************* * A minimal Forth compiler in C * By Leif Bruder * Release 2014-04-04 * Modified by Devine Lu Linvega for Plan9 ARM * Patch 2020-10-06 * * Based on Richard W.M. Jones' excellent Jonesforth sources/tutorial * * PUBLIC DOMAIN * * I, the copyright holder of this work, hereby release it into the public * domain. This applies worldwide. In case this is not legally possible, I grant * any entity the right to use this work for any purpose, without any conditions, * unless such conditions are required by law. *******************************************************************************/ /* Only a single include here; I'll define everything on the fly to keep * dependencies as low as possible. In this file, the only C standard functions * used are getchar, putchar and the EOF value. */ #include /* Base cell data types. Use short/long on most systems for 16 bit cells. */ /* Experiment here if necessary. */ #define CELL_BASE_TYPE short #define DOUBLE_CELL_BASE_TYPE long /* Basic memory configuration */ #define MEM_SIZE 65536 /* main memory size in bytes */ #define STACK_SIZE 192 /* cells reserved for the stack */ #define RSTACK_SIZE 64 /* cells reserved for the return stack */ #define INPUT_LINE_SIZE 32 /* bytes reserved for the WORD buffer */ /******************************************************************************/ /* Our basic data types */ typedef CELL_BASE_TYPE scell; typedef DOUBLE_CELL_BASE_TYPE dscell; typedef unsigned CELL_BASE_TYPE cell; typedef unsigned DOUBLE_CELL_BASE_TYPE dcell; typedef unsigned char byte; #define CELL_SIZE sizeof(cell) #define DCELL_SIZE sizeof(dcell) /* A few constants that describe the memory layout of this implementation */ #define LATEST_POSITION INPUT_LINE_SIZE #define HERE_POSITION (LATEST_POSITION + CELL_SIZE) #define BASE_POSITION (HERE_POSITION + CELL_SIZE) #define STATE_POSITION (BASE_POSITION + CELL_SIZE) #define STACK_POSITION (STATE_POSITION + CELL_SIZE) #define RSTACK_POSITION (STACK_POSITION + STACK_SIZE * CELL_SIZE) #define HERE_START (RSTACK_POSITION + RSTACK_SIZE * CELL_SIZE) #define MAX_BUILTIN_ID 71 /* Flags and masks for the dictionary */ #define FLAG_IMMEDIATE 0x80 #define FLAG_HIDDEN 0x40 #define MASK_NAMELENGTH 0x1F /* This is the main memory to be used by this Forth. There will be no malloc * in this file. */ byte memory[MEM_SIZE]; /* Pointers to Forth variables stored inside the main memory array */ cell* latest; cell* here; cell* base; cell* state; cell* sp; cell* stack; cell* rsp; cell* rstack; /* A few helper variables for the compiler */ int exitReq; int errorFlag; cell next; cell lastIp; cell quit_address; cell commandAddress; cell maxBuiltinAddress; /* The TIB, stored outside the main memory array for now */ char lineBuffer[128]; int charsInLineBuffer = 0; int positionInLineBuffer = 0; /* A basic setup for defining builtins. This Forth uses impossibly low * adresses as IDs for the builtins so we can define builtins as * standard C functions. Slower but easier to port. */ #define BUILTIN(id, name, c_name, flags) \ const int c_name##_id = id; \ const char* c_name##_name = name; \ const byte c_name##_flags = flags; \ void c_name(void) #define ADD_BUILTIN(c_name) addBuiltin(c_name##_id, c_name##_name, c_name##_flags, c_name) typedef void (*builtin)(void); builtin builtins[MAX_BUILTIN_ID] = {0}; /* This is our initialization script containing all the words we define in * Forth for convenience. Focus is on simplicity, not speed. Partly copied from * Jonesforth (see top of file). */ char* initscript_pos; const char* initScript = ": DECIMAL 10 BASE ! ;\n" ": HEX 16 BASE ! ;\n" ": OCTAL 8 BASE ! ;\n" ": 2DUP OVER OVER ;\n" ": 2DROP DROP DROP ;\n" ": NIP SWAP DROP ;\n" ": 2NIP 2SWAP 2DROP ;\n" ": TUCK SWAP OVER ;\n" ": / /MOD NIP ;\n" ": MOD /MOD DROP ;\n" ": BL 32 ;\n" ": CR 10 EMIT ;\n" ": SPACE BL EMIT ;\n" ": NEGATE 0 SWAP - ;\n" ": DNEGATE 0. 2SWAP D- ;\n" ": CELLS CELL * ;\n" ": ALLOT HERE @ + HERE ! ;\n" ": TRUE -1 ;\n" ": FALSE 0 ;\n" ": 0= 0 = ;\n" ": 0< 0 < ;\n" ": 0> 0 > ;\n" ": <> = 0= ;\n" ": <= > 0= ;\n" ": >= < 0= ;\n" ": 0<= 0 <= ;\n" ": 0>= 0 >= ;\n" ": 1+ 1 + ;\n" ": 1- 1 - ;\n" ": 2+ 2 + ;\n" ": 2- 2 - ;\n" ": 2/ 2 / ;\n" ": 2* 2 * ;\n" ": D2/ 2. D/ ;\n" ": +! DUP @ ROT + SWAP ! ;\n" ": [COMPILE] WORD FIND >CFA , ; IMMEDIATE\n" ": [CHAR] key ' LIT , , ; IMMEDIATE\n" ": RECURSE LATEST @ >CFA , ; IMMEDIATE\n" ": DOCOL 0 ;\n" ": CONSTANT CREATE DOCOL , ' LIT , , ' EXIT , ;\n" ": 2CONSTANT SWAP CREATE DOCOL , ' LIT , , ' LIT , , ' EXIT , ;\n" ": VARIABLE HERE @ CELL ALLOT CREATE DOCOL , ' LIT , , ' EXIT , ;\n" /* TODO: Allot AFTER the code, not before */ ": 2VARIABLE HERE @ 2 CELLS ALLOT CREATE DOCOL , ' LIT , , ' EXIT , ;\n" /* TODO: Allot AFTER the code, not before */ ": IF ' 0BRANCH , HERE @ 0 , ; IMMEDIATE\n" ": THEN DUP HERE @ SWAP - SWAP ! ; IMMEDIATE\n" ": ELSE ' BRANCH , HERE @ 0 , SWAP DUP HERE @ SWAP - SWAP ! ; IMMEDIATE\n" ": BEGIN HERE @ ; IMMEDIATE\n" ": UNTIL ' 0BRANCH , HERE @ - , ; IMMEDIATE\n" ": AGAIN ' BRANCH , HERE @ - , ; IMMEDIATE\n" ": WHILE ' 0BRANCH , HERE @ 0 , ; IMMEDIATE\n" ": REPEAT ' BRANCH , SWAP HERE @ - , DUP HERE @ SWAP - SWAP ! ; IMMEDIATE\n" ": UNLESS ' 0= , [COMPILE] IF ; IMMEDIATE\n" ": DO HERE @ ' SWAP , ' >R , ' >R , ; IMMEDIATE\n" ": LOOP ' R> , ' R> , ' SWAP , ' 1+ , ' 2DUP , ' = , ' 0BRANCH , HERE @ - , ' 2DROP , ; IMMEDIATE\n" ": +LOOP ' R> , ' R> , ' SWAP , ' ROT , ' + , ' 2DUP , ' <= , ' 0BRANCH , HERE @ - , ' 2DROP , ; IMMEDIATE\n" ": I ' R@ , ; IMMEDIATE\n" ": SPACES DUP 0> IF 0 DO SPACE LOOP ELSE DROP THEN ;\n" ": ABS DUP 0< IF NEGATE THEN ;\n" ": DABS 2DUP 0. D< IF DNEGATE THEN ;\n" ": .DIGIT DUP 9 > IF 55 ELSE 48 THEN + EMIT ;\n" ": .SIGN DUP 0< IF 45 EMIT NEGATE THEN ;\n" /* BUG: 10000000000... will be shown wrong */ ": .POS BASE @ /MOD ?DUP IF RECURSE THEN .DIGIT ;\n" ": . .SIGN DUP IF .POS ELSE .DIGIT THEN ;\n" ": COUNTPOS SWAP 1 + SWAP BASE @ / ?DUP IF RECURSE THEN ;\n" ": DIGITS DUP 0< IF 1 ELSE 0 THEN SWAP COUNTPOS ;\n" ": .R OVER DIGITS - SPACES . ;\n" ": . . SPACE ;\n" ": ? @ . ;\n" ": .S DSP@ BEGIN DUP S0@ > WHILE DUP ? CELL - REPEAT DROP ;\n" ": TYPE 0 DO DUP C@ EMIT 1 + LOOP DROP ;\n" ": ALIGN BEGIN HERE @ CELL MOD WHILE 0 C, REPEAT ;\n" ": s\" ' LITSTRING , HERE @ 0 , BEGIN KEY DUP 34 <> WHILE C, REPEAT DROP DUP HERE @ SWAP - CELL - SWAP ! ALIGN ; IMMEDIATE\n" ": .\" [COMPILE] s\" ' TYPE , ; IMMEDIATE\n" ": ( BEGIN KEY [CHAR] ) = UNTIL ; IMMEDIATE\n" ": COUNT DUP 1+ SWAP C@ ;\n" ": MIN 2DUP < IF DROP ELSE NIP THEN ;\n" ": MAX 2DUP > IF DROP ELSE NIP THEN ;\n" ": D0= OR 0= ;\n" ": DMIN 2OVER 2OVER D< IF 2DROP ELSE 2NIP THEN ;\n" ": DMAX 2OVER 2OVER D> IF 2DROP ELSE 2NIP THEN ;\n"; /******************************************************************************/ /* The primary data output function. This is the place to change if you want * to e.g. output data on a microcontroller via a serial interface. */ void putkey(char c) { putchar(c); } /* The primary data input function. This is where you place the code to e.g. * read from a serial line. */ int llkey(void) { if(*initscript_pos) return *(initscript_pos++); return getchar(); } /* Anything waiting in the keyboard buffer? */ int keyWaiting(void) { return positionInLineBuffer < charsInLineBuffer ? -1 : 0; } /* Line buffered character input. We're duplicating the functionality of the * stdio library here to make the code easier to port to other input sources */ int getkey(void) { int c; if(keyWaiting()) return lineBuffer[positionInLineBuffer++]; charsInLineBuffer = 0; while((c = llkey()) != EOF) { if(charsInLineBuffer == sizeof(lineBuffer)) break; lineBuffer[charsInLineBuffer++] = c; if(c == '\n') break; } positionInLineBuffer = 1; return lineBuffer[0]; } /* C string output */ void tell(const char* str) { while(*str) putkey(*str++); } /* The basic (data) stack operations */ cell pop(void) { if(*sp == 1) { tell("? Stack underflow\n"); errorFlag = 1; return 0; } return stack[--(*sp)]; } cell tos(void) { if(*sp == 1) { tell("? Stack underflow\n"); errorFlag = 1; return 0; } return stack[(*sp) - 1]; } void push(cell data) { if(*sp >= STACK_SIZE) { tell("? Stack overflow\n"); errorFlag = 1; return; } stack[(*sp)++] = data; } dcell dpop(void) { cell tmp[2]; tmp[1] = pop(); tmp[0] = pop(); return *((dcell*)tmp); } void dpush(dcell data) { cell tmp[2]; *((dcell*)tmp) = data; push(tmp[0]); push(tmp[1]); } /* The basic return stack operations */ cell rpop(void) { if(*rsp == 1) { tell("? RStack underflow\n"); errorFlag = 1; return 0; } return rstack[--(*rsp)]; } void rpush(cell data) { if(*rsp >= RSTACK_SIZE) { tell("? RStack overflow\n"); errorFlag = 1; return; } rstack[(*rsp)++] = data; } /* Secure memory access */ cell readMem(cell address) { if(address > MEM_SIZE) { tell("Internal error in readMem: Invalid addres\n"); errorFlag = 1; return 0; } return *((cell*)(memory + address)); } void writeMem(cell address, cell value) { if(address > MEM_SIZE) { tell("Internal error in writeMem: Invalid address\n"); errorFlag = 1; return; } *((cell*)(memory + address)) = value; } /* Reading a word into the input line buffer */ byte readWord(void) { char* line = (char*)memory; byte len = 0; int c; while((c = getkey()) != EOF) { if(c == ' ') continue; if(c == '\n') continue; if(c != '\\') break; while((c = getkey()) != EOF) if(c == '\n') break; } while(c != ' ' && c != '\n' && c != EOF) { if(len >= (INPUT_LINE_SIZE - 1)) break; line[++len] = c; c = getkey(); } line[0] = len; return len; } /* toupper() clone so we don't have to pull in ctype.h */ char up(char c) { return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; } /* Dictionary lookup */ cell findWord(cell address, cell len) { cell ret = *latest; char* name = (char*)&memory[address]; cell i; int found; for(ret = *latest; ret; ret = readMem(ret)) { if((memory[ret + CELL_SIZE] & MASK_NAMELENGTH) != len) continue; if(memory[ret + CELL_SIZE] & FLAG_HIDDEN) continue; found = 1; for(i = 0; i < len; i++) { if(up(memory[ret + i + 1 + CELL_SIZE]) != up(name[i])) { found = 0; break; } } if(found) break; } return ret; } /* Basic number parsing, base <= 36 only atm */ void parseNumber(byte* word, cell len, dcell* number, cell* notRead, byte* isDouble) { int negative = 0; cell i; char c; cell current; *number = 0; *isDouble = 0; if(len == 0) { *notRead = 0; return; } if(word[0] == '-') { negative = 1; len--; word++; } else if(word[0] == '+') { len--; word++; } for(i = 0; i < len; i++) { c = *word; word++; if(c == '.') { *isDouble = 1; continue; } else if(c >= '0' && c <= '9') current = c - '0'; else if(c >= 'A' && c <= 'Z') current = 10 + c - 'A'; else if(c >= 'a' && c <= 'z') current = 10 + c - 'a'; else break; if(current >= *base) break; *number = *number * *base + current; } *notRead = len - i; if(negative) *number = (-((scell)*number)); } /******************************************************************************* * * Builtin definitions * *******************************************************************************/ BUILTIN(0, "RUNDOCOL", docol, 0) { rpush(lastIp); next = commandAddress + CELL_SIZE; } /* The first few builtins are very simple, not need to waste vertical space here */ BUILTIN(1, "CELL", doCellSize, 0) { push(CELL_SIZE); } BUILTIN(2, "@", memRead, 0) { push(readMem(pop())); } BUILTIN(3, "C@", memReadByte, 0) { push(memory[pop()]); } BUILTIN(4, "KEY", key, 0) { push(getkey()); } BUILTIN(5, "EMIT", emit, 0) { putkey(pop() & 255); } BUILTIN(6, "DROP", drop, 0) { pop(); } BUILTIN(7, "EXIT", doExit, 0) { next = rpop(); } BUILTIN(8, "BYE", bye, 0) { exitReq = 1; } BUILTIN(9, "LATEST", doLatest, 0) { push(LATEST_POSITION); } BUILTIN(10, "HERE", doHere, 0) { push(HERE_POSITION); } BUILTIN(11, "BASE", doBase, 0) { push(BASE_POSITION); } BUILTIN(12, "STATE", doState, 0) { push(STATE_POSITION); } BUILTIN(13, "[", gotoInterpreter, FLAG_IMMEDIATE) { *state = 0; } BUILTIN(14, "]", gotoCompiler, 0) { *state = 1; } BUILTIN(15, "HIDE", hide, 0) { memory[*latest + CELL_SIZE] ^= FLAG_HIDDEN; } BUILTIN(16, "R>", rtos, 0) { push(rpop()); } BUILTIN(17, ">R", stor, 0) { rpush(pop()); } BUILTIN(18, "KEY?", key_p, 0) { push(keyWaiting()); } BUILTIN(19, "BRANCH", branch, 0) { next += readMem(next); } BUILTIN(20, "0BRANCH", zbranch, 0) { next += pop() ? CELL_SIZE : readMem(next); } BUILTIN(21, "IMMEDIATE", toggleImmediate, FLAG_IMMEDIATE) { memory[*latest + CELL_SIZE] ^= FLAG_IMMEDIATE; } BUILTIN(22, "FREE", doFree, 0) { push(MEM_SIZE - *here); } BUILTIN(23, "S0@", s0_r, 0) { push(STACK_POSITION + CELL_SIZE); } BUILTIN(24, "DSP@", dsp_r, 0) { push(STACK_POSITION + *sp * CELL_SIZE); } BUILTIN(25, "NOT", not, 0) { push(~pop()); } BUILTIN(26, "DUP", dup, 0) { push(tos()); } BUILTIN(27, "!", memWrite, 0) { cell address = pop(); cell value = pop(); writeMem(address, value); } BUILTIN(28, "C!", memWriteByte, 0) { cell address = pop(); cell value = pop(); memory[address] = value & 255; } BUILTIN(29, "SWAP", swap, 0) { cell a = pop(); cell b = pop(); push(a); push(b); } BUILTIN(30, "OVER", over, 0) { cell a = pop(); cell b = tos(); push(a); push(b); } BUILTIN(31, ",", comma, 0) { push(*here); memWrite(); *here += CELL_SIZE; } BUILTIN(32, "C,", commaByte, 0) { push(*here); memWriteByte(); *here += sizeof(byte); } BUILTIN(33, "WORD", word, 0) { byte len = readWord(); push(1); push(len); } BUILTIN(34, "FIND", find, 0) { cell len = pop(); cell address = pop(); cell ret = findWord(address, len); push(ret); } cell getCfa(cell address) { byte len = (memory[address + CELL_SIZE] & MASK_NAMELENGTH) + 1; while((len & (CELL_SIZE - 1)) != 0) len++; return address + CELL_SIZE + len; } BUILTIN(35, ">CFA", cfa, 0) { cell address = pop(); cell ret = getCfa(address); if(ret < maxBuiltinAddress) push(readMem(ret)); else push(ret); } BUILTIN(36, "NUMBER", number, 0) { dcell num; cell notRead; byte isDouble; cell len = pop(); byte* address = &memory[pop()]; parseNumber(address, len, &num, ¬Read, &isDouble); if(isDouble) dpush(num); else push((cell)num); push(notRead); } BUILTIN(37, "LIT", lit, 0) { push(readMem(next)); next += CELL_SIZE; } /* Outer and inner interpreter, TODO split up */ BUILTIN(38, "QUIT", quit, 0) { cell address; dcell number; cell notRead; cell command; int i; byte isDouble; cell tmp[2]; int immediate; for(exitReq = 0; exitReq == 0;) { lastIp = next = quit_address; errorFlag = 0; word(); find(); address = pop(); if(address) { immediate = (memory[address + CELL_SIZE] & FLAG_IMMEDIATE); commandAddress = getCfa(address); command = readMem(commandAddress); if(*state && !immediate) { if(command < MAX_BUILTIN_ID && command != docol_id) push(command); else push(commandAddress); comma(); } else { while(!errorFlag && !exitReq) { if(command == quit_id) break; else if(command < MAX_BUILTIN_ID) builtins[command](); else { lastIp = next; next = command; } commandAddress = next; command = readMem(commandAddress); next += CELL_SIZE; } } } else { parseNumber(&memory[1], memory[0], &number, ¬Read, &isDouble); if(notRead) { tell("Unknown word: "); for(i = 0; i < memory[0]; i++) putkey(memory[i + 1]); putkey('\n'); *sp = *rsp = 1; continue; } else { if(*state) { *((dcell*)tmp) = number; push(lit_id); comma(); if(isDouble) { push(tmp[0]); comma(); push(lit_id); comma(); push(tmp[1]); comma(); } else { push((cell)number); comma(); } } else { if(isDouble) dpush(number); else push((cell)number); } } } if(errorFlag) *sp = *rsp = 1; else if(!keyWaiting() && !(*initscript_pos)) tell(" OK\n"); } } BUILTIN(39, "+", plus, 0) { scell n1 = pop(); scell n2 = pop(); push(n1 + n2); } BUILTIN(40, "-", minus, 0) { scell n1 = pop(); scell n2 = pop(); push(n2 - n1); } BUILTIN(41, "*", mul, 0) { scell n1 = pop(); scell n2 = pop(); push(n1 * n2); } BUILTIN(42, "/MOD", divmod, 0) { scell n1 = pop(); scell n2 = pop(); push(n2 % n1); push(n2 / n1); } BUILTIN(43, "ROT", rot, 0) { cell a = pop(); cell b = pop(); cell c = pop(); push(b); push(a); push(c); } void createWord(const char* name, byte len, byte flags); BUILTIN(44, "CREATE", doCreate, 0) { byte len; cell address; word(); len = pop() & 255; address = pop(); createWord((char*)&memory[address], len, 0); } BUILTIN(45, ":", colon, 0) { doCreate(); push(docol_id); comma(); hide(); *state = 1; } BUILTIN(46, ";", semicolon, FLAG_IMMEDIATE) { push(doExit_id); comma(); hide(); *state = 0; } BUILTIN(47, "R@", rget, 0) { cell tmp = rpop(); rpush(tmp); push(tmp); } BUILTIN(48, "J", doJ, 0) { cell tmp1 = rpop(); cell tmp2 = rpop(); cell tmp3 = rpop(); rpush(tmp3); rpush(tmp2); rpush(tmp1); push(tmp3); } BUILTIN(49, "'", tick, FLAG_IMMEDIATE) { word(); find(); cfa(); if(*state) { push(lit_id); comma(); comma(); } } BUILTIN(50, "=", equals, 0) { cell a1 = pop(); cell a2 = pop(); push(a2 == a1 ? -1 : 0); } BUILTIN(51, "<", smaller, 0) { scell a1 = pop(); scell a2 = pop(); push(a2 < a1 ? -1 : 0); } BUILTIN(52, ">", larger, 0) { scell a1 = pop(); scell a2 = pop(); push(a2 > a1 ? -1 : 0); } BUILTIN(53, "AND", doAnd, 0) { cell a1 = pop(); cell a2 = pop(); push(a2 & a1); } BUILTIN(54, "OR", doOr, 0) { cell a1 = pop(); cell a2 = pop(); push(a2 | a1); } BUILTIN(55, "?DUP", p_dup, 0) { cell a = tos(); if(a) push(a); } BUILTIN(56, "LITSTRING", litstring, 0) { cell length = readMem(next); next += CELL_SIZE; push(next); push(length); next += length; while(next & (CELL_SIZE - 1)) next++; } BUILTIN(57, "XOR", xor, 0) { cell a = pop(); cell b = pop(); push(a ^ b); } BUILTIN(58, "*/", timesDivide, 0) { cell n3 = pop(); dcell n2 = pop(); dcell n1 = pop(); dcell r = (n1 * n2) / n3; push((cell)r); if((cell)r != r) { tell("Arithmetic overflow\n"); errorFlag = 1; } } BUILTIN(59, "*/MOD", timesDivideMod, 0) { cell n3 = pop(); dcell n2 = pop(); dcell n1 = pop(); dcell r = (n1 * n2) / n3; dcell m = (n1 * n2) % n3; push((cell)m); push((cell)r); if((cell)r != r) { tell("Arithmetic overflow\n"); errorFlag = 1; } } BUILTIN(60, "D=", dequals, 0) { dcell a1 = dpop(); dcell a2 = dpop(); push(a2 == a1 ? -1 : 0); } BUILTIN(61, "D<", dsmaller, 0) { dscell a1 = dpop(); dscell a2 = dpop(); push(a2 < a1 ? -1 : 0); } BUILTIN(62, "D>", dlarger, 0) { dscell a1 = dpop(); dscell a2 = dpop(); push(a2 > a1 ? -1 : 0); } BUILTIN(63, "DU<", dusmaller, 0) { dcell a1 = dpop(); dcell a2 = dpop(); push(a2 < a1 ? -1 : 0); } BUILTIN(64, "D+", dplus, 0) { dscell n1 = dpop(); dscell n2 = dpop(); dpush(n1 + n2); } BUILTIN(65, "D-", dminus, 0) { dscell n1 = dpop(); dscell n2 = dpop(); dpush(n2 - n1); } BUILTIN(66, "D*", dmul, 0) { dscell n1 = dpop(); dscell n2 = dpop(); dpush(n1 * n2); } BUILTIN(67, "D/", ddiv, 0) { dscell n1 = dpop(); dscell n2 = dpop(); dpush(n2 / n1); } BUILTIN(68, "2SWAP", dswap, 0) { dcell a = dpop(); dcell b = dpop(); dpush(a); dpush(b); } BUILTIN(69, "2OVER", dover, 0) { dcell a = dpop(); dcell b = dpop(); dpush(b); dpush(a); dpush(b); } BUILTIN(70, "2ROT", drot, 0) { dcell a = dpop(); dcell b = dpop(); dcell c = dpop(); dpush(b); dpush(a); dpush(c); } /******************************************************************************* * * Loose ends * *******************************************************************************/ /* Create a word in the dictionary */ void createWord(const char* name, byte len, byte flags) { cell newLatest = *here; push(*latest); comma(); push(len | flags); commaByte(); while(len--) { push(*name); commaByte(); name++; } while(*here & (CELL_SIZE - 1)) { push(0); commaByte(); } *latest = newLatest; } /* A simple strlen clone so we don't have to pull in string.h */ byte slen(const char* str) { byte ret = 0; while(*str++) ret++; return ret; } /* Add a builtin to the dictionary */ void addBuiltin(cell code, const char* name, const byte flags, builtin f) { if(errorFlag) return; if(code >= MAX_BUILTIN_ID) { tell("Error adding builtin "); tell(name); tell(": Out of builtin IDs\n"); errorFlag = 1; return; } if(builtins[code] != 0) { tell("Error adding builtin "); tell(name); tell(": ID given twice\n"); errorFlag = 1; return; } builtins[code] = f; createWord(name, slen(name), flags); push(code); comma(); push(doExit_id); comma(); } /* Program setup and jump to outer interpreter */ int main() { errorFlag = 0; if(DCELL_SIZE != 2 * CELL_SIZE) { tell("Configuration error: DCELL_SIZE != 2*CELL_SIZE\n"); return 1; } state = (cell*)&memory[STATE_POSITION]; base = (cell*)&memory[BASE_POSITION]; latest = (cell*)&memory[LATEST_POSITION]; here = (cell*)&memory[HERE_POSITION]; sp = (cell*)&memory[STACK_POSITION]; stack = (cell*)&memory[STACK_POSITION + CELL_SIZE]; rsp = (cell*)&memory[RSTACK_POSITION]; rstack = (cell*)&memory[RSTACK_POSITION + CELL_SIZE]; *sp = *rsp = 1; *state = 0; *base = 10; *latest = 0; *here = HERE_START; ADD_BUILTIN(docol); ADD_BUILTIN(doCellSize); ADD_BUILTIN(memRead); ADD_BUILTIN(memWrite); ADD_BUILTIN(memReadByte); ADD_BUILTIN(memWriteByte); ADD_BUILTIN(key); ADD_BUILTIN(emit); ADD_BUILTIN(swap); ADD_BUILTIN(dup); ADD_BUILTIN(drop); ADD_BUILTIN(over); ADD_BUILTIN(comma); ADD_BUILTIN(commaByte); ADD_BUILTIN(word); ADD_BUILTIN(find); ADD_BUILTIN(cfa); ADD_BUILTIN(doExit); ADD_BUILTIN(quit); quit_address = getCfa(*latest); ADD_BUILTIN(number); ADD_BUILTIN(bye); ADD_BUILTIN(doLatest); ADD_BUILTIN(doHere); ADD_BUILTIN(doBase); ADD_BUILTIN(doState); ADD_BUILTIN(plus); ADD_BUILTIN(minus); ADD_BUILTIN(mul); ADD_BUILTIN(divmod); ADD_BUILTIN(rot); ADD_BUILTIN(gotoInterpreter); ADD_BUILTIN(gotoCompiler); ADD_BUILTIN(doCreate); ADD_BUILTIN(hide); ADD_BUILTIN(lit); ADD_BUILTIN(colon); ADD_BUILTIN(semicolon); ADD_BUILTIN(rtos); ADD_BUILTIN(stor); ADD_BUILTIN(rget); ADD_BUILTIN(doJ); ADD_BUILTIN(tick); ADD_BUILTIN(key_p); ADD_BUILTIN(equals); ADD_BUILTIN(smaller); ADD_BUILTIN(larger); ADD_BUILTIN(doAnd); ADD_BUILTIN(doOr); ADD_BUILTIN(branch); ADD_BUILTIN(zbranch); ADD_BUILTIN(toggleImmediate); ADD_BUILTIN(doFree); ADD_BUILTIN(p_dup); ADD_BUILTIN(s0_r); ADD_BUILTIN(dsp_r); ADD_BUILTIN(litstring); ADD_BUILTIN(not ); ADD_BUILTIN(xor); ADD_BUILTIN(timesDivide); ADD_BUILTIN(timesDivideMod); ADD_BUILTIN(dequals); ADD_BUILTIN(dsmaller); ADD_BUILTIN(dlarger); ADD_BUILTIN(dusmaller); ADD_BUILTIN(dplus); ADD_BUILTIN(dminus); ADD_BUILTIN(dmul); ADD_BUILTIN(ddiv); ADD_BUILTIN(dswap); ADD_BUILTIN(dover); ADD_BUILTIN(drot); maxBuiltinAddress = (*here) - 1; if(errorFlag) return 1; initscript_pos = (char*)initScript; quit(); return 0; }