Contents

Computer Specs
ZX80/ZX81
ZX Spectrum
Lambda 8300
Jupiter ACE

Z80 CPU
Z80 CPU Specifications

The BASIC Interpreter
BASIC Interpreter

About this doc
About this document


 ZX80/ZX81

ZX80/ZX81 Technical Information
ZX80/ZX81 Models
ZX80/ZX81 Keyboard Assignment
ZX80/ZX81 I/O Map
ZX80/ZX81 I/O Ports
ZX80/ZX81 Video Mode Text and Blockgraphics
ZX80/ZX81 Video Mode Pseudo Hi-Res Graphics
ZX80/ZX81 Video Mode True Hi-Res Graphics
ZX80/ZX81 Video Blanking and Retrace
ZX80/ZX81 Video Interrupts (INTs and NMIs)
ZX80/ZX81 Video Display Timings
ZX80/ZX81 Video Character Set
ZX81 Video Nonstandard UDG/CHRS Expansions
ZX81 Video Nonstandard HIRES Expansions
ZX81 Video Nonstandard Picture Size
ZX81 Video Nonstandard Color Expansions
ZX81 Joystick Expansions
ZX81 Sound Expansions
ZX80/ZX81 Memory Map and System Area
ZX80/ZX81 Memory Mirrors and Expansions
ZX80/ZX81 Memory Binary Data/Machine Code Programs
ZX80/ZX81 Cassette File Images
ZX80/ZX81 Cassette File Content
ZX80/ZX81 Cassette Signals
Hardware, Connectors, Upgrading

Z80 CPU
Z80 CPU Specifications

The BASIC Interpreter
BASIC Interpreter


 ZX80/ZX81 Models

ZX81
 CPU: NEC P1X108-144 D780C-1  (Z80 compatible)  3.25MHz
  The clock frequency is gained from a high tolerance 6.5MHz
  chewing gum, not exactly a quartz oscillator, in fact, my
  oscilloscope tells me that my ZX81 is running at 3.33Mhz.
 Effective CPU Speed in SLOW mode (when Display is enabled)
  For 50Hz Display Refresh:  0.804600 MHz
  For 60Hz Display Refresh:  0.536400 MHz
 Custom: FERRANTI ULA 2C184E 8147
  Combines the ZX80 video circuit in one chip, plus NMI-generator
 Video Tech Data
  Video: 32x24 Characters (256x192 pixels), 64x48 Dots Block Graphics
  Characters: 64 Characters, defined in ROM area
  Attributes: Normal and Inverted (separately for each char)
 Memory
  8 Kbytes ROM
  1 KByte RAM built-in
  Expanision RAM: 16KBytes (most popular) up to 56KBytes

ZX80
Memory: 4 KBytes ROM, 1 KByte RAM
Video: Same as ZX81, but without NMI-generator, ie. not supporting SLOW mode, thus cannot display a picture while program is operating.
Sinclair also sold an upgrade kit for it: The 8K ZX81 ROM bundled with a ZX81-style keyboard overlay (but still lacking the NMI-generator).

TS1000
A ZX81 clone, distributed in USA by a company called Timex. Includes 2 KBytes RAM, and obviously 60Hz NTSC TV modulator, otherwise same as ZX81. The BIOS is 1:1 the same as in ZX81.

TS1500
Same as above TS1000, but with 16 KBytes of RAM built-in, with rubber keyboard, and slightly modified BIOS.

Microdigital TKs (Brazil)
Reportedly brazilian ZX81 clones are called the "TKs".
  TK80    ZX80 clone with 4K ROM, 1K RAM, TTL-chips, without NMI
  TK82    ZX80 clone with 4K ROM, 2K RAM, TTL-chips, without NMI
  TK82-C  ZX81 clone with 8K ROM, 2K RAM, TTL-chips, with NMI on daughterboard
  TK83    ZX81 clone with 8K ROM, 2K RAM, ULA-chip, with NMI,joystick,new case
  TK85    ZX81 clone with 10K ROM, 16K RAM, psg-solder-points, rubber keyb,
           and other extras...
TK83/TK85 have DIN joystick connector
The C in TK82-C is for "Cientifico", or Scientific in English.

____ ZX80 vs ZX81 Compatibility ____

Hardware
The ZX80 and ZX81 hardware is mostly the same, the same I/O address are used, RAM is located at 4000h and up, and mirrored as VRAM at C000h and up. The only relevant physical difference is that ZX81 includes an NMI generator, which is (optionally) allowing to execute programs during vertical blanking periods.

Firmware
Compatibility ends at the firmware side, the revised ZX81 BIOS version dropped any compatibility issues. The content of cassette files is different, some characters in the character set have been exchanged, the system area is different, the BASIC interpreter 'opcodes' have different meanings, variables and immediates are stored as floating point numbers (ZX81) instead as integers (ZX80), and any addresses in the BIOS ROM are different also.

Software
As described above, ZX80 and ZX81 cassette files are completely incompatible to each other. However, the ZX81 BASIC syntax is mostly compatible to ZX80 syntax, so that ZX80 software could be easily imported to ZX81 with little changes; either by translation program that adjusts the file content, or by manually entering the source code.

Performance
The main advantage of ZX81 hardware is the ability to execute program code and to display a picture simultaneously in SLOW mode. As the name says, this fancy feature is heavily slowing down effective CPU speed.
Otherwise ZX81 should be theoretically as fast as the trusty ZX80, unfortunately the new BASIC interpreter uses floating point values rather than integers, and thus ZX81 BASIC programs are ways slower even when using FAST mode.

TS1500 BIOS
The inefficient RAM size detection function is removed, instead, the BIOS uses a hardcoded RAM size of 16K, additionally it checks for "TS1510 Command Cartridge Player" ROMs at location 2000h, and starts them if present (the first opcode byte at 2000h must be 01h, ie. "ld bc,nnnn").
The TS1500 BIOS does fix the LPRINT bug, and it does (attempt) to increase floating point conversion accurracy (the ZX81/TS1000 tends to round-down values by accident, whilst the TS1500 tends to round-up values by accident, which isn't any better, and in some cases it's even worse).

____ Sinclair ZX81 Personal Computer ____

Multimedia
The first step to modern decadency was done by releasing the ZX81, this computer had the ability to operate in a so called SLOW mode, during which it could both execute a program AND display a picture on the screen at the SAME time, while its predecessor the ZX80 could do only either one at once. This older computer has been restricted to FAST mode, offering high performance execution time.

Energy
Anyways, a very amazing thing's been the keyboard heating. Even though the computer's been operated at 5 Volts, it's been delivered with external 9V power supply. The cooling plate of the 7805 voltage regulator's been located directly under the keyboard, whereas cooling plate is a possibly misleading expression, it's been heating up after a while, giving the keyboard an irritating feverish temparature. If somebody'd claim that ZX81 are tough guys and that they'd never get cold fingers - I could only confirm that.

Computers Today
Unfortunately, the rather ineffective new invention of animated pictures has been adapted by newer computers, up to today!
As far as concerning power supplies, this has been another milestone in home computer technology, and there's still nobody who stands up, takes his ventilated heavy 200 Watts supply, and smashes it into the face of the guy who has mass-manufactured it.


 ZX80/ZX81 Keyboard Assignment

Here's the ZX81 keyboard assignment. The Graphics column is meant to be Shift+Graphics (Graphic without Shift just returns inverted 'normal' characters).
The two columns to the right are for the ZX80, as there's no Function mode, ZX80 function names (such like CHR$) can be (and must be) typed by hand as single characters.
  <-key-> <------------- ZX81 -----------> <------ZX80------>
  NORMAL  COMMAND SHIFT  FUNCTION GRAPHICS ZX80/CMD ZX80/SHFT
  1       1       <edit>  --      1000     1        NOT
  2       2       AND     --      0100     2        AND
  3       3       THEN    --      0001     3        THEN
  4       4       TO      --      0010     4        TO
  5       5       <left>  --      0101     5        <left>
  6       6       <down>  --      0011     6        <down>
  7       7       <up>    --      1100     7        <up>
  8       8       <right> --      0101     8        <right>
  9       9    <graphics> --      --       9        <hide???>
  0       0      <rubout> --      --       0        <rubout>
  Q       PLOT    ""      SIN     0111     NEW      1010
  W       UNPLOT  OR      COS     1011     LOAD     0011
  E       REM     STEP    TAN     1110     SAVE     1000
  R       RUN     <=      INT     1101     RUN      0100
  T       RAND    <>      RND     0110     " "      0022
  Y       RETURN  >=      STR$    1001     REM      "
  U       IF      $       CHR$    --       IF       $
  I       INPUT   (       CODE    --       INPUT    (
  O       POKE    )       PEEK    --       PRINT    )
  P       PRINT   "       TAB     --       "?"      *
  A       NEW     STOP    ARCSIN  2222     LIST     2222
  S       SAVE    LPRINT  ARCCOS  2200     STOP     0110
  D       DIM     SLOW    ARCTAN  0022     DIM      0010
  F       FOR     FAST    SGN     2211     FOR      0001
  G       GOTO    LLIST   ABS     1122     GO TO    2200
  H       GOSUB   **      SQR     2222     POKE     **
  J       LOAD    -       VAL     --       RANDOMISE -
  K       LIST    +       LEN     --       LET      +
  L       LET     =       USR     --       "?"      =
  ENTER   newline function        --       newline  <edit>
  SHIFT   --      --      --      --       --       --
  Z       COPY    :       LN      --       "?"      :
  X       CLEAR   ;       EXP     --       CLEAR    ;
  C       CONT    ?       AT      --       CLS      ?
  V       CLS     /       --      --       GO SUB   /
  B       SCROLL  *       INKEY$  --       RETURN   OR
  N       NEXT    <       NOT     --       NEXT     <
  M       PAUSE   >       PI      --       "?"      >
  .               ,       --      --       "."      ,
  SPACE   BREAK   pound   --      1111     BREAK    pound

