Signetics 2650 & 2636 programming/Interrupts
Interrupt mechanisms
editInterrupts are a mechanism that allows an external event to signal to the microprocessor that it should stop what is doing and take care of the needs of that event. In these consoles it is only the PVI that does this. The events that cause an interrupt are:
- the leading edge of each vertical reset
- the completion of video generation of an object.
The PVI's interrupt is reset on the trailing edge of the vertical reset signal.
The PVI signals an interrupt request to the processor on its INTREQ pin. If the Interrupt Inhibit bit in the Processor's Program Status Word Upper is not set, the following events take place:
- processor finishes the instruction it was executing
- processor pushes the Instruction Address register on to the Return Address Stack
- processor sets the Interrupt Inhibit bit
- processor signals its acceptance of the request on the INTACK signal
- processor begins to execute a ZBSR (branch to subroutine relative to location zero)
- PVI responds by outputting $03 on the data bus, its in-built interrupt vector and the second byte of the ZBSR instruction
- processor completes the ZBSR instruction by starting execution at address $0003
Notice that the only information the processor has saved automatically is the Return Address. It is up to the programmer to save the Status register and any general purpose registers which the interrupt service routine might use (see below).
The processor can determine why the PVI requested an interrupt by examining the Object Complete bits of register $1FCA and the VRLE bit of register $1FCB. Note that all bits of these registers are cleared when read, and at the trailing edge of VRST.
The interrupt service routine is terminated by a return from subroutine instruction, usually a RETE which enables interrupts once again, or a RETC instruction can be used if it is not desired to enable interrupts.
Saving and restoring processor status
editAt the end of any interrupt service the registers must be restored to the same condition they were in at the time of the interrupt. For registers r1, r2 and r3, this is usually achieved by switching to the second register bank during the interrupt service routine.
Register r0 and the Program Status Lower are more problematic. The problem arises because on the 2650A the Program Status Word can only be transferred to r0, and not directly to memory. This was fixed on the 2650B with the addition of the LDPL and STPL instructions.
This doesn't work:
stra,r0 STORER0 ;save r0 in memory spsl ;r0 = PSL stra,r0 STOREPSL ;save PSL in memory ...... ..the rest of the interrupt service routine goes here ...... loda,r0 STOREPSL lpsl ;restore PSL loda,r0 STORER0 ;restore r0 rete,un
The problem here is that the last loda instruction affects the condition code bits in the restored PSL.
One clever solution is as follows:
start_interrupt: stra,r0 PRESERVER0 ;PRESERVER0 = r0 ppsl $10 ;bank 1 spsl stra,r0 PRESERVEPSL ;save PSL with bank 1 already selected! .......... end_interrupt: loda,r0 PRESERVEPSL ; strz r4 ;put a copy of PSL in r4 lpsl ;restores psl (but with bank 1 still selected) loda,r0 PRESERVER0 ;restore r0 (but probably changes the condition code) andi,r4 $C0 ;this restores the condition code! cpsl $10 ;switch to bank 0 rete,un ;
If it isn’t clear how that AND operation works, it’s because the condition code is in bits 7 and 6 of the PSL. Their binary values (00=zero, 01=positive, 10=negative) correspond to the result of the AND $C0 operation:
- 00000000 is zero
- 01000000 is a positive number
- 10000000 is a negative number
Tutorial code
editThis tutorial's program performs the same task as that in Sync to Object completion but rather than continuously checking the state of VRST or polling the PVI to see if video generation of object 1 has completed, it relies on interrupts from the PVI to make the processor stop what it is doing and take care of whichever event has occurred.
Once an interrupt occurs, and Register0 and the PSL have been saved, the interrupt service routine checks to see if the interrupt was caused by the vertical reset. It does this by testing the VRLE bit in the 'collisions' register, $1FCB. If it is, subroutine Object1A takes care of setting up the initial state of the object. The interrupt routine is then exited and the processor, in this case, sits idle waiting for the next interrupt. When that comes, it must be caused by the completion of object 1. At this point though, it is necessary to determine whether it was the primary object, its first duplicate or its second duplicate that caused the interrupt. The PVI cannot tell us that directly, so a variable Obj1dup has been used to do this. If the variable is 1, then subroutine Object1B is executed; this sets up the first duplicate and sets the variable to 2. On the next interrupt, Object1C is executed, and this sets up the second duplicate and the variable to 0.
This is a very simple program with just one object causing interrupts, and it has only been necessary to determine which duplicate caused the interrupt. When more objects are in use, the interrupt service routine must also determine which object is causing the interrupt. This is done by reading objectstatus at $1FCA. Bits 0-3 are set according to which object has completed.
Care has to be take when using this register since it gets reset when it is read, and it also contains the object/background collision flags. This means that all set bits either need to be acted upon straight away, or saved in a variable to be used later.