Implementing controls for the 8080

The RST, IN, and OUT instructions

Now that our team has an interrupt function that can interpret when an RST instruction is present, we can pause computation and interpretation of all other instructions until the RST call is resolved.

Last time I mentioned that the processor has an internal “interrupt enable” bit, which is a special register that holds a value of either 0 or 1. When an interrupt is enabled, i.e) the value of the IE register is 1, an RST instruction is placed on the bus; this is an instruction fetch cycle that occurs only in response to an interrupt.

There are eight RST instructions, RST 0 – RST 7. RST 7 is opcode 0xFF, and the rest are staggered one byte behind until we get to RST 0 (0xc7). RST instructions cause the processor to push the program counter on the stack, and commence execution at the low memory location indicated by RST. The addresses these opcodes map to is as follows: RST 0 maps to 0x0000, RST 1 to 0x0008, RST 2 to 0x0010, and so on. These addresses will contain executable code that our team can use to map specific keys to the CPU, which will then act on the program. But how do we know whether an instruction is meant for the CPU to receive data, rather than send it?

This part is easy: IN and OUT are two opcodes (0xdb and 0xd3 respectively), that help the CPU determine whether we’re dealing with an IN instruction or an OUT instruction. Once we’ve determined which instruction we’re dealing with, we pass the port number to a separate function for each, and handle the I/O from there.

I/O Ports for the 8080

So we know that the 8080 does I/O via the IN and OUT instructions. There are 8 separate IN and OUT ports. For example, IN 3 puts the value of port 3 into register A, and OUT 2 sends A to port 2. For a detailed breakdown of what the 8080s ports are used for in Space Invaders, this link has more information.

Now each port has eight bits that can be set, and each bit indicates some behavior in the program. For instance IN 1 addresses port 1, and if the 4th bit is set, player one (P1) fires a shot. The 5th bit being set means P1 moves left, while the 6th bit being set means P1 moves right. We’ll discuss the OUT instruction a bit more when we get to audio implementation, but let’s take a closer look at how we might implement controls for our Space Invaders game.

A Spot of Code

MachineKeyDown(char key)
{
switch(key)
{
case LEFT:
port[1] |= 0x20; //Set bit 5 of port 1
break;
case RIGHT:
port[1] |= 0x40; //Set bit 6 of port 1
break;
/*....*/
}
}

PlatformKeyUp(char key)
{
switch(key)
{
case LEFT:
port[1] &= 0xDF //Clear bit 5 of port 1
break;
case RIGHT:
port[1] &= 0xBF //Clear bit 6 of port 1
break;
/*....*/
}
}

As we can see in the code example above, this could a way controls end up being implemented in our game. This code would be nested in our IN function. When we know that we’re dealing with player input in the IN function, we use the SDL library (our team is using SDL to help implement graphics and controls) to detect which key is being pressed. The above examples only contain LEFT, RIGHT, but we could imagine FIRE, INSERT_COIN, P1_START, etc., being other keys that might be triggered. The switch statements in the two functions above then determine which bit on IN 1 should be triggered. We could hold the values to be triggered in an array and call PlatformKeyUp to ensure that all other keys are cleared in the mean time.

Right now our team is going to be running into a lot of issues with integration, but we look forward to the challenges and continue to grow our knowledge and confidence!