💾 Archived View for mirrors.apple2.org.za › archive › apple.cabi.net › System › GSOS.P8.Anatomy › IN… captured on 2024-02-05 at 14:49:10.

View Raw

More Information

⬅️ Previous capture (2023-01-29)

-=-=-=-=-=-=-

Interrupts

In this chapter, we see how GS/OS and ProDOS 8 react to and handle interrupt
signals generated by I/O devices. GS/OS and ProDOS 8 both let you install assembly-
language subroutines to service sources of interrupts. They also define rules these
subroutines must follow to ensure they will function smoothly together. In particular,
the rules dictate the method an interrupt-handling subroutine must use to indicate
whether it serviced the interrupt.

Before we begin, we should review the concept of an interrupt. An interrupt is an
electrical signal an I/O device sends to the microprocessor in an attempt to get its
immediate and undivided attention. The signal is sent down a special line connected
between a specific pin on the expansion slot connector used by the interrupting device
and the IRQ (interrupt request) pin on the microprocessor. (On the Apple IIc and IIGS,
equivalent connections are made between the microprocessor and each built-in I/O
device capable of interrupting the system.)

An I/O device typically generates an interrupt signal when it has new data to be
read or when it is ready to receive more data. When the microprocessor detects an
active IRQ signal, it completes the current instruction, stops executing the main
program, and then passes control to an interrupt-handling subroutine. This subroutine
(installed by the operating system or the application) is responsible for servicing the
interrupt by clearing the condition that caused the interrupt and performing the
necessary I/O operation. When it finishes, control returns to the main program at the
point where it was interrupted, and execution of that program continues as if it had
never been disturbed.

The advantage of using an interrupt scheme like this to control I/O devices is that

it is the most efficient one for handling asynchronous I/O operations (that is, opera-
tions that can occur at any time). If interrupts were not available, a program would
have to waste a lot of time frequently polling each I/O device in the system to ensure
that incoming data was not lost or that outgoing data was being pushed out as quickly
as possible. This is comparable to picking up a telephone without a ringer every few
seconds to see if anyone is calling in. By adding the ringer (the interrupt signal), you
can go about your normal duties until the phone rings (an active interrupt signal
occurs), and then you can pick up the telephone (service the interrupt).

265

COMMON INTERRUPT SOURCES

Many I/O devices available for the Apple II are capable of generating interrupts.
look at the sources of interrupts usually available on three of the most common
devices: the clock, the asynchronous serial interface, and the mouse.

Clock - A clock device is able to keep track of the time and date without
assistance of the microprocessor. (The logic is handled by a discrete ii
circuit.) It typically contains a small battery that allows the clock to keep -
of the time even when the computer is off. Most clock cards generate -
at regular intervals: every second, minute, or hour.

Asynchronous serial interface - An asynchronous serial interface is
commonly used to link the computer to printers and modems. It can be told
generate interrupts whenever it is ready to send out a character or whenever
receives a character.

Mouse - A mouse is an input device that is normally capable of
interrupts when it is moved or its button is pressed.

REACTING TO INTERRUPTS

It is important to realize that the IRQ interrupt signal is mask able. In other words, it
is possible for a program to instruct the microprocessor to ignore an active IRQ
interrupt signal. It can do this by executing an SEI (set interrupt disable flag
instruction. (The interrupt disable flag is a bit in the microprocessor status register.) If
interrupts are disabled like this, the main program running in the system won't be
disturbed. Time-critical operations, like disk reads and writes, cannot be interrupted
without loss of data, so interrupts are always disabled first.

The instruction that causes the microprocessor to respond to IRQ interrupts is CLI
(clear interrupt disable flag). An application should clear the interrupt disable flag
whenever possible so that it will perform smoothly in an environment in which
interrupting devices may be active.

When the microprocessor receives an IRQ signal, it immediately pushes the
contents of the program counter register and the status register on the stack. If the
processor is a 6502 (or, on the IIGS, a 65816 in 6502 emulation mode), it passes control
to a low-level interrupt handler whose address is stored at $FFFE-$FFFF (low-order
byte first). If the processor is in 65816 native mode, the handler's address is stored at
$FFEE-$FFEF in bank $00.

The low-level interrupt handler is in the firmware ROM on any Apple II. On
models prior to the Apple IIgs, its main duty is to pass control to a high-level
interrupt handler whose address is stored in the user-definable interrupt vector at
$03FE and $03FF (low-order byte first). On the Apple IIgs, the low-level handler
actually tries to process interrupts from built-in devices and passes control to the
user-definable interrupt vector only if it is unable to do so.

266 Interrupts

A properly designed high-level interrupt handler should perform the following
chores in the following order:

z Save the current values in the A, X, and Y registers and all information about
the current machine state.

z Clear the source of the interrupt. (It usually does this by reading the status
registers of the I/O device.)

z Service the interrupt by performing the I/O operation required.
z Restore the A, X, and Y registers to their initial values, and restore the same
machine state.

z End with an RTI (return from interrupt) instruction.

When ProDOS 8 is active, the user-definable interrupt vector points to a general-
purpose interrupt handler within the main body of the operating system called the
interrupt dispatcher. When GS/OS is active, the vector points to a similar dispatcher
which manages ProDOS 16-style interrupt handlers. GS/OS-style interrupt handlers
actually bind to the system at the low-level firmware level; control never passes to the
user-definable interrupt vector unless the interrupt is unclaimed. GS/OS-style inter-
rupt handlers are added to the system with the Bindlnt command.

The ProDOS 8 interrupt dispatcher contains no specific code for identifying and
servicing an interrupt. (This isn't too surprising since it could hardly be expected to
support every possible source of interrupts.) To service an interrupt, it polls each
member in a group of user-installed interrupt subroutines, the addresses of which are
stored in an internal interrupt vector table. These subroutines are integrated into the
system with the ProDOS 8 ALLOC - INTERRUPT command.

Figure 6-1 shows the events that take place when an interrupt occurs under
ProDOS 8. The interrupt dispatcher takes over and calls the first subroutine whose
address it finds in the interrupt vector table. This subroutine will either recognize and
claim the interrupt or not. If it does, the operating system restores all registers and
returns to the interrupted program. If it doesn't, the operating system tries again by
calling the next subroutine whose address is in the interrupt vector table. (The
operating system examines the state of the carry flag to determine if the interrupt was
claimed; if it was claimed, the carry flag comes back cleared.) This process repeats
until the interrupt is claimed, at which point the interrupt dispatcher returns control
to the interrupted application by executing an RTI instruction. If none of the installed
subroutines claim the interrupt, a critical error occurs and the system hangs.

The advantage of using a dispatching scheme like this to handle interrupts is that it
allows for the development of interrupt-handling subroutines that are specific to only
one device. That is, a subroutine need not concern itself with handling mouse, clock,
serial, and "you-name-it" interrupts all at once. If the operating system rules are
followed, you can easily install a mouse interrupt subroutine from one manufacturer

Reacting to Interrupts 267

Figure 6-I How ProDOS 8 handles interrupts

Eat here if
interrupt not

SEC
SEC
SEC

Main ProDOS 8 User-installed error
program interrupt-handling interrupt unclaimed

subrouinne subroutlnes interrupt

(system

Eicit here if hangs)
interrupt is
serviced

and a clock interrupt subroutine from another and they should work properly
(See Eyes and Lichty's Programming the 65816 for detailed information on how
6502 and 65816 microprocessors react to interrupt signals.)

INTERRUI'TS AND PRODOS 8

The ProDOS 8 general-purpose interrupt-handling subroutine (stored in the
IRQ vector at $03FE-$03FF) did not work flawlessly in the first versions of
8; the one used in the newest versions of ProDOS 8 do. The moral is to always use ~L
most current version of ProDOS 8 if you want the system to work smoothly with
interrupts.

You use ALLOC INTERRUPT to store the address of an interrupt-handling
subroutine at the next available location in an 8-byte interrupt vector table in the
ProDOS 8 global page beginning at $BF80. (A dummy $0000 address is stored in the
table if a vector is unused.) Table 6-1 lists all the global page locations used by the
ProDOS 8 interrupt-handling subroutine.

268 Interrupts

Table 6-1 Global page data areas nsed by the ProDOS 8 interrupt-handling subroutine
Address Symbolic label Description

$BF80 1NTRUPT1 The address of the first user-installed interrupt subroutine
$BF82 1NTRUPT2 The address of the second user-installed interrupt

subroutine

$BF84 1NTRUPT3 The address of the third user-installed interrupt

subroutine

$BF86 1NTRUPT4 The address of the fourth user-installed interrupt

subroutine

$BF88 INTAREG The A register is stored here when an interrupt occurs
$BF89 INTXREG The X register is stored here when an interrupt occurs
$BF8A INTYREG The Y register is stored here when an interrupt occurs
$BF8B 1NTSREG The stack pointer is stored here when an interrupt occurs
$BF8C INTPREG The processor status register is stored here when an

interrupt occurs

$BF8D 1NTBANKID The identification code for the active $Dx bank is stored

here when an interrupt occurs

$BF8E 1NTADDR The address of the instruction being executed when an

interrupt occurred is stored here when an interrupt occurs

The user-installed interrupt subroutine must adhere to the following rules:
1 Its first instruction must be CLD.

z If the interrupt was not generated by its device, it must set the carry flag (with
an SEC instruction) and exit.

z If its device is the source of the interrupt, it must claim the interrupt by
performing the necessary I/O operation, clear the interrupt condition (usually by
reading the device status), clear the carry flag with CLC, and exit.

z It must exit with all soft switches in the states they were in on entry. Most of
these switches are used for memory bank switching or for controlling video
display modes. (See Appendix 111 of Inside the Apple lIe.)

z The subroutine must end with an RTS instruction (not an RT1 instruction). The
ProDOS 8 interrupt handler executes the necessary RT1 instruction.

Interrupts and ProDOS 8 269

There is no need for such a subroutine to save and restore the
registers. The main ProDOS 8 interrupt-handling subroutine automatically
for you. Two other nice features of the ProDOS 8 subroutine that significantly
the writing of an interrupt subroutine are

z The contents of locations $FA-$FF are saved before control passes to
interrupt subroutine and are restored when you're through. This frees up
convenient zero page locations for unrestricted use by your subroutine.

z At least 16 bytes of stack space are freed up before your interrupt 5Lz
gets control. This should be enough for even the most complex subroutines.

The program in Table 6-2 (MOUSE.MOVE) shows how to properly
interrupt-handling subroutine in a ProDOS 8 environment. To be able to run
specific example, you must be using an Apple Ilc with the Apple Mouse -
Apple lIe (or II Plus) with an Apple Mouse card installed in slot 4, or an Apple
with its built-in mouse. The program assumes the mouse firmware is in slot 4; if
not, change the SLOT EQU 4 directive to reflect the actual slot. (The mouse
is in slot 7 of the Ilc Plus and the memory expandable version of the Ilc, but it
slot 4 of earlier models.)

MOUSE.M0VE directs the mouse to generate interrupts whenever it is
across a tabletop. When the mouse is moved, the interrupt handler identifies
mouse as the source of the interrupt and then prints the letter M on the screen.
this happens more or less invisibly to the main program that is running; it just
down by the time it takes to service the interrupt.

The first thing M0USE.MOVE does is install the address of the interrupt
(1RQHNDL) in the ProDOS 8 interrupt vector table using the ALLOC - I 1
RUPT command. If an error occurs, the program branches to ERROR and enters
system Monitor. (An error occurs only if the interrupt vector table is full.)
the next step is to initialize the mouse and enable mouse movement interrupts
sending a mouse mode code of 3 to a subroutine called SETMOUSE. The address
this subroutine, and all other standard mouse subroutines, begin somewhere in
mouse interface's firmware in page $C4; the exact offset for each subroutine is
in a table beginning at location $C412. The offset for SETM0USE is the zeroth
in this table; the offsets for the other mouse subroutines used are indicated at
beginning of the program.

MOUSER is the standard subroutine the programs calls to execute a
subroutine. It is responsible for setting up the correct subroutine address and plac
the correct numbers in the microprocessor registers before passing control to -
mouse firmware.

When the mouse is moved, an interrupt occurs, and ProDOS 8 quickly
1RQHNDL. This subroutine first does what all good interrupt handlers should:
determines whether the interrupt was caused by the expected source (that is,

270 Interrupts