                     Opening, Closing



          There are two commands on your Spectrum

            which are for the Microdrive alone.

              Richard Taylor says "So what?"



A subject completely neglected in the Sinclair ZX Spectrum

programming manual is the operation of the OPEN # and

CLOSE # commands. Sinclair claims that these commands are

designed for use with its Microdrive. Much of their power,

however, can be realised without a Microdrive. To illu-

strate this untapped power I include, as an example, a

routine which will emulate the Play command found in one

form or another on the majority of computers with sound

generators. It saves you the time-consuming job of conver-

ting music into the complicated decimal numbers required by

Sinclair's rather feeble BEEP command.

  The way in which your Spectrum communicated with the

devices in the outside world, namely the screen, keyboard

and printer, is complicated but extremely powerful. From

page 165 of the Basic manual you will find a memory allot-

ment called the channel information area, sandwiched

between the system variables and the Basic program area in

the memory map.

  This area holds information about the input/output

devices connected to the system. This memory section can be

broken down into smaller units, each unit holding infor-

mation about a particular I/O device. This is termed a

channel.

  The Spectrum has four channels, denoted S, P, K and R.

Channel S is an abbreviation for the screen and, as you

might expect, handles output to the display. Channel P

stands for printer and handles output to this device, if

connected. If you do not own a printer then this infor-

mation is still stored but obviously not used. Channel K

stands for keyboard but handles both input and output. It

inputs from the keyboard but also outputs to the bottom of

the screen. This is the area where lines are entered and

edited.

  Channel R is not directly accessible to the user since it

is used by the Spectrum for its own internal purposes. Now

here's where the powerful bit comes in. For instance, when

you use the PRINT command the output goes through channel S

to the main part of the screen. You can, however, change

their behaviour by cautious use of the OPEN# command which

can make its output go through any other channel.

  In addition to channels there are things termed streams

which are associated with a particular channel. There are

16 streams, each one represented by a unique number from 0

to 15 inclusive. When you use the PRINT command its output

goes through stream 2 which directs it towards channel S.

The trick is to make stream 2 point to a different channel,

not channel S. This is where the OPEN# command comes in.

  This instruction has two arguments, the first is numeric

and the second is a string type. The first one tells the

computer which stream you are altering and the latter one

gives the single character abbreviation of the channel you

want that stream to succeedingly point to. For example,

type:

        OPEN #2,"k"

  This will make all PRINT statements output through

channel K to the lower part of the screen. Now, to test

this type:

        PRINT "hello": PAUSE 0

  The PAUSE 0 command prevents the production of an error

report until you press a key, as this would immediately

obliterate the printing. You will also find that the LIST

command also uses stream 2 so it tries to list the program

in the lower part of the screen, but this is not particu-

larly succesful. Printer owners may find it useful to write

printer programs with output initially going to the screen

while still using the LPRINT or LLIST commands by entering:

        OPEN #3,"s"

Stream 3 similarly points to channel P and is used by

LPRINT and LLIST. When you are positive that the program is

debugged you can type:-

        OPEN #3,"p"

  This will revert stream 3 back to normal. This is a

useful technique since it saves a good deal of time and

printer paper. At switch on, only four of the 16 streams

are used or opened.

      Stream 0    #0    Points to channel K

      Stream 1    #1    Points to channel K

      Stream 2    #2    Points to channel S

      Stream 3    #3    Points to channel P

  The Spectrum operating system is protected against your

upsetting the way in which the editor works - the routine

which allows you to enter or edit lines in a program - and

the automatic lister. Whatever is done to the streams,

automatic listings will appear in the main part of the

screen and lines will be entered or edited via the keyboard

in the lower part of the screen. A stream which does not

point to any channel at all is termed closed.



 Closing down a stream



The CLOSE # command's single numeric argument denotes the

number of the stream which you want to close. The command

makes that stream point to what it did on power-up, inclu-

ding nowhere at all. As you can see, you cannot actually

close the first four streams.

  For instance, in the last example, instead of typing

        OPEN #3,"p"

you could have simply typed

        CLOSE #3

which is certainly a much neater way. In PRINT, LPRINT,

LIST, LLIST, INPUT and INKEY$ commands you can alter the

stream that is used by the command. For example, PRINT

usually outputs on stream 2, its default stream. If you

type:

        PRINT #0;"Hello": PAUSE 0

this will perform the same function as the first example

did. The hash - # - symbol indicates to the computer that

you wish to change the stream, and must be followed by an

integer value between 0 and 15 inclusive. Again, the

PAUSE 0 is to prevent the word being immediately destroyed

by the production of an error report. Printing in stream 0

gives you access to the previously forbidden lower two

lines of the display. LPRINT #2 is the same as PRINT and

PRINT #3 is the same as LPRINT.

  You cannot use streams which are closed; that is,

        PRINT #12;"a"

