Hello children, today we're going to be looing at raster interrupts and later on we're going to be making one of our own. You'll need the inside of a toilet roll, some sticky backed plastic and two washing up liquid bottles - remember to use plasticine behind card when you want to make a hole with your scissors and you may want the help of a responsible adult when cutting things. Or at least an irresponsible adult, since they're more fun.
Before I start a word of warning; the first piece of example code in this article use stroboscopic effects and some viewers may find these effects at the least uncomfortable to watch. If you have a history of epilepsy or an adversion to strobe effects, don't execute the first piece of code, just read the documentation and dissection. This only applies to the file called raster_1.asm and the later files can be executed with no difficulty.
Last issue I left you all messing around with the sine curves on that little demo we did, didn't I? So, did you enjoy yourselves? Good! Okay, now up until now our code has been using what's called "runtime", and in theory anything can be written that way. But the C64 can also do something called an interrupt which, as the name suggests, involves interrupting the runtime code and popping off to do something else. Normally the C64's interrupt is set to a part of the Kernel ROM (the set of housekeeping code that Commodore supplied with the machine) and it takes care of reading the keyboard and a few other little tasks.
Since we believe in leading by example I won't just go off and teach you about all the different kinds of interrupt, for now the only one you'll need is the raster interrupt. If you remember our demo, it waited for a raster line using $D012 and then did something, but using a raster interrupt we can do something else on the runtime and whenever that raster position comes around the machine interrupts what the runtime code is doing and perform a few other tasks. Okay, so we need some code to look at to get this concept across, load Turbo Assembler (it's on this issue's cover disk) and the source file raster_1.asm As usual I'll do a breakdown of what you've just loaded in a second, but for now assemble and run it with SYS2304.
The code appears to have run but but the cursor has come back up again and the border is flashing very rapidly, right? That's what we mean by interrupt, the C64 is stopping the runtime code (where BASIC and our previous pieces of code work from) once a frame and is doing a little INC $D020 to flash the border colour. And we can do anything from interrupt, play music, move sprites, scroll the screen and, with more than one split, change what the VIC chip is doing at various points down the screen. Okay, so lets take a look at our code:
split = $00
* = $0900
First off, we set up split as a label, giving it an actual value of $00. If we change that value we can alter where the raster actually takes place on the screen, from line $00 (as here) to line $FF. And, as before, we set our code start with the *, this time to $0900 (2304). That's followed by a quick SEI to turn off the interrupts, so that nothing goes amiss while we change things.
$0314 and $0315 are where the C64 looks to find the address it should call for the interrupt, normally it's set to $EA31 (the housekeeping routines we mentioned earlier) but we want it pointing to our code called int, which will later call $EA31 itself. The LDA #< and LDA #> will get the memory location of int for us, saving us from working it out; in this case, $0314 contains $27 and $0315 gets $40, since INT gets put at $4027 by the assembler. This reverse the numbers system is called lowbyte/highbyte format (since the lowbyte, the $27 here, goes first and the highbyte goes second) and from here onwards, reference to the lowbyte will mean the last two digits of a hex number and highbyte will mean the first two.
Despite the interrupts being "halted" by the SEI command earlier, the interrupt flags themselves can still be set; writing $7F over $DC0D and clearing the top bit prevents a rogue IRQ interrupt occurring whilst doing the same to $DD0D prevents rogue NMI interrups. The latter are less common but more likely the longer the program spends in "SEI mode" so this is a nice little precaution to prevent some hard to track erratic crashes that may occur as your code becomes more complex.
Now we set the position of the split we want by writing it to $D012, the raster register. In this case it's defined by the label split, as we've previously mentioned, so it will be positioned on rasterline $00 which is at the top of the screen. The reason we set $D011 is to clear the Most Significant Bit (or MSB) of the raster, since there are over 256 raster lines on the screen (on PAL machines, as used in the U.K. and Europe) a single $00 to $FF range is too small so one of the bits of $D011 is used as a ninth bit to give a total range of $000 to $1ff.
$D019 is an indicator, the first bit tells us when an interrupt has happened, so we're just priming it here ready for the main routine and then writing to $D01A in order to tell the C64 that we want a raster interrupt, not any of the other kinds it has available.
Ah, now CLI is a new command. It's pretty much the reverse of SEI, it turns all the interrupts back on and therefore enables our new interrupt. Then it's an RTS to take us back to BASIC, at least for the runtime code, and that's the interrupt setup out of the way! Now it's time to get onto what happens during the interrupt itself.
int lda $d019
int is the routine we pointed $0314 and $0315 to earlier, so it's now where the C64 looks when a raster interrupt is called. At this point, we want to check $D019 to see if the first bit is 0 or 1 (in other words, if the Most Significant Bit of the raster is set, since $D012 can be $00 twice during the screen and we want the one where the MSB isn't set) and...
...if it's the former we can go on to our raster split, the routine dosplit (or "do split"). Otherwise we call $EA81, which is another of the little housekeeping routines from the C64's ROM that does most of what our housekeeping friend $EA31 does but without the keyboard reading. After all, we don't want the keyboard read more than once every 50th of a second, it causes all sorts of problems!
dosplit lda #split
Since the value we put into $D012 gets cleaned out when the interrupt is triggered and a split happens, we have to put it back in again for the next frame.
Then we increment the border colour, just to make what's happening a little more noticable (otherwise it would look exactly the same as a normal system interrupt).
And to round things off we call $EA31 which, as I've already mentioned, is where the C64 normally goes on it's own interrupts (the values in $0314 and $0315 are $31 and $EA respectively) so that the housekeeping happens. Well, that's pretty much your lot again, but just for something to play with until I get back next issue, load raster_2.asm from your hard disk, then assemble the code and run it. Most of the code and data is from the original demo which we've covered quite extensively before now but a few slight changes have been made. The most major are that it now runs using a raster interrupt and the settings for $D018 and the addition of a custom character set but I'll explain the latter in more detail next time. Once again, if you have any queries, comments or suggestions contact me and we'll do lunch.
The source code for the routines above can be downloaded here for easier reference.