equi

A self-descriptive stack-based PC platform
git clone git://git.luxferre.top/equi.git
Log | Files | Refs | README | LICENSE

commit 0be72ee3fd670b8e8f935fe998167414e26d10f9
parent 31166c8e7f9eeccf19ed1ee412efe94b42f3de72
Author: Luxferre <lux@ferre>
Date:   Tue,  9 Aug 2022 20:59:08 +0300

Hopefully fixed literals and implemented most instructions

Diffstat:
MREADME.md | 3++-
Mequi.c | 203+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
2 files changed, 144 insertions(+), 62 deletions(-)

diff --git a/README.md b/README.md @@ -99,6 +99,7 @@ CR |`( -- )` |In the command mode, output a line break an `*`|`( a b -- a*b )` |Product `/`|`( a b -- a/b rem )` |Integer division (with remainder) `N`|`( a -- -a )` |Single-instruction negation (complement to 65536) +`T`|`( a XY -- [a >> X] << Y )` |Bitwise shif**t**: by the first nibble to the right and then by the second nibble to the left `~`|`( a -- ~a )` |Bitwise NOT `&`|`( a b -- a&b )` |Bitwise AND `\|`|`( a b -- a\|b )` |Bitwise OR @@ -106,7 +107,7 @@ CR |`( -- )` |In the command mode, output a line break an `.`|`( a -- ) ` |Output a character by the ASCII (or Unicode, if supported) value into the standard terminal `,`|`( -- a ) ` |Non-blocking key input of an ASCII (or Unicode, if supported) value from the standard terminal `?`|`( -- a ) ` |Blocking key input of an ASCII (or Unicode, if supported) value from the standard terminal -`P`|`( p1 p2 port -- status r1 r2 )`|**P**ort I/O: pass two 16-bit parameters to the port and read the operation status and results into the words on the stack top +`P`|`( p1 p2 port -- r1 r2 status )`|**P**ort I/O: pass two 16-bit parameters to the port and read the operation status and results into the words on the stack top `}`|`( adh adl len maddr -- status)`|Persistent storage write operation. Stack parameters: persistent address high 2 bytes, low 2 bytes, data length, RAM address `{`|`( adh adl len maddr -- status)`|Persistent storage read operation. Stack parameters: persistent address high 2 bytes, low 2 bytes, data length, RAM address `Q`|`( -- )` |**Q**uit the interpretation mode (unset IM flag if set), or the interpreter shell itself if in command mode (halt the machine when it's nowhere to exit to) diff --git a/equi.c b/equi.c @@ -44,6 +44,7 @@ #define CLT_ENTRY_SIZE (CLT_ENTRY_LEN + WS) /* Full size in bytes taken by one CLT entry */ #define BS 8u /* Backspace character code */ #define CR 13u /* Character return code */ +#define LF 10u /* Line feed code */ /* Configuration section (constants overridable at compile-time) */ @@ -131,28 +132,28 @@ void trapout(errcode) { fprintf(stderr, "Error %d at 0x%x (instruction %c): ", errcode, ram.pc, ram.cmdbuf[ram.pc]); switch(errcode) { case STACK_OVERFLOW: - cerr("Stack overflow"); + cerr("Stack overflow\n"); break; case STACK_UNDERFLOW: - cerr("Stack underflow"); + cerr("Stack underflow\n"); break; case DIV_BY_ZERO: - cerr("Division by zero"); + cerr("Division by zero\n"); break; case CLT_OVERFLOW: - cerr("Compilation lookup table full"); + cerr("Compilation lookup table full\n"); break; case CMD_OVERFLOW: - cerr("Command buffer full"); + cerr("Command buffer full\n"); break; case INVALID_INSTRUCTION: - cerr("Invalid instruction"); + cerr("Invalid instruction\n"); break; case INVALID_WORD: - cerr("Word not found in CLT"); + cerr("Word not found in CLT\n"); break; case PORT_IO_ERROR: - cerr("Port I/O error"); + cerr("Port I/O error\n"); break; } exit(errcode); @@ -191,11 +192,13 @@ enum EquiInstructions { INS_MUL='*', INS_DIV='/', INS_NEG='N', + INS_SHIFT='T', INS_NOT='~', INS_AND='&', INS_OR='|', INS_XOR='^', - INS_COUT='.', + INS_COUT='.', /* character output */ + INS_NHOUT='H', /* numeric hex output */ INS_NBKIN=',', /* non-blocking key input */ INS_BKIN='?', /* blocking key input */ INS_PORTIO='P', @@ -238,11 +241,11 @@ ushort popRet() { return ram.return_stack[--ram.rsp]; } -/* push a character to the literal stack, auto-discarding overflown items */ +/* push a character to the literal stack */ void pushLit(uchar c) { ram.literal_stack[ram.lsp++] = c; if(ram.lsp >= LIT_STACK_SIZE) - ram.lsp = 0; + trapout(STACK_OVERFLOW); } /* pop a character from the literal stack */ @@ -255,9 +258,9 @@ uchar popLit() { return ram.literal_stack[--ram.lsp]; } -/* clear the literal stack */ -void clearLit() { - ram.lsp = 0; /* clear the literal stack */ +/* convert ASCII code of a hex digit to the digit value */ +uchar a2d(uchar a) { + return (a < 0x3aU) ? (a - 0x30U) : (a - 55U); } /* shape 2-byte vlaue on the main stack from the 4 values of the literal stack */ @@ -271,10 +274,10 @@ void pushLitVal(strict) { } } else { - p4 = popLit(); - p3 = popLit(); - p2 = popLit(); - p1 = popLit(); + p4 = a2d(popLit()); + p3 = a2d(popLit()); + p2 = a2d(popLit()); + p1 = a2d(popLit()); pushMain((p1<<12U) | (p2<<8U) | (p3<<4U) | p4); ram.lsp = 0; /* clear the literal stack */ } @@ -293,17 +296,25 @@ ushort crc16(const uchar* data_p, uchar length) { return crc; } +/* persistent operation handler */ + +ushort persistOp(maddr, dataLen, adl, adh, isWrite) { + +} + /* Main interpreter loop */ void equi_main_loop() { uchar instr, i; - ushort lhash, pbuf; + ushort lhash, pbuf, pbuf2; /* reset all stacks before running and reinit CLT */ ram.msp = ram.rsp = ram.lsp = ram.cltp = 0; /* reset pc */ ram.pc = 65535U; while(1) { /* iterate over the instructions in the command buffer */ instr = ram.cmdbuf[++ram.pc]; + /* silently exit on zero */ + if(instr == 0) break; /* first, check for II mode */ if(ram.II) { if(instr == INS_IIEND) @@ -344,12 +355,19 @@ void equi_main_loop() { ram.cbp = ram.pc + 1U; /* save CBP */ ram.CM = 1U; /* raise CM flag */ break; + case CR: + case LF: + case ' ': /* all nops in interpretation mode */ + break; case INS_QUIT: /* gracefully quit the interpretation mode */ goto brx; case INS_LITINT: /* literal stack -> main stack as short */ pushLitVal(1U); break; - case INS_LITSTR: + case INS_LITSTR: /* literal stack -> each char at main stack as short */ + while(ram.lsp) + pushMain((ushort)popLit()); + ram.lsp = 0; break; case INS_LITCALL: /* call the saved word from the literal */ if(ram.lsp < CLT_ENTRY_LEN) @@ -364,80 +382,149 @@ void equi_main_loop() { trapout(INVALID_WORD); ram.lsp = 0; /* clear the literal stack */ break; - case INS_RET: + case INS_RET: /* jump to the instruction at ret stack */ ram.pc = popRet(); break; - case INS_M2R: + case INS_M2R: /* main -> ret stack */ + pushRet(popMain()); break; - case INS_R2M: + case INS_R2M: /* ret -> main stack */ + pushMain(popRet()); break; - case INS_LOAD: + case INS_LOAD: /* mem -> main stack */ + pbuf = popMain(); + pushMain((flatram[pbuf] << 8)|flatram[pbuf+1U]); break; - case INS_STORE: + case INS_STORE: /* main stack -> mem (word) */ + pbuf = popMain(); + pbuf2 = popMain(); + flatram[pbuf] = pbuf2 >> 8U; + flatram[pbuf + 1U] = pbuf2 & 255U; break; - case INS_STOREBYTE: + case INS_STOREBYTE: /* main stack -> mem (byte) */ + pbuf = popMain(); + flatram[pbuf] = popMain() & 255U; break; - case INS_DROP: + case INS_DROP: /* ( a -- ) */ + pbuf = popMain(); break; - case INS_DUP: - break; - case INS_SWAP: + case INS_DUP: /* ( a -- a a ) */ + if(ram.msp < 1) + trapout(STACK_UNDERFLOW); + pushMain(ram.main_stack[ram.msp-1]); break; - case INS_ROT: + case INS_SWAP: /* ( a b -- b a ) */ + if(ram.msp < 2) + trapout(STACK_UNDERFLOW); + pbuf = ram.main_stack[ram.msp-2]; + ram.main_stack[ram.msp-2] = ram.main_stack[ram.msp-1]; + ram.main_stack[ram.msp-1] = pbuf; break; - case INS_OVER: + case INS_ROT: /* ( a b c -- b c a ) */ + if(ram.msp < 3) + trapout(STACK_UNDERFLOW); + pbuf = ram.main_stack[ram.msp-3]; + pbuf2 = ram.main_stack[ram.msp-1]; + ram.main_stack[ram.msp-3] = ram.main_stack[ram.msp-2]; + ram.main_stack[ram.msp-2] = pbuf2; + ram.main_stack[ram.msp-1] = pbuf; + break; + case INS_OVER: /* ( a b -- a b a ) */ + if(ram.msp < 2) + trapout(STACK_UNDERFLOW); + pushMain(ram.main_stack[ram.msp-2]); break; - case INS_JUMP: + case INS_JUMP: /* unconditional signed jump: ( rel -- ) */ + ram.pc = (ushort) (ram.pc + (signed short) popMain()); break; - case INS_IF: + case INS_IF: /* conditional signed jump if cond is not zero: ( cond rel -- ) */ + pbuf = popMain(); /* reladdr */ + pbuf2 = popMain(); /* cond */ + if(pbuf2 != 0) + ram.pc = (ushort) (ram.pc + (signed short) pbuf); break; case INS_EXPOINT: /* Locate execution point */ pushMain(ram.pc + 1); break; - case INS_GT: + case INS_GT: /* ( a b -- a>b ) */ + pushMain((popMain() < popMain()) ? 1u : 0u); + break; + case INS_LT: /* ( a b -- a<b ) */ + pushMain((popMain() > popMain()) ? 1u : 0u); break; - case INS_LT: + case INS_EQ: /* ( a b -- a==b ) */ + pushMain((popMain() == popMain()) ? 1u : 0u); break; - case INS_EQ: + case INS_ADD: /* ( a b -- a+b ) */ + pushMain((popMain() + popMain())&65535U); break; - case INS_ADD: + case INS_SUB: /* ( a b -- a-b ) */ + pushMain((-popMain() + popMain())&65535U); break; - case INS_SUB: + case INS_MUL: /* ( a b -- a*b ) */ + pushMain((ushort)(popMain() * popMain())&65535U); break; - case INS_MUL: + case INS_DIV: /* ( a b -- a/b rem ) */ + pbuf2 = popMain(); + pbuf = popMain(); + pushMain((ushort) pbuf/pbuf2); + pushMain((ushort) pbuf%pbuf2); break; - case INS_DIV: + case INS_NEG: /* ( a -- -a ) */ + pushMain((ushort)-popMain()); break; - case INS_NEG: + case INS_SHIFT: /* ( a XY -- [a >> X] << Y ) */ + pbuf = popMain(); + pushMain((ushort)(popMain() >> (pbuf&15U)) << (pbuf>>4U)); break; - case INS_NOT: + case INS_NOT: /* ( a -- ~a ) */ + pushMain((ushort)(~popMain()&65535U)); break; - case INS_AND: + case INS_AND: /* ( a b -- a&b ) */ + pushMain(popMain() & popMain()); break; - case INS_OR: + case INS_OR: /* ( a b -- a|b ) */ + pushMain(popMain() | popMain()); break; - case INS_XOR: + case INS_XOR: /* ( a b -- a^b ) */ + pushMain(popMain() ^ popMain()); break; - case INS_COUT: + case INS_COUT: /* output a character, no Unicode support for now */ + cputc((uchar)(popMain()&255U)); break; - case INS_NBKIN: - case INS_BKIN: + case INS_NHOUT: /* output the hex number from the stack top */ + printf("%04X", popMain()); break; - case INS_PORTIO: + case INS_NBKIN: /* non-blocking key input - in this implementation it's equal to: */ + case INS_BKIN: /* blocking key input (again, no Unicode support yet, assuming a single byte incoming to the 16-bit value on the stack) */ + pushMain((ushort)cgetc()); break; - case INS_PERSIST_READ: + case INS_PORTIO: /* ( p1 p2 port -- r1 r2 status ) - simulate port I/O according to the spec */ + pbuf = popMain(); /* port */ + pbuf2 = popMain(); /* p2 */ + fprintf(stderr, "[PORTIO ] Call to port 0x%X with P1=%X and P2=%X, returning status=0, R1=0, R2=0\n", pbuf, pbuf2, popMain()); + pushMain(0u); + pushMain(0u); + pushMain(0u); break; - case INS_PERSIST_WRITE: + case INS_PERSIST_READ: /* ( adh adl len maddr -- status) */ + pushMain(persistOp(popMain(), popMain(), popMain(), popMain(), 0)); + break; + case INS_PERSIST_WRITE: /* ( adh adl len maddr -- status) */ + pushMain(persistOp(popMain(), popMain(), popMain(), popMain(), 1)); break; default: /* all characters not processed before are invalid instructions */ trapout(INVALID_INSTRUCTION); goto brx; } + if(ram.msp >= STACK_SIZE_WORDS || ram.rsp >= STACK_SIZE_WORDS) /* check for stack overflow after any operation */ + trapout(STACK_OVERFLOW); continue; brx: break; } /* unset interpretation mode flag and exit */ ram.IM = 0; + ram.pc = ram.ibp = 65535U; } /* Equi VM entry point */ @@ -484,17 +571,11 @@ int main(int argc, char* argv[]) { cputc(instr); /* echo it */ #endif ram.II = 0; - } else if(instr == CR) { /* process carriage return */ - cputc(CR); /* echo it */ -#ifdef __CC65__ - cputc(10); /* echo it */ -#endif + } else if(instr == LF) { /* process carriage return */ + cputc(LF); /* echo it */ ram.IM = 1; /* set the mandatory interpretation mode flag */ equi_main_loop(); /* and run the interpreter loop */ - cputc(CR); /* echo it */ -#ifdef __CC65__ - cputc(10); /* echo it */ -#endif + cputc(LF); /* echo it */ cputc('>'); cputc(' '); }