Pixelis Arcanum
contact rss


⏮ Dusting off the Project *

August 25, 2026

Completing the RAM Module

So far, we've been using the RAM as simply as possible. We wired it to the address bus and the data bus and the ESP32 was just sending the read and write signals. But the actual CPC is more complex with a RAM module comprised of three part:

Original RAM Module

The design of the Frankenstrad is different though. Instead of eight DRAM chips, we are using one single SRAM chip KM681000C. This is going to change a few things:

RAM Module

Cleaning Up the Monitoring

I made it sound like these modifications are simple, but that's a lie. Up until now, I've been following the circuit diagram. But a different RAM calls for different supporting chips. And, without fully understanding yet what the gate array does, I'm only guessing what these supporting chips are doing. And figuring out this new design takes a lot of trials, errors, and monitoring.

At first, I had both the data buffer and the data latch enabled at all time. Both chips were feeding off of eachother with a highly unstable result that ends up sitting on the bus when reading or writing. We start a read, bits are coming out from the RAM, they're latched right away and sent back to the data buffer, possibly conflicting with other bits still coming out of the RAM, on the same wires, latched again, and so on and so forth. Both chips are basically crossing the streams, which, as we know, should never be done!

I had to debug the system to understand that. Until now, the monitoring program was only looking at the write operations, but I needed to also look at the read operation to properly troubleshoot the memory module. The read operations include reading the instruction op codes though. That's a lot of "noise" when we just want to look at the memory operations.

Lucky for us, the CPU has a /M1 signal. The active low signal goes low during the machine cycle, which is when the opcode is fetched from memory and executed. The cycles that are not the machine cycles are the actual reads and writes from memory or I/O. And that's the ones we're interested in.

This is the new monitor loop that only display what we want, free of opcode reads:

void loop()
{
    const bool write = digitalRead(_write_pin) == LOW;
    const bool read = digitalRead(_read_pin) == LOW;
    const bool m1 = digitalRead(_m1) == LOW;
    if (!m1 && (read || write))
    {
        BusState state;
        state.read = read;
        state.write = write;
        state.address = read_address_bus();
        state.data = read_data_bus();

        if (state != last_state)
        {
            print_format("%c %04x: %02x '%c'", read ? 'r' : 'W', state.address, state.data, state.data);
            last_state = state;
        }
    }
}

Seeing exactly what is read, from where, and what is written is really helpful, because then I can see when the system stops working and where by moving the data probe along the bus. And the solution is obvious: only enable the data buffer when writing, only enable the data latch when reading, and, at least for now, always latch the data.

What's Next

We're getting there! Only 3 important chips to go: the CRTC, the PIO, and the sound chip. Once this is all wired, we can flash the ROM with the CPC BIOS, and execute it! Or at least try... At that point, our Z80 is going to run a whole bunch of instructions. It's going to do a lot of crazy stuff and I'm expecting a lot of hiccups.

Seeing what it's doing is going to be crucial. To that end, we are going to give our monitor an upgrade and have it look at the machine cycles and print out what instructions are being executed.


⏮ Dusting off the Project *