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: 0XXX
Calls a machine code subroutine at the address 0x0XXX.
This instruction group allows CDP1802 machine code to be integrated with a Chip-8 programme. I analysed the routine that calls the machine code subroutine in a previous post.
The ability of a Chip-8 programme to be mixed with machine code was undoubtedly a very powerful feature for COSMAC VIP users, however there are restrictions.
It is important that the machine code subroutine does not change registers that are reserved for use by the Chip-8 interpreter. The registers that can be safely used are RC, RD, RE and RF. The Chip-8 interpreter uses these for temporary storage only. All other registers have a specific purpose and should not be changed. Except in the explicit case of R5 mentioned below, changing the value of any of the other registers could prevent the Chip-8 interpreter from functioning correctly. See this earlier post for a table showing how the registers are used by Chip-8.
R3 will be the designated programme counter when the machine code subroutine is called. It is possible for the Chip-8 program to pass arguments to a machine code subroutine. To do this the arguments should immediately follow the instruction that calls the subroutine. The Chip-8 programme counter R5 will be pointing to the byte immediately following this instruction. After reading the arguments, the machine code subroutine should advance R5 to the next valid Chip-8 instruction before returning.
The machine code subroutine should use a D4 (SEP 4) instruction to return control to the Chip-8 interpreter.
In a previous post I mentioned that the Chip-8 interpreter adds two instructions to the start of every Chip-8 programme. Both of these are group 0 instructions that call machine code subroutines within the interpreter. The first is 0x00E0. This calls a routine that clears the display memory by setting all the bytes to 0. It’s quite a straightforward subroutine. Here’s the flowchart for it:
And here’s the code:
|Address (hex)||Code (hex)||Labels||Assembly||Comments|
|00DF||D4||RETURN_ TO_ FETCH_ LOOP:||SEP 4|| Set R4 as program counter (this returns execution to the fetch and decode routine at 0042)
This is actually the last instruction of the draw sprite subroutine, but it is ‘borrowed’ by the clear screen subroutine (see instruction at 0x00E8 below)
|00E0||9B||CLS:||GHI B||This is the actual entry point of the clear screen subroutine
Get the display page …
|00E1||BF||PHI F||… and store it in RF.1|
|00E2||F8 FF||LDI 0xFF||Store 0xFF in RF.0 …|
|00E4||AF||PLO F||… so that RF now points to the final byte in the display page|
|00E5||93||CLEAR_ SCREEN_ LOOP:||GHI 3||Get zero into the accumulator (D)
R3 is the interpreter subroutine/machine code subroutine programme counter. Since this routine is in page 0, R3.1 will contain 0 and it takes just a one byte instruction to get the value from this source rather than using a two-byte immediate addressing instruction. This is another example where saving a few bytes of memory here or there was more important than code clarity!
|00E6||5F||STR F||Zero the memory location currently pointed to by RF|
|00E7||8F||GLO F||Get the low order byte of the current address in RF|
|00E8||32 DF||BZ RETURN_ TO_ FETCH_ LOOP||If the byte that’s just been zeroed is at address 0x00 in the display page then we’re done, so jump to the return instruction|
|00EA||2F||DEC F||Otherwise point to the previous byte in the display (bytes are zeroed starting at the final byte in the page and moving backwards through the display memory)|
|00EB||30 E5||BR CLEAR_ SCREEN_ LOOP||Jump back to the top of the loop|
|00ED||00||DB 0x00||There’s one byte of filler at the end of this subroutine|
Since some Chip-8 programmes will use this machine code subroutine elsewhere in the programme as a convenient way of clearing the screen, 0x00E0 should be treated by contemporary interpreters as a clear screen instruction.
This routine takes 24 cycles (108.96 microseconds) to execute.
The second instruction added to the start of every Chip-8 programme is 0x004B. This routine switches on the COSMAC VIPs display. This is necessary because, by default, the display is off. This is a very short routine that simply sends an input command to the display controller that causes it to turn on. From this point an interrupt will be generated 60 times a second. I’ll discuss this further in a future post on the Chip-8 timers. Here’s the code for this subroutine:
|Address (hex)||Code (hex)||Labels||Assembly||Comments|
|004B||22||SWITCH_ ON_ DISPLAY:||DEC 2||R2 is the stack pointer. The 1802 has no push or pop operations, so this has to be done manually. The stack grows downwards in memory, so to push a value onto the stack, the stack pointer has to be decremented first.|
|004C||69||INP 1||This sends a control signal to the video controller to turn it on. The video controller now starts generating interrupts every 60th of a second and using DMA to read the display memory. The memory to be accessed is controlled by the interrupt handler that is pointed to be the address in R1. INP instructions will read the byte placed on the data bus. This is read into the accumulator (D) and placed onto the stack (R2). We don’t have any use for this value, so…|
|004D||12||INC 2||… we immediately pop it off the stack|
|004E||D4||SEP 4||Return control to the interpreter’s fetch and decode routine at 0x0042|
This takes 8 machines cycles (36.32 microseconds) to execute.
As the display should remain on while the Chip-8 interpreter is running, there will be no need for this subroutine to be called again. A contemporary interpreter can safely ignore this instruction in favour of its own display handling code.
The Chip-8 interpreter makes one final machine code subroutine available to the Chip-8 programme. This is located at 0x00EE and must be called (with a 00EE instruction) whenever a Chip-8 subroutine needs to return to its caller. Since this goes hand in hand with the group 2 CALL instructions, I will analyse it in the post that looks at that instruction group.