Pixelis Arcanum
contact rss


⏮ Completing the RAM Module *

September 22, 2026

Monitoring Execution

So far, the Arduina Mega 2560 has been plugged into our system to monitor the reads and writes in memory. This has been really helpful for our simple program. But the CPC ROM is going to run a lot more instructions, some of which we don't even know exist! And things will get worse when running games or demos! We want to keep an eye on what is being executed, and the Mega 2560 is going to be our little spy. It is going to look at each instruction and output it for us.

Z80 Instructions

Instructions are divided into three cycle types:

Machine, Read, and Write cycles can span several clock cycles, but we won't count them. Instead we are just going to rely on the CPU signals: \M1, \RD, and \WR.

Machine Cycles

The length of the opcodes varies from 1 to 4 bytes and it takes one cycle to read each byte. During the first machine cycle, the \M1 CPU signal goes low and we know a new instruction is starting. Subsequent cycles are not signaled though. To us, they are just reads from memory and it is up to us to know how long an instruction is. Lucky for us, they are all documented in the Z80 User Manual, and we could go through it and make the list ourselves. And even better, the instructions have been documented in nice opcode tables. These matrices are going to be very helpful to have an exhaustive list of instructions and figure out their length.

Let's start by looking at the \M1 signal. We were already looking at it in the previous article to actually ignore the reads while it's active in order to filter out opcodes. The tables have turned though and we now want to know what these reads are to retrieve the instruction opcodes.

So we read the first byte, and we look it up in the first matrix. If it is EDh, it is the prefix for a misc instruction, and we'll know what that instruction is when we'll read the next byte. If it is DDh or FDh, it is an indexed instruction, and here again, the following byte will tell us what the instruction really is.

Some instructions are longer and the remaining bytes will contain their arguments to describe registers, memory addresses or immediate values.

The monitor code to decode the instructions is not much more than a gigantic switch/case to return a string as a mnemonics followed by a couple of bit masks and shifts to retrieve the arguments.

For example, if we get the bytes [0x21, 0x20, 0x00], we start looking up the first. It is the main instruction LD HL, nn, where nn is represented by the two following bytes, in little-endian. Our final decoding will output LD HL, 0020h which tells us the value 20h will be loaded into the register HL.

Read and Write Cycles

Seeing the instructions is going to be useful, but we still want to see what they read or write and where.

I'm going to use a convention that makes sense to me. Addresses in ROM will be displayed in square brackets [0000], while addreses in RAM will be in curly braces {0000}. If we read, we will use >>, showing that the data is extracted from memory. Writes will use << to show that the data is going into the memory.

if (read_from_rom)
{
    print_format("\n[%04x] >> '%c' (%02x)", state.address, state.data, state.data);
}
else
{
    print_format("\n{%04x} %s '%c' (%02x)", state.address, read_from_ram ? ">>" : "<<", state.data, state.data);
}

And what we'll see is something like this:

[0000] LD HL, 0020 (21 20 00)
[0003] LD DE, 4000 (11 00 40)
[0006] LD BC, 0011 (01 11 00)
[0009] LDIR (ed b0)
[0020] >> 'F' (46)
{4000} << 'F' (46)
[0009] LDIR (ed b0)
[0021] >> 'r' (72)
{4001} << 'r' (72)
[0009] LDIR (ed b0)
[0022] >> 'a' (61)
{4002} << 'a' (61)

And those of us fluent in assembly can see that:

And we can then see what is being copied, F, r, a, etc. We see what you're doing!

What's Next

I except to revisit this code. It has been tested against a couple of very simple instructions and doesn't handle any kind of memory bank swapping. But for now, it's good enough!

We have three chips left to plug in to have everyone: the PIO, the Sound chip and the CRT Controller. The PIO should be easier than the display controller. It is also required for the Sound Chip. But wiring the controller may take us more quickly to a visual result. And at this point, I am very eager to see something on a screen! On one hand, an easier path to have sound, inputs and external storage; on the other hand, a steeper path to seeing things on a screen. A tough call that we are going to make in the next article!


⏮ Completing the RAM Module *