Explaining the code: executing the first word
After all the initializations, the program counter (PC
) is at 0x0008
and the registers are pointing1 to memory as follows:
PC ---> 0x08: 0x9613
0x09: 0x7003
0x0A: 0x2043
0x0B: 0x7003
0x0C: 0x2004
… …
A ---> 0x32: 0x0007
IP ---> 0x33: 0x0003
0x34: 0x002E
0x35: 0x0020
… …
DB[SP] ---> 0x39: 0xFFFF
0x3A: 0xFFFF
0x3B: 0xFFFF
Let’s add some mnemonics to each line. Note, the values from addresses 0x08
to 0x0C
represent machine instructions, the values from 0x32
to 0x35
represent Forth words, and the values from 0x39
to 0x3B
are there to show as of yet unused stack space.
PC ---> 0x08: 0x9613 PUSH DB[SP], *(IP)
0x09: 0x7003 INC IP
0x0A: 0x2043 LD A, *(IP)
0x0B: 0x7003 INC IP
0x0C: 0x2004 JMP *(A)
… …
A ---> 0x32: 0x0007 LIT
IP ---> 0x33: 0x0003 0x0003
0x34: 0x002E DUP
0x35: 0x0020 DOT
… …
DB[SP] ---> 0x39: 0xFFFF
0x3A: 0xFFFF
0x3B: 0xFFFF
Register A
points to a memory cell that contains the address of the active word while register IP
, the instruction pointer, points to the next instruction to be executed on Forth level. (In contrast to register PC
, the program counter, which points to the next machine instruction to be executed.)
The purpose of the LIT word is to put the content of the following memory cell onto the data stack. This is exactly what the first instruction, PUSH DB[SP], *(IP)
, does. Afterwords the situation looks like this:
0x08: 0x9613 PUSH DB[SP], *(IP)
PC ---> 0x09: 0x7003 INC IP
0x0A: 0x2043 LD A, *(IP)
0x0B: 0x7003 INC IP
0x0C: 0x2004 JMP *(A)
… …
A ---> 0x32: 0x0007 LIT
IP ---> 0x33: 0x0003 0x0003
0x34: 0x002E DUP
0x35: 0x0020 DOT
… …
0x39: 0x0003
DB[SP] ---> 0x3A: 0xFFFF
0x3B: 0xFFFF
Then INC IP
moves the instruction pointer to the next Forth word to be exectued, which will be DUP
.
0x08: 0x9613 PUSH DB[SP], *(IP)
0x09: 0x7003 INC IP
PC ---> 0x0A: 0x2043 LD A, *(IP)
0x0B: 0x7003 INC IP
0x0C: 0x2004 JMP *(A)
… …
A ---> 0x32: 0x0007 LIT
0x33: 0x0003 0x0003
IP ---> 0x34: 0x002E DUP
0x35: 0x0020 DOT
… …
0x39: 0x0003
DB[SP] ---> 0x3A: 0xFFFF
0x3B: 0xFFFF
The inner interpreter (next)
The next three machine instructions implement a core piece of any Forth, the inner interpreter often just called next.
The first instruction, LD A, *(IP)
, updates the active word.
0x08: 0x9613 PUSH DB[SP], *(IP)
0x09: 0x7003 INC IP
0x0A: 0x2043 LD A, *(IP)
PC ---> 0x0B: 0x7003 INC IP
0x0C: 0x2004 JMP *(A)
… …
A ---> 0x2E: 0x0028
0x2F: 0xB461
0x30: 0x8614
… …
0x32: 0x0007 LIT
0x33: 0x0003 0x0003
IP ---> 0x34: 0x002E DUP
0x35: 0x0020 DOT
… …
0x39: 0x0003
DB[SP] ---> 0x3A: 0xFFFF
0x3B: 0xFFFF
The next instruction, INC IP
, moves the instruction pointer one instruction ahead.
0x08: 0x9613 PUSH DB[SP], *(IP)
0x09: 0x7003 INC IP
0x0A: 0x2043 LD A, *(IP)
0x0B: 0x7003 INC IP
PC ---> 0x0C: 0x2004 JMP *(A)
… …
A ---> 0x2E: 0x0028
0x2F: 0xB461
0x30: 0x8614
… …
0x32: 0x0007 LIT
0x33: 0x0003 0x0003
0x34: 0x002E DUP
IP ---> 0x35: 0x0020 DOT
… …
0x39: 0x0003
DB[SP] ---> 0x3A: 0xFFFF
0x3B: 0xFFFF
Then JMP *(A)
starts exeucting the next word.
0x08: 0x9613 PUSH DB[SP], *(IP)
0x09: 0x7003 INC IP
0x0A: 0x2043 LD A, *(IP)
0x0B: 0x7003 INC IP
0x0C: 0x2004 JMP *(A)
… …
A ---> 0x2E: 0x0028
PC ---> 0x2F: 0xB461
0x30: 0x8614
… …
0x32: 0x0007 LIT
0x33: 0x0003 0x0003
0x34: 0x002E DUP
IP ---> 0x35: 0x0020 DOT
… …
0x39: 0x0003
DB[SP] ---> 0x3A: 0xFFFF
0x3B: 0xFFFF
-
The registers are pointing to memory insofar as the following instructions will interpret their values as dping so. ↩