Some time ago (uh, about two years), I came across a little single board computer in a scrap pile:

This seemed interesting because it has a Z80 CPU and several other Zilog chips. Namely:
  • Z84C0006 Z80 CPU, rated for operation at up to 6 MHz, and it's built with a CMOS process so also has no minimum operating frequency (no dynamic logic inside!)
  • Z8470 dual UART (DART), providing two serial ports
  • Z8420 PIO, providing two 8-bit GPIO ports usable for input, output and interrupts
  • Z8430 CTC, a clock generator and timer with three timer channels and two clock outputs

It also has a 32-kilobyte SRAM and 32-kilobyte EPROM to fill up the 64k address space, a little RTC and a modem adapter chip.

Given my familiarity with the Z80 in calculators, I rescued this board from the scrap heap even though I didn't have any plans to use it for anything. I did try to power it up to see if it did anything interesting (and found that it expected AC input to the barrel jack in the top left of the photo), but although I could see activity on the address bus that seemed consistent with the CPU executing code there didn't seem to be any meaningful activity on any of the ports.

(I later happened to learn from the person who had left the board on the scrap heap that this was the brain of an old point-of-sale terminal, where the pin header to the right of the CPU connected to a keypad and vacuum fluorescent display.)

After this sat for a while, I decided it would be interesting to scavenge the Zilog chips from it and use them to build my own little Z80 computer.

In designing a system, I didn't take any cues from other Z80 systems and simply sought to build something that I could get running some kind of software with relative ease. It also seemed like it would be fun (and somewhat convenient) if I could design it such that it was possible to start with completely blank memory and get the system running without any external tools, but manually flipping switches on a front panel and feeding data in.

Some initial design sketches put the entire computer on a single board with serial ports and all of the memory, but I grew uncomfortable with the complexity. It would be somewhat difficult to test and debug all of the components on a single board, so I instead chose to split the design up into a series of cards, with the Z80 system bus broken out to a card slot where I could connect additional features incrementally as I designed them.

As it turns out, both a front panel with lights and switches for bootstrap and a mainboard that's basically just a backplane for plugging cards into it are both characteristics of the Altair 8800 computer from the 70s! It was encouraging to realize that the overall design I had come up with resembled one of the earliest microcomputers that came before the Z80-based home computers of the 1980s which this thing has similar technologies to.

Here's what my brain board looks like, with everything you need to get the CPU running:

I've included 32 kilobytes of RAM and 32 kilobytes of ROM (which is actually a FM18W08 FRAM for ease of programming, so I don't need to use higher voltages to write), and 1 MHz and 4 MHz clocks which can be manually switched between (or neither used, and clock sourced from elsewhere). There are provisions to disable the ROM either in software (by writing values into the 74HC174 4-bit register mapped to I/O address 0xFF), or manually by putting a jumper across a header for that purpose, so expansion cards can provide extra RAM (which might be pageable) or do something else with the bottom half of the system address space.

There's still some complexity here that I'm a bit nervous of in how the bus control signals get decoded to the chips here, and I haven't yet been able to do any real verification because I don't have a backplane yet. The design looks like this:

This board has four 58-pin card sockets (using Samtec MECF-30s), and are meant to be daisy-chainable with the 64-pin DIN41612 connectors on either end. To assist with debugging I also included some LEDs to show the values on the data and address buses, since it costs almost nothing to add a few LEDs to a board that's being assembled commercially. I haven't been able to get this built yet because the lead time on the card edge connectors was very long (they're currently estimated to arrive at the assembler near the end of August), so the brain board has been sitting while I wait.

I haven't been idle while I wait, but I'll discuss that in another post soon since this one is getting long.
This is fantastic and super interesting. I've wanted to do something similar but haven't made the time.

Great to see the mighty z80 making the world tick over as a pos terminal but also getting a new lease in life (hopefully if it's all still functional).

Looking forward to your updates.
This project didn't die, but I ended up waiting a lot longer for my card edge connectors to arrive than expected. Since they're surface-mount connectors I wanted to order assembled boards rather than assemble them myself, so I had the board house (JLC) order connectors to use. Despite the delivery estimate being near the end of August, there hadn't been any updates until today when I finally decided to contact them about it. The result was that they apparently had arrived but inventory on the web site hadn't gotten updated. Now it has been, and the parts are available for me to use.

So, I'll have to pull up my backplane design again and put in an order for some boards that I can use to start checking out correct behavior of everything.
Still not dead! I got my backplanes made and it's pretty satisfying being able to chonk boards into it. If I were to redesign the board form factor I'd probably use DIN41612 connectors to connect boards to the backplane as well as to connect copies of the backplane together. That would probably be cheaper than those Samtec card-edge sockets, and more accurate to the idea of 80s hardware.

Once the backplanes showed up, this sat for a while because testing out the hardware and writing firmware for the debug board seemed hard. This week I was inspired to work on the firmware again after reading a paper comparing C with Rust firmware implementation and concluding that I would probably enjoy writing the firmware in Rust more than in C using Zephyr, even though I had written a basic firmware using Zephyr already. Since this is a project that really depends on me having fun in order for any progress to occur, Rust seems like the right choice. (It also helps that I feel much more confident that any given code I write in Rust will work correctly than I do about similar C: debugging random issues is not fun.)

So this week I've gotten a Rust firmware about up to the same place I had Zephyr: the debug probe provides a USB serial console that I can interact with on a PC to manually exercise features, and I've sketched out how to control the Z80 bus interface. I also wrote out a bit of a test plan to validate the correct behavior of all the hardware parts which doesn't seem too difficult to execute on as long as I can find a couple hours of "lab" time.
Awesome ... but where are the pictures man?!

Glad it's still going - especially because it's for the fun of it 🙂
Okay okay, here's a short video of what I wrote to verify the bus control is making sense (iterating through a Gray code only because that's more interesting to watch than toggling each line on then off in order).


The USB serial console is illustrated, and the gratuitous "breathing" status LED that blinks while the USB connection is active as a visual status indicator.
Looks VERY slick!

The status LED did catch my eye, nice to see it testing as it should.

Keep us updated, this is very interesting.
I've been continuing to make progress on my debug probe firmware, and was confident enough in it yesterday that I connected the CPU board and the debug probe to the backplane at the same time to start seeing how the CPU and memories respond. No smoke which is good, but I found a few bad assumptions that need some work to fix.

Backing up a little bit, when I went to validate that all the bus control signals from the debug card were being driven as expected on the backplane, I discovered that all of the control signals had been incorrectly labelled on my schematic, so the GPIOs on the microcontroller were labelled wrong (but the net labels at the card edge connector were correct). Looking at the labels near the level shifters made the mistake obvious, where the high-voltage (backplane) side of one signal was marked ~RD and the low-voltage (microcontroller) side was marked ~WAIT for instance.

The fixed schematic looks like this now:



Fortunately that's mostly easy to fix just by changing the pin mapping in software, but it does require a small hardware modification to close the jumpers (JP1 and JP2 in the image above) on the two IOs that I thought were unused (I'm glad I included provisions to use those if needed!). It may also make the firmware to respond to bus events (like emulating a memory) a little more complex later.

I believe the cause of this issue was that I had reordered the physical mapping of pins while I was doing the layout so there would be fewer crossings on the final board to help maintain good signal integrity, but neglected to update the labels at the low-voltage side of the level shifters after I did that. You can see in this image of the layout that I succeeded at running basically all of the bus signals in straight lines without any crossings:



Once I connected the mainboard, I discovered that my assumption about being able to hold the CPU in reset and arbitrarily control the bus wasn't correct. I had assumed that it would tri-state all of the control signals while in reset, but also somewhat reasonably I found that when powered on it was asserting M1 and MREQ as if beginning an instruction fetch, and those remained asserted when I put it into reset. Looking back at the 1987 Z80 technical manual, it turns out to need a clock to run through reset:
Quote:
During reset time, the address and data bus go to a high-impedance state, and all control signals go to the inactive state. Note that ~RESET must be active for a minimum of three full clock cycles before the reset operation is complete.
That it says control signals go to the inactive state probably means that even when coaxed into releasing MREQ and M1, it will be driving those high rather than leaving them floating.

I had intended to have two ways for the debugger to become bus initiator: hold the CPU in reset, or by asserting BUSREQ and waiting for the CPU to respond with BUSACK. It seems like only the latter approach actually works, which means I'll need to be able to clock the system (to begin with, manually toggling the clock from the debugger) in order to test out the memories and check the CPU behaves as I expect.

