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
HW Upgrading RamPaks for True Hi-Res Graphics
HW Connecting a Monitor
HW Getting rid of the 9V DC Power Supply
HW Uploading Programs from the PC to the ZX81


 HW External Connectors

TV - Video Output - UHF Channel 36 (Cinch, female)
  Ring: Shield
  Tip:  TV Signal
Note that modern TV sets (such manufactured after 1981, especially such with automatic channel-detection) might have problems to detect/handle the signal. If necessary, adjust the contrast/brightness, and (as far as supported) adjust the channel manually.

EAR - Cassette Input (from recorder earphone socket to ZX81)
  Ring: Ground
  Tip: Cassette Input

MIC - Cassette Output (from ZX81 to recorder microphone socket)
  Ring: Ground
  Tip: Cassette Output
This is in fact the video signal, output through resistor and capaciator, causing black and white stripes to be displayed during cassette output.

9V DC - Power Supply
  Ring: Ground
  Tip: 9V DC
Even though using the official 9V ZX power supply, the voltage is rather 11V than 9V in my computer. Internally, the ZX81 is operated at 5V, generated from a 7805 voltage regulator, so the input voltage could be anything in range from 7V up to I think 24V. However, a higher voltage would heat up the regulator even more, and be aware that it is output to the expansion port, possibly damaging attached hardware if it is too high.

Power Consumption
Internal 5V, 0.31A, ie. 1.55W, without 16K RAM
Internal 5V, 0.39A, ie. 1.95W, with Memotech Mempak 16KB

Expansion Port (46 Pin Cartridge Slot, male)
Only 44 pins are actually used, the two ---SLOT--- marked pins are cut-out, used as polarisation mark. When attaching a 46pin female plug, note that you must cut off the plastic at the left and right ends, preferably before doing any soldering work.
  Upper Side         Lower Side
   1A  D7             1B  5V DC
   2A  /RAM CS        2B  9V DC
   3A  ---SLOT---     3B  ---SLOT---
   4A  D0             4B  0V
   5A  D1             5B  0V
   6A  D2             6B  CLK
   7A  D6             7B  A0
   8A  D5             8B  A1
   9A  D3             9B  A2
  10A  D4            10B  A3
  11A  /INT          11B  A15
  12A  /NMI          12B  A14
  13A  /HALT         13B  A13
  14A  /MREQ         14B  A12
  15A  /IORQ         15B  A11
  16A  /RD           16B  A10
  17A  /WR           17B  A9
  18A  /BUSAK        18B  A8
  19A  /WAIT         19B  A7
  20A  /BUSRQ        20B  A6
  21A  /RESET        21B  A5
  22A  /M1           22B  A4
  23A  /REFSH        23B  /ROM CS
Expansion cartridges should have a male connector at the rear side - allowing to connect further cartridge(s) - one to each other.


 HW Internal Pins

ZX81 Keyboard Connector
  Pin  1-8:  A15, A14, A8, A13, A9, A12, A10, A11.
  Pin 9-13:  KBD0, KBD1, KBD2, KBD3, KBD4.

Video Signal
The video output consists of a /SYNC signal (low during horizontal and vertical retrace), and the actual VIDEO signal (low=black, high=white). In the ZX81 both signals are internally mixed into a single signal, and then output as shown below at Pin 16 of the Ferranti chip.
Reportedly these voltages are output on Pin 16 (no idea if/when that happens in reality, maybe on US versions with VHF-modulator):
  0V   sync
  2.5V black
  5V   white
On my own ZX81 with UHF modulator (ie. without the VHF pulldown resistors):
  2.5V sync
  3.7V black
  5.0V white
When connecting Pin16 directly to the AV-input of my television set:
  0.0V sync
  0.5V black
  1.0V white

50Hz/60Hz Refresh Rate Jumper
Even though the display refresh rate is actually software based, the BIOS determines the local desired rate by reading from I/O port FEh. In the ZX81, the setting depends on whether Pin 22 of the Ferranti chip is shortcut to ground or not.
When it is grounded, by a 0 Ohm "resistor" called R 30, then vertical blanking time is reduced for 60Hz timing - note that this heavily reduces the user-available CPU time in SLOW mode.

ZX81 Ferranti ULA
  1 A7'
  2 A8'
  3 A2'
  4 A1'
  5 A0'
  6 /RD
  7 /IORQ
  8 /WR
  9 /MREQ
 10 M1
 11 A14
 12 /RAMCS
 13 /ROMCS
 14 OSC (EXPANSION)
 15 /NMI
 16 TV/TAPE
 17 /HALT
 18 A15
 19 D7
 20 TAPE.IN
 21 D6
 22 /NTSC
 23 D5
 24 D4
 25 KDB4
 26 D3
 27 KDB3
 28 D2
 29 KDB2
 30 D1
 31 KDB1
 32 D0
 33 KBD0
 34 GND
 35 OSC (CERAM)
 36 A3'
 37 A4'
 38 A5'
 39 A6'
 40 +5V


 HW Replacing the ZX81 ROM by an EPROM

Even though the ZX81 includes a 24 pin 8K ROM, it should be usually delivered with a 28 pin socket - matching for an 28 pin 8K EPROM. However, a few pins must be exchanged:
- Disconnect A11 from Pin 20.
- Disconnect A12 from Pin 23.
- Connect /OE Pin 22 to /CE Pin 20.
- Connect A11 to Pin 23.
(Each counted in 28 pin device units, ie. Pin 14 is lower left.)
Caution:
Best use a 2764 EPROM, CMOS chips (27C64) don't seem to work very well - probably they are too fast and/or outputs aren't amplified enough.


 HW 56K RAM Upgrade

Upgrading to 56K RAM by krbaker
ROM is enabled at 0000h-1FFFh (8K), RAM at 2000h-FFFFh. /MREQ is for normal memory access, /RFSH is for character pattern & hires bitmap memory fetches.
/M1 mirrors 0000h-7FFFh to 8000h-FFFFh on opcode fetches (aka BG map), in result, memory at 8000h-FFFFh can be used for data storage only, machine code cannot be executed in that area.
           ____    __________________________________
  /M1 ----|AND \__|__ ____        ____                RAM A15
  A15 ----|____/     |OR  \______|INV \_____ ____
  A14 ----|OR  \_____|____/   |  |____/  ___|OR  \___ /RAMCS
  A13 ----|____/              |         |   |____/
  /MREQ --|AND \_____________ | ________|___|OR  \___ /ROMCS
  /RFSH --|____/              |_____________|____/

Parts List: 7404 hex inverter, 7408 quad 2-input AND gate, 7432 quad 2-input OR gate, and 128Kx8bit static RAM. Notes: Remove the resistors in the /ROMCS and /RAMCS lines on the board, connect RAM A16 to VCC or GND. Circuit tested & working with either 74LSxx or 74HCxx chips.

Caution: Mapping RAM to address 2000h works only with ZX80 and ZX81/TS1000 BIOSes (the TS1500 BIOS checks for [2000h]=01h, and if so, treats it as expansion ROM).

Compatibility Problem: Memopak expansions do allow d_file at 4000h-BFFFh (eg. 30K BASIC programs, followed by d_file at Bxxxh), but, above circuit supports only 4000h-7FFFh. To fix this, RAM A15 should be also HIGH whenever A15=1 AND A14=0.


 HW Connecting a Joystick

As far as I know, there isn't any (in-)official standard for sticks, nor do any games support a standard keyboard/joystick control scheme. Anyways, a digital four direction/one button joystick could be easily connected to one of the keyboard rows.

I'd personally vote for row 4, Bits 0-4 used as Fire, Left, Right, Up, Down, and the A12-diode as /Select. Using this method, the Keys for Right, Up, Down, comply with ZX cursor controls (Shift+8,7,6). As used for my 'Starfight' game.

Caution: When having a PC data transfer cable connected to the keyboard lines as well, the joystick cable may disturb the data transfer. If so, insert diodes (1N4148 or else) into each of the four direction lines, ring pointing to the joystick side (nocash upload data lines), and if necessary for fire button also (nocash upload clock line).


 HW Making the ZX81 Faster

Wilf Rigter's WAITMOD (7/2005) speedup 9%
The ZX81 generates a 14-cycle WAIT along with every NMI. However, the WAIT is actually required only after the HALT opcode at 0079h (ie. on the final NMI at the end of the upper/lower blanking period). Rewiring the R1 resistor, and adding another 10K resistor and a PNP transistor (eg. BC557) causes the WAIT to be generated only after HALT opcodes, and thus saves 14-cycles on all other NMIs.
Before:
  ------- R1 ---------------- /HALT CPU.18
After:
  ----+-- R1 ------+--/cut/-- /HALT CPU.18
      |      ___   |
      +-----|C  |  +--------- GND
            |  B|_____________
  VCC--10K--|E__|             /WAIT CPU.24
All basic/machine code software with video output enabled will run 9% faster, there should be no compatibility problems, except, eventually with a few unstable (bugged) programs.

Nocash NMI Patch (5/2010) speedup 2%
Removing the useless 10-cycle "JP @@blah" causes the 7-cycle "JR @@zero" to be executed instead, thus saving 3-cycles per NMI. After the HALT, a final NMI is executed, and in this case, the 10-cycle "JP @@blah" is required as delay, this is solved by moving the JP @@blah after the HALT opcode.
  <---- Original NMI Handler ---->   <----- Tuned NMI Handler ----->
  0066 08         ex   af,af         0066 08         ex   af,af
  0067 3C         inc  a             0067 3C         inc  a
  0068 FA 6D 00   jp   m,@@blah      0068 28 02      jr   z,@@zero
  006B 28 02      jr   z,@@zero      006A 08         ex   af,af
                 @@blah:             006B C9         ret
  006D 08         ex   af,af                        @@zero:
  006E C9         ret                006C 08         ex   af,af
                 @@zero:             006D F5         push af
  006F 08         ex   af,af         006E C5         push bc
  0070 F5         push af            006F D5         push de
  0071 C5         push bc            0070 E5         push hl
  0072 D5         push de            0071 2A 0C 40   ld   hl,[400Ch]
  0073 E5         push hl            0074 CB FC      set  7,h
  0074 2A 0C 40   ld   hl,[400Ch]    0076 76         halt
  0077 CB FC      set  7,h           0077 C3 7A 00   jp   @@blah
  0079 76         halt                              @@blah:
  007A D3 FD      out  [0FDh],a      007A D3 FD      out  [0FDh],a
  007C DD E9      jp   ix            007C DD E9      jp   ix
All basic/machine code software with video output enabled will run 2% faster, there should be no compatibility problems, with a few exceptions: buggy programs (with unstable timings), programs that jump to addresses 0068..0079, and, pseudo-hires programs that use I=0 (only Madjump II does do that).

Nocash Increased Blanking Time - speedup around 50% possible
Finally, the CPU speed can be (greatly) improved by increasing the number of blank lines. There are three ways to do this:
  1) Decrease Vertical Picture Size, increase upper/lower blanking accordingly
  2) Decrease Vertical Retrace Time, increase upper blanking accordingly
  3) Decrease Frame Rate by increasing upper/lower blanking
Method 1) is perfectly legal and can be about 250% faster - but works only with small pictures. Methods 2) and 3) can be around 50% faster, but might cause problems with some TV sets, so, using them should be made optional. If desired, Method 3) can be also implemented at BIOS level (increase the byte at 02E2h) - that patch will speedup all BASIC programs & most machine code programs.


 HW Upgrading RamPaks for True Hi-Res Graphics

When using "True Hi-Res" software, pixel data is read from RAM rather than ROM area. Pixel data is requested by a /RFSH (refresh) signal, rather than by a normal /RD (read) signal. So, RAM is required to output data whenever when /RD or /RFSH are low.

Internal RAM does support the above combination, but external RAM usually generates its own read signal - which senses /RD only. Below example for fixing this problem is taken from Wilf Ritgers ZX81VID.TXT document:

  ZX81.+5V   --------------------+-----+---- RAMPACK.+5V
                                 |     |
  ZX81./RFSH ----|<|---+--[4K7]--+     +---- RAMPACK./RFSH ;ALWAYS HIGH
                       |
  ZX81./RD   ----|<|---+-------------------- RAMPACK./RD   ;/RD AND /RFSH

The RAMPACK is modified to enable the data output at RFSH time by cutting the RD and RFSH lines at the edge connector and installing 2 only 1N34A Germanium diodes and a 4.7K pullup resistor.

Note: ZX hardware often uses a couple of small RAM chips (instead of a single 28 pin SRAM chip), anyways these are SRAM chips either (so cutting the RFSH signal shouldn't cause problems). Ie. you can be quite sure that no DRAM is used because the refresh register is used as interrupt counter.


 HW Connecting a Monitor

Internally the ZX81 is producing a more or less crystal clear video signal, which is normally converted into a TV signal by the UHF modulator in the computer, and then re-decoded by the de-modulator in the TV Set, most likey resulting in a more than bad display quality.

Anyways, the 'raw' video signal can be found at pin 16 of the ZX81 Ferranti chip, that is: 0V=Sync, 2.5V=black, 5V=white, which might or might not work with various types of computer displays. Some displays might not understand the refresh rates, and others might require other voltages - which might be adjusted by one or more resistors.

I've currently only tried to connect a GT65 Green Monitor (for Amstrad/Schneider CPC homecomputers): Connect Ground as such, and connect the Video signal to both /Sync, and (preferably through a 1kOHm resistor) to Luminance. The voltage is apparently much too high (not actually blowing the display, but the picture appears very bright), the resistor is more or less healing this.


 HW Getting rid of the 9V DC Power Supply

When connecting the ZX81 to the PC, the external ZX 9V power supply becomes more or less useless, and it might be recommended to use the PC supply instead. Two possible methods are:

Using 12V DC (Yellow cable in PCs)
The official ZX so-called-9V-supply actually outputs about 11V, thus the slightly higher voltage wouldn't cause much more overheat, but keep in mind that the voltage is forwarded to any connected hardware expansions, if necessary lower the voltage to approx. 11V by inserting a 1N4004 diode into the 12V line.

Using 5V DC (Red cable in PCs)
The ZX81 is internally operated at 5V only, the additional volts are just blown into heat by the 7805 voltage regulator. So, connecting 5V directly to the 7805 output does work (and prevents the keyboard from heating up). However, the "9V" voltage is output to the expansion port, and some hardware expansions (including 16K Memotech RAM Paks) actually require this voltage.

A separate ground signal isn't forcefully required if the ZX is connected to the PCs parallel port, as the existing ground/shield connection could be badly mis-used as ground.


 HW Uploading Programs from the PC to the ZX81

The Transmit function in no$zx Utility menu supports three different transfer methods, each using the PCs parallel port to output data to the ZX (only into that direction, not vice versa):

1) Nocash Highspeed (transmission time 100-1300ms)
This is a very simple six-diodes-network. The transmission rate is approx. 12500 bytes/sec (about 300 times faster than 38 bytes/sec cassette loading). The ZX is automatically reset and immediately switched into read-mode, even a huge 16K file is transferred in less than 1.5 seconds - ways faster than the unpatched ZX81/16K CLS boot procedure.
However, this method requires a patched BIOS ROM, and it's thus restricted to more or less serious users only. Data is transferred in units of 4 Bits, synchronized by a separate clock signal, thus making the timing rather uncritical, even when using a multitasking operating system.

2) Mixed (transmission time 3-4 seconds)
This method combines the above/below protocols, but works well without patched BIOS ROM. Before transmission, you'll have to type LOAD "" manually on remote side, a small loader will be then transferred by using the slow transfer method (approx 3 seconds), followed by the actual file using highspeed transfer (approx 0-1 second).

3) ZXTAPE (transmission time up to 7 minutes)
This protocol is transferring data at original cassette speed (38 bytes per second), similar to Wilf Ritger's ZXTAPE program. A possible advantage is that you won't need to open the ZX81, and won't need to install any diodes, just connect parallel port pin 2 and ground to the ZX EAR socket, and ignore further instructions below.
Note that this method won't work very well with multi-tasking operating systems.

You need:
  1  PC with Parallel Port, and a normal Centronics Printer Cable
  1  Centronics plug (36 pins, female)
  1  EPROM (2764 non-CMOS) and EPROM burner (optional)
  6  Diodes (for example 1N4148, ie. the most cheapest standard ones)
  and some short isolated wires

Schematic
  ZX Mainboard Side                    Centronics Side
  Cassette Input (EAR)   ------------- Centr.Pin2 (D0)
  Keyb Col0/Pin9 (CLK)   -----|>|----- Centr.Pin3 (D1)
  Keyb Col1/Pin10 (D0)   -----|>|----- Centr.Pin4 (D2)
  Keyb Col2/Pin11 (D1)   -----|>|----- Centr.Pin5 (D3)
  Keyb Col3/Pin12 (D2)   -----|>|----- Centr.Pin6 (D4)
  Keyb Col4/Pin13 (D3)   -----|>|----- Centr.Pin7 (D5)
  Expansion 21A (RESET)  -----|>|----- Centr.Pin8 (D6)
  ROM Pin26 (A13)        ------------- Centr.Pin9 (D7) (LO=ZX80, HI=ZX81)
  VCC                    ----[10K]---- Centr.Pin9 (D7)
  Expansion 4B/5B (GND)  ------------- Centr.Pin24 (GND)
                            _______
  CPU.Pin36 (A6)         --|D1   Q1|-- Centr.Pin10 (ACK)
  CPU.Pin20 (/IORQ)      --|CK1    |
  CPU.Pin26 (/RESET)     --|SET1   | 74LS74 FlipFlop
  VCC ---------------------|RES1   |
                           |_______|

Step 1 - The Diodes
Connect the diodes to Pin 3-8 of the Centronics plug. The end with the black ring (or other color) must point to the plug, the other end will be connected to the ZX81 mainboard. The diodes are required because otherwise the strong PC LPT port pulls up the ZX81 address signals when pressing a key. (Even though bi-directional LPT ports might be able to avoid that.)

Step 2 - Connecting the Data Lines.
When looking on top of the ZX81 board, you will see the keyboard connector in lower right. Counted from the right end, pin 1-8 are A8-A15 signals (in no specific order) - ignore these. Pin 9-13 are Keyboard Columns 0-4 (in exactly that order) - connect these to the diodes at Pin 3-7 of the Centronics plug.

Step 3 - Connecting Ground, Reset and Cassette.
Connect Ground (found at Pin 4B and 5B of the expansion port) to Pin 24 or else of the Centronics plug. Connect Reset (Pin 21A of expansion port) to the diode at Pin 8 of Centronics plug (the reset signal isn't actually required, but it's quite comfortable if the transmission program can reset the ZX81). Finally, connect the Cassette Input (EAR) to Pin 2 of the Centronics plug (this is required only if you are not using patched BIOS).

Step 4 - Connecting the ACK Flipflop (NEW, added 2010)
Connect the 74LS74 flipflop as shown above. This is allowing to send feedback info and EPROM version string to the PC. It's making the circuit a little bit more complicated than necessary, but it's rather useful to solve connection problems.

Software
The transmit function (for uploading .P .O .81 .80 files to the ZX80/ZX81) is found in no$zx's "Utility" menu. The "Utility" menu also contains a function called Create Patched BIOS Image, which creates a file called XMITZX81.ROM, which is to be stored in the EPROM; the ROM-image is 16K in size, containing the original ZX80/ZX81 BIOSes, and the upload software.
Keep in mind that some pins of the ROM socket must be reconnected for EPROM use (see chapter about replacing BIOS ROM by EPROM).

Joystick Compatibility
When having a joystick connected to the ZX keyboard lines as well, then above nocash/mixed transfer methods may not work. This can be fixed by disconnecting the joystick, or by inserting diodes (1N4148 or else) into the joystick connectors data lines (Keyboard Bits 1-4).
Due to the rather high transfer rate, the incoming signal will run into the joystick cable, and as there are no 'terminators' (resistors) at the end of the cable (as for ethernet networks for example), the signal will 'bounce back' at the cable end and run back to the ZX, confusing the transmission program that simultaneously wants to read arrived data.

History
2002 - original circuit for zx81
2010 - added feedback-flipflop, added zx80 support, changed eprom content


 ZX Spectrum

Spectrum
Spectrum Models
Spectrum I/O Map
Spectrum Memory Maps
Spectrum System Variables
Spectrum ULA
Spectrum VRAM
Spectrum Video Connectors
Spectrum Timings
Spectrum Sound
Spectrum Disc and Tape Drives
Spectrum Interrupts
Spectrum Keyboards
Spectrum Joystick Ports
Spectrum Mouse Ports
Spectrum Light Guns
Spectrum Printer Ports
Spectrum Serial Port
Spectrum Expansion Ports
Spectrum ROM Cartridges
Spectrum Chipset Pinouts
Spectrum Xboo

Z80 CPU
Z80 CPU Specifications

The BASIC Interpreter
BASIC Interpreter

"What is a Computer Good For?
Your new Timex Sinclair 2000 computer is a very special instrument. It is a tool that can increase the power of your mind as a hammer or a wheelbarrow assists your muscles." - quoted from TS2068 User Manual


 Spectrum Models

Original Spectrums (by Sinclair, later by Amstrad)
  1982 Spectrum 16K - original low-cost version (only 16K RAM)
  1982 Spectrum 48K - original full version (48K RAM)
  1984 Spectrum +   - new keyboard, reset button
  1985 Spectrum 128 - 128K-RAM,PSG,MIDI,RS232,RGB,32K-ROM
  1986 Amstrad +2  (typewriter keyboard, built-in cas drive, two joy ports)
  1987 Amstrad +2A (manufactured in Taiwan) (+3 style mainboard, Centronics)
  1987 Amstrad +2B (same as +2A but manufactured in China instead of Taiwan)
  1987 Amstrad +3  (more ROM, RAM map-able at 0000h for CP/M, built-in disk)

Timex (licensed Spectrum clones)
  1983 TS2068 special video modes, timex bios, PSG, 48K RAM, NTSC (USA)
  1984 TC2068 special video modes, timex bios, PSG, 48K RAM, PAL (Portugal)
  1984 TC2048 special video modes, spectrum bios, no PSG, 16K RAM, PAL (Portu.)

Unlicensed Spectrum Clones
There are about 50 unlicensed spectrum clones: Mass-produced clones (like russian pentagon and scorpion), as well as homebrew modifications.


 Spectrum I/O Map

The original spectrum did use only one I/O address (the ULA at port FEh). And only one pre-assigned expansion I/O address (the ZX printer at port FDh). Although that original design was very simple, the lack of further pre-assigned addresses resulted in a mess. For example, there are at least 5 different "standards" for connecting joysticks, 3 for PSG sound chips (probably more), many dozens of floppy and centronics standards, etc.

Spectrum I/O Ports (accessed via IN/OUT opcodes)
  Port Binary Address   Dir Description
  xxFF ................ R   Spectrum Current VRAM Data (dirt effect)
  xxFE nnnnnnnn.......0 R   Spectrum ULA (Keyboard, and CAS Input)
  xxFE ...............0 W   Spectrum ULA (Border Color, Speaker/CAS Output)
  F7FE ....0..........0 R   Spectrum +2/+2A/+3 Joystick 2
  EFFE ...0...........0 R   Spectrum +2/+2A/+3 Joystick 1
  F7FE ...10..........0 R   Interface 2 Joystick 2
  EFFE ...01..........0 R   Interface 2 Joystick 1
  FFFD 11............0. W   Spectrum 128K/+2/+2A/+3 PSG index register
  FFFD 11............0. R   Spectrum 128K/+2/+2A/+3 PSG data read (and RS232)
  BFFD 10............0. W   Spectrum 128K/+2/+2A/+3 PSG data write (and RS232)
  7FFD 0.............0. W   Spectrum 128K/+2 Memory Control
  BFFD 10............0. R   Spectrum +2A/+3 Mirror of Port FFFDh (+2A/+3 only)
  7FFD 01............0. W   Spectrum +2A/+3 Memory Control A
  1FFD 0001..........0. W   Spectrum +2A/+3 Memory Control B and Disk Motor
  0FFD 0000..........0.     Spectrum +2A/+3 Centronics
  3FFD 0011..........0. R/W Spectrum +3 Floppy FDC NEC uPD765 data
  2FFD 0010..........0. R   Spectrum +3 Floppy FDC NEC uPD765 status
  xxFB .............0.. R/W ZX Printer
  xxF7 ...........10... R/W Interface 1 RS232/Network Data (1bit send/receive)
  xxEF ...........01... R/W Interface 1 Microdrive/RS232/Network Control
  xxE7 ...........00... R/W Interface 1 Microdrive Data    (8bit send/receive)
  xx1F ........000..... R   Kempston joystick

Timex I/O Ports
  xxFF ........11111111     Timex Display Mode
  xxFE nnnnnnnn11111110 R   Timex ULA (Keyboard, and CAS Input)
  xxFE ........11111110 W   Timex ULA (Border Color, Speaker/CAS Output)
  xxF4 ........11110100     Timex Memory Mapping
  xxF6 ........11110110 R/W Timex TS2068/TC2068 PSG data R/W (sound registers)
  x1F6 .......111110110 R/W Timex TS2068/TC2068 PSG data R/W (and joystick 1)
  x2F6 ......1.11110110 R/W Timex TS2068/TC2068 PSG data R/W (and joystick 2)
  xxF5 ........11110101 W   Timex TS2068/TC2068 PSG index
  xxFB ........11111011 R/W Timex Printer (TS2040 or Alphacom 32)
  xx1F ..........0..... R   Timex TC2048 joystick (kempston-style, A5-only)
  xxEF ........???0???? R/W Timex FDD Interface (for FDD or FDD-3000 drive)

Expansion I/O Stuff (Audio related)
  0007                  W   Cheetah Sweet Talker - Speech Output (halts CPU)
  007F         0        W   DK'Tronics Speech Synthesiser - Speech Output
  007F         0        R   DK'Tronics Speech Synthesiser - Speech Status
  xx7F ........011      R   Fuller Box - Joystick port
  xx3F ........00       W   Fuller Box - PSG index (also DK'Tronics PSG)
  xx3F ........00       R   Fuller Box - PSG data read (maybe also DK'Tronics?)
  xx5F ........010      W   Fuller Box - PSG data write (also DK'Tronics PSG)
  xx9F ........ 00      W   Fuller Orator / Box Master Unit - Speech Output
  xxBF ........ 01      R   Fuller Orator / Box Master Unit - Speech Status
  xx9F ........ 00      W   William Stuart Systems Music Synthesiser PSG index
  xxDF ........ 10      W   William Stuart Systems Music Synthesiser PSG data
  xxFF ........1..11111 W   Bi-Pak ZON X Soundbox PSG index
  xx7F ........0..11111 W   Bi-Pak ZON X Soundbox PSG data write
  0038 0000000000111000 R   Currah uSpeech enable/disable BIOS (mirror)

Expansion I/O Stuff (Storage, Centronics, etc.)
  xx7F ........01111111     Aerco Centronics Interface
  xx8F ........10001111     JLO status/command
  xx9F ........10011111     JLO track
  xxAF ........10101111     JLO sector
  xxBF ........10111111     JLO data
  xxB7 ........10110111     JLO select
  xxFB ........1....0..     ZX LPrint III Enable ROM, and Write=Data, Read=Busy
  xx7B ........0....0..     ZX LPrint III Disable ROM
  E3BF          0       W   Kempston Centronics Ctrl (b0=0=Strobe)(0Eh,0Fh,81h)
  E2BF          0       R   Kempston Centronics Status (b0=1=Busy)
  E0BF          0       W   Kempston Centronics Data Output
  xxBB ........         R   DK'Tronics Centronics Status (b7=1=Busy)
  xxBB ........         W   DK'Tronics Centronics Ctrl (b1=0=Strobe) (02h,00h)
  xx9B ........         W   DK'Tronics Centronics Data
  xxDB ........         W   DK'Tronics Centronics Config 1 (set to 3Fh)
  xxFB ........         W   DK'Tronics Centronics Config 2 (set to FFh,80h)
  dd0E dddddddd         R   Wafadrive Centronics Data Out  ;via "IN A,(dd0Eh)"
  x00A   s              R   Wafadrive Centronics Strobe Out;via "IN A,(s00Ah)"
  0002                  R   Wafadrive Centronics Busy In  ;returned in bit5
  xxFB ........         W   Indescomp Centronics Data
  xxFB ........         R   Indescomp Status (b0=Busy,b1=Rs232Busy,b2=RxD)
  xx7F ........         W   Indescomp Control (b0=Strobe,b1=TxD,b2=CTS)
  xx1B ........     0   W   Disciple FDC Command
  xx1B ........     0   R   Disciple FDC Status
  xx5B ........     0   R/W Disciple FDC Track
  xx9B ........     0   R/W Disciple FDC Sector
  xxDB ........     0   R/W Disciple FDC Data
  xx1F ........         W   Disciple Control Register
  xx1F ........         R   Disciple Joystick/Printer/Network
  xx3B ........     0   ?   Disciple Network Wait
  F7FE     0          0 R   Disciple Disciple Joystick
  EFFE    0           0 R   Disciple Disciple Joystick
  xxBB ........     0   R   Disciple Enable Internal ROM/RAM
  xxBB ........     0   W   Disciple Disable Internal ROM/RAM
  xxFB ........     0   W   Disciple Centronics Data
  xx7B ........     0   R   Disciple Map ROM=0000h, RAM=2000h
  xx7B ........     0   W   Disciple Map ROM=2000h, RAM=0000h
  xxE3 ........   000   W   PlusD FDC Command
  xxE3 ........   000   R   PlusD FDC Status
  xxE7 ........   001   R   PlusD Enable Internal ROM/RAM
  xxE7 ........   001   W   PlusD Disable Internal ROM/RAM
  xxEB ........   010   R/W PlusD FDC Track
  xxEF ........   011   W   PlusD Control Register
  xxF3 ........   100   R/W PlusD FDC Sector
  xxF7 ........   101   W   PlusD Centronics Data
  xxF7 ........   101   R   PlusD Centronics Busy
  xxFB ........   110   R/W PlusD FDC Data
  xx1F ........         W   Beta Disk FDC Command
  xx1F ........         R   Beta Disk FDC Status
  xx3F ........         R/W Beta Disk FDC Track
  xx5F ........         R/W Beta Disk FDC Sector
  xx7F ........         R/W Beta Disk FDC Data
  xxFF ........         R   Beta Disk Status
  xxFF ........         W?  Beta Disk Control
And many more. See Floppy Disc and Centronics chapters for details on some various further expansion ports.

Other
  xx9F ........             Multiface I In
  xx1F ........             Multiface I Out
  xxBF ........             Multiface 128 In
  xx9F ........             Multiface 128 In v2 (Disciple) (uh/what?)
  xx3F ........             Multiface 128 Out
  xx3F ........             Multiface III Button
  xx3F ........             Multiface III In
  xxBF ........             Multiface III Out
  7F3F                      Multiface III P7FFD (uh?)
  1F3F                      Multiface III P1FFD (uh?)
  FF3F                      British Micro Grafpad Pen up/down
  FFBF                      British Micro Grafpad Pen position X coordinate
  FF7F                      British Micro Grafpad Pen position Y coordinate
  FADF ......?0..0....?     Kempston Mouse Buttons (b0=Right,b1=Left,b2=Mid)
  FBDF .....0?1..0....?     Kempston Mouse X
  FFDF .....1?1..0....?     Kempston Mouse Y
  xx1F ........000..... R/W AMX Mouse Z80PIO Data A: X, CentronicsLSBs/Strobe
  xx3F ........001..... R/W AMX Mouse Z80PIO Data B: Y, CentronicsMSBs/Busy
  xx5F ........010.....     AMX Mouse Z80PIO Ctrl A: X, CentronicsLSBs/Strobe
  xx7F ........011.....     AMX Mouse Z80PIO Ctrl B: Y, CentronicsMSBs/Busy
  xxDF ........110..... R   AMX Mouse 74LSXX Logic: Mouse Buttons (R)
  xxDF ........         R   Stack Light Rifle (R) (light gun trigger/sensor)

Memory Mapped Ports (eg. triggered on opcode fetches from that addresses)
  0008h,1708h               Interface 1 - Enable external ROM
  0700h                     Interface 1 - Disable external ROM
  0008h                     Wafadrive - Enable external ROM
  0008h,0048h,1708h         Discovery - Enable external ROM/RAM/WD/PIA
  1748h                     Discovery - Disable external ROM/RAM/WD/PIA
  2800h..2803h              Discovery - WD1770 registers (disk)
  3000h..3003h              Discovery - PIA6821 registers (centronics, etc.)
  0000h,0008h,0066h,028Eh   Disciple and PlusD - Enable external ROM/RAM
  0000h,0008h               Timex FDD Interface - Enable ROM/RAM
  0604h                     Timex FDD Interface - Disable ROM/RAM
  0038h.Read                Currah uSpeech enable/disable BIOS
  1000h.Read                Currah uSpeech Status (R)
  1000h.Write               Currah uSpeech Output (W)
  3C00h..3CFFh              Beta/BetaPlus Disk    - Enable ROM and I/O Ports
  3D00h..3DFFh              Beta128 Disk          - Enable ROM and I/O Ports
  4000h..FFFFh              Beta/BetaPlus/Beta128 - Disable ROM and I/O Ports
Note: Disciple and PlusD do only have the <enable> functions mapped to memory (whilst <disable> is done via I/O ports).


 Spectrum Memory Maps

Spectrum 16K Memory Map
  0000h-3FFFh  ROM (BASIC)
  4000h-7FFFh  RAM (Work RAM and VRAM)               (with waitstates)
  8000h-FFFFh  N/A

Spectrum 48K/Spectrum + Memory Map
  0000h-3FFFh  ROM (BASIC)
  4000h-7FFFh  RAM (Work RAM and VRAM)               (with waitstates)
  8000h-FFFFh  Additional RAM

Spectrum 128K/+2 Memory Map
  0000h-3FFFh  ROM0 (EDITOR) or ROM1 (BASIC) (see Port 7FFDh.Bit4)
  4000h-7FFFh  RAM Page 5 (VRAM)                     (with waitstates)
  8000h-BFFFh  RAM Page 2 (Work RAM)
  C000h-FFFFh  RAM Page 0..7 (see Port 7FFDh.Bit0-2) (Page1,3,5,7=waitstates)

Spectrum +2A/+3 Memory Map (in normal mode)
  0000h-3FFFh  ROM0..ROM3 (see Port 7FFDh.Bit4 and 1FFDh.Bit2)
  4000h-7FFFh  RAM Page 5 (VRAM)                     (with waitstates)
  8000h-BFFFh  RAM Page 2 (Work RAM)
  C000h-FFFFh  RAM Page 0..7 (see Port 7FFDh.Bit0-2) (Page 4-7 with waitstates)

Spectrum +2A/+3 Memory Map (in special mode) (with RAM at 0000h for CP/M)
  0000h-3FFFh  RAM Page 0, 4, 4, or 4 (see Port 1FFDh.Bit0-2)
  4000h-7FFFh  RAM Page 1, 5, 5, or 7 (see Port 1FFDh.Bit0-2)
  8000h-BFFFh  RAM Page 2, 6, 6, or 6 (see Port 1FFDh.Bit0-2)
  C000h-FFFFh  RAM Page 3, 7, 3, or 3 (see Port 1FFDh.Bit0-2)

Timex TC2048 Memory Map (see Port F4h, and Port FFh)
  Address      HOME             EX-ROM               DOCK
  0000h-1FFFh  BIOS             Expansion Bank 0     N/A
  2000h-3FFFh  BIOS             Expansion Bank 1     N/A
  4000h-5FFFh  VRAM (Screen 0)  Expansion Bank 2     N/A
  6000h-7FFFh  VRAM (Screen 1)  Expansion Bank 3     N/A
  8000h-9FFFh  N/A              Expansion Bank 4     N/A
  A000h-BFFFh  N/A              Expansion Bank 5     N/A
  C000h-DFFFh  N/A              Expansion Bank 6     N/A
  E000h-FFFFh  N/A              Expansion Bank 7     N/A

Timex TC2068/TS2068 Memory Map (see Port F4h, and Port FFh)
  Address      HOME             EX-ROM               DOCK
  0000h-1FFFh  BIOS             Extended BASIC ROM   Cartridge Bank 0
  2000h-3FFFh  BIOS             Expansion Bank 1     Cartridge Bank 1
  4000h-5FFFh  VRAM (Screen 0)  Expansion Bank 2     Cartridge Bank 2
  6000h-7FFFh  VRAM (Screen 1)  Expansion Bank 3     Cartridge Bank 3
  8000h-9FFFh  RAM  (Work RAM)  Expansion Bank 4     Cartridge Bank 4
  A000h-BFFFh  RAM  (Work RAM)  Expansion Bank 5     Cartridge Bank 5
  C000h-DFFFh  RAM  (Work RAM)  Expansion Bank 6     Cartridge Bank 6
  E000h-FFFFh  RAM  (Work RAM)  Expansion Bank 7     Cartridge Bank 7
EX-ROM bank 0 is reportedly mirrored to EX-ROM banks 1-7 (which conflicts with external EX-ROM banks) (external DOCK banks have no such problems).

Spectrum 128K/+2/+2A/+3 Memory Banks
The RAM pages are used as:
  RAM Page 0 - Main RAM at C000h-FFFFh
  RAM Page 2 - Main RAM at 8000h-BFFFh
  RAM Page 5 - Main RAM at 4000h-7FFFh (with first VRAM block and system area)
  RAM Page 7 - Second VRAM block and Editor variables
  RAM Page 1,3,4,6 - RAM Disk
Upon Reset, ROM0 is mapped.

Expansion ROM/RAM
On Spectrum machines, the 16K ROM at 0000h-3FFFh (or smaller portions of it) can be externally disabled by dragging the /ROMCS pin HIGH, allowing to replace that region by external ROM and/or RAM. (Eg. Interface 1 ROM or Interface 2 ROM, or Interface 2 Cartridge ROM) (also external disk drives usually contain both ROM and RAM).
Timex machines don't have external /ROMCS pin, instead, the memory mapping is handled via the internal Port F4h and FFh registers. And, Timex machines can map external memory anywhere at 0000h..FFFFh (rather than at 0000h-3FFFh only).


 Spectrum System Variables

Spectrum RAM Map
  4000h  VRAM Bitmap     (256x192 pixels)
  5800h  VRAM Attributes (32x24 characters)
  5B00h  System Area
  5CB6h  Memory (starting with CHANS)
Memory
  CHANS    channels      (usually at 5CB6h)
  PROG     basic program (usually at 5CCBh)
  VARS     basic variables
  E_LINE   input buffer
  WORKSP   temporary work space
  STKBOT   bottom of calculator stack (same as WORKSP when empty)
  STKEND   start of spare space (same as STKBOT when empty)
  RAMTOP   CPU stacktop+1 (usually FF57h)
  UDG      User-defined graphics (charset) (usually FF58h)
  P_RAMT   physical RAM top (FFFFh for 48K RAM, or 7FFFh for 16K RAM)
Variants
Expansion hardware like Interface 1 and Beta Disk may allocate memory between 5CB6h and CHANS. Other hardware like Currah uSpeech may allocate memory between RAMTOP and UDG.

TS2068 Memory Map
  0000h..3FFFh   BIOS ROM
  4000h..57FFh   VRAM Bitmap (1800h bytes)
  5800h..5AFFh   VRAM Attr (300h bytes)
  5B00h..5BFFh   Printer Buffer
  5C00h..5FFFh   System Variables
Next, for 1 Display File:
  6000h..61FFh   Machine Stack (200h bytes)
  6200h..683Fh   OS RAM-Resident Code
  6840h..ARSBUF  (Machine Code Variables)
  ARSBUF
  CHANS
  PROG
  VARS
  E_LINE
  WORK_SP
  STKBOT
  STKEND
  RAMTOP..P_RAMT  UDG (user defined graphics)
Or, for 2 Display Files:
  6000h..77FFh   VRAM Bitmap 2 (1800h bytes)
  7800h..7AFFh   VRAM Attr 2 (300h bytes)
  7B00h..ARSBUF  (Machine Code Variables)
  ARSBUF..RAMTOP (as above)
  RAMTOP..F7BFh  UDG (user defined graphics)
  F7C0h..F9BFh   Machine Stack (200h bytes)
  F9C0h..P_RAMT  OS RAM-Resident Code

Spectrum System Area (parts accessed via IY=5C3Ah)
  5B00h 256 PRBUFF  LPRINT Buffer (32x8 pixel bitmap)
                    (On Spectrum 128/+2/+2A/+3, PRBUFF is used
                    as Extended System Area, see below)
  5C00h 8   KSTATE  Used in reading the keyboard.
  5C08h 1   LASTK   Stores newly pressed key.
  5C09h 1   REPDEL  Time (in 50ths of a second) that a key
                    must be held down before it
                    repeats. This starts off at 35, but
                    you can POKE in other values.
  5C0Ah 1   REPPER  Delay (in 50ths of a second) between
                    successive repeats of a key held down - initially 5.
  5C0Bh 2   DEFADD  Address of arguments of user defined
                    function (if one is being evaluated), otherwise 0.
  5C0Dh l   K_DATA  Stores 2nd byte of colour controls entered from keyboard.
  5C0Eh 2   TVDATA  Stores bytes of colour, AT and TAB controls going to TV.
  5C10h 38  STRMS   Addresses of channels attached to streams.
                    (16bit pointers for Stream FDh..FFh, and 00..0Fh)
  5C36h 2   CHARS   256 less than address of character set
                    (which starts with space and carries
                    on to (C)). Normally in ROM, but you
                    can set up your down in RAM and make
                    CHARS point to it.
  5C38h 1   RASP    Length of warning buzz.
  5C39h 1   PIP     Length of keyboard click.
  5C3Ah 1   ERRNR   1 less than the report code. Starts
                    off at 255 (for -1) so 'PEEK 23610' gives 255.
  5C3Bh 1   FLAGS   Various flags to control the BASIC system.
  5C3Ch 1   TVFLAG  Flags associated with the TV.
  5C3Dh 2   ERRSP   Address of item on machine stack to be used as error return
  5C3Fh 2   LISTSP  Address of return address from automatic listing.
  5C41h 1   MODE    Specifies 'K', 'L', 'C', 'E' or 'G' cursor.
  5C42h 2   NEWPPC  Line to be jumped to.
  5C44h 1   NSPPC   Statement number in line to be jumped to. Poking first
                    NEWPPC and then NSPPC forces a jump to a specified
                    statement in a line.
  5C45h 2   PPC     Line number of statement currently being executed.
  5C47h 1   SUBPPC  Number within line of statement currently being executed.
  5C48h 1   BORDCR  Border colour multiplied by 8; also
                    contains the attributes normally used
                    for the lower half of the screen.
  5C49h 2   E_PPC   Number of current line (with program cursor).
  5C4Bh 2   VARS    Address of variables. (End of BASIC Program)
  5C4Dh 2   DEST    Address of variable in assignment.
  5C4Fh 2   CHANS   Address of channel data (usually 5CB6h)
  5C51h 2   CURCHL  Address of current I/O channel (CHANS+n)
  5C53h 2   PROG    Address of BASIC program. (End of CHANS)
  5C55h 2   NXTLIN  Address of next line in program. (PROG+n, or 0000h=None)
  5C57h 2   DATADD  Address of terminator of last DATA item. (initially PROG-1)
  5C59h 2   E_LINE  Address of input buffer (aka line editor) (End of VARS)
  5C5Bh 2   K_CUR   Address of cursor in input buffer (E_LINE+n)
  5C5Dh 2   CH_ADD  Address of the next character to be interpreted
                      (the character after the argument of PEEK,
                      or the NEWLINE at the end of a POKE statement)
  5C5Fh 2   X_PTR   Address of the character after the [] marker. (or 00D7h?)
  5C61h 2   WORKSP  Address of temporary work space.
  5C63h 2   STKBOT  Address of bottom of calculator stack.
  5C65h 2   STKEND  Address of start of spare space.
  5C67h 1   BREG    Calculator's B register.
  5C68h 2   MEM     Address of area used for calculator's memory
                      (usually MEMBOT, but not always).
  5C6Ah 1   FLAGS2  More flags. (Bit3 set when CAPS SHIFT or CAPS LOCK is on.)
  5C6Bh 1   DF_SZ   Number of lines in lower part of screen (including one
                      blank line)
  5C6Ch 2   S_TOP   The number of the top program line in automatic listings.
  5C6Eh 2   OLDPPC  Line number to which CONTINUE jumps.
  5C70h 1   OSPPC   Number within line of statement to which CONTINUE jumps.
  5C71h 1   FLAGX   Various flags.
  5C72h 2   STRLEN  Length of string type destination in assignment.
  5C74h 2   T_ADDR  Address of next item in syntax table.
  5C76h 2   SEED    The seed for RND. This is set by RANDOMIZE.
  5C78h 3   FRAMES  Frame counter incremented at 50Hz (or 60Hz) (24bit)
  5C7Bh 2   UDG     Address of first user-defined graphic (usually FF58h)
  5C7Dh 2   COORDS  X,Y coordinates of last point plotted.
  5C7Fh 1   P_POSN  33-column number of printer position.
  5C80h 2   PRCC    Address of Current Column in PRBUFF (5B00h..5B1Fh)
                    [Not used in 128K mode or when certain peripherals
                    are attached]
  5C82h 2   ECHO_E  33-column number and 24-line number
                    (in lower half) of end of input buffer.
  5C84h 2   DF_CC   Address in display file of PRINT position.
  5C86h 2   DF_CCL  Like DF_CC for lower part of screen.
  5C88h 1   S_POSN  33-column number for PRINT position.
  5C89h 1           24-line number for PRINT position.
  5C8Ah 2   SPOSNL  Like S_POSN for lower part.
  5C8Ch 1   SCR_CT  Counts scrolls - it is always 1 more
                    than the number of scrolls that will
                    be done before stopping with
                    'scroll?'. If you keep poking this
                    with a number bigger than 1 (say 255),
                    the screen will scroll on and on
                    without asking you.
  5C8Dh 1   ATTR_P  Permanent current colours, etc., (as
                    set up by colour statements).
  5C8Eh 1   MASK_P  Used for transparent colours, etc. Any
                    bit that is 1 shows that the
                    corresponding attribute bit is taken
                    not from ATTR_P, but from what is
                    already on the screen.
  5C8Fh 1   ATTR_T  Temporary current colours, etc., (as
                    set up by colour items).
  5C90h 1   MASK_T  Like MASK_P, but temporary.
  5C91h 1   P_FLAG  More flags.
  5C92h 30  MEMBOT  Calculator's memory area - used to
                    store numbers that cannot conveniently
                    be put on the calculator stack.
  5CB0h 2   NMIADD  Holds the address of the users NMI service routine.
                    NOTE - On previous machines, this did
                    not work correctly and these two bytes
                    were documented as 'Not used.'
                    Programs that used these two bytes for
                    passing values may need to be
                    modified.
  5CB2h 2   RAMTOP  Address of last byte of BASIC system area.(usually FF57h)
  5CB4h 2   P_RAMT  Address of last byte of physical RAM.     (usually FFFFh)

Timex 2068 - Extended System Area
  5CB6h 2   ERRLN   Line number to GOTO on error
  5CB8h 2   ERRLN   Line number in which error occurred
  5CBAh 1   ERRS    Statement number within line in which error occurred
  5CBBh 1   ERRT    Error number (Report Code)
  5CBCh 2   SYSCON  Pointer to System Configuration Table
  5CBEh 1   MAXBNK  Number of Expansion Banks in System
  5CBFh 1   CURCBN  Current Channel Bank Number
  5CC0h 2   MSTBOT  Address of location above machine stack
  5CC2h 1   VIDMOD  Video Mode (non-zero if 2nd display file is used)
  5CC3h 1           <undocumented/unused byte> ?
  5CC4h 7           "Various variables used for BASIC cartridges" ...details?
  5CCBh 1   STRMNM  Current Stream number
  5CCCh 334h -      Not used (except, some stuff at 5EF3h..5EF6h is used?)

Spectrum 128/+2/+2A/+3 - Extended System Area (formerly Printer buffer)
  Addr  Siz Name    Expl.
  Below is for Spectrum 128/+2:
  5B00h 20  SWAP     Paging subroutine.
  5B14h 9   YOUNGER  Paging subroutine.
  5B1Dh 18  ONERR    Paging subroutine.
  5B2Fh 5   PIN
  5B34h 22  POUT
  5B4Ah 14  POUT2
  Below is for Spectrum +3:
  5B00h 16  SWAP    Paging subroutine.
  5B10h 17  STOO    Paging subroutine. Entered with IRQs disabled, AF,BC pushed
  5B21h 9   YOUNGER Paging subroutine.
  5B2Ah 16  REGNUOY Paging subroutine.
  5B3Ah 24  ONERR   Paging subroutine.
  5B52h 2   OLDHL   Temporary register store while switching ROMs.
  5B54h 2   OLDBC   Temporary register store while switching ROMs.
  5B56h 2   OLDAF   Temporary register store while switching ROMs.
  Below is for both Spectrum 128 and +3:
  5B58h 2   TARGET  Subroutine address in ROM 3.
  5B5Ah 2   RETADDR Return address in ROM 1.
  5B5Ch 1   BANKM   Copy of last byte output to I/O port 7FFDh.
  5B5Dh 1   RAMRST  RST 8 instruction. Used by ROM 1 to
                    report old errors to ROM 3.
  5B5Eh 1   RAMERR  Error number passed from ROM 1 to ROM
                    3. Also used by SAVE/LOAD as temporary drive store.
  5B5Fh 2   BAUD    RS232 bit period in T states/26. Set by FORMAT LINE.
  5B61h 2   SERFL   Second-character-received-flag, and data.
  5B63h 1   COL     Current column from 1 to width.
  5B64h 1   WIDTH   Paper column width. Defaults to 80. (on +3 at least)
  5B65h 1   TVPARS  Number of inline parameters expected by RS232.
  5B66h 1   FLAGS3  Various flags. Bits 0, 1, 6 and 7
                    unlikely to be useful. Bit 2 is set
                    when tokens are to be expanded on
                    printing. Bit 3 is set if print output
                    is RS232. The default (at reset) is
                    Centronics (on +3) and whatever? (on 128/+2).
                    Bit 4 is set if a disk
                    interface is present. Bit 5 is set if
                    drive B: is present.
  Below is for Spectrum 128/+2:
  5B67h 10  NSTR1
  5B71h 1   HD00
  5B72h 2   HD0B
  5B74h 2   HD0D
  5B76h 2   HD0F
  5B78h 2   HD11
  5B7Ah 1   SC00
  5B7Bh 2   SC08
  5B7Dh 2   SC0D
  5B7Fh 2   SC0F
  5B81h 2   OLDSP
  5B83h 2   SFNEXT
  5B85h 3   SPSPACE
  5B88h 1   ROW01
  5B89h 1   ROW23
  5B8Ah 1   ROW45
  5B8Bh 2   SYNRET
  5B8Dh 5   LASTV
  5B92h 2   RNLINE
  5B94h 2   RNFIRST
  5B96h 2   RNSTEP
  5B98h 8   STRIP1
  5BA0h 8   STRIP2
  5BA8h 87  TSTACK
  5BFFh 1   N/A
  Below is for Spectrum +3:
  5B67h 1   BANK678 Copy of last byte output to I/O port 1FFDh.
  5B68h 1   XLOC    Holds X location when using the unexpanded COPY command.
  5B69h 1   YLOC    Holds Y location when using the unexpanded COPY command.
  5B6Ah 2   OLDSP   Old SP (stack pointer) when TSTACK is in use.
  5B6Ch 2   SYNRET  Return address for ONERR.
  5B6Eh 5   LASTV   Last value printed by calculator.
  5B73h 2   RCLINE  Current line being renumbered.
  5B75h 2   RCSTART Starting line number for renumbering. The default is 10.
  5B77h 2   RCSTEP  Incremental value for renumbering. The default is 10.
  5B79h 1   LODDRV  Holds 'T' if LOAD, VERIFY, MERGE are
                    from tape, otherwise 'A', 'B' or 'M'.
  5B7Ah 1   SAVDRV  Holds 'T' if SAVE is to tape, otherwise 'A', 'B' or 'M'.
  5B7Bh 1   DUMPLF  Holds the number of 1/216ths inch used for
                    line feeds in 'COPY EXP'. This is
                    normally set to 9. If problems are
                    experienced fitting a dump onto a
                    sheet of A4 paper, POKE this
                    location with 8. This will reduce the
                    size of the dump and improve the
                    aspect ratio slightly.
  5B7Ch 8   STRIP1  Stripe one bitmap. ;\allowed to be destroyed by
  5B84h 8   STRIP2  Stripe two bitmap. ;/Temporary stack
  5B8Ch 115 TSTACK  Temporary stack (when RAM7 is mapped to C000h-FFFFh)
  5BFFh 1   N/A     Not used

In 48 BASIC mode, all the variables and routines below 5C00h (23552) do not exist; instead there is a buffer between 5B00h (23296) and 5C00h (23552) which is used for controlling the printer. This was quite a popular location for small machine code programs on the old 48K Spectrum, and if any of these routines are tried in +3 BASIC, the computer will invariably crash. Any old program that uses PEEK, POKE and USR is therefore a safer bet if it is run in 48 BASIC mode (although it can be entered in +3 BASIC mode and transferred using the SPECTRUM command). If there is a chance that a program might inadvertently address the added I/O ports of the +3, then 'OUT 32765,48' will set bit 5 in port 7FFDh to disable further use of the added ROM/RAM switching.


 Spectrum ULA

Port nnFEh - Sinclair ULA (R)
  0-4  Keyboard Inputs (0=Pressed, 1=Released)
  5    Not used
  6    EAR Input (CAS LOAD)
  7    Not used
  A8..A15 Keyboard Address Output (0=Select)

Port FEh - Sinclair ULA (W)
  0-2  Border Color               (0..7) (always with Bright=off)
  3    MIC Output (CAS SAVE)      (0=On, 1=Off)
  4    Beep Output (ULA Sound)    (0=Off, 1=On)
  5-7  Not used

Port FFh (or other) (R) Spectrum Current VRAM Data (dirt effect)
Reading from an unused port (eg. Port FFh) does return the byte being most recently read from VRAM via video DMA. During Vblank and Hblank, the return value is always FFh. During the scanline drawing periods, the return value may be FFh, or bitmap data, or attribute data (depending on when exactly the port is read).
Some games are using this dirt effect to synchronize the program with the cathode ray beam (eg. Arkanoid is using display timings to generate in-game sound frequencies).
The method does NOT work on all computers: All Timex machines are using Port FFh for something else, though the effect may still work with other unused ports (if there are any unused ports). And, newer Spectrum machines (eg. Spectrum +3) don't produce the effect at all.

Port FFh - Timex Video and Memory Control
  0-2  Screen mode. 000=screen 0, 001=screen 1, 010=hi-colour, 110=hi-res
  3-5  INK color in hi-res mode (INK=0..7) (with PAPER=INK XOR 7, BORDER=PAPER)
  6    Disable Frame Interrupt  (0=Normal, 1=Disable)
  7    External memory mode  (0=DOCK/Cartridge Slot, 1=EX-ROM/Expansion Port)
Screen 0 is the normal screen at 4000h. Screen 1 uses the same format but at 6000h.

Port F4h - Timex Memory Mapping
  0  Memory at 0000h-1FFFh  (0=HOME, 1=DOCK or EX-ROM) ;HOME=BIOS, EX-ROM=BASIC
  1  Memory at 2000h-3FFFh  (0=HOME, 1=DOCK or EX-ROM) ;HOME=BIOS
  2  Memory at 4000h-5FFFh  (0=HOME, 1=DOCK or EX-ROM) ;HOME=VRAM
  3  Memory at 6000h-7FFFh  (0=HOME, 1=DOCK or EX-ROM) ;HOME=VRAM
  4  Memory at 8000h-9FFFh  (0=HOME, 1=DOCK or EX-ROM) ;HOME=RAM
  5  Memory at A000h-BFFFh  (0=HOME, 1=DOCK or EX-ROM) ;HOME=RAM
  6  Memory at C000h-DFFFh  (0=HOME, 1=DOCK or EX-ROM) ;HOME=RAM
  7  Memory at E000h-FFFFh  (0=HOME, 1=DOCK or EX-ROM) ;HOME=RAM
The TC2048 has only 16K BIOS, the TS2068/TC2068 have an additional 8K BASIC extension in the first EX-ROM bank. Whether DOCK (Cartridge) or EX-ROM (Expansion) is mapped depends on Port FFh.Bit7.
Note: This register is often called "Horizontal Select Register", that name has nothing to do with video resolution or coordinates, instead, the name is based on treating the memory as a two-dimensional array (with y=16bit Z80 memory address, and x=bank number).

Port 7FFDh - Spectrum 128K/+2/+2A/+3 Memory Bank Register (W) (and RAM 5B5Ch)
(initially 00h)
  0-2  RAM Page to be mapped to C000h-FFFFh (0..7=Page 0..7)
  3    RAM Page to be used as VRAM          (0=Page 5, 1=Page 7)
  4    ROM Page (0=New "128K" ROM0, 1=Old "48K" ROM1)
  5    Lock Port 7FFDh (0=Normal/No change, 1=Lock/Until Reset)
  6-7  Not used
On +2A/+3:
RAM Pages 0..3 are "uncontended" (aka accessed solely by the Z80)
RAM Pages 4..7 are "contended" (aka accessed by both the Z80, and by the ULA)
On 128/+2:
RAM Pages 0,2,4,6 are "uncontended" (aka accessed solely by the Z80)
RAM Pages 1,3,5,7 are "contended" (aka accessed by both the Z80, and by ULA) (ie. the ULA can access Page 5 or 7 as VRAM; although the ULA accesses only ONE of that two pages at a time, and NEVER accesses Page 4 or 6, the whole Page 4..7 region is treated as "contended", meaning that the whole region suffers under video waitstates).

Port 1FFDh - Spectrum +2A/+3 Memory (W) (and RAM 5B67h=R/W)
  0    Paging mode  (0=Normal; ROM and RAM, 1=Special; RAM only)
  1-2  In special mode: RAM mapping mode (RAM only, for CP/M)
         Mode  0000h..3FFFh  4000h..7FFFh   8000h..BFFFh  C000h..FFFFh
         0     Bank0         Bank1          Bank2         Bank3
         1     Bank4         Bank5/Screen1  Bank6         Bank7/Screen2
         2     Bank4         Bank5/Screen1  Bank6         Bank3
         3     Bank4         Bank7/Screen2  Bank6         Bank3
  1    In normal mode, ignored.
  2    In normal mode, MSB of ROM selection (LSB is in Port 7FFDh.Bit4)
       The four ROMs banks are (mapped to 0000h..3FFFh):
         ROM 0: 128k editor, menu system and self-test program
         ROM 1: 128k syntax checker
         ROM 2: +3DOS
         ROM 3: 48 BASIC
  3    Disk motor; 1=on, 0=off
  4    Printer port strobe.
  5-7  Not used
RAM banks 1,3,4 and 6 are used for the disc cache and RAMdisc, while Bank
7 contains the second VRAM bank, editor scratchpads, and +3DOS workspace.


 Spectrum VRAM

Bitmap Area (4000h..57FFh) (1800h bytes) (6K)
Bitmap Address
  A0..A4   Horizontal Coordinate in 8-pixel steps (0..31)
  A5..A7   Vertical Coordinate in 8-pixel steps (0..7)  ;Vertical Charline
  A8..A10  Vertical Coordinate in 1-pixel steps (0..7)  ;Vertical Scanline
  A11..A12 Vertical Coordinate in 64-pixel steps (0..2) ;Vertical Block
Bitmap Data
  D0..D7   Pixels (0=Paper, 1=Ink) (D7=Left-most ... D0=Right-most)
Note: A0..A7 are arranged that a single /RAS signal can be used for reading both Bitmap and Attribute via DMA. A8..A10 are arranged that an address in HL can be vertically manipulated via INC H opcodes.

Attribute Area (5800h..5AFFh) (300h bytes) (0.75K)
Attribute Address
  A0..A4   Horizontal Coordinate in 8-pixel steps (0..31)
  A5..A9   Vertical Coordinate in 8-pixel steps (0..23)
Attribute Data
  D0..D2   Ink    (0..7 = color used for "1" bits in bitmap data)
  D3..D5   Paper  (0..7 = color used for "0" bits in bitmap data)
  D6       Bright (brightness for both Ink and Paper) (0=Dark, 1=Bright)
  D7       Flash  (0=Normal, 1=Blink; 16 frames normal, 16 frames inverted)
Note: The Screen Border color can be set via Port FEh (the border is always having Bright=off). Except, the Timex hi-res mode sets the border equal to the paper color (which is the inverse of the hi-res INK value in Port FFh, and which is always having Bright=on, for both INK and PAPER/BORDER).

Color Palette (via TV Modulator or Composite Video)
Below table shows the normal TV palette (as seen on Spectrum 48K Issue 2). Whereas, "r,g,b" = basic Red/Green/Blue intenstity on color display, "i" = basic intensity on monochrome display, "R,G,B,I" = additional intensity when the Bright bit is set.
   0 Black   ...............  ...............  ...............  .......
   1 Blue    g..............  r..............  bbbbbbbbB......  i......
   2 Red     gg.............  rrrrrrrrRR.....  bb.............  ii.....
   3 Magenta ggG............  rrrrrrrrrRR....  bbbbbbbbbBB....  iiI....
   4 Green   gggggggggGGG...  rrrR...........  bbbB...........  iiiI...
   5 Cyan    ggggggggggGGG..  rrrrR..........  bbbbbbbbbbBBB..  iiiiI..
   6 Yellow  ggggggggggGGGG.  rrrrrrrrrrRRRR.  bbbbBB.........  iiiiII.
   7 White   gggggggggggGGGG  rrrrrrrrrrrRRRR  bbbbbbbbbbbBBBB  iiiiiII
On a color display, there would be theoretically 16 colors possible, however, Dark Black is exactly same as Bright Black, so there are only 15 different colors (also, Dark Blue isn't much different than Bright Blue). Note: If the color isn't 100% properly adjusted via VR1 and VR2 on the mainboard, then White/Gray tend to appear a bit yellowish.
On a monochrome display, only the eight Bright colors are relevant, and the Dark colors are just duplicating some bright colors (for example, Dark Blue is almost same as Bright Blue, and Dark Cyan or Dark Yellow are almost same as Bright Green).

Palette on CGA Monitors (via TTL output on TS2068/TC2068 or Spectrum 128)
  TS2068 outputs only R,G,B (but NOT intensity, ie. bright is ignored)
  TC2068 and Spectrum 128 do output R,G,B,I     (with intensity)
  CGA shows intensity=1 as FULL intensity       (unlike darker blue/red on TV)
  CGA shows intensity=0 as HALF intensity or so (unlike 80% of bright on TV)
  CGA shows dark-yellow as brown                (unlike Dark-Yellow on TV)
  CGA shows bright-black as dark-gray           (unlike Black on TV)
The bright-black effect may be fixed by masking I when R,G,B are all zero.

Palette on TVs with RGB input (via Analogue output on Spectrum +2A/+3)
Spectrum +2A/+3 have a RGB output with analog levels, which can be wired to 21pin Scart connectors (most TVs with Scart sockets support only Composite A/V, but not RGB though), and which may (or may not?) produce same colors as the standard TV output.

Second Screen
Spectrums with 128K RAM (Spectrum 128/+2/+2A/+3) can use two frame buffers, the normal one at 4000h..5AFFh, and the second one in RAM Page 7; which can be mapped to CPU addresses C000h..DAFFh (see Port 7FFDh).
All Timex machines (TC2048/TC2068/TS2068) can use two frame buffers, too. The normal one at 4000h..5AFFh, and the second one at 6000h..7AFFh (see Port FFh).

Timex Hi-Color (Timex only, not supported on any Spectrums)
In this mode, the 32x24 attributes (per 8x8 pixels) at 5800h..5AFFh are unused, and instead 32x192 attributes are read from 6000h..77FFh (per 8x1 pixels). The Attribute Data is same as in Standard mode, but the Attribute Address bits are arranged in the same way as the Bitmap address.

Timex Mono Hi-Res (Timex only, not supported on any Spectrums)
In this mode, the horizontal resolution is doubled, ie. instead of reading 1 bitmap and 1 attribute byte, it reads 2 bitmap bytes. The colors (for the whole screen) can be set via Port FFh. The bitmap data is located at 4000h..57FFh and 6000h..77FFh. The data is horizontally interlaced, each 8 pixels at 4000h, 6000h, 4001h, 6001h, and so on, the vertical addresses are same as in the other modes.


 Spectrum Video Connectors

Spectrum 128 - RGB Connector
  1  Composite PAL  (75 Ohms, 1.2 Volts pk-pk)     .. ..
  2  GND            (0 Volts DC)                 /  '-'  \
  3  Bright output  (TTL)                       .  7   6  .
  4  Composite sync (TTL)                       | 3  8  1 |
  5  Vertical sync  (TTL)                       '  5   4  '
  6  Green          (TTL)                        \   2   /
  7  Red            (TTL)                          '---'
  8  Blue           (TTL)
128K Composite Video Output:
Pin 1 is composite PAL, 2 is GND. The picture is a bit dull unless your TV's video input is High Z (high impedance) However, that's quite unlikely. Normally the impedance of the video input is around 75 Ohms. To alleviate this problem, short the 68 Ohms resistor inside the Speccy that's in series with pin 1 (follow the track on the PCB). Or you can hook it directly to the input of the RF modulator.
There's no audio on any pin of the RGB connector. The audio can be taken from the MIC socket but a better balance between 48K and 128K sound is obtained directly from pin 5 of IC38.

Spectrum +2A/+3 - RGB Connector
  1  +12V                                          .. ..
  2  GND (0V)                                    /  '-'  \
  3  Audio Out                                  .  7   6  .
  4  /Composite Sync (TTL)                      | 3  8  1 |
  5  +12V                                       '  5   4  '
  6  Green (Analogue 1.67V p-p)                  \   2   /
  7  Red   (Analogue 1.67V p-p)                    '---'
  8  Blue  (Analogue 1.67V p-p)

TS2068: If the RGB output is used, BRIGHT is ignored.


 Spectrum Timings

  Maker           Spectrum    Spectrum    Spectrum    Timex        Timex
  Version         16K/48K/+   128/+2      +2A/+3      TC2048/2068  TS2068
  CPU Osc         14.000MHz   17.73447MHz ?           ?            14.112MHz
  Color Osc       4.4336MHz   CPU Osc/4   CPU Osc/n   ?            3.579545MHz
  CPU Clock       3.500000MHz 3.54690MHz  3.54690MHz  3.50000MHz   3.52800MHz
  PSG Clock       N/A         1.7734MHz   1.7734MHz   ? (if any)   1.764(75)?
  HorizontalTotal 224 clks    228 clks    228 clks    ?            224 clks
  HorizontalDraw  128 clks    128 clks    128 clks    128 clks     128 clks
  HorizontalBlank 96 clks     100 clks    100 clks    ?            ?
  Vertical Total  312 lines   311 lines   ?           ?            262 lines
  Screen          192 lines   192 lines   192 lines   192 lines    192 lines
  Upper Border    64 lines    63 lines    ?           ?            ?
  Lower Border    56 lines    56 lines    ?           ?            ?
  Frame Rate      50.08Hz/PAL 50.01Hz/PAL 50.?Hz/PAL  50.?Hz/PAL   60.11Hz/NTSC
  Flash Rate      50/32=1.6Hz 50/32=1.6Hz 50/32=1.6Hz 50/32=1.6Hz  60/32=1.9Hz
  First Delay at  14335 clks  ?           14361 clks  ?            ?
  Delay Pattern   6..1,0,0    ?           1,0,7..2    ?            ?
  Port FEh Delay  Yes         Yes         No          Yes          Yes
  VRAM Delay at   4000h-7FFFh RAM1,3,5,7  RAM4,5,6,7  4000h-7FFFh  4000h-7FFFh
  Snow Effect     Yes         Yes/Crash   ?           No?          Yes/No?

Some Clones
The russian Scorpion seems to have same timings as Spectrum 48K. The russian Pentagon has 320 lines total (64+192+48+16), and, it generates INT at begin of its 16-lines Vsync period (all other computers have INT at end of Vsync, aka begin of upper border). Horizontal total is 224 clks on both Pentagon and Scorpion, but hsync position is slightly different on each. Both Scorpion and Pentagon do not have slow (contended) memory.

VRAM Waitstates (aka "contended memory" waitstates)
Within each 8 cycles, the ULA reads 2 bitmap bytes and 2 attribute bytes via video DMA (which takes up 4 clks), followed by 4 cycles where bus is free for the CPU. Although there are 4 free cycles, the ULA allows the CPU to invoke a RAM access (or a Port FEh access) only during 2 of that 4 cycles:
  UUUUUUccUUUUUUccUUUUUUcc..   ;<--- U=Blocked by ULA, c=Free for CPU access
  654321006543210065432100..   ;<--- Number of waitstates (0=none)
For whatever reason, the access pattern is different on the +2A/+3:
  UcUUUUUUUcUUUUUUUcUUUUUU..   ;<--- U=Blocked by ULA, c=Free for CPU access
  107654321076543210765432..   ;<--- Number of waitstates (0=none)
Waitstates occur only during drawing, ie. not during Vblank and Hblank. The first ULA access appears 14335 (or 14361) cycles after the Vsync IRQ (or unknown number of cycles on NTSC and others). The above access pattern is then repeated throughout the 128 cycles drawing time, and pauses throughout the 96 (or 100) cycles hblank time.
Although a screen with attributes consists of only 6.75Kbytes, the waitstates apply on any access to memory chips that are connected to the ULA (ie. the whole 16K at 4000h..7FFFh) (or one half of RAM on 128K machines, either in banks 1,3,5,7 on Spectrum 128/+2, or in banks 4,5,6,7 on Spectrum +2A/+3). Other portions of RAM (and ROM) can be accessed without waitstates.

Details on VRAM-DMA Access Timings
As said above, within each 8 cycles, the ULA reads 2 bitmap bytes and 2 attribute bytes, the LSBs of the bitmap address are the same as of the attribute address, so two bytes (1x bitmap, and 1x attr) can be fetched with a single long /RAS signal (1.5 clks low) and two short /CAS signals (0.5 clks low):
                  |-----8 clks----||-----8 clks----|-----H/V-Blank...------
  /RAS (RAM.Pin4)  -___-___---------___-___--------------------------------
  /CAS (RAM.Pin15) -_-_-_-_---------_-_-_-_--------------------------------
During drawing, the ULA outputs all /RAS addresses in range 00h..7Fh (though not in linear order) to the memory, so the above /RAS signals do also serve as refresh signals.

Details on CPU Access Timings
CPU memory accesses are slower than video DMA (the CPU outputs /CAS and /RAS low for 1.5 clks, while DMA uses only 0.5 clks for /CAS). Normally, the CPU does also output a "dummy" /RAS for refresh (1.0 clks low), so a stream of NOP opcodes would look like so:
                  |-----8 clks----||-----8 clks----|
  /RAS (RAM.Pin4)  -___-__--___-__--___-__--___-__-   ;four NOP opcodes
  /CAS (RAM.Pin15) -___-----___-----___-----___----
When accessing "contended" memory, the ULA takes care of refresh, so the CPU's refresh signal can be suppressed, and a stream of NOP opcodes would look like so (assuming that no VRAM-DMA is taking place, eg. during Hblank):
                  |-----8 clks----||-----8 clks----|
  /RAS (RAM.Pin4)  -___-----___-----___-----___----   ;four NOP opcodes
  /CAS (RAM.Pin15) -___-----___-----___-----___----

Scanline Effects
The Spectrum doesn't have a Scanline interrupt, so changing video attributes in specfic scanlines (for using more than 2 colors per 8x8 pixels), or changing the horizontal resolution in a given scanline (on Timex machines) can be implemented only by waiting so-and-so-many clock cycles after the Vsync interrupt has occurred. That method wastes a lot of CPU load, and its difficult to get it compatible with all computers (due to different horizontal and vertical timings, and different waitstates).
Another approach would be reading from Port FFh (or other unused ports), and to wait until contains a specific color attribute, that method is more flexible than hardcoded delays, but, it doesn't work on all computers (see Port FFh info in ULA chapter for details).

Refresh and Snow
The Z80 CPU generates refresh signals used for the non-contended RAM only.
The contended VRAM uses a different refresh signal generated from the ULA.
Although contended VRAM doesn't use CPU refresh signal, the ULA can get "confused" when the CPU refresh occurs with the address bus containing a contended memory address (ie. when the IR register pair points to 4000h..7FFFh; aka when the I register is 40h..7Fh), in that case the ULA thinks that the CPU wants to access memory, causing the ULA not to load new data from VRAM (and to re-use old data), which results in "snow" on the screen. On Spectrum 128K/+2, the snow effect does exist, but it does reportedly "crash the machine shortly after I is set to point to contended memory".
Hmmm.......... assuming that the effect doesn't just block VRAM reads, but also VRAM refresh.... then it actually CRASH programs (and BIOS code) that have code/data located in the "contended" memory regions.... eg. the 16K spectrum has ONLY contended memory, so the program would survive on a few seconds or so.... the reported crash on 128K/+2 might have the same reason (but could be simply avoided by using only uncontended memory)...?


 Spectrum Sound

Standard ULA Beeper
Spectrum Sound ULA
Spectrum Sound Speaker/Amplifier/Modulator

Programmable Sound Generator
The 3-channel PSG is used in the Spectrum 128/+2/+2A/+3 (and occassionally supported by newer games).
Spectrum Sound PSG (Programmable Sound Generator)
The same PSG chip is also used in Timex TS2068/TC2068 computers and in various expansion boxes (but at different I/O addresses, and rarely supported by any games).

Speech Hardware
Spectrum Sound Speech Overview
Spectrum Sound Speech I/O Ports
Spectrum Sound Speech SP0256 Voice Generator
Spectrum Sound Speech SP0256 Instruction Set
Spectrum Sound Speech SP0256 Allophones/Words
Spectrum Sound Speech SP0256 Pin-Outs

Digital Sound (Playback/Recording)
Spectrum Sound Digital Playback/Sampling

MIDI Interfaces
Cheetah MIDI Interface (Cheetah)
EMR MIDI Interface (Electro Music Research)
MIDISYNC (Icon Design Ltd.)
MIDI Sync Boxes (XRI Systems)
Siel MIDI Interface (Siel UK Ltd)
Upstream MIDI Interface (Upstream Computer Systems Ltd)
XRI Micon MIDI Interface (XRI Systems)
ZX Spectrum 128 +2 to MIDI Lead (Cheetah)
Music Machine (see Digial Sound chapter for details)


 Spectrum Sound ULA

Sound Frequency vs Video Waitstates
Observe that video waitstates occur when drawing the bitmap area (but not during Hblank and Vblank), so the presence/absence of waitstates can have some nasty effects on software generated sound frequencies.
Basically, do not use code/data in RAM at 4000h..7FFFh when generating sounds. Waitstates do also occur on accessing Port FEh, which may become a problem with high-frequency PWM sounds, a workaround would be to access Port FEh only within multiples of 8 clock cycles (so it runs in sync with video DMA).
Aside from waitstates, it may be also good to disable IRQs to prevent the sound to get paused during IRQ handling (or at least to use a custom fast IRQ handler).

Sound Volume via Bit3 (not recommended / unstable results)
The ULA has two separate control bits for Sound and Cassette output:
  Port FEh.Bit4 = Audio Output     (0 = 0.3 Volts, 1 = 3.7 Volts)
  Port FEh.Bit3 = Cassette Output  (0 = 0.3 Volts, 1 = 0.7 Volts)
However, it does output both signals on the same pin.
  Port FEh = 18h --> 3.7V
  Port FEh = 10h --> 3.6V
  Port FEh = 08h --> 0.7V
  Port FEh = 00h --> 0.3V
Below are examples for a Spectrum 48K with Issue 2 mainboard; one column shows results for the internal speaker, the other column shows results for the MIC output passed to an external amplifier (eg. to the Audio Input of a TV set). Furthermore, results may differ on other mainboards (after issue 2, Sinclair seem to have added a transistor as internal amplifier).
  Toggle between     Internal Speaker     External Amplifier
  00h and 10h        Volume = #####       Volume = #####      ;normal
  00h and 08h        Volume = .....       Volume = ##...      ;unpredictable
  10h and 18h        Volume = ##...       Volume = #....      ;unpredictable
Normally, sound should be generated by toggling bit4 of Port FEh. As shown above, messing with bit3 has rather unpredictable results; toggling between 00h and 08h has a good voltage difference which is audible on external amplifiers, but not enough amperes to drive the internal speaker; for toggling between 10h and 18h it's vice versa, the voltage difference is smaller, but there are more amperes (it becomes audible on internal speaker, but the volume drops with external amplifier).


 Spectrum Sound Speaker/Amplifier/Modulator

Internal Speaker (Spectrum 16K/48K/Plus and Timex TS/TC20xx)
Older Spectrums with 16K/48K RAM are containing an internal speaker, used to output the ULA sound (and PSG sound in case of the Timex 2068). The dimensions (and volume) of the internal speaker would be more suitable for headphones. The TV modulator generates a raw video signal (so the TV speaker is not used).

Internal Audio UHF Modulator (Spectrum 128/+2/+2A/+3)
Newer Spectrums with 128K RAM don't include an internal speaker, instead, the ULA sound (and PSG sound) is passed to the TV set via the UHF modulator.

External Speakers/Amplifiers and Modulators
To "boost" the volume, several companies offered external amplifiers with bigger external speakers. Most external PSG/Speech devices do also mix and amplify the ULA sound with the PSG/Speech sound.
For using the TV speakers, several companies have offered internal and external UHF modulators which mixed the audio signal with the video signal (for modern TVs with A/V inputs sets it'd be easier/cheaper to pass MIC/VID to A/V input of the TV set, which'd also give better picture quality than UHF).

External Sound Connectors
In general, sound can be obtained from the MIC port (or anywhere from the mainboard). The Timex TS2068 does also output PSG/ULA on the expansion port. The Spectrum expansion Port doesn't have a sound output (however, external hardware could easily reconstruct ULA sound by latching D4 upon /IORQULA and /WR). The analogue RGB output on the Spectrum +2A/+3 does also include an audio pin (unlike the TTL RGB output on the Spectrum 128).

Mono/Stereo Sound
The standard spectrum hardware supports only mono sound output. Some of the external PSG devices allow to produce stereo sounds by passing one PSG channel to left, one to right, and the third channel to both left and right speakers; but, external PSG devices are rarely supported by any games, and there's no real standard which channel is passed to which speaker.


 Spectrum Sound PSG (Programmable Sound Generator)

I/O Ports of known PSG devices
  Model                                    Index.W  Data.W  Data.R Chip Year
  Spectrum 128/+2/+2A/+3                   FFFDh    BFFDh   FFFDh  8912 1985
  Didaktik Melodik (spectrum 128 style)    FFFDh    BFFDh   FFFDh  891x 19xx
  Timex TS2068 (NTSC)                      xxF5h    xxF6h   xxF6h  8912 1983
  Timex TC2068 (PAL)                       xxF5h    xxF6h   xxF6h  8912 1984
  Fuller Box                               xx3Fh    xx5Fh   xx3Fh  8912 1983
  Fuller Box Master Unit (with Orator)     xx3Fh    xx5Fh   xx3Fh  8912 1983
  DK'Tronics 3 Channel Sound Synthesiser   xx3Fh    xx5Fh   ?      8912 1984
  Timedata ZXM Soundbox (ZX81)             xx9Fh    xxDFh   xxBFh  8912 198x
  Timedata ZXM Soundbox (Spectrum)         xx9Fh    xxDFh   xxBFh  8912 198x
  William Stuart Systems Music Synthesiser xx9Fh    xxDFh   ?      891x 1983
  Bi-Pak ZON X81 Soundbox (ZX81)           xxDFh    xx0Fh   N/A?   891x 198x
  Bi-Pak ZON X Soundbox (Spectrum)         xxFFh    xx7Fh   N/A    8912 1982
  Ricoll RISG Sound Generator              ?        ?       ?      8910 1983
  Ricoll RIFG Sound Generator with filters ?        ?       ?      8910 1983
  Petron Trichord (includes PROM)          ?        ?       ?      8910 1983
  Signpoint Sound Synthesiser Plus         ?        ?       ?      8910 1984
  Datel 3 Channel Sound Synthesiser        ?        ?       ?      ?    1989
  Investronica INAXEL Sound & Joystick     ?        ?       ?      ?    ?

Clock Frequency
The "3.5xxMHz/32" in the formulas are meant to be the CPU clock divided by 32 (externally divided by 2 on the mainboard, and internally divided by 16 inside of the PSG). The CPU clock for different models is:
  Model                      CPU Clock  Divide by
  Spectrum 128/+2/+2A/+3     3.54690MHz / 32
  Timex TC2068 (PAL)         3.50000MHz / 32
  Timex TS2068 (NTSC)        3.52800MHz / 32
For Spectrum 16K/48K/Plus and Timex TC2048, external PSGs are available, but in most cases the clock and divider is unknown (the CPU clock on the expansion port gets stopped on VRAM waitstates, so it isn't suitable for sound, and the expansion hardware requires to include its own oscillator):
  ZON X81 Soundbox (ZX81)    3.25MHz    / 32
  ZON X Soundbox (Spectrum)  3.5MHz     / ?
  Other Models...            ? MHz      / ?
  Timedata ZXM Soundbox (ZX81)      approx 100000 Hz (probably means 3.25/32)
  Timedata ZXM Soundbox (Spectrum)  approx 100000 Hz (probably means 3.xx/32)

00h = Tone Frequency channel A, low (0-255)
01h = Tone Frequency channel A, high (0-15)
02h = Tone Frequency channel B, low (0-255)
03h = Tone Frequency channel B, high (0-15)
04h = Tone Frequency channel C, low (0-255)
05h = Tone Frequency channel C, high (0-15)
The actual listened frequency in Hertz is calculated as follows:
  F = 3.5xxMHz / 32 / nn      ;with nn in range 1..4095 (nn=0 acts as nn=1)

06h = Noise Frequency (0-31)
The actual noise frequency in Hertz is calculated as follows:
  F = 3.5xxMHz / 32 / nn      ;with nn in range 1..31 (nn=0 acts as nn=1)
Noise can be output on all 3 channels, but there is only one noise generator (so all channels share the same noise frequency). The noise generator consists of 17bit shift register, and a 1bit noise level (0=LOW or 1=HIGH). These are updated at the selected frequency as follows:
  noise_level = noise_level XOR shiftreg.bit0
  newbit = shiftreg.bit0 XOR shiftreg.bit3
  shiftreg = (shiftreg SHR 1) + (newbit SHL 16)
Note that level isn't set equal to bit0, instead, it toggles when bit0=1.

07h = Mixer Control
  Bit Expl.
  0   Channel A tone enable       (0=Enable, 1=Disable)
  1   Channel B tone enable       (0=Enable, 1=Disable)
  2   Channel C tone enable       (0=Enable, 1=Disable)
  3   Channel A noise enable      (0=Enable, 1=Disable)
  4   Channel B noise enable      (0=Enable, 1=Disable)
  5   Channel C noise enable      (0=Enable, 1=Disable)
  6   I/O port A mode             (0=Input, 1=Output)
  7   I/O port B mode             (0=Input, 1=Output)
If both Tone and Noise are disabled on a channel, then a constant HIGH level is output (useful for digitized speech). If both Tone and Noise are enabled on the same channel, then the signals are ANDed (the signals aren't ADDed) (ie. HIGH is output only if both are HIGH).

08h = Volume channel A (0-15, 16=Envelope)
09h = Volume channel B (0-15, 16=Envelope)
0Ah = Volume channel C (0-15, 16=Envelope)
Defines the volume, 0=off, 15=max. If bit4=1, then the volume is taken from the envelope generator. The volume is non-linear:
  amplitude = max / sqrt(2)^(15-nn)       ;(according to datasheet)
  eg. 15 --> max/1, 14 --> max/1.414, 13 --> max/2, etc.
The volume affects only HIGH levels. LOW levels are always NULL. Ie. sound output toggles between +VOL and NULL (not between +VOL and -VOL).
Digitized samples can be written to the volume registers (mind that volume is non-linear). When doing that, it's best to switch the channel to constant HIGH level (by disabling both Tone and Noise). Another method would be to set tone frequency to 000h or 001h (the resulting frequency is too high to be audible, so the HIGH/LOW levels sound like a constant HALF level).

0Bh = Volume Envelope Frequency, low (0-255)
0Ch = Volume Envelope Frequency, high (0-255)
Envelope step frequency (tone or noise) calculated as follows:
  F = 3.5xxMHz / 32 / nn     ;with nn in range 0..65535 (nn=0 acts as nn=1)
Depending on the envelope shape, the volume is incremented from 0 to 15, or decremented from 15 to 0. In either case it takes 16 steps to complete, the completion time for 16 steps is therefore:
  T = nn*512 / 3.5xxMHz      ;with nn in range 1..65535 (0 .. ca. 9.5 seconds)

0Dh = Volume Envelope shape (0-15)
Writing to this register (re-)starts the envelope. Additionally, the written value specifies the envelope shape, the four bits have the following meaning:
  CONT ATT ALT HLD
    0   0   X   X  \_________  0-3 (same as 9)
    0   1   X   X  /_________  4-7 (same as F)
    1   0   0   0  \\\\\\\\\\  8   (Repeating)
    1   0   0   1  \_________  9
    1   0   1   0  \/\/\/\/\/  A   (Repeating)
    1   0   1   1  \"""""""""  B
    1   1   0   0  //////////  C   (Repeating)
    1   1   0   1  /"""""""""  D
    1   1   1   0  /\/\/\/\/\  E   (Repeating)
    1   1   1   1  /_________  F

0Eh = I/O port A (with external pinouts on AY-3-8910 and AY-3-8912)
0Fh = I/O port B (with external pinouts on AY-3-8910 only)
These are two general purpose 8bit I/O ports (direction can be set via register 7). Port A exists on both AY-3-8910 and AY-3-8912 chips. Port B exists on AY-3-8910 only (or actually, it does internally exist even on the smaller AY-3-8912, but it doesn't have external pinouts).
  Model                    Port A                Port B
  Timex TC2068 and TS2068  Joysticks (*)         N/A (uses 8912 chip)
  Spectrum 128/+2/+2A/+3   RS232/Keypad          N/A (uses 8912 chip)
  Fuller Box               ?                     N/A (uses 8912 chip)
  DK'Tronics               Unused?               N/A (uses 8912 chip)
  Timedata ZXM Soundbox    Joystick/User Port    N/A (uses 8912 chip)
  Signpoint                ioport                ioport
  Other expansions...?     ?                     ?   (some do use 8910 chip)
(*) Timex also passes Bit5 of Port A to Expansion Port Pin 30 on upper side.

Spectrum 128/+2/+2A/+3 PSG Software
Some newer games (made after the Spectrum 128 was released in 1985) do support its PSG hardware (eg. Cybernoid, Exolon, Into the Eagles Nest, Head Over Heels), but many other newer games don't do so.

Fuller Box and DK'Tronics PSG Software (compatible with each other)
  Fuller Box Demonstration Program (Fuller)
  Sound Designer (DK'Tronics)
  Invasion of the Body Snatchas
Aside from the Fuller Box and DK'Tronics demonstration tapes, there seems to be almost no other software that supports these PSGs (even DK'Tronics didn't support their own hardware in their own games).

William Stuart Systems Music Synthesiser Software
  Composer (William Stuart Systems)

Timex 2068 Software
The TS2068 (USA) wasn't very popular, and the TC2068 (Portugal/Poland) was mainly used with 'normal' european Spectrum 48K software - so there are probably not too many (if any?) commercial games that support the PSG in Timex computers.


 Spectrum Sound Speech Overview

Known "General Instrument SP0256-AL2 Narrator" Speech devices
  Currah MicroSpeech (1983) - uses SP0256-AL2 speech processor, output via TV
  Fuller Orator - Speech Synthesis  (1984) - uses G1-SP0256 AL voice chip
  Fuller Box Master Unit (PSG and Orator)
  Cheetah Sweet Talker (1983) (crude thing that HALTs the CPU during speech)
  DK'Tronics Speech Synthesiser (1985) - uses the SLO/256 chip
The Currah MicroSpeech (aka Currah uSpeech) seems to be the most popular speech device, possibly because of it's easy to use BIOS extension, although that extension causes very serious compatibility problems with other software.

Other known Speech devices
  Spirit Instruments - Spectrum Speech Synthesiser (1983)
  SS1 Speech Synthesiser (1983)
  VTR Vocal Synthesizer (1985)
  Timedata ZXS Speech Synthesiser (1983)
  William Stuart Systems Chatterbox (1983)
  William Stuart Systems Chatterbox II (1985) (with intonation)
  Zebra Talker Speech Synthesiser (for Timex)
  Datel Vox Box (1984) (allophone)
  DCP Microdevelopments S-Pack (1983) - vocabulary of 71 words (upgradeable)
  DCP Microdevelopments Speech Pack (1982) for ZX81
There isn't much known about these devices, most are probably containing SP0256-AL2 chips, too. One exception are the devices from DCP, which contain Digitalker chips from National Semiconductor, and which use a fixed vocabulary instead of allophones. Not too sure if Datel has actually manfactured any hardware (possibly they've just renamed and redistributed hardware from other companies).

Cheetah Sweet Talker Software
  Chatbox (Demonstration Program by Cheetah Marketing Ltd.)

Fuller Orator / Master Unit Speech Software
  Spectrum Voice Chess (Fuller Box version; ie. NOT the ULA version) (Artic)

DK'Tronics Speech Software
  Speech Software (Demonstration Program by DK'Tronics Ltd.)

Currah MicroSpeech Software
  Title                                Publisher
  3-D Monster Chase                    Romik Software
  All or Nothing                       Abbex Electronics
  Arrow of Death Part 1                Digital Fantasia
  Arrow of Death Part 2                Digital Fantasia
  Atic Atac                            Ultimate Play The Game
  Auf Wiedersehen Pet                  Tynesoft
  Bingo                                Tynesoft
  Birds and the Bees, The              Bug-Byte Software Ltd
  Blade Alley                          PSS
  Blastermind!                         Martech Games Ltd
  Blob, The                            16/48 Tape Magazine
  Bongo                                Anirog Software
  Cagara                               Players Software
  Colour Clash                         Romik Software
  Connect 4                            John Westhead
  Covenant, The                        PSS
  Crazy Golf                           Mr. Micro Ltd
  Currah MicroSpeech                   Currah Computer Components Ltd
  Currah MicroSpeech Software          Cecomsa
  Evil Dead, The                       Palace Software
  Fruit Snapper                        LiveWire Software
  Giant's Revenge                      Thor Computer Software
  Golden Baton                         Digital Fantasia
  Grand Prix Driver                    Britannia Software Ltd
  Gremlins                             Thor Computer Software
  Harlequin                            Mr. Micro Ltd
  Hunchback                            Ocean Software Ltd
  Insult Generator                     Greg Fox
  Island, The                          Crystal Computing
  Jack and the Beanstalk               Thor Computer Software
  Learn with Ted                       Playground Software
  Lunar Jetman                         Ultimate Play The Game
  Lunar Rescue                         Lyversoft
  Max Headroom                         Quicksilva Ltd
  Maze of Terror                       Kevin Hillyer
  Maziacs                              DK'Tronics Ltd
  Mega Fruit                           Thor Computer Software
  Mined-Out                            Quicksilva Ltd
  Moon Alert                           Ocean Software Ltd
  Mr. Wimpy                            Ocean Software Ltd
  Mystic Tower                         Aardvark Software
  Night Stalker                        Thor Computer Software
  Pi-Balled                            Automata UK Ltd
  Pogo                                 Ocean Software Ltd
  Pontoon                              Oxford Computer Publishing
  Psi-Spy                              Postern Ltd
  Punchy                               Mr. Micro Ltd
  Rainy Day                            CCS
  Rockfall                             Crash
  Rockfall II                          Crash
  Roulette                             Oxford Computer Publishing
  Sexy Black Jack                      Load 'n' Run [Ita]
  Shark Attack                         Romik Software
  Skelby - The Schizophrenic Droid     Q Bit
  Spectrum Voice Chess                 Artic Computing Ltd
  Spiders Web                          Thor Computer Software
  Sport of Kings                       Mastertronic Added Dimension
  Spyship SOS                          Dynamic Software
  Starbike                             The Edge Software
  Steve Davis Snooker                  CDS Microsystems
  Terrahawks                           CRL Group PLC
  Timebomb                             CDS Microsystems
  Time Machine, The                    Digital Fantasia
  Titanic                              R&R; Software Ltd
  Twin Kingdom Valley                  Bug-Byte Software Ltd
  War of the Worlds, The               CRL Group PLC
  Xavior                               PSS
  Zepherus                             National Software Library
  Zig Zag                              DK'Tronics Ltd
Not verified if the above list is correct (Spectrum Voice Chess seems to be wrong, versions that support ULA and Fuller speech do exist, but there seems to be no Currah version).


 Spectrum Sound Speech I/O Ports

xx9Fh - Fuller Orator / Fuller Box Master Unit - Speech Output (W)
  0-5  SP0256-AL2 Allophone number
  6-7  Unknown

xxBFh - Fuller Orator / Fuller Box Master Unit - Speech Status (R)
  0-6  Unknown
  7    Load Request (LRQ) (0=Buffer full, 1=Ready to receive data)

007Fh - DK'Tronics Speech Synthesiser - Speech Output (W)
  0-5  SP0256-AL2 Allophone number
  6-7  Unknown

007Fh - DK'Tronics Speech Synthesiser - Speech Status (R)
  0-6  Unknown
  7    Load Request (LRQ) (0=Ready to receive data, 1=Not ready)

DK'Tronics Speech Synthesiser - Software Driver
The DK'Tronics hardware doesn't include a BIOS/BASIC extension ROM, but, the BASIC demonstration program includes a machine code driver, which allows the LPRINT command with backslash to be used to output speech, eg.:
  LPRINT "\d k tronics"
  LPRINT "print this\speak this"
the DK'Tronics driver does attempt to properly pronounce words automatically, without needing to use special codes like (aa) (dth) as used by Currah.

0007h - Cheetah Sweet Talker - Speech Output (halts CPU) (W)
  0-5  SP0256-AL2 Allophone number
  6-7  Unknown
This poor piece of hardware doesn't seem to have a status register that indicates when new data can be sent, instead, it does simply HALT the CPU when speech is busy. Not sure if halting is bound to the SBY or /LRQ pin (?) (in the latter case one could still execute some program code when implementing complex timings in software).

Read from Memory Address 0038h - Enable/Disable Currah uSpeech BIOS
Reading from this memory address enables/disables the 2K uSpeech BIOS (and probably also the uSpeech command/status register), ie. on each second read it gets enabled (at 0000h..07FFh), on each other read it gets disabled.
  0-7  Data/Opcode from the (newly) activated ROM
Usually the enable takes place when the CPU jumps to the vblank IRQ handler at 0038h, which is then redirected to the uSpeech BIOS, which does then do it's job, and jumps to 0038h, which re-enables the Spectrum BIOS and executes the normal IRQ handler.
There are no further BIOS hooks; initialization (upon reset) is also done from inside of the hooked IRQ handler.

Read from Memory Address 1000h - Currah uSpeech Status (R)
This port is probably accessible only when enabled via address 0038h.
  0    Load Request (LRQ) (1=Buffer full, 0=Ready to receive data)
  1-7  Unknown/unused
Bit0 seems to be the /LRQ pin of the SP0256 chip. Unknown if the SBY pin (indicating when all data is finished) is also found in this register?
The size of the buffer is unknown, maybe it can only hold only a single value (additionally to the sound being currently played)...?

Write to Memory Address 1000h - Currah uSpeech Output (W)
This port is probably accessible only when enabled via address 0038h.
  0-5  SP0256-AL2 Allophone Number (00h..3Fh)
  6    Intonation       (0=Decrease Pitch, 1=Increase Pitch)
  7    Unknown/unused   (should be zero)
Data can be written to this register when the Status.Bit0 indicates ready (not sure happens when it doesn't). The last allophone should be followed by a pause (eg. 00h=Pause10ms), otherwise the SP0256 does reportedly gets stuck repeating the last allophone (not sure what gets repeated exactly, maybe it repeats only the end of the last allophone, ie. "shhhhhhh" for "sh", or maybe the whole last allophone, ie. "shshshsh"?)
The intonation bit seems to be a special feature of the Currah hardware (not a feature of the SP0256 voice chip itself). Unknown how it is implemented... it might modify the CLK passed to the SP0256... so, aside from the frequency, this would also alter the durations, ie. the length of a "100ms" pause would vary... or it might modify whatever kind of external low-pass and/or high-pass filter...? The intonation does increase step-by-step (rather than changing abruptly from low to high)... the stepping rate, and the max/min values are unknown?

Currah uSpeech Mirrors of 0038h and 1000h and ROM
Reads from memory address 0038h are also mirrored to reads from I/O address 0038h (used by Rockfall to detect the uSpeech), apparently the port is decoded by checking only A0..A15 and /RD.
For address 1000h it's still unknown if there are mirrors, possibly, only /RD, /WR, A12, A14, A15 are decoded (probably together with the uSpeech BIOS enable flag which gets set/cleared via 0038h); if so, mirrors would occur at 1000h-1FFFh and 3000h-3FFFh in both memory and I/O region? If so, using the I/O mirrors would be not too recommended since they could conflict with other internal/external I/O ports.
For the ROM it's unknown if it's mirrored, too. Might be mirrored to 800h..FFFh and 2000h-2FFFh?

Currah uSpeech BIOS/BASIC extension
Unlike most or all other speech devices, the Currah hardware includes a BIOS extension. Currah related BASIC commands are:
  LET keys=0           ;disable spoken keystrokes (recommended for LOAD/SAVE)
  LET keys=1           ;enable spoken keystrokes (default)
  LET s$="he(ll)(oo)"  ;say hello (normal lowercase)
  LET s$="hE(ll)(oO)"  ;say hello (with raised intonation on "e" and "(oo)")
  PAUSE 1              ;wait for Vblank IRQ (where s$ is processed)
  CLEAR n              ;change RAMTOP (speech buffer is between RAMTOP and UDG)
  IF PEEK(65364)=81    ;buffer pointer LSB, [FF54h]=51h=buffer_empty (on 48K)
  IF s$(TO 1)="*"      ;check if s$ was processed (copied to speech buffer)
  IF s$(TO 1)="?"      ;check if s$ was rejected (contained invalid characters)
  IF s$(TO 1)=other    ;check if s$ was not yet processed (or buffer full)
On power up, the Currah BIOS allocates 256 bytes between RAMTOP and UDG, containing a 6-byte "header", and a 250-byte buffer; which contains allophone numbers (and intonation flag in bit6), in the format as written to address to 1000h (ie. not in ASCII format as used in the BASIC s$ variable). On the Spectrum this region is at FFxxh (on Spectrum 16K it's at 7Fxxh):
  FF57h - Flag byte
  FF56h - Spare (unused general purpose byte; NOT reserved for anything)
  FF55h - Hi byte of buffer pointer ;\contains FF51h when empty
  FF54h - Lo byte of buffer pointer ;/
  FF53h - Spare (unused general purpose byte; NOT reserved for anything)
  FF52h - Spare (unused general purpose byte; NOT reserved for anything)
  FF51h..FE58h - Speech buffer (default size=250) [FF51h]=next allophone
In BASIC, the spoken keystrokes and s$ strings are good for a first "oh it does really speak" impression, although after soon, the keystroke feature may become annoying, and the automatic modification of the first character in s$ may cause compatibility problems with various BASIC programs that do use s$ for other purposes.
Moreover, allocating 256 bytes between RAMTOP and UDG may cause problems with many machine code programs. And, hooking the IRQ handler changes Vblank handling timings which may also cause problems with a few programs.


 Spectrum Sound Speech SP0256 Voice Generator

Voice Generator
The voice generator relies on the Amplitude, Pitch, F0..F5, and B0..B5 registers, which are processed like so:
  Amplitude   --> F0 --> F1 --> F2 --> F3 --> F4 --> F5 --> PWM --> External
  Pitch/Noise     B0     B1     B2     B3     B4     B5             5kHz Filter
Another important register is the Repeat counter, which indicates when the next opcode shall be executed (and which may then load new values into the above registers).

Sample Rate and Repeat Timings
The SP0256 is (usually) driven by a 3.12MHz oscillator, and it uses 7bit PWM output, which is clocked at 3.12MHz/2. To obtain a 10kHz sample rate, the chip issues some dummy steps with constant LOW level additionally to the 128 steps needed for 7bit PWM, making it a total number of 156 steps per sample.
  Sample Rate = 3.12MHz/2/156 = 10.0kHz   ;100us per sample
Which means one sample is 100us long, that value multiplied by 64 or 91 gives the following timings per repeat:
  6.4ms per repeat (noise and pause), or
  9.1ms per repeat (tone with pitch=91)
Note: Some speech interfaces have the chip overclocked to 4MHz, resulting in higher pitch & sample rate, and shorter timings as with the normal 3.12MHz.

Amplitude/Pitch/Repeat
The 8bit amplitude register defines the volume in floating point form,
  Amplitude = lower5bit SHL upper3bit
The pitch defines the frequency, counted in numbers of samples per period.
For pitch=91, one HIGH sample (amplitude) is output, followed by 90 zero samples (null). That pattern is repeated as many times as specified in the repeat count, for example, with repeat=3:
                                  __ Amplitude level (+)
  |        |        |
  |________|________|________     __ Zero level             PITCH

  <-Pitch->                       __ Amplitude level (-)
  <--------repeat=3--------->
As shown above, the generated waveform is NOT a square wave (which would have 50% high, and 50% low). After applying filters, the final waveform may look somewhat like so:
                                  __ Amplitude level (+)
  |        |        |
  |_|_.____|_|_.____|_|_.____     __ Zero level             PITCH+FILTERS
   | |      | |      | |
   |        |        |            __ Amplitude level (-)
Note that (aside from noise) the AL2 ROM uses only one pitch value: 5Bh aka 91 decimal (meaning that all vowels are using the same base frequency, and they differ only by using different filter settings).

Amplitude/Noise/Repeat
Noise is activated when setting pitch=0. The timings are then same as when pitch=64, but instead of outputting HIGH and NULL levels, the hardware does now randomly output HIGH or LOW levels, for example, pitch=0 and repeat=5:
                                  __ Amplitude level (+)
  ||| || |  | |  || |   || ||| |
  |||_|| |__|_|__||_|___|| |||_|  __ Zero level             NOISE
     |  | || | ||  | |||  |   |
  <-64->| || | ||  | |||  |   |   __ Amplitude level (-)
  <----------repeat=5---------->
The exact random algorithm is unknown (probably some shift/xor stuff?), the random levels seem to be output on each sample (not only on the first sample of a repeat). Like normal pitch, the noise is passed to the 6 filters.

Pause/Repeat
The pause command sets amplitude=0. The timings are then same as when pitch=64, but the output is always NULL, for example, pause and repeat=5:
                                  __ Amplitude level (+)

  ______________________________  __ Zero level             PAUSE (SILENCE)

  <-64->                          __ Amplitude level (-)
  <----------repeat=5---------->
Pause does reset the filters to 0, so the silence is not affected by filters.

Digital Filters
As shown above, the amplitude/pitch/noise output is passed through six digital filter stages (using the F0..F5 and B0..B5 registers), each stage looks like so:
                      _____                      _____
  ------------------>|     |------------------->|     |-----+----->
           _____     | SUB |          ______    | SUB |     |
     +--->| *B  |--->|_____|    +--->| *2*F |-->|_____|     |
     |    |_____|     _____     |    |______|    _____      |
     +---------------|OLDER|<---+---------------| OLD |<----+
                     |_____|                    |_____|
Ie. the incoming samples are adjusted like so:
  for i=0 to 5                                         ;filter number
    sample = sample - quant_table[F.i] * OLD.i * 2     ;F0..F5 registers
    sample = sample - quant_table[B.i] * OLDER.i       ;B0..B5 registers
    OLDER.i = OLD.i
    OLD.i   = sample
  next i
Whereas, quant_table is a non-linear translation table that translates the signed 8bit registers to signed 10bit factors (with 9bit fractional part, ie. 511 means 0.99), with following entries:
  0  ,9  ,17 ,25 ,33 ,41 ,49 ,57 ,65 ,73 ,81 ,89 ,97 ,105,113,121
  129,137,145,153,161,169,177,185,193,201,209,217,225,233,241,249
  257,265,273,281,289,297,301,305,309,313,317,321,325,329,333,337
  341,345,349,353,357,361,365,369,373,377,381,385,389,393,397,401
  405,409,413,417,421,425,427,429,431,433,435,437,439,441,443,445
  447,449,451,453,455,457,459,461,463,465,467,469,471,473,475,477
  479,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495
  496,497,498,499,500,501,502,503,504,505,506,507,508,509,510,511
Above shows only positive values for index 0..127. Values for index -1..-128 should be 0..-511, or maybe -9..-512.


 Spectrum Sound Speech SP0256 Instruction Set

Memory
The SP0256 can address 60Kbytes (480Kbits) of internal or external ROM, however, usally the ROMs are only 2Kbytes (16Kbit). The ROM contains plain program code, without any kind of data arrays.
The upper 4bit of the program counter cannot be zero, so the memory starts at byte-address 1000h, and ends at FFFFh. The first 512 bytes are entrypoints, usually containing JUMP opcodes for up to 256 allophones or words.
The opcodes and their parameters are transferred serially, LSB first (except, for some reason, the JUMP/CALL/SETPAGE "Target" values are MSB first).
The JUMP/CALL/RET opcodes can address only byte-aligned addresses, however, opcodes aren't always multiples of 8bits in size, so following opcodes may begin on any bit boundary.

Opcode Summary
  0000b  SETPAGE/RET  Set Page for JUMP/CALL, or Return from CALL
  0001b  SETMODE      Set the MODE bits and Repeat MSBs
  0010b  LOAD_23      Load Pitch, Amplitude, 2-3 Coefficients
  0011b  LOAD_56      Load Pitch, Amplitude, 5-6 Coefficients
  0100b  LOAD_56D     Load Pitch, Amplitude, 5-6 Coefficients, Delta
  0101b  SETMSB_3     Load Amplitude, MSBs of 3 Coefficients
  0110b  SETMSB_23    Load Amplitude, MSBs of 2 or 3 Coeffcients
  0111b  LOAD_PA      Load Pitch, Amplitude
  1000b  LOAD_ALL     Load All Parameters (at full 8bit precision)
  1001b  DELTA_56     Add Delta to Amplitude, Pitch, 5 or 6 Coefficients
  1010b  SETMSB_3P    Load Amplitude, MSBs of 3 Coefficients, Pitch
  1011b  DELTA_23     Add Delta to Amplitude, Pitch, 2 or 3 Coefficients
  1100b  SETMSB_3D    Load Amplitude, MSBs of 3 Coefficients, Delta
  1101b  CALL         Jump to Subroutine (12-bit PAGE-Relative Address)
  1110b  JUMP         Jump to 12-bit PAGE-Relative Address
  1111b  PAUSE        Silent Pause
Each opcode starts with a 4bit parameter field, followed by the 4bit opcode number, eventually followed by further Nbit parameter(s).

Opcode 1110b - JUMP - Jump to 12-bit PAGE-Relative Address
Opcode 1101b - CALL - Jump to Subroutine (12-bit PAGE-Relative Address)
  4    Target bit8-11 (in reversed bit-order!)
  4    Opcode (must be 1110b or 1101b)
  8    Target bit0-7  (in reversed bit-order!)
  0..7 Byte-alignment for next opcode (should be padded with 0 bits)
Jumps to the specified memory address, Target bit12-15 are taken from the PAGE register (which is usually 01h, set like so on power up, but can be set to other values in range 01h..0Fh via SETPAGE).
CALL pushes the 16bit byte-aligned return address onto stack, and marks the stack as not empty. The previous contents of the stack are lost (the stack is only one entry deep).

Opcode 0000b with Zero-Operand - RET - Return from Subroutine (or HALT)
  4    Zero for RET (00h=Return)
  4    Opcode (must be 0000b)
  0..7 Byte-alignment for next opcode (should be padded with 0 bits)
If the stack was not empty: Jumps to the pushed return address (ie. to the next byte after the most recent CALL opcode), and marks the stack as empty.
If the stack was already empty: Enters HALT state until/unless new data is/was input via /ALD pin. As soon as new data is available, it jumps to 1000h+data*2.
Note: HALT state stops program execution, but does not stop the sound generator - to obtain silence, issue a short PAUSE (or another opcode that sets amplitude=0).

Opcode 0000b with Nonzero-Operand - SETPAGE - Set the PAGE register
  4    Target bit12-15 (in reversed bit-order!) (01h..0Fh=Page)
       The above target bits are used for ALL following JUMP/CALL opcodes
  4    Opcode (must be 0000b)
The PAGE register retains its setting until the next SETPAGE is encountered.
(Note that address loads via ALD appear to ignore PAGE, and set the four MSBs to $1000. They do not modify the PAGE register, so subsequent JUMP/CALL instructions will jump relative to the current value in PAGE.)

Opcode 0001b - SETMODE - Set the MODE bits and Repeat MSBs
  2    Repeat Count bit4-5 (expands the next ONE opcode that uses 4bit repeat)
  1    WIDTH for ALL following opcodes (0=Smaller, 1=Bigger bit-width)
  1    EXTRA for ALL following opcodes (0=Exclude, 1=Include optional params)
  4    Opcode (must be 0001b)
Some opcodes have coefficient parameters of variable width (for example "3/6" means 3bit or 6bit), the smaller width is used when WIDTH=0, the bigger when WIDTH=1.
Some opcodes have optional parameters (for example "(8)" means an optional 8bit parameter), which is included in the opcode only when EXTRA=1.

Opcode 1111b - PAUSE - Silent Pause
  4    Repeat Count
  4    Opcode (must be 1111b)
Provides a silent pause of varying length. The pause behaves identially to a pitch with Amplitude=0 and Period=64. All coefficients are cleared, as well.

Opcode 0111b - LOAD_PA - Load Pitch, Amplitude
  4    Repeat Count
  4    Opcode (must be 0111b)
  6    Amplitude MSBs (upper 3bit are exponent)
  8    Pitch (00h=Noise)

Opcode 1000b - LOAD_ALL - Load All Parameters (at full 8bit precision)
  4    Repeat Count
  4    Opcode (must be 1000b)
  8    Amplitude unsigned (upper 3bit are exponent)
  8    Pitch unsigned (00h=Noise)
  8    Coeff B0 signed   ;\coeff pair 0
  8    Coeff F0 signed   ;/
  8    Coeff B1 signed   ;\coeff pair 1
  8    Coeff F1 signed   ;/
  8    Coeff B2 signed   ;\coeff pair 2
  8    Coeff F2 signed   ;/
  8    Coeff B3 signed   ;\coeff pair 3
  8    Coeff F3 signed   ;/
  8    Coeff B4 signed   ;\coeff pair 4
  8    Coeff F4 signed   ;/
  8    Coeff B5 signed   ;\coeff pair 5
  8    Coeff F5 signed   ;/
  (8)  Amplitude Interpolation, signed          ;\when EXTRA=1 only
  (8)  Pitch Interpolation, signed              ;/
Notes: The pitch and amplitude deltas that are available when EXTRA=1 are applied every pitch period, not just once. Wraparound may occur. If the Pitch goes to zero, the periodic excitation switches to noise.

Opcode 0010b - LOAD_23 - Load Pitch, Amplitude, 2-3 Coefficients
Opcode 0011b - LOAD_56 - Load Pitch, Amplitude, 5-6 Coefficients
Opcode 0100b - LOAD_56D - Load Pitch, Amplitude, 5-6 Coefficients, Delta
  4    Repeat Count
  4    Opcode (must be 0010b or 0011b or 0100b)
  6    Amplitude MSBs (upper 3bit are exponent)
  8    Pitch (00h=Noise)
  3/6  Coeff B0 Bit4/1..6 unsigned ;\coeff pair 0       ;\
  5/6  Coeff F0 Bit3/2..7 signed   ;/                   ;
  3/6  Coeff B1 Bit4/1..6 unsigned ;\coeff pair 1       ; opcode LOAD_56D,
  5/6  Coeff F1 Bit3/2..7 signed   ;/                   ; and LOAD_56 only
  3/6  Coeff B2 Bit4/1..6 unsigned ;\coeff pair 2       ;
  5/6  Coeff F2 Bit3/2..7 signed   ;/                   ;/
  4/6  Coeff B3 Bit3/1..6 unsigned ;\coeff pair 3
  6/7  Coeff F3 Bit2/1..7 signed   ;/
  7/8  Coeff B4 Bit1/0..7 signed   ;\coeff pair 4
  6/8  Coeff F4 Bit2/0..7 signed   ;/
  (8)  Coeff B5 Bit0..7 signed     ;\coeff pair 5       ;\when EXTRA=1 only
  (8)  Coeff F5 Bit0..7 signed     ;/                   ;/
  5    Amplitude Interpolation LSBs, unsigned           ;\opcode LOAD_56D only
  5    Pitch Interpolation LSBs, unsigned               ;/
Sets the unspecified coefficients to 0. The "unsigned" B0,B1,B2,B3 values are zero-expanded from N bits to (N+1) bits, and are then copied to the upper (N+1) bits of the register.

Opcode 0110b - SETMSB_23 - Load Amplitude, MSBs of 2 or 3 Coeffcients
Opcode 0101b - SETMSB_3 - Load Amplitude, MSBs of 3 Coefficients
Opcode 1010b - SETMSB_3P - Load Amplitude, MSBs of 3 Coefficients, Pitch
Opcode 1100b - SETMSB_3D - Load Amplitude, MSBs of 3 Coefficients, Delta
  4    Repeat Count
  4    Opcode (must be 0101b or 0110b or 1010b or 1100b)
  6    Amplitude MSBs (upper 3bit are exponent)
  [8]  Pitch (00h=Noise)                          ;-Opcode SETMSB_3P only
  5/6  New F0 MSBs signed                         ;\
  5/6  New F1 MSBs signed                         ; Opcode SETMSB_3/3P/3D only
  5/6  New F2 MSBs signed                         ;/
  6/7  New F3 MSBs signed                         ;\
  6/8  New F4 MSBs signed                         ; Opcode SETMSB_23 only
  (8)  New F5 MSBs signed (when EXTRA=1 only)     ;/
  (0)  Set F5=0 and B5=0  (when EXTRA=0 only)
  [5]  Amplitude Interpolation LSBs, unsigned     ;\Opcode SETMSB_3D only
  [5]  Pitch Interpolation LSBs, unsigned         ;/
All other coefficient bits are unaffected (ie. all coefficients that aren't accessed by the specific opcode, as well as LSBs of accessed coefficients).

Opcode 1001b - DELTA_56 - Add Delta to Amplitude, Pitch, 5 or 6 Coefficients
Opcode 1011b - DELTA_23 - Add Delta to Amplitude, Pitch, 2 or 3 Coefficients
  4     Repeat Count
  4     Opcode (must be 1001b or 1011b)
  4     Amplitude Interpolation 6 MSBs signed
  5     Pitch Interpolation LSBs signed
  3/4   B0 4,7 MSBs signed  ;\   ;\
  3/4   F0 5,6 MSBs signed  ;/   ;
  3/4   B1 4,7 MSBs signed  ;\   ; opcode DELTA_56 only
  3/4   F1 5,6 MSBs signed  ;/   ;
  3/4   B2 4,7 MSBs signed  ;\   ;
  3/4   F2 5,6 MSBs signed  ;/   ;/
  3/4   B3 5,7 MSBs signed  ;\
  4/5   F3 6,7 MSBs signed  ;/
  4/5   B4 x,8 MSBs signed  ;\  <---- DELTA_56: x=6, and DELTA_23: x=7 (?)
  4/5   F4 6,8 MSBs signed  ;/
  (5)   B5 (8) MSBs signed  ;\   ;\when EXTRA=1 only
  (5)   F5 (8) MSBs signed  ;/   ;/
Performs a delta update, adding small 2s complement numbers to a series of coefficients. The 2s complement updates for the various filter coefficients only update some of the MSBs -- the LSBs are unaffected. The exact bits which are updated are noted above.
Normal 2s complement arithmetic is performed, and no protection is provided against overflow. Adding 1 to the largest value for a bit field wraps around to the smallest value for that bitfield.
Notes: The delta update is applied only once (even if the repeat count is bigger than 1).
The delta updates are applied to the 8-bit encoded forms of the coefficients, not the 10-bit decoded forms.
The update to the amplitude register is a normal 2s complement update to the entire register. This means that any carry/borrow from the mantissa will change the value of the exponent. The update doesn't know anything about the format of that register.

Program Counter and Stack Note
As seen in the datasheets for external speech ROMs, the Program Counter & Stack seem to be part of the ROM (not of the microprocessor). So, when using external ROMs, one would theoretically have separate stacks for each ROM. NB. this explains why the Target values are reversed; apparently the ROMs use different bit-order (for memory addresses) than the microprocessor does for its own values (ie. the opcodes and voice-parameters).

Credits
The SP0256 opcodes were reverse engineered by Joe Zbiciak and Frank Palazzolo.

Repeat Count = 0
According to Joe and Frank, a repeat count of zero "causes the instruction to not execute" (and not to fetch any of its following paramters, so the opcode becomes only 8bits long; or to fetch, but not apply them?), however, they've also mentioned that "conflicting documentation suggests there's more going on".

XXX...
Bit fields narrower than 8 bits are MSB justified unless specified otherwise, meaning that the least significant bits are the ones that are missing. These LSBs are filled with zeros.

When updating filter coefficients with a delta-update, the microsequencer performs plain 2s-complement arithmetic on the 8-bit value in the coefficient register file. No attention is paid to the format of the register.


 Spectrum Sound Speech SP0256 Allophones/Words

SP0256-AL2 Allophone List
  Num Name  Example  Funny   Actual       Num Name  Example  Funny   Actual
  00h PA1   PAUSE     10ms    6.4ms       20h /AW/  Out      370ms  254.8ms
  01h PA2   PAUSE     30ms   25.6ms       21h /DD2/ Do       160ms   72.1ms
  02h PA3   PAUSE     50ms   44.8ms       22h /GG3/ Wig      140ms  110.5ms
  03h PA4   PAUSE    100ms   96.0ms       23h /VV/  Vest     190ms  127.4ms
  04h PA5   PAUSE    200ms  198.4ms       24h /GG1/ Got       80ms   72.1ms
  05h /OY/  Boy      420ms  291.2ms       25h /SH/  Ship     160ms  198.4ms
  06h /AY/  Sky      260ms  172.9ms       26h /ZH/  Azure    190ms  134.1ms
  07h /EH/  End       70ms   54.6ms       27h /RR2/ Brain    120ms   81.9ms
  08h /KK3/ Comb     120ms   76.8ms       28h /FF/  Food     150ms  108.8ms
  09h /PP/  Pow      210ms  147.2ms       29h /KK2/ Sky      190ms  134.4ms
  0Ah /JH/  Dodge    140ms   98.4ms       2Ah /KK1/ Can't    160ms  115.2ms
  0Bh /NN1/ Thin     140ms  172.9ms       2Bh /ZZ/  Zoo      210ms  148.6ms
  0Ch /IH/  Sit       70ms   45.5ms       2Ch /NG/  Anchor   220ms  200.2ms
  0Dh /TT2/ To       140ms   96.0ms       2Dh /LL/  Lake     110ms   81.9ms
  0Eh /RR1/ Rural    170ms  127.4ms       2Eh /WW/  Wool     180ms  145.6ms
  0Fh /AX/  Succeed   70ms   54.6ms       2Fh /XR/  Repair   360ms  245.7ms
  10h /MM/  Milk     180ms  182.0ms       30h /WH/  Whig     200ms  145.2ms
  11h /TT1/ Part     100ms   76.8ms       31h /YY1/ Yes      130ms   91.0ms
  12h /DH1/ They     290ms  136.5ms       32h /CH/  Church   190ms  147.2ms
  13h /IY/  See      250ms  172.9ms       33h /ER1/ Letter   160ms  109.2ms
  14h /EY/  Beige    280ms  200.2ms       34h /ER2/ Fir      300ms  209.3ms
  15h /DD1/ Could     70ms   45.5ms       35h /OW/  Beau     240ms  172.9ms
  16h /UW1/ To       100ms   63.7ms       36h /DH2/ Bath     240ms  182.0ms
  17h /AO/  Aught    100ms   72.8ms       37h /SS/  Vest      90ms   64.0ms
  18h /AA/  Hot      100ms   63.7ms       38h /NN2/ No       190ms  136.5ms
  19h /YY2/ Yes      180ms  127.4ms       39h /HH2/ Hoe      180ms  126.0ms
  1Ah /AE/  Hat      120ms   81.9ms       3Ah /OR/  Store    330ms  236.6ms
  1Bh /HH1/ He       130ms   89.6ms       3Bh /AR/  Alarm    290ms  200.2ms
  1Ch /BB1/ Business  80ms   36.4ms       3Ch /YR/  Clear    350ms  245.7ms
  1Dh /TH/  Thin     180ms  128.0ms       3Dh /GG2/ Guest     40ms   69.4ms
  1Eh /UH/  Book     100ms   72.8ms       3Eh /EL/  Saddle   190ms  136.5ms
  1Fh /UW2/ Food     260ms  172.9ms       3Fh /BB2/ Business  50ms   50.2ms
Mind that completion of an allophone doesn't mute the voice generator (it'll keep repeating the end of the allophone until receiving a new allophone). To mute the voice generator, output a short pause (eg. PA1) after your last allophone.
The "Funny" timings are from the SP0256-AL2 data sheet (these values are totally wrong). The "Actual" timings are calculated from the pitch/repeat values in the AL2 ROM (these values should be 100% correct, when clocked at 3.12MHz).
  <--------------- Currah Codes --------------->
  Num ASCII  Num ASCII  Num ASCII      Num ASCII
  18h a      27h r      14h (aa)/(ay)  29h (ck)
  1Ch b      37h s      13h (ee)       3Ch (ear)
  08h c      11h t      06h (ii)       1Ah (eh)
  15h d      0Fh u      35h (oo)/(eau) 33h (er)
  07h e      23h v      3Fh (bb)       34h (err)
  28h f      2Eh w      21h (dd)       2Ch (ng)
  24h g      -   x "ks" 3Dh (gg)       3Ah (or)
  1Bh h      31h y      22h (ggg)      16h (ou)
  0Ch i      2Bh z      39h (hh)       1Fh (ouu)
  0Ah j                 3Eh (ii)       20h (ow)
  2Ah k      36h N/A    38h (nn)       05h (oy)
  2Dh l      00h EOL    0Eh (rr)       25h (sh)
  10h m      01h '      0Dh (tt)       1Dh (th)
  0Bh n      02h N/A    19h (yy)       12h (dth)
  17h o      03h SPACE  3Bh (ar)       1Eh (uh)
  09h p      04h ,      2Fh (aer)      30h (wh)
  -   q "kw" 04hx2 .    32h (ch)       21h (zh)

Other Allophone/Word Sets
For curiosity, below are some other SP0256-xx variants (none of the known Spectrum devices is using that variants though).

SP0256-012 Word List
This chip is used in the "Intellivoice" expansion module for Mattel's Intellivision.
  00h (SPB640      08h One         14h Thirteen    20h Seventy
      Speech       09h Two         15h Fourteen    21h Eighty
      FIFO)        0Ah Three       16h Fifteen     22h Ninety
  01h pause4       0Bh Four        17h Sixteen     23h Hundred
  02h pause3       0Ch Five        18h Seventeen   24h Thousand
  03h pause2       0Dh Six         19h Eighteen    25h -teen
  04h pause1       0Eh Seven       1Ah Nineteen    26h -ty
  05h pause0       0Fh Eight       1Bh Twenty      27h Press
  06h "Mattel      10h Nine        1Ch Thirty      28h Enter
      Electronics  11h Ten         1Dh Fourty      29h Or
      Presents"    12h Eleven      1Eh Fifty       2Ah And
  07h Zero         13h Twelve      1Fh Sixty
There isn't much known about the SPB640 FIFO, as far as I understand, it does have three functions: It can hold up to 64 word/allophone numbers (to be injected to A1..A8 pins of the SP0256), it can hold opcodes/parameters (to be injected to SER IN pin of the SP0256), and it includes a general purpose I/O port.

SP0256-017 Word List
  00h Oh           09h Nine        12h Eighteen    1Bh Hour
  01h One          0Ah Ten         13h Nineteen    1Ch Minute
  02h Two          0Bh Eleven      14h Twenty      1Dh Hundred Hour
  03h Three        0Ch Twelve      15h Thirty      1Eh Good Morning
  04h Four         0Dh Thirteen    16h Forty       1Fh Attention Please
  05h Five         0Eh Fourteen    17h Fifty       20h Please Hurry
  06h Six          0Fh Fifteen     18h It is       21h Melody A
  07h Seven        10h Sixteen     19h A.M.        22h Melody B
  08h Eight        11h Seventeen   1Ah P.M.        23h Melody C

SP0256-019 (or rather SP0256B-019) Allophone/Word/Phrase List
This chip is used in "The Voice", an expansion module for the Odyssey 2. The chip contains the following allophones and words:
  80h..BFh Allophones (same as 00h..3Fh on SP0256-AL2)    C0h "Enemy"
  C1h "All clear"     C2h "Please"     C3h "Get off"      C4h "Open fire"
  C5h "Watch out"     C6h "Mercy"      C7h "Hit it"       C8h "You blew it"
  C9h "Do it again"   CAh "Incredible" FAh "U.F.O."       FBh "Monster!"
Not sure why bit7 is set in the codes (it should be written like so to the O2 I/O ports, but maybe the bit isn't actually passed to the SP0256 chip)?
Additionally, the expansion module contains 3 built-in speech ROMs with sound effects and various words/phrases like "Amazing", "Come on", "Outch", etc. Moreover, Odyssey 2 game cartridges can contain up to 5 external speech ROMs. For details see "Odyssey 2 Technical Specs" from Daniel Boris.


 Spectrum Sound Speech SP0256 Pin-Outs

SP0256 - Speech chip
  1 GND
  2 /RESET
  3 ROM DISABLE
  4 C1
  5 C2
  6 C3
  7 VCC1
  8 SBY
  9 /LRQ
  10 A8
  11 A7
  12 SER OUT
  13 A6
  14 A5
  15 A4
  16 A3
  17 A2
  18 A1
  19 SE
  20 /ALD
  21 SER IN
  22 TEST
  23 VCC2
  24 PWM OUT
  25 /SBY RESET
  26 ROM CLK
  27 OSC1
  28 OSC2
The oscillator should be 3.12MHz
Allows to output sounds up to 5kHz (ie. the output is updated at 10kHz rate).
The SP0256 is reportedly expandable to "491 K of ROM" (probably bullshit).
The SP0256B is reportedly expandable to "480 K of ROM" (probably K=Kbits).
The TEST pin of the SP0256 chip can be used (among others) to dump its internal ROM. Note: Details on TEST are found in the "SP0256B" datasheet - but not in the "SP0256" datasheet - not sure if the pin works identical for both chip types, nor if there's a difference between them at all.

SPR16 (16kbit) and SPR32 (32kbit) - External Serial-bus Speech ROM
  1 GND
  2 C3
  3 NC
  4 ROM CLK
  5..6 NC
  7 CS1
  8 /CS2
  9 /ROM ENABLE
  10 SERIAL OUT
  11 VCC
  12..13 NC
  14 SERIAL IN
  15 C1
  16 C2

SPR128 (128kbit) - External Serial-bus Speech ROM
  1 GND
  2 C3
  3 NC
  4 ROM CLK
  5..10 NC
  11 CS1
  12 /CS2
  13 /ROM ENABLE
  14 SERIAL OUT
  15 VCC
  16..21 NC
  22 SERIAL IN
  23 C1
  24 C2


 Spectrum Sound Digital Playback/Sampling

Cheetah Specdrum (Playback)
  OUT DFh   ;output unsigned sample data (80h=silence)
Advertised as Drum machine, although hardware-wise it's simply a general purpose D/A playback device. Supported by the included SpecDrum software, and by additional Afro Kit, Electro Kit, Latin Kit tapes. Functionally same as the Cheetah Sound Sampler's playback, but using a different port address.

Latin/Electro/Afro-KitSideB --> OUTs to DF,FF,9F,BF

Cheetah - Sound Sampler (Playback/Recording)
  OUT 7Fh   ;write any data to start sampling (to be followed by delay)
  IN  BFh   ;read sampled data (after OUT 7Fh)
  OUT BFh   ;output (unsigned?) sample data (write twice?)
Supported only by the included software. Functionally same as Datel's sampler, but with port addresses BFh and 7Fh exchanged.

Datel - Digital Sound Sampler (Playback/Recording)
  OUT BFh   ;write any data to start sampling (to be followed by delay)
  IN  7Fh   ;read sampled data (after OUT BFh)
  OUT 7Fh   ;output (unsigned?) sample data (output)
Supported only by the included software. Functionally same as Cheetah's sampler, but with port addresses BFh and 7Fh exchanged.

RAM Electronics - Music Machine (Playback/Recording/MIDI)
  IN  DFh   ;dummy read to start sampling (followed by delay, and then IN BFh)
  IN  BFh   ;read sampled data (unsigned, 80h=silence) (after IN DFh)
  OUT 9Fh   ;output 8bit unsigned sample data (80h=silence)
  OUT 5Fh   ;maybe MIDI (used 4 times with values 00h,01h)         ;-INTERRUPT?
  OUT FC7Fh ;maybe MIDI (used 5 times with values 03h,91h,31h,11h) ;\
  OUT FD7Fh ;maybe MIDI (used 1 time with variable value)          ; ACIA?
  --  FE7Fh ;maybe MIDI (not used by existing software?)           ;
  IN  FF7Fh ;maybe MIDI (used 1 time)                              ;/
Note: The Amstrad CPC version has these MIDI ports:
  &F8E8;(Write only)  INTERUPT SEL  Writing 01 to this port disables internal
    Amstrad interrupts and replaces the IRQ signal from ACIA.
    Writing 00 restores normality.
  &F8EC;(Write only)  ACIA Control    See 6850 ACIA chip for details
  &F8ED;(Write only)  ACIA Data write See 6850 ACIA chip for details
  &F8EE;(Read only)   ACIA Status     See 6850 ACIA chip for details
  &F8EF;(Read only)   ACIA Data read  See 6850 ACIA chip for details
Supported only by the included software.


 Spectrum Disc and Tape Drives

Disc and Tape Overviews
Spectrum Disc and Tape Drive Summary
Spectrum Disc and Tape Commands

Standard Audio Cassette Drives
Spectrum Cassette
Spectrum Cassette TZX Format

Some other more or less popular Disc and Tape drives
Spectrum Interface 1 (Microdrive, Network, RS232)
Spectrum Disc Spectrum +3 Disc Controller (NEC uPD765)
Spectrum Disc Opus Discovery
Spectrum Disc Disciple and Plus D
Spectrum Disc Beta/BetaPlus/Beta128 Disk Interface (TRDOS)
XXX
x--> Spectrum Disc I/O Ports
x--> Spectrum Disc Formats

Disc Controllers
Spectrum Disc Controller (Western Digital WD177x)
Spectrum Disc Spectrum +3 Disc Controller (NEC uPD765)
Spectrum Disc Controller (Other)

Timex FDD Interface
The interface uses a logic circuit to page in the 4KB FDD ROM whenever there is a call to 0x0000 or 0x0008 and page it out again whenever there is a call to 0x0604. When it is needed the FDD ROM is paged in at 0x0000 and 0x1000 and the area 0x2000-0x3fff holds eight copies of 1K of RAM or four copies of 2K of RAM. The disk drive is controlled by port 0xef

NMI Snapshot Button
Many disk interfaces have a NMI button, allowing to save a snapshot of the RAM and CPU registers to disk. Since most spectrum programs are sold on cassettes, this is a useful feature; allowing to "copy" the program to disk, it can be also useful for saving game postition on disks in games that don't support saving (or that only support saving to cassette).

Floppy I/O Ports
  Interface      Chip Type Cmd/Stat Track  Sector Data   Misc
  MFT PlusD      1772 IO   E3h      EBh    F3h    FBh    EFh,E7h,F7h
  MFT Disciple   1772 IO   1Bh      5Bh    9Bh    DBh    1Fh,3Bh,7Bh,BBh,FBh
  Beta Disk      1793 IO   1Fh      3Fh    5Fh    7Fh    FFh
  Didaktik D80   ?    IO   81h      83h    85h    87h    89h
  JLO            ?    IO   8Fh      9Fh    AFh    BFh    B7h
  Putnik 1991    1772 IO   F1h      F3h    F5h    F7h    DFh/1Fh
  Putnik 1998    1772 IO   73h      F3h    77h    F7h    DFh/1Fh
  Opus Discovery 1770 MEM  2800h    2801h  2802h  2803h  3000h-3003h, IO:1Fh
  Spectrum +3    765  IO   N/A      N/A    N/A    3FFDh  2FFDh,1FFDh
IO: Accessed via IN/OUT opcodes, MEM: Accessed via LD opcodes (memory mapped).
The first 4 ports are used to access the disc controller.
The "Misc" port(s) can have various (non-standarized) purposes: Reading the controllers DRQ or IRQ signal (not supported by all interfaces), outputting Drive Select signals (or Drive Number), Motor enable (done automatically by WD1770/WD1772), Side Select, and/or non-disk related add-ons like joysticks, centronics ports, etc.

          Stepping   Compatible   MotorOn       Pin20
  WD179x  Slow                    No
  WD1770  Slow                    Yes(Pin20)    MO
  WD1772  Faster                  Yes(Pin20)    MO
  WD1773  Slow       WD1793       No            END/RDY
The newer WD177x chips contain a digital data separator and write precompensation circuitry (meaning that they require less external components than the older WD179x chips).

28pin 177x Pinouts
  1 /CS              28 INTRQ           ->
  2 R/W              27 DRQ             ->
  3 A0               26 /DDEN           <-  Double Density Enable
  4 A1               25 /WRPT           <-
  5 D0               24 /IP             <- Index Hole
  6 D1               23 /TR00           <-
  7 D2               22 WD     Write Data              ->
  8 D3               21 WG     Write Gate              ->
  9 D4               20 MO     Motor On                ->
  10 D5              19 /RD    Read Data               <-
  11 D6              18 CLK    8MHz                    <-
  12 D7              17 SD     Step-Direction          ->
  13 /RES            16 STEP   Step                    ->
  14 GND             15 +5V

40pin 179x Pinouts
...?

Putnik Port 1Fh/DFh (decoded via A5)
  0-4  Kempston Joystick
  5    Not used (always 0)
  6    INTRQ (from FDC chip)
  7    DRQ (from FDC chip)
The interface contains a manual side select switch. The interface doesn't include a ROM, maybe it's intended to be used with a patched BASIC ROM, or additional ROM cartridge, or with a boot cassette.
(Note: There's also a "Putnik RomSwitch" circuit, which uses Port A9h, probably that's intended to be used in combination with the floppy circuit.)

IDE Interface I/O Ports
  Type       divIDE Vehmaa Vehmaa  1-Chip Putnik  Putnik  Putnik  Putnik  SAfri
                    ZXCF   ZXATASP        8bitv1  8bitv2  16bitv1 16bitv2 16bit
  Data       &xxA3;  &00BF  &039F?  &00EF  &xx2B;   &00BF   &xx69;   &xx2B;   &xx59;
  Error      &xxA7;  &01BF  &039F?  &01EF  &xx2F;   &01BF   &xx79;   &xx2F;   &xx5B;
  Count      &xxAB;  &02BF  &039F?  &10EF  &xx6B;   &10BF   &xx6B;   &xx6B;   &xx5D;
  Sector     &xxAF;  &03BF  &039F?  &11EF  &xx6F;   &11BF   &xx7B;   &xx6F;   &xx5F;
  CylinderLo &xxB3;  &04BF  &039F?  &20EF  &xxAB;   &20BF   &xx6D;   &xxAB;   &xx79;
  CylinderHi &xxB7;  &05BF  &039F?  &21EF  &xxAF;   &21BF   &xx7D;   &xxAF;   &xx7B;
  Drive      &xxBB;  &06BF  &039F?  &30EF  &xxEB;   &30BF   &xx6F;   &xxEB;   &xx7D;
  Command    &xxBF;  &07BF  &039F?  &31EF  &xxEF;   &31BF   &xx7F;   &xxEF;   &xx7F;
  DataLo            &08BF  &009F?
  DataHi            &09BF  &019F?
  Error             &0EBF
  AltStatus         &0FBF
  Control    &xxE3;

ROM selection
  Type        Vehmaa      Vehmaa      Putnik      JGH         Sinclair
              ZXCF        ZXATSP      RomSwitch   ROMBox      Spectrum128
  Range       &00-&3F     &00-&3F     &00-&03     &00-&F0;     &00-&10
  Page ROM    &10BF,n     &029F,n     &xxA9;,n     &xxFD;,16*n  &7FFD,16*n
  ZX ROM      &10BF,&80+x                         &xxFD;,&10   &7FFD,&10
  RESET ROM   &00         &00         &03         &00         &00



 Spectrum Disc and Tape Drive Summary

The standard media for Spectrum software are regular Audio Cassettes. Instead of supporting disk drives, Sinclair has focused on selling Microdrive Cartridges as alternative media, until, finally Amstrad released an "official" disk drive in the Spectrum +3. However, at that time third-party companies have had already produced various disk drives, all incompatible with each other, using different controllers, I/O ports, BIOSes, file systems, and different physical dimensions (2.8", 3", 3.5", and 5.25").

Standard Audio Cassette Drives
Any standard data or audio Cassette Recorder connected to MIC/EAR sockets
Spectrum +2/+2A with internal cassette recorder
Challenge Research Ltd: Challenge Sprint (quad-speed "audio" cassette drive)
Challenge Research Ltd: Challenge Sprint Mk II (new: Interface 1 compatible)
Evesham Micro Centre: Doubler (external tape input for tape-to-tape copy)
Evesham Micro Centre: Doubler Mk II (new: more compatible with more tape decks)

Sequential Disk/Tape Drives
Sinclair Microdrive (endless tape drive) (85K..100K) (requires Interface 1)
Rotronics Wafadrive (endless tape drive) (16K, 64K, 128K)
Crescent Quick Disk (2.8" disk, with single spiral-track, 128K or 256K)
Triton Quick Disk (2.8" disk, with single spiral-track, 100K)

Floppy Disk Drives/Interfaces
Opus: Discovery (1985) (centronics, video out, kempston stick, intern 3.5" drv)
Opus: Spectra Disc Interface (1984) (centronics, external 3.5" or 5.25" drive)
Miles Gordon Technology: MGT Plus D Interface (for MGT Lifetime Drive) (1988)
Miles Gordon Technology/Rockford Products: Disciple (1987)
Spectrum +3 (internal 3" disk drive) (1987)
Technology Research: Beta Disk Interface (1984)
Technology Research: Beta Plus Disk Interface (1985) (magic button)
Technology Research: Beta 128 Disk Interface (1986)
Timex FDD and FDD-3000 Disk Drives (with external Z80 CPU)
Omnitronix Ltd: Pacer Disk Interface
John Oliger Company: JLO TS2068 Disk System
Ramex Millenia K disk interface (SPDOS, or alternately LKDOS)
Acme Electric Robot Company: AERCO FD-68 (for TS2068) (Aerco DOS, or LKDOS)
LarKen: Disk Interface and LKDOS cartridge (for TS2068)
Kempston: Disc Interface (KDOS)
Logitek: Disc Interface (connect a Commodore 1541 drive to spectrum)
Sixword Ltd.: Swift Disc
Circuit Design: CS-DISK Interface
Haytech: Cyborg Disk Drive with Spectrum Personality Module
Statacom: Datafax Disk System
Cumana Ltd: Double Density Disk Interface
Dove Microtronix Ltd: Dove Disk Interface
8BC: MB-02+ Disk Interface
Morex Peripherals Ltd: Morex Floppy Disk System
Saga Systems Ltd: Saga Disk Interface
Saga Systems Ltd: Saga Disk Interface II (with printer port)
Thurnall Electronics Ltd: Thurnall Disk System (MCD-1)
Interactive Instruments Ltd: Viscount Disk Drive System
ITL & Tyrell Systems: Byte Drive 500
Ergo Systems Ltd/Video Vault Ltd: Clive Drive
Didaktik D40
Didaktik D80
Putnik 1991 (homebrew schematic) (uses A1, incompatible with Spectrum 128)
Putnik 1998 (homebrew schematic) (uses A7, now compatible with Spectrum 128)

Compact Disc Interface
Code Masters CD Games Pack (load games from Audio CD via joystick port)

Other Interfaces
Additonally, there are a number of newer harddisk and compact flash interfaces, though they are probably rather rarely used.


 Spectrum Disc and Tape Commands

General Spectrum Load/Save commands (cas/disk)
  LOAD ""                                   ;load basic program (by wildcard)
  LOAD <filename>                           ;load basic program (by name)
  LOAD "picture.bin" CODE                   ;load binary to original addr
  LOAD "picture.bin" CODE dest(,len_verify) ;load binary to other addr
  LOAD "picture.bin" CODE SCREEN$           ;load binary to VRAM
  LOAD <filename> DATA a ()                 ;load numeric array
  LOAD <filename> DATA a$ ()                ;load character array
  SAVE <filename>                           ;save basic program
  SAVE <filename> LINE <linenumber>         ;save basic program with autostart
  SAVE "picture.bin" CODE SCREEN$           ;save binary file (VRAM at 4000h)
  SAVE "picture.bin" CODE 16384,6912        ;save binary file (same as SCREEN$)
  SAVE "picture.bin" CODE start,length      ;save binary file (custom area)
  SAVE <filename> DATA a ()                 ;save numeric array
  SAVE <filename> DATA a$ ()                ;save character array
  VERIFY <filename>  ;compare file with program in in RAM
  MERGE <filename>   ;merge file with program in in RAM

Disk commands (Spectrum +3)
  filename="DISK"          ;default name for "loader" option in +3 BIOS menu
  filename="filename.ext"  ;max 8.3 characters (for CP/M-style filesystem)
  MOVE oldname newname     ;rename (or move to other drive)
  MOVE oldname TO attr     ;change attr "+p" "+s" "+a" or "-p" "-s" "-a"
  COPY oldname newname     ;copy
  COPY textfile TO SCREEN$
  COPY textfile TO LPRINT
  COPY binfile TO SPECTRUM FORMAT   ;creates binfile.HED (with binary HEaDer)
  LOAD "d:"   ;set default load drive (a:disk1, b:disk2, m:ramdisk, t:tape)
  SAVE "d:"   ;set default save drive (a:disk1, b:disk2, m:ramdisk, t:tape)
  CAT (#stream),"filename"      ;show directory (on stream #n)
  ERASE filename           ;delete
  FORMAT drive
  FORMAT LINE baudrate     ;set RS232 baudrate
  FORMAT LPRINT "r"        ;redirect printer ("p") to RS232 ("r") stream
Streams:
  OPEN #n,"k"   ;keyboard    (default=#1)
  OPEN #n,"s"   ;screen      (default=#2)
  OPEN #n,"p"   ;printer     (default=#3)

RAM-Disk (Spectrum 128/+2/+2A/+3)
On the Spectrum 128/+2/+2A/+3, 64Kbytes of RAM are used as RAM-Disk, which can be accessed by appending an exclamation mark to cassette commands,
  LOAD! SAVE! MERGE! CAT! ERASE! --> access RAM disk
On the Spectrum +2A/+3, one can additionally/alternately access the RAM-Disk by specifying drive letter M as part of the filename, eg. LOAD "m:filename".

Microdrive (Sinclair Interface I)
  LOAD * "m" ;1; "filename"
  SAVE * "m" ;1; "filename"
  FORMAT "m" ;1; "filename"
  CAT 1
The microdrive syntax is also used by the Opus Discovery.

Rotronics Wavadrive
  CAT *           ;directory for default drive
  CAT * "a:"      ;directory for drive A
  CAT # "a:"      ;set A as default drive and load (but do not show) directory
  POKE 23767,n    ;set default drive (0=A, 1=B)
  SAVE * "a:name" ;save on drive A
  SAVE * "name"   ;save on default drive
The thing also has a one-directional centronics port (stream "c"), and a RS232 port with RXD,TXD,RTS,CTS lines (stream "r").
Extended System Area occupies 102 bytes of RAM (starting at 23734), read/write buffer occupies 1026 bytes (at 23836), directory A and B occupy 582 bytes each (at 24862 and 25444).

Opus Discovery
Uses microdrive-style syntax. Additional commands are:
  FORMAT "j";1        ;enable kempston joystick port in opus discovery
  FORMAT "j";0        ;disable kempston joystick port in opus discovery
Includes a kempston joystick port (which must be enabled as described above), and a centronics port. The centronics port can be also use as network interface, via LOAD/SAVE/VERIFY * "b".

Technology Research - Beta Disk Interface (TRDOS)
In BASIC:
  LET DOS=15360  ;=3C00h  ;for Beta and Beta Plus (TRDOSv1 or TRDOSv4)
  LET DOS=15616  ;=3D00h  ;for Beta 128 (TRDOSv5)
  RANDOMIZE USR DOS+3:REM:LOAD"D:FILENAME"  ;load from drive D
  RANDOMIZE USR DOS+0                       ;switch from BASIC to TRDOS
In TRDOS:
  LIST (K-key)                              ;show directory ;<B>=bootable
  LOAD (J-key) or RUN (R-key)               ;load a file
  USR                                       ;set disk password
  RETURN (Y-key)                            ;switch from TRDOS to BASIC

Kempston Disk Interface (K-DOS)
Uses a rather strange syntax where disk-commands are preceeded by "PRINT #4:", and in some cases followed by ":PRINT parameter(s)", eg:
  PRINT #4:LOAD"filename"
  PRINT #4:CAT:PRINT d          ;d=drive number (1..4)

Larken (LARKEN DOS aka LKDOS)
Uses similar syntax as Kempston, but must be first initialized as:
  RANDOMIZE USR 100: OPEN# 4, "dd"
Thereafter, "PRINT #4:" can be used to preceed disk-commands, eg.
  PRINT #4:LOAD"filename"
The built-in joystick port is accessed via IN 31 (kempston style).
There are also LKDOS versions for use with Ramex and Aerco disk interfaces.


 Spectrum Cassette

The cassette is accessed via bits in ULA Port FEh (see ULA chapter). Cassette input is received from EAR port, output is passed to MIC port. For the bizarre part, the MIC and EAR leads cannot be simultaneously connected, so one of the leads must be disconnected/reconnected for loading/saving. There is no Motor On signal, so PLAY/STOP must be pressed manually.

Spectrum Cassette Blocks
  Silence (low level)
  8063 (Header) or 3223 (Data) Pilot Pulses (2168 clks/pulse) (619us/pulse)
  1st Sync Pulse (667 clks/pulse) (190us/pulse)
  2nd Sync Pulse (735 clks/pulse) (210us/pulse)
  Blocktype Byte (00h=Header, FFh=Data)
  Data Byte(s) (two 855 or 1710 clks/pulse per bit) (244 or 488 us/pulse)
  Checksum Byte (above Blocktype and Data Bytes XORed with each other)
  End pulse (opposite level of last pulse)
  Silence (low level)

Spectrum Pilot/Sync/Bits/Bytes
  /"""\__/                            Sync Pulses (1st=667, 2nd=735 clks/pulse)
  /""""\____/                         "0"-Bit (two pulses, 855 clks/pulse)
  /"""""""""\_________/               "1"-Bit (two pulses, 1710 clks/pulse)
  /""""""""""""\____________/ etc.    Pilot Pulses (2168 clks/pulse)
Bytes are transferred MSB (Bit7) first. Signals may be inverted.

Spectrum Header Block
  Pre  1    Blocktype (aka Flag Byte) (must be 00h for Header blocks)
  0    1    Filetype (0..3, see below)
  1    10   Filename (padded with blanks, ie. chr(20h))
  11   2    Length of following Data Block (LEN)
  13   4    Parameters (depends on Filetype, see below)
  Post 1    Checksum (above 18 bytes XORed with each other)

Spectrum Data Block
  Pre  1    Blocktype (aka Flag Byte) (must be FFh for Data Blocks)
  0    LEN  Data
  Post 1    Checksum (above LEN+1 bytes XORed with each other)

Spectrum Filetypes/Parameters (Bytes 13..16 of Header Blocks)
For Filetype=00h / BASIC Program File (Program and Variables):
  13   2    Autostart LINE Number (or 8000h..FFFFh if no autostart)
  15   2    Size of the PROG area aka start of the VARS area
For Filetype=01h / BASIC Number Array, or Filetype=02h / BASIC Character Array:
  13   1    Unknown
  14   1    Name of the variable
  15   2    Unknown
For Filetype=03h / Binary CODE File (or SCREEN$ file):
  13   2    Memory Address
  15   2    Unused (Should be 8000h)
A SCREEN$ file is a CODE file with Memory Address 4000h and LEN=1B00h.

.TAP Files (Standard Cassette-Image for normal unprotected files)
A .TAP file can contain one or more spectrum file(s). Each file should usually consist of two blocks (header and data). Each block is preceeded by a 16bit length value (eg. 0013h for Header blocks), followed by "length" bytes which contain the same information as stored on real cassettes (including the leading Blocktype byte, and ending Checksum byte).
TAP files do not contain pilot/sync pulses, nor baudrate information, so they can be used only for standard files (as used by the BIOS functions), not for copyprotected software.

.TZX Files (Standard Cassette-Image for copy-protected files)
Spectrum Cassette TZX Format


 Spectrum Cassette TZX Format

For some general definitions, see:
Spectrum Cassette TZX Notes

TZX Header (length: 0Ah bytes)
  00h 8  TZX signature ("ZXTape!",1Ah)
  08h 1  TZX major revision number (currently 01h, for v1.20)
  09h 1  TZX minor revision number (currently 1Ah, for v1.20)

ID 10h - Standard Speed Data Block (length: [02h..03h]+4)
This block must be replayed with the standard Spectrum BIOS timings - see the values in curly {} brackets in block ID 11h.
  00h 2  Pause after this block (ms) (0=none) {1000}
  02h 2  Length of data that follow (N)
  04h N  Data as in .TAP files (first byte implies number of pilot pulses)
The pilot tone consists of 8063 pulses if the first data byte (flag byte) (aka block_type) is < 128, or 3223 pulses otherwise. This block can be used for the BIOS loading routines AND for custom loading routines that use the same timings as BIOS ones do.

ID 11h - Turbo Speed Data Block (length: [0Fh..11h]+12h)
  00h 2  Length of PILOT pulse                {2168} ;-occurs P times at begin
  02h 2  Length of SYNC first pulse           {667}  ;-occurs ONCE after above
  04h 2  Length of SYNC second pulse          {735}  ;-occurs ONCE after above
  06h 2  Length of ZERO bit pulse             {855}  ;-occurs TWICE per bit
  08h 2  Length of ONE bit pulse              {1710} ;-occurs TWICE per bit
  0Ah 2  Number of PILOT pulses (P)           {8063 for header, 3223 for data)
  0Ch 1  Number of used bits in the last byte {8}
          (e.g. if this is 6, then the bits used (x) in the last byte are:
          xxxxxx00, where MSb is the leftmost bit, LSb is the rightmost bit)
  0Dh 2  Pause after this block (ms) (0=none) {1000}
  0Fh 3  Length of data that follow (N)
  12h N  Data as in .TAP files                {block_type, data[N-2], chksum}
This block is very similar to the normal TAP block but with some additional info on the timings and other important differences. The same tape encoding is used as for the standard speed data block. If a block should use some non-standard sync or pilot tones (i.e. all sorts of protection schemes) then use the next three blocks to describe it.

ID 12h - Pure Tone (length: 04h)
  00h 2  Length of one pulse in T-states
  02h 2  Number of pulses
This will produce a tone which is basically the same as the pilot tone in the ID 10h, ID 11h blocks. You can define how long the pulse is and how many pulses are in the tone.

ID 13h - Pulse sequence (length: [00h]*2+1)
  00h 1    Number of pulses (N)
  01h 2*N  Pulse lengths (16bit each)
This will produce N pulses, each having its own timing. Up to 255 pulses can be stored in this block; this is useful for non-standard sync tones used by some protection schemes.

ID 14h - Pure Data Block (length: [07h..09h]+0Ah)
  00h 2  Length of ZERO bit pulse
  02h 2  Length of ONE bit pulse
  04h 1  Used bits in last byte (other bits should be 0)
          (e.g. if this is 6, then the bits used (x) in the last byte are:
          xxxxxx00, where MSb is the leftmost bit, LSb is the rightmost bit)
  05h 2  Pause after this block (ms) (0=none)
  07h 3  Length of data that follow (N)
  0Ah N  Data as in .TAP files
Same as in the turbo loading data block, but without pilot and sync pulses.

ID 15h - Direct Recording (length: [05h..07h]+08h)
  00h 2  Number of T-states per sample-bit (usually 158 or 79)
  02h 2  Pause after this block (ms) (0=none)
  04h 1  Used bits (samples) in last byte of data (1..8)
          (eg. 2 means only first two samples of the last byte will be played)
  05h 3  Length of sample data in bytes (N)
  08h N  Sample data. Each bit represents a state on the EAR port
          (i.e. one sample). MSb is played first.
This block is used for tapes which have some parts in a format such that the turbo loader block cannot be used. This is not like a VOC file, since the information is much more compact. Each sample value is represented by one bit only (0 for low, 1 for high) which means that the block will be at most 1/8 the size of the equivalent VOC.
The preferred sampling frequencies are 22050 or 44100 Hz (158 or 79 T-states/sample), this will ensure correct playback when using PC's soundcards. Please, if you can, don't use other sampling frequencies.
Please use this block only if you cannot use any other block.

ID 16h - C64 BIOS Type Data Block (length: [00h..03h]+0 or 4?) (v1.13 only)
ID 17h - C64 Turbo Tape Data Block (length: [00h..03h]+0 or 4?) (v1.13 only)
These two blocks were defined (although rarely/never used) in TZX version v1.13 only, intended to support Commodore 64 files.
Bug: Blocks invented after v1.10 are stated to have a length of "[00h..03h]+4" bytes, while the C64 blocks invented in v1.13 were ALSO stated by "[00h..03h]" in length, so it's somewhat unclear how to the C64 blocks. Anyways, they should never show up in Spectrum files, and as far as I know the format wasn't actually used in the C64 world either.

ID 18h - CSW Recording (length: [00h..03h]+4) (v1.20 and up)
  00h 4  Block length (without these four bytes) (10h+N)
  04h 2  Pause after this block (ms) (0=none)
  06h 3  Sampling rate
  09h 1  Compression type (01h=RLE, 02h=Z-RLE)
  0Ah 4  Number of stored pulses (after decompression, for validation purposes)
  0Eh N  CSW data, encoded according to the CSW file format specification.
This block contains a sequence of raw pulses encoded in Ramsoft's CSW format v2 (Compressed Square Wave). The encoding of the RLE and Z-RLE data is undocumented. It can reportedly compress a 12MB 44kHz VOC file to 1MB, which is still very bloated, so better don't use the CSW stuff.

ID 19h - Generalized Data Block (length: [00h..03h]+4) (v1.20 and up)
This block has been specifically developed to represent an extremely wide range of data encoding techniques.
Spectrum Cassette TZX Format ID 19h

ID 20h - Pause (silence) or 'Stop the Tape' command (length: 2)
  00h 2  Pause after this block (ms) (or 0=stop tape, ie. infinite pause)
This will make a silence for a given time in milliseconds. If the value is 0 then the emulator or utility should (in effect) STOP THE TAPE, ie. it should make a pause of infinite length (until the user or emulator requests to restart the tape motor). As usually, the first 1 ms of the pause must be opposite of the old level, the remaining milliseconds must be low level.

ID 21h - Group start (length: [00h]+1)
  00 1  Length of the group name string (L)
  01 L  Group name in ASCII format (please keep it under 30 characters long)
This block marks the start of a group of blocks which are to be treated as one single (composite) block. This is very handy for tapes that use lots of subblocks like Bleepload (which may well have over 160 custom loading blocks). You can also give the group a name (example 'Bleepload Block 1').
For each group start block, there must be a group end block. Nesting of groups is not allowed.

ID 22h - Group end (length: 0)
This indicates the end of a group. This block has no body.

ID 23h - Jump to block (length: 2)
  00h 2  Relative jump value (number of blocks, from current block)
This block will enable you to jump from one block to another within the file. Some examples:
  Jump +0 = Loop Forever         ;this should never happen
  Jump +1 = Go to the next block ;acts as a NOP in assembler
  Jump +2 = Skip one block
  Jump -1 = Go to the previous block
All blocks are included in the block count!

ID 24h - Loop start (length: 2) (v1.10 and up)
  00h 2  Number of repetitions (greater than 1)
If you have a sequence of identical blocks, or of identical groups of blocks, you can use this block to tell how many times they should be repeated. This block is the same as the FOR statement in BASIC.
For simplicity reasons don't nest loop blocks!

ID 25h - Loop end (length: 0) (v1.10 and up)
This is the same as BASIC's NEXT statement. It means that the utility should jump back to the start of the loop if it hasn't been run for the specified number of times. This block has no body.

ID 26h - Call sequence (length: [00h..01h]*2+2) (v1.10 and up)
  00h 1   Number of calls to be made (N)
  02h 2*N Array of call block numbers (relative-signed 16bit offsets)
This block is an analogue of the CALL Subroutine statement. It basically executes a sequence of blocks that are somewhere else and then goes back to the next block. Because more than one call can be normally used you can include a list of sequences to be called. The 'nesting' of call blocks is also not allowed for the simplicity reasons. You can, of course, use the CALL blocks in the LOOP sequences and vice versa. The value is relative for the obvious reasons - so that you can add some blocks in the beginning of the file without disturbing the call values. Please take a look at 'Jump To Block' for reference on the values.

ID 27h - Return from sequence (length: 0) (v1.10 and up)
This block indicates the end of the Called Sequence. The next block played will be the block after the last CALL block (or the next Call, if the Call block had multiple calls). This block has no body.

ID 28h - Select block (length: [00h..01h]+2) (v1.10 and up)
  00h 2  Length of the whole block (without these two bytes)
  02h 1  Number of selections (N)
  03h .. List of selections
Each selection has the following format:
  00 2  Relative Offset (16bit)
  02 1  Length of description text (L)
  03 L  Description text in ASCII (please use single line and max 30 chars)
This block is useful when the tape consists of two or more separately-loadable parts. With this block, you are able to select one of the parts and the utility/emulator will start loading from that block. For example you can use it when the game has a separate Trainer or when it is a multiload. Of course, to make some use of it the emulator/utility has to show a menu with the selections when it encounters such a block. All offsets are relative signed words.

ID 2Ah - Stop the tape if in 48K mode (length: 4) (v1.xx? and up)
  00h 4  Length of the block without these four bytes (=0)
When this block is encountered, the tape will stop ONLY if the machine is an 48K Spectrum. This block is to be used for multiloading games that load one level at a time in 48K mode, but load the entire tape at once if in 128K mode.
This block has no body of its own, but follows the extension rule.

ID 2Bh - Set signal level (length: 5) (v1.20 and up)
  00h 4  Block length (without these four bytes) (=1)
  04h 1  Signal level (0=low, 1=high)
This block sets the current signal level to the specified value (high or low). It should be used whenever it is necessary to avoid any ambiguities, e.g. with custom loaders which are level-sensitive.

ID 30h - Text description (length: [00h]+1)
  00h 1  Length of the text description (N)
  01h N  Text description in ASCII format
This is meant to identify parts of the tape, so you know where level 1 starts, where to rewind to when the game ends, etc. This description is not guaranteed to be shown while the tape is playing, but can be read while browsing the tape or changing the tape pointer.
The description can be up to 255 characters long but please keep it down to about 30 so the programs can show it in one line (where this is appropriate).
Please use 'Archive Info' block for title, authors, publisher, etc.

ID 31h - Message block (length: [01h]+2)
  00h 1  Time (in seconds) for which the message should be displayed
  01h 1  Length of the text message (N)
  02h N  Message that should be displayed in ASCII format
This will enable the emulators to display a message for a given time. This should not stop the tape and it should not make silence. If the time is 0 then the emulator should wait for the user to press a key.
The text message should:
  stick to a maximum of 30 chars per line;
  use single 0x0D (13 decimal) to separate lines;
  stick to a maximum of 8 lines.
If you do not obey these rules, emulators may display your message in any way they like.

ID 32h - Archive info (length: [00h..01h]+2)
  00h 2  Length of the whole block (without these two bytes)
  02h 1  Number of text strings (N)
  03h N  List of Text Structures
Text Structure format:
  00h 1  Text identification byte (see list below)
  01h 1  Length of text string (L)
  02h L  Text string in ASCII format
Text identification byte values:
  00h Full title
  01h Software house/publisher
  02h Author(s)
  03h Year of publication                     (eg. 1982) (in ASCII, too)
  04h Language                 (v1.10 and up) (eg. English) (=default)
  05h Game/utility type        (v1.12 and up) (eg. arcade adventure, puzzle)
  06h Price with currency      (v1.12 and up) (eg. GBP 5.99, or chr(A3h)=GBP)
  07h Protection scheme/loader (v1.12 and up) (eg. Speedlock 1, Alkatraz)
  08h Origin                   (v1.12 and up) (eg. Original, Budget re-release)
  FFh Comment(s)
  xxh Reserved for future      (decoders should skip any unknown entries)
When using this block, store it at the beginning of the tape. The information about what hardware the tape uses is in the 'Hardware Type' block, so no need for it here. According to the tzx format release notes, v1.10 and up allows multiple lines in the 'Archive info' (ID 32h) block.

ID 33h - Hardware type (length: [00h]*3+1)
  00h 1   Number of machines and hardware types for which info is supplied (N)
  01h 3*N List of machines and hardware
This blocks contains information about the hardware that the programs on this tape use. For details, see:
Spectrum Cassette TZX Format HW
This feature is (theoretically) the main advantage of the TZX format over the TAP format, however, in practice, most (or all) existing TZX files don't use this feature. So, for that matter, one could as well keep using normal TAP files.

ID 34h - Emulation info (length: 8) (up to v1.13 only)
This block was defined (although rarely used) in TZX versions up to v1.13 only.
  00h 2 General emulation flags:
         bit 0 : R-register emulation [1]
         bit 1 : LDIR emulation [1]
         bit 2 : high resolution colour emulation with true interrupt freq. [1]
         bit 3,4 : video synchronisation (1=high, 3=low, 0,2=normal) [0]
         bit 5 : fast loading when BIOS load routine is used [1]
         bit 6 : border emulation [1]
         bit 7 : screen refresh mode (0=off, 1=on) [1]
         bit 8 : start playing the tape immediately (0=no, 1=yes) [0]
         bit 9 : auto type LOAD"" or press ENTER when in 128k mode [0]
  02h 1 Update screen every N frames (1..255) (used only when flags.bit7=1)
  03h 2 Interrupt Frequency in Hertz (0..999)
  05h 3 Reserved for future expansion
This is a special block that would normally be generated only by emulators.
Those bits that are not used by the emulator that stored the info, should be left at their [default] values.

ID 35h - Custom info block (length: [10h..13h]+14h) (works in v1.01 and up)
  00h 16 Identification string (in ASCII) (usually padded with spaces)
  10h 1  Length of the custom info (L)
  14h L  Custom info
This block can be used to save any information you want. For example, it might contain some information written by a utility, extra settings required by a particular emulator, or even poke data.
Spectrum Cassette TZX Format Custom
Note: The Length of the ID string was accidently documented as 15 bytes in v1.00 (although it should have been 16), this has been fixed in v1.01.

ID 40h - Snapshot block (length: [01h..03h]+4) (v1.10..v1.13 only)
This block was defined (although rarely used) in tzx versions v1.10..v1.13 only.
  00h   1  Snapshot type (00h=.Z80 format, 01h=.SNA format)
  01h   3  Snapshot length (L)
  04h   L  The Snapshot itself
Allows to snapshot the game at the start and still have all the tape blocks (level data, etc.) in the same file. The emulator should take care of that the snapshot is not taken while the actual Tape loading is taking place. When an emulator encounters the snapshot block it should load it and then continue with the next block.

ID 5Ah (aka "Z") - Glue block (length: 09h)
  00h 9  ZXTape Header (excluding the "Z", ie. "XTape!",1Ah,MajR,MinR)
This block is generated when you merge two ZX Tape files together. It is here so that you can easily copy the files together and use them. Of course, this means that resulting file would be 10 bytes longer than if this block was not used. If you can avoid using this block for this purpose, then do so; it is preferable to use a utility to join the two files and ensure that they are both of the higher version number.

ID xxh - Reserved (length [00h..03h]+4)
  00h 4  Block length (32bit) (without these four bytes)
  04h .. Reserved
Reserved for future. As shown above, all new blocks added after v1.10 should begin with a 32bit Block length entry in the first 4 bytes; the length value should exclude that first 4 bytes). With that definition, decoders may skip unknown blocks - and may still work (if the blocks contain only comments or so).
A special case are the C64 blocks (ID 16h and ID 17h) where the TZX format does "clearly" define BOTH to include AND to exclude first 4 bytes in the length entry (that blocks should never show up in Spectrum files, so it should be no problem, as far as I know they even weren't ever actually used for the C64).


 Spectrum Cassette TZX Format HW

ID 33h - Hardware type (length: [00h]*3+1)
  00h 1   Number of machines and hardware types for which info is supplied (N)
  01h 3*N List of machines and hardware (HWINFO)
HWINFO structure format:
  00h 1  Hardware type        (00h..10h; see the headlines of the ID lists)
  01h 1  Hardware ID          (00h..xxh; see the ID list entries)
  02h 1  Hardware information (00h..03h; see the small table below)
This blocks contains information about the hardware that the programs on this tape use. Please include only machines and hardware for which you are 100% sure that it either runs (or doesn't run) on or with, or you know it uses (or doesn't use) the hardware or special features of that machine.
If the tape runs only on the ZX81 (and TS1000, etc.) then it clearly won't work on any Spectrum or Spectrum variant, so there's no need to list this information.
If you are not sure or you haven't tested a tape on some particular machine/hardware combination then do not include it in the list.
The list of hardware types and IDs is somewhat large, and may be found at the end of the format description.

Hardware information (3rd byte)
  00h The tape RUNS on this hardware (and may or may not use its features)
  01h The tape RUNS and DOES use the special features of the hardware
  02h The tape RUNS but DOES-NOT use the special features of the hardware
  03h The tape DOES-NOT-WORK on this hardware

Hardware information reference
This is the list of all hardware types and hardware identification ID's that are used in the 'Hardware info' block. Please send any additions you might have to the TZX maintainers so that they can be included.
By default you don't have to write any of the information if the game is made for the ZX Spectrum and complies with the following:
  - runs on ZX Spectrum 48K
  - runs on, but doesn't use any of the special hardware of ZX Spectrum 128K
  - doesn't run on ZX Spectrum 16K
If, for example, game works on BOTH ZX 48K and 128K, and uses the hardware of the 128K Spectrum, then you would just include the 128K Spectrum in the list (because by default it has to work on 48K too).
If the game is 128K ONLY then you would include two entries: The game works on AND uses the hardware of a 128K Spectrum AND the game DOESN'T work on a 48K Spectrum.
If the game works on both 48K and 128K Spectrum, but it only uses the sound chip (AY) of the 128K Spectrum and none of its extra memory then you would only include the entry that says that the game uses the 'Classic AY hardware (Spectrum 128 compatible sound device)'.
These were only some examples of the usage of this block.

Computer Types (first byte = 00h)
  00h ZX Spectrum 16k
  01h ZX Spectrum 48k, Plus
  02h ZX Spectrum 48k ISSUE 1
  03h ZX Spectrum 128k + (Sinclair)
  04h ZX Spectrum 128k +2 (grey case)
  05h ZX Spectrum 128k +2A, +3
  06h Timex Sinclair TC-2048
  07h Timex Sinclair TS-2068
  08h Pentagon 128
  09h Sam Coupe
  0Ah Didaktik M
  0Bh Didaktik Gama
  0Ch ZX-80 (via ID 19h, generalized data block, supported in v1.20 and up)
  0Dh ZX-81 (via ID 19h, generalized data block, supported in v1.20 and up)
  0Eh ZX Spectrum 128k, Spanish version
  0Fh ZX Spectrum, Arabic version
  10h Microdigital TK 90-X
  11h Microdigital TK 95
  12h Byte
  13h Elwro 800-3
  14h ZS Scorpion 256
Below in v1.02 and up (For Amstrad, use extension .CDT instead of .TZX)
  15h Amstrad CPC 464
  16h Amstrad CPC 664
  17h Amstrad CPC 6128
  18h Amstrad CPC 464+
  19h Amstrad CPC 6128+
Below in v1.12 and up
  1Ah Jupiter ACE
  1Bh Enterprise
Below in v1.13 only
  1Ch (was intended for Commodore 64)
  1Dh (was intended for Commodore 128)
Below in v1.20 and up
  1Eh Inves Spectrum+
  1Fh Profi
  20h GrandRomMax
  21h Kay 1024
  22h Ice Felix HC 91
  23h Ice Felix HC 2000
  24h Amaterske RADIO Mistrum
  25h Quorum 128
  26h MicroART ATM
  27h MicroART ATM Turbo 2
  28h Chrome
  29h ZX Badaloc
  2Ah TS-1500
  2Bh Lambda
  2Ch TK-65
  2Dh ZX-97

External storage types (first byte = 01h)
  00h ZX Microdrive
  01h Opus Discovery
  02h MGT Disciple
  03h MGT Plus-D
  04h Rotronics Wafadrive
  05h TR-DOS (BetaDisk)
  06h Byte Drive
  07h Watsford
  08h FIZ
  09h Radofin
  0Ah Didaktik disk drives
  0Bh BS-DOS (MB-02)
  0Ch ZX Spectrum +3 disk drive
  0Dh JLO (Oliger) disk interface
  0Eh Timex FDD3000
  0Fh Zebra disk drive
  10h Ramex Millenia
  11h Larken
Below in v1.20 and up
  12h Kempston disk interface
  13h Sandy
  14h ZX Spectrum +3e hard disk
  15h ZXATASP
  16h DivIDE
  17h ZXCF

ROM/RAM type add-ons (first byte = 02h)
  00h Sam Ram
  01h Multiface ONE
  02h Multiface 128k
  03h Multiface +3
  04h MultiPrint
  05h MB-02 ROM/RAM expansion
Below in v1.20 and up
  06h SoftROM
  07h 1k
  08h 16k
  09h 48k
  0Ah Memory in 8-16k used

Sound devices (first byte = 03h)
  00h Classic AY hardware (compatible with 128k ZXs)
  01h Fuller Box AY sound hardware
  02h Currah microSpeech
  03h SpecDrum
  04h AY ACB stereo (A+C=left, B+C=right); Melodik
  05h AY ABC stereo (A+B=left, B+C=right)
Below in v1.20 and up
  06h RAM Music Machine
  07h Covox
  08h General Sound
  09h Intec Electronics Digital Interface B8001
  0Ah Zon-X AY
  0Bh QuickSilva AY
  0Ch Jupiter ACE

Joysticks (first byte = 04h)
  00h Kempston
  01h Cursor, Protek, AGF
  02h Sinclair 2 Left (12345)
  03h Sinclair 1 Right (67890)
  04h Fuller

Mice (first byte = 05h)
  00h AMX mouse
  01h Kempston mouse

Other controllers (first byte = 06h)
  00h Trickstick
  01h ZX Light Gun
  02h Zebra Graphics Tablet
Below in v1.20 and up
  03h Defender Light Gun

Serial ports (first byte = 07h)
  00h ZX Interface 1
  01h ZX Spectrum 128k

Parallel ports (first byte = 08h)
  00h Kempston S
  01h Kempston E
  02h ZX Spectrum +3
  03h Tasman
  04h DK'Tronics
  05h Hilderbay
  06h INES Printerface
  07h ZX LPrint Interface 3
  08h MultiPrint
  09h Opus Discovery
  0Ah Standard 8255 chip with ports 31,63,95

Printers (first byte = 09h)
  00h ZX Printer, Alphacom 32 & compatibles
  01h Generic printer
  02h EPSON compatible

Modems (first byte = 0Ah)
  00h Prism VTX 5000
  01h T/S 2050 or Westridge 2050

Digitizers (first byte = 0Bh)
  00h RD Digital Tracer
  01h DK'Tronics Light Pen
  02h British MicroGraph Pad
Below in v1.20 and up
  03h Romantic Robot Videoface

Network adapters (first byte = 0Ch)
  00h ZX Interface 1

Keyboards & keypads (first byte = 0Dh)
  00h Keypad for ZX Spectrum 128k

AD/DA converters (first byte = 0Eh)
  00h Harley Systems ADC 8.2
  01h Blackboard Electronics

EPROM programmers (first byte = 0Fh)
  00h Orme Electronics

Graphics (first byte = 10h)
Below in v1.20 and up
  00h WRX Hi-Res
  01h G007
  02h Memotech
  03h Lambda Colour


 Spectrum Cassette TZX Format ID 19h

ID 19h - Generalized Data Block (length: [00h..03h]+4) (v1.20 and up)
  00h 4  Block length (without these four bytes)
  04h 2  Pause after this block (ms) (0=none)
  06h 4  Total number of symbols in pilot/sync block (can be 0) (TOTP) ;\pilot
  0Ah 1  Maximum number of pulses per pilot/sync symbol         (NPP)  ; hdr
  0Bh 1  Number of pilot/sync symbols in alphabet table (0=256) (ASP)  ;/
  0Ch 4  Total number of symbols in data stream (can be 0)      (TOTD) ;\data
  10h 1  Maximum number of pulses per data symbol               (NPD)  ; hdr
  11h 1  Number of data symbols in the alphabet table (0=256)   (ASD)  ;/
  12h .. Pilot/Sync symbols definition table         (only if TOTP>0)  ;\pilot
  ..  .. Pilot/Sync RLE-compressed stream (PRLE)     (only if TOTP>0)  ;/stream
  ..  .. Data symbols definition table               (only if TOTD>0)  ;\data
  ..  .. Data stream (as in .TAP files)              (only if TOTD>0)  ;/stream
Can be used to encode nonstandard copy-protected files. Also used for ZX81 files.

Symbol Definition Table entry format (for both Pilot/Sync and Data)
  00h 1       Symbol flags (b0-b1: starting symbol polarity)
               0: opposite to current level (make an edge, as usual) - default
               1: same as current level (no edge - prolongs previous pulse)
               2: force low level
               3: force high level
  01h 2*MAXP  Pulse lengths (16bit) (padded with 0000h if less than MAXP used)
The number of table entries is ASP or ASD. The size per entry is 1+2*MAXP, where MAXP is NPP or NPD.

Run-Length Encoded Pilot/Sync Stream format (PRLE)
  00h 1  Symbol to be output    (eg. a symbol that defines the Pilot pulse)
  01h 2  Number of repetitions  (eg. number of Pilot pulses)
The number of entries is TOTP, each entry is 3 bytes in size.

Data Stream format
Most commonly, 2 symbols are used, so the 1st symbol is in bit7 of 1st byte.
If 3 or 4 symbols are used, then the 1st symbol is in bit7-6 of 1st byte.

Example (Standard Spectrum Block)
  00h 0000xxxxh   ;Total block length (28h+LEN)
  04h 03E8h       ;Pause after this block (ms)
  06h 00000002h   ;Total number of pilot/sync symbols       TOTP;\
  0Ah 02h         ;Max pulses per symbol                    NPP ; pilot header
  0Bh 02h         ;Number of symbols in pilot/sync alphabet ASP ;/
  0Ch LEN*8       ;Total number of data symbols             TOTD;\
  10h 02h         ;Max pulses per data symbol               NPD ; data header
  11h 02h         ;Number of symbols in data alphabet       ASD ;/
  12h 00h,02168,00000      ;Pilot Symbol 0 (single pilot pulse)
  17h 00h,00667,00735      ;Pilot Symbol 1 (two sync pulses)
  1Ch 00h,08063            ;PRLE: take Pilot-Symbol 0, and repeat it 8063 times
  1Fh 01h,00001            ;PRLE: take Pilot-Symbol 1, and repeat it 1 times
  22h 00h,00855,00855      ;Data Symbol 0
  27h 00h,01710,01710      ;Data Symbol 1
  2Ch Data (LEN bytes: block_type, data[LEN-2], chksum)

Example (Standard ZX81 Block)
  00h 0000xxxxh   ;Total block length (58h+LEN)
  04h 03E8h       ;Pause after this block (ms)
  06h 00000000h   ;Total number of pilot/sync records       TOTP;\no pilot
  0Ah 00h         ;Max pulses per symbol                    NPP ; for zx81
  0Bh 00h         ;Number of symbols in pilot/sync alphabet ASP ;/
  0Ch LEN*8       ;Total number of data symbols             TOTD;\
  10h 12h         ;Max pulses per data symbol               NPD ; data header
  11h 02h         ;Number of symbols in data alphabet       ASD ;/
  12h 3,530,520,530,520,530,520,530                   ;\Data Symbol 0
        4689,0,0,0,0,0,0,0,0,0,0                      ;/
  37h 3,530,520,530,520,530,520,530                   ;\Data Symbol 1
        520,530,520,530,520,530,520,530,520,530,4689  ;/
  5Ch Data (LEN bytes: ZX81 filename, followed by memory [4009h..E_LINE-1])
Note: As usually, data symbol definitions are referred to spectrum-style 3.5MHz clock frequency (ie. not to ZX81-style 3.25MHz).

ZX80/ZX81 Filetype Detection
ZX80/ZX81 files SHOULD have a Hardware Info block (ID 33h) that identifies them as ZX80/ZX81 file. Unfortunately, in practice, they usually do NOT contain that information. So, to detect a ZX80/ZX81 file, one would need to check if the first data block has ID 19h, and if so, if it contains the above Symbols. To separate between ZX80 and ZX81 one may need to examine the presence of the ZX81 filename, and the values in the System Area.

ZX80/ZX81 Leading Silence
ZX80/ZX81 files are preceeded by leading silence (instead of a pilot tone). For the ZX80 this leading silence is REQUIRED (must be at least around 500ms). For ZX81, it can be shorter, and it is required only if the signal is preceeded by noise (if loading starts at the exact time when the 1st pulse is output, then the ZX81 BIOS doesn't need the silence).
The ID 19h block itself doesn't support leading silence, so ZX80 files MUST have it preceeded by Pause block (ID 20h) - one could almost expect that people will violate that rule, if so, software must automatically insert the leading pause.


 Spectrum Cassette TZX Format Custom

Custom Info Blocks...
Some of the most common uses of the Custom info block (ID 35h) have become standardized in the past revisions, although now they are deprecated:

POKEs block (v1.10..v1.13 only)
The purpose of this custom block is to hold any amount of different trainers for the game.
  00h     16 Custom block ID ("POKEs" + 11 spaces)
  10h     4  Length of data that follow
  14h     1  General description length (L)
  15h     L  General description in ASCII format
  15h+L   1  Number of trainer definitions (N)
  15h+L+1 .. Trainer definitions
Each trainer can have its own description and any number of POKEs.
TRAINER structure format
  00h     1  Trainer description length (L)
  01h     L  Trainer description in ASCII format
  01h+L   1  Number of poke definitions in this trainer (N)
  01h+L+1 .. POKEs definitions
For each POKE entry you can supply the memory page number and/or the original value of the address (if you want to restore it some way through the game). Normally you would enter these pokes with the help of some freezer-type tool like Multiface, but hopefully in the future the emulators will support this block directly, in which case you could use the 'User inserts the POKE value' feature. You can specify the point at which to insert the POKEs in the 'General description' field.
NOTE: All ASCII Descriptions can use more than one line. Please use only up to 30 characters per line and separate the lines by one CR (13 decimal).
POKE structure format
  00h 1 POKE type:
         bit 0-2 : memory page number
         bit 3   : ignore memory page number
         bit 4   : user inserts the POKE value
         bit 5   : unknown original value
  01h 2 POKE address
  03h 1 POKE value (leave 0 if 'user inserts' bit set)
  04h 1 POKE original value (leave 0 if 'unknown' bit set)

Instructions block (v1.10..v1.13 only)
This block can hold any general .TXT file, with the main purpose of storing the instructions to the program or game that is in the tape.
To ensure consistency with all other ASCII texts in this format please use a single CR character (13 decimal, 0Dh) to separate lines; also please use only up to 80 characters per line.
  00h 16 Custom block ID ("Instructions" + 4 spaces)
  10h 4  Length of the following data (L)
  14h L  Instructions text in ASCII format

Spectrum screen block (v1.10..v1.13 only)
If the game on the tape is not an original and lacks the original loading screen then you can supply it separately within this block. This is also very handy when you want the loading screen stored separately because the original is either encrypted (like with the 'Speedlock' or 'Alkatraz' loaders) or it is corrupted by some on-screen info (like the 'Bleepload' loader). Of course not only loading screens can be stored here... you can use it to store maps or any other picture that is in Spectrum Video format (that's why the Description is there for), but because the Loading Screen will be the most common you can just set the description length field to 0 when you use it for that. Also the border colour can be specified.
  00h     16   Custom block ID ("Spectrum Screen" + 1 space)
  10h     4    Length of data that follow
  14h     1    Description length (L) (if 0 then handle it as 'Loading Screen')
  15h     L    Description of the picture in ASCII format
  15h+L   1    BORDER Colour in Spectrum colour format (0=black, 1=blue, ...)
  15h+L+1 6912 Screen in standard Spectrum video format

ZX-Edit document block (v1.12..v1.13 only)
This block can hold files created with the new utility called ZX-Editor. This utility gives documents the look and feel of ZX-Spectrum and its documents can contain text, graphics (with Spectrum attributes), different type faces, colours, etc. Normally these files use extension .ZED. Also the description is added, in case you want to use it for something else than 'Instructions' - you can use it for MAPs, etc.
  00h   16 Custom block ID ("ZX-Edit document")
  10h   4  Length of data that follow
  14h   1  Description length (L) (if 0 then handle it as 'Instructions')
  15h   L  Description of the document in ASCII format
  15h+L .. The ZX-Editor document (.ZED file)

Picture block (v1.12..v1.13 only)
Finally you can include any picture (in supported formats) in the TZX file too. So cover pictures, maps, etc. can now be included in full colour (or whatever the formats supports). The best way for utilities to use this block is to spawn an external viewer, or the authors can write their own viewers (yeah, right ;) ). For inlay cards and other pictures that have zillions of colours use the JPEG format, for more simple pictures (drawing, maps, etc.) use the GIF format.
  00h   16 Custom block ID ("Picture" + 9 spaces)
  10h   4  Length of data that follow
  14h   1  Picture format (00h=GIF, 1=JPEG)
  15h   1  Description length (L) (if 0 then handle it as 'Inlay Card')
  16h   L  Description of the document in ASCII format
  16h+L .. The picture itself


 Spectrum Cassette TZX Notes

Format revision: v1.20, 19 Dec 2006

Introduction
TZX is a file format designed to preserve all (hopefully) of the tapes with turbo or custom loading routines. Even though some of the newer and 'smarter' emulators can find most of the info about the loader from the code itself, this isn't possible if you want to save the file to tape, or to a real Spectrum. And with all this information in the file, the emulators don't have to bother with finding out the timings and other things.
This file format is explicitly targeted to the ZX Spectrum compatible computers only. Specialized versions of the TZX format have been defined for other machines too, e.g. the Amstrad CPC and C64, but they are now available as distinct file formats with other filename extensions.
At the end of this document you can find a description of encoding differences between these machines and a table which displays timings used by their respective BIOS loading routines (some of them are not official). If you know of any other machines that have similar encoding that could be represented with this file format then let us know.

If you're looking for TZX files, you can find an extensive collection at Martijn van der Heide's 'World of Spectrum'.

The format was first started off by Tomaz Kac who was maintainer until revision 1.13, and then passed to Martijn v.d. Heide. After that, Ramsoft were the maintainers for a brief period during which the v1.20 revision was put together. If you have any questions about the format, visit the forums at World of Spectrum and ask.

The default format file extension is "TZX" and hopefully this won't have to change in the future; for RISC OS, the current TAP file type will be used. Amstrad CPC files should use the extension "CDT" to distinguish them from the ZX Spectrum files, otherwise the inner structure is totally the same.

Rules and definitions
  - All values are stored in little endian format (i.e. LSB first)
  - All unused bits should be set to zero
  - All timings are given in 3.5MHz clock cycles (unless otherwise stated)
  - All ASCII texts use the ISO 8859-1 (Latin 1) encoding
  - Some ASCII texts can have several lines, to be separated by chr(0Dh)
  - MSB = most significant byte, LSB = least significant byte
  - MSb = most significant bit, LSb = least significant bit
  - The values in curly brackets {} are the default values as used by BIOS

High and low levels
This document refers to 'high' and 'low' pulse levels. Whether this is implemented as ear=1 and ear=0 respectively or the other way around is not important, as long as it is done consistently.
Zeros and ones in 'Direct recording' blocks mean low and high pulse levels respectively. The 'current pulse level' after playing a Direct Recording block of CSW recording block is the last level played.
The 'current pulse level' after playing the blocks ID 10h,11h,12h,13h,14h or 19h is the opposite of the last pulse level played, so that a subsequent pulse will produce an edge.
An emulator should put the 'current pulse level' to 'low' when starting to play a TZX file, either from the start or from a certain position. The writer of a TZX file should ensure that the 'current pulse level' is well-defined in every sequence of blocks where this is important, i.e. in any sequence that includes a 'Direct recording' block, or that depends on edges generated by 'Pause' blocks. The recommended way of doing this is to include a Pause after each sequence of blocks.

Silence (pause, stop, end of tzx file)
Some blocks allow to define a pause of N milliseconds (ms) duration. If the old level was low, then the first 1 ms of that pause should be high (required to terminate the last pulse), and the remaining N-1 ms should be low. If the old level was high, then the full N ms should be low.
The same requirement applies also when stopping the tape (on stop command, or at end of tzx file): if the old level was low, then output 1ms high before stopping the motor (required because some files do not include a pause after the last data block which would imply the 1ms thing)
Pause of 0 ms duration is completely ignored, so the 'current pulse level' will NOT change in this case. If there is no pause between two data blocks then the second one should follow immediately; not even so much as one T state between them.

Machine specific information
Currently supported machines are: ZX Spectrum, Amstrad CPC, SAM Coupe, ZX-81 Jupiter ACE and Enterprise. Only ZX-81 (or the old Timex 1000) and Jupiter ACE use different tape encoding than the others.

Jupiter ACE encoding
See a dedicated site.

ZX Spectrum, Amstrad CPC, SAM Coupe & Enterprise tape encoding
These computers share the same tape encoding, which is normal frequency type encoding. Each bit is represented by one 'wave' of different duration for bit 0 and bit 1. Normally bit 1 is twice as big as bit 0. In the blocks, the timings are presented with Z80 T-states (cycles) per pulse. One wave is made from two pulses.
The standard data blocks have always the same structure: first there is the PILOT tone, then the two SYNC pulses (one wave) and after that the actual DATA (which can include a FLAG byte at the beginning and a CHECKSUM byte at the end).
Here is a table showing the pulse timings for each part of the standard BIOS load/save routine for each of these machines. If you have more accurate information then we would be glad to include it here. Enterprise has two loading speeds so timings for both are included.
  Machine           Pilot pulse Length Sync1  Sync2   Bit 0  Bit 1
  ZX Spectrum       2168        (1)    667    735     855    1710
  SAM Coupe'        58+19*W     6000   58+9*W 113+9*W 10+8*W 42+15*W (*)
  Amstrad CPC       Bit 1       4096   Bit 0  Bit 0   (2)    (2)
  Enterprise (fast) 742         ?      1280   1280    882    602 (**)
  Enterprise (slow) 1750        ?      2800   2800    1982   1400 (**)
Notes:
  (1)  The Spectrum uses different pilot lengths for header and data blocks.
       Header blocks have 8063 and data blocks have 3223 pilot pulses.
  (2)  Amstrad CPC BIOS load/save routine can use variable speed for loading,
       so the bit 1 pulse must be read from the pilot tone and bit 0 can be
       read from the sync pulses, and is always half the size of bit 1. The
       checksum is also different than the other two machines. The speed can
       vary from 1000 to 2000 baud.
  (*)  The SAM Coupe' timings can be user selected by a system variable. The
       standard value is 112, which is VERY close to ZX Spectrum loading
       speed, and therefore the 'Standard Speed Data' block can be used for
       those blocks. However if this system variable is changed then the
       timings will change accordingly. In the above table the value of this
       variable is given as 'W', the 'H' value is 'W/2'. Of course the best
       way to determine it is to calculate it from the timings you get when
       sampling the Pilot tone. The values in the table could be by a
       fraction of the real ones, but it should not matter. Note: All timings
       are written in 3.5MHz clock. Also, there might be some junk bits
       (usually 7 or 8) AFTER the checksum (XOR) byte at the end of the
       block, but they can just be ignored.
  (**) The Enterprise stores data in a different way than all other computers
       do. It stores the LSb first, but the data blocks require the data to
       be MSb first. This might lead to some confusion, but if the same
       mechanism is used to replay data for all machines then there will be
       no problem. Just store the data as MSb first, but if you want to view
       the raw data in the TZX file then you will have to mirror each byte to
       get the correct values.
IMPORTANT: The ZXTape format has ALL timings written according to a 3.5MHz clock (the standard Spectrum 16/48K clock), so if the tape should be used on an Amstrad CPC with 4MHz clock then you should multiply ALL timings with 4/3.5 when you use that tape (this should be trivial). Similarly goes for SAM Coupe, that uses a 6MHz clock and Jupiter ACE that uses 3.2448 MHz and Enterprise that uses 4 MHz!


 Spectrum Interface 1 (Microdrive, Network, RS232)

Sinclair Interface 1 Features
Allows to connect up to 8 Microdrives (endless tape drives), allows to set up a network with up to 64 computers, allows to connect a RS232 printer.

Memory Mapped Ports
  0008h,1708h - Interface 1 - Enable external ROM (8K) (error and close#)
  0700h       - Interface 1 - Disable external ROM (8K) (ret)

Port E7h (R/W)
  0-7 Microdrive Data (send/receive)
Accessing this port will HALT the Z80 until the Interface I has collected 8 bits from the microdrive head; therefore, if the microdrive motor isn't running, or there is no formatted cartridge in the microdrive, the Spectrum hangs. This is the famous 'IN 0 crash'.

Port EFh (R)
  0   Microdrive Write Protected
  1   Microdrive Sync
  2   Microdrive Gap
  3   RS232 DTR
  4   Busy (what busy?)
  5-7 Not used

Port EFh (W)
  0   Microdrive Comms Data (and Mode for Write to PortF7.Bit0)
  1   Microdrive Comms Clk
  2   Microdrive R/W
  3   Microdrive Erase
  4   RS232 CTS
  5   Network Wait (used to synchronize)
  6-7 Not used

Port F7h (R)
  0   Network Input
  1-6 Not used
  7   RS232 TXDATA

Port F7h (W)
  0   Network Output or RS232 RXDATA (depending on Port EFh.Bit0.Write)
  1-7 Not used
If the microdrive is not being used, the COMMS DATA output selects the function of bit0 of out-port F7h.


 Spectrum Disc Spectrum +3 Disc Controller (NEC uPD765)

The Spectrum +3 uses Plus3DOS (which is based on AMSDOS for Amstrad CPC), like the CPC it's using a NEC uPD765 floppy disk controller.

Port 1FFDh - Spectrum +2A/+3 Memory Control B and Disk Motor (W)
Port 2FFDh - Spectrum +3 Floppy FDC NEC uPD765 status (R)
Port 3FFDh - Spectrum +3 Floppy FDC NEC uPD765 data (R/W)

Accessing the FDC 765
The Data Register (Port 3FFDh) is used to write Commands and Parameters, to read/write data bytes, and to receive result bytes. These three operations are called Command-, Execution-, and Result-Phase. The Main Status Register signalizes when the FDC is ready to send/receive the next byte through the Data Register.

Command Phase
A command consists of a command byte (eventually including the MF, MK, SK bits), and up to eight parameter bytes.

Execution Phase
During this phase, the actual data is transferred (if any). Usually that are the data bytes for the read/written sector(s), except for the Format Track Command, in that case four bytes for each sector are transferred.

Result Phase
Returns up to seven result bytes (depending on the command) that are containing status information. The Recalibrate and Seek Track commands do not return result bytes directly, instead the program must wait until the Main Status Register signalizes that the command has been completed, and then it must (!) send a Sense Interrupt State command to 'terminate' the Seek/Recalibrate command.

FDC Command Table
 Command     Parameters              Exm Result               Description
 02+MF+SK    HU TR HD ?? SZ NM GP SL <R> S0 S1 S2 TR HD NM SZ read track
 03          XX YY                    -                       specify spd/dma
 04          HU                       -  S3                   sense drive state
 05+MT+MF    HU TR HD SC SZ LS GP SL <W> S0 S1 S2 TR HD LS SZ write sector(s)
 06+MT+MF+SK HU TR HD SC SZ LS GP SL <R> S0 S1 S2 TR HD LS SZ read sector(s)
 07          HU                       -                       recalib.seek TP=0
 08          -                        -  S0 TP                sense int.state
 09+MT+MF    HU TR HD SC SZ LS GP SL <W> S0 S1 S2 TR HD LS SZ wr deleted sec(s)
 0A+MF       HU                       -  S0 S1 S2 TR HD LS SZ read ID
 0C+MT+MF+SK HU TR HD SC SZ LS GP SL <R> S0 S1 S2 TR HD LS SZ rd deleted sec(s)
 0D+MF       HU SZ NM GP FB          <W> S0 S1 S2 TR HD LS SZ format track
 0F          HU TP                    -                       seek track n
 11+MT+MF+SK HU TR HD SC SZ LS GP SL <W> S0 S1 S2 TR HD LS SZ scan equal
 19+MT+MF+SK HU TR HD SC SZ LS GP SL <W> S0 S1 S2 TR HD LS SZ scan low or equal
 1D+MT+MF+SK HU TR HD SC SZ LS GP SL <W> S0 S1 S2 TR HD LS SZ scan high or eq.

Parameter bits that can be specified in some Command Bytes are:
  MT  Bit7  Multi Track (continue multi-sector-function on other head)
  MF  Bit6  MFM-Mode-Bit (Default 1=Double Density)
  SK  Bit5  Skip-Bit (set if secs with deleted DAM shall be skipped)

Parameter/Result bytes are:
  HU  b0,1=Unit/Drive Number, b2=Physical Head Number, other bits zero
  TP  Physical Track Number
  TR  Track-ID (usually same value as TP)
  HD  Head-ID
  SC  First Sector-ID (sector you want to read)
  SZ  Sector Size (80h shl n) (default=02h for 200h bytes)
  LS  Last Sector-ID (should be same as SC when reading a single sector)
  GP  Gap (default=2Ah except command 0D: default=52h)
  SL  Sectorlen if SZ=0 (default=FFh)
  Sn  Status Register 0..3
  FB  Fillbyte (for the sector data areas) (default=E5h)
  NM  Number of Sectors (default=09h)
  XX  b0..3=headunload n*32ms (8" only), b4..7=steprate (16-n)*2ms
  YY  b0=DMA_disable, b1-7=headload n*4ms (8" only)

Format Track: output TR,HD,SC,SZ for each sector during execution phase
Read Track: reads NM sectors (starting with first sec past index hole)
Read ID: read ID bytes for current sec, repeated/undelayed read lists all IDs
Recalib: walks up to 77 tracks, 80tr-drives may need second recalib if failed
Seek/Recalib: All read/write commands will be disabled until succesful senseint
Senseint: Set's IC if unsuccesful (no int has occured) (until IC=0)

FDC Status Registers
The Main Status register can be always read through Port 2FFDh. The other four Status Registers cannot be read directly, instead they are returned through the data register as result bytes in response to specific commands.

Main Status Register (Port 2FFDh)
  b0..3  DB  FDD0..3 Busy (seek/recalib active, until succesful sense intstat)
  b4     CB  FDC Busy (still in command-, execution- or result-phase)
  b5     EXM Execution Mode (still in execution-phase, non_DMA_only)
  b6     DIO Data Input/Output (0=CPU->FDC, 1=FDC->CPU) (see b7)
  b7     RQM Request For Master (1=ready for next byte) (see b6 for direction)
Status Register 0
  b0,1   US  Unit Select (driveno during interrupt)
  b2     HD  Head Adress (head during interrupt)
  b3     NR  Not Ready (drive not ready or non-existing 2nd head selected)
  b4     EC  Equipment Check (drive failure or recalibrate failed (retry))
  b5     SE  Seek End (Set if seek-command completed)
  b6,7   IC  Interrupt Code (0=OK, 1=aborted:readfail/OK if EN, 2=unknown cmd
             or senseint with no int occured, 3=aborted:disc removed etc.)
Status Register 1
  b0     MA  Missing Adress Mark (Sector_ID or DAM not found)
  b1     NW  Not Writeable (tried to write/format disc with wprot_tab=on)
  b2     ND  No Data (Sector_ID not found, CRC fail in ID_field)
  b3,6   0   Not used
  b4     OR  Over Run (CPU too slow in execution-phase (ca. 26us/Byte))
  b5     DE  Data Error (CRC-fail in ID- or Data-Field)
  b7     EN  End of Track (set past most read/write commands) (see IC)
Status Register 2
  b0     MD  Missing Address Mark in Data Field (DAM not found)
  b1     BC  Bad Cylinder (read/programmed track-ID different and read-ID = FF)
  b2     SN  Scan Not Satisfied (no fitting sector found)
  b3     SH  Scan Equal Hit (equal)
  b4     WC  Wrong Cylinder (read/programmed track-ID different) (see b1)
  b5     DD  Data Error in Data Field (CRC-fail in data-field)
  b6     CM  Control Mark (read/scan command found sector with deleted DAM)
  b7     0   Not Used
Status Register 3
  b0,1   US  Unit Select (pin 28,29 of FDC)
  b2     HD  Head Address (pin 27 of FDC)
  b3     TS  Two Side (0=yes, 1=no (!))
  b4     T0  Track 0 (on track 0 we are)
  b5     RY  Ready (drive ready signal)
  b6     WP  Write Protected (write protected)
  b7     FT  Fault (if supported: 1=Drive failure)

Motor On/Off Flipflop
The motor is controlled via a bit in Port 1FFDh (see ULA chapter), the bit controls motors for ALL drive(s), it is not possible to turn on/off the motor of a specific drive separately.

Notes:
Before accessing a disk you should first Recalibrate the drive, that'll move the head backwards until it reaches Track 0 (that's required to initialize the FDCs track counter). On a 80 track drive you may need to repeat that in case that the first recalibration attempt wasn't successful (that's because the FDC stops searching after 77 steps).

Now if you want to format, read or write a sector on a specific track you must first Seek that track (command 0Fh). That'll move the read/write head to the physical track number. If you don't do that, then the FDC will attempt to read/write data to/from the current physical track, independendly of the specified logical Track-ID.

The Track-, Sector-, and Head-IDs are logical IDs only. These logical IDs are defined when formatting the disk, and aren't required to be identical to the physical Track, Sector, or Head numbers. However, when reading or writing a sector you must specify the same IDs that have been used during formatting.

Despite of the confusing name, a sector with a "Deleted Data Address Mark" (DAM) is not deleted. The DAM-flag is just another ID-bit, and (if that ID-bit is specified correctly in the command) it can be read/written like normal data sectors.

At the end of a successful read/write command, the program should send a Terminal Count (TC) signal to the FDC. However, in the CPC (and probably also in the Spectrum) the TC pin isn't connected to the I/O bus, making it impossible for the program to confirm a correct operation. For that reason, the FDC will assume that the command has failed, and it'll return both Bit 6 in Status Register 0 and Bit 7 in Status Register 1 set. The program should ignore this errormessage.

The CPC doesn't support floppy DMA transfers, and the FDCs Interrupt signal isn't used in the CPC also.

Usually single sided 40 Track 3" disk drives are used in CPCs, whereas 40 tracks is the official specification, practically 42 tracks could be used. The FDC controller can be used to control 80 tracks, and/or double sided drives also, even though AMSDOS isn't supporting such formats. AMSDOS is supporting a maximum of two disk drives only.


 Spectrum Disc Opus Discovery

MEM:0008h,0048h,1708h - Discovery - Enable external ROM/RAM/WD/PIA
MEM:1748h - Discovery - Disable external ROM/RAM/WD/PIA
Opcode fetches from these addresses enable/disable the Discovery hardware, when enabled, the following is mapped to first 16K of memory:
  0000-1FFF ROM (8K)
  2000-27FF RAM (2K)
  2800-2803 WD 1770 Ports (floppy controller)
  3000-3003 PIA 6821 Ports (drive/side select, centronics etc.)

DRQ (Data Request)
The DRQ output from WD 1770 is inverted and connected to /NMI input (unlike as most other drives which map DRQ to a status register bit, and which use /NMI as snapshot button).

MEM:2800h - FDC Command (W)
MEM:2800h - FDC Status (R)
MEM:2801h - FDC Track (R/W)
MEM:2802h - FDC Sector (R/W)
MEM:2803h - FDC Data (R/W)
Spectrum Disc Controller (Western Digital WD177x)

MEM:3000h when CRA.Bit2=1 - PIA Peripheral Interface A (PA0..7)
MEM:3000h when CRA.Bit2=0 - PIA Data Direction Reg. A (0=Input, 1=Output)
MEM:3001h - PIA Control Register A (CRA)
MEM:3002h when CRB.Bit2=1 - PIA Peripheral Interface B (PA0..7)
MEM:3002h when CRB.Bit2=0 - PIA Data Direction Reg. B (0=Input, 1=Output)
MEM:3003h - PIA Control Register B (CRB)
The PIA 6821 lines are connected as such:
  PA0    drive 2 select (and enable motors for both drives)
  PA1    drive 1 select (and enable motors for both drives)
  PA4    side select
  PA5    double density enable
  PA6    centronics busy (input)
  PA7    enable kempston joystick port on A5=0 (from BASIC: type FORMAT "j";1)
  PB0..7 centronics data
  CA2    centronics ack
  CB2    centronics strobe
The PA2,PA3,CA1,CB1,/IRQA,/IRQB pins are not used.

PIA Control Register A (CRA)
  0   CA1 Input Control (0..3)
        0 = Set IRQA1 when CA1=Negative, but do not trigger /IRQA
        1 = Set IRQA1 when CA1=Negative, and do trigger /IRQA
        2 = Set IRQA1 when CA1=Positive, but do not trigger /IRQA
        3 = Set IRQA1 when CA1=Positive, and do trigger /IRQA
  2   DDRA Access (0=Data Direction Register A, 1=Data Register A)
  3-5 CA2 Input/Output Control (0..7)
        0 = Set IRQA2 when CA2=Negative, but do not trigger /IRQA
        1 = Set IRQA2 when CA2=Negative, and do trigger /IRQA
        2 = Set IRQA2 when CA2=Positive, but do not trigger /IRQA
        3 = Set IRQA2 when CA2=Positive, and do trigger /IRQA
        4 = Handshake Output Mode (CA2=High on CA1, CA2=Low on "Read A Data")
        5 = Pulse Output Mode     (CA2=Low for 1 clk after "Read A Data")
        6 = Manual Output, set CA2=Low
        7 = Manual Output, set CA2=High
  6   IRQA2 Flag (CA2 input)
  7   IRQA1 Flag (CA1 input)

PIA Control Register B (CRB)
Same as CRA, but using Port B, CB1, CB2, IRQB, etc. And, CRB handshake/pulse modes are bound to "Write B Data" instead of "Read A Data" conditions.

IO:1Fh - Discovery Joystick Port (uses A5 only, eg. mirrored to DFh)
  0-4  Kempston Joystick (disabled by default)
  5-7  Not used
Unlike normal kempston joystick ports this port is disabled by default (presumably for best compatibility with other hardware that uses Port 1Fh, or mirrors like DFh, for something else). The port can be enabled via PA7 bit, or by typing FORMAT "j";1 in BASIC (type ;0 to disable it again).

PIA 6821 Pin-Outs
  1 VSS   6 PA4   11 PB1   16 PB6   21 R/W    26 D7   31 D2      36 RS1(A1)
  2 PA0   7 PA5   12 PB2   17 PB7   22 CS0    27 D6   32 D1      37 /IRQB
  3 PA1   8 PA6   13 PB3   18 CB1   23 /CS2   28 D5   33 D0      38 /IRQA
  4 PA2   9 PA7   14 PB4   19 CB2   24 CS1    29 D4   34 /RES    39 CA2
  5 PA3   10 PB0  15 PB5   20 VCC   25 PHI2   30 D3   35 RS0(A0) 40 CA1

DOS Replacement for Opus Discovery
The MegaSoft QuickDOS package consists of a disc, manual, and replacement ROM which contains faster load/save routines than the original Opus ROM.


 Spectrum Disc Disciple and Plus D

The Disciple and PlusD are both developed by Miles Gordon Technology, both contain 16K ROM, 8K RAM, a Snapshot/NMI button (works only when SHIFT held down?), a Western Digital disk controller, and a centronics port (although using different I/O addresses).
The older Disciple (1987) does additionally contain Joystick and Network ports, and a feature to swap ROM and RAM addresses, and a Inhibit button (to lock out the interface). The newer PlusD (1988) does not include these extras (so the "Plus D" is actually a "Disciple Minus Extras").

1Bh/E3h Disciple/PlusD - FDC Command (W)
1Bh/E3h Disciple/PlusD - FDC Status (R)
5Bh/EBh Disciple/PlusD - FDC Track (R/W)
9Bh/F3h Disciple/PlusD - FDC Sector (R/W)
DBh/FBh Disciple/PlusD - FDC Data (R/W)
Spectrum Disc Controller (Western Digital WD177x)

EFh PlusD only - PlusD Control Register (W)
  0    Drive A Select
  1    Drive B Select
  2    Single/double density
  3    ROM bank select (first/second 8K of total 16K ROM)
  4    Not used
  5    Ext. select (?)
  6    Centronics Strobe
  7    Side select

1Fh Disciple only - Disciple Control Register (W)
  0    Drive select
  1    Side select
  2    Single/double density
  3    ROM bank select (first/second 8K of total 16K ROM)
  4    Inhibit switch control
  5    Ext. select (?)
  6    Centronics Strobe
  7    Network Data Output (0=0V, 1=5V)

1Fh Disciple only - Disciple Joystick 2 aka Kempston, and Printer/Network (R)
  0-4  Kempston Joystick Port (also mapped to Port F7FEh)
  5    Not used
  6    Centronics Busy
  7    Network Data Input  (0=0V, 1=5V)

3Bh Disciple only - Network Wait when Network Input is 1=5V (R or W ?)
    Port 3Bh is used for network synchronization (same as bit 5 of Interface
    One's port EFh). Any OUT to port 3Bh will halt the Spectrum until the
    logic level on the network is 0. It is used to wait for the start bit
    of a transmission frame.
    The network bus carries TTL logic levels (0 = 0 Volts, 1 = 5 Volts).
    The bit rate is 87.5 Kbps and data is exchanged in packets of max 256
    bytes using a simple data-link level protocol.
    Network is an Interface 1 compatible net.

F7FEh Disciple only - Disciple Joystick 1 aka Sinclair Joystick 2 (R)
EFFEh Disciple only - Disciple Joystick 2 aka Sinclair Joystick 1 (R)
These are standard Sinclair joystick ports (although numbered in opposite order, otherwise same as in Interface 2 and Spectrum +2/+2A/+3). Disciple Joystick 1 aka Sinclair Joystick 2 is also mapped to Port 1Fh.

FBh/F7h Disciple/PlusD - Centronics Data (W)
  0-7  Printer Data

F7h PlusD Only - Centronics Busy (R)
  0-6  Not used
  7    Printer Busy

BBh/E7h Disciple/PlusD - Enable Internal ROM/RAM (R)
BBh/E7h Disciple/PlusD - Disable Internal ROM/RAM (W)
MEM:0000h,0008h,0066h,028Eh - Enable Internal ROM/RAM (R)
Reading/Writing from these Ports (or opcode fetches from MEM addresses) enables/disables the internal ROM/RAM (instead of the Spectrum BIOS).
In the PlusD, the ROM is always at 0000h-1FFFh, and RAM at 2000h-3FFFh. In the Disciple it is the same on reset, but can be changed via Port 7Bh.

7Bh Disciple only - Map ROM at 0000h, RAM at 2000h (R) (default on Boot)
7Bh Disciple only - Map ROM at 2000h, RAM at 0000h (W) (default in GDOS)
Allows to swap ROM and RAM locations.

DOS versions
  GDOS           -->  Built-in in Disciple
  G+DOS          -->  Built-in in PlusD
  Beta DOS       -->  Third-party DOS Replacement for PlusD
  UniDOS         -->  Third-party DOS Replacement for PlusD and Disciple
Beta DOS (from Beta Soft / Andy Wright) is an alternate DOS for the Plus D (from MGT), it doesn't have anything to do with the "Beta Disk Interface" (from Technology Research). Beta DOS boots from diskette (it doesn't seem to require a ROM replacement).
UniDOS is another DOS replacement that can be used on both Disciple and PlusD, it consists of a software diskette and a replacement ROM (there are two ROM versions, one for PlusD, and one for Disciple).


 Spectrum Disc Beta/BetaPlus/Beta128 Disk Interface (TRDOS)

The Beta (1984), Beta Plus (1985), and Beta 128 (1987) Disk Interfaces with TRDOS ROM were developed by Technology Research and may have been more or less popular in Europe.
In Russia, it became the standard disk interface (even more popular than cassettes), the Beta I/O addresses, and TRDOS ROM were used in Spectrum clones like Pentagon (1989) and Scorpion.

Versions
  Interface Year TRDOS Description
  Beta      1984 v1    original version
  Beta Plus 1985 v4    auto-boot, drive-detect, magic-button, seq/rnd access
  Beta 128  1986 v5.0x changed USR addresses, auto-boot-disable-switch
  Pentagon  1989 v5.0x russian spectrum clone with beta 128 disk interface

Features
The Interface is quite simple, it consists of the TRDOS ROM, the FDC chip, and eventually a Magic button and/or Reset button. It doesn't include RAM, nor any extras like Centronics or Joystick ports.
Since there's no built-in RAM, it allocates 70h bytes at 5CB6h (or at higher address if other hardware like Interface 1 has already allocated memory). Additionally, it does temporarily allocate a 100h bytes as sector buffer while accessing the disk.

MEM:3C00h..3CFFh - Enable ROM & I/O Ports (Beta/TRDOSv1 and BetaPlus/TRDOSv4)
MEM:3D00h..3DFFh - Enable ROM & I/O Ports (Beta128/TRDOSv5.0x)
The enable region was originally at 3Cxxh (which is FFh-filled in Spectrum 16K/48K/Plus BIOSes), and was invoked via RANDOMIZE USR 15360 and 15363 from BASIC. For compatibility with the Spectrum 128 (which contains BIOS code in that region), the region was moved to 3Dxxh (which contains only character set data, but no program code) and is now invoked via USR 15616 and 15619. The first address is used to enter TRDOS,
  RANDOMIZE USR 15360 or 15616  --> switch from BASIC to TRDOS ;3C00h/3D00h
  RETURN (aka Y-key)            --> switch from TRDOS to BASIC
The second address is used as prefix to normal BASIC commands, eg.:
  RANDOMIZE USR 15363 or 15619:REM:LOAD"filename"              ;3C03h/3D03h

MEM:4000h..FFFFh - Disable ROM & I/O Ports (all versions)
Opcode fetches outside of the ROM region (ie. at 4000h..FFFFh) do automatically disable the TRDOS ROM and I/O ports.

MEM:0000h? - Enable ROM & I/O Ports (BetaPlus/TRDOSv4 and Beta128/TRDOSv5.0x)
The Beta Plus and Beta 128 have an Auto-Boot feature that automatically enters TRDOS instead of BASIC on power-up. Unknown which opcode address activates TRDOS... probably 0000h.
The Auto-Boot feature works only on Spectrum 48K/Plus, but isn't compatible with the Spectrum 128 (the Beta 128 includes a "system switch" that allows to disable auto-boot for Spectrum 128 compatibility, ie. on the Spectrum 128 TRDOS can be started only manually via RANDOMIZE USR 15616).

MEM:0066h? - Enable ROM & I/O Ports (BetaPlus/TRDOSv4 and Beta128/TRDOSv5.0x)
The Beta Plus and Beta 128 have a Magic button, presumably triggering an /NMI and thus needing to enable ROM on address 0066h (?) although in poor quality schematics it looks more like /RESET than /NMI. The button does usually allow to save snapshots (except, on the Scorpion, it launches a Service-Monitor program).

I/O Port Activation
Below I/O ports are enabled only while the ROM is enabled, too. Ie. when disabled, there's no conflict with Kempston Joysticks (Port 1Fh) or with the VRAM Garbage effect (Port FFh). Mind that ROM and I/O ports are automatically disabled when accessing opcodes at 4000h..FFFFh (so the I/O ports can be accessed ONLY from inside of the TRDOS ROM).

1Fh - Beta Disk FDC Command (W)
1Fh - Beta Disk FDC Status (R)
3Fh - Beta Disk FDC Track (R/W)
5Fh - Beta Disk FDC Sector (R/W)
7Fh - Beta Disk FDC Data (R/W)
Spectrum Disc Controller (Western Digital WD177x)
According to some untitled schematics, the interface uses a FD1793-02 chip (which is maybe correct), according to Sinclair User magazine it uses a 1771 chip (such a thing probably doesn't exist at all), according to russian source it uses a KR1818VG93 aka VG93 chip (which is probably a 1793 clone).

FFh - Beta Disk Status (R)
  0-5 Unused?
  6   DRQ (data request)
  7   INTRQ (command completion)

FFh - Beta Disk Control (W?)
  0-1 Drive select (0..3 = Drive A..D)
  2   Hardware microcontroller reset (0=reset, 1=normal)
  3   Blocks signal HLT of microcontroller (=disk controller?) (1=normal)
  4   Head select (0=First/bottom, 1=Second/Top)
  5   Density select (0=FM, 1=MFM)
  6-7 Unused


 Spectrum Disc Controller (Western Digital WD177x)

Command description
Commands should only be loaded in the Command Register when the Busy status bit is off (Status bit 0). The one exception is the Force Interrupt command. Whenever a command is being executed, the Busy status bit is set.
At the completion of every command an INTRQ is generated. INTRQ is reset by either reading the status register or by loading the command register with a new command. In addition, INTRQ is generated if a Force Interrupt command condition is met.
When a command is completed, an interrupt is generated and the busy status bit is reset. The Status Register indicates whether the completed command encountered an error or was fault free.

Command Summary (models 1791, 1792, 1793, 1794) (and 1770,1772,1773)
  Type Command         b7  b6  b5  b4  b3   b2  b1   b0
  I    Restore         0   0   0   0   h/M  V   r1   r0
  I    Seek            0   0   0   1   h/M  V   r1   r0
  I    Step            0   0   1   T   h/M  V   r1   r0
  I    Step-In         0   1   0   T   h/M  V   r1   r0
  I    Step-Out        0   1   1   T   h/M  V   r1   r0
  II   Read Sector     1   0   0   m   S/M  E   C/0  0
  II   Write Sector    1   0   1   m   S/M  E   C/P  a0
  III  Read Address    1   1   0   0   0/M  E   0    0
  III  Read Track      1   1   1   0   0/M  E   0    0
  III  Write Track     1   1   1   1   0/M  E   0/P  0
  IV   Force Interrupt 1   1   0   1   i3   i2  i1   i0

Flag Summary
  r1,r0  Stepping Motor Rate (0..3 = 6ms,12ms,20ms,30ms) (WD179X,WD1770/WD1773)
  r1,r0  Stepping Motor Rate (0..3 = 2ms,3ms,5ms,6ms) (WD1772 Preliminary docs)
  r1,r0  Stepping Motor Rate (0..3 = 6ms,12ms,2ms,3ms) (WD1772 Other docs... ?)
  V      Track Number Verify Flag (0: no verify, 1: verify on dest track)
  h      Head Load Flag (1: load head at beginning, 0: unload head) (WD179X)
  T      Track Update Flag (0: no update, 1: update Track Register)
  a0     Data Address Mark (0: FB, 1: F8 (deleted DAM))
  C      Side Compare Flag (0: disable, 1: enable)  (WD179X only) (and WD1773?)
  E      15 ms delay (0: no 15ms delay, 1: 15 ms delay)
  S      Side Compare Flag (0: compare for side 0, 1: compare for side 1)
  m      Multiple Record Flag (0: single record, 1: multiple records)
  P      Write Precompensation (0=On, 1=Off) (WD1770/WD1772 only) (and WD1773?)
  M      Motor On (0=on, 1=off)              (WD1770/WD1772 only)
Interrupt Condition Flags
  i3-i0  0 = Terminate with no interrupt (INTRQ)
  i3     1 = Immediate interrupt, requires a reset
  i2     1 = Index pulse
  i1     1 = Ready to not ready transition (Not used on WD1770/WD1772)
  i0     1 = Not ready to ready transition (Not used on WD1770/WD1772)

Type I commands (Restore, Seek, Step-Out, Step-In, Step)
Used to move the read/write head. The stepping motor rate should be usally set to 6ms (r1 and r0 bits set to zero) for a 3.5-inch floppy disk drive. An optional verification of head position can be performed by setting bit 2 (V=1) in the command word.

When V=1: When the seek is completed, the drive automatically goes into read mode, the track number from the first encountered Sector ID Field is then compared against the contents of the Track Register (or Data Register?), if the track numbers compare (and the ID Field CRC is correct) the verify operation is complete and an INTRQ is generated with no errors. If these track numbers do not match, the Seek Error bit of the status register is set.

When V=0: When the seek is completed, the track position is not verified (this mode is required for unformatted disks). The command ends once the last step pulse is output. Since the result is that there is not enough time for step stability, the host system must use its software to make the floppy disk wait a certain period before reading or writing the track just arrived at.

When the seek command is complete, the interrupt request is set and at the same time, the Busy bit in the status register is set to 0. When the CPU reads the status register, it resets the interrupt request signal.

Restore (Seek Track 0)
If TR00 is not active, stepping pulses are issued until the TR00 input is activated. The Track Register is set to zero, and an interrupt is generated when track 0 has been reached.

Seek
This command assumes that the Track Register contains the <current> position of the head, and that the Data Register contains the <desired> destination track number. The FD179X will update the Track Register and issue stepping pulses in the appropriate direction until the contents of the Track and Data Register are equal to each other. An interrupt is generated at the completion of the command.

Step-Out, Step-In, Step
Issues one stepping pulse to the disk drive. Step-Out: towards track 0. Step-In: towards track 76 (that feature is not useful for 80-track drives). Step: same direction as for previous step command. The track register is updated (ie. incremented or decremented) only if the "T" bit has been set in the command word. An interrupt is generated at the end of each command.

Type II commands (Read Sector, Write Sector)
Prior to loading the Type II command into the Command Register, the computer must load the Sector Register with the desired sector number. Upon receipt of the Type II command, the busy status bit is set. The FD179X must find an ID field with a matching Track number and Sector number, otherwise the Record not found status bit is set and the command is terminated with an interrupt.
Each of the Type II commands contains an m flag which determines if multiple records (sectors) are to be read or written. If m=0, a single sector is read or written and an interrupt is generated at the completion of the command. If m=1, multiple records are read or written with the sector register internally updated so that an address verification can occur on the next record. The FD179X will continue to read or write multiple records and update the sector register in numerical ascending sequence until the sector register exceeds the number of sectors on the track or until the Force Interrupt command is loaded into the Command Register.
The Type II commands for 1791-94 also contain side select compare flags. When C=0 (bit 1), no comparison is made. When C=1, the LSB of the side number is read off the ID Field of the disk and compared with the contents of the S flag.

Read Sector
Upon receipt of the command, the head is loaded, the busy status bit set and when an ID field is encountered that has the correct track number, correct sector number, correct side number, and correct CRC, the data field is presented to the computer.
An DRQ is generated each time when the CPU must read a byte from the data register, the Lost Data bit is set if the CPU didn't read data in time, and the Read operation continues until the end of sector is reached.
At the end of the Read operation, the type of Data Address Mark encountered in the data field is recorded in the Status Register (bit 5).

Write Sector
Upon receipt of the command, the head is loaded, the busy status bit set and when an ID field is encountered that has the correct track number, correct sector number, correct side number, and correct CRC, a DRQ is generated.
The FD179X counts off 22 bytes (in double density) from the CRC field and the Write Gate output is made active if the DRQ is serviced (ie. the DR has been loaded by the computer). If DRQ has not been serviced, the command is terminated and the Lost Data status bit is set. If the DRQ has been serviced, 12 bytes of zeroes (in double density) are written to the disk, then the Data Address Mark as determined by the a0 field of the command.
The FD179X then writes the data field and generates DRQ's to the computer. If the DRQ is not serviced in time for continuous writing the Lost Data Status bit is set and a byte of zeroes is written on the disk and the command continues until the last byte of the sector is reached.
After the last data byte has been written on the disk, the two-byte CRC is computed internally and written on the disk followed by one byte of logic ones.

Type III commands

Read Address
Upon receipt of the Read Address command, the head is loaded and the Busy Status bit is set. The next encountered ID field is then read in from the disk, and the six data bytes of the ID field are assembled and transferred to the DR, and a DRQ is generated for each byte. The six bytes of the ID field are : Track address, Side number, Sector address, Sector Length, CRC1, CRC2. Although the CRC bytes are transferred to the computer, the FD179X checks for validity and the CRC error status bit is set if there is a CRC error. The track address of the ID field is written into the sector register so that a comparison can be made by the user. At the end of the operation, an interrupt is generated and the Busy status bit is reset.

Read Track
Upon receipt of the Read Track command, the head is loaded, and the busy status bit is set. Reading starts with the leading edge of the first encountered index pulse and continues until the next index pulse. All gap, header, and data bytes are assembled and transferred to the data register and DRQ's are generated for each byte. The accumulation of bytes is synchronized to each address mark encountered. An interrupt is generated at the completion of the command. The ID Address Mark, ID field, ID CRC bytes, DAM, Data and Data CRC bytes for each sector will be correct. The gap bytes may be read incorrectly during write-splice time because of synchronization.

Write Track (formatting a track)
Upon receipt of the Write Track command, the head is loaded and the Busy Status bit is set. Writing starts with the leading edge of the first encountered index pulse and continues until the next index pulse, at which time the interrupt is activated. The Data Request is activated immediately upon receiving the command, but writing will not start until after the first byte has been loaded into the DR. If the DR has not been loaded by the time the index pulse is encountered, the operation is terminated making the device Not Busy, the Lost Data status bit is set, and the interrupt is activated. If a byte is not present in the DR when needed, a byte of zeroes is substituted. This sequence continues from one index mark to the next index mark.
Normally, whatever data pattern appears in the data register is written on the disk with a normal clock pattern. However, if the FD179X detects a data pattern of F5 thru FE in the data register, this is interpreted as data address marks with missing clocks or CRC generation. The CRC generator is initialized when an F5 data byte is about to be transferred (in MFM). An F7 pattern will generate two CRC bytes. As a consequence, the patterns F5 thru FE must not appear in the gaps, data fiels, or ID fiels. Tracks may be formatted with sector lengths of 128, 256, 512 or 1024 bytes. See "Formatting" below for more info and example.

Type IV command (Force Interrupt)
The Forced Interrupt command is generally used to terminate a multiple sector read or write command or insure Type I status register. This command can be loaded into the command register at any time. If there is a current command under execution (busy status bit set), the command will be terminated and the busy status bit reset.

Status Register
Upon receipt of any command, except the Force Interrupt command, the Busy Status bit is set and the rest of the status bits are updated or cleared for the new command.
The user has the option of reading the status register through program control or using the DRQ line with DMA or interrupt methods. When the DR is read the DRQ bit in the Status register and the DRQ line are automatically reset. A write to the DR also causes both DRQ's to reset. The busy bit in the status may be monitored with a user program to determine when a command is complete, in lieu of using the INTRQ line. When using the INTRQ, a busy status check is not recommended because a read of the status register to determine the condition of busy will reset the INTRQ line.

Status for Type I commands
  Bit Expl.
  0   Busy         (1=Command is in progress)
  1   Index        (1=Index mark detected from drive)
  2   Track 0      (1=Read/Write head is positioned to Track 0)
  3   CRC Error    (1=CRC encountered in ID field)
  4   Seek Error   (1=Desired track was not verified)  (reset 0 when updated)
  5   Head Loaded  (1=Head loaded an engaged) (WD1770/WD1772: Spin-up ready)
  6   Protected    (1=Disk write protected)
  7   Not Ready    (1=Drive not ready)        (WD1770/WD1772: Motor On)

Status for type II & III commands
  Bit Expl.
  0   Busy         (1=Command is under execution)
  1   Data Request (1=CPU must read/write next data byte) (DRQ)
  2   Lost Data    (1=CPU did not respond to DRQ in one byte time)
  3-4 Error Code   (1=Bad Data CRC, 2=Sector not found, 3=Bad ID Field CRC)
  5   Fault/Type   (Any Write:1=Write Fault, Read Sector:1=Deleted Data Mark)
  6   Protected    (Any Write:1=Write Protect, Any Read:Not used)
  7   Not Ready    (1=Drive not ready)        (WD1770/WD1772: Motor On)
Notes: Bit 1-6 are reset when updated. Bit 3-4 not used for read/write track.

Status for type IV command
If the Force Interrupt command is received while a command is under execution, the Busy status bit is reset and the rest of the status bits are unchanged.
If the Force Interrupt command is received when there is no command under execution, the Busy Status bit is reset and the rest of the status bits are updated or cleared. In this case, Status reflects the Type I commands.

External Circuit
The floppy disk controller itself cannot select the drive number, side, disk density, and it cannot turn on/off the drive motor(s). These settings must be set up by an external circuit, which are (as far as they aren't set to a fixed setting) controlled through separate I/O addresses. For details have a look at the MSX FDC I/O addresses.

Note that the FDC contains only one track register which is used for all drives. The current track position for each drive should be backed up in memory, and the track register should be updated each time when changing the current drive number.

The external circuit might also use the INTRQ and DRQ lines to handle FDC operations by DMA transfers, and/or to produce interrupts upon completion. In the MSX these methods are not used, however, most MSX adapters allow to read out the state of INTRQ and DRQ from a custom I/O address (this isn't actually required because INTRQ is just a inverted copy of the Status Busy bit, and DRQ can be read out from the normal Status register either.

Formatting
This table shows DATA PATTERNs and their FD179X interpretation in MFM.
  00-F4   Write 00 thru F4
  F5      Write A1, preset CRC
  F6      Write C2
  F7      Generate 2 CRC bytes
  F8-FF   Write F8 thru FF

Formatting Example
The example below shows the data stream that must be presented to the Write Track command for the "IBM system 34 format" (256 bytes/sector), note that the MSX usually uses 512 bytes/sector. The left values in the tables below identify the write-repeat count (in decimal) for the values to the right. First the Track Header must be written, followed by Sector ID and Sector Data Fields (for each sector). Finally 4E bytes must be written until the command has completed.

Track Header
  80 x 4E
  12 x 00
   3 x F6 (writes C2)
   1 x FC (index mark)
  50 x 4E
1st Sector ID Field
  12 x 00
   3 x F5 (writes A1, preset CRC)
   1 x FE (ID address mark)
   1 x Track number
   1 x Side number
   1 x Sector number
   1 x 01 (sector length=256)
   1 x F7 (write 2 CRC bytes)
  22 x 4E
1st Sector Data Field
  12 x 00
   3 x F5 (writes A1, preset CRC)
   1 x FB (data address mark)
 256 x DATA
   1 x F7 (write 2 CRC bytes)
  54 x 4E
2nd Sector ID Field (as above)
2nd Sector Data Field (as above)
  etc.
Track End (Fill unused bytes)
  .. x 4E (padding until index hole interrupt)


 Spectrum Disc Controller (Other)

Challenge Research Ltd: Challenge Sprint and Sprint Mk II
This is a quad-speed "audio" cassette drive, it connects to the expansion port, apparently spins faster than normal cassette drives, and works with existing cassette software (that was recorded at slow speed). The included BIOS replaces the normal cassette functions by faster functions, works only with software that uses the BIOS functions, eg. not with copyprotected games.

Evesham Micro Centre: Doubler Mk I / Double Mk II
External input for 2nd tape drive, for tape-to-tape transfers. The included software executes an endless loop that forwards input from the Double I/O port to the normal ULA cassette port.
  Version  Hardware          Software
  Mk I  :  Port BFh, Bit0    I.R.Lavell (quite efficently coded)
  Mk II :  Port 1Fh, Bit3    Draysoft (less efficiently coded)
The Mk II version is advertised to be more compatible with different tape decks.

Sinclair: Microdrive (endless tape drive)
Rotronics Wafadrive (endless tape drive) (not Microdrive compatible)
These are tape-drives, so, obviously, they don't use standard floppy disk controllers.
Unlike normal cassette recorders, these drives can seek files (or free space) automatically (without needing to press any play/stop/forward buttons), ie. they do have a disk-like filesystem, however, they must wind the tape until it reaches the desired location, so the seek-time is relative slow. Wafadrive cartridges are manufactured with 16K, 64K, 128K capacity (of which 16K has the shortest tape, and therefore fastest seek-time).

Crescent Quick Disk (2.8" disk, with single spiral-track, 128K or 256K)
Triton Quick Disk (2.8" disk, with single spiral-track, 100K)
Quick Disks (also used by Nintendo's Famicom Disk System) have only a single spiral-shaped track (like CDROMs and music records), so, obviously, they don't use standard floppy disk controllers.
Note: Quick Disk drives don't have a stepping motor for random access, instead the head is following the spiral windings in sequential access fashion (giving it a relative slow seek-time; similar to endless tape drives).

Timex FDD and FDD-3000
These drives are using standard a Western Digital controller, but, the controller is connected to a separate Z80 CPU. So the spectrum is communicating with that external Z80 CPU rather than directly with the WD chip.

Logitek: Disc Interface (connect a Commodore 1541 drive to spectrum)
An adaptor for communicating with Commodore 1541 drive (the standard disk drive for the C64) via serial data cable. The 1541 doesn't contain any standard disk controller (it consists of a 6502 CPU and a bunch of TTL logic). Although the drive uses standard 5.25" disks, the sector encoding isn't compatible with that used by Western Digital WD177x and NEC uPD765 disk controllers.

Spectrum CD Games Pack (Code Masters) (1989)
The pack consists of a Audio CD with about 30 spectrum games (each one recorded twice), and a connection cable (cd player's 3.5mm (or 6.3mm via adapter) stereo headphone socket to spectrum 9pin DSUB joystick port) (for Spectrum 128/+2/+3 or 48K with Kempston interface), and loading software on tape, once when a game is loaded one can press Q,U,I,T on keyboard to load a new game (without needing to load tape loader again).
No info if/how the stereo signal is used (maybe one channel used as CLOCK, and the other channel as DATA...?) The cable does reportedly contain a 1bit A/D converter, which is probably a single transistor or so (so it's mono, not stereo?). The CD capacity is said to be "12 Megs" (presumably meaning 12 Megabit = 60 tracks with 25Kbytes each).

Unknown Controllers
For most spectrum disk interfaces it is still unknown if they do contain a standard disk controller chip, and if so, which one (the WD177x chips seem to be quite popular in the spectrum world, so many of the unknown interfaces are probably using that chips, too).


 Spectrum Interrupts

The IRQ is triggered on each vsync.
With IM1 opcode, the interrupt handler is at 0038h in BIOS ROM.
With IM2 opcode, the interrupt handler is at [I*100h+databus].

IRQ (IM1)
The normal IM1 handler does solely execute in ROM (without redirecting to a RAM vector), so, since ROM is read-only, the IM1 vector cannot be hooked (unless by external hardware that replaces the BIOS by other data, as done in the Currah uSpeech).
To wait for an interrupt, one can use the HALT opcode, and/or check if the FRAMES system variable has changed.

IRQ (IM2)
This mode allows to use a custom IRQ handler at [I*100h+databus]. Because of the Display Snow bug, the table may not be at 4000h..7FFFh.
Normally [I*100h+databus] would be table in RAM at 8000h..FFFFh, however, some games have it in ROM: Spectrum 48K has [3B00h..3C00h]=FFh, and Spectrum 128 and up have [3BFFh..3C00h]=FFh filled, the handler is then at FFFFh, which would be usually set to 18h (a JR opcode) whose parameter byte is read from address 0000h in BIOS, which is F3h (a DI opcode), thus forming a JR FFF4h opcode.
Assuming that the databus contains a "random" number in range of 00h..FFh upon IRQs, it requires a table with 101h bytes at [I*100h+0..100h]. On the Spectrum 128 and up the databus seems to be stable FFh, needing only a 2 bytes at [I*100h+FFh..100h].

External IRQ
The /IRQ signal is available on the expansion port, eventually allowing external hardware to sense when vblanks occur, or allowing external hardware to inject additional IRQs (done by the AMX Mouse in IM2), though it's problematic to distingush external IRQs from normal vblank IRQs, especially if they occur simultaneously.

External NMI
The NMI handler is at 0066h in BIOS. On the Spectrum 16K/48K/+/128/+2, and on Timex TC2048/TC2068/TS2068, the BIOS handler does (accidently) do this:
  IF [5CB0h]=0000h then JMP [5CB0h]    ;ie. jump to 0000h (bullshit)
On the Spectrum +2A/+3, the bug was fixed, and it does:
  IF [5CB0h]<>0000h then JMP [5CB0h]
A couple of disk-interfaces are hooking address 0066h (or address 0000h, which can be easier decoded and which works too, due to the bug, except in the bug-fixed Spectrum +2A/+3 versions), the most common use is a Snapshot function, allowing to load/save the entire RAM to/from disk (useful because most spectrum programs support only cassette loading/saving). Some disk interfaces are using NMI to handle data requests (not so useful as that could be also done without NMIs).


 Spectrum Keyboards

Spectrum Keyboard Assignment

Keyboard Matrix
  Port        Bit4 Bit3 Bit2 Bit1 Bit0
  FEFEh A8    V    C    X    Z    SHIFT
  FDFEh A9    G    F    D    S    A
  FBFEh A10   T    R    E    W    Q
  F7FEh A11   5    4    3    2    1
  EFFEh A12   6    7    8    9    0
  DFFEh A13   Y    U    I    O    P
  BFFEh A14   H    J    K    L    ENTER
  7FFEh A15   B    N    M    SYM  SPACE

40-key Spectrum Keyboard (like ZX80/ZX81, but with SYM instead DOT)
  | 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   SYM   SPACE|

42-key TC2048/TS2068 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   SYM BRK SHF|
  |         [---------SPACE---------]         |
The two SHIFT keys are probably the same in the matrix, and BRK is probably just SHIFT+SPC or so, so the matrix has only 40 keys.

58-key Spectrum +/128/+2/+3 Keyboard, and 18-key Spectrum 128 Keypad
  |TRUE INV  1   2   3   4   5   6   7   8   9   0 ESC|     | /   *   (  ) |
  |DEL  TAB   Q   W   E   R   T   Y   U   I   O   P   |     | 7   8   9  - |
  |EXT  COPY   A   S   D   F   G   H   J   K   L   RET|     | 4   5   6  + |
  |SHIFT CAPS   Z   X   C   V   B   N   M   .    SHIFT|     | 1   2   3 RET|
  |SYM ;  "  LEFT RIGHT      SPACE      UP DOWN  , SYM|     |   0     .    |
In 48K mode: TAB=GRAPH, COPY=EDIT.
Concerning the keyboard matrix, the keyboard has only 40 real keys, plus two duplicated SHIFT/SYM keys, plus 16 keys with SHIFT/SYM+key combinations (eg. UP=SHIFT+7).
The external 18-key numeric keypad can be connected via a special serial cable, it was shipped only with early Spectrum 128 models in Spain, but soon discontinued.


 Spectrum Keyboard Assignment

Spectrum
  NORMAL  SHIFT  CMD    SYMBOL EXT      EXT+SYM   EXT+SHIFT GRAPH    GRAPH+SHFT
  1       EDIT   1      !      P=BLUE   DEF FN    I=BLUE    [ ']     [:.]
  2       CAPS   2      @      P=RED    FN        I=RED     [' ]     [.:]
  3       TRUE_V 3      #      P=MAGENT.LINE      I=MAGENT. ['']     [..]
  4       INV_V  4      $      P=GREEN  OPEN#     I=GREEN   [ .]     [:']
  5       LEFT   5      %      P=CYAN   CLOSE#    I=CYAN    [ :]     [: ]
  6       DOWN   6      &      P=YELLOW MOVE      I=YELLOW  ['.]     [.']
  7       UP     7      '      P=WHITE  ERASE     I=WHITE   [':]     [. ]
  8       RIGHT  8      (      (BRIGHT) POINT     FLASH=OFF [  ]     [::]
  9       GRAPH  9      )      (DARK)   CAT       FLASH=ON  GRA=OFF  GRA=OFF
  0       DELETE 0      _      P=BLACK  FORMAT    I=BLACK   DELETE   DELETE
  q       Q      PLOT   <=     SIN      ASN       ASN       USR.Q    USR.Q
  w       W      DRAW   <>     COS      ACS       ACS       INKEY$   INKEY$
  e       E      REM    >=     TAN      ATN       ATN       USR.E    USR.E
  r       R      RUN    <      INT      VERIFY    VERIFY    USR.R    USR.R
  t       T      RAND   >      RND      MERGE     MERGE     USR.T    USR.T
  y       Y      RETURN AND    STR$     [         [         FN       FN
  u       U      IF     OR     CHR$     ]         ]         USR.U    USR.U
  i       I      INPUT  AT     CODE     IN        IN        USR.I    USR.I
  o       O      POKE   ;      PEEK     OUT       OUT       USR.O    USR.O
  p       P      PRINT  "      TAB      RESET (C) RESET (C) USR.P    USR.P
  a       A      NEW    STOP   READ     FREE   ~  FREE   ~  USR.A    USR.A
  s       S      SAVE   NOT    RESTORE  STICK  |  STICK  |  USR.S    USR.S
  d       D      DIM    STEP   DATA     \      \  \      \  USR.D    USR.D
  f       F      FOR    TO     SGN      ON_ERR {  ON_ERR {  USR.F    USR.F
  g       G      GOTO   THEN   ABS      SOUND  }  SOUND  }  USR.G    USR.G
  h       H      GOSUB  ^      SQR      CIRCLE    CIRCLE    USR.H    USR.H
  j       J      LOAD   -      VAL      VAL$      VAL$      USR.J    USR.J
  k       K      LIST   +      LEN      SCREEN$   SCREEN$   USR.K    USR.K
  l       L      LET    =      USR      ATTR      ATTR      USR.L    USR.L
  ENTER   ENTER  ENTER  ENTER  ENTER    ENTER     ENTER     ENTER    ENTER
  SHIFT   SHIFT  SHIFT  EXT=ON SHIFT    EXT=OFF   SHIFT     SHIFT    SHIFT
  z       Z      COPY   :      LN       BEEP      BEEP      POINT    POINT
  x       X      CLEAR  pound  EXP      INK       INK       PI       PI
  c       C      CONT   ?      LPRINT   PAPER     PAPER     USR.C    USR.C
  v       V      CLS    /      LLIST    FLASH     FLASH     RND      RND
  b       B      BORDER *      BIN      BRIGHT    BRIGHT    USR.B    USR.B
  n       N      NEXT   ,      INKEY$   OVER      OVER      USR.N    USR.N
  m       M      PAUSE  .      PI       INVERSE   INVERSE   USR.M    USR.M
  SYMBOL  EXT=ON SYMBOL SYMBOL SYMBOL   SYMBOL    EXT=OFF   SHIFT    EXT=ON
  SPACE   SPACE  SPACE  SPACE  SPACE    SPACE     SPACE     SPACE    SPACE

Timex TS2068 and TC2068
The DELETE, RESET, FREE, STICK, ON_ERR, SOUND keywords exist on Timex TS2068 and TC2068 only. FREE/STICK are premanently mapped (not allowing to use "~" and "|" on these computers). The other four commands are mapped only when the [K] cursor is shown (to use the backspace function with [K] cursor: Keep Shift+1 held down for 2-3 seconds, the key-repeat does then act as backspace instead of repeatedly showing up the DELETE keyword).

Spectrum 128/+2/+2A/+3
On the Spectrum 128/+2/+2A/+3 all keywords must be entered letter-by-letter (ie. type P,L,O,T instead of pressing Q), letters and punctuation marks are kept at the same locations as in the above table, the EDIT key shows the options menu (which includes a RENUMBER function).


 Spectrum Joystick Ports

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___
  xx1F Kempston (and Timex TC2048)             ? ? ? F U D L R   (1=Pressed)
  xx7F Fuller Box                              F ? ? ? R L D U   (0=Pressed)
  EFFE Protek (cursor mode, A12, keys 6,7,8,0) - - - D U R - F   (0=Pressed)
  F7FE Protek (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)
  EFFE Sinclair Port 1 (keys 6,7,8,9,0)        - - - L R D U F   (0=Pressed)
  F7FE Sinclair Port 2 (keys 5,4,3,2,1)        - - - F U D R L   (0=Pressed)
  E7FE Sinclair Garbage or Nothing             - - - x x x x x   (0=Pressed)
  01F6 Timex TS2068/TC2068 PSG Reg 14, Port 1  F ? ? ? R L D U   (0=Pressed)
  02F6 Timex TS2068/TC2068 PSG Reg 14, Port 2  F ? ? ? R L D U   (0=Pressed)
  03F6 Timex TS2068/TC2068 PSG Reg 14, Both    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)
  ?    Rotronics Wafadrive Joystick Adaptor    ? ? ? ? ? ? ? ?   (?=Select)

Recommended Controls
When making Spectrum software, best support ALL of the following controls:
  6,7,8,9,0             --> Interprete as Sinclair Joystick Port 1
  SHIFT+5,6,7,8,SPACE   --> Interprete as Cursor Keys (for use in emulators)
  Q,A,O,P,SPACE         --> Interprete as Keyboard Keys
  Port 1Fh              --> Interprete as Kempston Joystick Port

Kempston Port Notes
This is a quite popular standard, and should be supported by most games. There are a lot of homebrew clones, including some released in magazines, many variants are decoding only A5=0, ignoring A6 and A7. Some variants are using the upper data bits (D5-D7) as centronics busy input, or as floppy interrupt or data requests.

Sinclair Port Notes
Sinclair ports are supported by the Interface 2 adaptor, by the Disciple disk interface, and are built-in in Spectrum +2/+3. The Sinclair joysticks are equivalent to keyboard keys 1,2,3,4,5 (port2) and 6,7,8,9,0 (port1), both keyboard groups are arranged as L,R,D,U,F (that keyboard arrangement is why the data bits are in opposite order for port1 and port2) (note: the arrangement does NOT comply with the cursor keys on keys 5,6,7,8). Reading both ports at once doesn't work well: Interface 2 doesn't respond to reads from Port E7FEh, Spectrum +2/+3 does respond to it (but, due to the reversed data bit arrangement, it returns rather useless garbage, eg. Up1 ANDed with Fire2).

Protek Port Notes
The original Protek supports only cursor mode, newer variants can be reportedly switched to three modes (probably cursor, kempston, and sinclair or so).
AGF and Thurnall support Cursor+Rubout, too. AGF additionally supports T-Y-U-I-P as second joystick. Thurnall additionally supports Q-A-Z-X for whatever usage.

Timex TS2068/TC2068 Port Notes
Timex Ports are using PSG register 14, which must be configured properly: Clear Bit6 of PSG Register 7 (input direction), write FFh or 8Fh or so to PSG register 14 (output HIGH levels), and set PSG index to 14. For details see PSG/sound specs.

Rotronics Wafadrive Joystick Adaptor
According to the Rotronics Wafadrive manual, one can connect a joystick to the centronics port of the Wafadrive via an adaptor. There's no info on how to build that adaptor, but the manual says it is sold separately (if that adaptor was actually ever produced, then it didn't became very popular). Because the Wafadrive centronics port is one-directonal, it is probably using the joystick switches in reversed direction (ie. outputting LOW levels to L,R,U,D,F via some of the Data lines, and reading the result from the common pin via the Busy input).

Joystick Port Pin-outs
The five switches are connected to Pin8 as a common pin, in the Atari standard that'd be GND, but in some variants it is a /SELECT signal that goes low when reading from the joystick port (that method works with joystick that consists of raw switches; but won't work with sticks that contain autofire functions or other logic that requires +5V/GND supply at Pin7/Pin8).
      Kempston Homebrew TS2068/TC2068 Interface2     Spectrum+2   Protek
  Pin Joy1     Joy1     Joy1  Joy2    Joy1  Joy2     Joy1  Joy2   Joy1
  1   Up       Up       Up    Up      Up    Up       N/A?  N/A?   Up
  2   Down     Down     Down  Down    Down  Down     MA3   MA4    Down
  3   Left     Left     Left  Left    Left  Left     N/A?  N/A?   Left
  4   Right    Right    Right Right   Right Right    Fire  Fire   Right
  5   N/A      N/A      N/A   N/A     N/A   N/A      Up    Up     ?
  6   Fire     Fire     Fire  Fire    Fire  Fire     Right Right  Fire
  7   +5V      (Fire 2) +5V   +5V     N/A   N/A      Left  Left   ?
  8   GND      +5V      /A8   /A9     GND   GND      MA3   MA4    ?
  9   N/A      N/A      GND   GND     N/A   N/A      Down  Down   ?
Amstrad's Spectrum +2 uses standard DSUB-9 ports, but with nonstandard pinouts (so it works only when re-wiring the connection on the mainboard, using an external adaptor, or buying an original "SJS1" joystick from Amstrad) (the MA3/MA4 lines are DRAM row/column address bits; essentially same as A11/A12 at time when reading the ports).


 Spectrum Mouse Ports

Kempston Mouse (hardware counters)
This is probably the most popular, best working, and most often supported mouse interface.
Spectrum Mouse - Kempston Mouse
The original package consists of a Kempston Mouse and Kempston Mouse Interface, there are also compatible interfaces that can be used with Amiga, PS/2, or USB mice, and maybe also some for RS232, Atari, or even analog mice.

AMX Mouse (IM2 interrupts)
This is almost as powerful as the Kempston Mouse, but conflicts with various other hardware expansions, the AMX Mouse is rarely supported by any programs.
Spectrum Mouse - AMX Mouse (Advanced Memory Systems)
The I/O addresses conflict with various other expansions like Kempston Joysticks (Port 1Fh), Beta Disk, Fuller Box, etc.

Amiga/Atari Mice (software counters)
Amiga/Atari mice can be connected directly to Kempston Joystick ports (which have +5V and GND), without needing any mouse interface. Although it's simple at hardware side, it's supported only by a dozen of programs.
Spectrum Mouse - Amiga Mouse or Atari Mouse in Kempston Joystick port
The problem is that the mickeys must be counted by software, meaning that the software must check Port 1Fh more than 500 times per second, ie. much more often than it could be done via the 50Hz vblank interrupt.

Other Mice
Some other less popular and/or less powerful solutions are:
Spectrum Mouse - Other Mice

Double Speed Threshold
Don't forget to implement Threshold in software, ie. if you have received more than so-and-so-many mickeys per frame, multiply that value by two.


 Spectrum Mouse - Kempston Mouse

FADFh - Kempston Mouse Buttons (R)
  0   Right Button  (0=Pressed, 1=Released)
  1   Left Button   (0=Pressed, 1=Released)
  2   Middle Button (not supported in original Kempston Mouse interface)
  3   Not used
  4-7 Mouse Wheel Counter (supported in some russian interfaces only)
The wheel "Counter is 4-bit and its value increases with the movement of wheels down, decrases the movement up." Whatever that means...? normally the wheel turns away from, or towards to user (where "away from user" should look UP-wards in a document; which means the screen scrolls DOWN-wards).

FBDFh - Kempston Mouse X (R)
FFDFh - Kempston Mouse Y (R)
  0-7 Counter (mickeys)
"8bit reverse counters" whatever that means... increasing or decreasing when moving towards where? The counters aren't reset after reading, and may wrap from FFh to 00h, or vice versa, when exceeding the 8bit range. To get mickeys since last read, subtract old/new value.

Notes
Usually deconds A5, A7, A8, A10, /RD, /IORQ, sometimes also /RD, A9, and/or A0. Uh, why A0...?
There is port conflict with incompletely implemented Kempston Joystick ports that decode only A5 (eg. Timex TC2048).


 Spectrum Mouse - AMX Mouse (Advanced Memory Systems)

1Fh - AMX Mouse - Z80PIO Data Reg A, Mouse X, Centronics LSBs/Strobe (R/W)
  0   Mouse X Direction (XB input from mouse) (?=Left, ?=Right)
  1-2 Not used
  3   Centronics Strobe    (should be usually output in Mode 3)
  4-7 Centronics Data LSBs (should be usually output in Mode 3)

3Fh - AMX Mouse - Z80PIO Data Reg B, Mouse Y, Centronics MSBs/Busy (R/W)
  0   Mouse Y Direction (YB input from mouse) (?=Up, ?=Down)
  1-2 Not used
  3   Centronics Busy       (should be usually input in Mode 3)
  4-7 Centronics Data MSBs  (should be usually output in Mode 3)

5Fh - AMX Mouse - Z80PIO Control Register A, Horizontal Control (W)
Used to configure Port A and Interrupt A by writing 1 or 2 byte(s) to this register, the meaning depends on the LSBs of the 1st byte:
  1stByte  2ndByte  Function
  vvvvvvv0 N/A      Set Interrupt Vector
  exxx0011 N/A      Set Interrupt Enable
  eah00111 N/A      Set Interrupt Enable/Control      (for Mode 3 only)
  eah10111 mmmmmmmm Set Interrupt Enable/Control/Mask (for Mode 3 only)
  00xx1111 N/A      Set Mode 0 (all 8 data bits = Output)
  01xx1111 N/A      Set Mode 1 (all 8 data bits = Input)
  10xx1111 N/A      Set Mode 2 (all 8 data bits = Bidirectional) (Port A only)
  11xx1111 dddddddd Set Mode 3 and Direction bits (0=Out, 1=In)
Whereas,
  vvvvvvv  Interrupt Vector (Bit0=0, Bit1-7=vvvvvvv, Bit8..15="I"-Register)
  e        Interrupt Enable (0=Disable, 1=Enable; trigger on each mickey)
  a        Interrupt AND/OR (for Mode3 only)
  h        Interrupt HI/LO  (for Mode3 only)
  mmmmmmmm Interrupt Mask   (for Mode3 only) (0=Monitor, 1=Mask)
  dddddddd Data Direction bits 0-7  (0=Output, 1=Input)

7Fh - AMX Mouse - Z80PIO Control Register B, Vertical Control (W)
Same as Control Register A (see there), but for Port B accordingly.

DFh - AMX Mouse - 74LSXX Logic, Mouse Buttons (R)
  0-4 Not used
  5   Right Button
  6   Middle Button
  7   Left Button
Buttons are handled via separate 74LSxx chips. Although, theoretically, they could be mapped to bit1-2 of the Z80PIO data ports; maybe the whole 8bit data ports are latched on interrupts; which would freeze the state of bit1-2.

Interrupts
The AMX mouse triggers an interrupt and outputs an IM2 interrupt vector on the databus on each mickey, the CPU should be in IM2, and the CPUs I-register should contain MSB of the interrupt table, the Z80 does then do am indirect "CALL [I*100h+databus]" on interrupts. Mind that trying to set I=40h..7Fh results in ULA Snow bug.
The two Z80PIO interrupt vectors should point to the address of X and Y motion handler; which should read the X or Y direction bit, and inrease/decrease the mickey counter accordingly. If both X and Y mickeys occor simultaneously, then the Z80PIO would first produce vector A, and then vector B (after vector A was processed).
The normal Vblank interrupt doesn't output anything on databus, so databus should be (hopefully) FFh, causing a "CALL [I*100h+FFh]" on vblank. If mickeys occur simulaneously with vblank, then the vblank interrupt is probably lost...? which would be bad for general animation synchronizations, keyboard handling, as well as for mouse specific threshold calculations.

Notes
The Z80PIO is reset on power-up (when raising VCC) only, but NOT on /RESET.
The AMX Mouse is supported by Minesweeper (new version - EN), and Artist II.


 Spectrum Mouse - Amiga Mouse or Atari Mouse in Kempston Joystick port

Pinouts
  Pin Amiga Mouse     Atari Mouse     Kempston Joystick  Kempston Mouse
  1   YA              XB              Joystick up        +5V
  2   XA              XA              Joystick down      XA
  3   YB              YA              Joystick left      YB
  4   XB              YB              Joystick right     Left Button
  5   Middle Button   Not connected   Not connected      Right Button
  6   Left Button     Left Button     Fire button        GND
  7   +5V             +5V             +5V                Not connected
  8   GND             GND             GND                YA
  9   Right Button    Right Button    Not connected      XB
As shown in 1st/2nd column, Amiga and Atari have XA/XB/YA/YB arranged differently. 3rd column shows how the signals are mapped on the joystick port. The 4th column is just for curiosity, it shows the connection between Kempston Mouse and Kempston Mouse Interface (aside from the different pinouts it's much the same as Amiga/Atari mice, the big difference is that the Kempston Mouse Interface contains hardware counters, without needing to count mickeys by software).

Motion Signals
The XA/XB indicate horizontal motion (and YA/YB vertical motion), like so:
      One Direction            Opposite Direction
  XA  _______----____          _____----______
  XB  _____----______          _______----____
The signal timings depend on how fast the mouse is moved (ie. if the mouse is not moved, then it may stop anywhere in the above diagram, eg. while XA and XB are both high, both low, or one high and one low).
The simpliest approach is to wait for raising edge on XA, and then to check XB (either XB is already high, or XB is still low) and increase/decrease the software's mickey counter accordingly. In situations where the direction changes, it may be also recommended to handle "half mickeys" on falling edges on XA, eg.:
      One Direction    Stop    Opposite Direction
  XA  _______---------------------------______
  XB  _____-------------------------------____
In the above case, checking only raising XA would miss the direction change on falling XA (if that happens repeatedly, then the mouse would disappear towards the screen border, eg. if the mouse is in an unstable position, without actually being moved).

Notes
Mind that the Kempston Joystick port inverts all signals (that is don't care for XA,XB,YA,XB, but the button bit may be opposite as expected). And, of course, a Kempston Joystick port does have only one Fire button (so only the Left mouse button can be read).
Games that support BOTH Amiga and Atari mouse: Godzilla, Ultra Reflect, Advanced Patience, Pyramid Patience.
Games that support ONLY Amiga mouse: Black Raven 1, Hexxagon, Indian Patience, Magic Field, Mezi Vezemi 128 (cz), Sparrows, and Art Studio.


 Spectrum Mouse - Other Mice

Datel Genius Mouse
Early Datel mice (April-March 1987) had 3 buttons, all later mice had only 2 buttons. Very late Datel mouse interfaces (1989 or so) additionally included a kempston joystick port. The interface (at least the latest version) contains only a single 28pin SMD chip, plus some resistors and capacitors.
The used I/O addresses are still unknown, however, the interface with the SMD chip connects to D0-D7, /IORQ, A5, A8-A10, +5V, and GND... so it might be simply a clone of the Kempston Mouse interface? Although, Kempston Mouse and Kempston Joystick both use A5=LOW, so, without checking A6/A7, it couldn't separate between them...?

Joystick Mouse (supports only fixed speed motion)
Some mice (Commodore Mouse 1350, Noris Data Mouse M1, Boeder GEOS-Mouse, Elite Mouse, Datalux SV-705) emulate a digital joystick. The advantage is that these mice can be used with any games that support joystick input (provided that the joystick port outputs +5V and GND pn Pin 7 and 8, ie. works only with standard Kempston Joystick ports, not with standard Sinclair Joystick ports). The big disadvantage that these mice can't separate between slow and fast motion.

RS232 Mouse on RS232 Port
Standard RS232 PC mice can be theoretically connected to Spectrum RS232 ports. In practice it's difficult at both hardware and software side, and so it's rarely supported. The first problem is that there are at least 16 Spectrum RS232 interfaces from different companies. Next, some Spectrum RS232 ports have non-standard pinouts (eg. Interface 1), simple Spectrum RS232 ports would require to watch the incoming bitstream permanently by software (eg. Interface 1). So, to execute any other program code, one would need a RS232 interface with built-in shift register and baudrate timer, and preferably also an interrupt notification, or, even more preferably a FIFO that can hold at least 3 characters (so the 1200 baud mouse data would need to be processed only on vblank interrupts).

AY-Mouse (C) V.M.G. (schematic in russian ZX Power digital magazine)
This is a (rather rarely supported) mouse interface. Similar to the Kempston Mouse interface it contains two hardware counters and button inputs, intended to be used with mice that have XA/XB/YA/YB signals (ie. any Amiga, Atari, AMX, or Kempston mice) (of course, needing to recurse the different pinouts of that mice).
The interface connects to PSG register 14, with the PSG being accessed via Port FFFDh and BFFDh, ie. the Spectrum 128 style PSG addresses, but with PSG register 14 being used to access the mouse (the real Spectrum 128 uses it as RS232/Keypad port). The PSG register 14 bits are:
  0-3 In  Horizontal or Vertical 4bit Counter (as selected via bit6)
  4   In  Button 1 (0=Pressed, 1=Released) (presumably Left)
  5   In  Button 2 (0=Pressed, 1=Released) (presumably Right)
  6   Out Select Counter to be mapped to bit0-3 (?=Horizontal, ?=Vertical)
  7   ?   Border ("for testing, and computer effects, type multicolor")
Changing bit6 (raising or falling edge?) does additionally reset the two 4bit counters to a value of 8 each, so, the difference since last access is new-8 (or 8-new), rather than new-old.
Supported by Art Mouse (Art Studio), ZX Power 1-4, Lemmings (TRDOS version).

Nidd Valley - Digimouse (digital)
Nidd Valley - Anamouse (analog)
Saga Systems - Star Mouse
Individual Software - Keyboard Mouse
Tropic Informatica - TPX Mouse (for brazilian TK90X and TK95)
No idea how these devices work? They don't seem to be very popular.


 Spectrum Light Guns

Stack Light Rifle (Stack Computer Services Ltd) (1983)
connects to expansion port, accessed by reading from Port DFh:
  bit1       = Trigger Button (0=Pressed, 1=Released)
  bit4       = Light Sensor   (0=Light, 1=No Light)
  other bits = Must be "1"    (the programs use compare FDh to test if bit1=0)
The vertical position is measured by counting time between vblank & light sensor signal; none of the games seems to attempt to measure the horizontal position; so there should be only one bright object per scanline).
The software timings are matched for PAL Spectrum 48K (and probably don't work on other/newer spectrums due to different BIOS (irq handler), video (clks per scanline), and cpu speed (waitstate) timings.

Magnum Light Phaser for Spectrum +2/+3 (Sinclair/Amstrad/Trojan) (1989)
Defender Light Gun for Spectrum 128/+2/+3 (Cheetah) (1989)
These two guns seem to be compatible with each other. Both are connected to AUX/Keypad (aka PSG Register 0Eh):
  bit4 = light sensor   (0=None, 1=Light)
  bit5 = trigger button (0=Pressed, 1=Released)
Although the Spectrum 128 does have an AUX/Keypad socket, the Magnum manual says that one should use the MIC-socket based gun on the Spectrum 128 (dunno why).

Magnum Light Phaser for Spectrum 48K (Sinclair/Amstrad/Trojan) (1989)
Requires 9V battery, connects to MIC-output socket (which is misused as input in this case). In the output direction, port FEh should be set to 00h (accordingly, the 48K games cannot output sound while reading the gun) (mind that CAS-IN, CAS-OUT, and SOUND-OUT are sharing the same ULA pin).
The trigger button is sensed as LONG pulse (Jungle Warfare reads 8Ch times, and accepts trigger only if [FEh].Bit6=ZERO on all 8Ch reads). The light sensor is sensed as EDGE (ie. 0-to-1 or 1-to-0 transitions on [FEh.Bit6]). Knowing that, it seems that the trigger button and light sensor signals are XORed with each other.

Defender Light Gun for Spectrum 48K (Cheetah) (1989)
Connects to Kempston Joystick interface. The Kempston Port 1Fh bits are:
  Joystick UP   (bit3) = Trigger button
  Joystick FIRE (bit4) = Light sensor
  Other   (bit0-2,5-7) = Must be 0 (Jungle W. compares [1Fh] with 08h,10h,18h)
Works only with Kempston interfaces (other joystick interfaces don't seem to be supported at software side, and, other interfaces usually don't provide the required +5V supply voltage at hardware side).

Gunstick for Spectrum 48K/+/128 (MHT Ingenieros)
Connects to joystick interface. Requires four 1.5V batteries (and so, doesn't require 5V/GND on joystick port).
  Joystick FIRE = Trigger Button (LOW=Pressed, HIGH=Released)
  Joystick DOWN = Light Sensor   (LOW=?)
Most games seem to support only Sinclair Joystick Interfaces (Port EFFEh or F7FEh), although the hardware should work with all joystick interfaces.

Gunstick for Spectrum +2/+3 (MHT Ingenieros)
Same as the 48K version, but matched to the (distorted) pin-outs of the bizarre Spectrum +2/+3 joystick ports.

Note on Defender "sliding autofire/function select switch"
Additionally to the trigger button, the Defender guns have a "sliding autofire/function select switch", reportedly used to select one-shot or machine-gun mode in some games, however, unknown how this switch is accessed by software (there seem to be only two data bits used; the light sensor and trigger button; so maybe it affects the length of the trigger button signal), also unknown if that switch acts as a push button, sticky switch, or even as potentiometer?

Existing Lightgun Games for the Spectrum
Stack Light Rifle Games:
  Grouse Shoot                Stack Computer Services Ltd (compiled BASIC code)
  Shooting Gallery            Stack Computer Services Ltd (compiled BASIC code)
  High Noon                   Stack Computer Services Ltd (compiled BASIC code)
  Invasion Force              Micromania (real Z80 assembler code)
Defender Light Gun Games:
  Advanced Pinball Simulator  Code Masters Ltd
  Billy the Kid               Code Masters Ltd
  Bronx Street Cop            Code Masters Ltd
  F-16 Fighting Falcon        Code Masters Ltd
  Jungle Warfare              Code Masters Ltd
  Super Car Trans Am          Code Masters Ltd
  And... theoretically the AUX-based Magnum games should work, too?
Magnum Light Phaser Games:
  Billy the Kid               Code Masters Ltd
  Bronx Street Cop            Code Masters Ltd
  Bullseye                    Macsen Software
  F-16 Fighting Falcon        Code Masters Ltd
  Jungle Warfare              Code Masters Ltd
  Living Daylights            Domark Ltd
  Lord Bromley's Estate       Amstrad
  Missile Ground Zero         Software Creations
  Operation Wolf              Ocean Software Ltd
  Q's Armoury                 Amstrad
  Robot Attack                Mastertronic Ltd
  Rookie                      Mastertronic Ltd
  Solar Invasion              Mastertronic Ltd
  Super Car Trans Am          Code Masters Ltd
Gunstick Games:
  Acid Killer                 New Frontier
  Bestial Warrior             Dinamic Software
  Cosmic Sheriff              Dinamic Software
  El Equipo A (aka A-Team)    Zafiro Software Division
  Guillermo Tell              Opera Soft S.A.
  Hunting Mix                 New Frontier
  Mike Gunner                 Dinamic Software
  Shooting Range: Mix I       New Frontier
  Solo                        Opera Soft S.A.
  Sootland                    Zafiro Software Division
  Space Smugglers             MHT Ingenieros S.L.
  Target Plus                 Dinamic Software
  Trigger                     Opera Soft S.A.
Note: In many cases there are two versions of each Magnum game: one hardcoded to work with MIC port, another one hardcoded to work with AUX port. The Code Masters titles do auto-detect if the Magnum (or Defender) is connected to MIC, AUX, or Kempston joystick port.


 Spectrum Printer Ports

Sinclair focused on selling their own 32-column printer (see ZX81 chapter), and (until the Spectrum +2A/+3 were released) didn't support regular centronics printers. The only official alternative to the 32-column thing was to use (rather rare) RS232 printers connected to Interface 1 (see Serial port chapter). Various other companies have released Centronics ports for the spectrum (often included in disk interfaces), but without using any standarized I/O addresses...

Centronics Ports
  Type                   Data       Busy          Strobe
  Interface Bis          #1D        (#1D).7       auto
  M'ody Technik 1986-12  #1F (inv)  (#9F).4=0     (#9F).4=1
  MB-02                  #3F        (#5F).7=1     (#5F).1=0
  YAMOD.8211             #0n58      (#0n59).7=0   (#0n5A).0=1   ;n=0..1
  Unipolbrit 2086        #xx7B      (#BB).?=1     (#BB).?=0
  AERCo Centronics       #7F        (#7F).4=1     auto
  MultiPrint             #BF        (#BB).6=1     (#BF)=X,(#BB)=X,(#BF)=X
  Informik 1989-II       #E3        (#F3).4=0     (#F3).4=1
  Sam FDD                #E8        (#E9).0       (#E9).0=1
  Elwro 800 Jr           #DE        (#DD).7=1     (#DD).2=0
  Scorpion               #DD        (#FE).7       auto(?)
  AERCo FD-68            #E0DF ?    (#xx??).?     ??
  PlusD                  #F7        (#F7).7       (#EF).6
  DISCiPLE               #FB        (#1F).6       (#1F).6
  ZX Lprint 3 & Pentagon #FB        (#FB).7=1     auto (?)
  Centronics Microface   #FB        (#FB).7=1     auto
  ZX Spectrum +3         #0FFD      (#0FFD).0=1   (#1FFD).4=0
  GP500A PZ "APINA"      #xx??      (#xx??).?     ??
  Kempston S (Software)  #E0BF      (#E2BF).0     E3BF.0 ;with SOFTWARE driver
  Kempston E (EPROM)     #xx??      (#xx??).?     ??     ;with EPROM driver
  DK'Tronics             #xx9B      (#xxBB).7     xxBB.1
  INES Printerface       #xx??      (#xx??).?     ??
  Opus Discovery         (memory mapped PIA6821)     ;BiDir.D0-D7,STB,READY,ACK
  Rotronics Wafadrive    dd0Eh      0002h.5       000Ah/200Ah ;via IN (not OUT)
  Indescomp              #FB        (#FB).0       (#7F).0
  Watford                ?          ?             ?
  Fuller                 ?          ?             ?
  VISMO ZP83 parallel interface
  VTR Parallel Centronics Interface
  Tasman Parallel Printer Interface
  ADS Advanced Digital Systems Centronics Interface
  Dorsch Centronics Interface
  Technology Research S-Print
  Cambridge Intelligent Printer Interface
  Logitek Floppy/Printer Interface (centronics port and c64 floppy port)
  Miracle ZX Printer Interface (rs232-to-centronics converter for Interface 1)
  Morex Printer Interface (centronics, rs232, built-in word processor)
  Romantic Robot Multiprint
  Cambridge Microelectronics Polyprint Interface
  Camel Products Print-SP
  Ram Electronics RamPrint Interface (centronics, joystick, RamWrite in ROM)
  Euroelectronics ZX Lprint (centronics)
  Euroelectronics ZX Lprint III (centronics and rs232)
  AMX Mouse (with 8bit centronics data split into two 4bit fragments)
  Nidd Valley Digimouse Interface (professional version with Centronics port)

Cloned and/or Re-distributed Centronics interfaces
  Hilderbay Interface (same as the Kempston S thing)
  Datel Inter Printer (same as the DK'Tronics thing, distributed by datel)
  Datel Ramprint      (same as the Ram Electronics thing, distributed by datel)
  Pentagon            (russian spectrum clone, with built-in ZX Lprint 3)

Centronics Notes
  Interface Bis          5 input lines for LapLink
  M'ody Technik 1986-12  8255 used: #DF=config; PA=data. All signals inverted.
  MB-02                  8255 used; #7F=config; PB=data, PC2=/ACK,
                           PC3=/AUTOFEED, PC4=/ERROR, PC5=SELECT, PC6=PAPEROUT
  Unipolbrit 2086        8255 used; #FB=config; PA=data; only PA and PC0, PC1,
                           PC4, PC5 are in DB15 slot
  Informik 1989-II       8255 used: #FB=config; PA=data (PB switches memory
                           banks). All signals inverted.
  Elwro 800 Jr           8255, other bits reserved
  YAMOD.8211             PC LPT connected as YABUS.ZXISA
  AERCo Centronics       (#7F).5=0 - printer present
  MultiPrint             8kB ROM, 8kB RAM
  ZX Lprint 3 Pentagon   2kB ROM
  Centronics Microface   see files for documentation
ZX Lprint by Euro Electronics: The test for printer ready is IN A,(#E0): AND #E0: CP #40. The ROM is paged into #0800..#0FFF. Looks like paged permanently.
ZX Lprint MK III: The ROM is paged into #0000..#07FF (with out to #FB - on, out #7B - off). The code for selected printer is copied into printer buffer (at #5B00).
Multiprint by Romantic Robot: Is NMI & debug interface with parallel port.

Basic Features
All cards should have STROBE and BUSY lines, and (at least) 7bit one-directional DATA lines (most or all have probably 8bit?). Some cards allow/require to control STROBE manually, others do generate it automatically after writing to DATA.

Extended Features
A few parallel ports may have bi-directional DATA lines (namely those with 8255 chips) (also some, but not all, ISA parallel cards may have bi-directional ports). Aside from STROBE and BUSY, some cards may have ACK, INIT, AUTOLF, etc.


 Spectrum Serial Port

Interface 1 -> RS232/Network: Port F7h and EFh
Spectrum 128/+2/+2A/+3 -> RS232/Keypad: PSG Register 14
Rotronics Wafadrive -> RS232: Port ? (uses RX,TX,RTS,CTS)
Timex RS232 Interface -> RS232: Port ?
Indescomp -> RS232: Port FBh.R and 7Fh.W
Cobra RS232 Interface (1983)
Cole RS232 (100/300 baud RS232 output port) (1983)
Fuller RS232 Interface aka Fuller FD Printerface (1983)
Fuller Dual Interface (RS232 and Centronics)
Kempston RS232 Interface (1984)
Tasman RS232 Printer Interface (1984)
U-Microcomputers USP-232D (dual RS232 with Z80DART chip, 75..9600 baud)
Watford Centronics/RS232 Interface (1985)
Morex Printer Interface (centronics, rs232, built-in word processor)
Euroelectronics ZX Lprint III (centronics and rs232)
YAMOD.8250

"Most of ZX Spectrum + 128K is VERSION 6K (C) 1985. There's a VERSION 9G with DB9 female slot for the RS232."

Network
Supported by Interface 1 (via Port F7h/EFh) and by Disciple (via Port 3Bh/1Fh). Despite of the different I/O ports, the transmission protocol is the same, and Interface 1 and Disciple can communicate with each other.
Spectrum Interface 1 (Microdrive, Network, RS232)
Spectrum Disc Disciple and Plus D
The network interfaces have two mono 3.5mm headphone sockets: One for the next, and one for the previous computer in the network. Reportedly up to 64 computers can be connected. On the first and last computer one socket is to be left unconnected (no matter which one) (the sockets contain a switch which connects the end of the data line via a terminator resistor to ground when no plug is inserted).
Note: Another (incompatible) network-type is supported by the Opus Discovery BIOS (which allows to use the Centronics port to transfer files to/from another computer).

RS232 (Sinclair) (9pin DSUB female)
  1     Not Connected.                                      ___________
  2 In  TXD                                                | 5 4 3 2 1 |
  3 Out RXD                                                 \_9_8_7_6_/
  4 In  DTR  ;should be high when ready
  5 Out CTS  ;should be high when ready
  6     Not Connected.
  7     GND
  8     Not Connected.
  9     +9V - Pull up. (DSR)
Although standard RS232 ports on PCs are using 9pin DSUB connectors too, the pinouts are NOT compatible (PCs are using male connectors, and, DTR/CTS/GND are at other locations).

AUX/Keypad and RS232/MIDI (PSG Register 14)
  0  AUX/Keypad pin 2 (out)
  1  AUX/Keypad pin 4 (out)
  2  RS232/MIDI pin 5 (RS232: output to remote CTS) (MIDI: DATA OUT)
  3  RS232/MIDI pin 3 (RS232: output to remote RXD) (MIDI: Not used)
  4  AUX/Keypad pin 3 (in) (Lightgun: light sensor:   0=No Light, 1=Light)
  5  AUX/Keypad pin 5 (in) (Lightgun: trigger button: 0=Pressed, 1=Released)
  6  RS232/MIDI pin 4 (RS232: input from remote DTR) (MIDI: Not used)
  7  RS232/MIDI pin 2 (RS232: input from remote TXD) (MIDI: Not used)
Using software control loops, the I/O lines could be driven as a second RS232 port (in the same way as the RS232/MIDI socket is driven using bits 2, 3, 6 and 7). Alternatively, the I/O lines could be used to drive, for example, a robot or some other external device.
  Pin     Function
  1       GND                         ___________________________
  2       OUTPUT BIT 0             __|   6   5   4   3   2   1   |
  3       INPUT BIT 4             |__   ### ### ### ### ### ###  |
  4       OUTPUT BIT 1               |___________________________|
  5       INPUT BIT 5
  6       +12V                              AUX/Keypad socket

  Pin     Function
  1       GND                    (MIDI: RETURN)          ____________________
  2       input from remote TXD  (MIDI: not used)     __|                    |
  3       output to remote RXD   (MIDI: not used)    |__   6  5  4  3  2  1  |
  4       input from remote DTR  (MIDI: not used)       |____________________|
  5       output to remote CTS   (MIDI: DATA OUT)
  6       +12V                   (MIDI: not used)            RS232/MIDI socket
Rather unconventional "BT 603A" sockets. As far as I understand, the plugs are similar to RJ/Western plugs, but with the plastic clip at the left-side (next to pin6), rather than at the top-side (above pin3/4).

Signal Naming
Spectrum RS232 pins are usually named according to the REMOTE side, eg. the spectrums transmit output is named RXD (not TXD) meaning to be connected to the remote receive input.


 Spectrum Expansion Ports

Expansion Ports (male cart-edge, 2x23 or 2x28 or 2x32 pins)
  -------TS2068------- ------TC2068------ ----Spectrum----   -ZX80/ZX81/TS1x00-
       Top   Bottom      Top/A   Bottom/B
       GND 1 GND           /BE 1 /EXROM      Top   Bottom
       EAR 2 SPKR/TAPE     A15 2 A14         A15 1 A14
       A7R 3 +15V          A13 3 A12         A13 2 A12          Top   Bottom
        D7 4 +5V            D7 4 +5V          D7 3 +5V           D7 1 5V
   (DZ IN) 5 N.C.         N.C. 5 +9V        N.C. 4 +9V      /RAM CS 2 9V
    (SLOT) 6 (SLOT)     (SLOT) 6 (SLOT)   (SLOT) 5 (SLOT)    (SLOT) 3 (SLOT)
        D0 7 PWR GND        D0 7 0V           D0 6 0V            D0 4 0V
        D1 8 PWR GND        D1 8 0V           D1 7 0V            D1 5 0V
        D2 9 CPU CLK        D2 9 CPU CLK      D2 8 CPU CLK       D2 6 CPU CLK
       D6 10 A0            D6 10 A0           D6 9 A0            D6 7 A0
       D5 11 A1            D5 11 A1          D5 10 A1            D5 8 A1
       D3 12 A2            D3 12 A2          D3 11 A2            D3 9 A2
       D4 13 A3            D4 13 A3          D4 12 A3           D4 10 A3
     /INT 14 A15'B       /INT 14           /INT 13 /IORQULA   /INT 11 A15
     /NMI 15 A14'B       /NMI 15 0V        /NMI 14 0V         /NMI 12 A14
    /HALT 16 A13'B      /HALT 16 VIDEO    /HALT 15 VIDEO     /HALT 13 A13
  /MREQ'B 17 A12        /MREQ 17          /MREQ 16 /Y        /MREQ 14 A12
  /IORQ'B 18 A11       /IOREQ 18         /IOREQ 17 V         /IORQ 15 A11
    /RD'B 19 A10          /RD 19            /RD 18 U           /RD 16 A10
    /WR'B 20 A9           /WR 20 /BUSREQ    /WR 19 /BUSREQ     /WR 17 A9
   /BUSAK 21 A8               21 /RESET     -5V 20 /RESET   /BUSAK 18 A8
    /WAIT 22 A7         /WAIT 22 A7       /WAIT 21 A7        /WAIT 19 A7
   /BUSRQ 23 A6               23 A6        +12V 22 A6       /BUSRQ 20 A6
   /RESET 24 A5               24 A5        -12V 23 A5       /RESET 21 A5
      /M1 25 A4           /M1 25 A4         /M1 24 A4          /MI 22 A4
  /RFSH'B 26 (DZ OUT)   /RFSH 26 /ROMCS   /RFSH 25 /ROMCS   /REFSH 23 /ROM CS
   /EXROM 27 RGB-R         A8 27 /BUSACK     A8 26 /BUSACK
   /ROSCS 28 RGB-G        A10 28 A9         A10 27 A9
      /BE 29 RGB-B       N.C. 29 A11       N.C. 28 A11
  (IO A5) 30 (BUSISO)     RED 30 CSYNC
    SOUND 31 VIDEO      GREEN 31 BRIGHT
      GND 32 GND         BLUE 32 GND
The Spectrum/TC20xx port (europe) is compatible only with the signals used by the ZX81 printer, but not compatible with other ZX80/ZX81 hardware (for some crazy reason, the /RESET pin is replaced by -12V, giving it a good chance to vaporize any ZX80/ZX81 hardware). The TS2068 port (usa) is almost fully backwards compatible with the ZX80/ZX81 and TS1000/TS1500 ports, but incompatible with Spectrum/TC20xx ports.
On the Spectrum 48K, the /Y,U,V,VIDEO signals are passed through jumpers on the mainboard, the Y,U,V jumpers are usually installed, but the VID jumper isn't (so without adding that jumper, VIDEO isn't actually output on Pin15/Bottom).

Signal Notes
  CPU CLK   normally 3.5MHz clock, but, PAUSED during waitstates
  /ROMCS    BIOS chipselect (can be dragged to 5V to disable internal BIOS)
  /IORQULA  ULA chipselect (can be dragged to 5V to disable ULA mirrors)

TS2068 Notes
(BUSISO),(IO A5),(DZ IN),(DZ OUT) are "not connected" (but are reserved for something or so). "IO A5 (Bit5 of PSG Port A) available on edge-slot)" so it is NOT not-connected...?

TC20xx
Although the TC2048/TC2068 are based on the US TS2068, their expansion port pin-outs were converted back to the 2x28 pin Spectrum layout to match the european market. During that backwards-conversion the /IORQULA pin got lost, the TC devrs apparently didn't understand its purpose, in the first version of the mainboard they have left it unconnected, in the next version it's accidently connected to /IOREQ via a jumper, in the third version it's directly wired to /IOREQ without any jumper.

Later Spectrums
Later Spectrums (eg. +3) don't use a +9V power supply, and thus leave the +9V pin unconnected; also, later models changed the pin numbering from 1..28 to 1..27 (with the SLOT spacings not being counted as pins). Also, the +3 has Pin4/comp = /ROM1OE, no -5V, no /BUSACK, no /IORQULA, no /ROMCS, Pin28/comp=RESET, and VIDEO,/Y,V,U replaced by /ROM2OE,/DISKRD,/DISKWR,/MOTORON.


 Spectrum ROM Cartridges

Cartridge Slots (female cart-edge, 2x18 or 2x15 pins)
  TS2068/TC2068 Cartridges (2x18 pin)       Interface 2 Cartridges (2x15 pin)
  Solder Side      Component Side           Whatever Side    Other Side
  1  A14'B         2 +5V                    1A /ROMCS (+5V)  1B +5V
  3..A12...........4..A13'B.....SLOT..      2A A12           2B A15 (/CS2)
  5  D0            6  D7                    3A A7            3B A13
  7  D1            8  A0                    4A A6            4B A8
  9  D2            10 A1                    5A (SLOT)        5B (SLOT)
  11 D6            12 A2                    6A A5            6B A9
  13 D5            14 A3                    7A A4            7B A11
  15 D3            16 A15B                  8A A3            8B /MREQ (/OE)
  17 D4            18 /MREQ'B               9A A2            9B A10
  19 /IORQ'B       20 A7R'B                 10A A1           10B A14 (/CS1)
  21 /RD'B         22 /M1                   11A A0           11B D7
  23 /WR'B         24 A8                    12A D0           12B D6
  25 A7            26 A9                    13A D1           13B D5
  27 A6            28 A10                   14A D2           14B D4
  29 A5            30 A11                   15A GND          15B D3
  31 A4            32 /RFSH'B or /ROMCS
  33 /BE or ROMDIS 34 /EXROM
  35 /ROSCS        36 GND

Timex TS2068/TC2068 Cartridges
The TS2068/TC2068 (but not the TC2048) contain a built-in cartridge slot.
The TC2068 cartridges are slightly bigger in height than TS2068 cartridges (so the bigger TC2068 carts won't fit into the smaller TS2068 slot without modifying the plastic case), and, the pinouts are slightly different for TS2068/TC2068 (pin32 and pin33).
A7R'B=Refresh Address Bit7, /RFSH=Refresh (TS2068 only), /BE=Bank Enable (TS2068 only), /EXROM (useless), /ROMCS (useless) (TC2068 only), ROMDIS="disables both internal ROM and also forces high state on both /ROMCS and /EXROM in the slot" (TC2068 only), /ROSCS=ROS Chip Select (Dock Bank Enable), xx'B=buffered (not directly connected to corresponding xx pin on CPU).

Timex Cartridge Types
  LROS --> Language ROM-Oriented Software    at 0000h (Z80 Code)
  AROS --> Application ROM-Oriented Software at 8000h (BASIC or Z80 Code)
BASIC code isn't directly executed in ROM, instead, the current BASIC line is copied to ARSBUF in RAM, and then executed from there. USR function does address ROM. While PEEK/POKE address RAM?. User-Defined BASIC functions (DEF FN) aren't supported.

Timex LROS Header
  0000h  Not used (should be F4h for Spectrum ROMs, see comment)
  0001h  Cartridge Type (01h=LROS)
  0002h  Entrypoint (16bit)
  0004h  Initial Value Port F4h (XORed by FFh)
  0038h  IRQ Handler
Note: [0004h].Bit3 should be set (otherwise the Machine Stack and Bank Switching code gets replaced by ROM). Caution the LROS is started with IRQs enabled (so the handler at 0038h might be called even before the Entrypoint handler is executed). Accordingly, [0004h] must enable any ROM locations used by the Entrypoint, and such used by the IRQ handler.
Comment: Some Spectrum games have JR FFF4h opcode as IM 2 interrupt handler at FFFFh, the opcode consists of [FFFFh]=18h (in RAM), and [0000h]=F4h (ie. initial DI opcode - or 1st byte of the LROS header) in BIOS ROM. Ie. an LROS cartridge with Spectrum ROM and LROS header should contain the DI opcode at 0000h.

Timex AROS Header
  8000h  Language Type (01h=BASIC, 02h=Machine Code) (other=Error)
  8001h  Cartridge Type (02h=AROS) (other=no cartridge)
  8002h  Entrypoint (16bit) (ptr to first BASIC Line, or to first opcode)
  8004h  Initial Value Port F4h (not XORed) (Bit0-3 must be set for BIOS/VRAM)
  8005h  Autostart Flag         (0=No, 1=Autostart)
  8006h  Number of bytes of RAM reserved for Machine Code variables (16bit)
Bug: [8006h] must be set to "N+15h" to allocated "N" bytes.

Timex The Spectrum Emulator Cartridge:
"This cartridge made by Timex of Portugal, contains a ROM image of TC2048. I think that this isn't a ROM cartridge, but a LROS cartridge, because it is simply plugged to TC2068 and it starts automaticaly. Many American Timex Sinclair 2068 users made a cartridge with a ROM chip of a ZX Spectrum and because of this, they have to type the OUT 244,3 command."

Spectrum Interface 2 Cartridges
In the cartridge, /ROMCS is usually wired to +5V, completely disabling the BIOS, and allowing to map 16K ROM to 0000h..3FFFh. There were only 10 cartridges manufactured, all of them only 16K in size, using ROM chips with two /CS pins. For normal EPROMs, /CS1 and /CS2 would need to be ORed by external logic (although /CS2 aka A15 could be left unconnected when not using the upper 32K of RAM). Bank switching for more than 16K ROM can be implemented by reading from a specific memory region, and latching LSBs of the address as bank number; observe that /MREQ gets low on /RD, /WR, and /RFSH; during refresh, the IR register pair is output to the address bus, so the I register should be set to a value that doesn't conflict with the bank switching addresses.

Timex TS1510 Command Cartridge Player (Pinouts unknown?)
An external cartridge adaptor for the TS1500. It can be also used with TS1000/ZX81 with a 16K RAM upgrade (assuming that most or all cartridges do require 16K RAM; there were only 4 carts manufactured).
The TS1500 BIOS starts cartridges automatically; it jumps to 2000h if the first byte at [2000h] is 01h (ie. a LD BC,nnnn opcode). On a ZX81/TS1000, the cartridge must be manually started (eg. by typing RAND USR 8192). Aside from the cartridge connector, the TS1510 includes a RESET button.
The first 8K of ROM is obviosly mapped to 2000h. Carts with 16K or more ROM are mapped to unknown (?) memory locations. According to the manual, such bigger carts won't work on computers expanded to 32K RAM (which have extra RAM at 8000h..BFFFh) (so the extra ROM is probably mapped to that region).
The memory mapping logic seems to be built-in in the TS1510 (rather than in the cartridge), looking at photo found in the internet, it seems to contain a 74LS00 chip, and another chip whose part number is unknown due to bad quality of the photo.
The TS1510 was advertised to support up to 24K ROM.
Only four TS1510 cartridge titles were ever released:
  07-9001 Supermath
  07-9002 States and Capitals
  07-9003 Chess
  07-9004 Flight Simulator


 Spectrum Chipset Pinouts

Ferranti 6C001 (Spectrum 48K) - Uncommitted Logic Array (ULA):
Ferranti 7C001 (Spectrum 128K) - Uncommitted Logic Array (ULA):
  1 /CAS  6 A1   11 A6       16 VIDEO V  21 D1   26 KB4    31 D7       36 A14
  2 /WR   7 A2   12 /INT     17 VIDEO /Y 22 D2   27 D4     32 CLOCK    37 A15
  3 /RD   8 A3   13 +5V      18 D0       23 KB2  28 SOUND  33 /IO-ULA  38 /MREQ
  4 /WE   9 A4   14 +5V'     19 KB0      24 KB3  29 D5     34 /ROM CS  39 OSC
  5 A0    10 A5  15 VIDEO U  20 KB1      25 D3   30 D6     35 /RAS     40 GND
SOUND is the analog-I/O-line for beep, save and load.
CLK is the clock-source to the CPU including the inhibited T-states.
IO-ULA is "A0(CPU) OR /IORQ" for the I/O-port FEh.
OSC is the 14MHz-crystal, other side grounded through a capacitor.
One of the +5V is decoupled through a RC-low-pass.

TS2068 SCLD
  1 A0       11 /EXROM   21 MUX     31 MA0       41 /CAS1    51 KB2   61 D4
  2 A1       12 /BE      22 MA7     32 /ROSCS    42 CLK CPU  52 KB3   62 D7
  3 A2       13 OSC OUT  23 MA3     33 /RAS1     43 +5V      53 KB4   63 /RD
  4 A3       14 OSC IN   24 MA4     34 VIDEO U   44 A14      54 /RD'  64 /IORQ
  5 A4       15 TAPE IN  25 MA2     35 VIDEO V   45 A15      55 D1    65 /WR
  6 A5       16 TAPE OUT 26 GND     36 VIDEO /Y  46 VIDEO B  56 D2    66 /MREQ
  7 A6       17 PSG BC1  27 /ROMCS  37 /DRAMWE   47 VIDEO G  57 D0    67 /INT
  8 A7       18 PSG BDIR 28 MA6     38 A7R       48 VIDEO R  58 D3    68 /RFSH
  9 A13      19 PSG CLK  29 MA1     39 /CAS3     49 KB0      59 D5
  10 CLK EXP 20 /TS      30 MA5     40 /CAS2     50 KB1      60 D6

Gate Array +2A/+3
  1 BUSY  14 MA3   27 A15      40 GND2  53 K10   66 GND3  79 /RFS   92 PSG CLK
  2 EAR   15 GND1  28 K0       41 VCC1  54 DV7   67 VA3   80 /IRQ   93 PSG BC1
  3 A0    16 MA4   29 K1       42 BRIT  55 DV6   68 VA2   81 /MRQ   94 PSG BDIR
  4 A1    17 MA5   30 K2       43 /SNC  56 DV5   69 VA1   82 /WR    95 /VWE
  5 A2    18 MA6   31 K3       44 FSC2  57 DV4   70 VA0   83 /RD    96 /VCS
  6 A3    19 MA7   32 K4       45 /RS   58 DV3   71 D7    84 /ROM2  97 /VRS
  7 A4    20 A8    33 /MTR     46 /CS   59 DV2   72 D6    85 /ROM1  98 MIC
  8 A5    21 A9    34 PRNT     47 STRB  60 DV1   73 D5    86 RA14   99 /RS
  9 A6    22 A10   35 /DRD     48 K5    61 DV0   74 D4    87 /ZCK   100 OSC
  10 A7   23 A11   36 /DWR     49 K6    62 VA7   75 D3    88 /INT
  11 MA0  24 A12   37 VIDEO B  50 K7    63 VA6   76 D2    89 /WAIT
  12 MA1  25 A13   38 VIDEO R  51 K8    64 VA5   77 D1    90 GND4
  13 MA2  26 A14   39 VIDEO G  52 K9    65 VA4   78 D0    91 VCC2

AY-3-8912 Sound Chip (PSG)
  1 SND C    5 SND A   9 P5   13 P1    17 A8 (+5V)  21 D7    25 D3
  2 TEST,NC  6 GND    10 P4   14 P0    18 BDIR      22 D6    26 D2
  3 +5V      7 P7     11 P3   15 CLK   19 BC2(+5V)  23 D5    27 D1
  4 SND B    8 P6     12 P2   16 /RES  20 BC1       24 D4    28 D0
Timex TS2068 uses CLK=1.76475Mhz, Spectrum +3 uses CLK=1.7734MHz.


 Spectrum Xboo

Step 1 - Connect Centronics Port and EPROM
  GND ------------------- CNTR.24 GND
  ULA.Pin28 ------|>o---- CNTR.10 ACK (SPKR)
  EAR ------------------- CNTR.2 D0 (EAR)
  KB1.Pin1 "0" ---|>|---- CNTR.3 D1 (CLK)
  KB1.Pin2 "9" ---|>|---- CNTR.4 D2 (D0)
  KB1.Pin3 "8" ---|>|---- CNTR.5 D3 (D1)
  KB1.Pin4 "7" ---|>|---- CNTR.6 D4 (D2)
  KB1.Pin5 "6" ---|>|---- CNTR.7 D5 (D3)
  CPU.Pin26   ----|>|---- CNTR.8 D6 (/RESET)
  EPROM.Pin27 ----------- CNTR.9 D7 (ROM.A14)
  EPROM.Pin27 ---/cut/--- /RD
  EPROM.Pin27 ---[3k3]--- +5V
  EPROM.Pin1  ----------- +5V
ACK must be amplified via a TTL inverter (eg. by using an unused NAND gate, found on Pin1-3 of IC24 (74LS00) on Spectrum 48K Issue 2 mainboards; note that the unused inputs are wired to 5V). Pass the reset and keyboard lines through 1N4148 diodes to prevent them to get stuck. Remove the ROM, and replace it by a 28pin socket for the EPROM, with 1:1 connection, except for pin1 (usually NC), and pin27 (usually /RD), omitting the /RD line causes the EPROM to respond also to writes and refresh, but that should be no problem.

Step 2 - Connect Joystick (Sinclair 1)
  KB1.PIN1 "0" ---|>|--- DSUB.Pin6 Fire
  KB1.PIN2 "9" ---|>|--- DSUB.Pin1 Up
  KB1.PIN3 "8" ---|>|--- DSUB.Pin2 Down
  KB1.PIN4 "7" ---|>|--- DSUB.Pin4 Right
  KB1.PIN5 "6" ---|>|--- DSUB.Pin3 Left
  KB2.PIN4 A12 ---|<|--- DSUB.Pin8 Common
Connect all joystick lines through 1N4148 diodes (the diode in A12 line facing in opposite direction). The five joystick data diodes are required because the joystick cable doesn't contain terminator resistors; without these diodes, the xboo transmission won't work without the diodes when signals "bounce back" from the end of the long joystick cable). Without the A12 diode, the Spectrum game "Zynaps" would crash after 1-2 minutes (no idea why, there is already a diode on the mainboard, so adding another diode should have little effect, but it does fix the zynaps crash, maybe it just works as a resistor).

Step 3 - Connect AV-Cable
        GND ----------------------------------------- Cinch GND
        VID ----------------------------------------- Cinch V
        MIC ----------------------------------------- Cinch A
Connect the Cinch sockets to the "VID" jumper, and to the "MIC" socket, the cables don't need to be shielded, but, Cinch GND should be connected somewhere close to the TV modulator (this gives a better picture).

Step 4 - Connect Supply from PC
There are two simple ways to replace the 9V supply by voltages from a PC power supply. The straight way would be to use a 7809 to lower 12V to 9V, but the 7809 produces a lot of heat (and the internal 7805 produces further heat when it lowers the 9V to 5V). So, my preferred way would be to use 5V:
        Spectrum 5V ------------ PC 5V (red floppy cable)
        Spectrum 5V -----||----- Spectrum GND
        Spectrum 5V ----|>|----- Spectrum 9V
Inject 5V from the PC supply to 5V on the mainboard (there's no need to inject GND, since that's passed through the centronics cable). Add a 470uF capacitor between 5V and GND for better video quality. Pass 5V to 9V via a 1N4001 diode (this feeds the internal +12V/-5V voltage generator which is usually driven at 9V, but it does also work at 5V, with 5V it's reaching only +10V instead of +12V, but does still work; the voltages are required for DRAM and video signal generation).
Basically 5V and 9V could be directly shortcut with each other, however, the diode has two purposes: First, it prevents 9V being passed to 5V (in case somebody connects a 9V supply). Second, it reduces noise from the voltage generator on the 5V line (without the diode one might need a larger 2200uF capactitor instead of 470uF one).
Caution - The 5V solution has two disadvantages: The TV modulator does no longer work (not sure why, maybe it requires full 12V, or maybe the voltage generator is producing too much noise when driven at 5V) (anyways, the AV cinch output does work). And, it doesn't output 9V on the expansion port (which may be required by some expansion hardware).

Step 5 - Connect Keyboard
The keyboard is connected via extremly fragile printed plastic wires, which tend to break when fiddling with them (or just when they get old). The membrane with the keyboard matrix consists of the same material, but the wires are the most fragile part.
Desolder the keyboard connectors. Connect real metal wires to the connectors. Cut the original plasitic wires close to the membrane. Carefully slide the connectors on the remaining ends (best, put the ends on a solid surface, if there's no good surfaces: put 1-2 layers of board under the ends, then firmly push the ends onto that surface, and move the connector onto the cable; rather than trying to move the cable into the connector. If needed, add some tuner/contact spray, and move the cable end in and out a little. Finally, glue the connectors to the case.

Software
The transmit function (for uploading .TAP files to the Spectrum) is found in no$zx's "Utility" menu. The "Utility" menu also contains a function called Create Patched BIOS Image, which creates a file called XMITSPEC.ROM, which is to be stored in the EPROM; the ROM-image is 32K in size, containing two copies (one original, and one patched) of the ZX Spectrum BIOS.

Note
Above Pin-numbers are for Spectrum 48K Issue 2 board (some may vary on other boards). The transmit software currently works only with ZX Spectrum 16K/48K/Plus (not with Spectrum 128 and up).


 Lambda 8300

The Lambda 8300 is a ZX81 clone (not an exact clone) made in china, and distributed by various companies, in various countries, under various different names...
  Name___________Usage_______________
  Lambda 8300    (Denmark, Sweden, Norway)
  Power 3000     Creon Enterprises (Hongkong) (Denmark, Germany)
  Marathon 32K   (Denmark)
  Futura 8300    Unisonic (USA)
  DEF 3000       (France)
  Your Computer
  IQ8300         (China?)
  BASIC 2000
  BASIC 3000
  PC 2000
  PC 8300        Text on mainboard

PC8300 BASIC
The BASIC syntax is more or less the same as on ZX81, so it accepts ZX81 BASIC type-in listings in most cases, there is also (limited) support for loading ZX81 cassette files. A few commands/functions are different: LOG (instead LN), LET (can be optionally omitted), and the quad-quotes token ("") isn't supported (ie. one cannot define double-quotes in strings). Additional commands are TEMPO, MUSIC, SOUND, BEEP, NOBEEP (for internal speaker), and INK, PAPER, BORDER (for external COLOR module).

PC8300 BIOS
The BIOS is apparently based on a disassembled and modified ZX81 BIOS. About ALL procedures are moved to different addresses, so there's no compatibility when CALLing BIOS functions. Only the INT and NMI handler are (more or less) same as in ZX81 (the timings are slightly changed, the INT handler has same function, but C and B registers are exchanged).

PC8300 RAM
Some entries in system area are modified. D_FILE is hardcoded at 407Dh, D_FILE is always expanded (full 1+33*24 bytes). BASIC program is located after D_FILE (ie. always at 4396h since D_FILE has fixed size). The BASIC program is terminated by an FFh byte (ZX81 has no such end byte). The remaining memory (VARS and up) is same as on ZX81.
The RAMTOP detection supports up to 32K RAM (unlike ZX81 which detects max 16K). The Lambda includes 2K RAM built-in (unlike 1K in ZX81). Note: despite of its name, the Marathon 32K also has only 2K RAM built-in (not 32K).

PC8300 CHARSET
Six characters are different as on ZX81. The charset is located in the ULA chip without using I register (the ZX81 has it in BIOS ROM, accessed via I=1Eh). Accordingly, changing the I register doesn't affect video (so neither True Hires nor Pseudo Hires will work, and any UDG/CHRs hardware expansions won't work either). The CPU can read the charset via I/O ports (mainly required for printers).
The video output is reportedly the inverse of ZX81, ie. white text on black background with black screen border.

PC8300 PRINTERS
The BIOS supports the Sinclair Printer as well as an external Centronics interface. The Centronics feature includes two modes: ZX chars converted to ASCII, or ZX chars converted to bitmaps (via ESC codes, which must be supported by the printer).

PC8300 KEYBOARD
42 Keys. The left 40 keys are same as on ZX81. The upper right key is wired to /RESET causing a warmboot (pressing /RESET+ENTER forces a coldboot, /RESET+L_KEY jumps to 2000h in external memory such like ROM, or COLOR RAM). The lower right key is same as the left SHIFT key. Punctuation marks and Cursor, Rubout, Edit, Graphics keys are at different location as than in ZX81. Commands are entered letter-by-letter (eg. type L-O-A-D, unlike pressing J on ZX81).

PC8300 SOUND
Contains a built-in speaker. The output level can be toggled HIGH/LOW by reading from an I/O port. Allows to produce a single square wave at fixed volume (though using PWM, one could eventually mix multiple channels at variable volume). Since both sound and video require a lot of CPU load, it's barely possible to output sound & video together. The BEEP (keyclick) is a very remarkable feature: each key has a different tone assigned, resulting in a funny melodic old-school SciFi typing effect.

PC8300 SOFTWARE
A few photos of cassettes do exist. As far as known there aren't any tape images. Strange RAW <--> A83 conversion programs exists (purpose unknown).

PC8300 JOYSTICK
DSUB 9pin Atari-style joystick port (with only four directions and one fire button implemented). Wired to keyboard matrix Bit3=Common, and
  A11 = 2 = Up,
  A10 = W = Down,
  A12 = 9 = Left,
  A9  = S = Right,
  A13 = O = Fire
(according to Kai Fischer's schematic).

PC8300 FILES
Cassette files are SAVEd with circa 16000 leading sync pulses (though, this is a fake, the LOAD function simply ignores them). Followed by a ZX81-style file: Some silence, followed by the Filename terminated with bit7=1, followed by memory at [4009h..[4014h]-1]. The memory content isn't ZX81 compatibile though (different system area, different D_FILE location, different BASIC tokens). The VERSN byte at 4009h is FFh (unlike 00h on ZX81).
Bits and bytes are encoded same as on ZX81 (0=four pulses, 1=nine pulses, with same pulse/silence timings).

PC8300 SUPPORT FOR ZX81 FILES
The Lambda BIOS can load ZX81 files, but with several restrictions: The files are converted to Lambda format (and cannot be converted back to ZX81 format). Information in the system area (such like Autostart), and VARS and D_FILE regions are discarded (only the raw BASIC code is accepted).
BUG: After loading a ZX81 file, the BIOS accidently jumps to 2000h, normally this is a mirror of 0000h which produces a warmboot - however, it'll crash if a Color RAM expansion is installed (workaround: POKE a RST0 opcode to 2000h), or accidently start any expansion ROM at 2000h.

PC8300 COLOR EXPANSION
Colors are optionally supported via an external color module. The Lambda BIOS contains built-in commands (INK/PAPER/BORDER) for it. The module connects to expansion port, and also to the monochrome composite video output, deciphers the "analogue" signal into TTL black/white and TTL sync/nosync signals.
Color attribtes are stored in 1K read/write-able RAM at 2000h..23FFh. During drawing, the module takes the D_FILE address (usually 407Dh..4395h) ANDed with 3FFh as index in the RAM, and reads the color attribute for the current character. The attributes associated with the HALT opcodes in D_FILE define the current section of the screen border color. The attribute bytes are Bit0-2 = INK, Bit4-6 = PAPER/BORDER, bit3,7 can be used as general purpose flags, but do not affect the video output. The monochrome luminance level selects INK/PAPER, but otherwise doesn't affect the outgoing signal (eg. PEN=3 and PAPER=3 do produce exactly the same color & same brightness).
   XXX does the border affect the COLOR BURST signal?
   XXX and thus alter the colors in the picture region?
   XXXXXX the 3bit color values allow to use 8 colors,
   XXXXXX but it's unknown WHICH colors?

PC8300 TIMINGS
The INT handler (0038h) is made 1 cycle slower as than on ZX81, meaning that the Lambda executes 208 clks/scanline (ZX81 only 207 clks).
The NMI handler is "improved" (the useless JP opcode removed, but the JR is replaced by a JP, so there is no speedup gained, to the worst, there's a bizarre NOP inserted making NMI handling 4 cycles slower than on ZX81, making the SLOW mode even slower-than-slow.

PC8300 I/O PORTS
  I/O:xxF5h.W  ;select charset line number (00..07)   ;\use only in FAST mode,
  I/O:xxF6h.W  ;select charset char number (00..3F)   ;/or after HSYNC (NMI)
  I/O:xxFBh.W  ;printer control (sinclair pixel-style, or ASCII character)
  I/O:xxFDh.W  ;disable NMIs
  I/O:xxFEh.W  ;enable NMIs
  I/O:xxFFh.W  ;terminate retrace / CAS output
  I/O:xxF5h.R  ;toggle sound output level
  I/O:xxF6h.R  ;read selected charset data (8 pixels)
  I/O:xxFBh.R  ;printer status
  I/O:NNFEh.R  ;read keyboard (A8..A15=row) (and CAS.IN) (and JOYSTICK)
               ;(but no PAL/NTSC bit here, unlike ZX81)
  I/O:FF7Eh.R  ;read PAL/NTSC flag (A7=row) (via diode from A7 to KEYB.0)
  MEM:00XXh dummy blink addr in write-protected BIOS ROM
  MEM:2000h optional boot entryoint (in COLOR RAM, or TS1510-cartirdges)
  MEM:2XXXh COLOR ATTRIBUTES (external color module)
  MEM:3000h disable color ram (on any write to 3000-3FFE with A0=0)
  MEM:3001h enable color ram  (on any write to 3001-3FFF with A0=1)

PC8300 System Area
  4000 2  unused   (unlike ZX81: ERR_NR, FLAGS) (not used)
  4002 2  err_sp   (same as ZX81)
  4004 2  ramtop   (same as ZX81, but, max 32K)
  4006 1  prmode   (unlike ZX81: MODE, cursor shape) (instead: printer mode)
  4007 1  err_nr   (unlike ZX81: ppc.lsb)
  4008 1  color    (unlike ZX81: ppc.msb)
SAVE area... (same as ZX81)
  4009 1  versn    (versn, and other temporary flags) (00h=ZX81, FFh=Lambda)
  400A 2  nxtlin   (unlike ZX81: e_ppc)
  400C 2  program  (unlike ZX81: d_file)
  400E 2  df_cc    (same as ZX81)
  4010 2  vars     (same as ZX81)
  4012 2  dest     (same as ZX81)
  4014 2  e_line   (same as ZX81) (input/workspace buffer, end of SAVE area)
  4016 2  ch_add   (same as ZX81)
  4018 2  x_ptr    (same as ZX81) (error/abort address)
  401A 2  stkbot   (same as ZX81)
  401C 2  stkend   (same as ZX81) (increases on PUSHes)
  401E 1  flags    (unlike ZX81: berg) (same as 4001h on ZX81)
  401F 2  mem      (same as ZX81)
  4021 1  munit    (unlike ZX81: unused) (tempo for music)
  4022 1  df_sz    (same as ZX81)
  4023 2  s_top    (same as ZX81)
  4025 2  last_k   (same as ZX81)
  4027 1  bounce   (same as ZX81)
  4028 1  margin   (same as ZX81) (but, derived from other I/O mechanism)
  4029 2  e_ppc    (unlike ZX81: nxtlin)
  402B 2  oldppc   (same as ZX81)
  402D 1  flagx    (same as ZX81)
  402E 2  strlen   (same as ZX81)
  4030 2  t_addr   (same as ZX81)
  4032 2  seed     (same as ZX81)
  4034 2  frames   (same as ZX81)
  4036 2  ppc      (unlike ZX81: coords) (same as 4007h on ZX81)
  4038 1  pr_cc    (same as ZX81)
  4039 2  s_posn   (same as ZX81)
  403B 1  cdflag   (additional bit4=graphics_cursor, bit5=beep_disable)
  403C 33 prbuff   (same as ZX81)
  405D 30 membot   (same as ZX81)
  407B 2  blink    (unlike ZX81: blink address instead unused)
  N/A  1  mode     (no [K],[L],[F] cursors, only [G] = flag in cdflag.bit4)
  N/A  1  berg     (none such, memorized somehow elsewhere)
  N/A  2  coords   (none such,isn't really used on ZX81 either)
4009 VERSN Should be 00h to identify ZX81 cassette files
BUG: [400Ch] is NOT properly set after loading ZX81 files, so better use 4396h than [400Ch].

PC8300 BIOS-MOD
There is also an alternate Lambda BIOS in the internet. It's source is unknown, it seems to be an commercial or homebrewn BIOS replacement, intended to improve ZX81 compatibility (called "replacement" because the BIOS doesn't match-up with the Lambda keyboard, so it's unlikely that Lambda's were sold with that BIOS built-in).
Patched TS1500 BIOS (around 1K modified, remaining 7K same as in original BIOS). RAMTOP is hardcoded as 16K (won't work with less memory). Frame rate is hardcoded as 60Hz. Keyboard layout is same as in ZX81 (cursor keys, punctuation marks, and token-hotkeys don't match up with the Lambda keyboard). Doesn't include charset in BIOS (uses the Lambda ULA charset, so, for video and printer output, six chars are still different as on ZX81).
Fileformat and memory map are exactly same as in ZX81, so it's compatible with machine code programs with hardcoded ZX81 memory addresses. System area is same as ZX81, though with some extra bits in 403Bh (bit3=REM n Cursor Blink Enable, bit4=STEP n Auto-Edit enable, bit5=BEEP n Keyclick Beep disable) and 407Bh (16bit Cursor address for blink).
BASIC includes some new/modified commands/functions: BEEP n (n=1 enables keyclick sounds, n=0 disables it), IN/OUT (executes IN/OUT opcode, the port address is only 8bit wide, not 16bit), STEP n (usually suffix for FOR, but in this BIOS it can be also as command to edit line "n" and all following/existing lines), REM n (n=1 enables cursor blink, n=0 disables it).


 Jupiter ACE

The ACE was manufactured by Jupiter Cantab from England in 1983. It was designed by two ex-Sinclair Research employees, Steven Vickers and Richard Altwasser, who earlier worked on the Sinclair ZX-80 and ZX-81, as well as the Spectrum.

Jupiter ACE I/O Ports
Jupiter ACE Video
Jupiter ACE Dimensions and Timings
Jupiter ACE Memory Map and System Area
Jupiter ACE Files

FORTH
FORTH Overview
FORTH Stack and Memory
FORTH Maths
FORTH Input/Output
FORTH ...
FORTH Error Codes

Links
http://www.jupiter-ace.co.uk/


 Jupiter ACE I/O Ports

Port FEh Read (or any Read with A0=0)
  0-4  Keyboard Bits
  5    Cassette Input (EAR/LOAD)
  6-7  Not used
Any read from this port toggles the speaker "off".

Port FEh Write (or any Write with A0=0)
  0-2  Not used
  3    Cassette Output (MIC/SAVE)
  4-7  Not used
Any write to this port toggles the speaker "on".


 Jupiter ACE Video

General
For VRAM Addressing & Timings, see:
Jupiter ACE Memory Map and System Area
Jupiter ACE Dimensions and Timings

Colors
The colors are 0=Black, 1=White, Border=Black (opposite of ZX81). Bit7 of the character number can be used as invert-attribute.

BIOS charset at 1D7Bh..1FFBh
The ROM character set is slightly compressed (of the 8 character rows, blank upper/lower rows are omitted for some characters):
  Char     Rows
  20h..3Eh 7    ;1Fh chars (upper row blank) (lower row is used in $,;)
  3Fh..5Eh 6    ;20h chars (upper and lower row blank)
  5Fh..7Eh 7    ;20h chars (upper row blank) (lower row is used in _gjpqy)
  7Fh      8    ;01h char  (no blank rows) (copyright symbol)
All character numbers ASCII (except 60h=pounds and 7Fh=copyright).

Video Capabilities
With only 1K Charset RAM, the Jupiter is more restrictive than a ZX Spectrum (or HiRes ZX81) with 6K Bitmap RAM. On the other hand, there's more CPU load available than on ZX81, so the Jupiter has some potential.
Possible would be games with not too detailed graphics (like racing games which have relative featureless BG graphics, or shoot-em-ups without BG graphics, etc.)
Another approach would be a medium-resolution Bitmap with 128x96 pixels (using 128 characters with 4x4 pixel "block graphics", and their inverses, giving 256 characters in total).


 Jupiter ACE Dimensions and Timings

The Z80 CPU is clocked at 3.25MHz. Picture size is 256x192 pixels.

Horizontal Timings
  Phase__________Dotclock_______________=_CPU Clock___________
  Hsync           32 pixels (320..351)  = 16 clks
  Left Border     64 pixels (352..415)  = 32 clks
  Picture        256 pixels (0..255)    = 128 clks
  Right Border    64 pixels (256..319)  = 32 clks
  Total          416 pixels (0..415)    = 208 clks

Vertical Timings
  Phase__________50_Hz_version__________60_Hz_Version_______
  Vsync            8 lines (248..255)     8 lines (224..231)
  Upper Border    56 lines (256..311)    32 lines (232..263)
  Picture        192 lines (0..191)     192 lines (0..191)
  Lower Border    56 lines (192..247)    32 lines (192..223)
  Total          312 lines (0..311)     264 lines (0..311)
Note: There are separate mainboards for 50Hz/60Hz (ie. the framerate isn't software selectable).

VSYNC Interrupt
The default interrupt handler at 0038h isn't too useful, however, one can put a custom IM2 interrupt handler in RAM. The Z80's /INT pin is wired directly to /VSYNC. This implies two problems:
1) The /VSYNC signal is LOW for 8 scanlines (1664 cycles), so, the IRQ handler should not re-enable IRQs during that period (otherwise the same interrupt would be executed another time). If necessary include a 1664-cycle delay in the IRQ handler, or return without enabling IRQs.
2) For flicker/waitstate-free drawing, it'd be ideal to access VRAM during VBLANK, but since /INT is generated on VSYNC rather than VBLANK, one can use only little more than half of the VBLANK periond.


 Jupiter ACE Memory Map and System Area

Jupiter ACE Memory Map
 0000h-1FFFh R   BIOS ROM (8K) (FORTH Interpreter)
 2000h-23FFh R/W VRAM BgMap/Pad with CPU priority (video DMA shows garbage)
 2400h-27FFh R/W VRAM BgMap/Pad with DMA priority (CPU gets paused by /WAIT)
 2800h-2BFFh W   VRAM Charset with CPU priority   (video DMA shows garbage)
 2C00h-2FFFh W   VRAM Charset with DMA priority   (CPU gets paused by /WAIT)
 3000h       R/W Unused (mirrors of 1K Work RAM)
 3C00h       R/W Work RAM (1K)
 4000h-FFFFh R/W Expansion RAM (or open-bus if none such)
The total amount of internal RAM is 3K (the CPU/DMA priority regions mirror to a single 1K VRAM block).
Note: BgMap/Pad contains 300h bytes BgMap, followed by 100h bytes Pad (the Pad is used to HOLD characters during formatted output, and to store filenames and fileheaders during load/save).

Jupiter ACE System Area
  FP_WS   3C00h  19 Workspace for floating point arithmetic
  LISTWS  3C13h  5  Workspace for 'LIST' and 'EDIT'
  RAMTOP  3C18h  2  Work RAM End Address (4000h=1K, 8000h=17K, 0000h=49K)
  HLD     3C1Ah  2  VRAM PAD Address of the latest character held in the pad by
                    formatted output ('#', 'HOLD' and so on)
  SCRPOS  3C1Ch  2  VRAM Output Address: location of next char to be printed
  INSCRN  3C1Eh  2  VRAM InputBuf Address: start of current logical line
  CURSOR  3C20h  2  VRAM InputBuf Address: cursor addresss in input buffer
  ENDBUF  3C22h  2  VRAM InputBuf Address: end of current logical line
  L_HALF  3C24h  2  VRAM InputBuf Address: input buffer start (ends at 26FFh)
  KEYCOD  3C26h  1  Keyboard ASCII code of the last key pressed (00h=none)
  KEYCNT  3C27h  1  Keyboard Debounce counter (key accept and repeat delay)
  STATIN  3C28h  1  Keyboard Flags (0=newkey,1=caps,2=graph,3=invert,5=enter)
  EXWRCH  3C29h  2  Address of print chr(A) routine (or 0000h=print to screen)
  FRAMES  3C2Bh  4  Frames counter (number of /VSYNC interrupts since power-on)
  XCOORD  3C2Fh  1  PLOT last used x-coordinate
  YCOORD  3C30h  1  PLOT last used y-coordinate
  CURRENT 3C31h  2  Vocabulary Address of CURRENT vocabulary
  CONTEXT 3C33h  2  Vocabulary Address of CONTEXT vocabulary
  VOCLNK  3C35h  2  Vocabulary Address of NEWEST vocabulary (plus 3)
  STKBOT  3C37h  2  Work RAM Address of End of Dictionary / Start of Stack
  DICT    3C39h  2  Work RAM Address of the (still undefined) length field of
                    the newest word in the dictionary (once when that length
                    field is correctly filled in then DICT may be 0000h)
  SPARE   3C3Bh  2  Work RAM Address of first byte past top of the FORTH stack
  ERR_NO  3C3Dh  1  Error code (FFh=no error) (ERROR shown on ABORT if bit7=0)
  FLAGS   3C3Eh  1  Flags...
                     Bit2 incomplete definition at the end of the dictionary
                     Bit3 output is to be fed into the input buffer
                     Bit4 the Ace is in invisible mode
                     Bit6 the Ace is in compile (editing) mode
  BASE    3C3Fh  1  The system number base (default is 0Ah=decimal)
          3C40h  5  FORTH name string "FORTH"+80h
          3C45h  2  FORTH disp to next word (or 0000h if no next)
          3C47h  2  FORTH ptr to 1FFFh in ROM (links to ROM words)
          3C49h  1  FORTH length of "FORTH" name at 3C40h (ie. =05h)
          3C4Ah  2  FORTH ptr to 11B5h in ROM (vocabulary handler)
          3C4Ch  2  FORTH ptr to next word in chain (or to 3C49h if none)
          3C4Eh  1  FORTH unknown/unused (usually 00h)
          3C4Fh  2  FORTH ptr to previous vocabulary (always 0000h=none)
          3C51h  .. user dictionary


 Jupiter ACE Files

Forth File Headers
  Pre  1  Blocktype (00h=Header)
  00h  1  Filetype  (00h=Forth)
  01h  10 Filename  (ASCII, padded with spaces) (chr 00h = no name)
  0Bh  2  Filesize  (LEN=STKBOT-3C51h)
  0Dh  2  Unused    (usually 3C51h, dictionary start address)
  0Fh  2  Offset to newest word at end of file (relative to 3C51h)
  11h  2  Unused    (usually 3C4Ch, CURRENT vocabulary)
  13h  2  Unused    (usually 3C4Ch, CONTEXT vocabulary)
  15h  2  Unused    (usually 3C4Fh, NEWEST vocabulary+3)
  17h  2  Unused    (usually 3C51h+filesize, STKBOT)
  Post 1  Chksum [00h..18h] XORed together (unlike Spectrum without Blocktype)
The file body is loaded to the begin of free memory (3C51h when memory is empty). Addresses inside of the file body are relative to 3C51h (if the file is loaded to a higher memory location, then the addresses are increased accordingly).

Binary File Header
  Pre  1  Blocktype (00h=Header)
  00h  1  Filetype  (20h=Binary)
  01h  10 Filename  (ASCII, padded with spaces) (chr 00h = no name)
  0Bh  2  Filesize  (LEN)
  0Dh  2  Fileaddr  (Default target address, used when desired target=0)
  0Fh  10 Unused    (filled with spaces)
  Post 1  Chksum [00h..18h] XORed together (unlike Spectrum without Blocktype)

File Body
  Pre  1   Blocktype (FFh=Body)
  00h  LEN Forth stuff (starting with 1st word name) / Binary Data
  Post 1   Chksum [00h..LEN-1] XORed together (unlike Spectrum without Blktyp)

Cassette Signals
The overall format is nearly identical to ZX Spectrum. On the ACE, the Chksum doesn't include the Blocktype byte. The bit-encoding and various pilot/data pulse timings are identical as on spectrum (+/- 5 us or so, which can be ignored). The number of Pilot pulses is slightly different (8192 for header, 1024 for data) (on Spectrum it's 8063 for Header, and 3223 for Data).

.TAP Images
Jupiter ACE cassette images are stored in .TAP files, using the same extension, and (almost) the same format as ZX Spectrum .TAP files - the difference is that Jupiter ACE images do not contain Blocktype bytes (software must insert the missing byles: toggle between FFh or 00h on each second block). Most or all .TAP images are starting with a header block, so the first 2 bytes in the file should be 0019h for Jupiter ACE, and 0013h for ZX Spectrum.

Forth Word Definitions
  Pos Len Content
  -N  ..  word name in ASCII (terminated by bit7=1 in last char)
  -4  2   disp from current addr to NEXT <word name> (RAM words only, not ROM)
  -2  2   ptr to PREVIOUS word definition
  +0  1   length of word name (up to including the last char with bit7=1)
  +1  2   ptr to Z80 machine code handler for this word (usually to ROM)
  +3  ..  whatever, probably TYPE and definition...

          3C40h  5  FORTH name string "FORTH"+80h
          3C45h  2  FORTH disp to next word (or 0000h if no next)
          3C47h  2  FORTH ptr to 1FFFh in ROM (links to ROM words)
          3C49h  1  FORTH length of "FORTH" name at 3C40h (ie. =05h)
          3C4Ah  2  FORTH ptr to 11B5h in ROM
          3C4Ch  2  FORTH ptr to next word in chain (or to 3C49h if none)

Starting files
There is no (intended) autostart mechanism provided. The normal way to load and start ACE files is bizarre and usually requires detailed loading instructions: One must know both the name of the file, and name of its "main" function, in some cases one must also manually execute initialization functions. For example Jupiter Cantab's "Worms" is - seriously - meant to be started like so:
  LOAD worms GET graphics GO
If the names are unknown one can do: To discover file names: Type <LOAD dummy>, watch the first filename listed on screen, rewind tape, type <LOAD filename>.
To discover function names: Type VLIST, watch the function/variable names (watch quickly - before they get scrolled offscreen). Then try typing some of the names, hoping that it's the main function (variables will produce errors, subfunctions may act useless) (to start with: <FILENAME>, GO, RUN, PLAY, START, MAIN, GAME, etc. would be possible candidates) (some programs may have multiple main funtions, eg. PLAY/HELP, or PLAY_EASY/PLAY_DIFFICULT).

Empty Filenames
The BIOS doesn't allow to SAVE something without filenames, however, it can LOAD such files (so, with suitable tools, one can create files with empty, space-padded filenames). There is no "wildcard" to LOAD the first file on tape, so - unless it's empty - the user must always type-in the filename.

Autostarting Files
There are two ways to autostart binary files by just typing "0 0 BLOAD" (or "0. BLOAD"):
1) Load the entrypoint to EXWRCH in system area (EXWRCH gets executed almost immediately after loading when the BIOS wants to display "OK"). This method is best for machine code.
2) Load a commandline string into the Input Buffer in VRAM (ie. load <00h,"LOAD filename mainfunction"> to address 22E0h). This method is best for FORTH programs, and existing (older) files which originally didn't include autostart.
The VRAM method is used by Spacefighter (also loads a picture into VRAM alongside with the commandline). The EXWRCH method is used by Magic Floor.
Autostarting works only with BLOAD, not with LOAD (the FORTH fileheader doesn't include any useful entries; the relocation feature could be tweaked to add an offset to whatever memory locations, but that offset is usually 0000h, so it'd be useless).


 FORTH Overview

Stack
  Stack Notation  Cells Description
  c               1     Character (high byte ignored)
  flag            1     Boolean (0 = False, 1 = True)
  n               1     Signed 16bit number
  u               1     Unsigned 16bit number
  x               1     Non-specific 16bit number
  adr             1     Memory address (16bit)
  d               2     Signed 32bit double number
  ud              2     Unsigned 32bit double number
  xd              2     Non-specific 32bit number
  f               2     Floating point 32bit number
Each stack cell is 16bit wide, the cells do not contain type information, so the programmer must take care to use the correct commands on each cell.

Word definitions
Defining a word (a procedure, variable, etc.) doesn't erase old words with the same name, same applies when LOADing words from tape. So, it can happen that a word is defined multiple times, of which, fortran will "see" the newest definition; one can also make "newer" definitions of the predefined ROM words.


 FORTH Stack and Memory

Stack Manipulation
  x     (--> x)                       Store 16bit immediate on stack (eg. 1)
  f     (--> f)                       Store float immediate on stack (eg. 1.0)
  DROP  (x -->)                       Discard TOS (top of stack)
  DUP   (x --> x x)                   Copy 1st cell to top (Duplicate TOS)
  OVER  (x2 x1 --> x2 x1 x2)          Copy 2nd cell to top
  PICK  (xn..x1 n --> xn..x1 xn)      Copy nth cell to top
  SWAP  (x2 x1 --> x1 x2)             Rotate 2nd cell to top
  ROT   (x3 x2 x1 --> x2 x1 x3)       Rotate 3rd cell to top
  ROLL  (xn..x1 n --> xn-1..x1 xn)    Rotate nth cell to top
  ?DUP  (x --> x (x))                 Conditional DUP, only if x = non-zero
  >R    (x -->) (R: --> x)            Move TOS to Return Stack
  R>    (R: x -->) (--> x)            Retrieve from Return Stack

Memory
  @     (adr --> x)     Read x (2 bytes) from adr
  !     (x adr -->)     Store x (2 bytes) to adr
  C@    (adr --> c)     Read c (1 byte) from adr
  C!    (c adr -->)     Store c (1 byte) to adr


 FORTH Maths

Integer Arithmetic
  +       (n1 n2 --> n3)       n3 = n1 + n2
  -       (n1 n2 --> n3)       n3 = n1 - n2
  *       (n1 n2 --> n3)       n3 = n1 * n2
  /       (n1 n2 --> n3)       n4 = n1 / n2
  MOD     (n1 n2 --> n3)       Remainder of n1 / n2 (sign of n1)
  /MOD    (n1 n2 --> n3 n4)    n3 = remainder of n1/n2, n4=n1/n2
  */      (n1 n2 n3 --> n4)    n4 = n1*n2/n3
  */MOD   (n1 n2 n3 --> n4 n5) n4 = remainder of n1*n2/n3, n5=n1*n2/n3
  1+      (n1 --> n2)          n2 = n1 + 1
  1-      (n1 --> n2)          n2 = n1 - 1
  2+      (n1 --> n2)          n2 = n1 + 2
  2-      (n1 --> n2)          n2 = n1 - 2
  ABS     (n --> u)            u = |n| (absolute value)
  NEGATE  (n1 --> n2)          n2 = -n1 (two's complement)
  U*      (u1 u2 --> ud)       ud = u1 * u2
  U/MOD   (ud u1 --> u2 u3)    u2 = remainder of ud/u1, u3 = ud/u1
  D+      (d1 d2 --> d3)       d3 = d1 + d2
  DNEGATE (d1 --> d2)          d2 = -d1 (two's complement)

Floating Point Arithmetic
  INT     (f --> n)            Convert floating number to integer
  UFLOAT  (u --> f)            Convert unsigned integer to float
  F+      (f1 f2 --> f3)       f3 = f1 + f2
  F-      (f1 f2 --> f3)       f3 = f1 - f2
  F*      (f1 f2 --> f3)       f3 = f1 * f2
  F/      (f1 f2 --> f3)       f3 = f1 / f2
  FNEGATE (f1 --> f2)          f2 = -f1

Comparison
  <       (n1 n2 --> flag)     True if n1 < n2
  =       (n1 n2 --> flag)     True if n1 = n2
  >       (n1 n2 --> flag)     True if n1 > n2
  0<      (n --> flag)         True if n < 0
  0=      (n --> flag)         True if n = 0
  0>      (n --> flag)         True if n > 0
  U<      (u1 u2 --> flag)     True if u1 < u2
  D<      (d1 d2 --> flag)     True if d1 < d2
  MAX     (n1 n2 --> n3)       Leave greater of two numbers
  MIN     (n1 n2 --> n3)       Leave lesser of two numbers

Logical
  AND     (x1 x2 --> x3)       Bitwise boolean AND
  OR      (x1 x2 --> x3)       Bitwise boolean OR
  XOR     (x1 x2 --> x3)       Bitwise boolean XOR


 FORTH Input/Output

Character Output
  CR         (-->)           Print carriage return and line feed
  EMIT       (c -->)         Print ASCII character c
  SPACE      (-->)           Print one space
  SPACES     (n -->)         Print n spaces, if n > 0
  ." ..."    (-->)           Print string terminated by " (." 123 test")
  TYPE       (adr n -->)     Print n characters from adr

Number Output
  .       (n -->)         Print n with one trailing space
  U.      (u -->)         Print unsigned with one trailing space
  F.      (f -->)         Print float with one trailing space

Formatted Output
  <#      (-->)           Initiate formatted output
  #       (ud1 --> ud2)   Convert one digit from ud1 and HOLD it in the PAD
  #S      (ud --> 0 0)    Convert and HOLD all remaining significant digits
  HOLD    (c -->)         Insert character into formatted string
  SIGN    (n -->)         HOLD minus sign if n < 0
  #>      (ud --> adr n)  Finish formatted output leaving address & length
                          of the resulting string

Conversion
  BASE       (--> adr)       1-byte variable containing system number base
  DECIMAL    (-->)           Set base to decimal
  ASCII text (--> c)         ASCII code of first character in text

Character Input
  QUERY      (-->)           Accept entry at the input buffer
  WORD       (c --> adr)     Take text from input buffer using c
                             as delimiter, leave adr of length byte
  RETYPE     ( --> )         Allow input buffer editing, turning cursor to "?"
  INKEY      (--> x)         Read keyboard (0 = no key pressed)

Number Input

  CONVERT (d1 adr1 --> d2 adr2)   Convert string at adr1 to double number
                                  and add into d1 leaving result d2
  NUMBER  (--> x (adr))   Get number from input buffer
          (--> n 4102)    Converted to integer
          (--> f 4181)    Converted to float
          (--> 0)         Conversion failed

Misc Screen Output
  CLS   (-->)          Clear screen
  AT    (n1 n2 -->)    Set print position to row n1 and column n2
  PLOT  (n1 n2 n3 -->) Plot X=n1,Y=n2,Mode=n3 (0=unplot,1=plot,2=move,3=change)


 FORTH ....

Control Structures
  IF      (flag -->)    Conditional structure IF..(ELSE)..THEN
  ELSE    (-->)         False condition of an IF structure
  THEN    (-->)         End of an IF conditional structure
  DO      (n1 n2 -->)   Counted loop structure DO...LOOP, (n2=start, n1=end)
  LOOP    (-->)         Increment loop count, terminate if end
  +LOOP   (n -->)       Add n to loop count, terminate if end
  I       (--> n)       Get current loop count
  I'      (--> n)       Get current loop count limit
  J       (--> n)       Get outer loop count
  LEAVE   (-->)         Force a DO...LOOP count to end
  BEGIN   (-->)         Begin a WHILE or UNTIL loop
  UNTIL   (flag -->)    Loop until flag = true (BEGIN..UNTIL)
  WHILE   (flag -->)    Exit loop when flag = false (BEGIN..WHILE..REPEAT)
  REPEAT  (-->)         Jump back to BEGIN in a WHILE loop
  EXIT    ( --> )       Exit current word execution
  EXECUTE (adr -->)     Execute word with compilation adr
  CALL    (adr -->)     Call Z80 code (terminated with JP IY) (not by RET)
  ABORT   (... -->)     Quit program, clearing data stack
  QUIT    (-->)         Quit program, not clearing data stack

Word Definition
  : pname ... ;   (-->)     Define a procedure (terminated by semicolon)
  VARIABLE vname  (x -->)   Define a variable with initial value x
  CONSTANT cname  (x -->)   Define a constant with constant value x
  CREATE aname    (-->)     Define an (empty) array (usually followed by ALLOT)
  ALLOT           (n -->)   Allocate n bytes at end of newest word (see CREATE)
  vname           (--> adr) Get variable adr (eg. "vname @" to get its value)
  aname           (--> adr) Get array address
  cname           (--> x)   Get constant value
  pname           (-->)     Call a procedure
  IMMEDIATE       (-->)     Make newest word to execute even in compile mode
  DEFINER word    (-->)     Start a defining word definition
  DOES>           (--> adr) Define the action routine of a defining word
  COMPILER word   (n -->)   Start a compiling word definition (and ALLOT n)
  RUNS>           (--> adr) Defines the action routine of a compiling word
  FIND word       (--> adr) Find word compilation address (0 if not found)
  LIST word       (-->)     List word definition
  EDIT word       (-->)     Edit word definition (doesn't delete old word)
  FORGET name     (-->)     Delete <name> as well as ALL newer words
  REDEFINE name   (-->)     Delete old <name> and replace it by newest word

Vocabulary
  VOCABULARY dname (-->)     Define a new vocabulary
  dname            (-->)     Set CONTEXT to the <dname> vocabulary
  FORTH            (-->)     Set CONTEXT to the FORTH vocabulary
  DEFINITIONS      (-->)     Set CURRENT vocabulary to CONTEXT
  CONTEXT          (--> adr) Get current word search vocabulary address  ;3C33h
  CURRENT          (--> adr) Get current word definition vocabulary addr ;3C31h
  VLIST            (-->)     List dictionary to screen (press BREAK to stop it)

Compiler
  ,               (x -->)   Compile x into dictionary (ALLOT 2 byte, set to x)
  C,              (c -->)   Compile c into dictionary (ALLOT 1 byte, set to c)
  LITERAL         (x -->)   Compile x into edited procedure (preceed by [ x ])
  [ ... ]         (... -->) Force ... to be interpreted (even during EDITing)

Miscellaneous
  ( ...) (-->)          Start a comment, terminated by ")"
  HERE   (--> adr)      Next available dictionary location, ie. [STKBOT]
  PAD    (--> adr)      Scratch pad area address (2701h aka 9985)
  SLOW   (-->)          Normal execution. Enable error checks
  FAST   (-->)          Faster execution. Disable error checks (and BREAK-key ?)
  BEEP   (u1 u2 -->)    Play tone, u1=1000000/(8*freq [Hz]), u2=duration [ms]
  IN     (adr --> c)    Read byte from Z80 input port adr
  OUT    (c adr -->)    Write byte to Z80 output port adr
  INVIS  (-->)          Disable copy-up mechanism and OK
  VIS    (-->)          Enable copy-up mechanism and OK
  LINE   (-->)          Interpret the input buffer as FORTH

Tape Files
  LOAD name     (-->)       Load vocabulary          ;\lists all filenames
  VERIFY name   (-->)       Verify vocabulary        ;/when no name typed
  SAVE name     (-->)       Save vocabulary
  BLOAD name    (adr u -->) Load u bytes to adr      ;\use file header value(s)
  BVERIFY name  (adr u -->) Verify u bytes from adr  ;/when adr=0 or u=0
  BSAVE name    (adr u -->) Save u bytes from adr
filenames are case sensitive, length must be 1-10 characters, may not include space characters.
CAUTION: The filenames are ALWAYS taken from the input buffer - that means one cannot specify filenames inside of procedures (unless the procedure 'pokes' the filename into the input buffer in VRAM before executing a cassette command).


 FORTH Error Codes

Error Codes
  1  Not enough memory
  2  Data Stack Underflow
  3  BREAK pressed
  4  Compile only word
  5  Structure imbalance
  6  Name size < 1 or > 64
  7  PICK or ROLL operand = 0
  8  Floating point overflow
  9  AT or PLOT to the input buffer
  10 Tape error
  11 REDEFINE or FORGET error
  12 Incomplete definition in dictionary
  13 Word not found or is ROM or is FORTH
  14 Word not Listable


 Z80 CPU Specifications

Z80 Register Set
Z80 Flags
Z80 Instruction Format
Z80 Load Commands
Z80 Arithmetic/Logical Commands
Z80 Rotate/Shift and Singlebit Operations
Z80 Jumpcommands & Interrupts
Z80 I/O Commands
Z80 Interrupts
Z80 Meaningless and Duplicated Opcodes
Z80 Garbage in Flag Register
Z80 Compatibility
Z80 Pin-Outs
Z80 Local Usage


 Z80 Register Set

Register Summary
  16bit Hi   Lo   Name/Function
  ---------------------------------------
  AF    A    -    Accumulator & Flags
  BC    B    C    BC
  DE    D    E    DE
  HL    H    L    HL
  AF'   -    -    Second AF
  BC'   -    -    Second BC
  DE'   -    -    Second DE
  HL'   -    -    Second HL
  IX    IXH  IXL  Index register 1
  IY    IYH  IYL  Index register 2
  SP    -    -    Stack Pointer
  PC    -    -    Program Counter/Pointer
  -     I    R    Interrupt & Refresh

Normal 8bit and 16bit Registers
The Accumulator (A) is the allround register for 8bit operations. Registers B, C, D, E, H, L are normal 8bit registers, which can be also accessed as 16bit register pairs BC, DE, HL.
The HL register pair is used as allround register for 16bit operations. B and BC are sometimes used as counters. DE is used as DEstination pointer in block transfer commands.

Second Register Set
The Z80 includes a second register set (AF',BC',DE',HL') these registers cannot be accessed directly, but can be exchanged with the normal registers by using the EX AF,AF and EXX instructions.

Refresh Register
The lower 7 bits of the Refresh Register (R) are incremented with every instruction. Instructions with at least one prefix-byte (CB,DD,ED,FD, or DDCB,FDCB) will increment the register twice. Bit 7 can be used by programmer to store data. Permanent writing to this register will suppress memory refresh signals, causing Dynamic RAM to lose data.

Interrupt Register
The Interrupt Register (I) is used in interrupt mode 2 only (see command "im 2"). In other modes it can be used as simple 8bit data register.

IX and IY Registers
IX and IY are able to manage almost all the things that HL is able to do. When used as memory pointers they are additionally including a signed index byte (IX+d). The disadvantage is that the opcodes occupy more memory bytes, and that they are less fast than HL-instructions.

Undocumented 8bit Registers
IXH, IXL, IYH, IYL are undocumented 8bit registers which can be used to access high and low bytes of the IX and IY registers (much like H and L for HL). Even though these registers do not officially exist, they seem to be available in all Z80 CPUs, and are quite commonly used by various software.


 Z80 Flags

Flag Summary
The Flags are located in the lower eight bits of the AF register pair.
  Bit Name  Set  Clr  Expl.
  0   C     C    NC   Carry Flag
  1   N     -    -    Add/Sub-Flag (BCD)
  2   P/V   PE   PO   Parity/Overflow-Flag
  3   -     -    -    Undocumented
  4   H     -    -    Half-Carry Flag (BCD)
  5   -     -    -    Undocumented
  6   Z     Z    NZ   Zero-Flag
  7   S     M    P    Sign-Flag

Carry Flag (C)
This flag signalizes if the result of an arithmetic operation exceeded the maximum range of 8 or 16 bits, ie. the flag is set if the result was less than Zero, or greater than 255 (8bit) or 65535 (16bit). After rotate/shift operations the bit that has been 'shifted out' is stored in the carry flag.

Zero Flag (Z)
Signalizes if the result of an operation has been zero (Z) or not zero (NZ). Note that the flag is set (1) if the result was zero (0).

Sign Flag (S)
Signalizes if the result of an operation is negative (M) or positive (P), the sign flag is just a copy of the most significant bit of the result.

Parity/Overflow Flag (P/V)
This flag is used as Parity Flag, or as Overflow Flag, or for other purposes, depending on the instruction.
Parity: Bit7 XOR Bit6 XOR Bit5 ... XOR Bit0 XOR 1.
8bit Overflow: Indicates if the result was greater/less than +127/-128.
HL Overflow: Indicates if the result was greater/less than +32767/-32768.
After LD A,I or LD A,R: Contains current state of IFF2.
After LDI,LDD,CPI,CPD,CPIR,CPDR: Set if BC<>0 at end of operation.

BCD Flags (H,N)
These bits are solely supposed to be used by the DAA instruction. The N flag signalizes if the previous operation has be an addition or substraction. The H flag indicates if the lower 4 bits exceeded the range from 0-0Fh. (For 16bit instructions: H indicates if the lower 12 bits exceeded the range from 0-0FFFh.)
After adding/subtracting two 8bit BCD values (0-99h) the DAA instruction can be used to convert the hexadecimal result in the A register (0-FFh) back to BCD format (0-99h). Note that DAA also requires the carry flag to be set correctly, and thus should not be used after INC A or DEC A.

Undocumented Flags (Bit 3,5)
The content of these undocumented bits is filled by garbage by all instructions that affect one or more of the normal flags (for more info read the chapter Garbage in Flag Register), the only way to read out these flags would be to copy the flags register onto the stack by using the PUSH AF instruction.
However, the existence of these bits makes the AF register a full 16bit register, so that for example the code sequence PUSH DE, POP AF, PUSH AF, POP HL would set HL=DE with all 16bits intact.


 Z80 Instruction Format

Commands and Parameters
Each instruction consists of a command, and optionally one or two parameters. Usually the leftmost parameter is modified by the operation when two parameters are specified.

Parameter Placeholders
The following placeholders are used in the following chapters:
  r      8bit  register A,B,C,D,E,H,L
  rr     16bit register BC, DE, HL/IX/IY, AF/SP   (as described)
  i      8bit  register A,B,C,D,E,IXH/IYH,IXL/IYL
  ii     16bit register IX,IY
  n      8bit  immediate 00-FFh                   (unless described else)
  nn     16bit immediate 0000-FFFFh
  d      8bit  signed offset -128..+127
  f      flag  condition nz,z,nc,c AND/OR po,pe,p,m  (as described)
  (..)   16bit pointer to byte/word in memory

Opcode Bytes
Each command (including parameters) consists of 1-4 bytes. The respective bytes are described in the following chapters. In some cases the register number or other parameters are encoded into some bits of the opcode, in that case the opcode is specified as "xx". Opcode prefix bytes "DD" (IX) and "FD" (IY) are abbreviated as "pD".

Clock Cycles
The clock cycle values in the following chapters specify the execution time of the instruction. For example, an 8-cycle instruction would take 2 microseconds on a CPU which is operated at 4MHz (8/4 ms). For conditional instructions two values are specified, for example, 17;10 means 17 cycles if condition true, and 10 cycles if false.
Note that in case that WAIT signals are sent to the CPU by the hardware then the execution may take longer.

Affected Flags
The instruction tables below are including a six character wide field for the six flags: Sign, Zero, Halfcarry, Parity/Overflow, N-Flag, and Carry (in that order). The meaning of the separate characters is:
  s    Indicates Signed result
  z    Indicates Zero
  h    Indicates Halfcarry
  o    Indicates Overflow
  p    Indicates Parity
  c    Indicates Carry
  -    Flag is not affected
  0    Flag is cleared
  1    Flag is set
  x    Flag is destroyed (unspecified)
  i    State of IFF2
  e    Indicates BC<>0 for LDX(R) and CPX(R), or B=0 for INX(R) and OUTX(R)


 Z80 Load Commands

8bit Load Commands
 Instruction    Opcode  Cycles Flags  Notes
 ld   r,r       xx           4 ------ r=r
 ld   i,i       pD xx        8 ------ i=i
 ld   r,n       xx nn        7 ------ r=n
 ld   i,n       pD xx nn    11 ------ i=n
 ld   r,(HL)    xx           7 ------ r=(HL)
 ld   r,(ii+d)  pD xx dd    19 ------ r=(ii+d)
 ld   (HL),r    7x           7 ------ (HL)=r
 ld   (ii+d),r  pD 7x dd    19 ------
 ld   (HL),n    36 nn       10 ------
 ld   (ii+d),n  pD 36 dd nn 19 ------
 ld   A,(BC)    0A           7 ------
 ld   A,(DE)    1A           7 ------
 ld   A,(nn)    3A nn nn    13 ------
 ld   (BC),A    02           7 ------
 ld   (DE),A    12           7 ------
 ld   (nn),A    32 nn nn    13 ------
 ld   A,I       ED 57        9 sz0i0- A=I  ;Interrupt Register
 ld   A,R       ED 5F        9 sz0i0- A=R  ;Refresh Register
 ld   I,A       ED 47        9 ------
 ld   R,A       ED 4F        9 ------

16bit Load Commands
 Instruction    Opcode  Cycles Flags  Notes
 ld   rr,nn     x1 nn nn    10 ------ rr=nn    ;rr may be BC,DE,HL or SP
 ld   ii,nn     pD 21 nn nn 13 ------ ii=nn
 ld   HL,(nn)   2A nn nn    16 ------ HL=(nn)
 ld   ii,(nn)   pD 2A nn nn 20 ------ ii=(nn)
 ld   rr,(nn)   ED xB nn nn 20 ------ rr=(nn)  ;rr may be BC,DE,HL or SP
 ld   (nn),HL   22 nn nn    16 ------ (nn)=HL
 ld   (nn),ii   pD 22 nn nn 20 ------ (nn)=ii
 ld   (nn),rr   ED x3 nn nn 20 ------ (nn)=rr  ;rr may be BC,DE,HL or SP
 ld   SP,HL     F9           6 ------ SP=HL
 ld   SP,ii     pD F9       10 ------ SP=ii
 push rr        x5          11 ------ SP=SP-2, (SP)=rr  ;rr may be BC,DE,HL,AF
 push ii        pD E5       15 ------ SP=SP-2, (SP)=ii
 pop  rr        x1          10 (-AF-) rr=(SP), SP=SP+2  ;rr may be BC,DE,HL,AF
 pop  ii        pD E1       14 ------ ii=(SP), SP=SP+2
 ex   DE,HL     EB           4 ------ exchange DE <--> HL
 ex   AF,AF     08           4 xxxxxx exchange AF <--> AF'
 exx            D9           4 ------ exchange BC,DE,HL <--> BC',DE',HL'
 ex   (SP),HL   E3          19 ------ exchange (SP) <--> HL
 ex   (SP),ii   pD E3       23 ------ exchange (SP) <--> ii

Blocktransfer
 Instruction    Opcode  Cycles Flags  Notes
 ldi            ED A0       16 --0e0- (DE)=(HL), HL=HL+1, DE=DE+1, BC=BC-1
 ldd            ED A8       16 --0e0- (DE)=(HL), HL=HL-1, DE=DE-1, BC=BC-1
 ldir           ED B0  bc*21-5 --0?0- ldi-repeat until BC=0
 lddr           ED B8  bc*21-5 --0?0- ldd-repeat until BC=0


 Z80 Arithmetic/Logical Commands

8bit Arithmetic/Logical Commands
 Instruction    Opcode  Cycles Flags  Notes
 daa            27           4 szxp-x decimal adjust akku
 cpl            2F           4 --1-1- A = A xor FF
 neg            ED 44        8 szho1c A = 00-A
 <arit>  r      xx           4 szhonc see below
 <arit>  i      pD xx        8 szhonc see below, UNDOCUMENTED
 <arit>  n      xx nn        7 szhonc see below
 <arit>  (HL)   xx           7 szhonc see below
 <arit>  (ii+d) pD xx dd    19 szhonc see below
 <cnt>   r      xx           4 szhon- see below
 <cnt>   i      pD xx        8 szhon- see below, UNDOCUMENTED
 <cnt>   (HL)   xx          11 szhon- see below
 <cnt>   (ii+d) pD xx dd    23 szhon- see below
 <logi>  r      xx           4 szhp00 see below
 <logi>  i      pD xx        8 szhp00 see below, UNDOCUMENTED
 <logi>  n      xx nn        7 szhp00 see below
 <logi>  (HL)   xx           7 szhp00 see below
 <logi>  (ii+d) pD xx dd    19 szhp00 see below
Arithmetic <arit> commands:
 add   A,op     see above 4-19 szho0c A=A+op
 adc   A,op     see above 4-19 szho0c A=A+op+cy
 sub   op       see above 4-19 szho1c A=A-op
 sbc   A,op     see above 4-19 szho1c A=A-op-cy
 cp    op       see above 4-19 szho1c compare, ie. VOID=A-op
Increment/Decrement <cnt> commands:
 inc   op       see above 4-23 szho0- op=op+1
 dec   op       see above 4-23 szho1- op=op-1
Logical <logi> commands:
 and   op       see above 4-19 sz1p00 A=A & op
 xor   op       see above 4-19 sz0p00 A=A XOR op
 or    op       see above 4-19 sz0p00 A=A | op

16bit Arithmetic Commands
 Instruction    Opcode  Cycles Flags  Notes
 add  HL,rr     x9          11 --h-0c HL = HL+rr    ;rr may be BC,DE,HL,SP
 add  ii,rr     pD x9       15 --h-0c ii = ii+rr    ;rr may be BC,DE,ii,SP (!)
 adc  HL,rr     ED xA       15 szho0c HL = HL+rr+cy ;rr may be BC,DE,HL,SP
 sbc  HL,rr     ED x2       15 szho1c HL = HL-rr-cy ;rr may be BC,DE,HL,SP
 inc  rr        x3           6 ------ rr = rr+1     ;rr may be BC,DE,HL,SP
 inc  ii        pD 23       10 ------ ii = ii+1
 dec  rr        xB           6 ------ rr = rr-1     ;rr may be BC,DE,HL,SP
 dec  ii        pD 2B       10 ------ ii = ii-1

Searchcommands
 Instruction    Opcode  Cycles Flags  Notes
 cpi            ED A1       16 szhe1- compare A-(HL), HL=HL+1, DE=DE+1, BC=BC-1
 cpd            ED A9       16 szhe1- compare A-(HL), HL=HL-1, DE=DE-1, BC=BC-1
 cpir           ED B1   x*21-5 szhe1- cpi-repeat until BC=0 or compare fits
 cpdr           ED B9   x*21-5 szhe1- cpd-repeat until BC=0 or compare fits


 Z80 Rotate/Shift and Singlebit Operations

Rotate and Shift Commands
 Instruction    Opcode  Cycles Flags  Notes
 rlca           07           4 --0-0c rotate akku left
 rla            17           4 --0-0c rotate akku left through carry
 rrca           0F           4 --0-0c rotate akku right
 rra            1F           4 --0-0c rotate akku right through carry
 rld            ED 6F       18 sz0p0- rotate left low digit of A through (HL)
 rrd            ED 67       18 sz0p0- rotate right low digit of A through (HL)
 <cmd> r        CB xx        8 sz0p0c see below
 <cmd> (HL)     CB xx       15 sz0p0c see below
 <cmd> (ii+d)   pD CB dd xx 23 sz0p0c see below
 <cmd> r,(ii+d) pD CB dd xx 23 sz0p0c see below, UNDOCUMENTED modify and load
Whereas <cmd> may be:
 rlc    rotate left
 rl     rotate left through carry
 rrc    rotate right
 rr     rotate right through carry
 sla    shift left arithmetic (b0=0)
 sll    UNDOCUMENTED shift left (b0=1)
 sra    shift right arithmetic (b7=b7)
 srl    shift right logical (b7=0)

Singlebit Operations
 Instruction    Opcode  Cycles Flags  Notes
 bit  n,r       CB xx        8 xz1x0- test bit n  ;n=0..7
 bit  n,(HL)    CB xx       12 xz1x0-
 bit  n,(ii+d)  pD CB dd xx 20 xz1x0-
 set  n,r       CB xx        8 ------ set bit n   ;n=0..7
 set  n,(HL)    CB xx       15 ------
 set  n,(ii+d)  pD CB dd xx 23 ------
 set r,n,(ii+d) pD CB dd xx 23 ------ UNDOCUMENTED set n,(ii+d) and ld r,(ii+d)
 res  n,r       CB xx        8 ------ reset bit n ;n=0..7
 res  n,(HL)    CB xx       15 ------
 res  n,(ii+d)  pD CB dd xx 23 ------
 res r,n,(ii+d) pD CB dd xx 23 ------ UNDOCUMENTED res n,(ii+d) and ld r,(ii+d)
 ccf            3F           4 --h-0c h=cy, cy=cy xor 1
 scf            37           4 --0-01 cy=1


 Z80 Jumpcommands & Interrupts

General Jump Commands
 Instruction    Opcode  Cycles Flags  Notes
 jp   nn        C3 nn nn    10 ------ jump to nn, ie. PC=nn
 jp   HL        E9           4 ------ jump to HL, ie. PC=HL
 jp   ii        pD E9        8 ------ jump to ii, ie. PC=ii
 jp   f,nn      xx nn nn 10;10 ------ jump to nn if nz,z,nc,c,po,pe,p,m
 jr   nn        18 dd       12 ------ relative jump to nn, ie. PC=PC+d
 jr   f,nn      xx dd     12;7 ------ relative jump to nn if nz,z,nc,c
 djnz nn        10 dd     13;8 ------ B=B-1 and relative jump to nn if B<>0
 call nn        CD nn nn    17 ------ call nn ie. SP=SP-2, (SP)=PC, PC=nn
 call f,nn      xx nn nn 17;10 ------ call nn if nz,z,nc,c,po,pe,p,m
 ret            C9          10 ------ pop PC ie. PC=(SP), SP=SP+2
 ret  f         xx        11;5 ------ pop PC if nz,z,nc,c,po,pe,p,m
 rst  n         xx          11 ------ call n  ;n=00,08,10,18,20,28,30,38
 nop            00           4 ------ no operation

Interrupt Related Commands
 Instruction    Opcode  Cycles Flags  Notes
 di             F3           4 ------ IFF1=0, IFF2=0  ;disable interrupts
 ei             FB           4 ------ IFF1=1, IFF2=1  ;enable interrupts
 im   0         ED 46        8 ------ read opcode from databus on interrupt
 im   1         ED 56        8 ------ execute call 0038h on interrupt
 im   2         ED 5E        8 ------ execute call (i*100h+databus) on int.
 halt           76         N*4 ------ repeat until interrupt occurs
 reti           ED 4D       14 ------ pop PC, IFF1=IFF2, ACK (ret from INT)
 retn           ED 45       14 ------ pop PC, IFF1=IFF2      (ret from NMI)
 </INT=LOW,IM=0,IFF1=1>  1+var ------ IFF1=0,IFF2=0, exec opcode from databus
 </INT=LOW,IM=1,IFF1=1>     12 ------ IFF1=0,IFF2=0, CALL 0038h
 </INT=LOW,IM=2,IFF1=1>     18 ------ IFF1=0,IFF2=0, CALL [I*100h+databus]
 </NMI=falling_edge>         ? ------ IFF1=0,        CALL 0066h


 Z80 I/O Commands
 Instruction    Opcode  Cycles Flags  Notes
 in   A,(n)     DB nn       11 ------ A=PORT(A*100h+n)
 in   r,(C)     ED xx       12 sz0p0- r=PORT(BC)
 in   (C)       ED 70       12 sz0p0- **undoc/illegal** VOID=PORT(BC)
 out  (n),A     D3 nn       11 ------ PORT(A*100h+n)=A
 out  (C),r     ED xx       12 ------ PORT(BC)=r
 out  (C),0     ED 71       12 ------ **undoc/illegal** PORT(BC)=00
 ini            ED A2       16 xexxxx MEM(HL)=PORT(BC), HL=HL+1, B=B-1
 ind            ED AA       16 xexxxx MEM(HL)=PORT(BC), HL=HL-1, B=B-1
 outi           ED A3       16 xexxxx B=B-1, PORT(BC)=MEM(HL), HL=HL+1
 outd           ED AB       16 xexxxx B=B-1, PORT(BC)=MEM(HL), HL=HL-1
 inir           ED B2   b*21-5 x1xxxx same than ini, repeat until b=0
 indr           ED BA   b*21-5 x1xxxx same than ind, repeat until b=0
 otir           ED B3   b*21-5 x1xxxx same than outi, repeat until b=0
 otdr           ED BB   b*21-5 x1xxxx same than outd, repeat until b=0


 Z80 Interrupts

Interrupt Flip-Flop (IFF1,IFF2)
The IFF1 flag is used to enable/disable INTs (maskable interrupts).
In a raw INT-based system, IFF2 is always having the same state than IFF1. However, in a NMI-based system the IFF2 flag is used to backup the recent IFF1 state prior to NMI execution, and may be used to restore IFF1 upon NMI completion by RETN opcode.
Beside for the above 'backup' function, IFF2 itself is having no effect. Neither IFF1 nor IFF2 affect NMIs which are always enabled.
The following opcodes/events are modifying IFF1 and/or IFF2:
  EI     IFF1=1, IFF2=1
  DI     IFF1=0, IFF2=0
  <INT>  IFF1=0, IFF2=0
  <NMI>  IFF1=0
  RETI   IFF1=IFF2
  RETN   IFF1=IFF2
When using the EI instruction, the new IFF state isn't applied until the next instruction has completed (this ensures that an interrupt handler which is using the sequence "EI, RET" may return to the main program before the next interrupt is executed).
Interrupts can be disabled by the DI instruction (IFF=0), and are additionally automatically each time when an interrupt is executed.

Interrupt Execution
An interrupt is executed when an interrupt is requested by the hardware, and IFF is set. Whenever both conditions are true, the interrupt is executed after the completion of the current opcode.
Note that repeated block commands (such like LDIR) can be interrupted also, the interrupt return address on the stack then points to the interrupted opcode, so that the instruction may continue as normal once the interrupt handler returns.

Interrupt Modes (IM 0,1,2)
The Z80 supports three interrupt modes which can be selected by IM 0, IM 1, and IM 2 instructions. The table below describes the respective operation and execution time in each mode.
  Mode  Cycles  Refresh  Operation
  0     1+var   0+var    IFF1=0,IFF2=0, read and execute opcode from databus
  1     12      1        IFF1=0,IFF2=0, CALL 0038h
  2     18      1        IFF1=0,IFF2=0, CALL [I*100h+databus]
Mode 0 requires an opcode to be output to the databus by external hardware, in case that no byte is output, and provided that the 'empty' databus is free of garbage, then the CPU might tend to read a value of FFh (opcode RST 38h, 11 cycles, 1 refresh) - the clock cycles (11+1), refresh cycles (1), and executed operation are then fully identical as in Mode 1.
Mode 1 interrupts always perform a CALL 0038h operation. The downside is that many systems may have ROM located at this address, making it impossible to hook the interrupt handler directly.

Mode 2 calls to a 16bit address which is read from a table in memory, the table pointer is calculated from the "I" register (initialized by LD I,A instruction) multiplied by 100h, plus an index byte which is read from the databus. The following trick may be used to gain stable results in Mode 2 even if no index byte is supplied on the databus: For example, set I=40h the origin of the table will be then at 4000h in memory. Now fill the entire area from 4000h to 4100h (101h bytes, including 4100h) by the value 41h. The CPU will then perform a CALL 4141h upon interrupt execution - regardless of whether the randomized index byte is an even or odd number.

Non-Maskable Interrupts (NMIs)
Unlike INTs, NMIs cannot be disabled by the CPU, ie. DI and EI instructions and the state of IFF1 and IFF2 do not have effect on NMIs. The NMI handler address is fixed at 0066h, regardless of the interrupt mode (IM). Upon NMI execution, IFF1 is cleared (disabeling maskable INTs - NMIs remain enabled, which may result in nested execution if the handler does not return before next NMI is requested). IFF2 remains unchanged, thus containing the most recent state of IFF1, which may be used to restore IFF1 if the NMI handler returns by RETN instruction.
Execution time for NMIs is unknown (?).

RETN (return from NMI and restore IFF1)
Intended to return from NMI and to restore the old IFF1 state (assuming the old state was IFF1/IFF2 both set or both cleared).

RETI (return from INT with external acknowledge)
Intended to return from INT and to notify peripherals about completion of the INT handler, the Z80 itself doesn't send any such acknowledge signal (instead, peripherals like Z80-PIO or Z80-SIO must decode the databus during /M1 cycles, and identify the opcode sequence EDh,4Fh as RETI). Aside from such external handling, internally, RETI is exactly same as RETN, and, like RETN it does set IFF1=IFF2 (though in case of RETI this is a dirt effect without practical use; within INT handlers IFF1 and IFF2 are always both zero, or when EI was used both set). Recommended methods to return from INT are: EI+RETI (when needing the external acknowledge), or EI+RET (faster).


 Z80 Meaningless and Duplicated Opcodes

Mirrored Instructions
NEG (ED44) is mirrored to ED4C,54,5C,64,6C,74,7C.
RETN (ED45) is mirrored to ED55,65,75.
RETI (ED4D) is mirrored to ED5D,6D,7D.

Mirrored IM Instructions
IM 0,X,1,2 (ED46,4E,56,5E) are mirrored to ED66,6E,76,7E.
Whereas IM X is an undocumented mirrored instruction itself which appears to be identical to either IM 0 or IM 1 instruction (?).

Duplicated LD HL Instructions
LD (nn),HL (opcode 22NNNN) is mirrored to ED63NNNN.
LD HL,(nn) (opcode 2ANNNN) is mirrored to ED6BNNNN.
Unlike the other instructions in this chapter, these two opcodes are officially documented. The clock/refresh cycles for the mirrored instructions are then 20/2 instead of 16/1 as for the native 8080 instructions.

Mirrored BIT N,(ii+d) Instructions
Unlike as for RES and SET, the BIT instruction does not support a third operand, ie. DD or FD prefixes cannot be used on a BIT N,r instruction in order to produce a BIT r,N,(ii+d) instruction. When attempting this, the 'r' operand is ignored, and the resulting instruction is identical to BIT N,(ii+d).
Except that, not tested yet, maybe undocumented flags are then read from 'r' instead of from ii+d(?).

Non-Functional Opcodes
The following opcodes behave much like the NOP instruction.
ED00-3F, ED77, ED7F, ED80-9F, EDA4-A7, EDAC-AF, EDB4-B7, EDBC-BF, EDC0-FF.
The execution time for these opcodes is 8 clock cycles, 2 refresh cycles.
Note that some of these opcodes appear to be used for additional instructions by the R800 CPU in newer turbo R (MSX) models.

Ignored DD and FD Prefixes
In some cases, DD-prefixes (IX) and FD-prefixes (IY) may be ignored by the CPU. This happens when using one (or more) of the above prefixes prior to instructions that already contain an ED, DD, or FD prefix, or prior to any instructions that do not support IX, IY, IXL, IXH, IYL, IYH operands. In such cases, 4 clock cycles and 1 refresh cycle are counted for each ignored prefix byte.


 Z80 Garbage in Flag Register

Nocash Z80-flags description
This chapter describes the undocumented Z80 flags (bit 3 and 5 of the Flags Register), these flags are affected by ALL instructions that modify one or more of the normal flags - all OTHER instructions do NOT affect the undocumented flags.

For some instructions, the content of some flags has been officially documented as 'destroyed', indicating that the flags contain garbage, the exact garbage calculation for these instructions will be described here also.

All information below just for curiosity. Keep in mind that Z80 compatible CPUs (or emulators) may not supply identical results, so that it wouldn't be a good idea to use these flags in any programs (not that they could be very useful anyways).

Normal Behaviour for Undocumented Flags
In most cases, undocumented flags are copied from the Bit 3 and Bit 5 of the result byte. That is "A AND 28h" for:
  RLD; CPL; RLCA; RLA; LD A,I; ADD OP; ADC OP; XOR OP; AND OP;
  RRD; NEG; RRCA; RRA; LD A,R; SUB OP; SBC OP; OR OP ; DAA.
When other operands than A may be modified, "OP AND 28h" for:
  RLC OP; RL OP; SLA OP; SLL OP; INC OP; IN OP,(C);
  RRC OP; RR OP; SRA OP; SRL OP; DEC OP
For 16bit instructions flags are calculated as "RR AND 2800h":
  ADD RR,XX; ADC RR,XX; SBC RR,XX.

Slightly Special Undocumented Flags
For 'CP OP' flags are calculated as "OP AND 28h", that is the unmodified operand, and NOT the internally calculated result of the comparision.
For 'SCF' and 'CCF' flags are calculated as "(A OR F) AND 28h", ie. the flags remain set if they have been previously set.
For 'BIT N,R' flags are calculated as "OP AND 28h", additionally the P-Flag is set to the same value than the Z-Flag (ie. the Parity of "OP AND MASK"), and the S-flag is set to "OP AND MASK AND 80h".

Fatal MEMPTR Undocumented Flags
For 'BIT N,(HL)' the P- and S-flags are set as for BIT N,R, but the undocumented flags are calculated as "MEMPTR AND 2800h", for more info about MEMPTR read on below.
The same applies to 'BIT N,(ii+d)', but the result is less unpredictable because the instruction sets MEMPTR=ii+d, so that undocumented flags are "<ii+d> AND 2800h".

Memory Block Command Undocumented Flags
For LDI, LDD, LDIR, LDDR, undocumented flags are "((A+DATA) AND 08h) + ((A+DATA) AND 02h)*10h".
For CPI, CPD, CPIR, CPDR, undocumented flags are "((A-DATA-FLG_H) AND 08h) + ((A-DATA-FLG_H) AND 02h)*10h", whereas the CPU first calculates A-DATA, and then internally subtracts the resulting H-flag from the result.

Chaotic I/O Block Command Flags
The INI, IND, INIR, INDR, OUTI, OUTD, OTIR, OTDR instructions are doing a lot of obscure things, to simplify the description a placeholder called DUMMY is used in the formulas.
  DUMMY = "REG_C+DATA+1"    ;for INI/INIR
  DUMMY = "REG_C+DATA-1"    ;for IND/INDR
  DUMMY = "REG_L+DATA"      ;for OUTI,OUTD,OTIR,OTDR
  FLG_C = Carry  of above "DUMMY" calculation
  FLG_H = Carry  of above "DUMMY" calculation (same as FLG_C)
  FLG_N = Sign   of "DATA"
  FLG_P = Parity of "REG_B XOR (DUMMY AND 07h)"
  FLG_S = Sign   of "REG_B"
  UNDOC = Bit3,5 of "REG_B AND 28h"
The above registers L and B are meant to contain the new values which are already incremented/decremented by the instruction.
Note that the official docs mis-described the N-Flag as set, and the C-Flag as not affected.

DAA Flags
Addition (if N was 0):
  FLG_H = (OLD_A AND 0Fh) > 09h
  FLG_C = Carry of result
Subtraction (if N was 1):
  FLG_H = (NEW_A AND 0Fh) > 09h
  FLG_C = OLD_CARRY OR (OLD_A>99h)
For both addition and subtraction, N remains unmodified, and S, Z, P contain "Sign", Zero, and Parity of result (A). Undocumented flags are set to (A AND 28h) as normal.

Mis-documented Flags
For all XOR/OR: H=N=C=0, and for all AND: H=1, N=C=0, unlike described else in Z80 docs. Also note C,N flag description bug for I/O block commands (see above).

Internal MEMPTR Register
This is an internal Z80 register, modified by some instructions, and usually completely hidden to the user, except that Bit 11 and Bit 13 can be read out at a later time by BIT N,(HL) instructions.
The following list specifies the resulting content of the MEMPTR register caused by the respective instructions.
  Content Instruction
  A*100h  LD (xx),A               ;xx=BC,DE,nn
  xx+1    LD A,(xx)               ;xx=BC,DE,nn
  nn+1    LD (nn),rr; LD rr,(nn)  ;rr=BC,DE,HL,IX,IY
  rr      EX (SP),rr              ;rr=HL,IX,IY (MEMPTR=new value of rr)
  rr+1    ADD/ADC/SBC rr,xx       ;rr=HL,IX,IY (MEMPTR=old value of rr+1)
  HL+1    RLD and RRD
  dest    JP nn; CALL nn; JR nn   ;dest=nn
  dest    JP f,nn; CALL f,nn      ;regardless of condition true/false
  dest    RET; RETI; RETN         ;dest=value read from (sp)
  dest    RET f; JR f,nn; DJNZ nn ;only if condition=true
  00XX    RST n
  adr+1   IN A,(n)                ;adr=A*100h+n, memptr=A*100h+n+1
  bc+1    IN r,(BC); OUT (BC),r   ;adr=bc
  ii+d    All instructions with operand (ii+d)
Also the following might or might not affect MEMPTR, not tested yet:
  OUT (N),A and block commands LDXX, CPXX, INXX, OUTXX
  and probably interrupts in IM 0, 1, 2
All other commands do not affect the MEMPTR register - this includes all instructions with operand (HL), all PUSH and POP instructions, not executed conditionals JR f,d, DJNZ d, RET f (ie. with condition=false), and the JP HL/IX/IY jump instructions.


 Z80 Compatibility

The Z80 CPU is (almost) fully backwards compatible to older 8080 and 8085 CPUs.

Instruction Format
The Z80 syntax simplifies the chaotic 8080/8085 syntax. For example, Z80 uses the command "LD" for all load instructions, 8080/8085 used various different commands depending on whether the operands are 8bit registers, 16bit registers, memory pointers, and/or an immediates. However, these changes apply to the source code only - the generated binary code is identical for both CPUs.

Parity/Overflow Flag
The Z80 CPU uses Bit 2 of the flag register as Overflow flag for arithmetic instructions, and as Parity flag for other instructions. 8080/8085 CPUs are always using this bit as Parity flag for both arithmetic and non-arithmetic instructions.

Z80 Specific Instructions
The following instructions are available for Z80 CPUs only, but not for older 8080/8085 CPUs:
All CB-prefixed opcodes (most Shift/Rotate, all BIT/SET/RES commands).
All ED-prefixed opcodes (various instructions, and all block commands).
All DD/FD-prefixed opcodes (registers IX and IY).
As well as DJNZ nn; JR nn; JR f,nn; EX AF,AF; and EXX.

8085 Specific Instructions
The 8085 instruction set includes two specific opcodes in addition to the 8080 instruction set, used to control 8085-specifc interrupts and SID and SOD input/output signals. These opcodes, RIM (20h) and SIM (30h), are not supported by Z80/8080 CPUs.

Z80 vs Z80A
Both Z80 and Z80A are including the same instruction set, the only difference is the supported clock frequency (Z80 = max 2.5MHz, Z80A = max 4MHz).

NEC-780 vs Zilog-Z80
These CPUs are apparently fully compatible to each other, including for undocumented flags and undocumented opcodes.


 Z80 Pin-Outs

         _____   _____
        |     |_|     |
    A11 |1          40| A10
    A12 |2          39| A9
    A13 |3          38| A8
    A14 |4          37| A7
    A15 |5          36| A6
    CLK |6          35| A5
     D4 |7          34| A4
     D3 |8          33| A3
     D5 |9          32| A2
     D6 |10   Z80   31| A1
    VCC |11   CPU   30| A0
     D2 |12         29| GND
     D7 |13         28| /RFSH
     D0 |14         27| /M1
     D1 |15         26| /RST
   /INT |16         25| /BUSRQ
   /NMI |17         24| /WAIT
  /HALT |18         23| /BUSAK
  /MREQ |19         22| /WR
  /IORQ |20         21| /RD
        |_____________|



 Z80 Local Usage

CPU and System Clock Overview
The ZX81 is equipped with a NEC D780C-1 CPU (fully compatible to Zilog Z80 CPUs, including for undocumented opcodes & flags). The CPU is operated at 3.25 MHz (generated by a 6.5MHz oscillator with very high tolerance - my ZX81 appears to be rather ticking at 3.33MHz even though the oscillator DOES have the value 6.5 printed onto it) the available CPU time is reduced when the display is enabled. The ZX81 produces a /WAIT signal for the duration while a NMI is requested, otherwise the CPU runs free of waitstates.

Vectors, Registers and Memory Overview
The INT, NMI, and RST vectors are located in ROM and cannot be changed by software. The AF' register (ie. EX AF,AF), the IX register, and IR register (interrupt/refresh) are reserved for video and should be normally not used, except in FAST mode. Attempting to execute program code stored at addresses 8000h-FFFFh is interpreted as video data (if the opcodes Bit 6 was set), if so, a NOP opcode is forwarded to the CPU.

Using Machine Code
A machine code program is called by using the USR function, for example by using the BASIC expression "LET N=USR <startaddress>", startaddress must be a decimal number (hex numbers are not supported). If the machine code program returns to BASIC, the content of the BC register is used as return value. For info about storing binary data/machine code, see Memory chapter.


 BASIC Interpreter

The BASIC Interpreter
BASIC Editor
BASIC Commands
BASIC Functions
BASIC Error Codes
BASIC Programs and Variables (ZX81/Spectrum)
BASIC Programs and Variables (ZX80)


 BASIC Editor

This chapter explains ZX81 keyboard modes and key-combinations. The table with all keywords/characters assigned to each key can be found here:
ZX80/ZX81 Keyboard Assignment
Spectrum Keyboard Assignment

Special Keys
  Key        Name      Expl.                      Equivalent/emulated PC Key
  Ret        NEWLINE   Confirm Input                            Enter
  Space      BREAK     Stops the program!                       Space
  Shift+0    RUBOUT    Deletes a character                      Backspace
  Shift+1    EDIT      Edits [>] Cursor selected line           -
  Shift+5/8  <CURSOR>  Moves Cursor left/right in input buffer  Cursor Keys
  Shift+7/6  <CURSOR>  Moves [>] Cursor up/down in listing      Cursor Keys
  Shift+9    GRAPHICS  Switches to [G] Graphics Cursor Mode     Alt
  Shift+Ret  FUNCTION  Switches to [F] Function Cursor Mode     Control
Beside for these general special keys, all ZX81 keys are behaving kinda special, depending on various input modes as shown below.

[K] Cursor - Keyword Mode (Expecting Keywords and/or Line Numbers)
This mode is automatically used at the beginning of each line (and after THEN).
At the beginning of line, a line number can be entered by using 0-9 keys. Character keys are interpreted as command keywords (for example, P=PRINT). Some commands must be entered as SHIFT combinations (for example, SHIFT+F=FAST).
Entering a line number without keyword deletes the specified line.

[L] Cursor - Letter Mode (Expecting Operands or Functions)
This mode is automatically selected after entering a keyword in [K] mode.
Allows to enter expressions, such like "HELLO" or 1234.5678. When SHIFT is held down, functions/operators such like +,-,*,>=,OR,AND can be entered (for example, SHIFT+W=OR), note that typing "OR" as characters by pressing "O", "R" will result in syntax errors.

[F] Cursor - Function Mode (Expecting Functions) (ZX81 only)
This mode is entered by pressing SHIFT+ENTER from inside of [L] Mode, allowing to enter functions such like L=USR, U=CHR$ which cannot be entered in [L] mode directly.

[G] Cursor - Graphics Mode (Inverted Text and Block Graphics) (ZX81 only)
This mode is entered by pressing SHIFT+9 from inside of [L] Mode, allowing to enter inverted characters (including inverted SPACE).
When keeping SHIFT held down, all combinations of Block Graphics can be entered (for example, SHIFT+1=Dot-in-upper-left).

[>] Cursor/Symbol - Currently selected line
This cursor isn't part of the input line. Instead, it is displayed in the program listing (if any) in the upper screen area. The cursor can be moved by UP/DOWN key combinations (SHIFT+7/6), causing the listing to be scrolled if necessary.
The EDIT key combination (SHIFT+1) copies the line that is currently selected by the [>] Cursor to the input buffer (overwriting any old input).

[S] Symbol - Syntax Error
This is not actually a cursor, when entering an incorrect line, the [S] symbol appears, indicating the fault position, and prompting the user to repair the problem.

Note
The ZX80/ZX81 allows only one command per line. On the Spectrum, multiple commands can be separated by ":" colons.


 BASIC Commands

BEEP n,n (Spectrum only)
Plays a sound via ULA port FEh. The 1st parameter specifies the duration in seconds (or fractions thereof, like 0.5), the 2nd parameter specifies the pitch in semitones above middle C (or below thereof, if negative). The waveform is software generated, so the program becomes paused during beep.

BORDER n (Spectrum only)
Changes the screen border color (0..7).

CLEAR [n]
Erases all variables and frees up memory. The optional parameter changes RAMTOP (supported on Spectrum only).

CIRCLE x,y,radius (Spectrum only)
Draws a circle.

CLS
Clears the screen.

CONT (called CONTINUE on ZX80/Spectrum)
Works much like GOTO, continues the program in the current line, or in the next line if program has been halted by STOP.

COPY (ZX81/Spectrum only)
Sends a screen copy to the printer (if connected). Only topmost 32x22 characters are printed, ie. excluding bottommost 2 lines.

DATA item[,item[,item]] (Spectrum only)
Contains expressions (numbers, variables, or quoted strings) which may be loaded into variables via READ command. DATA is just a data definition, not an executable command (trying to execute DATA lines acts as REM lines).

DEF FN v(v1,v2,etc)=expression (Spectrum only)
Creates a user defined function. The function name must be a single letter expression (like x or a$). For example, "DEF FN s(a)=a*a" would cause "FN s(n)" to act identical as "n**2".

DELETE [n],[m] (Timex TS2068/TC2068 only)
Deletes BASIC line numbers n..m. Defaults to from 1st/to last line if n/m are omitted. Note: To delete a single line, simply enter its line number and press Enter.

DIM b(n1,...,nk) or DIM b$(n1,...,nk)
Defines dimensions for an array. All values (or strings) in the array are reset to 0, (or SPACE-filled for strings/characters). The lastmost dimension of a character array indicates the string length. For example, DIM A$(x,y,z) could be accessed either as three-dimensional character array A$(x,y,z), or as two-dimensional string array A$(x,y) with LENGTH=z.
The ZX80 supports only one-dimensional numeric arrays, ie. no multi-dimensional arrays, and no string/character arrays.

DRAW x,y[,angle] (Spectrum only)
Draws a line (from most recent DRAW/PLOT/CIRCLE position) to x,y. The 3rd parameter allows to draw curved lines (angle should be below +/-2*PI).

FAST (ZX81 only)
Switches to FAST mode, the CPU focuses on program execution only (approx. 4 times faster) and the display becomes black until end of program, or until switching to SLOW mode. Also, the screen becomes temporarily enabled during PAUSE or INPUT periods.
The ZX80 is always operating in 'FAST' mode.

FOR b=x TO y [STEP z]
Defines the begin and range of a FOR-NEXT loop, the default STEP is +1. The ZX80 doesn't support the STEP operand.

GOSUB n (called GO SUB on ZX80/Spectrum)
Saves RETURN address on GOSUB stack, and jumps to the specified line number, see GOTO for details.

GOTO n (called GO TO on ZX80/Spectrum)
Jumps to the specified line number. An immediate, a variable and/or other expression may be used as line number, such like "GOTO A*10+230".

IF x THEN command
Executes command if x<>0 (eg. if an expression like "A=0" is true). The ZX does not understand "THEN n" as alias for "THEN GOTO n".

INK/PAPER/FLASH/BRIGHT n (affects VRAM attributes) (Spectrum only)
INVERSE/OVER n (affects VRAM bitmap) (Spectrum only)
Changes parameters for following printing/drawing operations (like PRINT, INPUT, PLOT, DRAW, CIRCLE).
INK/PAPER can be a color number in range 0..7, or 8=transparent, FLASH/BRIGHT can be 0=off, 1=on, or 8=transparent, transparent means that the old attribute value in VRAM is left unchanged. INVERSE/OVER can be 0=off, or 1=on. INVERSE inverts the character data when printing, or causes pixels to become 0 when drawing. OVER causes the new character/pixel data to be XORed with the old VRAM bitmap content.
INK/PAPER/FLASH/BRIGHT/INVERSE/OVER can be used as "standalone" commands (affecting all following printing/drawing commands) (eg. "INVERSE 1"), or as "temporary" parameters inserted between a printing/drawing command and its parameters (affecting only that command) (eg. "PLOT INVERSE 1;x,y" is equivalent to UNPLOT x,y).
Note: Another way to use colors in printing is to use "hidden" tokens, like typing PRINT"Hello {INK RED}Test", where {INK RED} is entered as EXT+SHIFT+2. The {INK RED} token isn't displayed in text form when listing/editing the program, it just causes the following characters to be drawn in red (this method was supported only by the original 48K BASIC, and was discontinued in 128 BASIC).

INPUT ["text",] [LINE] v
Prompts the user to enter a value (or string) to be assigned to v. In FAST mode, the display is temporarily re-enabled. Program is breaked if user enters STOP as first character.
Strings must enclosed in quotes (otherwise any text input is treated as a variable). Adding the LINE keyword allows to enter strings without quotes (Spectrum only). "text" is an optional message displayed on the screen (Spectrum only).

LET v=e
Assigns e to the variable v. The ZX does not understand "v=e" as alias for "LET v=e". In case that v is a string-fragment, ie. A$(..TO..), the TO-length remains unchanged, and truncated/space padded string is assigned to the TO-area.

LIST [n]
Lists the program on the screen. The default starting line number is 0. Use CONT to continue if program does not fit onto screen.

LLIST [n] (ZX81/Spectrum only)
Lists the program to the printer.

LOAD f
Loads a memory image from cassette. When specifying an empty filename, ie. LOAD "", the first encountered file is loaded. Text display is suspended during loading, white lines are shown on the screen when receiving cassette signals. The program and all variables that are already in memory become overwritten.
The memory image contains all memory from 4009h up to (4014h), ie. most of the system area, the actual BASIC program, the video memory, and any defined variables. The program is automatically started, continuing at the next line number, if it has been saved from <inside> of a running program.
The ZX80 does not support filenames, just type LOAD and hit NEWLINE.

LPRINT (ZX81/Spectrum only)
Works much like PRINT, but outputs to the printer. AT may be used to specify a horizontal position inside of the currently printed line.
Bug: Numbers with more than one zero in their fractional portion aren't displayed properly, for example, LPRINT 0.00001 does output "0.0XYZ1" (this bug appears only with the ZX81/TS1000 BIOS, it's fixed in the TS1500, Lambda 8300, and Spectrum BIOSes).

NEW
Restarts BASIC. The program, all variables, and all further memory up to RAMTOP are erased. In the ZX80 this is equivalent to CALL 0.

NEXT b
Adjusts b as specified in the corresponding FOR command, and (if the target condition has not been reached or exceeded) loops back to the line following to the FOR command. The ZX does not understand "NEXT" as alias for "NEXT b".

ON ERR command (Timex TS2068/TC2068 only)
Defines error handling. command can be combined GOTO, CONT, or RESET.

OUT n,m (Spectrum only)
Writes a byte (m) to the specified 16bit I/O address (n).

PAUSE n (ZX81/Spectrum only)
Pauses the program for n/50 seconds (assuming 50Hz display refresh rate) or until the user hits a key, and displays the screen for the duration of this period (even if display was disabled by FAST mode). The highest allowed value for n is 32767. Note that hitting the BREAK key (SPACE) stops the program rather than continuing it. On the Spectrum, PAUSE 0 waits endless (until a key is pressed).

PLAY string1[,string2[,string3]] (Spectrum 128/+2/+2A/+3 only)
Plays up to three sounds (on the three PSG sound channels). The strings are composed of following characters:
  c-b  Play note within current octave     (c,d,e,f,g,a,b)
  C-B  Play note within next higher octave (C,D,E,F,G,A,B)
  $    Flattens note following it (can be more flattened by $$ or $$$ etc.)
  #    Sharpens note following it (can be more sharpened by ## or ### etc.)
  &    Play silence (a pause of same length as normal c-b and C-B notes)
  _    Tied notes (eg. "5_7c" = crotchet c and a minim c tied together)
  On   Set current octave (0..8)
  Tn   Set tempo (60..240)
  n    Set length of notes (1..12) (use "Nn" if preceeded by other digits)
  Nn   Separates two numbers (eg. octave=1,length=2 --> O1N2, instead of O12)
  Vn   Set volume level (0..15) (0=Off, 15=Loudest)
  Wn   Set volume envelope type (0..7) (bit0=ATTACK, bit1=HOLD, bit2=REPEAT)
  Xn   Set volume envelope frequency (0..65535) (affects all 3 channels)
  U    Turn on volume envelope in any string
  (    Set loop start (if none specified: default is begin of string)
  )    Jump to loop start (repeat 2x, multiple brackets can repeat 4x,8x,16x..)
  ))   Jump to loop starts (repeats endless if only ONE loop start was defined)
  ! !  Enclose a comment
  H    Stop PLAY command (stops all 3 channels, including endless looping ones)
  Mn   Configure PSG channels (bit0..2=Tone A..C, bit3..5=Noise A..C)
  Yn   Turn on MIDI channel (1..16)
  Zn   Send MIDI programming/configuration code (numbers depend on hardware)
Although the sounds are hardware generated, the program gets paused until the PLAY command has finished, which is making the feature rather useless.

PLOT x,y (ZX81/Spectrum only)
ZX81: Draws a black dot (of 4x4 pixels, ie. one quarter of a 8x8 pixel character) at the specified position by using graphics characters. Spectrum: Draws a single pixel. For both ZX81/Spectrum: The origin (0,0) is at the lower left of the screen, excluding the bottom-most two character lines.

POKE m,n
Writes the byte n at address m into memory, both must be decimal values (the ZX does not recognize hexadecimal numbers).

PRINT ...
Displays the operand(s) - if any - on the screen. Possible operands are:
  a) nothing
  b) a numeric expression, displayed either as
       normal decimal number (if in range 10^-5 .. 10^13)
       otherwise as nnEmm indicating nn*10^mm
     leading zeroes are displayed only if the first digit after
     the "." is not zero, ie. 0.3 and .03 are displayed as such.
  c) a string. undefined characters are displayed as question marks.
  d) AT y,x; - moves the PRINT position to the specified screen
     location, 0,0 is upper left.
  e) TAB n; - moves the PRINT position to the specified horizontal
     location. If this is to the left of the current location, then
     the vertical position is incremented.
Operands may be separate by semicolon ";" (next operand displayed directly at current position) or comma "," (next operand displayed at next TAB 0 or TAB 16 position).
Upon completion, the print position is moved to the begin of the next line (unless expression was terminated by semicolon or comma).
An error may be generated if memory or screen is full, if so, CONT may be used to clear the screen and to continue the program.

RAND [n] (called RANDOMISE on ZX80, and RANDOMIZE on Spectrum)
Initializes the random generators seed (n=1..65535). When n is zero (the default), the current frame counter is used as seed.

READ v1[,v2[,v3]] (Spectrum only)
Loads expressions from DATA lines into the specified variable(s). See also: RESTORE.

REM ...
Defines a remark, ignored by the program. The comment field may be also mis-used to define binary machine code inside of a basic program.
However, the comment may not contain a NEWLINE character (76h) - ie. a HALT instruction (opcode 76h) may not be used, as well as any other opcodes with 76h as parameter byte, such like JP 4176h, or LD A,76h, etc.

RESET (Timex TS2068/TC2068 only)
Intended to reset connected peripherals. No idea if/which/how any peripherals do actually support this command.

RESTORE [n] (Spectrum only)
Sets the first line number where following READ commands start searching for DATA statements, the default is the begin of the program.

RETURN
Returns to the line following to the most recently executed GOSUB command.

RUN [n]
Clears all variables and jumps to the specified line number. The default is the first line of the program. See GOTO for details.

SAVE f
Saves the system area, the program, the video memory, and any defined variables to cassette. The filename f must be a 1-127 characters string, which may not include inverted characters. The text display is suspended for the duration of saving, white lines are displayed indicating the cassette signals.
When using SAVE from inside of the program, then the program is automatically continued at the following line. Note that the GOSUB stack is not saved, so that SAVE should not be used from inside of a sub-routine.
The ZX80 does not support filenames, just enter SAVE without parameter.

SCROLL (ZX81 only)
Moves the display upwards, and inserts a blank line at the bottom.
Note: The blank line is totally empty, in VRAM it is defined as a single HALT opcode, without any SPACE characters.

SLOW (ZX81 only)
Switches to SLOW mode (approx. 4 times slower than FAST mode). The text screen is displayed, and the program is executed during vertical blanking periods only. The ZX81 is initially operating in SLOW mode.
The ZX80 does not support SLOW and FAST commands, and it is always operating in FAST mode.

SOUND index,data [;index,data] [...] [;index,data] (Timex TS2068/TC2068 only)
Allows to write one or more values to the PSG sound registers. Unlike the PLAY/BEEP commands, it does just start (or stop or change) sounds, but doesn't pause program execution.

SPECTRUM (Spectrum 128/+2/+2A/+3 only)
Switches to Spectrum 48K backwards-compatible mode. Disables the new I/O ports, disables the Extended System area, re-enables the old color codes like {INK RED}, re-enables the hotkey mode like R=RUN, etc. The BASIC program (if any) is kept in memory (eg. allows to load 48K program from Spectrum +3 disk drive, and then run it in 48K mode).

STOP
Stops the program. User may enter CONT to continue in following line.

UNPLOT x,y (ZX81 only)
Same as PLOT, but drawing a white dot instead of a black dot.

Additional commands...

FORMAT (Spectrum only)
       FORMAT LINE baudrate    ;default=9600
       FORMAT LPRINT target    ;"R"=RS232, "C"=Centronics
       FORMAT LPRINT tokenmode ;"E"=Expanded, "U"=Unexpanded tokens
     COPY   - normal screen copy to printer
     COPY EXP - expanded screen copy to printer (recurse BRIGHT attributes)
     COPY EXP INVERSE - expanded inverse screen copy to printer
CAT (Spectrum only)
MOVE (Spectrum only)
ERASE (Spectrum only)
OPEN # (Spectrum only)
CLOSE # (Spectrum only)
MERGE (Spectrum only)
VERIFY (Spectrum only)
Spectrum Disc and Tape Commands


 BASIC Functions

Note that all ZX81 and Spectrum 48K functions must be entered in form of SHIFT or FUNCTION mode key combinations. For example, type SHIFT+ENTER,B for the INKEY$ keyword - attempting to enter I,N,K,E,Y,$ would result in a syntax error. The same applies for multi-character operands such like ">=" and "**".
For the ZX80 and Spectrum 128/+2/+2A/+3 it is vice-versa, and all functions must be entered character by character. Also, ZX80 function parameters must be put into () brackets.

ABS n
Returns the unsigned value of n.

ATTR (y,x) (Spectrum only)
Returns the characters 8bit color attritbute stored in VRAM attribute area.

BIN n (Spectrum only)
Treats the following digits as binary number (max 16bit).

CHR$ n
Returns character n. (Converts a number into a single-character string.)

CODE string
Returns the character number of the first character (or 0 if string is empty). Opposite of CHR$.

FN name(n,n,etc) (Spectrum only)
Executes a user defined function (which can be defined with DEF FN).

FREE (Timex TS2068/TC2068 only)
Returns amount of free memory in bytes.

IN n (Spectrum only)
Returns a byte read from the specified 16bit I/O address.

INKEY$ (ZX81/Spectrum only)
Returns the character of the currently pressed key, or "" if none pressed.

LEN string (ZX81/Spectrum only)
Returns the length of the string.

PEEK n
Returns a byte read from decimal (!) memory address n. The ZX does not recognize hexadecimal numbers.

POINT (x,y) (Spectrum only)
Returns the pixel color (0 or 1) stored in VRAM bitmap area.

RND (for ZX80: RND n)
Returns a random number in range 0 to 0.99999... (ZX81/Spectrum), or 1 to n (ZX80).

SCREEN$ (y,x) (Spectrum only) ;returns character at location x,y
Returns character that matches the 8x8 pixel cell in VRAM bitmap area.

SGN n (ZX81/Spectrum only)
Returns the sign (-1, 0, or +1) of n.

STICK n,m (Timex TS2068/TC2068 only)
Returns joystick status (via PSG register 14). n=1 returns 1bit button status, n=2 returns 4bit direction status. m selects the joystick port (1 or 2).

STR$ n
Returns n converted into a string.

TL$ string (-ZX80- only)
Returns string with leftmost character truncated. Equivalent to the ZX81 expression 'string(2 TO)'.

USR n
Calls a machine program in memory at address n, and returns the value of the BC register when (if) the machine code program returns. The ZX does not recognize hexadecimal addresses.

VAL string (ZX81/Spectrum only)
Converts the string into a value, stops the program if failed.

VAL$ string (Spectrum only)
This is a subfunction of the VAL function, it removes bounding quotes from the string, but without actually doing a string-to-value conversion. No idea if it's useful for anything.

Floating Point related functions (ZX81/Spectrum only)
  ACS n         Arcus Cosinus.
  ASN n         Arcus Sinus.
  ATN n         Arcus Tangens.
  COS n         Cosinus.
  EXP n         Exponent e^n.
  INT n         Returns an integer value (rounded DOWN).
  LN n          Logarhytmn of n (base e).
  PI            3.14159265...
  SIN n         Sinus.
  SQR n         Square root.
  TAN n         Tangens.

Numeric operations
  Op.   Prio. Expl.
  a+b   6     Addition
  a-b   6     Subtraction
  a*b   8     Multiplication
  a/b   8     Division
  -a    9     Invert sign
  a**b  10    a^b
Logical operations (Returns 1 if true, 0 if false)
  Op.   Prio. Expl.
  a=b   5     Equal
  a>b   5     Greater than
  a<b   5     Less than
  a<=b  5     Less or equal
  a>=b  5     Greater or equal
  a<>b  5     Not equal
Special Numeric/Logical operations
  Op.         Prio. Expl.
  a OR b      2     b<>0: Returns 1;          b=0: Returns a.
  a[$] AND b  3     b<>0: Returns a (or a$);  b=0: Returns 0 (or "").
  NOT b       4     b<>0: Returns 0;          b=0: Returns 1.
Priority for indexing (DIM) and string-fragments (TO) is 12. Priority for all functions is 12.

String ( [first] TO [last] ) Operand
Used to specify a fragement of a string from first character to last character. The default values are: first=1, and last=LEN.
May be used either in source and destination of string operations, eg.:
  LET A$(5 TO 7) = "***"    ;overwrites 5th-7th character of A$
  LET A$ = B$(TO 4)         ;copies 1st-4th character of B$ to A$


 BASIC Error Codes

Commandline Errors
When entering a line incorrectly, the [S] symbol (Syntax Error) is displayed at the fault location, prompting the user to correct the problem.

ZX81 Execution Errors
Otherwise, when errors occur during program execution, the following error codes are displayed.
  0  No Error, program terminated succesfully.
  1  Encountered NEXT without FOR.
  2  Undefined Variable.
  3  DIM Index out of range. May also return error B if 16bit exceeded.
  4  Memory full.
  5  Screen full. Type CONT to continue with cleared screen.
  6  Arithmetic Overflow, a value is greater than 10^38.
  7  Encountered RETURN without GOSUB.
  8  Attempted to use INPUT as command (without line number).
  9  Program halted by STOP command, may continue by CONT.
  A  Function with bad argument.
  B  Integer out of range.
  C  Failed VAL function.
  D  Program aborted by BREAK (SPACE KEY), or entered STOP at begin of INPUT.
  E  Not used.
  F  Attempted to SAVE a file with empty "" name.
Errors are always displayed as ERR/LINE indicating the line number which caused the error.

ZX80 Execution Errors
  0  program completed (or breaked), no error
  1  NEXT without FOR
  2  variable not defined
  3  dimension out of range (DIM)
  4  memory or gosub stack full
  5  (???)
  6  integer overflow (value exceeds -32768..+32767)
  7  RETURN without GOSUB
  8  attempted INPUT but program isn't running
  9  STOP command


 BASIC Programs and Variables (ZX81/Spectrum)

BASIC Program Line Structure
  Bytes  Expl.
  2      Line Number  (MSB,LSB) (!)
  2      Line Length  (LSB,MSB)
  LEN-1  Text
  1      Newline      (ZX81=76h) (Spectrum=0Dh)
The following are used in the Text area: 00h-3Fh and 80h-BFh for normal and inverted characters, C0h-FFh and 40h-42h for keywords, 7Eh for values, and 76h indicates the end of the line. On the Spectrum, characters are in ASCII, keywords are different, values are 0Eh, and end of line is 0Dh.
Values are duplicated in program lines: First, as normal text, ie. as entered by the user (this field is ignored during execution, still it must exist, but might be always set to "0" if desired). And second, invented by the code 7Eh, as pre-calculated 5-byte FLOAT number.
An immediate thus occupies at least 7 bytes. However, there are some grindy methods to save memory, such like: PI-PI defines 0 in only 3 bytes, VAL "9" defines 9 in only 4 bytes, etc.

BASIC One-Letter Variables
  Bytes  Expl.
  1      60h + Letter (5bit)
  5      Floating point number (exponent/value)

BASIC Multi-Character Variables
  Bytes  Expl.
  1      A0h + First Letter       (5bit)
  NN     00h + Further Characters (6bit)
  1      80h + Last Character     (6bit)
  5      Floating point number (exponent/value)

BASIC Numeric Array
  Bytes  Expl.
  1      80h + Letter (5bit)
  2      Total Length of all following data
  1      Number of Dimensions
  D*2    Range of each of the Dimension(s)
  N*5    Values

BASIC FOR-NEXT Counter
  Bytes  Expl.
  1      E0h + Letter  (5bit)
  5      Current Value (float)
  5      Target Value (float)
  5      Step
  2      Loop Linenumber (MSB,LSB) (!)

BASIC String
  Bytes  Expl.
  1      40h + Letter     (5bit)
  2      String Length    (0 if empty)
  LEN    Character String (none if empty)

BASIC Character Array
  Bytes  Expl.
  1      C0h + Letter     (5bit)
  2      Total Length of all following data
  1      Number of Dimensions
  D*2    Range of each of the Dimension(s)
  N*1    Characters

5-byte FLOAT numbers
  1      Exponent 81h +/- location of most significant bit
  4      Sign-Bit and Value excluding most significant bit
For example, the value 7FFFh would be defined as such: In this case the MSB is Bit 14 (ie. 4000h) the exponent byte must be set to 14+81h (=8Fh). The sign bit is zero (Bit 7 of first byte), and the remaining bits, in this case Bit 13-0 (3FFFh), are shifted to the left as much as possible (so that highest bit is located in Bit 6 of first byte) the four bytes must be: 7F FE 00 00.

5-byte NON-FLOAT numbers with Exponent=00h (Spectrum only / not ZX81)
  1      Exponent 00h (indicates that it is a non-float value)
  1      Sign    (00h=Positive, FFh=Negative)
  2      Integer (0000h..FFFFh) (or -0001h..-FFFFh when negative)
  1      Unused  (00h)
BUG: In some cases (not sure when?), -10000h is also stored as non-float, and, in some further cases (not sure when?) that causes problems.

5-byte FLOAT bug
There's some inaccuracy when converting text strings (eg. user input) to float numbers. For example, the correct value for 0.25 would be 7F 00 00 00 00, however, the ZX81/TS1000 BIOSes do convert it to 7E 7F FF FF FF (accidently rounded down), the TS1500 BIOS does unsuccessfully try to fix that problem by converting it to 7F 00 00 00 01 (accidently rounded up). This is resulting in incompatibility, for example,
  10 FOR I = 0 TO 1 STEP .25
  20 PRINT I
  30 NEXT I
The ZX81/TS1000 does correctly stop at 1 being displayed as last number, whilst the "improved" TS1500 does incorrectly stop at 0.75. Mind that immediates are pre-compiled at time of entering the program code, so a program SAVEd on a ZX81, may work exactly as on the ZX81 even when LOADing it on a TS1500 (unless it contains conversions being done at runtime, like VAL ".25" or VAL A$).
  1 IF VAL"0.25" < 1/4 THEN PRINT "TOO SMALL (ZX81 OR TS1000 OR SPECTRUM)"
  2 IF VAL"0.25" > 1/4 THEN PRINT "TOO BIG (TS1500 OR LAMBDA)"
  3 IF VAL"0.25" = 1/4 THEN PRINT "MATCH (WHOOPS)"
Typing IF VAL"0.25" forces conversion on runtime (typing IF 0.25 would be converted when entering the program, and stay so even when SAVing it and LOADing it on another computer). Interestingly, the TOO SMALL variants, do also (incorrectly) display a MATCH in line 3.



 BASIC Programs and Variables (ZX80)

BASIC Program Line Structure
  Bytes  Expl.
  2      Line Number  (MSB,LSB) (!)
  ...    Text
  1      Newline      (76h)
The following are used in the Text area: 00-3F and 80-BF for normal and inverted characters, D4-FF for keywords, and 76 indicates the end of the line. A couple of 'special' characters such like ';()*+/-' are stored in form as keywords rather than as normal character codes.
Function names and values in program lines are stored as normal text.

BASIC One-Letter Variables
  Bytes  Expl.
  1      60h + Letter (5bit)
  2      Signed Integer

BASIC Multi-Character Variables
  Bytes  Expl.
  1      40h + First Letter       (5bit)
  NN     00h + Further Characters (6bit)
  1      80h + Last Character     (6bit)
  2      Signed Integer

BASIC Numeric Array
  Bytes    Expl.
  1        A0h + Letter (5bit)
  1        Range (Counted from 0 to N)
  (N+1)*2  Signed Integers

BASIC FOR-NEXT Counter
  Bytes  Expl.
  1      E0h + Letter  (5bit)
  2      Current Value
  2      Target Value
  2      Loop Linenumber (normal LSB,MSB for ZX80)

BASIC String
  Bytes  Expl.
  1      80h + Letter     (5bit)
  ...    Character String (none if empty)
  1      Ending Quotes    (01h)

BASIC Character/String Arrays
None such - ZX80 supports numeric arrays only.


 About this document

This document has been extracted from the no$zx emulator/debugger.
Copyright 2001-2012 by Martin Korth.

Homepage
http://nocash.emubase.de/zx.htm

ZX Specifications
This document in .TXT and .HTML format:
- http://nocash.emubase.de/zxdocs.txt
- http://nocash.emubase.de/zxdocs.htm