I've really enjoyed reading some of Matthew Garrett's entries about legacy PC hardware features - specifically the cute hack involving A20 and the keyboard controller. Reading things like that really takes me back.
I remember when the z80 was cutting edge, and I discovered you could switch to a whole new set of shadow registers via "exx" and "ex af,af'". I remember using undocumented opcodes, and even now I can assemble and disassemble simple z80 machine code. (Don't get me started on the speedlock protectors and their fiendish use of the R register; that'll stick in your mind if you cracked it. I did.)
I remember being introduced to the PC, seeing subdirectories appear in MS-DOS 2.0, network redirectors appearing in DOS 3.x, and support for big hard drives appearing in MS-DOS 4.0.
I remember the controvosy over the AARD code in the betas of Windows 3, and the focus that was given in the book "Undocumented DOS" which mostly focussed upon the "list of lists". (At that time I'd have been running GEM on an IBM XT with hercules (monochrome) graphics.)
I remember learning about that a .COM file was a flat image, limited to 64k which loaded at offset 100h, to accomodate the PSP (program segement prefix) for compatbility with CP/M (something I've never seen, never used, and known nothing about. I just know you could use the file control blocks to get simple wildcard handling for your programs "for free")
I wrote simple viral code which exploited this layout, appending new code to end of binary, replacing the first three bytes with a jump to the freshly added code. Later you could restore the original bytes back to their original contents and jump back to 100h (I even got the pun in the name of 40Hex magazine.)
I recall when COM files started to go out of fashion and you had to deal with relocation and segment fixups. When MZ was an important figure.
I even remember further back to when switching to protected mode was a hell of tripple-faults, switching back from protected mode to real mode was "impossible" and the delight to be had with the discovery and exploitation of "unreal mode".
All these things I remember .. and yet .. they're meaningless to most now, merely old history. Adults these days might have grown up and reached age 20 having always had Windows 95 - never having seen, used, or enjoyed MS-DOS.
How far have we come since then? In some ways not far at all. In other ways the rise of technology has been exponential.
Back then when I was doing things I'd not have dreamed I could update a webpage from a mobile phone whilst trapped upon a stalled train.
There are now more mobile phones than people in the UK. In some ways that's frightening - People miss the clear seperation between home & work for example - but in other ways .. liberation.
I have no predictions for the future, but it amazing how far we've come just in my lifetime; and largely without people noticing.
The industrial revolution? Did that happen with people mostly not noticing? Or was there more concious awareness? Food for thought.
I've been tinkering with hardware for a couple of years now, most of this
is trivial stuff if I'm honest, for example:
Wiring a display to a WiFi-enabled ESP8266 device.
Making it fetch data over the internet and display it.
Hooking up a temperature/humidity sensor to a device.
Submit readings to an MQ bus.
Off-hand I think the most complex projects I've built have been complex
in terms of software. For example I recently hooked up a 933Mhz radio-receiver
to an ESP8266 device, then had to reverse engineer the protocol of the device
I wanted to listen for. I recorded a radio-burst using an SDR dongle on
my laptop, broke the transmission into 1 and 0 manually, worked out the payload
and then ported that code to the ESP8266 device.
Anyway I've decided I should do something more complex, I should build
"a computer". Going old-school I'm going to stick to what I know best
the Z80 microprocessor. I started programming as a child with a ZX Spectrum which is built around
Initially I started with BASIC, later I moved on to assembly language mostly
because I wanted to hack games for infinite lives. I suspect the reason I don't
play video-games so often these days is because I'm just not very good without
Anyway the Z80 is a reasonably simple processor, available in a 40PIN DIP format. There are the obvious connectors for power, ground, and a clock-source to make the thing tick. After that there are pins for the address-bus, and pins for the data-bus. Wiring up a standalone Z80 seems to be pretty trivial.
Of course making the processor "go" doesn't really give you much. You can wire it up, turn on the power, and barring explosions what do you have? A processor executing NOP instructions with no way to prove it is alive.
So to make a computer I need to interface with it. There are two obvious things that are required:
The ability to get your code on the thing.
i.e. It needs to read from memory.
The ability to read/write externally.
i.e. Light an LED, or scan for keyboard input.
I'm going to keep things basic at the moment, no pun intended. Because I have no RAM, because I have no ROM, because I have no keyboard I'm going to .. fake it.
The Z80 has 40 pins, of which I reckon we need to cable up over half. Only the arduino mega has enough pins for that, but I think if I use a Mega I can wire it to the Z80 then use the Arduino to drive it:
That means the Arduino will generate a clock-signal to make the Z80 tick.
The arduino will monitor the address-bus
When the Z80 makes a request to read the RAM at address 0x0000 it will return something from its memory.
When the Z80 makes a request to write to the RAM at address 0xffff it will store it away in its memory.
Similarly I can monitor for requests for I/O and fake that.
In short the Arduino will run a sketch with a 1024 byte array, which the Z80 will believe is its memory. Via the serial console I can read/write to that RAM, or have the contents hardcoded.
I thought I was being creative with this approach, but it seems like it has been done before, numerous times. For example:
Anyway I've ordered a bunch of Z80 chips, and an Arduino mega (since I own only one Arduino, I moved on to ESP8266 devices pretty quickly), so once it arrives I'll document the process further.
Once it works I'll need to slowly remove the arduino stuff - I guess I'll start by trying to build an external RAM/ROM interface, or an external I/O circuit. But basically:
Hook the Z80 up to the Arduino such that I can run my own code.
Then replace the arduino over time with standalone stuff.
The end result? I guess I have no illusions I can connect a full-sized keyboard to the chip, and drive a TV. But I bet I could wire up four buttons and an LCD panel. That should be enough to program a game of Tetris in Z80 assembly, and declare success. Something like that anyway :)
Expect part two to appear after my order of parts arrives from China.
My previous post on the subject of building a Z80-based computer briefly
explained my motivation, and the approach I was going to take.
This post describes my progress so far:
On the hardware side, zero progress.
On the software-side, lots of fun.
To recap I expect to wire a Z80 microprocessor to an Arduino (mega). The
arduino will generate a clock-signal which will make the processor "tick".
It will also react to read/write attempts that the processor makes to
access RAM, and I/O devices.
The Z80 has a neat system for requesting I/O, via the use of the IN and OUT
instructions which allow the processor to read/write a single byte to one of
255 connected devices.
With the appropriate tools available I could write some simple code. I implemented two I/O routines in the emulator, one to read a character from STDIN, and one to write to STDOUT:
IN A, (1) ; Read a character from STDIN, store in A-register.
OUT (1), A ; Write the character in A-register to STDOUT
With those primitives implemented I wrote a simple script:
; Simple program to upper-case a string
; show a prompt.
ld a, '>'
out (1), a
; read a character
jp z, quit
; is it lower-case? If not just output it
jp nc, output
; convert from lower-case to upper-case. yeah. math.
sub a, 32
; output the character
out (1), a
; repeat forever.
And that's where I'll leave it for now. When I have the real hardware I'll
hookup some fake-RAM containing this program, and code a similar I/O handler
to allow reading/writing to the arduino's serial-console. That will allow
the same code to run, unchanged. That'd be nice.
I've got a simple Z80-manager written, but since I don't have the chips yet
I can only compile-test it. We'll see how well I did soon enough.
This is part three in my slow journey towards creating a home-brew
Z80-based computer. My previous
demonstrated writing some simple code, and getting it running under an
emulator. It also described my planned approach:
Hookup a Z80 processor to an Arduino Mega.
Run code on the Arduino to emulate RAM reads/writes and I/O.
Profit, via the learning process.
I expect I'll have to get my hands-dirty with a breadboard and naked
chips in the near future, but for the moment I decided to start with the
least effort. Erturk Kocalar has a website where he
(read: expansion-boards) which contain a Z80, and which is designed to
plug into an Arduino Mega with no fuss. This is a simple design, I've
seen a bunch of people demonstrate how to wire up by hand, for example
Anyway I figured I'd order one of those, and get started on the easy-part, the software. There was some sample code available from Erturk, but it wasn't ideal from my point of view because it mixed driving the Z80 with doing "other stuff". So I abstracted the core code required to interface with the Z80 and packaged it as a simple library.
The end result is that I have a z80 retroshield library which uses an Arduino mega to drive a Z80 with something as simple as this:
// Our program, as hex.
unsigned char rom =
0x3e, 0x48, 0xd3, 0x01, 0x3e, 0x65, 0xd3, 0x01, 0x3e, 0x6c, 0xd3, 0x01,
0xd3, 0x01, 0x3e, 0x6f, 0xd3, 0x01, 0x3e, 0x0a, 0xd3, 0x01, 0xc3, 0x16,
// Our helper-object
// RAM I/O function handler.
char ram_read(int address)
return (rom[address]) ;
// I/O function handler.
void io_write(int address, char byte)
if (address == 1)
// Setup routine: Called once.
// Setup callbacks.
// We have to setup a RAM-read callback, otherwise the program
// won't be fetched from RAM and executed.
// Then we setup a callback to be executed every time an "out (x),y"
// instruction is encountered.
Serial.println("Z80 configured; launching program.");
// Loop function: Called forever.
// Step the CPU.
All the logic of the program is contained in the Arduino-sketch, and all
the use of pins/ram/IO is hidden away. As a recap the Z80 will make
requests for memory-contents, to fetch the instructions it wants to
execute. For general purpose input/output there are two instructions
that are used:
IN A, (1) ; Read a character from STDIN, store in A-register.
OUT (1), A ; Write the character in A-register to STDOUT
Here 1 is the I/O address, and this is an 8 bit number. At the moment
I've just configured the callback such that any write to I/O address 1
is dumped to the serial console.
Anyway I put together a couple of examples of increasing complexity, allowing me to prove that RAM read/writes work, and that I/O reads and writes work.
I guess the next part is where I jump in complexity:
I need to wire a physical Z80 to a board.
I need to wire a PROM to it.
This will contain the program to be executed - hardcoded.
I need to provide power, and a clock to make the processor tick.
With a bunch of LEDs I'll have a Z80-system running, but it'll be
isolated and hard to program. (Since I'll need to reflash the
The next step would be getting it hooked up to a serial-console of some
sort. And at that point I'll have a genuinely programmable standalone
In the past, I've talked about building a Z80-based computer. I made some progress towards that goal, in the sense that I took the initial (trivial steps) towards making something:
I built a clock-circuit.
I wired up a Z80 processor to the clock.
I got the thing running an endless stream of NOP instructions.
No RAM/ROM connected, tying all the bus-lines low, meaning every attempted memory-read returned 0x00 which is the Z80 NOP instruction.
But then I stalled, repeatedly, at designing an interface to RAM and ROM, so that it could actually do something useful. Over the lockdown I've been in two minds about getting sucked back down the rabbit-hole, so I compromised. I did a bit of searching on tindie, and similar places, and figured I'd buy a Z80-based single board computer. My requirements were minimal:
It must run CP/M.
The source-code to "everything" must be available.
I want it to run standalone, and connect to a host via a serial-port.
With those goals there were a bunch of boards to choose from, rc2014 is the standard choice - a well engineered system which uses a common backplane and lets you build mini-boards to add functionality. So first you build the CPU-card, then the RAM card, then the flash-disk card, etc. Over-engineered in one sense, extensible in another. (There are some single-board variants to cut down on soldering overhead, at a cost of less flexibility.)
The advantage of this design is that it loads code from a USB stick, making it easy to transfer files to/from it, without the need for a compact flash card, or similar. The downside is that the system has only 64K RAM, meaning it cannot run CP/M 3, only 2.2. (CP/M 3.x requires more RAM, and a banking/paging system setup to swap between pages.)
When the system boots it loads code from an EEPROM, which then fetches the CP/M files from the USB-stick, copies them into RAM and executes them. The memory map can be split so you either have ROM & RAM, or you have just RAM (after the boot the ROM will be switched off). To change the initial stuff you need to reprogram the EEPROM, after that it's just a matter of adding binaries to the stick or transferring them over the serial port.
In only a couple of hours I got the basic stuff working as well as I needed:
A z80-assembler on my Linux desktop to build simple binaries.
An installation of Turbo Pascal 3.00A on the system itself.
The Zork trilogy installed, along with Hitchhikers guide.
I had some fun with a CP/M emulator to get my hand back in things before the board arrived, and using that I tested my first "real" assembly language program (cls to clear the screen), as well as got the hang of using the wordstar keyboard shortcuts as used within the turbo pascal environment.
I have some plans for development:
Add command-line history (page-up/page-down) for the CP/M command-processor.
Add paging to TYPE, and allow terminating with Q.
Nothing major, but fun changes that won't be too difficult to implement.
Since CP/M 2.x has no concept of sub-directories you end up using drives for everything, I implemented a "search-path" so that when you type "FOO" it will attempt to run "A:FOO.COM" if there is no file matching on the current-drive. That's a nicer user-experience at all.
I also wrote some Z80-assembly code to search all drives for an executable, if not found in current drive and not already qualified. Remember CP/M doesn't have a concept of sub-directories) that's actually pretty useful:
I've also written some other trivial assembly language tools, which was surprisingly relaxing. Especially once I got back into the zen mode of optimizing for size.
I forked the upstream repository, mostly to tidy up the contents, rather than because I want to go into my own direction. I'll keep the contents in sync, because there's no point splitting a community even further - I guess there are fewer than 100 of these boards in the wild, probably far far fewer!
In my previous post I wrote about how I'd been running CP/M on a Z80-based single-board computer.
I've been slowly working my way through a bunch of text-based adventure games:
The Hitchhiker's Guide To The Galaxy
Along the way I remembered how much fun I used to have doing this in my early teens, and decided to write my own text-based adventure.
Since I'm not a masochist I figured I'd write something with only three or four locations, and solicited facebook for ideas. Shortly afterwards a "plot" was created and I started work.
I figured that the very last thing I wanted to be doing was to be parsing text-input with Z80 assembly language, so I hacked up a simple adventure game in C. I figured if I could get the design right that would ease the eventual port to assembly.
I had the realization pretty early that using a table-driven approach would be the best way - using structures to contain the name, description, and function-pointers appropriate to each object for example. In my C implementation I have things that look like this:
desc: "A small generator.",
A bit noisy, but simple enough. If an object cannot be picked up, or dropped, the corresponding entries are blank:
edesc: "The desk looks solid, but old."},
Here we see something that is special, there's no description so the item isn't displayed when you enter a room, or LOOK. Instead the edesc (extended description) is available when you type EXAMINE DESK.
Anyway over a couple of days I hacked up the C-game, then I started work porting it to Z80 assembly. The implementation changed, the easter-eggs were different, but on the whole the two things are the same.
Certainly 99% of the text was recycled across the two implementations.
Anyway in the unlikely event you've got a craving for a text-based adventure game I present to you:
To recap my game is a simple text-based adventure game, which you can complete in fifteen minutes, or less, with a bunch of Paw Patrol easter-eggs.
You enter simple commands such as "up", "down", "take rug", etc etc.
You receive text-based replies "You can't see a telephone to use here!".
My code is largely table-based, having structures that cover objects, locations, and similar state-things. Most of the code involves working with those objects, with only a few small platform-specific routines being necessary:
Clearing the screen.
Pausing for "a short while".
Reading a line of input from the user.
Sending a $-terminated string to the console.
My feeling was that I could replace the use of those CP/M functions with something custom, and I'd have done the 99% of the work. Of course the devil is always in the details.
Let's start. To begin with I'm lucky in that I'm using the pasmo assembler which is capable of outputting .TAP files, which can be loaded into ZX Spectrum emulators.
I'm not going to walk through all the code here, because that is available within the project repository, but here's a very brief getting-started guide which demonstrates writing some code on a Linux host, and generating a TAP file which can be loaded into your favourite emulator. As I needed similar routines I started working out how to read keyboard input, clear the screen, and output messages which is what the following sample will demonstrate..
First of all you'll need to install the dependencies, specifically the assembler and an emulator to run the thing:
# apt install pasmo spectemu-x11
Now we'll create a simple assembly-language file, to test things out - save the following as hello.z80:
; Code starts here
; clear the screen
; output some text
ld de, instructions ; DE points to the text string
ld bc, instructions_end-instructions ; BC contains the length
; wait for a key
ld hl,0x5c08 ; LASTK
cp (hl) ; wait for the value to change
jr z, wkey
; get the key and save it
; clear the screen
; show a second message
ld de, you_pressed
ld bc, you_pressed_end-you_pressed
;; Output the ASCII character in A
; loop forever. simple demo is simple
call 0x1601 ; ROM_OPEN_CHANNEL
call 0x0DAF ; ROM_CLS
defb 'Please press a key to continue!'
defb 'You pressed:'
Now you can assemble that into a TAP file like so:
$ pasmo --tapbas hello.z80 hello.tap
The final step is to load it in the emulator:
$ xspect -quick-load -load-immed -tap hello.tap
The reason I specifically chose that emulator was because it allows easily loading of a TAP file, without waiting for the tape to play, and without the use of any menus. (If you can tell me how to make FUSE auto-start like that, I'd love to hear!)
I wrote a small number of "CP/M emulation functions" allowing me to clear the screen, pause, prompt for input, and output text, which will work via the primitives available within the standard ZX Spectrum ROM. Then I reworked the game a little to cope with the different screen resolution (though only minimally, some of the text still breaks lines in unfortunate spots):
The end result is reasonably playable, even if it isn't quite as nice as the CP/M version (largely because of the unfortunate word-wrapping, and smaller console-area). So now my repository contains a .TAP file which can be loaded into your emulator of choice, available from the releases list.
Here's a brief teaser of what you can expect:
Outstanding bugs? Well the line-input is a bit horrid, and unfortunately this was written for CP/M accessed over a terminal - so I'd assumed a "standard" 80x25 resolution, which means that line/word-wrapping is broken in places.
That said it didn't take me too long to make the port, and it was kinda fun.
Recently I've been getting much more interested in the "retro" computers of my youth, partly because I've been writing crazy code in Z80 assembly-language, and partly because I've been preparing to introduce our child to his first computer:
An actual 1982 ZX Spectrum, cassette deck and all.
No hi-rez graphics
Easily available BASIC
And as a nice bonus the keyboard is wipe-clean!
I've got a few books, books I've hoarded for 30+ years, but I'd love to collect some more. So here's my request:
If you have any books covering either the Z80 processor, or the ZX Spectrum, please consider dropping me an email.
I'd be happy to pay €5-10 each for any book I don't yet own, and I'd also be more than happy to cover the cost of postage to Finland.
I'd be particularly pleased to see anything from Melbourne House, and while low-level is best, the coding-books from Usbourne (The Mystery Of Silver Mountain, etc, etc) wouldn't go amiss either.
I suspect most people who have collected and kept these wouldn't want to part with them, but just in case ..