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:
M | README.md | | | 3 | ++- |
M | equi.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(' ');
}