22C:18, Lecture 31, Fall 1996

Douglas W. Jones
University of Iowa Department of Computer Science

Interrupts and Traps

Up to this point, the only contact you are likely to have had with the interrupt and trap mechanisms of the Hawk machine are in the context of error messages generated by the monitor. The most common you are likely to have encountered is "Bus Trap", indicating an illegal memory reference, such as a write to ROM or a read from unimplemented memory. The other message you may have received is "Instruction Trap", indicating an attempt to execute an instruction that the Hawk hardware does not define.

In fact, the trap mechanisms of most modern CPU's are used for far more than detecting errors in user programs. Interrupt handling mechanisms are central to handling asynchronous input/output, and trap mechanisms are central to a class of system features known as virtual machine extensions.

Asynchronous input/output is involved whenever input or output is not synchronous with the request for input or output from the program. (It is worth noting that asynchronous serial communictations, for example, as used with modems and serial printers, is an unrelated use of the same word.) A common example of asynchronous input is with keyboard input. On most computers, input can be typed at any point, and input characters are queued by the software until they are needed by the user program. A second common example is output to a printer. Many computer systems will buffer entire documents, holding them until they can be output while allowing the program that requested the output to go on to other things while the data is transferred to the printer.

Two good examples of virtual machine extensions are software floating point and virtual memory. If your computer has a floating point coprocessor, of course you expect floating point instructions to run at full speed. If this coprocessor is removed, most systems will still operate correctly, but programs that use floating point will run more slowly. In this case, the floating point instructons are not executed by hardware; instead, the hardware traps the floating point instructions and lets the system software execute them.

With systems that have virtual memory, large programs can be run even if the main memory is small, but adding more memory makes the system run faster. What the system does is trap references to nonexistant memory, so that the operating system can copy things to and from disk, rearranging the system's available memory whenever the application program attempts to use a different area of memory.

Interrupt and Trap Mechanisms

All of these applications require that the CPU provide a way for the operating system to gain control of the CPU when a user program was running, take some action, and then return control to the user program. In a sense, this is not too different from the effect you would expect if the user program called a subroutine in the system, except that the user program does not include an explicit call instruction.

Instead, when a trap or interrupt conditon is detected, the CPU abandons the normal fetch-execute cycle, saves the state of the current computation, and begins fetching instructions from the appropriate trap or interrupt service routine.

The details of how a CPU saves the state of a computation when a trap or interrupt is encountered vary considerably from one architecture to another, as do the details of how the CPU determines where to transfer control. On machines with a complex instruction set or CISC design philosophy, it is common to find that the state is saved entirely by hardware. On machines with a reduced instruction set or RISC design philosophy, a considerable amount of the state save and restore work is done by software.

The Hawk machine is in the latter category! On the Hawk, only a small amount of state information is saved when an interrupt or trap occurs. The Hawk has a special register, TPC, used only to save the program counter when a trap occurs. Control transfer on interrupt or trap is similarly simplified. There are 16 fixed addresses in low memory used for each of 16 predefined interrupt or trap conditions. Thus, we can describe the hardware action at the time of a trap as follows:

	TPC = PC
	PC = code << 4
Thus, given that code is a 4 bit binary number, traps and interrupts will be served by control transfers to addresses 00, 10, 20, 30 and so on up through F0 (hexadecimal). The CPU hardware predefines codes 0 through 3, while codes 4 through F are available for external devices. The Hawk manual gived details of what trap codes are reserved for what purposes.

The Plug and Play Problem

Most processors were originally built with no clearly thought-out scheme for assigning interrupt or trap codes to external devices. As a result, such codes were typically assigned on a first-come first-served basis.

So long as such a machine is closely held by one manufacturer, and so long as the space for assigning device codes is not exhausted, this poses no problem, but as soon as third party manufacturers enter the scene, or as soon as one code needs to be assigned to two devices, problems arise.

In an ideal world, you ought to be able to buy any device that is compatable with your machine, plug it in, and use it. This is the model people describe with the phrase "plug and play." In the real world, what has happened with most mass produced computers, and most notably, what has happened with the IBM PC, is that multiple devices are on the market with the same code assigned to them.

As a result, when you plug such devices into a maching, you can never be sure that they don't conflict. So, you've got to carefully check the manuals for each device, remember what device codes are assigned to each, and before plugging in a new device, set the jumpers or address switches on that device to an unused device code, then configure the software so it knows what code to associate with what device.

