The Sinclair ZX81 is my first computer, in 1982 (kit). It is one of those home computers very popular in the '80s and affordable (£49.95 in kit). Other very popular computers were the Commodore 64 and the Apple II but were far more expensive (ZX81 was ten times cheaper than Apple II Plus). The successor of the ZX81 is the ZX Spectrum even more popular than the ZX81.
The ZX81 computer is based on a 8-bit processor (Z80A
from Zilog or µPD780C
from NEC) at 3.5 MHz with 1 KiB of RAM and 8 KiB of ROM. The 1 KiB of RAM can be expanded with a RAM extension (I had a 16 KiB one). There is only 4 chips (far less than other home computers): the ROM, the RAM, the processor and a custom logic chip made by Ferranti (ULA).
The ZX81 does not include a display. You connect it to an ordinary UHF television set to deliver a monochrome picture. The processor (Z80
) spends most if its time driving the display.
The characters set is not based on ASCII
but is custom and includes only capital letters. It includes also some graphic characters:
Using these characters, it is possible to make some graphics but it is very limited (64x48). Using some tricks (and sometimes some hardware modifications), it is possible to achieve (more or less) 256x192 (more or less because it is not possible to get all pixel combinations per line).
The keyboard is very simple: it is a flat, pressure-sensitive membrane QUERTY keyboard:
Data are saved or loaded with an audio cassette recorder at an average rate of 307 bps (the machine has no built-in storage capabilities). The recorder is connected to two 3.5 mm jacks (EAR and MIC) on the side of the ZX81.
The 8 KiB ROM contains a BASIC interpreter (called Sinclair BASIC) and there is no actual operating system, only a set of subroutines at known ROM addresses.
The machine was delivered with a very good manual: Sinclair ZX81 BASIC Programming by Steven Vickers. It was both a reference and a training course for completely beginners.
From BASIC, it is possible to directly access memory (with PEEK
and POKE
instructions) and to execute Z80
machine code using the USR
instruction (often with the RAND
statement, a trick to ignore the value returned by USR
). Most of the time, machine code is placed in a BASIC
REM
statement at the beginning of the program.
One of my first programs was a Z80
disassembler. Unfortunately, I lost it. I have also programmed some games and I still have one of them: FACTORY
(originally called USINE
in French). It is written with a mix of BASIC
(to display the menu, the rules of the game, etc) and Z80
assembly code (for the game itself). If you want to play with it, it available (in an emulator, see bellow) on my Biography page.
The source code of the game itself (BASIC and Z80 assembly) will be the subject of my next post.
This is a schematic of the keyboard:
The positions of the keys are thus given by the following table:
Port | Line | D0 | D1 | D2 | D3 | D4 | D4 | D3 | D2 | D1 | D0 | Line | Port | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
F7FEh | A11 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | A12 | EFFEh | |
FBFEh | A10 | Q | W | E | R | T | Y | U | I | O | P | A13 | DFFEh | |
FDFEh | A9 | A | S | D | F | G | H | J | K | L | NL | A14 | BFFEh | |
FEFEh | A8 | SH | Z | X | C | V | B | N | M | . | SP | A15 | 7FFEh |
Note: NL is New Line, SH is Shift, SP is Space.
D | Decimal | Binay |
---|---|---|
D0 | 1 | 00000001 |
D1 | 2 | 00000010 |
D2 | 4 | 00000100 |
D3 | 8 | 00001000 |
D4 | 16 | 00010000 |
Address (hex) | Address (dec) | Description |
---|---|---|
0000-1FFF | 0 - 8191 | ROM (8 KiB) |
2000-3FFF | 8192 - 16383 | Shadow ROM |
4000-7FFF | 16384 - 32767 | RAM (in the case of a 16K extension) |
Note: The exact layout of ROM and RAM are dependent of the quantity of external RAM installed. In some cases, ROM and RAM are mirrored at other addresses.
Address | Description |
---|---|
4000 / 16384 | System Area |
407D /16509 | Program |
D_FILE |
Video Memory |
VARS |
BASIC Variables |
E_LINE -1 |
Byte 80h |
E_LINE |
Input Buffer/Workspace |
STKBOT |
BASIC Calculator Stack |
STKEND |
Machine Stack/Free Memory |
SP |
Machine Stack/In Use (SP Z80 register) |
ERR_SP |
GOSUB Stack |
RAMTOP |
Unused/reserved memory |
Address | Hexadecimal | Bytes | Saved | Name | Description |
---|---|---|---|---|---|
16384 | 4000 | 1 | No | ERR_NR |
1 less than the report code. |
16385 | 4001 | 1 | No | FLAGS |
Various flags to control the BASIC system. |
16386 | 4002 | 2 | No | ERR_SP |
Address of first item on machine stack (after GOSUB returns). |
16388 | 4004 | 2 | No | RAMTOP |
Address of first byte above BASIC system area. |
16390 | 4006 | 1 | No | MODE |
Specified K , L , F or G cursor. |
16391 | 4007 | 2 | No | PPC |
Line number of statement currently being executed. |
16393 | 4009 | 1 | Yes | VERSN |
0 Identifies ZX81 BASIC in saved programs. |
16394 | 400A | 2 | Yes | E_PPC |
Number of current line (with program cursor). |
16396 | 400C | 2 | Yes | D_FILE |
Pointer to Video Memory (BG map). |
16398 | 400E | 2 | Yes | DF_CC |
Address of PRINT position in display file. |
16400 | 4010 | 2 | Yes | VARS |
Pointer to BASIC Variables Area. |
16402 | 4012 | 2 | Yes | DEST |
Address of variable in assignment. |
16404 | 4014 | 2 | Yes | E_LINE |
Pointer to Input Buffer/Workspace, and to the end of the area saved. |
16406 | 4016 | 2 | Yes | CH_ADD |
Address of the next character to be interpreted. |
16408 | 4018 | 2 | Yes | X_PTR |
Address of the character preceding the marker. |
16410 | 401A | 2 | Yes | STKBOT |
Pointer to BASIC Calculator Stack. |
16412 | 401C | 2 | Yes | STKEND |
Pointer to bottom of Machine Stack. |
16414 | 401E | 1 | Yes | BERG |
Calculator's B register. |
16415 | 401F | 2 | Yes | MEM |
Address of area used for calculator's memory. |
16417 | 4021 | 1 | Yes | not used | |
16418 | 4022 | 1 | Yes | DF_SZ |
The number of lines (including one blank line) in the lower part of the screen. |
16419 | 4023 | 2 | Yes | S_TOP |
The number of the top program line in automatic listings. |
16421 | 4025 | 2 | Yes | LAST_K |
Shows which keys pressed. |
16423 | 4027 | 1 | Yes | DEBOUN |
Key release delay. |
16424 | 4028 | 1 | Yes | MARGIN |
Number of blank lines above or below picture: 55 for 50Hz, 31 for 60Hz. |
16425 | 4029 | 2 | Yes | NXTLIN |
Address of next program line to be executed. |
16427 | 402B | 2 | Yes | OLDPPC |
Line number of which CONT jumps. |
16429 | 402D | 1 | Yes | FLAGX |
Various flags. |
16430 | 402E | 2 | Yes | STRLEN |
Length of string type destination in assignment. |
16432 | 4030 | 2 | Yes | T_ADDR |
Address of next item in syntax table. |
16434 | 4032 | 2 | Yes | SEED |
The seed for RND . This is the variable that is set by RAND . |
16436 | 4034 | 2 | Yes | FRAMES |
Counts the frames displayed on the television. |
16438 | 4036 | 1 | Yes | COORDS |
x-coordinate of last point ^PLOT`ted. |
16439 | 4037 | 1 | Yes | y-coordinate of last point PLOTted. | |
16440 | 4038 | 1 | Yes | PR_CC |
Less significant byte of address of next position for LPRINT to print as (in PRBUFF ). |
16441 | 4039 | 1 | Yes | S_POSN |
Column number for PRINT position. |
16442 | 403A | 1 | Yes | Line number for PRINT position. |
|
16443 | 403B | 1 | Yes | CDFLAG |
Various flags. Bit 7 is on (1) during compute & display mode. |
16444 | 403C | 33 | Yes | PRBUFF |
Printer buffer (33rd character is NEWLINE ). |
16477 | 405D | 30 | Yes | MEMBOT |
Calculator's memory area. |
16507 | 407B | 2 | Yes | not used |
The BASIC program begins at address 16509h
(just after System variables). Instructions have these general format:
Bytes | Description |
---|---|
2 | Line number in big endian |
2 | Length of the line (including terminal NEWLINE character, exluding line number and length) |
1 | Instruction code |
x | Data |
1 | NEWLINE |
If the program contains Z80 machine code, it is often stored in a REM
comment at the very beginning of the program. The layout is thus:
Address | Hexadecimal | Bytes | Description |
---|---|---|---|
16509 | 407D | 2 | Line number 1 in big endian: 0 1 |
16511 | 407F | 2 | Length of the line (including terminal NEWLINE character, exluding line number and length) |
16513 | 4081 | 1 | Instruction code: EAh |
16514 | 4082 | x | Data (machine code) |
1 | NEWLINE |
Z80 Machine code starts at address 16514
(4082h
). The jump to this address from BASIC is often performed with this instruction:
RAND USR 16514
RAND
is used because in BASIC, it is not possible to ignore the return value of USR
(call to user-defined code). RAND
takes a parameter and has very little side-effects.
Programs are stored on audio tapes this way:
Duration | Description |
---|---|
x seconds | Your voice, saying "filename" (optional) |
x seconds | Video noise |
5 seconds | Silence |
1-127 bytes | File name (bit 7 set in last char) |
LEN bytes |
Data, loaded to address 4009h, LEN = (4014h)-4009h |
1 pulse | Video retrace signal if display was enabled |
x seconds | Silence / video noise |
The data contains system area, basic program, video memory, VARS. The last byte of a (clean) file should be 80h (the last byte of VARS). Files should usually not exceed 16 KiB.
The system area should contain proper data. In particular:
Address | Description |
---|---|
4014h | Defines the end address (used to calculate the file length) |
4029h | Points to the next executed (autostarted) BASIC line |
403Bh | Indicates if program runs in SLOW or FAST mode (bit 6) |
.p
and .81
files are similar. They consist of the raw data as saved by the ZX81 tape saving routine (without the filename). They can store only one program. .p
files typically contain some garbage at the end.
.p81
files are like .p
files except they begin with the file name (127 bytes, last byte ORed with 80h
).
Data are copies of the memory area 4009h
(16393) up to E_LINE
-1.
There are emulators available, written in various languages. In particular:
EightyOne
, witten in C++. Copyright © 2003-2006 Michael D Wynne and by others.JtyOne
based on EightyOne (translation) and written in Java. Copyright © 2006 Simon Holdsworth and others.I have also written my own ZX81 emulator in Typescript. Thank to the transcompilation of Typescript into Javascript, it runs as a pure HTML5/Javascript application. The source code of the emulator is available on GitHub and you can play with the emulator on my Biography page.
The creation of this emulator (and more generally, the question of the conversion of a Java applet into a pure HTML5/Javascript application) will be the subject of a next post.
There are several web sites and forums discussing Sinclair ZX81, ZX80 or Spectrum. For example: