This is part of a series of posts analysing the Chip-8 interpreter on the RCA COSMAC VIP computer. These posts may be useful if you are building a Chip-8 interpreter on another platform or if you have an interest in the operation of the COSMAC VIP. For other posts in the series refer to the index or instruction index.
INSTRUCTION GROUP: 1MMM
Branches to the instruction at address 0x0MMM
INSTRUCTION GROUP: 2MMM
Calls a subroutine at address 0x0MMM
INSTRUCTION: 00EE
Returns from a subroutine
INSTRUCTION GROUP: BMMM
Branch to the address calculated by adding 0x0MMM to Chip-8 variable V0
The branch instruction group (1MMM) is probably the simplest Chip-8 instruction group in terms of its execution, so I’m not going to bother with a flowchart for this one. It simply replaces the first digit of the instruction with 0x0 to form an address, and then loads that address into the Chip-8 programme counter before returning to the fetch and decode routine.
With the call instruction group (2MMM), things are a little more complicated, but not much. The address of the next Chip-8 instruction in sequence is pushed onto the stack. From then on the sequence is the same as for a branch instruction.
Again, that doesn’t really warrant a flow chart, so here”s the code for both instruction groups:
Labels | Code (hex) | Comments |
2MMM: | 15 | Start of handler for instruction group 2MMM: Call subroutine at 0x0MMM. |
0176 | 85 | Get the low-order byte of the address. |
0177 | 22 | Decrement the stack pointer (R2). |
0178 | 73 | Push the low-order byte of the address onto the stack and decrement the stack pointer. |
0179 | 95 | Get the high-order byte of the address. |
017A | 52 | Push it onto the stack. |
017B | 25 | Reset the Chip-8 programme counter to where it was (pointing at the low-order byte of the current instruction). |
1MMM: | 45 | This is the entry point for the handler for group 1MMM instructions: Branch to instruction at address 0x0MMM. |
017D | A5 | Load it into the low-order byte of the Chip-8 programme counter |
017E | 86 | Get the low-order byte of the VX pointer (R6). This contains the most significant digit of the address to be called/branched to. |
017F | FA 0F | We just need to mask it off to get it. |
0181 | B5 | Load this into the high order byte of the Chip-8 programme counter (this now points to the first instruction of the subroutine/sequence to be branched to). |
0182 | D4 | Return to fetch and decode routine (First instruction fetched on return will be first instruction of subroutine/sequence that has been branched to). |
Timings for these instruction groups are 12 machine cycles (54.48 microseconds) for group 1MMM instructions and 26 machine cycles (118.04 microseconds) for group 2MMM instructions.
When a called subroutine wants to return, it uses instruction 00EE. This calls the machine code routine at 0x00EE. This simply pops the saved address off the stack and loads it into the Chip-8 programme counter, then returns execution to the fetch and decode routine. Here’s the code:
Labels | Code (hex) | Comments |
RET: | 15 | Start of routine to return from subroutine. |
00EF | B5 | Load the high-order byte of the return address into the Chip-8 programme counter (R5). |
00F0 | 42 | Pop the low-order byte of the return address off the stack. |
00F1 | A5 | Load the low-order byte of the return address into the Chip-8 programme counter. |
00F2 | D4 | Return to the fetch and decode routine. The next instruction to be fetched will be at the return address. |
This routine takes 10 machine cycles (45.4 microseconds).
There is another branch instruction BMMM, which adds an offset to the address before branching. The offset is whatever value is currently in V0. Here’s the flowchart for this handler:
And here’s the code:
Labels | Code (hex) | Comments |
BMMM: | F8 F0 | Start of handler for group BMMM instructions: branch to address 0x0MMM + V0. |
01A6 | A7 | Load this into the VY pointer (R7). This is already loaded with the high-order byte of the address, so it now points to V0. |
01A7 | E7 | Set the VY pointer to be used for register indirect addressing. |
01A8 | 45 | Get the low-order byte of the current Chip-8 instruction. |
01A9 | F4 | Add the value in V0 to the low-order byte of the current Chip-8 instruction to form the low-order byte of the address to be branched to. |
01AA | A5 | Load this into the Chip-8 programme counter. |
01AB | 86 | The VX pointer has the high-order byte of the address to be branched to. |
01AC | FA 0F | We just need to use a mask to set the most significant digit to 0x0 |
01AE | 3B B2 | If the ADD instruction at 0x01A9 did not cause a carry, then we know the addition of the offset has not crossed a page boundary, so we can skip the next instruction. |
01B0 | FC 01 | If a carry was generated, we need to add 1 so the high-order byte of the address points to the correct page. |
STORE_ HIGH_ ORDER_ BYTE: 01B2 | B5 | Load the high order byte of the address into the Chip-8 programme counter. |
01B3 | D4 | Return to fetch and decode routine (Next instruction fetched will be at the address branched to). |
Timing for this instruction group is 24 machine cycles (108.96 microseconds) if a page boundary is crossed or 22 machine cycles (99.88 microseconds) if a page boundary is not crossed.
A contemporary interpreter should execute all four of these instructions/instruction groups.
Be First to Comment