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                 ; get low byte of target routine
sta 788                       ; put into interrupt vector
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                 ; do the same with the high byte
sta 789
cli                           ; re-enable interrupts
rts                           ; return to caller


intcode = *

inc $d020                     ; change border colour

lda $d019                     ; clear source of interrupts
sta $d019

lda #100                      ; reset line number to go off at
sta $d012

jmp $ea31                     ; exit back to rom
--------------------------------------------------------------------------

As you can tell, there's a wee bit more to this code than there was in the 
original. Execute it, and you'll notice that it looks pretty much the same as 
the results from the first program. But there's a difference: the interrupts 
are now being generated from the video chip, not the CIA. For this program, it 
didn't make much difference. However, for a more complicated program, it makes
a world of difference.

I'd better explain some of the code used above:

     lda #$7f
     sta $dc0d

     - This piece disables any interrupts caused by the CIA chip.

     lda $d01a
     ora #$01
     sta $d01a

     - Location $d01a controls which sources may cause an interrupt
       (other than the normal CIA). There are 4 different possible
       sources: rasters, sprite to sprite collision, sprite to
       background collision, and the light pen. Bit #0 is the raster
       bit, and this piece of code activates it.

     lda $d011
     and #$7f
     sta $d011

     - This code clears bit #7 of location $d011. This location is used
       for many different things. Bit #7 represents the highest bit of
       the raster line (see segment below for more on the raster line
       #). More than 256 raster line numbers are possible (some are off
       screen, and some represent the upper and lower border areas).
       This bit is the 9th bit. I set it to zero because all my code
       affects rasters only on the normal 25*40 line display, well
       within the 0-255 range. This decision was an arbitrary choice on
       my part, to make the code simpler.

     lda #100
     sta $d012

     - Location $d012 is the lower 8 bits of the raster line on which
       the interrupt is to be generated. The number 100 was another
       arbitrary choice. For changing border colours, the actual line
       number was not important. Later on, in the next example, it will
       become important.

     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 #<intcode                 ; do the same with the high byte
sta 789

cli                           ; re-enable interrupts
rts                           ; return to caller

intcode = *

lda modeflag                  ; determine whether to do top or
                              ; bottom of screen
beq mode1
jmp mode2

mode1 = *

lda #$01                      ; invert modeflag
sta modeflag

lda #COLOUR1                  ; set our colour
sta $d020

lda #LINE1                    ; setup line for NEXT interrupt
sta $d012                     ; (which will activate MODE2)

lda $d019
sta $d019

jmp $ea31                     ; MODE1 exits to Rom

mode2 = *

lda #$00                      ; invert modeflag
sta modeflag

lda #COLOUR2                  ; set our colour
sta $d020

lda #LINE2                    ; setup line for NEXT interrupt
sta $d012                     ; (which will activate MODE1)

lda $d019
sta $d019

pla                           ; we exit interrupt entirely.
tay                           ; since happening 120 times per
pla                           ; second, only 60 need to go to
tax                           ; hardware Rom. The other 60 simply
pla                           ; end
rti

modeflag .byte 0

----------------------------------------------------------------------------

The above code, when executed, will result in the top half of your border being
white, and the bottom black. You may wish to fiddle with the equates (COLOUR1,
COLOUR2, LINE1, and LINE2) to get different effects.

I see some confused faces concerning why the above exit the interrupts the way 
they do. Remember, since we want a split screen effect, we have to have one 
interrupt occur at the TOP of the screen, to turn on the WHITE effect, and one 
midway down to turn on the BLACK effect. Two interrupts times 60 means 120
interrupts will be executed per second. The Rom only needs 60 per second to 
service the keyboard and its other stuff. So, we send 60 to the Rom (the 
interrupts which go through MODE1) by JMPing to $EA31, and the other 60 we 
trash. The PLA... RTI business is the proper way to bring an interrupt to an end
without going through the Rom. The RTI will ReTurn from Interrupt, and cause the
regular program to continue to execute.

That brings to an end this discussion on rasters. I hope the above examples 
have proved to be a valuable learning tool for you. With luck, they will 
motivate you to continue to experiment with rasters, and come up with some neat
effects.

If you have any questions, be sure to ask me about them.

