OZ0LTT - Lars Thomsen

You are here: Z80 Computer

Z80 Computer

Design Considerations

My first version of my homemade Z80 computer was a good experience. But due to a lots of experimental designs and "fun learning by trial-and-error", it became very difficult to add some new features and learning more about computer-construction from scratch. Furthermore, is was lucky to find a nice rack cabinet (from an old Brüel and Kjaer wired telephone tester).

Rack for my computer.

It is designed to fit EURO-boards and has a nice power supply (perfect for me!) but unfaltuneally is came with a very odd "back bone." The first part was to get rid of all the un-nessaray components (including the back bone A new back bone was made by new connectores and a piece for strip board.

New BBB (Back Bone Bus).

When using this backbone I could change the order of the plugin-cards (if nessersary): The orignal powersupply was tested (works perfect!) maximum current draw is 3,6A and the voltage is spot on The Back Bone Bus

The Back Bone Bus (BBB) is fitted with 64 pins connectors. But due to the design (using a stripboard) it will halfing the number of buslines. That is bad, but since pins is connected in pairs it will be very difficult to get bad connections. 32 lines isn't much for a CPU-bus. Just a quick calculation:

  • 2 power supply (0V and 5V).
  • 8 bit data bus
  • 16 bit adress bus.
  • 8 (approx) for control signals, such as M1*, MREQ*, IORQ*, WR*, RD*, INT*, MNI*, RESET*. Equals: 34 :-(

Due to my experience in my last Z80 project. I know, that I will get into troubles. I had to find a solution, where the number of BBB-lines can be reduced to 32 and be easier to make hardware-expansions. I will use some of the Z80's limitations and features:

  • Ports and perfericals can only use A0-A7 (256 8 bit ports).
  • MREQ* is only used for memory request.
  • Only memory can use the A8-A15 address lines.
  • Due to fast (modern TTL/CMOS) technology and relative slow CPU we don't need HALT and WAIT to introduce wait states.
  • MNI* don't have to be on the bus.


So alot of pins is only used for memory-communication. So it doesn't make sence for me to put them on the BBB. So if a internally bus was made for this, then they don't have to waste space on the BBB.

The calculation will then be:

  • 2 power supply (0V and 5V).
  • 8 bit data bus 8 bit (LSB) adress bus
  • 6 for control signals, such as M1*, WR*, RD*, INT*, IOREQ* and RESET*

Equals: 24 :-)

Perfect! - But now we have a diffent problem: Should the rest (32-24=8) just be wasted? - My "solution" is on next chapter.

Memory address decoding is easy: The Z80 can address 16 bits (65.536 bytes) and I can't find any good reason to not fill the memory-map with memory. So memory address decoding can be done by MREQ and A15. I.e.: If A15 is low, then 32KB ROM is selected, if A15 is high then 32KB RAM is selected. Port address decording is more difficult.

The Microcontroller

The microcontroller is just a kind of "Arduino"... It consists of:

Busdrivers for the BBB.

  • Fundamentals requirements for the Z80 CPU (reset and clock)
  • "Coarse" address decoder.
  • RAM (128KB).
  • ROM (32KB).
  • Z80 CPU.
  • Memory address decoding and bank switching logic.
Coarse Address Decoder

The Z80 CPU has 256 8-bit ports (located at 0h00-0hFFh and addressed by A0 - A7). So if a want to access the 8-bit port on address 1h, I had to be sure, that every address lines from A1 to A7 is 0 and A0 is high. Thats requirres a lot of logic to do for each port, that we want to access.

Often, designers are lazy, so the won't monitor all address lines. It workes, but causes "mirroring" in the map so we no longer has 256 avaliable ports. I gonna use ports (all memory space is ocupied), so ports is the only way to interact with the computer. By integrating a primary port adress decoder on the CPU-board makes it easy for us to do it right. This requires more lines on the BBB, but makes it easier to select the right address.

Instead of just put A0-A7 and IORQ on the bus, the upper part (A5-A7) is fed into a 3-8 decoder (74HCT138) with IORQ as chip selects. This divides the port addressing into 8 equal chunks with a size of 32 port, each.

The lower 5 bits from the address bus is still fed into the BBB, but since IOCSn* is only active when IOREQ* is active, then an external address decoding with higher "resolution" can be done, quiet easy.

So if I want port 1H, i simply pick another 3-8 decoder, puts the lower half addresslines on ABCD, and use IOCS0* to enable the chip (with G1* and G2*).

Clock Generator






The Sandwich CPU main board Is made by an internal-bus with three plug-in modules. The CPU main board and plug-in modules is a complete computer with all the needed components to run a computer. It doesn't have any "human" interfaces (such as display, mouse and keyboard), but it can run stand-alone and is compatibe with the BBB so other boards can interfaced, later.


The internal-bus is just a strip board with pin-connectors stratical places. At left, we have the memory, in the midle the CPU and on the right the UART/PORT.


The last board is fitted into the board just to show, how easy it is to make changes (upgrades, fail-searching or dischard modules), without ruin the whole board. The pin-connectores is placed, so different signals can be transfered between the boards, and finally to the BBB-bus-connector (far right).

The physical construction is done on perf boards, in following order:

  • Get an overview of the physical placements of the pin-connectors and try to mount all relevant IC sockles on the board.
  • Try to move them around: It doen't have to look pretty, but even small changes can make a huge difference.
  • Remember to make space for additional components (such as resistors, wires etc.) logical near the IC's.
  • Solder the sockles.
  • Place the de-coupling capacitors.
  • Make the power-lines: I make them solid, perhaps we don't know all about the components, but we know how to power it up!
  • Power the thing using current limiting:
  • Any shorts or burning?
  • Make connections:


When having pin-connections, just solder them togheter.

Use wirewrap wires for longer distance (use different colors). I prefer blue as address lines, green as data lines and yellow as control.

When adding a wire, I used to meld the end of a wire wrap with my soldering tip. It doesn't smoke a lot and you can easy clean the tip. If you used a cutting plier, then you can easy make growe in the wire that will tendt to break the wire. Often I use empty holes to secure the wire. This method is quiet easy and strong but still easy to modify. Personally, I don't like put all things togheter and then press "start". All things must be tested individually in order to find all mistakes and misunderstandings. Otherwise a complete failure can be hard to debug and all the frustations will soon ruin the project (I've been there many times!). It doesn't make sence to make the UART/PORT board (the simplest board), if we doesn't have the driver software running from the memory board. And we can't have any software running, until we have the most complicate part (the CPU up and run).

Therefore I start with the CPU-board, furthermore it is possible to test some of the functions, without other hardware than an oscilloscope (and perhaps a frequency counter). Here I do the tests: Pull-downs on all data-pins (that will make the computer beleave, that it actually is running a program just contains NOP's. That will make the computer decode the "instruction" and continue to the next address. Measure the reset, clock (9,83 MHz) and M1*: Is reset released, is the oscillator and M1* running? - The machine cycle is 4 times slower than the CPU-clock, since it takes 4 CPU clock cycles to complete a machine cycle. Therefore we should expect a M1* frequency of 2,4575 MHz.

Construction of the CPU-module



Test the address pins:

  • Measure each pin with an oscilloscope, it should work as a binary counter, so:
    • fA0 = fM1*/2 = 1,22875 MHz
    • fA1 = A0 / 2 = 0,614375 MHz... etc.

Bare in mind, that the only reason to do this test is to find eventually shorts, bad components and wrong wiring, not to be afraid of read wrong decimals. At A8 the address bus behave a bit stranges. It adds a burst when the signal is on, we don't have to make it complicated, just look for slower behavour, when increasing the address pin.


The Memory-module consist of a 256 kbit ROM (32KB) and 128 KB RAM. Since the Z80 CPU only are carabel of using 64KB memory, bank switching is nessarsery.

The bankswitch is the most simple, I've ever seen. And it is my own design! - It only uses four standard TTL-logic chips including the "basic" address decoding.


Normal operation: In normal operation the ROM and RAM is equally sharing the availlable address space, in this operation only A0 - A14 is used to addressing the 32kbyte.

ROM bank (32kbyte) 0000h-7FFFh 0000000000000000b-0111111111111111b
RAM Bank00(32kbyte) 8000h-FFFFh 1000000000000000b-1111111111111111b

This is easy to decode by only looking at the MSB (A15) and MEMRQ* from the CPU and it is now possible for the computer to boot up in ROM and use the RAM.


Normal operation occures, when the system is reset (or rebooted). Here our two bankswitch-bits (AE16 and AE17) are set to '00b' by a octal positive-edge triggered D-type flip-flop (74HCT273), which receives a RESET* signal from the startup (or manuel reset) circuit (on the CPU-board).

Here, the computer will work between ROM and RAM (Bank00, the lowest area of the RAM), since the higher addresses (caused by AE16 and AE17) on the RAM-chip is zero. If eighter AE16 and AE17 is set (by software), then the address decoder entering bankswitching mode. Here, the ROM will be swapped with RAM in the RAM-area selected by AE16 and AE17.

Even in bankswitching mode, it is still possible to access RAM bank00 starting from 8000h to FFFFh, this is done by monitoring A15 and temporary clears the AE16 and AE17 bits if A15 is high (RAM0 is selected). My reason for doing this is, that Z80 stackpointer is located here, therefore we don't have to think about what happend, if we suddenly decided to change to another memory bank.

Here, the 3-8 decoder should behave like this:

Input Output Memory (type/bank) AE16 AE17
000 11111110 ROM is selected XX
001 11111101 RAM Bank00 is selected 00
010 11111011 RAM Bank01 is selected 01
011 11110111 RAM Bank00 is selected 00
100 11101111 RAM Bank02 is selected 10
101 11011111 RAM Bank00 is selected 00
110 10111111 RAM Bank03 is selected 11
111 01111111 RAM Bank00 is selected 00

 Serial and Port interface

The UART-module is used as communication interface for serial communication and other ports. It gets its baud rate quarz-clock from the CPU-board and does async-communications by a address port decoder.

Port addressing

The aim of the port addressing circuit is to select the right interface when the CPU wants to access it. The address decording circuit contains:

4-16 decorder: Which "coarse" selects the address area. It inputs is connected at the 4-bit MSB's (A4-A7) and address is decoded while the IOREQ* (IO requist-line) from the CPU is enabled. The coarse decoder will divide the ports in 16 equally spaced blocks of 16 ports, each:



Address Range


0 (IO0*) 0-F
1 (IO1*) 10-1F
2 (IO2*) 20-2F
3 (IO3*) 30-3F
4 (IO4*) 40-4F
5 (IO5*) 50-5F
6 (IO6*) 60-6F
7 (IO7*) 70-7F
8 (IO8*) 80-8F
9 (IO9*) 90-9F
10 (IO10*) A0-AF
11 (IO11*) B0-BF
12 (IO12*) C0-CF
13 D0-DF
14 E0-EF
15 F0-FF

 The first 13 blocks (0- CF) are located at the BBB and can be used to other HW-interfacing. The UART and bankswitch will be set in the area of F0h-FFh.

The UART (SIO/0) is a dual port UART I decided to make it work with a fixed baud rate of 115.200 baud. The dual UART need four IO-ports, two IO-ports for each channels:

  • F8 (11111000b): Port A (Data).
  • F9 (11111001b): Port A (Control).
  • FA (11111010b): Port B (Data).
  • FB (11111011b): Port B (Control).

Since the UART has a build in port decoder for selecting these registers (C/D* and B/A* and IORQ*). Then we can full fill the requirements by setting A0 = C/D* and A1 = B/A* and now can mask the two LSB's as "don't care" = 111110xx in the code for selecting the UART.

To make things easy, we just divide the last 16 I/O ports into 4 blocks of 4 bytes, each. This can easily be done by using a 3-8 decoder with last MSB permantly set low, so only two bits are used in the 2-4 decoding process, since the MSB's already is decoded in the coarse decoder:

F0 11110000: Y0*: Not used, yet!
F1 11110001: Y0*: Not used, yet!
F2 11110010: Y0*: Not used, yet!
F3 11110011: Y0*: Not used, yet!
F4 11110100: Y1*: Not used, yet!
F5 11110101: Y1*: Not used, yet!
F6 11110110: Y1*: Not used, yet!
F7 11110111: Y1*: Not used, yet!
F8 11111000: Y2*: UART
F9 11111001: Y2*: UART
FA 11111010: Y2*: UART
FB 11111011: Y2*: UART
FC 11111100: Y3*: Bank Switch
FD 11111101: Y3*: Bank Switch
FE 11111110: Y3*: Bank Switch
FF 11111111: Y3*: Bank Switch


 Baud Rate Generator

The crystal oscillator is already located on the CPU-module. It is made by combining a crystal with a 14 stage counter (CMOS 4060). The fundamental frequency of the crystal is 9,830400 MHz which also will be the clockfrequency for the CPU. The 14 stage counter on the 4060 can divide this frequency:

Pinout for the 4060 CMOS.