I was able to confirm that my level shifters (which are TXS0108s) do provide some protection against incorrect bus use (a feature I had anticipated but didn't intend to test), which I exercised somewhat deliberately while exploring the CPU reset behavior above. I had the debugger attempt to become bus initiator by holding the CPU in reset even after seeing MREQ was asserted, and found that consequently MREQ started oscillating at about 15 MHz as the CPU was trying to drive it low and the debugger was trying to drive it high. That's obviously not a good behavior, but nothing was damaged and I believe the oscillation is a side-effect of how these bidirectional level shifters work.
There's also a little additional protection in my board design because I included inline resistors (representative ones on the left side of U8 in the second image above) on all of the signals coming into the debug card from the backplane, so if there were drivers fighting with each other then the power of them fighting would be dissipated in the resistor rather than inside a chip where the chip might be damaged.
I made some more progress on validating the memories and exploring exactly how the CPU behaves in reset.

Continuing from yesterday's work, I looked at what happened if I held the CPU in reset after power-on, then applied the three clock cycles (plus a fourth for good measure) the documentation says it needs to actually reset. My logic analyzer trace is below:

Initially the CPU seems to be in the midst of an instruction fetch (looks like T2 of an M1 cycle because both MREQ and RD are asserted) and we can see MREQ being asserted for memory refresh (from the falling edge of the clock in T3 until the falling edge in T4, assumed to be refresh because we're not watching the RFSH signal and can't confirm that). The address bus only goes idle after the third cycle, consistent with what the documentation says.

I still haven't tried driving RD, MREQ and friends from the debugger once the CPU has actually been reset, but expect the CPU is in fact still driving them high rather than setting them high-impedance.


While using the debug probe as a logic analyzer in addition to my real logic analyzer, it looked like the CPU was in fact managing to fetch a byte from address 0 after reset, since I was able to watch it do an instruction fetch and get a value. That was unexpected because the CPU board has a jumper on it to disable the ROM, and I had jumpered it for this initial testing!

After some time investigating, I believe the issue is that I had a pulldown to ground on the ROM enable signal, where it should have been a pull-up to make it disabled by default. Here's the board layout in the relevant section:

J5 is jumpered to disable the ROM by pulling FP_FLASH_EN low. U7 is a 74HC173 4-bit register with FP_FLASH_EN wired to one of the output enables, so if the jumper is shorted then the register's output is high-impedance, with its default set by R7. U2 is a dual 4-input OR gate with FLASH_CE going to the flash's chip enable input (active-low), so if any of RFSH, FLASH_EN, A15 or MREQ is high then the flash chip select is deasserted; thus the goal is that if the register is disabled, the flash chip select should be permanently deasserted. Otherwise, writing a value to the register (which is meant to be I/O port 0xFF) with bit 0 set should allow the flash to be disabled in software.

Noticing that R7 is instead pulling down to ground however, that means the default state you get with J5 shorted is that the flash chip enable is allowed to become asserted sometimes. It's a fairly simple fix to pull it up to Vcc instead, although physically doing that rework was rather awkward because of the small surface-mount resistors next to the large plastic DIP sockets. I had some difficulty desoldering R7 without melting the sockets for U2 or U7, but eventually managed it and routed a bodge wire to a conveniently-placed Vcc pad (on the pull-up for the INT signal) that I could attach to:


Next time, I'll return to exercising the RAM and check that the flash seems disabled as expected.
Through the magic of lasers and 3d printing, most of a case:

I probably should have made it something like 0.5mm wider since the side panels have a bit of flex in them to mate with the top (I think because the DIN connectors are interfering), but it works well enough for now. I've conveniently left the part that keeps the cards in place for later..

I also completed validation of the memories, confirming that I can read and write both the FRAM and SRAM while claiming the bus from the Z80. I can also watch it fetch instructions, but haven't written any code for the Z80 to do something useful with yet.

Probably next I'll need to make the debug probe able to operate as a bus responder and do something like act as a Z80 DART, in order to give myself a serial terminal on the Z80.
Looking good! Do you have both a laser cutter and a 3D printer, or is there a local makerspace or facility that you use? What's holding the plexiglass/Lexan to what look like separate 3D printed top and bottom components?

I realize this project is more opportunistic than anything, but have you come up with a motivating application that you want to use it for?
I use the maker space at work (what a luxury! Intended only for non-work projects!) for most physical fabrication, where there is a fleet of Prusa mk4 printers and an Epilog laser. In the above photo, all the case parts are press-fit; the current batch of "3mm" acrylic is actually more like 3.2mm thick so it's a tighter fit than I'd like. I intend to eventually have posts between the mounting holes on the top and bottom so each side screws into the posts to hold it all together.

The farthest I've gotten on any real use for this is that it would be neat to get it running CP/M. The backplane-centric design means I have the freedom to build a variety of cards, so I might build a video card and so forth.
I want to ask is the eeprom at the start or end of the address space ?

Cause if its at the end then you have a cpm compatible computer. Because cpm can be configured to use practically any amount of ram as long as that ram starts at the start of the address space. Plus you can save a bit of space by having the hardware specific code that u need to suply cpm with at the start of the eeprom instead of the end of ram.
The ROM is at the beginning of the address space, but can be disabled in software so it can operate as a boot code by loading things into RAM then disabling the ROM and enabling RAM in the address range the ROM occupies.

Given Z80s always reset to address 0, I imagine this is similar to how most CP/M compatible systems supporting 64k of RAM operate.

I opted to build it this way for several reasons:
  • For non-CP/M applications I can just write a program to ROM and run it, with full ability to handle interrupts and so forth.
  • Because the ROM is an FRAM (FeRAM if you prefer), it has exceptional write endurance but also limited read endurance because reads are destructive (like DRAM). As I noted in a past discussion, using FRAM as working memory rather than non-volatile storage will quickly run through its rated life in a surprisingly short amount of time (~days, possibly). Reading it ~once per boot should greatly improve the longevity.
  • Having the option to disable the ROM allows other memory-mapped peripherals to be implemented if I choose, as long as they fit in the lower 32k of memory.
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 1
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement