The Sinclair ZX-81 on Linux
Author: Michael Minn (see michaelminn.com for current address)
September 7, 2004 (rev October 1, 2009)
Instructions for loading and emulating ZX-81 programs on Linux machines
1. The Sinclair ZX-81
The Sinclair ZX81 was an extremely spartan home computer produced in the early 1980's. It was the first home computer available for under $100 and you got what you paid for.
- The entire CPU/keyboard unit was only 8"x8"x1".
- It used a TV as a display.
- It had a membrane keyboard that was impossible to touch-type
- 3 Mhz, 8-bit microprocessor
- It featured a whopping 2K of RAM (expandable)
- Programs were stored on very tempermental cassette tapes.
- BASIC was its native language, which it stored in a custom character set to reduce memory requirements.
- It was relatively easy to include inline Z-80 assembly code
- Video display was handled by the CPU with computation occuring in the 25 video retrace lines...although there was a "Fast Mode" in which the display went blank while the program was executing.
Expansion memory and peripherals were connected via an edge card connector on the right rear of the case. This was a particularly sketchy means of expansion and the slightest bump would cause the machine to crash. I had a 16K and 64K expansion modules and also homebrewed an A/D D/A board for doing some rudamentary digital audio.
Despite its drawbacks, it was a remarkable feat of economy and is often remembered fondly by it's owners well over two decades past it's heyday. Indeed, it's performance (except for the cassette tapes) was on par with the Apple IIe at considerably less cost. A clone of the ZX-81 was mass-marketed in the United States as the Timex TS1000.
There are numerous ZX-81 resources on the web. A good starting point is the Home Page for the ZX-81 Webring.
2. ZX-81 Emulation on Linux
While there are a number of ZX-81 emulators out there for DOS, as of this writing I only know of one that works under Linux: Z81, by Russell Marks, based on xz80 by Ian Collier. Z81 uses a Z-80 processor emulator running the original ZX81 ROM code. I have created a modified package that works well using the X interface which you can download here.
Complation is with a simple "make" to create a standalone executable "xz81". There is also a "make install" option if you want to place the executable permanently in /usr/local/bin. To compile you will need to have the X development libraries and headers installed as well as the Xt toolkit and X extensions. Ubuntu folks can get all these by installing (apt-get) the libx11-dev, libxt-dev, x11proto-xext-dev, and libxext-dev packages.
The original package was available here, but I had compilation issues (solved in my package) and the hosting account now seems to be closed.
ZX-81 programs were stored on cassette tapes. Program loading was often quite a challenge, especially when using cheap portable cassette decks.
z81 loads and saves files in the same .p file format that the Xtender emulator for DOS uses. .p files are exact binary copies of the file format used for storing programs on cassette (except the first character of the cassette data is skipped). There are numerous sites on the web that have games and other programs as .p files you can download.
Programs are loaded from .p files using the ZX-81 load command (the J key) followed by the name of the .p file enclosed in quotes and without the .p extension. z81 uses the same crazy key assignments as the ZX-81, so not all of the modern keyboard keys are valid (such as quotes). An image template of the keyboard layout called zx81big.gif is included in the emulater package.
For example to load the tetris.p game program included with the emulator, use the following keystrokes from the "K" prompt:
J <shift>P T E T R I S <shift>P
The F10 key kills the emulator
3. ZX-81 Programs from Cassette
If you still have cassettes of ZX-81 programs it is usually possible to play them into .wav sound files, run them through a convertor and load them into an emulator. The trick with .wav files is getting them into a clean enough state for the convertor programs to be able to interpret them. This is made more complicated since cassette tape is a poor medium for long-term storage and tapes often have dropouts and other degradations of age and/or poor care. However, with some work, the .wav files can usually be massaged into a state that can be cleanly read by a convertor program.
Although there is an existing .wav to .p file convertor, I had problems getting it to work reliably and I wrote my own. mmzx81 can both convert .wav files to .p files and list existing .p files. Download the mmzx81.cpp file here.. The file needs no libraries other than libc and will compile with g++:
g++ -o mmzx81 mmzx81.cpp cp mmzx81 /usr/local/bin
If you need a Makefile, there is one here.
3.2. Cassette Data Format
To work successfully with audio files, it will be helpful if you understand how data is stored on the cassettes.
Data is stored in audio format as pulses of 3.2Khz tones. Long pulses (8 cycles) represent binary 1 and short pulses (4 cycles) represent binary 0. The stream is a succession of 8-bit bytes with the most-significant bits sent first. There is no error recovery or synchronization built into the protocol...which makes it simple to read but also explains why loading from cassettes often failed.
The data saved to cassette is basically a dump of all the volatile memory with system variables, programs, display and variables. The memory layout is listed in the ZX-81 manual, which is available online. RAM started at memory address 0x4000 and the data stored on the tape started from memory address 0x4009 (i.e. skipping the first nine bytes of RAM). At around 40-50 bytes/second, tape storage was painfully slow by modern standards. But the programs were so small they could usually be loaded in a minute or two.
3.3 Steps for Conversion
While mmzx81 includes some rudamentary signal processing, you will probably want to use a digital audio file editor to clean up the audio before running through mmzx81.
Record the cassette into a .wav file: Data should be recorded mono at 44.1 Khz for best results. mmzx81 expects .wav format.
Below is an example of the beginning of a rather dirty ZX-81 file recorded to a cheap portable deck. The pulse just after 0:02 is the start of the data. Actually, the visible pulse is the flare caused by the start of the audio pumping the automatic volume control, before it settles down a few ms later. Everything before that is noise from the cheap recorder.
Below is a closeup of the start of the data, showing alot of low frequency hum that obfuscates the data. It is also possible to see the individual 3.2Khz cycles that make up the data pulses. The first byte is 10101101 or 0xAD.
Bandpass or highpass filter to remove hum and noise: Run the data through a highpass filter with a cutoff around 2Khz to remove hum. Below is the same area with the hum removed:
Remove leading and trailing noise: To avoid confusing the convertor, The leading and trailing noise should be clipped off. The end of saved programs was usually followed by a buzzing sound which is visible below:
Compression and Normalization: It is easiest to distinguish pulses when the all exceed a given threshold (around 6db). So, the file should be compressed and normalized.
First Pass of MMZX81: At this point, you should try running mmzx81 and see if you get a clean load. The program will issue diagnostic lines as it reads the data, then lists the complete program. If the load succeeds, you should get something like this:
 1020 PRINT AT X,0 ;"################################" [46a6] 1040 NEXT X [46ad] 1050 RETURN Variables ------------------ [49cc] X (control variable) [49de] A (number) ----------------------- 2524 bytes listed --------------------
Errors are usually caused by dropouts. These can often be seen by looking through the file.
The error message printed by mmzx81 may also be helpful in finding the general area of the dropout, although it often does not become obvious that an error has occurred until somewhat later in the program, so you should search before the sample number given:
000ac8 @ sample 3096499: 2d 16 10 10 cf 10 3f 2c H - ( ( INT ( Z G 000ad0 @ sample 3105917: c0 e8 e0 e3 f4 3a 40 00 ' CONT STEP STOP POKE U RND 000ad8 @ sample 3114865: 00 00 88 88 b8 e8 e0 e3 |8| |8| s CONT STEP STOP 000ae0 @ sample 3123537: f4 3a 40 00 00 00 8b b0 POKE U RND " k 000ae8 @ sample 3132002: .wav file error somewhere around sample 3140825
Closer examination will usually reveal some residual signal that can be used to reconstruct the bits:
In this case the three missing bits appear to be 010, which can either be normalized or copied from earlier bits.
Dropouts may not be so drastic and may simply be a drop behold the 6db threshold. These can usually be corrected by simply normalizing the affected area.
Although mmzx81 only uses mono data, or just the left channel if stereo data is provided, it may be helpful to record the .wav file in stereo. Severe dropouts in one channel may not affect the other channel and in those cases it may be possible to correct otherwise unreadable files by copying data between channels to create one good channel that can be fed into mmzx81.
Alternative Software: Bert van Oortmarssen has another package out there for converting to/from .wav/.p files, although it is even more crude than mmzx81. However, if you don't like my software, you're welcome to give zxtools a try. The page hasn't been updated since 1998.