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).
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.
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:
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:
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:
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 is just a kind of "Arduino"... It consists of:
Busdrivers for the BBB.
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*).
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:
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.
Test the address pins:
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|
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|
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.
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:
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:
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: