I know we have hijacked the thread, but I'll let it die after this comment.
I tried a register based byte-code, but it was slower because one of the most expensive operations in the interpreter is *reading* the byte-code, so minimizing the byte-code size make the code faster. In my interpreter, this is optimized by using a routine located in page zero, so the pointer and the load instructions are the same, this is the main interpreter loop (from https://github.com/d...interpreter.asm):
Didn't knew G-Pascal, on a simple look it also uses a stack-based byte-code, but as a joined stack with a 16bit stack-pointer, making stack operations slow.
I did a quick look to the opcodes, and I think that all 16bit math code would be a lot smaller, and the interpreter could keep the top-of-stack always in A/B registers, but I don't know if it's possible to implement the jump-table without using A/B (like in the 6502).
G-Pascal as a direct implementation of a Niklaus Wirth P-Machine but with editor and compiler coded in assembly instead of Pascal, and it is a subset for size and complexity purposes somewhat like Tiny Pascal.
16 bit index and data registers make a huge difference. If the 6803 had a 2nd index register like the 6809, 68hc11, etc... you could dedicate an index register to the bytecode program counter.
The 68hc12 has 3 index registers.
I came up with the following code off the top of my head, but it should be accurate.
I think these even work on the 6800... except register D and where I mention the 6809.
Maintaining a pointer to the top of the stack is a no brainter
You just push stuff onto the hardware stack, and for a stack relative addressing you grab a copy of the stack pointer into X and can index off of that
To pop the last call off the stack you can simply do this. The 6809 can directly modify the stack pointer with LEAS and are way better at this.
To avoid this sort of thing it's best to just pop parameters from the stack if possible which automatically adjusts the stack, and you don't need to modify X.
The 6809 has stack relative addressing and you just index off of the stack pointer. It's almost cheating.
You can even use the user stack pointer if you want to keep a separate stack pointer from the OS..
Incrementing the program counter is as simple as incrementing X or loading B with the number of bytes the opcode required and then adding them to the program counter in X.
;bottom of a bytecode function that requires 3 bytes for parameters. 4 = opcode + parameters
Note that all opcodes requiring a single byte jump to SingleBytePCInc. This should really be whatever is most common and B should be set accordingly.
For space purposes, there might be a TwoBytePCInc before this that BRAnches to MainLoop. Apple Pascal does this.
This is the slow version.
LDX ProgramCounter ;4
STX ProgramCounter ;4
LDAB ,X ;get bytecode 4
ROLB ;multipy by 2 2
LDX #BYTECODEPOINTERTABLE ;point to jump table 3
ABX ;add offset for bytecode 3
JMP 0,X ;call it 3
You don't have to maintain anything on the direct page other than keeping a copy of the program counter.
All the X shuffling is costly, but it would make other functions smaller and this avoids self modifying code.
The fast version uses self modifying code and assumes X contains the program counter on entry.
Any bytecode routine that modifies X would have to restore X to the program counter before exit .
The table must be on a 256 byte boundary,
ABX ; 3
BRA MainLoop ; 3
INX ;increment the program counter 3
LDAB ,X ;get next bytecode 4
ROLB ;multipy by 2 2
STAB Jump+2 ;update jump address 4
JMP BYTECODEPOINTERTABLE ; Jump 3
In spite of not having an instruction prefetch like the 6502, the 6803 is somewhat faster than the 6502 here, but some of that may be lost within a given bytecode routine to use X. Even then, it only adds 8 clock cycles and still less than the 6502. The code is also smaller than the 6502 code.