My first computer: Sinclair ZX81

Back to the future

Sinclair ZX81 Overview

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.

Motherboard

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).

Display

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).

Keyboard

The keyboard is very simple: it is a flat, pressure-sensitive membrane QUERTY keyboard:

Permanent storage

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.

Operating system & Programming

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.

My first programs

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.

Some technical details

Keyboard layout

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

Memory map

Overview

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.

RAM map

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

System variables

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

Program

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.

Program stored on an audio tape

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)

File formats (.p, .81 and .p81)

.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.

ZX81 Emulators

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.

Web sites

There are several web sites and forums discussing Sinclair ZX81, ZX80 or Spectrum. For example: