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.