Raster interrupt is an interrupt trigger signal that the VIC-II can supply, if desired, to the CPU whenever the raster in the VIC's video signal reaches a specific line. With a bit of (machine code) programming, this mechanism can be exploited to perform many kinds of VIC "trickery", like having both text and high-res graphics on screen simultaneously, and displaying more than the eight hardware-supported sprites at once. This method is often used on the C64/C128 for computer games and demos.
Setting up a raster interrupt
By default, the system is set up to receive timer-based signals from CIA-1's Timer A, but instead the VIC needs to be configured to deliver raster interrupt signals. This is handled by a small initialization routine:
Init SEI ; set interrupt bit, make the CPU ignore interrupt requests LDA #%01111111 ; switch off interrupt signals from CIA-1 STA $DC0D AND $D011 ; clear most significant bit of VIC's raster register STA $D011 LDA $DC0D ; acknowledge pending interrupts from CIA-1 LDA $DD0D ; acknowledge pending interrupts from CIA-2 LDA #210 ; set rasterline where interrupt shall occur STA $D012 LDA #<Irq ; set interrupt vectors, pointing to interrupt service routine below STA $0314 LDA #>Irq STA $0315 LDA #%00000001 ; enable raster interrupt signals from VIC STA $D01A CLI ; clear interrupt flag, allowing the CPU to respond to interrupt requests RTS
Notice that enabling the raster interrupts from the VIC takes place after setting up everything else. Also, the routine starts with SEI, to disable IRQ requests altogether. If the interrupts are enabled before e.g. the interrupt vector is re-directed to the new routine, an interrupt may occur just as this vector is being altered, sending the CPU to a "random" address and probably crashing the system.
Using a single interrupt service routine
Here is a very simple interrupt service routine that that "wiggles" the screen frame color (53280) to indicate where on the screen the interrupt occurs:
Irq LDA #$7 STA $D020 ; change border colour to yellow LDX #$90 ; empty loop to do nothing for just under half a millisecond Pause DEX BNE Pause LDA #$0 STA $D020 ; change border colour to black ASL $D019 ; acknowledge the interrupt by clearing the VIC's interrupt flag JMP $EA31 ; jump into KERNAL's standard interrupt service routine to handle keyboard scan, cursor display etc.
When this is run, the frame, or "border", around the screen is mostly black, except for a yellow stripe at the left and right edges: This stripe appears at raster line #210 as set in the initialization routine, because just as the raster starts out on this pixel line across the screen, the interrupt service routine sets the frame color to yellow. During the pause loop, the raster swipes swipe through 8 whole pixel lines, leaving a 8-pixel-wide stripe before the color is set back to black.
Using multiple interrupt service routines
Raster interrupts are often used with a system of two or more interrupt service routines, handling interrupts triggered at different raster lines: Each routine perfoms its "own" tasks, then sets up an interrupt to occur at the "next" desired raster line and re-directs the vector at 788–789/$0314–$0315 to point to the next routine associated with that line.
The following example shows two such "concatenated" raster interrupt service routines, splitting the screen in a high-resolution part and a text part: They can be initialized using the routine outlined at the beginning of this article.
The first routine is triggered at raster line 210, and handles the switch from high-res to text mode. It also sets up the interrupt pointer to the second interrupt service routine, and configures the corresponding raster line:
Irq LDA $D011 ; select text screen mode AND #%11011111 STA $D011 LDA #<Irq2 ; set interrupt vectors to the second interrupt service routine at Irq2 STA $0314 LDA #>Irq2 STA $0315 LDA #$0 STA $D012 ; next interrupt will occur at line no. 0 ASL $D019 ; acknowledge the interrupt by clearing the VIC's interrupt flag JMP $EA31 ; jump into KERNAL's standard interrupt service routine to handle keyboard scan, cursor display etc.
The second routine handles the switch from text to high-res at raster line 250. It also re-adjusts the interrupt pointer back to the first interrupt service routine, and sets up the raster to trigger it next time the raster passes by line 210:
Irq2 LDA $D011 ; select bitmap screen mode ORA #%00100000 STA $D011 LDA #<Irq ; set interrupt vectors back to the first interrupt service routine at Irq STA $0314 LDA #>Irq STA $0315 LDA #210 STA $D012 ; next interrupt will occur at line no. 210 ASL $D019 ; acknowledge the interrupt by clearing the VIC's interrupt flag JMP $EA81 ; jump into shorter ROM routine to only restore registers from the stack etc
In this example the "default" interrupt service routine at 59953/$EA31 is "included" through the JMP command at the end of the first of the two raster routines, enabling you to move the cursor, type commands etc. after the interrupt system has started. Notice that only one in such a set of raster-driven interrupt service routine need to end in a jump into the entire KERNAL service routine: The latter routine takes a jump to 60033/$EA81, where the ROM routine pulls back registers etc. from the stack before handing the CPU back to what it was doing when the interrupt occurred.