I've constructed a test scenario that consists of a partial EXIT, i.e. the NXT instruction of the PLI NXT instruction sequence where PLI pulls IP from the return stack. (Note: in my implementation, the return stack is implemented using X, and the parameter stack is implemented using S. It is also possible to swap these two stacks, but that instruction sequence has yet to be tested, although the logic for swapping default and alternate stack pointers has already been tested many times by other unit tests.)
Thus, at memory location 0x200 is the NXT instruction that will read the pointer in location 0x202 which points to a "secondary" WORD starting in location 0x205. Control is transferred to location 0x205, which is the ENT instruction. I am expecting that all "secondary" WORDs will have the ENT instruction as its first instruction. ENT will push the value of the IP register (0x204) onto the RS, and then execute a NXT using the first pointer in the "secondary", which in this particular case points to a "primitive" that begins with a bra $+2. So the "primitive" located at 0x20D begins by branching unconditionally to 0x20F, whereupon the test is complete.
As previously discussed in the M65C02A or a related thread (Programmable Logic), the form of the Forth WORDs has been chosen to follow the structure described by Brad Rodriguez. That form expects the code to start at offset 2 from the "header". A pointer to the "header" is deposited in the W register by the NXT operation, so ENT always "finds" the code field as W + 2.
It's been a while since the implementation of the NXT and ENT were discussed, so I would appreciate it if someone would given me a sanity check on the implementation. When py65 completes the test, the total number of cycles for these two instructions is 10. NXT is 3 cycles, and ENT is 5 cycles. Breaking ENT down further: 1 cycle for opcode fetch, 2 cycles for PHI, and 2 cycles to read address of next WORD. The last 2 cycles are for the BRA $+2 instruction in the
"header".
The following is the Python Unit Test function for this instruction sequence.
Code: Select all
def test_Forth_VM_enter_using_default_stack(self):
stdout = StringIO()
mon = Monitor(stdout = stdout)
mpu = mon._mpu
codeFieldPtr = 0x202
mpu.ip = codeFieldPtr # Codefield Pointer
mpu.x = {0 : 0x17F, 1 : 0x4444, 2 : 0x2222}
mpu.memory[0x200] = 0x3B # NXT - Primary Forth WORD Exit (pli nxt)
mpu.memory[0x201] = 0x00
# Forth WORD - Secondary Code Field
mpu.memory[0x202] = 0x05 # Pointer to header #1
mpu.memory[0x203] = 0x02
mpu.memory[0x204] = 0x00 # Pointer to header #2 (partial)
# Forth WORD - Secondary
mpu.memory[0x205] = 0x7B # header: ENT (Secondary WORD)
mpu.memory[0x206] = 0x00
mpu.memory[0x207] = 0x0D # Pointer to header #1
mpu.memory[0x208] = 0x02
mpu.memory[0x209] = 0x00 # Pointer to header #2 (not implemented)
mpu.memory[0x20A] = 0x00
mpu.memory[0x20B] = 0x00 # Pointer to header #3 (not implemented)
mpu.memory[0x20B] = 0x00
# Forth WORD - Primitive
mpu.memory[0x20D] = 0x80 # header: bra $+2
mpu.memory[0x20E] = 0x00
mpu.memory[0x20F] = 0x00
p = copy.copy(mpu.p)
a = copy.copy(mpu.a)
x = {0 : 0x17D, 1 : 0x4444, 2 : 0x2222}
y = copy.copy(mpu.y)
s = copy.copy(mpu.sp)
i = 0x209
w = 0x20D
mon.do_goto('200')
print('\n', mpu)
print(mpu.processorCycles)
self.assertEqual(0x20F, mpu.pc)
self.assertEqual(p, mpu.p)
for j in range(3):
self.assertEqual(a[j], mpu.a[j])
self.assertEqual(x[j], mpu.x[j])
self.assertEqual(y[j], mpu.y[j])
for j in range(2):
self.assertEqual(s[j], mpu.sp[j])
self.assertEqual(i, mpu.ip)
self.assertEqual(w, mpu.wp)
Code: Select all
PC AC XR YR SP VM NVMBDIZC
M65C02A: 020F 0000 017F 0000 01FF 0202 00110000
0000 4444 0000 01FF 020D DL YXSIZ
0000 2222 0000 10 00000
.g 200
IR: 3B <= mem[0200]
rdDM: 05 <= mem[0202]
rdDM: 02 <= mem[0203]
IR: 7B <= mem[0205]
wrDM: 02 => mem[017F]
wrDM: 04 => mem[017E]
rdDM: 0D <= mem[0207]
rdDM: 02 <= mem[0208]
IR: 80 <= mem[020D]
rdPM: 00 <= mem[020E]
PC AC XR YR SP VM NVMBDIZC
M65C02A: 020F 0000 017D 0000 01FF 0209 00110000
0000 4444 0000 01FF 020D DL YXSIZ
0000 2222 0000 10 00000