48k Spectrum
Technical Information
I/O Streams

The Spectrum has (or rather had, at the time) a sophisticated and general method of dealing with different I/O devices, based on Streams and Channels.

The software part of the equation (i.e. the code that sends/receives data) is the Stream and the hardware part is the Channel (i.e. the physical hardware). The channel belongs to a particular I/O device (such as a printer) and the stream is simply a flow of data into or out of a program. Streams are identified by a number in the range 0 to 15.

Using Channels and Streams

To read data from the stream, the INPUT command is used, in the format INPUT #s;'list'. For example, INPUT #0;A;B;A$ will read data from stream 0 and store it in numeric variables A, B and string A$.
To write data to the stream, the PRINT command is used, in the format PRINT #s;'list'. For example, PRINT #0,TOT;A$ will send the numeric variable TOT and the string A$ to the stream 0.

Before the streams can be used they must be initialised by opening the channel, i.e.

OPEN #s,c Where s is the stream number to open and c is a string specifying the channel it is being associated with. The build in devices (keyboard, screen, printer) are allocated strings K, S and P respectively. Thus, to start using the ZX Printer the command: OPEN #3, "P" would be used.

Streams 0 to 3 default to the Keyboard, Keyboard, Screen and Printer respectively.

To stop talking to a device, and to close communications correctly, the CLOSE command is used. The format is CLOSE #s.

Other Commands

LIST #s,n This will list the BASIC program from line n (or all if omitted) to stream s.
INKEY #s Returns a single character from the stream.


As described in system variables, the information that defines each channel is stored in the channel information area, starting at CHANS and ending in PROG-2. Each channel has a separate record in the following format:
Address Size in Bytes Details
N 2 address of output routine
n+2 2 address of input routine
n+4 1 channel code letter

The full address list is as follows:

Keyboard Channel Record
CHANS 2 address of lower screen printout routine
CHANS+2 2 address of keyboard input routine
CHANS+4 1 K channel K identifier

Screen Channel Record
CHANS+5 2 address of screen printout routine CHANS+6 2 address of error routine CHANS+9 1 S channel S identifier Edit Buffer Channel Record
CHANS+10 2 address of buffer input routine
CHANS+12 2 address of error routine
CHANS+14 1 R channel R identifier

ZX Printer Buffer Channel Record
CHANS+15 2 address of Printer routine
CHANS+17 2 address of error routine
CHANS+18 1 P channel P identifier

The output routine is the address of a machine code routine that must accept character codes passed to it in the A register. The input routine must return data in the form of character codes and signal data availability by setting the carry flag. Setting both the carry and zero flags to zero signifies no data available.

Handling errors in ZX BASIC is done via a Restart call to address 8, i.e.

DEFB errocode

The OPEN command cannot be used with the R (edit buffer) channel. Note that the record format changes when a Microdrive or Interface 1 is used.

At address STRMS (23568), for 38 bytes, the association of streams and channels is stored. Each entry in the table is one more than the number of memory locations that the channel record is offset from the start of the channel information area. As sixteen channels are available, 32 bytes are required. The remaining 6 bytes are used for channels R, S and K (stream numbers 255, 254, 253).

New Devices

For simple devices the IN and OUT command can be used. However, for more complicated devices, such as a full size printer, this is not feasible and a different method is required.
One method is the POKE the CHANS+15 (as described above) with the address of a custom print routine. This will make commands such as LPRINT and LLIST, as well as any other I/O command that is directed to the ZX Printer (stream P), be re-directed to the custom routine. The new routine, in theory, has to accept data from the A register. Problems occur when non printable character codes (e.g. tab, ink codes) are sent. The disadvantages of this method are that it removes an I/O device and some channels (e.g. keyboard) cannot be modified (the INPUT statement resets the K channel every time it is used).

A new channel can be created anywhere in memory, however if it is stored above the INPUT workspace (WORKSP system variable) then the CURCHL system variable, the current channel, will be altered as memory is changed when the INPUT command is used. Resulting in a crash. It is best to create channel records below this.

The example below (starting address is 23296) uses the ZX printer buffer to store the new channel record and the new I/O routine.

Chanrec DEFB 0 ;lsb/msb of output address
DEFB 11 ;lsb/msb of input address
DEFB "E" ;channel identifier
Outdrv LD BC,254
OUT (C),A ;send contents of A - data - to port 254
indrv RST 8 ;error restart
DEFB 18 ;invalid device error code

In the real world, the channel record would be added to the channel information area.

Expansion Connector
Built in I/O
ULA Output
ULA Input
System Variables
ZX Basic
I/O Streams
Video Display
Tape, Sound, Printer
Interface 1
©2002 ZeDeX82