40-key ZX80/ZX81 Keyboard
  | 1   2   3   4   5   6   7   8   9   0     |
  |  Q   W   E   R   T   Y   U   I   O   P    |
  |   A   S   D   F   G   H   J   K   L  ENTER|
  |SHF Z   X   C   V   B   N   M    .    SPACE|

Keyboard Translation
When the keyboard translation option is used, special characters such like comma, quotes, plus, etc. are mapped to the respective PC keys. In result, the meaning of the ZX key combinations SHIFT+1..4 changes also. The respective functions are remapped to the following PC-keys:
  ZX81  ZX80  PC
  EDIT  NOT   !
  AND   AND   &
  THEN  THEN  #
  TO    TO    %
Some other remapped keys are Cursor=Cursor, Rubout=Backspace, Graphics=Alt, Function/Symbol=Ctrl, Break=End.


 ZX80/ZX81 I/O Map

I/O Ports
  xx0Fh.W   PSG data  (Bi-Pak ZON-X81 Sound)
  xx1Dh.R   Zebra Joystick adapter
  xx2Fh.R   Memopak I/F Centronics Interface status (IN status,[dd3Fh]);dd=data
  xx3Fh.R   Memopak I/F Centronics Interface finish (IN dummy,[dd3Fh]) ;dd=data
  FF7Eh.R   Lambda - read PAL/NTSC flag (A7=row) (via diode from A7 to KEYB.0)
  xx9Fh.W   PSG index (Timedata ZXM Sound Box)
  xxBFh.R   PSG data  (Timedata ZXM Sound Box/Joystick Read)
  xxDFh.W   PSG index (Bi-Pak ZON-X81 Sound) (default address) (sometimes CFh)
  xxDFh.W   PSG data  (Timedata ZXM Sound Box)
  xxDFh.R   Mikro-Gen digital joystick
  xxDFh.R   AGB - JOYSTICK II (port A) (or B ?)
  xxEFh.R   AGB - JOYSTICK II (port B) (or A ?)
  xxF5h.R   Lambda - toggle sound output level
  xxF5h.W   Lambda - select charset line number (00..07)
  xxF6h.R   Lambda - read selected charset data (8 pixels)
  xxF6h.W   Lambda - select charset char number (00..3F)
  xxFBh.R   Sinclair Printer Status
  xxFBh.W   Sinclair Printer Output
  xxFDh.W   Disable NMI (ZX81 only)
  xxFEh.W   Enable NMI (ZX81 only)
  NNFEh.R   Keyboard read, when NMI=Off: also enter VSYNC and set CAS.OUT=Low
  xxFFh.W   Terminate Vsync and restart LINECNTR and set CAS.OUT=High

Accidently Used I/O Port Mirrors
  0Eh.R   Faulty mirror of FEh.R (used by a GRAND-PRIX game)
  CFh.W   PSG index (Bi-Pak ZON-X81 Sound) (used accidently in Bi-Pak manual)
  CFh.W   PSG index (Bi-Pak ZON-X81 Sound) (used by Lunar 10)
  FEh.W   Faulty mirror of FFh.W (used by animated Space Invaders for ZX80)

Memory Mapped Ports
  3E80h.R/W Mikro-Gen analog joystick A/D board
  7FFFh.W   PSG index (Quicksilva QS Sound board)
  7FFEh.R/W PSG data  (Quicksilva QS Sound board)

IR Register
  I=00h  Pseudo HiRes (Madjump II)
  I=08h  Pseudo HiRes (Bipods, Micromouse)
  I=0Ch  Pseudo HiRes (Rock Crush, Dans Revenge, etc.)
  I=1Eh  ZX81 ROM Charset (or QS CHRS board RAM)
  I=2xh  dk'tronics UDG ROM (or unknown UDG RAM)
  I=3xh  dk'tronics UDG RAM
  I=40h  Xtricator IM2 vector at [40FFh] (with databus assumed to be FFh)
  IR=x   True hires bitmap address
  R=x    Interrupt counter for "compressed" text output (INT wired to A6)
  R=x+EI Interrupt execution forces HSYNC (on ZX81: also resets NMI counter)


 ZX80/ZX81 I/O Ports

Output to Port FFh (or ANY other port)
Writing any data to any port terminates the Vertical Retrace period, and restarts the LINECNTR counter. The retrace signal is also output to the cassette (ie. the Cassette Output becomes High).

Input from Port FEh (or any other port with A0 zero)
Reading from this port initiates the Vertical Retrace period (and accordingly, Cassette Output becomes Low), and resets the LINECNTR register to zero, LINECNTR remains stopped/zero until user terminates retrace - In the ZX81, all of the above happens only if NMIs are disabled.
  Bit  Expl.
  0-4  Keyboard column bits (0=Pressed)
  5    Not used             (1)
  6    Display Refresh Rate (0=60Hz, 1=50Hz)
  7    Cassette input       (0=Normal, 1=Pulse)
When reading from the keyboard, one of the upper bits (A8-A15) of the I/O address must be "0" to select the desired keyboard row (0-7).
(When using IN A,(nn), the old value of the A register is output as upper address bits and <nn> as lower bits. Otherwise, ie. when using IN r,(C) or INI or IND, the BC register is output to the address bus.)
The ZX81/ZX80 Keyboard Matrix
  Port____Line____Bit__0____1____2____3____4__
  FEFEh  0  (A8)     SHIFT  Z    X    C    V
  FDFEh  1  (A9)       A    S    D    F    G
  FBFEh  2  (A10)      Q    W    E    R    T
  F7FEh  3  (A11)      1    2    3    4    5
  EFFEh  4  (A12)      0    9    8    7    6
  DFFEh  5  (A13)      P    O    I    U    Y
  BFFEh  6  (A14)    ENTER  L    K    J    H
  7FFEh  7  (A15)     SPC   .    M    N    B

Port FDh Write (ZX81 only)
Writing any data to this port disables the NMI generator.

Port FEh Write (ZX81 only)
Writing any data to this port enables the NMI generator.
NMIs (Non maskable interrupts) are used during SLOW mode vertical blanking periods to count the number of drawn blank scanlines.

ZX81 Printer
Below ports are for the printer. Both ZX80 and ZX81 aren't actually include any printer connector or hardware, but the ZX81 BIOS supports three BASIC instructions (LPRINT, LLIST, COPY) which access external printer hardware by these I/O addresses.
Port FBh Read - Printer Status
  Bit  Expl.
  0    Data Request   (0=Busy, 1=Ready/DRQ)
  1-5  Not used
  6    Printer Detect (0=Okay, 1=None)
  7    Newline        (0=Nope, 1=Begin of new line)
Port FBh Write - Printer Output
  Bit  Expl.
  0    Not used
  1    Undoc/Speed?   (0=Normal, 1=used to slow-down last 2 scanlines)
  2    Motor          (0=Start, 1=Stop)
  3-6  Not used
  7    Pixel Output   (0=White/Silver, 1=Black)
That serial 1-bit / 1-pixel protocol isn't compatible with normal character based printers.
Uses thermal paper rolls with 100 (or 110 ?) mm width, max 25 meters length.
Horizontal resolution is 256 pixels (32 characters).

Note
Beside for the actual I/O ports, the IR register, the most significant bit of the program counter, and (if that bit was set) the opcodes on the databus are also relevant for video output. For details refer to chapters about Video.


 ZX80/ZX81 Video Mode Text and Blockgraphics

Overview
This is the ZX standard video mode. The display area consists of 32x24 characters of 8x8 pixels each. The user cannot set single pixels though, only 64 predefined characters can be used. However, some of these characters are split into 2x2 blocks (4x4 pixel each), allowing to display 64x48 block low resolution graphics.

Video Memory
Video memory is addressed by the D_FILE pointer (400Ch) in ZX80/81 system area. The first byte in VRAM is a HALT opcode (76h), followed by the data (one byte per character) for each of the 24 lines, each line is terminated by a HALT opcode also. In case that a line contains less than 32 characters, the HALT opcode blanks (white) the rest of the line up to the right screen border. (Thus left-aligned text will take up less memory than centered or right-aligned text.)

Character Data, VRAM Size
Character data in range 00h..3Fh displays the 64 characters, normally black on white. Characters may be inverted by setting Bit 7, ie. C0h..FFh represents the same as above displayed white on black.
The fully expanded VRAM size is 793 bytes (32x24 + 25 HALTs, almost occupying the whole 1Kbyte of internal RAM), an empty fully collapsed screen occupies only 25 bytes (HALTs).

