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
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
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
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
Handling errors in ZX BASIC is done via a Restart call to
address 8, i.e.
ERROR RST 0008
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).
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
The example below (starting address is 23296) uses the ZX
printer buffer to store the new channel record and the new
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.