Z84C00 breadboard project
Home
last change: 2026-01-15
The Z80 architecture is quite special to me. For one, it's the processor used in the TI-84 plus calculator. My initial QCPU 2 architecture design is heavily influenced by the Z80, apart from the instruction set, before I opted for a full 16-bit RISC V-like architecture.

That aside, I'm quite interested in kernels (as indicated by QOS) so it makes sense that I'll be attempting to write a simple one for it. Ideally, my vision is to interface with a terminal (with UART through a SIO) and create a binary loader (through a DMA). From there, new software can be serialised into SRAM and ran immediately, without having to write to an EEPROM.

As frontend to this operating system, I'm going to attempt to recreate Wozmon (Apple I software for the 6502) on the Z80.

Part list

I'm using the 6 MHz specs of all the Z84Cxx components, except for the CTC (which is an NMOS spec):

Now, I'm running this system on a 4 MHz crystal oscillator. I also have a 1.8432 MHz crystal oscillator tied to the trigger inputs of the CTC for the first three channels. The fourth channel trigger input is tied to the trigger output of the third channel. The trigger inputs are used whilst in counter mode, and allow me to use them for flexible baudrate generation. Channels 0 and 1 of the CTC are tied to the clocks of SIO channels A and B, respectively.

Memory map

For those who are unfamiliar with the Z80 address space, and unlike most other CPUs around that time, the Z80 has separate memory and I/O spaces. This is indicated by the MREQ and IOREQ control lines from the CPU.

In the memory range, I set up the memory map so writable RAM takes up the majority of space. This is for the reason I stated before, as I'd like to upload programs through a loader into RAM. So:

  • 0000 - 3FFF : EEPROM (16k)
  • 4000 - FFFF : SRAM (48k)

Conversely, the I/O space is only 8 bit, rather than the full memory address range of 16 bits. The Z80 implemented it like this essentially due to the OTIR-like instructions, which use register B for byte countdown, and register C for the I/O address. In any case, the upper 3 bits of the address are decoded:

  • 0b101xxxxx : PIO
  • 0b110xxxxx : SIO
  • 0b111xxxxx : CTC
2025-12-28
Wozmon for the Z80 family

Wozmon is the original software that came with the Apple I. I ran it on my 6502 breadboard project. Now, one with a few brain cells might realise that the Apple I isn't made with the Z80 CPU. A short superficial research shows that few have recreated it, but it seemed fun to do so, too.

My Wozmon, or ZMon as I'd like to call it, uses at the time of writing 253 bytes of EEPROM, without initialisation of I/O. That last bit costs another 40-ish bytes because the SIO needs quite a lot of bytes in sequence for it to be set up.

This hex monitor allows you to now write programs in hexadecimal and jump to them. For example, 3E is the opcode for ld a, n, D3 for out (n), a, and C3 for the absolute jump instruction.

2026-01-15
Quest to add the Z80 DMA

My goal of this Z80 System on a Breadboard (SoB) is to allow you to upload a binary file and immediately write it to RAM using a loader. This way, you can program it without rewriting the EEPROM, just through the UART connection with the SIO.

Today, I was able to collect the Z80 DMA that I had ordered:

I've registered the DMA peripheral at the following I/O address:

  • 0b100xxxxx : DMA

I had to add another breadboard in order to fit the DMA. In the first picture, the DMA can be seen at the top. It connects to the control wires from the Z80 CPU, as well as bus request and bus acknowledge, and the address and data buses.

To test the DMA, I wrote a trivial program at address 1000 (in EEPROM) which copies 256 bytes from 5000 to 6000 (in RAM) and jumps back to ZMon. In the second picture, the addresses are first assessed before calling 1000R (jumping to the DMA copy routine), after which the addresses are printed out again, which shows that the DMA transferred the source range to the destination range. I also wrote a program (which is not depicted) that receives 16 bytes from an I/O port, which tests the SIO's ready line to the DMA. This allows the SIO to trigger DMA transfers from the I/O peripheral and sync it to memory automatically, without CPU intervention.

Maybe I'll add another DMA for SIO Channel A, in order to use it for other purposes like disk transfers (if I'll ever create that). In any case, this direct transfer method allows me to (not easily, but more neatly) create a file loader. In contrast, I can now transfer entire ASCII files to the SIO without having to stall the CPU due to SIO lag (transmission delay).