Character Set
The character set is addressed by the I register multiplied by 100h. In the ZX81 this is 1Eh for 1E00h..1FFFh, in ZX80 0Eh for 0E00h..0FFFh. Setting I=40h..7Fh in attempt to define a custom charset in RAM rather than ROM does not work.

Display procedure Tech Details
The display data is more or less 'executed' by the CPU. When displaying a line, the BIOS takes the address of the first character, eg. 4123h, sets Bit 15, ie. C123h, and then jumps to that address.

The hardware now senses A15=HIGH and /M1=LOW (signalizing opcode read), upon this condition memory is mirrored from C000-FFFF to 4000-7FFF. The 'opcode' is presented to the databus as usually, the display circuit interpretes it as character data, and (if Bit 6 is zero) forces the databus to zero before the CPU realizes what is going on, causing a NOP opcode (00h) to be executed. Bit 7 of the stolen opcode is used as invert attribute, Bit 0-5 address one of the 64 characters in ROM at (I*100h+char*8+linecntr), the byte at that address is loaded into a shift register, and bits are shifted to the display at a rate of 6.5MHz (ie. 8 pixels within 4 CPU cycles).

However, when encountering an opcode with Bit 6 set, then the video circuit rejects the opcode (displays white, regardless of Bit 7 and 0-5), and the CPU executes the opcode as normal. Usually this would be the HALT opcode - before displaying a line, BIOS enables INTs and initializes the R register, which will produce an interrupt when Bit 6 of R becomes zero.
In this special case R is incremented at a fixed rate of 4 CPU cycles (video data executed as NOPs, followed by repeated HALT), so that line display is suspended at a fixed time, regardless of the collapsed or expanded length of the line.

As mentioned above, an additional register called linecntr is used to address the vertical position (0..7) whithin a character line. This register is reset during vertical retrace, and then incremented once per scanline. The BIOS thus needs to 'execute' each character line eight times, before starting to 'execute' the next character line.


 ZX80/ZX81 Video Mode Pseudo Hi-Res Graphics

This method is used to display 256x192 pixels graphics, limited to max 128 combinations within each row of 8 pixels though. Even though not supported by the BIOS, a couple of games are using this video mode: Rock Crush, Dans Revenge, Rocketman, Forty Niner, Madjump II, Bipods, Micromouse, and possibly others.

Basically it is working much like Text video mode, the character height is reduced to a single scanline, so each tile consists of 8x1 pixels rather than 8x8 pixels. And the screen consists of 32x192 of these 'flat' characters, each line usually terminated by a RET opcode (C9h), thus occupying 6176 bytes of memory.

A special display procedure is required which forces the linecntr register to zero by issuing a very short 'vertical retrace' signal each scanline (preferably simultaneously to the hardware generated horizontal retrace signal). In result, only the topmost row of each character will be displayed, as the topmost row of most of the normal characters is just blank, it'd be recommended to change the characterset pointer in the I register to another address in ROM.

