Graphics Issue 21 Contents Mind Games

helpline



Andrew Hewson

Masking interruptions is worth the effort

Andrew Hewson suggests ways of producing continuous sound

A INTERESTING point about the Spectrum is raised by Michael Rodway, who writes: I have noticed that a small number of commercial games produce sound continuously while the program is running. How is that effect achieved?

The simplest technique is to call a sound routine at various points during the execution of the program but usually that creates an intermittent effect because it is difficult to arrange the calls to the routine so that they occur at regular intervals. The only alternative is to use the interrupt system built into the Z-80 microprocessor. This is a difficult task but the result is usually worth the effort involved.

The interrupt system, as its name implies, is a mechanism by which the processor can be diverted from the task it is undertaking to do something more urgent. When the processor has completed the more urgent task it resumes its previous job. All computers have an interrupt system and on more sophisticated machines they allow the computer to maintain a priority system for all the tasks it has in hand at any moment.

A low-priority task is deferred in favour of a more urgent one which, in turn, is suspended while a yet more urgent job is started, and so on. As each task is completed the machine reverts to a previous, half-completed job.

Two types of interrupts can occur in the Z-80. The more important is the Non-Maskable Interrupt - or NMI - so-called because the programmer is unable to prevent, or mask, the Z-80 from responding to such an interrupt when it occurs. The NMI is not of much interest to most Spectrum programmers because itis designed for use with external hardware.

The maskable interrupt is of more interest because it can be switched off, so that all subsequent interrupts are ignored, and because the Z-80 can beset to respond to it in any one of three modes. In mode zero the processor waits until it is directed by an external device to execute a routine somewhere in memory. In the Spectrum there is no provision for the use of this mode.

The Spectrum is designed to operate in mode one all the time. In that mode the Z-80 saves the contents of the program counter - so that it can later resume the task it was doing - and jumps to location 38h - 56 decimal. The Spectrum hardware forces the jump to be made by generating an interrupt 50 times per second.

The ROM routine at this address updates the clock by incrementing the FRAMES systems variable held at 23672 to 23674 and then scans the keyboard to see if a key is being pressed. If so, the appropriate code is stored in the system variables and various flags are altered. On completion of the keyboard routine all the registers are restored to their previous values and the processor resumes its previous task.

The final interrupt mode, mode two, is the most powerful. If the Z-80 is interrupted while in that mode it saves the program counter as previously and jumps to an address determined partly by software and partly by the hardware which caused the interrupt. To be more specific, the Z-80 takes the value in the I register and the value generated by the external hardware and calculates an address from them as:

Address = 256 * I register + hardware

It then looks at the contents of the calculated address and the subsequent one and calculates a new address from them as:

New address = Address + 256 (Address + 1)

It then jumps to the new address. Thus if the I register contained 143 and the hardware generated the value 27, the Z-80 would look at the contents of

256 * 143 + 27 = 36635 and 36636

If those two locations contained 137 and 93 respectively - i.e., PEEK 36635 = 137 and PEEK 36636 = 93-it would then jump to location:

137 + 256 * 93 = 23945

The rather complicated procedure is known, aptly, as an indirect jump and is not so cumbersome as it seems at first sight. It enables as many as 128 types of devices to be attached to the Z-80, each type generating its own value to contribute to the indirect jump address. The programmer then constructs a table containing 128 addresses, each held in two bytes and each pointing to the routine which handles a particular device. The I register points to the location of the beginning of the entire 256-byte table.

The Spectrum does not use interrupt mode two and the designers have used the I register for their own purposes connected with the scanning system for the TV screen. The initialising routine in ROM puts the value 63 into that register and if any value between 64 and 127 is loaded instead, interference occurs with the TV display.

10 DATA 62,0,237,71,201
20 FOR I=23296 TO 23300
30 READ A
40 POKE I,A
50 NEXT I
60 FOR I=0 TO 255
70 POKE 23297,1
80 RANDOMIZE USR 23296
90 PRINT AT 0,0;
100 FOR J=32 TO 127
110 PRINT CHR$ J;
120 NEXT J
130 PAUSE 9999
140 NEXT I

Table 1. A Spectrum program to demonstrate the interference
on the TV display caused by setting the I register in the range
64 to 127.

Decimal     Assembler       Comment  
               Code
237 86         im 1            Reset interrupt mode one
62 63          ld a,63         Reset I register
237 71         ld i,a          to 63
201            ret             End
62 24          ld a,24         Load FRAMES system variable
50 118 92      ld (23670),a    with the instruction
62 133         ld a,133        to jump back
50 119 92      ld (23671),a    to 23549
62 195         ld a,195        Load last three bytes
50 253 91      ld (23549),a    of printer buffer
62 39          ld a,39         with instruction
50 254 91      ld (23550),a    to jump back
62 91          ld a,91         to
50 255 91      ld (23551),a    23335
62 37          ld a,37         Load I register
237 71         ld i,a          with 37
237 94         im 2            Set interrupt mode two
201            ret             End
58 120 92      ld a,(23672)    Transfer clock value
50 116 89      ld (22900),a    to an attribute square
255            rst 56          Call clock and keyboard routines
201            ret             End

Table 2. Three Spectrum routines to be loaded into the printer
buffer which together allow the user to select either interrupt
mode one or two.

To see the effect, load and run the program listed in table one. The program puts a short machine code routine into the printer buffer which loads a value into the I register. The Basic loop increments the value loaded into I and shows the effect on some PRINTed characters.

I know of no cure for the interference on the screen and would be interested to hear from anyone who might be able to suggest one. The effect is to restrict the area of memory which a Spectrum programmer may use to hold the pointer for a mode two interrupt. A few moments' calculation shows that any address between 16384 and 32767 inclusive will require the I register to be set to some value in the range 64 to 127. As that is the entire area of RAM available in the 16K Spectrum, it would appear that interrupt mode two cannot be used in these machines.

10 FOR I=23296 TO 23548
20 INPUT J
30 PRINT I,J
40 POKE I,J
50 NEXT I

Table 3. A Spectrum program for
loading machine code in decimal
into the printer buffer.

There is a way round the problem. If there is no hardware connected to the 16K Spectrum, the low byte of the indirect address will take the value 255 by default. We can safely set the I register to any value in the range 0 to 63 inclusive. Suppose we choose to set the I register to 37. When an interrupt occurs in mode two the Z-80 will then look at the two addresses:

256 * 37 + 255 = 9727 and 9728

to determine the location to which it should jump. The two addresses are, of course, in ROM and they contain 118 and 92 respectively so that the Z-80 will jump to:

118 + 256 * 92 = 23670

That location is in the system variables area and usually is used to hold the SEED for the generation of the next random number. A relative jump instruction to the printer buffer, or anywhere else close by, can be placed here instead of the seed without great loss. Provided the user takes care not to execute a RANDOMIZE command the instruction will not be over-written.

There are several other values which could be placed in the I register which would cause an indirect jump to an address in RAM but for my purposes here 37 is the most suitable. Table two lists three machine code routines designed to be loaded into the printer buffer which switch between interrupt modes one and two and make use of the technique. They can be loaded using the decimal loader given in table three.

Decimal     Assembler       Comment  
               Code
221 29         push ix         Save registers
245            push af
197            push be
213            push de
229            push hl
17 10 00       ld d,10         Determines frequency * duration
33 132 1       ld hl,388       Determines duration
205 181 3      call 949        Call beeper routine
243            di              Disable interrupt
255            pop hl          Restore registers
209            pop de          in reverse order
193            pop bc
241            pop af
221 225        pp ix
205 191 2      call 703        Call clock and keyboard
201            ret             End

Table 4. A Spectrum routine to be added to the routine in
table two starting at 23336 which calls the beeper routine.

The first routine, loaded at 23296, restores interrupt mode and re-sets the value in the I register to 63 if required. The second routine establishes the chain linking the SEED system variable, first to the end of the printer buffer and then to the address of the beginning of the third routine. It then alters the value in the I register to 37 and sets mode two.

Thus when the routine is executed the Spectrum no longer jumps to the ROM clock and keyboard routine, at address 50, 50 times per second. Instead it jumps via SEED and the end of the printer buffer to the third routine.

The third routine, just to prove that the system works, transfers from FRAMES into the attributes area, causing a single square on the screen to change colour rapidly. A call is then made to the ROM clock and keyboard routine so that the Spectrum continues to function correctly.

The third routine can be extended to generate continuous sound by adding a suitable call to the beeper routine which is located in the ROM at address 949. An example is given in table four. Note that the contents of many of the registers are saved by pushing them on to the stack before the beeper routine is called.

That step is necessary because the beeper routine uses those registers and the routine which was interrupted will have almost certainly been using them for its own purposes.

For more complex interrupt service routines it may be necessary to save all the registers and some of the system variables as well.

The tone produced by the beeper routine is determined by the contents of de and hl. The values shown give a note about two octaves above middle C. All completion of the routine by popping the values in reverse order from the stack.

A number of readers have written asking for an opinion on the Microdrive now that it has finally appeared.

A typical question is from Tom Pendlebury, who asks: Is it possible to pick out a single item from a Microdrive file or must the whole file be read into memory?

The software in the interface which controls the Microdrive is fairly elementary so that the use of a Microdrive file is restricted in a number of important ways. For example, if a file already exists on a cartridge it is possible only to read it, not to write to it, so that corrections can be made only by reading the file into memory, making the correction, erasing the Microdrive copy and then writing the corrected file back on to the cartridge.

Similarly, it is not possible to read a single item, as Pendlebury asks, although it is possible to read part of a file so that if the information required is near the beginning only the first part needs to be read.

There is no doubt that the Microdrive is another Sinclair value-for-money innovation and I am sure that software will be developed to extend the facilities provided.

To help set the ball rolling I have written the Basic Bootstrap program listed in table five, which allows the user to select whichever program he requires from the Microdrive cartridges.

1   LOAD *"m";d;b$(1): STOP
10  PRINT "Microdrive Bootstrap Program"
20  INPUT "Enter microdrive number ";d
30  IF d<I OR d>8 THEN GO TO 20
100 REM Create temporary catalogue
110 OPEN # 4;"m";d;"catalogue"
120 CAT #4;d
130 CLOSE #4
200 REM Read catalogue
210 DIM a$(50,10)
220 OPEN #4;"m";d;"catalogue"
230 FOR i=1 TO 50
240 INPUT #4;a$(i)
250 IF a$(i,1)>= "0" AND a$(i,1)<= "9" THEN GO TO 300
260 NEXT i
300 REM list and erase catalogue
310 CLOSE #4
320 LET n=i-2
330 PRINT "Drive # ";d;" Cartridge  ";a$(1): PRINT
340 FOR T=3 TO n
350 PRINT a$(i)
360 NEXT i
370 ERASE "m";d;"catalogue"
400 REM Choose file
410 DIM b$(1,10)
420 INPUT "Enter file name ";b$(1)
430 FOR T=3 TO n
440 IF b$(l) = a$(i) THEN GO TO 500
450 NEXT i
460 INPUT "No such file. Try again (y/n)  ?";c$
470 IF c$(1)="y" THEN GO TO 420
480 STOP
500 REM Load file
510 CLS
520 GO TO 1

Table 5. A Spectrum Bootstrap to load programs from
a Microdrive determined by the user. Save the Bootstrap
on Microdrive 1 by entering:
SAVE *"m";1;"run" LINE 10.

The program generates a catalogue of the files on the cartridge selected by the user, reads the catalogue into memory and erases the cartridge copy, so that an up-to-date copy is always generated.

The Bootstrap program then loads the program selected by the user.

This rather convoluted approach is used because it would appear that the Microdrive does not maintain a catalogue of the files on each cartridge, so that the catalogue must be re-generated each time the program is used. I may be incorrect about this conclusion-I have not had the Microdrive long enough to be certain. If I am wrong I shall no doubt be deluged with letters pointing out the error.



Graphics Issue 21 Contents Mind Games

Sinclair User
December 1983