The Joy of Using Interrupts
Response to Part One of this series has been quite gratifying so far. However, from what I’ve heard, it seems that a little “fine tuning” of the format is in order. Essentially, we’ll do fewer things in each installment stretch the series out longer, and give a little more attention to detail. This will give those new to the subject a little more help and time to catch-up. It also means that we won’t cover as many things this time as promised, but it will all get done, eventually.
I hope readers who own or have access to a TS-2040 printer have tried out the demonstrator program. If not, it would be worthwhile to do so before reading further. Now back to the questions.
6 How does the demonstrator work?
Pretty well! Seriously, let’s first look at what the demonstrator sets up in memory, and then we will see how it all works together. Understanding this description will require a bit of knowledge of machine code, but only a bit. The demonstrator has been written to be understood by as wide an audience as possible. (That audience will also need a little persistence, though)
10 REM IM2 Demonstration Program
20 REM Causes a Copy-Screen WhenBREAK and SYMBOL-SHIFT are pressed together.
30 CLEAR 65020
40 FOR j=65024 TO 65280: POKE j,253: NEXT j
50 POKE 65021,195: POKE 65022,8: POKE 65023,255
60 FOR j=65281 TO 65314: READ k: POKE j,k: NEXT j
70 DATA 62,254,237,71,237,94,201,245,197,213,229,62,127,219,254,246,224,254,252,32,6,243,6,192,205,5,10,225,209,193,241,195,56,0
80 RANDOMIZE USR 65281
Line 30 CLEARs the necessary space for the Interrupt software. Line 40 fills memory locations FE00H (65024) to FF00H (65280) with FDH (253). Line 50 places a JP FF08H (65288) instruction at location FDFDH (65021). Lines 60 and 70 load the following machine code, starting at location FF01H (65281).
I’ve divided the code into five blocks, whose meaning will be explained shortly.
Line 80 executes machine code at location 65281, which, not so coincidentally, is equal to location FF01H in the machine code listing. Only block #1 is executed, since it ends with a RET instruction. Block #1 loads FEH (254) into the I-register, and sets the machine into Interrupt mode 2, whose operation was explained last time.
AADR HEXCODE LABEL MNEMONIC
FF01 3EFE LD A,FE (1)
FF03 ED47 LD I,A
FF05 ED5E IM 2
FF07 C9 RET
FF08 F5 PUSH AF (2)
FF09 C5 PUSH BC
FF0A D5 PUSH DE
FF0B E5 PUSH HL
FF0C 3E7F LD A,7F (3)
FF0E DBFE IN A,(FE)
FF10 F6E0 OR E0
FF12 FEFC CP FC
FF14 2006 JR NZ,FF1C
FF16 F3 DI (4)
FF17 06C0 LD B,C0
FF19 CD050A CALL 0A05
FF1C E1 POP HL (5)
FF1D D1 POP DE
FF1E C1 POP BC
FF1F F1 POP AF
FF20 C33800 JP 0038
From here on, the TS-2068 is doing something new that it does not ordinarily do. Every time an Interrupt occurs, the machine has to find out where it is to execute the Interrupt code. It gets the upper byte of an address from the I-register and the lower byte from the data bus. This combination is the ADDRESS OF THE ADDRESS of the Interrupt handler. It will become clearer (hopefully) as we “walk through” what happens.
When the TS-2068 gets an Interrupt it looks to the I-register and the data bus to generate the address FExx, where xx is a number that is not known because the TS-2068 mysteriously puts different values on the data bus at different times. The TS-2068 will then look to memory location FExx for the address of the Interrupt handler, and then run the code wherever that happens to be.
However, the BASIC program filled all memory locations from FE00H (65024) to FF00H (65280) with the number FDH (253), so no matter what value FExx happens to be, the TS-2068 will find FDFDH (65021) when it looks there! This is where it will start to execute the Interrupt handler.
Unfortunately, FDFDH (65021) is just 3 bytes less than FE00H (65024), where the “kluge block” of FD’s is located. There’s not room for much code, but there’s just enough space for the JP FF08H (65288) instruction that the BASIC program put there. This means that the Interrupt handler will continue at location FF08H or, block #2 in the machine code listing. (It gets a lot simpler from here on, honest)
If the explanation seems murky so far, it’s O.K. to forget it for awhile. Just take my word for it that the aforementioned code makes it appear that an Interrupt will cause code to be executed at location FF08H (65288). This is where our true Interrupt handler is to be found.
The handler begins with block #2, which saves all of the registers. We do this so that we can leave them as we found them when we’re done. This will ensure that we don’t disrupt the program that was running when the Interrupt occurred.
Block #3 reads a small portion of the keyboard. We won’t cover keyboard scanning here, but block #3 causes block #4 to be skipped if the BREAK and SYMBOL SHIFT keys are not being pressed simultaneously.
Block #4 causes the screen to be copied. Before CALLing the screen copy routine in ROM, we load B with the number of pixel lines to be copied. Changing this would allow us to COPY only part of the screen.
Block #5 prepares the computer to leave our Interrupt handler. All registers are restored to their original values. Note that the first item that POPs off the stack is the last item that was PUSHed on. This means the registers must be restored in reverse order.
Ordinarily an Interrupt handler ends with RETI (similar to RET) instruction. In this case, we’ll end it with a JP 38H (56), which jumps to the normal Interrupt handler. This allows the normal Interrupt functions of keyboard scanning and updating the system variable FRAMES to be performed.
7 YOU MENTIONED COPYING ONLY PART OF THE SCREEN, HOW IS THIS DONE?
By LOADing a different number into the B register before CALLing the COPY routine, You can change the number of lines printed in the following way. Suppose that LINES = the number of lines of characters (from the top of the screen) that you want to COPY. Just POKE 65304, (8 X LINES). The handler is now setup to COPY only part of the screen.
8 WHAT OTHER THINGS CAN AN INTERRUPT HANDLER DO?
By reading the system variable FRAMES, which is incremented every 1/60th of a second a nice real time clock can be made, that flashes the time up on some unused part of the screen, even when you’re running other programs, FRAMES isn’t updated when the Interrupts are disabled so the clock “stops” whenever you use cassette I/O, the TS-2040 printer, or the BEEP commands and resumes when you are done. Still, it’s a free, “software only Clock”.
If the Interrupt handler were linked to a hardware real time clock, the clock wouldn’t stop at all.
Among other uses is an item called a print spooler. Printers are very slow compared to the computers running them, and the computers spend most of their time waiting while the printer is running. It’s possible to send LPRINT commands to a buffer area in memory, and have the Interrupt handler “pick up” this data and print it one character per Interrupt.
This would allow the printer to run at up to 60 characters per second while you’re doing other things with your computer, In other words, you could be RUNning or ENTERing a program at the same time as the computer is printing something else, Those who’ve used such a feature on an IBM PC or other computers will agree that it is a great time saver.
Another use is a program that reads and “stacks up” keyboard entries before the computer requires them. When an INPUT is needed, it gets it from this stacked up data, This is called a keyboard buffer, and it’s also very convenient.
Since the Interrupt is synchronized to the video displays it’s possible to change the BORDER color some fixed time after the Interrupt and obtain a “full screen horizon” that extends into the border area. The Spectrum game Aquaplane does this, but the required timing may be different to make the effect work on the TS-2068’s 60 Hertz Interrupt. (The Spectrum uses a 50 Hertz Interrupt.) I’ve not seen the game working on a TS-2068, but the effect is still available to us.
These are items that come immediately to mind. Other less obvious uses are out there. One that I’m considering involved my software that makes BASIC work in the 64 column mode. Certain keyboard inputs cause the computer to change a system address table in an undesirable manner. I expect to use the Interrupt to “change the table back” before any harm is done.
There are many other uses.
A PROLOGUE
Doug Dewey, member extraordinaire, of the Triangle Sinclair Users Group, tells me that merely adding pull-up resistors to a Spectrum emulator as suggested last time, doesn’t clear up all of the problems related to certain “un-RUNnable” Spectrum programs. He’s sending me copies of some programs, and I’ll be checking them out especially in understanding the way the Spectrum handles the data bus during Interrupts and whether my “fix” works as expected on all machines.
Next time (or in later installments) we should be looking at the problems of relocating the demonstrator code, of the (solvable) problem of doing something like the demonstrator on a TS-1000, and constructing hardware to make use of the TS-2068 Non-Maskable Interrupt. Those looking for a challenge should try to relocate the demonstrator to reside in the 16-32K memory region. When a certain part of the Interrupt software resides in the same 16K region as the display file, something interesting happens. It still works, but …