For example, setting I=0Ch would select the area 0C00h..0DFFh (in steps of eight: topmost rows of chars #0, #1, #2 at C00h, C08h, C10h, etc). The machine code bytes in this memory region are then used as 'randomly' predefined pixel rows, which may or may not match the programmers requirements. Each of the 64 rows may be inverted as normal text characters, so theoretical a total of 128 different 8-pixel rows can be used, practically less because most likely a couple of rows will be duplicated.

As the interrupt based BIOS display procedure at 0038h does not support above, a raw software based handler is used in most cases, that's why each line is terminated by a RET rather than HALT opcode.

Observe that the TS1500 BIOS version contains some patched opcodes, so graphics may appear different as with original ZX81/TS1000 BIOS (the most commonly used region, with I=0Ch, is same in both BIOS versions).


 ZX80/ZX81 Video Mode True Hi-Res Graphics

This mode produces a 256x192 pix graphics screen, and, unlike Pseudo Hi-Res, it allows to set each pixel separately. The downside is that it does not work with most external RAM Paks (memory expansions can be quite easily upgraded by using two diodes and a resistor though, see chapter Hardware Modifications for details).

However, it does work with internal RAM and with modified RAM Paks.
When using internal RAM, take care about these two limitiations: Only a small picture will fit into 1K memory (so the display procedure must increase horizontal and/or vertical blanking times), and external RAM must be disconnected (as it'd otherwise disable internal RAM).

The true hi-res technique is used by the games Guus Flater, Starfight, and by some demos such like WRX1K.

The general idea is to move the character set into RAM at 4000h..7FFFh by setting I to 40..7Fh. Now this does NOT work as expected, ie. as
(I*100h+char*8+linecntr) as for text mode. Both the executed opcode (character number) and the linecntr value are ignored. Instead, pixels are directly read from memory at (IR). Each eight bits of each byte represent eight pixels. The picture is defined in form of a common monochrome bitmap.

The bitmap data can be located anywhere in RAM, and as it is not 'executed' as in other display modes, only raw data is required (ie. and no HALT or RET opcodes need to be attached to each line). Note that only the lower seven bits of the R register are incremented by the CPU; care should be taken that it does not overflow within a line. For example, a bitmap of 256 pixels width (32 bytes) should be aligned to 32 in memory.

The main display procedure should load the MSB of the current bitmap line into the I register, and the LSB into the A register, then jump to a dummy D_FILE display procedure in memory with A15=HIGH. This dummy procedure should copy the LSB from A into R register, and then execute a stream of 32 NOP opcodes (00h: Bit 7 indicates not inverted output, Bit 6 disables blanking, data is directly read from (IR), so that the character number in Bit 0-5 is ignored), and return to the main procedure - which'd then issue some delays, prepare new address in I and A and start over with the next line (using the same dummy procedure again), until the whole screen has been displayed.


 ZX80/ZX81 Video Blanking and Retrace

Display becomes white during blanking time. That is: when Bit 15 of the program counter (PC) is "0", and/or when Bit 6 of the current opcode is "1". Theoretically the CPU could execute whatever program code during blanking - however, as there is only limited interrupt feedback, this time is usually spent on HALT opcodes or other delay loops, required to keep the CPU synchronized to visible display output.

Vertical Blanking
Upper and lower screen borders are displayed above/below of the actual picture, the height of these borders depends on the display refresh rate. The BIOS permanently reads out the preferred refresh rate (50Hz or 60Hz) when checking for keystrokes, and uses this to re-calculate the desired border height for each frame - allowing to export the ZX to other countries without having to reboot it ;-)

ZX80 Vertical Blanking ("PAUSE")
The ZX80 points to a HALT opcode in the D_FILE area, which is repeatedly executed to display blank upper and lower screen borders (much like empty lines in a collapsed screen). The CPU thus wastes all its energies just on drawing blank lines (and on decreasing a remaining lines counter).

ZX81 Vertical Blanking ("SLOW")
Even though the ZX81 supports the above method either, it'd usually use NMI based blanking which allows program code to be executed during blanking time. NMIs (non maskable interrupts) are enabled by I/O, the NMI handler is then called each scanline. The handler increases a counter and (if the counter does not overflow) returns to the user program, otherwise it executes a HALT opcode to synchronize the CPU to the display at one-cycle resolution and terminates the blanking procedure.

Horizontal Blanking
In both ZX80 and ZX81, the CPU cannot be used to execute user programs during horizontal blanking periods - it is required to execute delays to be kept synchronized to video hardware. This could be gained by a hardcoded delay. However, ZX video is required to support variable length blanking when using collapsed screens, in that case the width of the right screen border must be increased when drawing an empty (or incomplete) line. This is gained by loading a counter value into the R register (before drawing the line), and terminating the line by a HALT opcode which is kept executed until Bit 6 of R becomes zero.

Retrace
The cathode ray is moved back to the begin of the scanline / top of display during horizontal / vertical retrace periods.
Horizontal retrace is generated by the video hardware, so care should be taken to keep the display procedure synchronized to retrace signals.
Vertical retrace must be manually initiated and terminated by I/O, a fixed length delay should be issued during v-retrace in order to produce a stable display.


 ZX80/ZX81 Video Interrupts (INTs and NMIs)

INTs
Maskable Interrupts (INTs) are generated when Bit 6 of the R (refresh) register becomes zero. As the R register is incremented once for each opcode (twice for prefixed opcodes), there is no linear relationship between clock cycles and refresh cycles.
In the ZX, INTs are used to terminate scanline drawing, the display data is 'executed' identical as NOP instructions, followed by a HALT opcode (which is identical as repeated NOPs), so that in this special case (as both HALTs and NOPs increment R once per 4 clock cycles) INTs can be used to produce a regular interval.
The above INT/HALT combination is used as variable length delay, which is required for variable length scanlines (ie. mixed collapsed and expanded scanlines) only. Fixed length scanlines could be terminated by hardcoded delays.
In IM 1 (default), the INT handler is located at 0038h in BIOS ROM. INTs are enabled by EI instruction, and are automatically disabled upon execution (or when issuing DI instruction).

NMIs (ZX81 only)
Non maskable interrupts (NMIs) are requested during horizontal retrace time (ie. at the end of each scanline), the CPU is forced into WAIT state for the duration of NMI request (unless when executing a HALT opcode which is allowed to complete without WAIT states).
NMIs are used to count the number of drawn scanlines during vertical blanking periods. This is allowing the user program to be executed in SLOW mode while drawing upper and lower screen borders, and to pass control back to the display/retrace procedure once the NMI handler decides to terminate the blanking period.
The NMI handler is located at 0066h in BIOS ROM (independently of IM interrupt mode). NMIs are enabled/disabled by I/O instructions - the CPU cannot disable NMIs (ie. DI/EI has no effect on NMIs).


 ZX80/ZX81 Video Display Timings

Horizontal Scanline Timings
  Horizontal Display    128 cycles (32 characters, 256 pixels)
  Horizontal Blanking    64 cycles (left and right screen border)
  Horizontal Retrace     15 cycles
  Total Scanline Time   207 cycles
Horizontal retrace rate and duration are fixed. The display procedure might increase or decrease the width of the display area (by respectively adjusting the blanking time) even though larger screens might exceed the visible dimensions of the attached TV set or monitor.

Vertical Timings (50Hz)
  Upper Blanking      11592 cycles      56 scanlines (7 charlines)
  Display Area        39744 cycles     192 scanlines (24 charlines)
  Lower Blanking   ca.11592 cycles  ca. 56 scanlines (or a bit less)
  Vertical Retrace     1235 cycles  ca.  6 scanlines

Vertical Timings (60Hz)
  Upper Blanking       6624 cycles      32 scanlines (4 charlines)
  Display Area        39744 cycles     192 scanlines (24 charlines)
  Lower Blanking   ca. 6624 cycles  ca. 32 scanlines (or a bit less)
  Vertical Retrace     1235 cycles  ca.  6 scanlines

User Available Blank Lines
Even though upper screen border consists of 56 scanlines (32 in 60Hz mode), only 54 scanlines (60Hz: 30 scanlines) are available for user program execution. The first of the remaining scanlines is occupied by a HALT opcode (which is suspended by a NMI; providing exact retrace synchronisation), and the next scanline is spent on a collapsed D_FILE row. Lower screen borders are idientical as above, except that no collapsed D_FILE line is drawn at the bottom. Instead, some cycles are spent upon incrementing the FRAMES counter.

User Available Blanking Time
The total of 207 cycles per scanline isn't available for user program, even during blanking periods: An NMI is generated each line, including some NMI-waitstates, the CALL 66h execution, and the execution of the NMI handler (which is counting the number of drawn scanlines).
  Item                    Original ZX81  Tuned ZX81
  Total scanline time       207 cycles     207 cycles
  NMI WAIT                 - 14 cycles    -  0 cycles (WAITMOD by Wilf Rigter)
  NMI CALL 66h             - 12 cycles    - 12 cycles
  NMI handler              - 32 cycles    - 29 cycles (NMIPATCH by Nocash)
  Remaining user time     = 149 cycles   = 166 cycles
Recursing both upper and lower border, 54*2 scanlines per frame are available for user programs in 50Hz mode; only 30*2 in 60Hz mode.

Resulting User Available CPU Time
  SLOW, 50Hz Effective Speed  0.804600 MHz (54*2*149 cycles, 50 frames) (ori)
  SLOW, 50Hz Effective Speed  0.896400 MHz (54*2*166 cycles, 50 frames) (tuned)
  SLOW, 60Hz Effective Speed  0.536400 MHz (30*2*149 cycles, 60 frames) (ori)
  SLOW, 60Hz Effective Speed  0.597600 MHz (30*2*166 cycles, 60 frames) (tuned)
  FAST, Total CPU Speed       3.250000 MHz (display disabled)
  ZX80/PAUSE/INPUT            0.0 MHz      (user program stopped)
For more info on tuning, see:
HW Making the ZX81 Faster


 ZX80/ZX81 Video Character Set

First, here's the ZX81 character set (64 characters)
  ____0___1___2___3___4___5___6___7___8___9___A___B___C___D___E___F____
  00 SPC [' ][ '][''][. ][: ][.'][:']{::}{..}{''} "  GBP  $   :   ?  0F
  10  (   )   >   <   =   +   -   *   /   ;   ,   .   0   1   2   3  1F
  20  4   5   6   7   8   9   A   B   C   D   E   F   G   H   I   J  2F
  30  K   L   M   N   O   P   Q   R   S   T   U   V   W   X   Y   Z  3F
For the ZX80, some characters are located at other positions:
  ____0___1___2___3___4___5___6___7___8___9___A___B___C___D___E___F____
  00 SPC  "  [: ][..][' ][ '][. ][ .][.']{::}{..}{''}GBP  $   :   ?  0F
  10  (   )   -   +   *   /   =   >   <   ;   ,   .   0   1   2   3  1F
  20  4   5   6   7   8   9   A   B   C   D   E   F   G   H   I   J  2F
  30  K   L   M   N   O   P   Q   R   S   T   U   V   W   X   Y   Z  3F
For both ZX80 and ZX81, all characters can be displayed normal (Bit 7 cleared) or inverted (Bit 7 set). For SPC and GRA see below, GBP means the 'pounds' currency symbol.

Block Graphics
For [solid] symbols, all 16 combinations of block graphics (with any number of 4x4 pixel dots at any position within a 8x8 pixel character) can be produced by using the invert-attribute bit. Only limited combinations are possible for {dithered} symbols.

ZX81 charset is stored at 1E00h-1FFFh in BIOS ROM (I register I=1Eh), ZX80 charset at 0E00h-0FFFh (I=0Eh).

The lower 9 bits (A8-A0) of the characterset are addressed by the 'display controller' (overriding the CPU supplied address signals, which outputs the whole IR register to the address bus) - the special address bits are output to the ROM chip only (but not to RAM, nor to Expansion Port), so neither internal, not external RAM can be used to store character data.


 ZX81 Video Nonstandard UDG/CHRS Expansions

UDG/CHRS boards (User Defined Graphics / Character Set expansions)

Internal Expansions
The ZX80/ZX81 has two variants of A0-A8 address lines: The normal A0-A8 are from the CPU, and are wired to RAM and Expansion Port. For character set addressing A0'-A8' are generated by the ULA, and wired to the BIOS ROM.
The dk'tronics/kayde expansions are mounted as internal daughterboard, plugged to the BIOS ROM chip. That method allows to use the A0'-A8' lines.

External Expansions
The QS expansion connects to expansion port. Accordingly, it must externally reproduce some of the ULA functionality (latch the character number & increment the LINECTR) in order to generate its own A0'-A8' signals. The QS also takes the inverse flag as character MSB (aka A9'), allowing to use 128 different symbols rather than only 64.

dk'tronics 4K Graphics ROM (plus optional RAM)
Contains a 4K EPROM, mapped to 2000h-2FFFh, with seven new character sets (of 64 symbols each), the region at 2E00h-2FFFh is mainly FFh-filled, and contains only a few USR functions for selecting the new character sets (each one: "LD A,20h+N*2, LD I,A, RET", except N=7: selects the default ZX81 charset with I=1Eh). As an upgrade option, the board can be fitted with 1K or 2K SRAM, or 4K EPROM (mapped to 3000h-3xFFh). dk'tronics offered 6 programs for use with the hardware:
  Asteroids
  Centipede
  Defender
  Meteor Storm
  Space Invaders
  Character Development System (aka UDG, User Defined Graphics editor)
The pre-defined ROM symbols are possibly useful for some games, but useless for others. Only 64 symbols can be used at once, for example one cannot simultaneously display lower-case letters and numeric digits. Many symbols (like A-Z) are duplicated in several 64-character banks, so one gets far less than 512 <different> characters.
Note: Of the above 6 programs, only Space Invaders and UDG are found in the internet. There's also a Centipede version, but it doesn't support the hardware (it can be somewhat tweaked by typing "RAND USR 11904" before "RUN", but this causes its text screens to become distorted, only its game screen looks more or less as it should).

Kayde 4K ROM (plus optional RAM)
This is a (probably unlicensed) clone of the dk'tronics 4K character board. Locations 000h..7FFh and A00h..FFFh in the ROM are 1:1 byte-identical to the dk'tronics ROM. Locations 800h..9FFh are modified: Containing inverse symbols in dk'tronics ROM, and Pac-Man symbols in Kayde ROM. Kayde advertised three games that support their hardware:
  Centipede
  Peckman (this one probably requires the Kayde-ROM with Pac-Man symbols)
  Space Invaders
The circuit with three logic chips & three ROM/RAM sockets looks identical to the dk'tronics board (aside from some cosmetic differences: top/bottom layers exchanged, and Kayde text instead of dk'tronics text on the PCB). Even the advert seems to be a shameless copy of the dk'tronics one (though with slight mis-spellings in the 1st advert: saying it "fits nearly" instead of "fits neatly"). dk'tronics had adverts in Sinclair User issues 1..8, Kayde had adverts in the same magazine, issues 4..8.
Note: One or two years later, Kayde also (illegally) cloned Compusounds patented "Telesound 84" (an add-on for amplifying the Spectrum's ULA sound via TV).

QuickSilva QS CHRS board
Maps 1K RAM to 8400h-87FFh, allows to define and display 128 characters (64 normal, and, independently thereof, 64 inverted characters) (for example, the inverse ones can be assigned as lowercase letters).
On power-up, the RAM is uninitialized, so garbage would be displayed. Quicksilva has "solved" this problem by providing a mechanical switch, where the user can (must) manually enable/disable the CHRS feature (there is no I/O port to do this by software). Moreover, the board has 4 DIP switches, presumably for adjusting HSYNC or so (should be all off for ZX81 in SLOW mode, or switch 3,4 on for ZX80 and ZX81 in FAST mode) (whereas "FAST" mode is probably meant to mean PAUSE/INPUT, accordingly software should avoid to switch between SLOW mode and PAUSE/INPUT).
Depending on the invert bit, the first or second 512 bytes are mirrored to 1E00h on ULA access (however, the CPU is still able to read ROM from 1E00h; though, eventually, ROM-reading MIGHT work only when the enable switch is off?)
Emulating the QS CHRS board with its original crude manual enable switch is easy, but automatically detecting and enabling it is more difficult, possible ways are: Detect the original driver (won't work with custom drivers), or, detect if 8400h..87FFh contains data whilst all other RAM at 8000h and up is zero-filled (or filled with whatever values you stored there on power-up).

Haven Hardware chrs board
Another character thing. Seems to be able to map RAM (or ROM?) to 1E00h-1FFFh and 3E00h-3FFFh. Low-level details are unknown? (sounds similar to the QS board)

AGB UDG graphic card
Whatever. Reportedly mirrors 512 byte RAM to 1E00h.

Memotech UDG Interface
Whatever - if it does exist at all. It is mentioned only on one single webpage.

Unknown UDG Type
There is a game called "FROGGER-HR" by "E LANTING". It sets I=20h and uses character RAM at 2000h (similar to dk'tronics/kayde, but having RAM at that location, not ROM). Unknown which hardware does support this (if any - it's also possible that the game is homebrewn and works only with emulators).


 ZX81 Video Nonstandard HIRES Expansions

QuickSilva QS HI-RES screen board
Maps 2K ROM to 2800h-2FAFh (errr, more likely: 2800h..2FFFh or so), and 6K RAM to A800h-BFFFh. Low-level details are unknown?

Memotech HRG
Maps 2K EPROM to 2000h-27FFh. The EPROM contains USR function for high-resolution PLOT and LINE drawing functions. Supported resolution is 248x192. Seems to require a separate RAM expansion for use as video RAM. Aside from the EPROM, it does contain an overkill of five 74LSxx chips, and two PAL chips. Low-level details are unknown?

G007 Graphic Card
Whatever.

HRG-MS (Matthias Swatosch)
Technically same as Wilf Ritger's hires, but doesn't work unless first loading an additional software driver from cassette, before loading the actual program. The driver contains some USR functions for drawing LINEs from within BASIC - in the BASIC era it would have been revolutionary, nowadays... it isn't. The driver loading is more than uncomfortable, and BASIC is crap anyways.


 ZX81 Video Nonstandard Picture Size

The official picture size is 32x24 tiles (256x192 pixels) with huge borders. However, on a PAL screen, the ZX81 can display 40x34 tiles (320x272 pixels).

34-lines Quicksilva Defender
Displays 31x32 tiles (the advert claims 34 lines, but it's actually only 32 lines). There's some glitch in the blanking/retrace that causes the topmost lines to become ultra-bright and horizontally unstable (at least on my Philips TV Set). Moreover, the framerate is only 43Hz (that part works fine on my TV Set).
Note: The game was also announced for ZX80 and works without NMI (even the ZX81 version doesn't use NMIs).

Wilf Ritger's WRX1K
A variant of Wilf Ritger's WRX hires. WRX1K works on an unexpanded ZX81 with 1K RAM, accordingly, the picture/bitmap is tiny.

MAXDEMO
Displays 40x32 tiles text and 320x256 pixels hires. There seems to be a vertical centering glitch: No upper border visible, but 16 lines lower border visible.

Mixed Video Modes
  Highres Chess (192 hires lines, plus 1-2 text lines)
  Maxdemo (splits hires/text regions)


 ZX81 Video Nonstandard Color Expansions

Lambda Color Module
External Color Module designed for the Lambda 8300. Contains 1K Color RAM (with color attributes similar as on ZX Spectrum). Supports 8 colors, but the palette is still unknown.
It should also work with ZX81, though with some restrictions: The normal ZX81 BIOS doesn't initialize the color attributes on power-up (upon power-on the module is disabled, displaying a "white-on-white?" picture; to activate color RAM one needs to init the RAM at 2000h-2FFFh and then enable it via write to 3001h) (the unit isn't re-disabled on warmboot via /RESET, only on power-on), and the ZX81 doesn't have a composite video output (so one would need some soldering to extract the monochrome video signal), and, compared to Lambda, the ZX81 has black/white inverted, so ink/paper will be exchanged.
See Lambda 8300 for details.

Interface 8 x Couleurs / Carte Couleur (PCB: "DISTR.(C) par S.A.M. 001")
A french add-on for the ZX81. Sold by EREL Boutique.
The interface doesn't contain any RAM for storing color attributes. Instead, it seems to watch the character numbers being read from D_FILE, and applies one of the 8 colors when seeing CHR numbers 86h..8Dh (aka inverse [A]..[H] symbols). The palette is unknown. Also unknown if it changes the foreground and/or background color. Also unknown if the [A]..[H] symbols are displayed, or if they are masked.
Video Output: Outputs UHF (SECAM) and Peritel (SCART, ie. probably RGB). UHF output is done using ZX81's internal UHF modulator (requires some soldering) (the signal can be adjusted by potentiometers on the interface, so colors might be "user-selectable" rather than having a constant palette?). Peritel requires a special cable that connects to a spare DIL socket on the interface (the pin-outs of the socket are unknown, so without the original cable, one could only guess which pins a R,G,B).

Carte couleur
Reportedly made by (provider: DIRECO International).
Claimed to support 16 colors with SCART.
Claimed to use ASCII... uh, ASCII, on a ZX81?
Said to be Chr $ 161 is the first color (pink).

the peripheral color (catalan translation)
As the highest town, the play calling this peripherique tour and see the iron because Souder Souder had a cable on the peripheral of the anode of diode D9 ordinateur, fortunately, a printed circuit board with the plan had branchement delivered with the peripheral.
The curseurs colored bands disappeared and became each other: the F curseur became a green hand, the K a white band, G one side magenta and yellow L a band. You could play with a palette of 16 colors:
  White plays 4 or CHR invested $ 160
  Yellow key 5 or CHR $ 161
  Light Blue plays 6 or CHR $ 162
  Green light touch 7 or CHR $ 163
  Fusch plays 8 or CHR $ 164
  Red key 9 or CHR $ 165
  Keyboard has blue or CHR $ 166
  Dark gray or key B CHR $ 167
  CHR turn gray or C $ 168
  Kaki plays D or CHR $ 169
  Cyan play E or CHR $ 170
  Green plays or F CHR $ 171
  Magenta G plays or CHR $ 172
  Carmin playing H or CHR $ 173
  And navy blue key or CHR $ 174
  Black J key or CHR $ 175
Example:
  10 PRINT CHR $ 173
  20 PRINT "THREE"
carmine dye gave the word THREE. So then each word you wanted color.


 ZX81 Joystick Expansions

Joystick Ports and Data Bits (U=Up, D=Down, L=Left, R=Right, F=Fire)
  Port_Joystick_Type___________________________7_6_5_4_3_2_1_0___Data Bits___
  EFFE Nocash A12 joystick (keys 6,7,8,9,0)    - - - D U R L F   (0=Pressed)
  EFFE AGF    (cursor mode, A12, keys 6,7,8,0) - - - D U R - F   (0=Pressed)
  F7FE AGF    (cursor mode, A11, key 5)        - - - L - - - -   (0=Pressed)
  DFFE AGF   (2nd Joystick, A13, keys Y,U,I,P) - - - D U R - F   (0=Pressed)
  FBFE AGF   (2nd Joystick, A10, key T)        - - - L - - - -   (0=Pressed)
  nnFE PC8300 (ZX81 clone using A9..A13, bit3) - - - - x - - -   (0=Pressed)
  xx1D Zebra Joystick                          - - - F R L D U   (0=Pressed)
  xxBF Timedata ZXM Sound Box, PSG Reg 14      0 F U D L R y x   (0=Pressed)
  xxDF Mikro-Gen (Digital joystick)(Frogs)     - - - F L R D U   (0=Pressed)
  MEM  Mikro-Gen (Analog joystick)             F a a a a a a a   (analog)
  MEM  Quicksilva Sound Board, PSG Reg 15      ? ? ? ? ? ? ? ?   (?=Pressed)
  ???? Microdigital TK83/TK85 (DIN socket)     ? ? ? ? ? ? ? ?   (?)

Quicksilva "Joystick" Games
As far as known, Quicksilva has NOT manufactured a joystick interface. However, at least one Quicksilva game (Asteroids) allows to use PSG reg 15 (of the QS Sound board) as joystick port (where the user needs to wire the joystick to the 16bit I/O port).

Micro Gen - for Micro Gen's "Space Raider" game (maybe also for "Bomber")
For several Micro Gen games. Requires their 4-channel A/D converter board. Can be used with 2 joysticks - that is, apparently ANALOGUE joysticks.
Note: The company name is written with "c" or "k": Micro Gen (adverts and catalog), or Mikro Gen (cassette inlays).
  IN (xxDFh) -- bits 1,2 // lunar: bit2,3,4
      xx == select something ?
scramble: LD A,(3E80h) others: LD HL,3E80h (3E80h is 16000 decimal)
scramble uses ONLY 3E80 (not DF), and supports up/dn/rt/fire

Mikro-Gen Analog Joystick (Memory 3E80h)
Memory mapped to address 3E80h (=16000 decimal). Write to 3E80h:
  00h  Axis 0 (request vertical position, and bit7=not used)
  01h  Axis 1 (request horizontal position, and bit7=Fire button 1=Pressed)
  ??h  Axis 2,3 (for second joystick) (not used by any games)
There must be a delay after writing (ld b,96h, djnz $). Thereafer, reading from 3E80h:
  returns 7bit analog for selected axis (and bit7=fire, if any)
  vertical:   00h..7Fh = up..down
  horizontal: 00h..7Fh = left..right
Supported by several Mikro-Gen games (and one Quicksilva game):
  Space Invaders (Mikro-Gen) (uses analog value as screen position)
  Scramble (Mikro-Gen) (converts analog value to digital move/no move)
  Bomber (Mikro-Gen) (supports both digital and analog joysticks)
  Croaka Crawler (aka Hopper) (Quicksilva)

Mikro-Gen Digital Joystick (Port DFh)
This is the "inofficial" digital joystick used in several Mikro-Gen games.
  Lunar Rescue (Mikro-Gen)
  Frogs (Mikro-Gen)
  Bomber (Mikro-Gen) (supports both digital and analog joysticks)
  Tempest (also does a single read from 3E80h, but otherwise supports DFh only)
Inofficial meaning that Mikro-Gen doesn't ever seem to have advertised the digital joystick (either they did silently replace their analog joystick by the digital one, or above games are homebrewn patches).

Thurnall Electronics - see advert from them
Spectrum Version advertised as "The Winners Joystick". Supports Cursor+Rubout keys, and Q-A-Z-X keys.

AGF Joystick (from AGF Hardware)
Available for ZX81 and Spectrum. Both versions work as so:
  5-6-7-8-0  Joystick 1 (Cursor keys & rubout)
  T-Y-U-I-P  Joystick 2 (same data bits as above, but other address lines)
The joystick interface connects to expansion port. For some reason, only the keyboard OR joystick can be operated at a time - there is a switch to select between keyboard and joystick mode.

AGB - JOYSTICK II : [driver in the P_Tools directory]
  Reportedly: Use the $DF & $EF IN IRQ Port.
  Port A:  Press "5" LEFT "6" DOWN "7" UP  "8" RIGHT "New/Line" FIRE
  Port B:  Press "A" LEFT "S" DOWN "W" UP  "D" RIGHT "F" FIRE

Timedata ZXM Sound Box - includes joystick port (PSG reg 14)
No known software supporting this.

Zebra Joystick (US) (Port 1Dh)
Supported by a homebrewn Hires Space Invaders game. Zebra also released some patches for a handful of third-party games.
hrinvaders --> zebra joystick, port 1Dh
 0   up    (0=Pressed)
 1   down  (0=Pressed)
 2   left  (0=Pressed)
 3   right (0=Pressed)
 4   fire  (0=Pressed)
 5-7
zebra listing
 1 REM 123456789
 2 POKE 16514,219    DB ;\IN A,[1Dh]
 3 POKE 16515,29     1D ;/
 4 POKE 16516,47     2F ;-CPL
 5 POKE 16517,230    E6 ;\AND A,1Fh
 6 POKE 16518,31     1F ;/
 7 POKE 16519,6      06 ;\LD B,0
 8 POKE 16520,0      00 ;/
 9 POKE 16521,79     4F ;-LD C,A
 10 POKE 16522,201   C9 ;-RET
 20 PRINT USR 16514;


 ZX81 Sound Expansions

ZON-X81 Sound (Bi-Pak)
Contains a PSG sound chip, built-in speaker with manual volume control. There seems to be no user I/O port.
  F = 1625000 Hz / 16 / nnn
Parameters are poke'd to RAM, and then forwarded via USR call.
Uses "1 REM YYPEEK TO YYPEEK ?TAN"
  OUT (CFh or DFh),A  ;index
  OUT (0Fh),A  ;data (w)
The manual is unclear about the "index" port:
BASIC example suggests "TO" token = port DFh.
But ASM example suggests port CFh.
Maybe both are working. Anyways, the recommended address would be DFh (assuming that the manual was mainly addressed to BASIC programmers, and the ASM example being only a confused side note). The technical specs in the french "Interface Sonore" manual uses DFh, too).
Note: The Spectrum version uses ports FFh and 7Fh, maybe these do work on ZX81, too (though under BASIC, 7Fh cannot be entered via keyboard, which may explain why the manual used other values)
Supported by
  Cosmic (brazilian magazine Revista Micro Sistemas, Oct/1986)
  Lunar 10 (brazilian magazine Revista Micro Sistemas, Nov/1986)

ZXM Sound Box (Timedata) (OUT 159/223)
For ZX81 and Spectrum.
Contains a 8912 PSG sound chip, built-in speaker with manual volume control, can be optionally connected to external amplifier. There is a 9pin DSUB atari/commodore joystick port; can be also used as 7bit user I/O port.
  F = 100000 Hz / nnn     ;approximately, according to manual
the above 100kHz is probably meant to be 3.25MHz/32.
  OUT (159),A  ;9F index    ;\same addresses for ZX81 and Spectrum, ZX81
  OUT (223),A  ;DF data (w) ; accesses via POKE+USR, Spectrum via OUT token
  IN  A,(191)  ;BF data (r) ;/   <--- for joystick port
DSUB 9pin male Pin-outs
  Pin  Bit
  1    PSG.PortA.Bit5   up / forward
  2    PSG.PortA.Bit4   down / back
  3    PSG.PortA.Bit3   left
  4    PSG.PortA.Bit2   right
  5    PSG.PortA.Bit1   general purpose (usually 1) (C64: Pot Y)
  6    PSG.PortA.Bit6   Fire
  7    +5V
  8    GND
  9    PSG.PortA.Bit0   general purpose (usually 1) (C64: Pot X)
According to manual, PSG.PortA.Bit7 is always zero.
Supported by:
  Sound Box Super Editor (Timedata) (zxm demo tape & type-in listing in manual)

QS Sound Board (QuickSilva)
AY-3-8910. Two 8bit I/O ports. Reportedly memory mapped to 7FFEh..7FFFh (see Sinclair User, Issue 2, May 1982, Page 24).
  7FFFh index (W)
  7FFEh data (R/W)
Supported by:
  Cosmic Guerilla (Quicksilva)
  Croaka (aka Hopper) (Quicksilva)
  Defenda (Quicksilva)
  Space Invaders
  Asteroids (Quicksilva) (also supports PSG reg 15 as alternate joystick)
  Scramble (Quicksilva)
PSG reg 15: used by asteroids (as OUTPUT, probably as pull-up)

William Stuart Systems ZX80/ZX81 Music Synthesiser
Three-part music and sound effects. 16-line control port for home security, robot control, etc. Can be used with the "Composer" music program.

Telesound 82 (ZX80/ZX81 version) - stops flicker (is THAT sound related?)
3-channel Sound with 16bit I/O port (Bolton Electronics)
Motherboard plus Sound-Card (Watford)

Interface Sonore
(assumed to be from Mageco Electronic)
(actually, seems to be same as the Bi-Pak thing)
AY-3-8912
Manual page 3 contains (distorted) instructions for "1 REM Y1PEEK TO Y2 ?TAB"
which would mean "LD A,index; OUT [DF],A; LD A,data; OUT [0F],A; RET"
  But...
Manual page 79-80 contains "Carte Sonore QS" 7FFEh,7FFFh stuff.
These pages probably refer to a DIFFERENT and MEMORY MAPPED sound interface.
Namely, to the QS Sound Board (see above).

ZXS Speech Synthesiser (Timedata)
DCP Microdevelopments Speech Pack (1982) for ZX81
Speech hardware. Both DCP and Timedata also released versions for ZX Spectrum. ZXS might be allophone based (?), DCP isn't allophone based (it uses a fixed digitized vocabulary stored in ROM).

Lambda 8300
ZX81 clone with built-in speaker. The speaker is software controlled (toggled HIGH/LOW via IN A,[F5h]).


 ZX80/ZX81 Memory Map and System Area

Overview
  0000-1FFF   BIOS ROM (8KBytes)
  2000-3FFF   not used
  4000-43FF   Internal RAM (1 KByte)
  4000-7FFF   External RAM (16 KBytes)
Internal RAM is disabled (and cannot be accessed) when external RAM is installed.

ZX81 RAM Map
  4000      System Area (see below)
  407D      BASIC Program
  D_FILE    Video Memory (BG Map)
  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 is meant to be the CPUs SP register)
  ERR_SP    GOSUB Stack
  RAMTOP    USR Programs (Begin of unused/reserved memory)

ZX81 System Area
  Addr.  Name    Expl
  4000   ERR_NR  Errorcode-1
  4001   FLAGS   Various BASIC Control flags
           Bit0=used (purpose unknown)
           Bit1=Redirect Output to printer
           Bit2=used (purpose unknown)
           Bit3-5=not used
           Bit6=used (purpose unknown)
           Bit7=used (purpose unknown)
  4002   ERR_SP  Pointer to top of Machine Stack / Bottom of GOSUB Stack
  4004   RAMTOP  Pointer to unused/free memory (Changes realized
                   at next NEW or CLS)
  4006   MODE    Selects [K], [L], [F], or [G] Cursor
  4007   PPC     Line Number of most recently executed BASIC line
  ---Begin of Save Area---
  4009   VERSN   Should be 00h to identify ZX81 cassette files (FFh=Lambda)
  400A   E_PPC   Line Number of currently selected line [>] Cursor
  400C   D_File  Pointer to Video Memory (BG Map) / End of Basic Program
  400E   DF_CC   Pointer to VRAM Address for PRINT
  4010   VARS    Pointer to BASIC Variables Area
  4012   DEST    Pointer to Variable when assigning a value to it
  4014   E_LINE  Pointer to Input Buffer/Workspace, and to --End of Save Area--
  4016   CH_ADD  Pointer to next interpreted character
  4018   X_PTR   Pointer to character prior to [S] Symbol (=Syntax Error) (or
                   ptr to aborted/breaked line)
  401A   STKBOT  Pointer to BASIC Calculator Stack / End of
                   Input Buffer/Workspace
  401C   STKEND  Pointer to bottom of Machine Stack / End of Calculator Stack
  401E   BERG    Calculator B-Register
  401F   MEM     Pointer to Calculator Memory (usually same as MEMBOT)
  4021   -       Not used
  4022   DF_SZ   Number of lines in lower display section (including 1 blank
                   line)
  4023   S_TOP   Line Number of first line for automatic LISTing
  4025   LAST_K  Keyboard - Recently pressed key (4025=row, 4026=shift/column)
  4027   DEBOUN  Keyboard - Debounce State (key release delay)
  4028   MARGIN  Vertical Border Height (55 lines at top/bottom for 50Hz,
                   31 for 60Hz)
  4029   NXTLIN  Address of next BASIC line which is to be executed,
                   pointing to a byte >=40h when stopped, indicates
                   autostart address in cassette files.
  402B   OLDPPC  Line Number for CONT
  402D   FLAGX   Various Flags
                   Bit0   used (purpose unknown)
                   Bit1   used (purpose unknown)
                   Bit2-4 not used
                   Bit5   used (purpose unknown)
                   Bit6   used (purpose unknown)
                   Bit7   not used
  402E   STRLEN  Length of string during assignment
  4030   T_ADDR  Pointer to next item in Syntax Table (or INPUT's old S_POSN)
  4032   SEED    Random Number Seed
  4034   FRAMES  Decrementing Video Frame Counter (Bit15: 0=PAUSE, ie.
                   display ON, program PAUSEd)
  4036   COORDS  X-Coordinate of last PLOT, Y-Coordinate of last PLOT
  4038   PR_CC   Least significant byte of PRBUFF printer buffer pointer
  4039   S_POSN  X-Coordinate for PRINT, Y-Coordinate for PRINT
  403B   CDFLAG  Various Flags
                  Bit7: Current Speed (1=SLOW (Display Enable), 0=FAST)
                  Bit6: Requested Speed (or old speed during
                    pause/cassette io, etc)
                  Bit5-1: Not used
                  Bit0: Keystroke (0=None, 1=Yes)
  403C   PRBUFF  Printer Buffer 32 characters + NEWLINE (76h)
  405D   MEMBOT  Default workspace for BASIC Calculator
  407B   -       Not used (2 bytes)

ZX80 Memory Map
  4000..4027                System Area
  4028..(4008)-1            Basic Program
  (4008)..(4004 or 400A)-1  VARS
  (4004 or 400A)..(400C)-1  Input Buffer, and probably something else ???
  (400C)...                 VRAM
  ...

ZX80 System Area
  4000 ERR_NR    Error Number (one less than report code)
  4001 FLAGS     Various Flags to control BASIC System
                  7  1-Syntax off        0-Syntax on
                  6  1-Numeric result    0-String result
                  5  1-Evaluating function (not used)
                  3  1-K cursor          0-L cursor
                  2  1-K mode            0-L mode
                  0  1-No leading space  0-Leading space
  4002 PPC       Line number which is to be executed next (bit15: 1=stopped)
  4004 P_PTR     Position in RAM of [K] or [L] cursor
  4006 E_PPC     Line Number of current line with [>] cursor (for LIST)
  4008 VARS      Address of start of variables area (end of BASIC Program)
  400A E_LINE    Address of start of Edit Line (end of VARS) (-save area end-)
  400C D_FILE    Start of Display File (VRAM) (end of Edit Line/Input Buffer)
  400E DF_EA     Address of the start of lower screen
  4010 DF_END    Display File End
  4012 DF_SZ     Number of lines in lower screen
  4013 S_TOP     The number of first line on screen
  4015 X_PTR     Address of the character preceding the [S] marker
  4017 OLDPPC    Line number to which CONTINUE jumps
  4019 FLAGX     More flags.
                  7  1-K mode            0-L mode
                  6  1-Numeric result    0-String result
                  5  1-Inputting         0-Editing
  401A T_ADDR    Address of next item in syntax table
  401C SEED      The seed for the random number
  401E FRAMES    Count of frames shown since start-up (incrementing)
  4020 DEST      Address of variable in statement
  ---Active/Basic:
  4022 RESULT    Value of the last expression
  4024 S_POSN_X  Column number for print position
  4025 S_POSN_Y  Line number for print position
  4026 CH_ADD    BASIC program pointer (address of next char/token)
  ---Pause/Input:
  4022 -         Keyboard debounce
  4023 MARGIN    Screen border height
  4024           ?
  4026 LAST_K    Keyboard last key pressed (4026=row, 4027=column)


 ZX80/ZX81 Memory Mirrors and Expansions

Memory Overview
RAM is originated at 4000h, for 1K RAM: at 4000h-43FFh; 48K: at 4000h-FFFFh, the area below 4000h is used only when more than 48K are installed.
In the ZX world, memory accesses can be split into three categories: data read, data write, and opcode read. Opcode read is sensed by the CPUs /M1 signal, and behaves different than normal data read in case that A15 is HIGH, ie. for addresses in range from 8000h-FFFFh.
  address     code  read  write
  0000..1FFF  ROM   ROM   ---    ;all ZX81's
  2000..3FFF  RAM4  RAM4  RAM4   ;ZX81 with 56K only
  4000..7FFF  RAM1  RAM1  RAM1   ;ZX81 with 16K or more RAM
  8000..BFFF  VRAM2 RAM2  RAM2   ;ZX81 with 32K or more RAM
  C000..FFFF  VRAM1 RAM3  RAM3   ;ZX81 with 48K or more RAM (*)
  (*) That, for the RAM3 part, VRAM1 is of course found in all ZX with 16K
The memory region 8000h-FFFFh cannot be used to execute machine code programs; any opcodes in that region with Bit 6 cleared are treated as video output (and are executed as NOPs).
Opcodes/video data at C000-FFFFh are read from memory at 4000h-7FFFh - the software should usually write video data into memory at 4000h-7FFF, and 'execute' the data in the mirrored region at C000-FFFF.

1K RAM
Default ZX81 includes only 1K RAM at 4000h-43FFh. However, the default RAM and ROM select signals are mirroring ROM across 0000h-3FFFh and 8000h-BFFFh, and RAM at 4000h-7FFFh and (including 'data read' accesses) at C000h-FFFFh.
The ZX81 mainboard provides space for either two 1K x 4bit SRAM chips, or one 1K x 8bit SRAM chip (with L1 jumper closed).

2K RAM
The american 'ZX81' (Timex TS1000) appears to have been delivered with 2K internal memory. The socket for 1K x 8bit SRAM on the ZX81 mainboard may be used (by closing L2 jumper) for a 2K x 8bit SRAM chip.

16K RAM
Even though above described 1K RAM signals are providing memory space for up to 16K RAM, the Memotech expansions (not sure about Sinclair or other expansions) are supplying their own RAM and ROM select signals; ROM is then located at 0000-1FFF only, and RAM at 4000-7FFFh only, all other areas are unused, typically 'FFh filled'. Except that, video memory opcode reads (but not data reads) from C000h-FFFFh are mirrored to 4000h-7FFFh as usually.
Timex TS1500 has been delivered with 16K built-in RAM.

The 16K RAM configuration may be more or less treated as standard configuration - programmers should recurse that below expansions of 32K or more RAM haven't been very popular - thus any programs that require more than 16K memory won't work on most ZX computers.

32K RAM
RAM is located at 4000h-BFFFh, whereas the upper half may be used to store data and/or to 'execute' video code, but not for normal machine code program code.
Note that the BIOS memory detection ends at 8000h, the BIOS will detect only a maximum of 16K RAM - and the stack pointer will be then initiated at 8000h. Thus, loading large cassette files will overwrite the stack. When using more than 16K RAM, the RAMTOP identifier in the system area must be changed manually by POKE instructions, and then applied by a NEW instruction (or by a short program that moves stack data and stack pointers to the new addresses).
Care should be taken that video memory may not cross the 7FFFh/8000h boundary; Video data at 7FFFh is executed by addressing FFFFh, and thus the next address will be 0000h instead 8000h! Ie. video memory may be located in either one of the two 16K blocks, not in both.

48K RAM
RAM is located at 4000h-FFFFh, same restrictions as for 32K RAM apply. The memory at C000h-FFFFh can be used as data storage only, but not for machine code execution, and not for video data 'execution'.
When patching the RAMTOP value use the maximum of FFFFh (indicating 48K minus one byte), as video memory must be below C000h, BASIC program code is restricted to less than 32K as well, BASIC variables may use the additional memory though.
Some 16K expansions can be combined with 32K expansions to gain a total of 48K RAM.

64K RAM
Even though called "64K" expansions, most or all of these expansions do not seem to support bank switching which'd allow to switch RAM into the 8K BIOS ROM area at 0000h-1FFFh, so only 56K of RAM at 2000h-FFFFh can be used.
As for 48K RAM, the highest RAMTOP value would be FFFFh, the 'RAMBASE' is fixed at 4000h, so that the additional memory at 2000h-3FFFh cannot be used by the BIOS/BASIC interpreter.


 ZX80/ZX81 Memory Binary Data/Machine Code Programs

Depositing Binary Data/Program in Memory
The ZX81 does not directly support loading binary files from cassette, when using LOAD, a memory image is loaded which contains the BIOS system variables, the BASIC program, the video memory, and the BASIC variables.
The following are places that can be used as reserved area for binary code.

Using a REM instruction (in the program area)
Preferably locate the REM in the first line number (ensuring that it is located at a fixed memory address), the length of the comment must provide enough space for the program, then use POKE or else to fill the program code into it.
RESTRICTION: The comment may not contain the value 76h (the NEWLINE character). Thus, the assembler program should not contain a "HALT" instruction (opcode 76h), or any other opcodes with operand 76h such like LD A,76h or JP 4276h etc., or any data definitions such like "DB 76h".

Using a string (in the variables area)
Define a string such like A$ and fill it by binary character numbers. Note that all variables are saved by SAVE command, it is not necessary that the program itself contains the LET A$=... definition.
RESTRICTION: The position of the string in memory may change when (re-)defining other variables, when modifying the BASIC program, or when altering video memory contents.

Using memory above RAMTOP (outside of the known memory)
By default, RAMTOP (4004h) points to the address following to the detected RAM area (4400h for 1KByte RAM). The user can alter this address by using POKE, then type NEW to let the BIOS realize the new value, all memory above RAMTOP may now be used for whatever purposes.
RESTRICTION: The (standard) SAVE instruction does not save this area to cassette.


 ZX80/ZX81 Cassette File Images

.81 and .80 Files
These are 1:1 copies of the content of real ZX81 and ZX80 cassette files.
ZX81 files are copies of the memory area 4009h up to E_LINE-1, the filename (which is usually part of ZX81 files) is not included in the file.
ZX80 files are copies of the memory area 4000h up to E_LINE-1, the filename is obviously not included as real ZX80 files do not have names.

.P and .O Files
Basically, these are identical as .81 (.P) and .80 (.O) files, except that an unpredictable amount of garbage is meant to be attached to each file.
Older versions of the Xtender emulator seem to have attached 1 byte of garbage. The current Xtender version apparently dropped this behaviour, and saves correct length. Files at ftp.nvg randomly contain between 28-38 bytes of garbage, probably caused by a cassette-to-disk transfer program. And some files appear to have went through a CP/M filesystem, which caused the length to be rounded up to multiples of 128 bytes.
Programs that deal with these files should determine the correct length (by examining the header/system area), and truncate the extra bytes.

.P Files with Filename
Some .P files contain the original "FILENAME.P" (in ZX81 charset) at 003Fh (within the PRBUFF area) (the purpose is probably to "preserve" long filenames in MSDOS) (the preceeding bytes at 003Ch are usually 00-1A-1C, or in FROGGER-HR: 00-1C-1C (unknown purpose)).

.TZX Files
This format was originally designed for the Spectrum, mainly intended to encode nonstandard formats (copy-protections / turbo loaders). It can be also used (via ID 19h) for ZX80/ZX81 programs. The format is quite complicated - it's advantage is that it can contain more than one file (a feature required for games that are split into two or more files).
Spectrum Cassette TZX Format

.C and .S and .V and .B or else
These are not actually real ZX files, programs that include such files won't work on real ZX81 or ZX80, nor in no$zx. The Xtender emulator includes several custom functions, allowing the user to create or delete directories on the harddisk, probably as well as to format it, and to save these kind of files.


 ZX80/ZX81 Cassette File Content

ZX81 Data Field (excluding filename)
The data field is loaded to address 4009h, and it contains the system area (excluding the first 9 bytes), the basic program, the video memory, and VARS area.
The system area should contain proper data. Some entries are of special interest:
  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)
Memory at 403Ch and up may be misused for whatever purpose, video memory is required to contain 25 HALT opcodes if the file was saved in SLOW mode.

ZX80 Data Field
The data field is loaded to address 4000h, and it contains the whole system area, the basic program, and VARS area. Video memory is NOT included in ZX80 files.
The system area should contain proper data. The entry at 400Ah defines the end address (used to calculate the file length). Memory at 4028h and up may be misused for whatever purpose.
Normally, ZX80 files cannot be autostarted - except via "nocash LD H,L trick":
  ;nocash LD H,L trick for autostarting ZX80 files, 9/2009 by martin korth
  4000h  8 x 00h          ;System area: Zerofilled stuff
  4008h  402Ch            ;System area: Pointer to VARS
  400Ah  nn3Dh            ;System area: Pointer to End of file
  400Ch  1Ch x 00h        ;System area: Zerofilled stuff
  4028h  00h,01h,65h,76h  ;BASIC program: Line 0001, LD H,L opcode, NEWLINE
  402Ch  80h              ;VARS area: End code
  402Dh  13h x 00h        ;Unused/padding (for entryoint 4040h)
  4040h  ...              ;Machine code (entrypoint at 4040h)
  xxxxh  (xx3Dh-$) x 00h  ;Unused/padding (for end address xx3Dh)
  xx3Dh                   ;End of file (must be at xx3Dh)
After loading, the BASIC line is LISTed on the screen. The ZX80 BIOS doesn't replace invalid tokens in range of 40h..7Fh by question marks, so Token 65h is written as-is to VRAM (ie. as LD H,L opcode). The file must end at xx3Dh, so the first character line starts at xx40h, which is (normally) excecuted 8 times via JP HL from inside of the IRQ handler. After first execution, the LD H,L opcode changes HL=xx40h to HL=4040h, so the next IRQ jumps to the autostart entryoint at 4040h. That is still done with IRQs enabled, so the first opcode at 4040h should be a DI.
Note: Emulators that do not handle opcodes in VRAM should reproduce autostart as "IF [402Ah]=7665h THEN JP 4040h" after loading the file (that, preferably with registers and memory initialized as on real hardware).

Maximum File Length
Files should usually not exceed 16 KBytes. The memory detection procedure in both ZX80 and ZX81 BIOS stops after 16 KBytes (at 8000h), and initializes the stack pointer at that address, even if more memory is installed. Thus loading files of 16K or more would destroy the stack area (unless a separate loader has previously moved the stack area to another location).
However, most ZXes don't have more than 16K RAM, so bigger files won't work on most computers anyways.


 ZX80/ZX81 Cassette Signals

ZX81 Cassette File Structure
  x seconds    your voice, saying "filename" (optional)
  x seconds    video noise
  5 seconds    silence (only some clock cycles required for ZX81)
  1-127 bytes  filename (bit7 set in last char)
  LEN bytes    data, loaded to address 4009h, LEN=(4014h)-4009h.
  1 pulse      video retrace signal (only if display was enabled)
  x seconds    silence / video noise
The data field contains the system area, the basic program, the video memory, and VARS area.

ZX80 Cassette File Structure
  x seconds    your voice, saying "filename" (optional)
  x seconds    video noise
  5 seconds    silence (at least 0.5 seconds REQUIRED for ZX80)
  LEN bytes    data, loaded to address 4000h, LEN=(400Ah)-4000h.
  x seconds    silence / video noise
ZX80 files do not have filenames, and video memory is not included in the file.

File End
For both ZX80 and ZX81 the fileend is calculated as shown above. In either case, the last byte of a (clean) file should be 80h (ie. the last byte of the VARS area), not followed by any further signals except eventually video noise.

Bits and Bytes
Each byte consists of 8 bits (MSB first) without any start and stop bits, directly followed by the next byte. A "0" bit consists of four high pulses, a "1" bit of nine pulses, either one followed by a silence period.
  0:  /\/\/\/\________
  1:  /\/\/\/\/\/\/\/\/\________
Each pulse is split into a 150us High period, and 150us Low period. The duration of the silence between each bit is 1300us. The baud rate is thus 400 bps (for a "0" filled area) downto 250 bps (for a "1" filled area). Average medium transfer rate is approx. 307 bps (38 bytes/sec) for files that contain 50% of "0" and "1" bits each.


 Hardware, Connectors, Upgrading

Hardware Plugs'n'Pins
HW External Connectors
HW Internal Pins

Hardware Modifications
Below are some ideas of how to attack ZX hardware just by using a few resistors, diodes and/or some wires. Ie. it's all very simple stuff, not requiring special hardware or etched circuit boards, but nethertheless quite useful and effective.

HW Replacing the ZX81 ROM by an EPROM
HW 56K RAM Upgrade
HW Connecting a Joystick
HW Making the ZX81 Faster