A lot of people build Z80 based computers. They build something with some flashing LEDs, some buttons, and maybe even a serial port. But the computers I loved as a kid had high-resolution displays, and keyboards, and joysticks attached. And you could spend your days playing games on them.
So, I’m going to attempt to build a Z80 based computer, on breadboards, with some fun factor involved. And learn about hardware and software development at the same time.
Now, I’ll admit, that at the moment I don’t actually have a fully sketched out plan for how I’m going to do this, or what the final computer will look like or be able to do. But I plan to have some fun in the process.
You’re more than welcome to follow along.
Getting started
Having promised you fun and excitement I’m now going to start you off with something really boring. Yeah, I know, but we have to understand the basics before we can get to the good stuff.
For this step you will need (see the end of the article for buying advice and some affiliate links for readers who can tolerate me earning a few pennies):
- A Z80 processor. I bet you weren’t expecting that.
- Two breadboards.
- An Arduino. The Arduino Nano is ideal as you can push it into a breadboard, but any one will work.
- Some breadboarding wire.
- Some form of 5V power supply.
- Eight 1k or similar resistors.
- A handful of 470ohm or similar resistors.
- A handful of LEDs (or an LED barcode if you have one).
- A couple of Dupont style wires.
- A Z80 pinout diagram. Here’s a handy one for you:
Basic Connections
- Pop your Z80 into the breadboard.
- Pop your Arduino into another breadboard.
- Wire up the 5V and Ground pins of both the Z80 and the Arduino. And, while you’re there join together the power and ground rails on the breadboards. If you have one of those nasty boards with a split in the power rails now would be a great time to wire across those too.
- On the Z80 wire up the INT, NMI, BUSRQ and WAIT signals to the 5V supply. These are input signals that make the Z80 do interesting things. They’ll also screw up this demo if they aren’t connected to 5V.
- Place resistors between each of the D0 to D7 pins and the ground rail. The resistors should be about 1k but the exact value doesn’t matter. Use whatever you have handy. (The blue resistors in the photos).
- Connect a longer lead or dupont lead to the RESET pin. This will be needed to reset the Z80 to start it operating. If you have any sense connect it via a momentary push button switch with a pull-up resistor to 5V. I’m connecting it to the 5V rail.
- Connect some resistors and LEDs to the A0 and upwards lines. I’d recommend connecting at least 8 lines here so we can get a good idea of what’s happening. Resistors should be between 220 ohm and 1k ohm. The exact values aren’t critical. But I’m wiring the cathode side of the LEDs all to a single breadboard line and then connecting that line to ground, rather than wiring them directly to ground. There is a very good reason for this which will soon become apparent.
- Wire the Clock pin of the Z80 (pin 6, the one with the weird symbol on it) to the D13 pin on the Arduino. This is also the pin which flashes the Arduino’s LED so you’ll be able to see when something should be happening.
If everything’s gone to plan you should have something which looks vaguely like this photo:
And, if you must, here’s a schematic:
Gettin’ Sketchy
We’re now ready to write an Arduino sketch to generate the clock signal. We’ll set the pin to output and then just toggle it on and off. Start with a value for wait of about 10ms. You can, of course, adjust this if you need to make your life easier.
#include "Arduino.h"
#define LED 13
void setup() {
Serial.begin(115200);
pinMode(LED,OUTPUT);
}
int wait = 10;
void loop() {
digitalWrite(LED,HIGH);
delay(wait);
digitalWrite(LED,LOW);
delay(wait);
}
Switch on the power. Connect the RESET wire to ground for a moment, (or push your reset button).
If all goes to plan you should see the address lines counting in binary.
What’s happening here? The Z80 is reading instructions and data from the D0 to D7 lines. It has a program counter which records the address the instructions are coming from. It outputs this address on the address bus (lines A0 to A15) and reads in the data at that address. This data is the opcode or command to be executed. Because we’ve connected all the data pins to ground (via the resistors) the Z80 is reading all zeroes. Opcode $00 is the NOP instruction. NOP stands for No OPeration and literally means, do nothing.
So, the processor reads in the opcode and does nothing. Well, technically it does quite a few things even for a NOP opcode, but it doesn’t do any useful work that’s worth describing here. The Z80 then adds one to the program counter and repeats the process for the next opcode.
It’s Working! … Let’s Break It!
Good. Now power down the system and change the resistors on the data pins so they connect to the 5V rail instead of the ground rail. Power up and reset again. Now what happens?
You should see that it all seems to have gone wrong. Those nicely counting addresses are now mangled. This is the point where most people take to an internet forum to ask if their Z80 is broken. No, it isn’t.
Remember we connected all our LEDs to ground via a single wire? Remember I said the reason for this would become apparent later? Well, that time is now.
Remove that connection from ground and plug it into the REFRESH (or RFSH1) pin of the Z80. If you didn’t short anything out by doing this while your computer was powered up you should see that the counting is now working properly again. …But if you leave it long enough you’ll notice it’s only counting the first seven bits of the address2.
You’ll also notice that the LEDs are flickering, sometimes showing the address, and sometimes showing nothing. The REFRESH line is active low. That means it defaults to outputting 5V, but when the signal is active it outputs zero volts. By connecting the cathode of our LEDS to it we only see output on the LEDs when the signal is active.
Promiscuous With Pins
Now try connecting the LED ground wire to each of the other outputs in turn: M1, WR, RD, IORQ and MREQ3. What happens?
You’ll probably see that some of them never show anything and others do, but different pins show different things.
By feeding in $FF4 to the data lines we’re feeding in an opcode which transfers execution to memory address hex $38. Don’t worry for now exactly what that opcode is doing or why it might be useful. Let’s run through some of the pins and what they’re actually showing:
M1: For the $38 opcode the address lines should always be showing binary 00111000 when M1 is active. This is because M1 is active whenever the Z80 is reading an opcode5. Binary 00111000 is also $38 hex. Remember from the paragraph above that the $FF opcode jumps to memory address $38. So the processor is continuously jumping back to the address $38, and so we always see that address for the M1 state.
RD is the read signal. It’s active whenever the Z80 is reading data, In this case the only reading that the Z80 is doing is for the opcode. Other opcodes may read further bytes as part of the instruction or data (or both).
WR is the write signal. It’s active whenever the Z80 is writing data. The NOP instruction doesn’t write any data so never activates this signal. The $FF opcode does write some data. If you watch the LEDs while it executes you should see decreasing binary numbers. I’ll leave what’s actually happening here for you to investigate in your own time.
MREQ is the memory request signal and it’s active whenever the Z80 is accessing memory, either reading or writing.
IORQ won’t actually show you anything useful at the moment. It’s the I/O request signal. It’s similar to the MREQ signal but is active when the processor is accessing input or output devices.
Let’s Refresh Our Memories
All of which brings us back to the RFSH signal.
Computer memory tends to, actually, be very bad at remembering things. If you left a memory chip powered up but on it’s own for more than a few microseconds it would forget everything it ever knew6 It needs constant care and attention with something called a ‘refresh cycle’. Do you remember those variety acts with plates spinning on the top of sticks? And the performer had to keep going back to the start of the line to respin each plate before it fell off? Well, that’s how the high tech world of memory works. We need to keep sending addresses to the memory chips so they can start respinning each plate in turn. Did I say plate? I meant each block of memory. Sorry.
In many old school computers the designer had to build a separate circuit to do this refreshing. The Z80 put that circuitry on board. When the RFSH pin is active it outputs the refresh address on the address lines. And that address only counts seven bits because that was all that the memory chips of the day needed7.
And with that nugget of information you now know more about the Z80 than 95% of the people who know nothing about the Z80. Well done.
Opcode Playtime
Anyway, lets get back to playing with opcodes. (And see, we did get to do some playing after all!) Have a look at a table of Z80 opcodes and try plugging them into the data lines by connecting your data pin resistors to 5V or ground as appropriate and see what happens.
(And note here that the data lines are actually in a jumble rather than a line – take care).
I can particularly recommend the JP nn opcode at hex $C3. This is a three byte opcode. The initial $C3 byte is followed by a two byte, 16-bit address to jump to. But since we’re passing $C3 as every input byte it will always jump to address $00C3. You’ll notice the program counter (RD pin) counting from $C3 to $C4 and $C5 as it reads those three bytes. The M1 pin will always show $C3 but show it a lot less often. (The first byte is the opcode, the other two are the data for the opcode so reading them doesn’t generate the M1 signal.)
Opcode $0A reads a byte from memory, so you’ll see the address it’s reading from being output. Opcode $02 does the opposite and writes a byte to a memory address, so you’ll see some memory writes.
Many opcodes are only a single byte and perform internal operations so you won’t see any more happening than with a NOP. But some of them take longer to do that so you’ll see a longer pause between reads and M1 states. A good example of this is the $09 opcode.
And, if you want something to ponder, try hex $18 opcode and see if you can work out what’s happening to the program counter.
Until next time, have fun.
Buying Links and Advice
You don’t need me to explain what an affiliate link is. They’re marked with (Aff) here.
- Z80 processor (Z84C0008) Mouser Ebay (Aff) Amazon (Aff) Make sure you get the CMOS variant – with the ‘C’ in the middle of the code. New chips are probably a safer bet here than old, second hand, ‘refurbished’ or ‘old new stock’. Make sure you get one in a ‘DIP’ package (two straight lines of pins). The ’08’ at the end is the speed (8MHz). Other speeds will work just as well here. (Beware of fake chips on eBay and Amazon).
- Breadboards Mouser (or a two-pack) Ebay (Aff) Amazon (Aff) (eBay and Amazon ones can be low quality).
- Arduino Nano Arduino store Ebay (Aff) Amazon (Aff) If you’re not comfortable with soldering the ones with headers soldered on will be convenient, otherwise the multi-packs are excellent value for money. Arduino hardware is open source so third party copies are perfectly acceptable and often just as good. Buy direct if you want to contribute to the project or have genuine hardware.
- Power supply Ebay (Aff) Amazon (Aff) Most of these run off a USB cable. Or you can just cut the end off a USB cable and solder on some pin header. If you do, bear in mind that not all cables use the correct wiring colours!
- 1k resistors Ebay (Aff) Amazon (Aff)
- 470ohm resistors Ebay (Aff) Amazon (Aff)
- 3mm LEDs Ebay (Aff) Amazon (Aff) Choose ones with bare wires.
- Breadboarding wire Ebay (Aff) Amazon (Aff) Make sure you get 22awg (also called 1/0.6mm) single core wire. Mixed colour packs are a very useful way to get started and you’ll find having mixed colours very useful for later stages of this project.
- Dupont wires Ebay (Aff) Amazon (Aff) You’ll need M-M (male to male) ones.
Footnotes
- Letters were a lot more expensive in the seventies, so shorter names were often used by chip designers. I tend to use long and short names interchangeably.
- Some variants of the Z80 may count eight bits of the address.
- For completeness you can also try the BUSAK and HALT lines but they’re very rarely used and you won’t see anything happening if you do.
- My choice of this opcode is purely because it’s easy to wire up and the output is interesting.
- Well, not quite ‘whenever’. The Z80 is full of quirks and exceptions, but for now take it as being ‘whenever’.
- Yes, yes, I know this only applies to DRAM and you can also get SRAM, and newer memory refreshes itself. I’m keeping things simple for the newbies. Pipe down and let me continue.
- Which became a problem later when bigger memory chips were invented. And that’s why some Z80 variants give an 8-bit address for memory refreshing.
7 Replies to “Couch to 64k – a.k.a. Building a Z80 Breadboard Computer. Part 1: Pins”
I noticed that in the schematic the INT, NMI, BUSRQ and WAIT pins are connected to ground but in the text there is written that thry should be connected to 5V
Thank you for pointing that out. I’ve updated the schematic.
Do you know if the NMOS Z80 CPU is logic level compatible with Atmega (328, 32) chips? They both operate on 5V supply but Atmega is a CMOS chip.
As long as the ATMega is a 5V one then they should be compatible. Bear in mind, though, that not everything inside an NMOS Z80 is static. I.e. you will need to make sure you clock it at the minimum clock speed and may be difficult driving that speed from an Arduino. (From a quick google it looks like official spec is somewhere between 100kHz and 250kHz, but people have had success at only 20Hz, so may be worth trying).
Comments are closed.