An attempt to build a Z80 computer capable of running CP/M
To make the experience fit your profile, pick a username and tell us what interests you.
Well, I did it. I ordered my first custom circuit boards. I laid out a prototyping board with an ISA bus card edge, sent it to dirtypcbs, and after a month of impatiently checking the mailbox, they arrived.
I ended up with eleven boards. After I've built a few and done some testing, my plan is to order a second set of boards to use as a backplane. I'm thinking around five card slots per backplane board, with right-angle headers on the ends to link them together. Until then, I've soldered a couple slots onto cheap China special protoboards, and one slot built as a breadboard adapter.
As for the computer design itself, I decided to keep the buffers on the address and data bus since I'm going with the card-based approach and have a full set of Z80 peripherals to experiment with. And after quite a bit of going back-and-forth on the matter, I've decided to go with convention for the Z80 and use the low byte of the address bus as the IO address, leaving the high byte as an optional parameter byte. This will give the full 255 possible IO devices (instead of just sixteen as in my previous design), and give up to 8 bits for device parameters (such as A/B and C/D select on the SCC, PIO, etc). I'm using a design I first saw [Jan Verhoeven] use for selecting I/O devices: 8 DIP switches set the address of the IO card, and the card address is compared to the address bus with a 74'688 equality comparator.Read more »
After the HAD article [Brandon Dunson] wrote about Tanner Electronics, and the subsequent realization that I'm only 30 miles away, I had to go check it out. Two hours and $35 later, this project has new life.
I picked up a 1.8432 MHz crystal and an oscillator of the same speed. It should be a good clock rate for the UART, and I think it would make a good rate for the main system clock as well. I also grabbed a couple 62-pin card edge sockets (ISA, anyone?) so I could experiment with mounting various sections of the computer on boards (à la [Quinn Dunki]), and I got a CompactFlash socket for booting CP/M (à la [Grant Searle] (whose web host blocked on all AT&T services)). For the sake of my future MC68000 project I got a couple 30-pin SIMM sockets (though if I can figure out the refresh, I might pull one into this project).
Most exciting of all though, Tanner's actually had the Z80 DMA. The DMA is the the hardest to find of the Z80 set. Mouser still carries the SIO, SCC, PIO, CTC, etc, but not the DMA. And since they had them, I did picked up a PIO and CTC. This means now I have a complete peripheral set for the Z80, and I'll be going back to almost to square one.
I'm thinking this time I'll do without the buffers. They worked, but I think they may have been the source of some of my problems when I tried running slightly more complex code. I do wish I had an oscilloscope to properly investigate it though. I'll probably keep my ROM/RAM setup the same, with a 32kB ROM swapping with the lower 32kB of RAM. I'll probably also stick with the '154 for IO address decoding, and the flags register for the ROM/RAM swap.
Now, planning is all well and good, but the project already got put on hold for one move, and another is coming up soon. So, I don't know when I'll actually get to sit and work on this again, but at least it's back on the front of my mind. As anxious as I am to sit down and draw it out, I should probably finish my weather display first, before it ends up permanently stuck in the limbo of the projects bin.
I never thought I'd be so happy to see a few characters slowly type out on screen. Serial communication has been the hardest part of this project so far. I'm using the Zilog Z8530 Serial Communication Controller (SCC). It's a versatile 2-channel programmable USART capable of speeds up to T1, and most notably used in the early Macintosh line for serial and LocalTalk.
With its versatility though, comes complexity. The SCC has sixteen registers for each channel (some shared though) that have to be programmed in the right order to properly configure the chip. Get the order wrong, and an internal race condition is likely. The manual warns of this possibility, but other than a few example assembly programs in the appendices, doesn't give much detail on what the proper order is. What finally worked for me was a combination of procedures found via Google, and old-fashioned trial-and-error.Read more »
It never fails. If you get to the point where you start to suspect the Assembler/Compiler/Automated-tool-built-by-someone-much-smarter-than-you is what isn't working, then humility is sure to follow.
I salvaged a Zilog 8530 SCC (UART) from an old motherboard, wired it in, and threw together a quick assembly program to initialise and test it. It didn't work. For debugging, and so I could know if my code was at least getting past the initialisation, I added an LED to a register I have tied to address $F0 on the IO bus (something I know worked before). It didn't work.
I have a '154 4-to-16 decoder I'm using for peripheral select. The Z80 OUT instruction will output an address on the low byte of the address bus. I'm using the high nybble (A4-A7) of this address to provide chip select lines for up to 16 peripherals, with the low nybble (A0-A3) available for the device (e.g. A/B select on the SCC).
Probing around the '154 to figure out why not even the LED would light, nothing was working as expected. Output 15 was never enabled, 0 only blipped, 3, blipped, and 1 was all over the place. It almost seemed like an endian mixup, but surely the assembler wouldn't mix up something like that.
No, of course it wasn't the assembler. I had wired the '154 to A0-A3, instead of A4-A7. It didn't show up before, because I had always used $FF or $00 as the output address. Now that I'm to the point of using $F0 for the register, and $00-$03 for the SCC, it's a significant problem.
In my defense, thanks to allergy medication, I'm surprised I know which way is up. Cursed Spring.
I spent a few hours last night wiring up my breadboard. I'm using three 74x244 buffers for the address bus and control signals, a '652 transceiver for the data bus, a '32 quad OR for some glue logic, and a '273 register for output. My Flash is serving as ROM on the low addresses, and an SRAM on the high addresses. The second RAM chip is on the board, but not wired in yet. Eventually I plan on being able to bank swap ROM for RAM in the low addresses. Clock is provided by an oscillator built from a 40106b hex Schmitt trigger inverter.Read more »
I've finally completed the code for programming my flash ROMs, and successfully tested all functions for the first time. The code is an absolute mess, but I'm posting it here in case anyone else might find it useful. I've broken out the functions specific to working with the flash into a library. This is my first experiment with classes in C++, so forgive me if its implementation is non-standard.
The Arduino sketch posted below will present a prompt and menu for working with the chip. The Read function will read the specified address range and output Intel Hex format. The Write function parses Hex format and programs the chip with it. Extended Linear Address entries are used to select the sectors on the chip (0-7, for 8 64k sectors). Verify checks the lock bits for the specified sector, and scans through the sector looking for any bytes that are programmed (not reading $FF).
I did run into a few problems while testing the programmer. The first was I noticed that between reads of the same area on the flash chip I was testing with, the value returned was inconsistent. Sometimes it would be just a single bit different on one read out of three ($FF $7F $FF), but sometimes it would be three very different reads ($7F $1F $60). My first thought was perhaps I needed decoupling capacitors, so I broke out the soldering iron. No change. Maybe I'm not accounting for setup time, so I added a delay, and read the byte three times in a row before bringing OE back up. No change. Finally, I threw together a quick sketch that would scan through the entire address range of the ROM and read each byte sixteen times, and report any that were inconsistent.
Clearly, this chip has a bad sector. I pulled these chips out of a wall-mount touch panel that is at least 15 years old, so a bad sector is not surprising. Of the four, two have bad sectors.
The other big problem I ran into while testing was none of the flash chips wanted to respond to any of the write commands. I pored over the datasheet for hours, checking, rechecking the commands, the timing diagrams, everything I could think of. Nothing seemed out of place, and I know I had it working before I moved from the breadboard. ... Which of course means that must be where it went bad. Sure enough, another quick sketch to step through the address pins one-by-one, and I had swapped A14 and A15 when building the board. Trying to desolder from the cheap import board resulted in completely removing the copper pad as well, but I got them connected the right way in the end. It was pretty exciting to see the byte write command complete successfully, and be able to read back in the data I had written.
Programmer library and Arduino sketch below:Read more »
I've been working through the code for reading/writing/erasing my flash chips. I've got reading down and outputting hex format nicely. Chip erase is working, but sector erase still needs some work. For writing, I've been working on the code separately, and have it correctly parsing hex format; I just need to roll it in to the rest of my code. I'm getting to the point where the code is unmanageable in the Arduino environment though. I need to get a programmer and start using a real IDE.
I spent the last two weeks obsessively checking the USPS tracking site hoping for any update on the parts I ordered from China to come in, and finally today they were delivered. I got some 40-pin ZIF sockets, pin headers, a Mega shield protoboard, and after a few hours soldering, I have an Arduino Mega Flash Programmer Shield. Not my prettiest soldering job, but it works. I am a little annoyed with myself for a careless wiring mistake—my control lines I ran around the board to Analog 2-4, when I meant to run them to Digital 2-4, which would have been a short, straight shot. Oh well, it works either way.
Hopefully once this semester has wrapped up I'll be able to finish the programmer and get some code burned, so I can start testing the Z80. I think I've got my initial schematic figured out, but need to order more buffer/drivers and maybe a few other odds and ends logic. I want to try to build the glue logic out of 7400-series, but if budget doesn't allow, I'll use the Altera CPLD I already have.
I also picked up a used copy of "Microcomputers and Microprocessors" at Half Price Books. It goes through interfacing and programming the Z80, 8080, and 8085. It has already proved very helpful for a few points I was fuzzy on.
With its 16-bit address bus, the Z80 can access up to 64KB of memory. It has built-in support for DRAM refresh, using the low byte of the address bus to provide a refresh address during the second half of the M1 cycle. I don't have any RAM on hand that would work with something so old as a Z80, so I'll have to order some. DRAM refresh sounds like a hassle (especially using such a slow clock starting out), and isn't currently available in through-hole packages or 5-Volt, so I'm going with the obvious choice of SRAM. To try and minimize chip count, I'm going to use a pair of 32KB SRAMs with A15 selecting between the two.
As for ROM, I have an old Crestron touch panel built primarily with socketed ICs, including four 512KB AMD AM29F040 Flash memory chips. My plan is to use the lowest 32K of one of them and bank switch with the low RAM. This will put my ROM at address $0000 on boot, so I don't have to try to feed a 3-byte jump instruction on reset. I've seen a number of different ways to pull off the switch, but what makes the most sense to me is to put a register on the IO Bus that handles the swap.
What I'm thinking is a pair of 4-to-16 decoders on the address bus, enabled by IORQ (which I'll need for peripherals anyway). A high address like $FF will select an 8-bit register. Bit 7 on the register will select between ROM (clear) and RAM (set). On reset, register is cleared, and Z80 starts executing from $0000, which is ROM. ROM copies what it needs into the high RAM (CP/M sits at the top anyway), then jumps up to a routine that will set the bit for bank swap. With this approach, I could use the additional bits of the register to swap out additional pages of the ROM, by connecting them to the address lines above A14.
Before I can do any of this though, I need to figure out how to program the Flash, and I want to back up what is currently on the chips, just in case. My first thought was an Arduino Uno with a couple of shift registers outputting the addresses, and PORTD as data bus with the Flash. I'm sure I could have made it work, but after my first attempt failed, I remembered I had picked up an Arduino Mega from one of the closing Radio Shack stores. I used ports A and C for the first 16 address lines, and since those are numbered as digital pins 22-37, I just used 38-40 as A16-A18 (though I would really like to know whose idea it was to number the pins on the Mega in such a haphazard way, with some ports counting up, some counting down, and some just mixed at random). PORTK was data, and PORTE serial and control signals. I can read the entire memory and output it as ASCII-encoded Hex over serial at 115200Kbps in no time at all.
Now I just need to make sense of the datasheet and figure out how to erase and write to the Flash.
For years, I have wanted to try building an 8-bit computer from bare chips—ever since as a young teenager I saw a build log where someone was building a computer around a 68HC11. I stared at those schematics for hours unable to decipher much beyond which thick line was the data bus. The idea was planted though.
I've learned much since then, but there is still much, much more that I don't know. I think I'm finally at the point where I know enough to at least get started.
Over the years as the idea has jumped in and out of my head, I've debated different processors—6502, 6811, 8080, Z80 ... even 16-bit chips like 8086—but the one that has always stood out is the Z80. When I was a kid, my family had an old Sanyo MBC-1000 which ran CP/M on a Z80. That old machine with its green phosphor display is etched into my memory.
In March of this year (2015), my uncle gave me his old electronics kit and a bucket of miscellaneous components and wires. Sorting through this I found a Z80, a Z80A, and a D780C-1 Z80 clone. Suddenly I had no excuse to not give it a try.
I'm working with the D780C-1, because it was in the best shape. To start out, I've built an oscillator out of a 40106b, running at about 10Hz. I've got it free-running with data pins all pulled low (NOP). It may not be much, but there is a certain feeling of accomplishment seeing the address lines count up.
It's hard to find a 7-segment decoder with hex support, and all the ones I have on-hand are BCD only. So, I'm currently driving the displays with an Arduino and a couple of '164 shift registers. Messy, but it works for now. I've ordered a Terasic USB Blaster, and I have an Altera MAX 7000 CPLD from a digital logic class. Plan is to drive a 4-digit multiplexed display with it so I can see the entire address bus.
Become a member to follow this project and never miss any updates