Hardware and software schemes that automatically search out unused device codes and assign them to devices are possible, and it is also possible to design operating system and hardware structures that allow devices to share device codes, but each of these schemes must be thought out in advance.

Trap Service Software

The basic problems the trap and interrupt service software must face can be cleanly divided between problems specific to a particular trap -- what input/output to perform or what other action to take, and problems of a general nature -- how to save and restore the complete system state, given the partial job done by the CPU.

For any machine, there are many possible ways of handling traps, so we can only give an illustrative example here -- a reasonable solution to the problem, but not one that is mandated by the architecture.

First, we will assume that every trap is associated with a data structure at some fixed memory location in RAM. This data structure is used to save the system state, and it includes a pointer to the code to be executed when that trap occurs, as well as a stack for use by any procedures called by that code.

A reasonable definition for such a data structure is as follows:

; record structure for trap and interrupt service
;   register save areas (4 bytes each)
R1SV	= 0 << 2
R2SV	= 1 << 2
R2SV	= 2 << 2
R2SV	= 3 << 2
 ...
R13SV	= 13 << 2
R14SV	= 14 << 2
R15SV	= 15 << 2
;   other CPU state save areas (4 bytes each)
PSWSV	= 16 << 2
PCSV	= 17 << 2
MASV	= 18 << 2
;   pointer to service routine (4 bytes)
SERVER	= 19 << 2
;   stack pointer for use by service routine (4 bytes)
SERVSP	= 20 << 2
;   size of record
TRAPSZ	= 24 << 2
Given this data structure, we can define a macro to generate the code to handle a trap with some particular code as follows:
	MACRO HANDLER code,server,size
	  . = code << 4
	  TSVSET R3
	  LOAD   R3,REF'code
	  JUMP   MAINTRAP
	  ALIGN  4
	REF'code:
          W      SAVE'code

	  COMMON SAVE'code,TRAPSZ
	  . = SAVE'code + SERVER
	  W      server
	  . = SAVE'code + SERVSP
	  W      STK'code

	  COMMON STK'code,size
	ENDMAC
This macro initializes the data structures in RAM appropriately, it sets up the contents of ROM, and it relies on a routine called MAINTRAP to do most of the work. Typically, MAINTRAP would look something like this:
; common code for all trap service
MAINTRAP:
;		R3 points to trap data structure
;		TSV holds R3
;		TPC,TMA, PSW and R1-15 must all be saved!
	STORE	R1,R3,R1SV
	STORE	R2,R3,R2SV
	STORE	R4,R3,R4SV
	STORE	R5,R3,R5SV
	TSVGET	R1
	TPCGET	R2
	TMAGET	R4
	PSWGET	R5
	STORE	R1,R3,R3SV
	STORE	R2,R3,PCSV
	STORE	R4,R3,TMASV
	STORE	R5,R3,PSWSV
	STORE	R6,R3,R6SV
	STORE	R7,R3,R7SV
	...
	STORE	R14,R3,R14SV
	STORE	R15,R3,R15SV
	LOAD	R2,R3,SERVSP
	LOAD	R1,R3,SERVER

	JSRS	R1,R1	; go handle the trap!

	LOAD	R15,R3,R15SV
	LOAD	R14,R3,R15SV
	LOAD	R13,R3,R15SV
	...
	LOAD	R5,R3,R5SV
	LOAD	R4,R3,R4SV
	LOAD	R2,R3,PSWSV
	LOAD	R1,R3,PCSV
	PSWSET	R2
	TPCSET	R1
	LOAD	R2,R3,R2SV
	LOAD	R1,R3,R1SV
	LOAD	R3,R3,R3SV
	RTT		; return to user!
This bit of code begins and ends on a tricky note, saving just enough of the registers to use those registers to save the special registers PSW, TPC, TMA and TSV, and then, at the end, restoring all the registers but three, then using those to recover the saved PSW and PC before restoring the final registers. The special RTT instruction is used at the very end to load the TPC register into PC, restoring the user's view of the CPU state to exactly what it was at the time of the trap or interrupt.

The trap or interrupt service routine, pointed to by the SERVER field of the data structure, can use R3 as a pointer to the save area data structure if it needs access to any of the saved state information. It may, of course, ignore the saved state.