Rasters - What They Are and How to Use Them by Bruce Vrieling - (bvrieling@undergrad.math.waterloo.edu) Anyone who has fiddled around with interrupts on the Commodore 64 has undoubtedly heard at one time or another of the concept of rasters being mentioned. Rasters are the 'ultimate' achievement of interrupt programming, or so they say. What is a raster? And how does one go about writing a program to use them? Overview of what Interrupts are all about ----------------------------------------- A raster is sort form for the concept of a 'raster interrupt'. Before going into rasters, perhaps a brief review of interrupts is in order. Interrupts are events generated by the CIA timer in the C64 to perform certain tasks. 60 times a second, the CIA chip signals an interrupt is due to be processed (ie. the interrupt timer timed out). This causes the 6510 CPU to stop executing the current program, save the registers on the stack, and begin to execute the interrupt code. Some of the things which get done during an interrupt include the keyboard scan, and updating TI (the software clock). When the interrupt code is finished, an RTI instruction is executed, which brings the interrupt's execution to a halt. The registers are retrieved from the stack, and the current program in memory continues to execute once again. It will continue to do so until the next interrupt occurs, about 1/60 of a second later. The above is what happens in a normal C64 (the C128 follows the same idea, but more events occur during a C128 interrupt). [Ed. Note: In addition, the C=128 generates its interrupts via a screen raster instead of the CIA chip.] However, you can change the normal course of events, and cause some code of your design to be added to the normal list of events which occur every interrupt. Some of the simple favourites include flashing the border 60 times per second. (Note that we have not begun the topic of rasters yet; this has nothing to do with rasters. That discussion begins at the next heading.) How do you change the interrupt's normal course of action? It's rather simple. The C64 contains an interrupt VECTOR at locations 788/9 which is 'jumped through' before the Kernal Rom gets a chance to execute its code. If you change this vector to point to YOUR code, and make the end of your code point to the normal Kernal location (where the interrupt normally would have jumped to, $EA31), and you are careful not to step on anything, your code will be executed 60 times per second. An example is in order: ; flasher ; ; this program causes the border to flash 60 times per second ; setup = * sei ; disable interrupts lda #intcode ; do the same with the high byte sta 789 cli ; re-enable interrupts rts ; return to caller intcode = * inc $d020 ; change border colour jmp $ea31 ; exit back to rom The above is an example of a very simple interrupt routine. If you were to assemble it with an assembler, and SYS to the SETUP routine, you would see your border flash 60 times per second. You will notice the SEI and CLI machine language instructions used above. They are very important. We don't want an interrupt occurring in between the STA 788 and the STA 789 instructions. Think what would happen if one did: 788 would have been modified, but 789 would still be pointing to the high byte of the Kernal address. Result: the interrupt would have jumped to heaven knows where. You can be virtually guaranteed that it would NOT be pointing to a valid piece of interrupt code. Your machine would crash. The SEI instruction turns interrupts OFF, so that there is no danger of an interrupt occurring during execution of the following routine. The CLI turns them back on. If you forget to turn them back on, and accidentally leave them off, your keyboard will freeze when you return to basic, and your machine will seem to lock up. The above was a very simple example. There are many useful things which can also be done on an interrupt. I have seen code which played music in the background of a running Basic program (it played the popular .MUS files). GEOS uses interrupts extensively to control the pointing of the mouse, and to trigger events. Interrupts are powerful beasts, and the following concept concerning raster interrupts specifically is a particularly useful animal for some people. The Raster ---------- A raster is a loosely used term. It refers to an interrupt that is triggered when the ray gun on the back of your monitor draws a certain line on the video screen. There are many different sources which can cause an interrupt. You are not limited to what the CIA chip can do. Rasters depend on interrupts specifically generated by the VIDEO chip. You could make this interrupt change the border colour of the screen below a certain screen line. When the screen line you specified gets redrawn, the interrupt goes off. Your code then quickly changes some memory locations to create a different video mode or effect. You could cause the bottom half of the screen to gets it's character definitions from another, different character set. Or, you could make the top 3/4 of your screen exist in hi-res multi-colour graphics, and keep the bottom 1/4 of the screen in text mode. Some facts about the video screen: it gets redrawn exactly 60 times per second. It contains 200 scan lines on the normal 25*40 display, numbered 50 to 250 or thereabouts (note that there are more visible scan lines though: the top and bottom borders, for example). The actual re-drawing of the screen is synchronized to the electrical power coming into your house, 60 Hz. That's why some programs behave differently when run on European machines. The power is delivered at 50 Hz over there. Why do we have to worry about a video interrupt? If the screen gets redrawn 60 times per second, and regular interrupts also occur at 60 times per second, why not simply put some code into the regular interrupt to do what we want with the screen? Because the two types of interrupts are not in sync. Neither one of them occurs EXACTLY 60 times per second, and the differences are enough to make it next to impossible to get coordinated activity of any kind happening on the screen. When we use the video interrupt, we KNOW we are at a certain line on the screen, as being on that line is what caused the interrupt to happen in the first place. So, let's summarize. We know that regular interrupts occur 60 times per second. We also know that the video screen gets re-drawn 60 times per second, and that we can cause an interrupt to be generated when a certain line gets drawn on the screen. One slight drawback to all of this is that BOTH types of interrupts (regular and raster driven) travel through the SAME vector (ie. about 120 interrupts per second, 60 of one, and 60 of the other). Your code will have to check and see what the source of the interrupt was, and act accordingly. Or will it? The system needs an interrupt to occur 60 times per second to do housekeeping, and uses the CIA clock to generate the interrupts. We want to interrupt every time a certain scan line is reached on the monitor, which will also just happen to occur at 60 times per second. We also have to make sure that they don't interfere with each other. The regular interrupts should be sent to their Rom destination, while our video interrupts should go to our code, and no where else. If both are occurring at 60 times per second, why not do the job of the system Rom, and our video code on the SAME interrupt? We know that the CIA chip is not good for this; it is out of sync with the video image. Why not turn OFF the CIA interrupt, enable the raster/video interrupt, and do both jobs on one interrupt? Then we would have an interrupt signal that occurs 60 times per second, and is in perfect sync with the video image. That's exactly what we're going to do. Astute reads will notice a slight flaw in the above logic. For simplification purposes, I didn't get into the fact that you will need TWO raster interrupts PER SCREEN to accomplish anything useful. Why two? Because any change to the video mode you put into effect 3/4 of the way down the screen will have to be undone at the TOP of the next screen update. If you decide to make the top 3/4 of the screen a hi-res image, and the bottom 1/4 text, you need one interrupt 3/4 of the way down the screen to change from hi-res to text, but you need a SECOND one at the top of the screen to change back to hi-res from text. So, we will now have 120 interrupts going off every second to accomplish our video desires, with 60 of them working a double shift, making sure the system interrupt code gets executed also. Remember that we are working with a specific example. There is no reason why you couldn't split the screen into N different video modes, and have (N+1)*60 interrupts going off per second. As long as you keep your code short (so your interrupts don't take too long, and have another interrupt occur before the current one is done - messy), it will work beautifully. So far, this is all talk. Let's write a few short code segments to accomplish some of the feats we've just discussed. The first we'll do is a re-hash of the one presented above. It flashes the border again. It does not do any mid-screen changes of video modes or anything fancy like that, so only 1 interrupt per screen is required (ie. 60 per second, not 120 etc.). This program simply shows the same idea, but this time using video interrupts as the source rather than the CIA. You probably won't notice a difference during execution. ---------------------------------------------------------------------------- ; flasher - part II ; ; this program causes the border to flash 60 times per second ; the source of the interrupts is the video chip ; setup = * sei ; disable interrupts lda #$7f ; turn off the cia interrupts sta $dc0d lda $d01a ; enable raster irq ora #$01 sta $d01a lda $d011 ; clear high bit of raster line and #$7f sta $d011 lda #100 ; line number to go off at sta $d012 ; low byte of raster line lda #>intcode ; get low byte of target routine sta 788 ; put into interrupt vector lda #intcode ... rts - Re-vectors the interrupt code to the new code. inc $d020 - Changes the border colour. lda $d019 sta $d019 - These lines clear the bit in the interrupt register which tells the source of the interrupt (in preperation for the next). lda #100 sta $d012 - This line resets the raster line to go off at line number 100 again (same as above). It should be reset, so the next interrupt will know what line to occur on. jmp $ea31 - Exit back to the Kernal Rom. A Useful Example ---------------- The following is an example of a more sophisticated piece of raster code. It makes the top half of the screen border white, and the bottom half black. --------------------------------------------------------------------------- setup = * ; some equates COLOUR1 = 0 COLOUR2 = 1 LINE1 = 20 LINE2 = 150 ; code starts setup = * sei ; disable interrupts lda #$7f ; turn off the cia interrupts sta $dc0d lda $d01a ; enable raster irq ora #$01 sta $d01a lda $d011 ; clear high bit of raster line and #$7f sta $d011 lda #LINE1 ; line number to go off at sta $d012 ; low byte of raster line lda #>intcode ; get low byte of target routine sta 788 ; put into interrupt vector lda #