Having done some work with interrupts now, I’ve realised just how powerful they can be for demo coding and how time sensitive some effects / routines are. I’ve looked at a few different ways to set them up and thought I’d recap.
Interrupts occur every 60th of a second. On the C64, memory locations $0314 and $0315 (788 & 789 in decimal) contain a vector to the execution address of the normal interrupt service routine. The default values here are $31 (49) and $ea (234), which form $ea31.
The first method of using the interrupt simply inserts our routine before the normal interrupt routine. To do this, we set new values into $0314 and $0315, which will point to the execution address of our routine. Once our routine is complete, we then jump to the usual interrupt routine.
This first method is OK and works, but for time sensitive routines (like colour bars, scrollers, etc), I’m finding that this method isn’t the best. For example, take the widescreen effect I recently put together (see here). If I use this method here, I would need the routine to poll the value in $d012 to see what the current raster line was to know when to adjust the border colour.
Using this method would result in a lot of wasted cycles. You could perhaps have the single interrupt routine, have it poll the current scan line, and then act accordingly based on a jump table. This method has its place and is useful in some instances, but it produced inaccurate results since you’re constantly asking the raster where are you. A better method would be to use the raster to trigger an interrupt. How about the raster tell us, “Hey, I reached that scan line you’re interested in” and have our interrupt code execute at that point!
This is the method I’ve been using more and more of lately. It involves turning off the CIA interrupts and enabling raster interrupts. By writing a value into the $d012 register, we are telling the raster what scan line to trigger the interrupt on. This helps produce much more accurate results, especially for things like colour splits and raster bars. It also easily support interrupt chaining – where by we can have an interrupt set new values into $0314, $0315 and $d012 to trigger another interrupt further down the screen.
Using my widescreen effect as an example, we can see how it setups up to use raster interrupts and then has “widetop” trigger when scan line 50 is reached. That routine then points the interrupt vector at “widebot” and sets the trigger line to 250. If we wanted to trigger on a scan line past 250, we can use the highest bit on register $d011 which will allow for values 256 onwards. There is a small loop at the start of each interrupt to make it stable – This sort of effect requires a stable raster, otherwise there would be visible flicker. Timing is very important with raster interrupts.
Another method I have read of is to use sprites for triggering an interrupt. I don’t know much about this method, or the benefits it brings, so will have to set aside to time to investigate it.
Some interesting things I found while playing around with rasters is that a normal screen line takes 63 cycles to execute. Bad scan lines, which occur every 8 line, allow only 23 cycles. The missing 40 cycles are spent doing other system activities, which is why it’s often useful to use a delay look up table when creating colour bars or creating splits for visual effects where timing is critical. I believe if you have sprites on a line it also chews up some cycles, meaning even further accuracy on time is required. Knowing the number of cycles each command takes is very important here and my eyes have been opened to the importance or knowing just what you are executing and how long it’s taking.