In this video I build a Z8000 (aka Z-8000) computer.
I stumbled across the Zilog Z8000 on eBay one day and decided I had to go off and build myself a Z8000 single board computer. I couldn’t fit it all on one board, so I figured I’d made it a two-board computer, then I figured why not add some displays and it because a 3-board computer. Well, next month it’ll probably be a 5-board computer!
Anyhow, the Z8000 was conceived by Zilog back in 1979. It was a 16-bit microprocessor, intended to compute in the 16-bit microcomputer market. It’s contemporaries in the 80s would have been the 8086/8088 and the Motorola 68000. The Z-8000 didn’t quite make it, it was only used in a very few computers. x86 won out, Z-8000 lost.
Zilog Z-8000 Background
The Zilog Z8000 is a 16-bit microprocessor. There’s two versions:
- Z8001. Supports segmented memory, up to 128 segments via the SN0-SN7 pins on the microprocessor. 128 segments with 64 KB of memory per segment allows easy access to 8 MB. An additional SEGT pin allows for the CPU to trap accesses to nonexistent memory. This is a 48 pin DIP IC.
- Z8002. Does not support segmented memory. Omits the SN0-SN7 pins and the SEGT pin. Allows easy access to 64 KB. This is a 40 pin DIP.
I’d suggest you avoid the Z8002 and only use the Z8001, with the segmentation support it’s a much more versatile design. Nevertheless, both CPUs do support two additional ways to get memory, and a clever enough person could probably find a way to get a Z8002 to access 256 KB, or a Z8001 to access 32 MB of memory.
- Split I/D mode. Allows instructions and data to be sent to different pages.
- Normal/System mode. Allows user mode code to be sent to different pages than system code.
Other nice features include a plethora of registers that can easily be combined to form larger registers. For example, the 16-bit R2 and R3 registers can be combined to form a 32-bit RR2. R0/R1/R2/R3 could be combined to form a 64-bit RQ0 register. Memory transfers generally occur 16-bits at a time, with provision to transfer 8-bits at a time for byte operations.
Very few Zilog Z8000 computers were manufactured that used the Z8000. A notable one was the Olivetti M20, which still has some software and other resources up on the web.
Scott’s Z8000 Computer, Modular Design
The project is designed into a series of separate modules, with the intention that they could be mixed and matched, and the boards small enough that they can be manufactured at a reasonable cost. A 68-bin BUS connects the boards together, mostly exposing a 16-bit data bus, 20-bit address bus, and some selects for ROM and RAM. The boards are as follows:
- CPU Board. Holds the CPU, Clock, address and data buffers, and bus and memory selection logic.
- Memory and Serial Board. 1 MB of Flash, 1 MB of RAM, and two serial ports using a Z8530 SCC.
- Display Board. Eight TIL311 displays for debugging info, with some provision for input switches and buttons
The CPU board uses 74F373 octal latches to latch addresses off the AD bus onto the A bus. The latch occurs on the edge of AS, and an inverter is used to get the appropriate edge. 74F245 transceivers are used for the data bus. The data bus can be live all the time, but it needs to have the direct set appropriately, and this is handled by the TX/~RX pin to the the 245s, which comes from the PLD devices.
The Memory PLD is an ATF22V10C programmable logic device that is used to do memory addressing. See my github repo for the PLD listing. This handles decoding of the segment registers, together with the state pins, and the Byte/Word control line to select the appropriate RAM/ROM ics and to set the upper address bits (A16-A19). Using a PLD allows the segments to be easily reconfigured by the designer, and implement exotic schemes such as separate instruction and data segments. The PLD makes a number of segments available and the listing will indicate which is which.
The Bus PLD is another ATF22V10C. It’s job is primarily to decode the signals necessary to generate IOR, IOW (IO read and Write), and MEMR, MEMW (Memory Read and Write). Again, the PLD listing will explain exactly how this is done. It also generates a few chip selects for common peripherals.
Boot is handled using a flip-flop scheme borrowed from the Olivetti M20. When reset, the Z8000 will read the following:
- FCW from address 00:0002
- Segment from address 00:0004
- Offset from address 00:0006
Then it’ll jump to that segment:offset and begin executing. The boot flip-flop on reset will assert a BOOT signal. The Memory PLD when BOOT is asserted will map the last bank (0x1F) of the Flash into segment 0. This causes the initialization vector to be read from Flash. As soon as A3 becomes positive, the flipflop will deassert the BOOT signal and memory addressing returns to normal (RAM at Segment 0). It’s entirely possible that a simpler scheme could be used, for example leaving the Flash at segment 0 and telling CP/M to use segments other than 0. However, the Olivetti approach seemed elegant and flexible.
A MAX811 IC may optionally be added to handle power-on reset. Without the MAX811, the CPU will sit there like a lump of coal until someone manually presses the rest button.
The LED is attached to the MO line on the CPU that was intended by Zilog for machine-to-machine communication. The “MSET” instruction with light the LED, and “MRES” will extinguish it. I have created the binaries LEDON.Z8K and LEDOFF.Z8K so that this can be done by the user.
The memory board uses two AS6C4008 SRAM chips for RAM, and two 39SF040 Flash memories for ROM. These are split into low (D0-D7) and high (D8-D15) on the data bus as appropriate. The RAM and ROM are selected by select lines that come from the CPU board.
Also on this board is a Z8530 SCC for serial IO. The SCC is wired to a pair of 6-pin headers than can be used with typical FTDI USB-to-TTL cables. A MAX202 is optionally provided to enable a physical DB-9 serial port for the RS-232 purists out there.
The display board uses TIL311 displays. These displays each take a 4-bit value and display a single hex digit. Eight displays allow four bytes to be displayed. The display board can be jumpered to display either the ports (SIO, PIO, or IDE) from the CPU board, or they can use a custom port selected by dip switches on a 74HCT688., In my configuration, I set the dip switches to place the display board at ports 0x50 – 0x53.
The display board also includes an input transciever than can allow a bank of dipswitches to be read by a user program. It’s my intention to use those dip switches to enable the bootloader to boot different functions. Four Cherry MX footprints are wired in parallel with four of the dip switches to enable momentary buttons. The input logic is optional, and I have not implemented it in the original video.
TODO: Pictures of PCBoards
Note that 68-pin headers are either not readily available or prohibitively expensive. If you’re building the clover configuration like I did, just use 80-pin right-angle headers and sockets and cut them down to size. Yes, even the sockets can easily be cut to size with a pair of flush cutters. If you’re building the stacking configuration, then I’d suggest using 40-pin raspberry pi stacking headers, and cut one down to make it fit the 68-pins.
I borrowed the boot scheme from the design of the Olivetti M20. The boot sequence is as follows:
- BOOT is asserted by the flip-flop.
- When BOOT is asserted, the Memory PLD maps segment 0 to Flash page 0F. Segment 60 is always mapped to Flash page 0F regardless of BOOT state. This is the last page in flash.
- Page 0F in flash (current mapped to segment 0) contains the initialization vector that the Z-8000 will use to determine where to start executing. The Z-8000 always looks for this in Segment 0.
- The loader’s initialization vector causes the CPU to jump to 60:000C.
- Shortly thereafter, A3 becomes positive, and the boot flip-flop deasserts the BOOT signal. The Memory PLD now maps Segment 0 to RAM page 0. Segment 60 remains mapped to Flash page 0F.
- The loader copies approximately 32KB from 60:0200 to 00:0000. This 32KB contains 4sun5bu’s z8kmon program.
- The loader jumps to 00:0008., starting z8kmon.
- Z8kmon displays a prompt over serial.
- The user presses “B” to boot from flash. z8kmon copies 32KB from Flash page 0, to 03:0000 and then jumps to 03:0000. This 32KB is the start of the flash disk image, and the first thing in the image is the CP/M system code.
- CP/M begins.
CP/M 1.1 is used. 4sun5bu in his github page includes a BIOS that supports IDE and the SCC. I modified this BIOS to use a Flash-based disk. At some point I’ll re-enable the IDE support, and maybe add floppy support.
CP/M-8000 supports (at least) two different modes for loading user programs:
- Nonsegmented shared I/D mode. Both instructions and data are in the same segment and it maps to the same memory page. You can read and write the instruction areas just like any other data. User programs are not aware of multiple segments.
- Nonsegmented split I/D mode. Both instructions and data are in the same segment, but instruction fetches map to a different memory page than data and stack accesses. User programs are not aware of multiple segments. The immediate benefit to this mode is that you gain up to double the space (64K instruction, 64K data) without having to write software that understands segments. A special third segment must be provided, the I-as-D segment, that allows CP/M itself access to the instruction page, so that program text can be loaded by the OS.
CP/M-8000 defines several different memory regions and these regions determine what memory segments are available.
|1||Shared I/D||1||1||For programs that are shared I/D|
|2||Split I/D, Instruction||11||1||For programs that are split I/D, instructions are here|
|3||Split I/D, Data||11||9||For programs that are split I/D, data is here|
|4||Split I/D, I-as-D||19||1||For programs that are split I/D, segment to use to write to the instruction page.|
|5||Special||0||0||Used by DDT. DDT loads itself here, so as not to interfere with the user program.|
|n/a||CPM System||3||3||Where the CP/M system (BIOS, etc) is loaded. The system is not replaced by user programs when they are loaded. It is resident all the time.|
Note that it’s up to the designer to choose these segment and page numbers, and the choices above reflect my board. Some programs (ZCC, ED) will load in split I/D mode. Others (PIP, STAT) will load in shared I/D mode. Typically only one user program is loaded at a time, so it’s technically not a problem that regions 1 and 2 are sharing the same page. These region numbers are described in the official CP/M-8000 documentation.
While you could add additional regions, and software could be written to examine the memtbl and make use of additional regions, it’s not clear that any existing CP/M-8000 software does this. Given that fact, the practical memory utilization of CP/M-8000 is 192KB — 64 KB system, 64 KB user instruction, and 64 KB data. The sole exception is DDT, which will allocate an additional 64KB, for a grand total of 256KB.
The ROM Disk
I implemented a ROM disk using the same 39SF040 flash memory that holds the BIOS. Technically this is Flash, but I’ll call it ROM anyway as that’s how we’re using it. 64KB (1 page) of ROM is reserved for the bootloader and monitor. That leaves 960KB for the ROM disk.
The ROM disk is implemented in the Bios. Rather than reading from external storage, it does a simple computation to convert the track and sector number to an offset, and transfers the data from RAM.
The RAM Disk
I also implemented RAM disk using the same AS6C4008 SRAM chips that are used for program and system memory. 256KB are required for the system, and I reserved another 64KB for a hypothetical second page (in case I want to try to make some segmented programs some day). That leaves 705KB for the RAM disk.
It’s implemented almost identically to the ROM disk — reads/writes are diverted to the ram disk code, and data is simply transferred between memory. The RAM disk uses pages 4,5,6,7,8,10,11,12,13,14,15 — page 9 is omitted because it’s part of the Split-I/D scheme.
Hunting the Wumpus!
If you watched the video, you might have noticed that I had some bugs where ED.Z8K and some other tools wouldn’t run. Those turned out to be a problem with memory segmentation for split I-D spaces, which I fixed. Then I managed to find some olivetti disks with wump.z8k on them… and now we have playing games on the Z-8000.
Z8001 Machine Code Monitor Ver.0.3.0 Modified by Scott Baker (email@example.com) for Scott's Z-8000 Computer Press 'B' to boot or 'H' for help b Boot CP/M from Flash… Jumping to CP/M start CP/M-8000 BIOS ver.0.11.smbaker1 CP/M-8000(tm) Version 1.1 12/19/84 Copyright (c) 1984, Digital Research Inc. A>wump Instructions? (y-n) n You are in room 19 Bats nearby There are tunnels to 2 16 18 Move or shoot (m-s) s Give list of rooms terminated by 0 2 16 18 0 You are in room 19 Bats nearby There are tunnels to 2 16 18 Move or shoot (m-s)
I ported tinybasic-for-arduino to the Z8K. It compiles and build using the zcc compiler that’s part of the CP/M-8000 distribution on the Z8000.
A>tbasic Z8000 TinyBasic, www.smbaker.com 32606 bytes free. OK 10 print "scott was here" 20 let a=1 30 let b=2 40 print a+b run scott was here 3 OK
Tinybasic is included in the rom disk image, so you too can jump right into writing BASIC programs on your CP/M-8000 computer. While I wouldn’t recommend this particular BASIC for serious programming, I’d use the zcc compiler instead, it is useful for quick demos.
I ported the mojozork Z-machine by Ryan C. Gordon to the Z8000. Like tinybasic, I compiled this (after significantly modifying it) right on the Z8000 computer. So now you can play Zork also. It helps to have a few things to do on the computer.
A>zork z8kzork based on mojozork by Ryan C. Gordon. modifed for cp/m-8000 by Scott M Baker, www.smbaker.com ZORK I: The Great Underground Empire Copyright (c) 1981, 1982, 1983 Infocom, Inc. All rights reserved. ZORK is a registered trademark of Infocom, Inc. Revision 88 / Serial number 840726 West of House You are standing in an open field west of a white house, with a boarded front door. There is a small mailbox here. >open mailbox Opening the small mailbox reveals a leaflet. >take leaflet Taken. >read leaflet "WELCOME TO ZORK! ZORK is a game of adventure, danger, and low cunning. In it you will explore some of the most amazing territory ever seen by mortals. No computer should be without one!"
The program takes a single argument that is the name of the game file to load, and it loads zork1.dat by default. With the appropriate data file, it can also play Zork 2, Zork 3, or any other Infocom game that is compatible with version 3 of the z-machine.
- Board does not boot / does not come out of reset. Make sure you’re using a high-quality power cable to supply the board. A poor quality cable may drop as much as a half of a volt. If you’re using the MAX811, it is a voltage-triggered device — too little voltage and it will keep the Z-8000 in reset. If you have less than 4.38V at the pins to the MAX811, it will not leave reset. As an alternative, you can leave the MAX811 out of circuit, solder the nearby solder jumper, and you will have to manually reset the CPU each time you power it on.
- Board is flaky / problematic / crashes often. I found that the logic family matters. When using 74F logic, the same family that 4sun5bu chose, I had flaky performance. Switching to 74HCT resolved the problem entirely. It’s possible that the higher energy consumption of the F chips, together with imperfect PCB routing of the power traces, could have been leading to intermittent issues.