will produce error "O Invalid stream". You can, however,

easily open them using the OPEN # statement. The INPUT

command uses a default of stream 0. Although it is possible

to change the stream used by this command, only a stream

pointing to channel K can be used. Any other does not allow

input from the keyboard, which is obviously essential. If

you do try, then the error "J Invalid I/O device" will

result. INKEY$ works in a similar way.

  As you can see, the use of streams in I/O statements, the

OPEN # command and the CLOSE # are very poswerful. Perhaps

the most useful thing is the ability to create new channels

and make previously unused streams point to them. This

method is used in the Play example.

  The machine code for Play is held in a line 1 REM state-

ment. This is unusual since most Spectrum machine code is

stored above RAMTOP but this necessitates a different

version for each memory size of machine. By storing the

machine code in a REM statement this problem is eliminated

and so this program will run on both 16K and 48K machines.

[It also means that this routine depends on the location of

the program in memory and therefore does not work well with

the Interface 1, but since it messes about with streams,

that combination probably wouldn't have been reliable

anyway.]

[This was followed by two paragraphs explaining how to

enter and debug the routine. None of this was particularly

exceptional, so I've omitted the code loader and memory

printer, and only provided the resulting REM statement

one-liner - it's on the TZX as "Play". We return to the

article where it starts describing its use:]

  To initialise the new channel a

        RANDOMIZE USR 23829

instruction is needed at the beginning of any program

which uses Play. Now everytime you print through stream 4

- PRINT #4 - the Play routine will be called. It expects a

string of characters which describe the tune to be played.

This mainly consists of notes represented by the letters

a to g. This, like the other parameters, can be in upper or

lower case, it does not matter. If you want to play a sharp

note then prefix the appropriate note with a hash charac-

ter, for example:

         RANDOMIZE USR 23829: PRINT #4;"#ab"

This plays A sharp and then B. It is possible to change the

octave by incorporating an exclamation mark followed by a

number from 0 to 9. Middle C - represented by the number 0

in a BEEP command - is in octave 5. Only the middle few

octaves are any good for producing music, as the others are

so low in frequency that you can hear the individual clicks

or so high that they can hardly be heard at all. The dura-

tion of the notes is controlled by the asterisk symbol,

also followed by a number between 0 and 9 since there are

10 definable durations. They are defined as in table 1.

____________________________________



 Table 1.



 Duration number   Length (seconds)

        0               0.05

        1               0.1

        2               0.2

        3               0.25

        4               0.3

        5               0.5

        6               0.75

        7               1

        8               1.5

        9               2

____________________________________



  You can change these durations if you wish, using the

short program given in listing 4 [and on the TZX, including

the Play REM statement, as "Durations"]. It will first

prompt you for the duration number - 0 to 9 - you want to

change. You will then be prompted for the new duration

value in seconds. Anything from 0 to 10 is legal.

  After using the RANDOMIZE USR 23829, the octave number is

set to 5, the central octave. The duration is set to 4,

which is initially 0.3 seconds. An example of the routine's

use is given in listing 5 [which is on the TZX as "Demo"].

  You will find that the machine code REM statement will

only partly list and then stop, preventing the rest of the

listing. To circumvent this behaviour, type:

          POKE 23660,10

and do not deliberately try to list the first line.

  The information for each channel occupies five bytes.

The first two bytes tell the computer the address of the

routine that channel should use for output. This normally

points to a routine in ROM. The next two bytes point to the

address of the routine used for input. In all but channel

K, this points to a routine which will produce error J. In

channel K, however, this points to a routine which calcu-

lates which key is being pressed. The remaining byte is the

code of the character which represents that channel.

Program 6 ["Channels" on the TZX] prints information on all

four channels.



 Output routine addresses



Notice how channels K, S and P all have the same output

routine address. [No, the program does not actually print

this information.] The difference between each channel is

shown by flags, in the system variables, which are conditi-

oned in a certain way for each individual channel. After

all, outputting to the two halves of the screen or to the

printer is very similar except for a few small details. The

stream information is held in the system variables between

locations 23568 and 23605 inclusive.

  Each stream occupies two bytes. Since there are 16

streams you might expect this area to be 32 bytes long, but

instead it is 38 bytes in length. This is because there are

three extra streams which are not available to the user but

are utilised by the editor and automatic listing routines

to make sure that their output goes to the right place.

  Anyway, each of these two bytes contains a relative

displacement, from the byte before the channel information

area, to the channel that is to be used. If the displace-

ment is 0 then the stream is closed. The Play routine works

by setting up five bytes of information for a new channel

just at the beginning of the REM statement. This is made to

be pointed to by stream 4.

  The computer unwittingly thinks that it is just printing

to an ordinary channel in the proper information area. The

potential applications of this are very powerful.