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: 6XNN
Load VX with NN
INSTRUCTION GROUP: 8XY0
Load VX with VY
INSTRUCTION GROUP: AMMM
Load I with the address 0MMM
INSTRUCTION GROUP: FX55
Save variables V0 through VX starting at the address in I
INSTRUCTION GROUP: FX65
Load variables V0 through VX from memory starting at the address in I
These instruction groups are used for loading or saving the Chip-8 variables.
Instruction group 6XNN is very straightforward. It simply copies the second byte of the instruction into VX.
Here’s the code for this routine:
Labels | Code (hex) | Comments |
6XNN: | 45 | Get the value in the second byte of the instruction into the accumulator (D) and then advance the Chip-8 programme counter to the next instruction. |
01B5 | 56 | Store the value in VX. |
01B6 | D4 | Return to fetch and decode routine. |
Execution time for this instruction is 6 machine cycles (27.24 microseconds).
Instruction group 8XY0 is part of the broader 8XYN group. These are largely arithmetic and logic instructions, the exception being this sub-group, which copies the value in VY into VX. Here’s the code:
Labels | Code (hex) | Comments |
8XY0: | 45 | Get the value in the second byte of the instruction into the accumulator (D) and then advance the Chip-8 programme counter to the next instruction. |
01BD | FA 0F | Mask the byte to save just the second hex digit. |
01BF | 3A C4 | If the second digit is not zero, it’s an arithmetic and logic instruction, so branch to the decode routine for these. |
01C1 | 07 | If we fall through to this point, it’s an 8XY0 instruction to copy VY into VX. Get the value of VY into the accumulator (D). |
01C2 | 56 | Copy this into VX. |
01C3 | D4 | Return to the fetch and decode routine. |
DECODE_ AL_ INSTR: | The rest of the AL instruction handler continues from this point. I’ll analyse this further in a future post on that instruction group. |
Execution time for this instruction is 12 machine cycles (54.48 microseconds).
Register A on the CDP1802 is used as a general memory pointer by Chip-8. For the purpose of Chip-8 programmes, it is designated as the variable I. Although RA can potentially point anywhere within the 64K that is addressable by the CDP1802, for Chip-8 programmes I is restricted to the first 4K (This is the maximum amount of on-card RAM for the COSMAC VIP). This variable can be set with the instruction AMMM where MMM is a 12-bit address from 0x000 to 0xFFF. To form the address, the interpreter uses the low-order byte of the instruction as it is and masks the high order byte so that the first hex digit is replaced with 0x0. So, for example, the instruction AD08 would set I to point to address 0x0D08.
Here’s the code:
Labels | Code (hex) | Comments |
AMMM: | 45 | Get the value in the second byte of the instruction into the accumulator (D) and then advance the Chip-8 programme counter to the next instruction. |
01EC | AA | Set this as the low-order byte of the address in I (RA). |
01ED | 86 | Get low order byte of VX pointer, as this retains the second hex digit of the first instruction byte. |
01EF | FA 0F | Set the first hex digit to 0x0. |
01F0 | BA | Set this as the high-order byte of the address in I. |
01F1 | D4 | Return to the fetch and decode routine |
Execution time for this instruction is 12 machine cycles (54.48 microseconds).
The next two instructions are part of a miscellaneous collection of FXXX instructions. These have a second decoding stage. The second byte of these instructions is actually the low-order byte of the address of their handling routines. The second stage decoding simply grabs this byte and uses it to update the interpreter’s programme counter so it continues execution from the correct point. Here’s the code for this second stage decoding:
Labels | Code (hex) | Comments |
DECODE_ FXXX_ INSTRUCTIONS: | 45 | Get the value in the second byte of the instruction into the accumulator (D) and then advance the Chip-8 programme counter to the next instruction. |
0106 | A3 | Use this to set the interpreter programme counter (R3) to the address of the relevant handler. Execution will continue from there. |
This second stage of decoding adds an overhead of 4 machine cycles (18.16 microseconds) to group FXXX instructions.
Here’s a flowchart for both of these instruction groups:
Now here’s the code for the first of those instruction groups, FX55, which stores variables V0 through VX in memory starting from the address pointed to by I.
Labels | Code (hex) | Comments |
FX55: | 22 | Decrement the stack pointer (R2), ready for a push. |
0156 | 86 | Get the low order byte of the VX pointer … |
0157 | 52 | … and push it onto the stack. |
0158 | F8 F0 | 0xF0 is the low-order byte of the address of the first variable (V0). |
015A | A7 | Set this as the low-order byte of the VY pointer (R7). |
STORE_ VARS_ LOOP: | 07 | Get the value of the next variable. |
015C | 5A | Store it in the address pointed to by I. |
015D | 87 | Get the low-order byte of the address in I. |
015E | F3 | XOR it with the value at the stack (the low-order byte of the VX pointer). This will result in 0 if they match – indicating that all the requested variables have been stored. |
015F | 17 | Point VY to the next variable |
0160 | 1A | Point I to the next address in memory |
0161 | 3A 5B | Return to top of loop if there are still more variables to store. |
0163 | 12 | Pop the low-order byte of VX off the stack. |
0164 | D4 | Return to the fetch and decode routine. |
The execution time for this instruction is 14 machine cycles (63.56 microseconds) plus 14 machine cycles for each variable stored. Total execution time will therefore range from 28 machine cycles (127.12 microseconds), if you store only V0, to 238 machine cycles (1080.52 microseconds), if you store all 16 variables.
One important thing to note about this routine is that I is not reset. At the end of the routine I will be pointing to the byte after the location of the last value stored. Another thing worth noting is that there is no validation of the address – the Chip-8 programmer must ensure that the address used here is sensible and there is sufficient space above it to store all the requested variables.
Finally, here’s the code for the complementary routine, FX65, which loads the values starting at the address in I into variables V0 through VX:
Labels | Code (hex) | Comments |
FX65: | 22 | Decrement the stack pointer (R2), ready for a push. |
0166 | 86 | Get the low order byte of the VX pointer … |
0167 | 52 | … and push it onto the stack. |
0168 | F8 F0 | 0xF0 is the low-order byte of the address of the first variable (V0). |
016A | A7 | Set this as the low-order byte of the VY pointer (R7). |
LOAD_ VARS_ LOOP: | 0A | Get the byte at the address currently pointed to by I. |
016C | 57 | Store it in the variable currently pointed to by VY. |
016D | 87 | Get the low-order byte of the address in I. |
016E | F3 | XOR it with the value at the stack (the low-order byte of the VX pointer). This will result in 0 if they match – indicating that all the requested variables have been loaded. |
016F | 17 | Point VY to the next variable. |
0170 | 1A | Point I to the next address in memory. |
0171 | 3A 6B | Return to top of loop if there are still more variables to load. |
0173 | 12 | Pop the low-order byte of VX off the stack. |
0174 | D4 | Return to the fetch and decode routine. |
The execution times for this instruction are identical to FX55. Once again note that I is left pointing to the address following the last one read into a variable.
A contemporary interpreter should implement all these instruction groups.
Be First to Comment