64K VDC RAM and an alternate GEOS128 Background Screen

Started by Blacklord, June 24, 2007, 05:18 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Blacklord

(Reprinted from C= Hacking #3)

64K VDC RAM and an alternate GEOS128 Background Screen
by Robert A. Knop Jr. (rknop@tybalt.caltech.edu, R.KNOP1 on GEnie)
 
I. Introduction
 
GEOS, both the 64 and 128 varieties, uses bitmapped screens for its output.
In 40 columns, this means 8K of system memory is set aside for the main
screen.  Then, in addition, GEOS also uses display buffering; in other words,
GEOS allocates a second 8K as a "background" (BG) screen that is used to keep
an intact copy of the foreground (FG) screen.  This can be very useful for a
number of reasons; one, it can be used as an undo buffer, as it is in
geoPaint.  When you have a delicate drawing, and then accidentally run the
eraser across it, the effects of the eraser are only written to the FG screen.
A click of the UNDO button brings the BG screen, with the pre-eraser version
of your painting, back to the fore.  Another use is for buffering the contents
of the screen when something like a dialog box or a menu is written over it.
When a dialog box is erased, and you see whatever had been underneatg it
magically reappear, the graphics underneath are being pulled from the BG
screen.
 
Applications have the option not to use the BG screen.  Change a couple of
vectors and flags, and you can use the 8K BG screen for application RAM.
(This is very convenient, since the BG screen is directly above the normal 22K
of application RAM in the GEOS memory map.)  Of course, the application then
has to provide some way of redrawing blocks of the screen hidden by menus and
dialog boxes.  geoWrite is an example of this; when you bring up, and exit
from, a dialog box in geoWrite, there is briefly a blank rectangle on the
screen before the text is redrawn on the screen.
 
Under GEOS128 in 80 columns, the bitmap screen is now twice as large: 640x200
instead of 320x200.  The FG screen, here 16K, occupies VDC memory.  The memory
used for both the 40 column FG and 40 column BG screen is used for the 80
column BG screen.
 
GEOS128 was written for, and runs on, 128's with only the usual 16K of VDC
RAM.  And, it uses basically all 16K of this RAM.  However, if you have 64K of
VDC RAM (as is the case with 128D's, and with flat 128's that have been
upgraded), you've got an additional 48K of VDC RAM that the GEOS system
doesn't touch.  So, why not use some of this RAM as a 80 column BG screen?
Then, if you are writing an 80-column only application, you get an extra 16K,
the 40-column BG screen at $6000 and the 40-column FG screen at $a000, in main
memory which your application can use however it sees fit.
 
 
II. Support Routines
 
Only a small number of routines actually need to be written to implement this
scheme; moreover, these routines are realatively straightforward.  After all,
we are simply copying memory from one part of VDC RAM to another.  The VDC's
block copy feature of the VDC is very helpful in this endeavor.  (See Craig
Taylor's article from last issue, or most any 128 programming guide.)  The
file vdc-bg.sfx, associated with this issue of the Hacking Mag, is a
self-extracting archive with a number of geoProgrammer source files (in
geoWrite 2.1 format) and a small dippy demonstration program.  The file VDC-BG
contains the following routines:
 
InitVDC       -- make sure your VDC knows it has 64K RAM
VDCImpLine    -- Imprint horizontal line from FG screen to BG screen
VDCRecLine    -- Recover horizontal line from BG screen to FG screen
VDCImpRect    -- Imprint rectangle from FG screen to BG screen
VDCRecRect    -- Recover rectangle from BG screen to FG screen
 
Each Imprint routine actually uses most of the same code as the corresponding
Recover routine; all that differs is the offset to the "source" and
"destination" screens in VDC RAM.  (The offset for the FG screen is $0000, and
for the BG screen is $4000.)  The routines take the same arguments as the
non-VDC Imprint and Recover routines as documented in the Hitchhiker's Guide.
(You will note, however, that for whatever reason the standard GEOS
ImprintLine and RecoverLine routines were only implemented for Apple GEOS.)
Briefly, these are:
 
Routine:     InitVDC
 
Pass:        Nothing
 
Return:      Nothing
 
Destroys:    a,x
 
Note:        This routine should be called at the very beginning of your
             program before you do any writing to the FG screen or the VDC.
 
------------------------------------------------------------------------------
 
 
Routine:     VDCImpLine
             VDCRecLine
 
Pass:        r3   -- left edge of line to imprint/recover (word)
             r4   -- right edge of line to imprint/recover (word)
             r11L -- y coordinate of line to imprint/recover (byte)
 
Return:      r3, r4 -- processed through NormalizeX
 
Destroys:    a,x,y,r5-r8,r11
 
-------------------------------------------------------------------------------
 
Routine:     VDCImpRect
             VDCRecRect
 
Pass:        r3   -- x-coordinate of upper-left corner (word)
             r2L  -- y-coordinate of upper-left corner (byte)
             r4   -- x-coordinate of lower-right corner (word)
             r2H  -- y-coordinate of lower-right corner (byte)
 
Return:      r3,r4 -- processed through NormalizeX
 
Destroys:    a,x,y,r5-r8,r10L,r11
 
------------------------------------------------------------------------------
 
 
To discuss the imprint and recover line routines, consider the ASCII diagram
of a portion of a line on the VDC screen.  A x indicates a pixel that is in
the line to be copied.  The I's indicate byte boundaries; the pixel below each
I is the msb of the corresponding byte in the VDC bitmap.  (Bytes increase
horizontally across the screen; there is no card structure found in the 80
column bitmap screen.)
 
           I       I       I       I
           ....xxxxxxxxxxxxxxxxxxxxxxxxx...
                ^  \______________/  ^
             left          I         right
          residual     full bytes    residual
 
The line moving routine needs to figure the location in VDC RAM of the
leftmost full byte, the number of full bytes, and the location of the two
residual bytes; additionally, it builds a bit mask for the residual bytes,
with bits set corresponding to pixels to be copied.  This mask is used to
create the proper combination of data from the source and destination screens
in the two residual bytes.
 
Once it knows all this, all the line routines do is (1) submit a VDC block
copy to copy the full bytes, (2) read the left residual byte from the source,
mask out the appropriate pixels, and OR it with the appropriate pixels from
the destination, and write that one byte (3) repeat (2) for the right residual
byte.
 
The rectangle routines simply call the line copy routines repeatedly,
(r2H)-(r2L)+1 times.  (Note that while this is the most efficient way to do it
from a coding time point of view , really the rectangle routines only
need calculate the residual bit masks and the locations once.  Thereafter,
locations can be updated by adding 80 (the length of a VDC line) for each new
line.  The changes to the code to implement this are not difficult, and it
isn't clear why I didn't make them....)
 
 
III. Use of the Routines
 
In a word, you use these routines whenever you would have used the normal GEOS
imprint/recover routines.  There are a few other consierations, though.
 
First of all, you need to set the flag dispBufferOn to ST_WR_FORE.  The GEOS
system graphic routines think that the BG screen is in main memory, and thus
will not correctly use your new VDC BG screen.  This means, unfortunately,
that you can't just blithely go drawing graphics, assuming that they'll be
buffered for recall when needed.  However, it is not too much trouble to make
a call to VDCImpRect either right after you've made some graphic change, or
right before something potentially hazardous will happen (e.g. a call to
DoDlgBox).  For instance, you might have all of your main menus be Dynamic
Submenus which call VDCImpRect before opening the submenu.
 
Second, you should load recoverVector with VDCRecRect.  Since VDCRecRect takes
the same parameters as RecoverRectangle, it can substitute directly for it.
Once you set this vector, all menus and dialog boxes erased from the screen
automatically restore the destroyed region from your VDC BG screen.
 
Both of these are demonstrated in the test program included in vdc-bg.sfx.
 
 
IV. Another 32K
 
The alert reader will have noticed that the VDC BG screen only takes as much
memory as the VDC FG screen, i.e. 16K.  Thus, even with this scheme, there is
still another 32K of free memory in VDC RAM.  Quadruple buffering, anyone?
 
A more tantalizing prospect would be to implement a 640x400 interlaced screen
for GEOS128.  This presents a number of problems, however.  First, there is
that terrible flicker.  But, this can be made reasonable through the use of
polarizing filters (in layman's terms, "sunglasses") and appropriate color
choices.  More seriously, the GEOS kernal graphic routines all take byte
argum`>:s for Y coordinates.  So, all 400 vertical pixels cannot be addressed
with those routines.  Thus, sombody implementing a GEOS interlaced screen is
faced with re-writing all of the graphics routines.  (Something to do with the
8K you've freed up at $a000, I suppose.)  Since each 640x400 graphic screen
would require 32K of memory for the bitmap, you could still have a VDC
Background screen.
 
[ Note: The code discussed within this article is available via anonymous
FTP at tybalt.caltech.edu under the directory pub/rknop/hacking.mag as
vdc-bg.sfx. This will dissolve into the GEOWRITE source files.]

============================================================================

GeoPaint File Format
--------------------
by Bruce Vrieling (bvrieling@undergrad.math.waterloo.edu)

GeoPaint is an excellent graphics program written for the GEOS environment. Its
disk access is relatively quick, compared it to what a comparable program would
do on a non-GEOS equipped C64. Part of this accomplishment can be attributed to
the diskTurbo that is an integral part of GEOS. However, the special GeoPaint
file-saving scheme deserves some of the credit.


VLIR
----

GeoPaint files are always stored in Variable Length Indexed Recording files.
VLIR files offer advantages not available without GEOS. Generally speaking, VLIR
is the ultimate in RELATIVE files.

The format of a VLIR file is not that difficult to figure out. While in a
regular C64 file, the two bytes directly following the FILETYPE byte in the
directory would point to the data file following, VLIR files use these two bytes
to point to a VLIR HEADER BLOCK (don't confuse the VLIR HEADER block with the
INFO block). The first two bytes of this block are $00/FF, as the header block
is a MAXIMUM of one block long. (This is why when you VALIDATE a GEOS disk from
C64 mode, GeoPaint pictures are lost. Only the header block is recognised as
being part of the file. The rest of the picture gets deallocated in the BAM).
The remaining 254 bytes in the block are divided into 127 2-byte pointers to
tracks/sectors on the disk. These pointers point to the individual records of
the VLIR file, which may be ANY number of blocks long. The VLIR track/sector
pointers in the VLIR header block only point to the FIRST block of the chain.
From then on, the sectors chain themselves together using the normal format ie.
the first two bytes of each block point to the following block.

A sample GeoPaint VLIR header might look like this:

0000:00 FF 03 11 03 05 03 01 ........
0008:04 03 00 FF 00 FF 00 FF ........
0010:04 07 00 00 00 00 00 00 ........
 etc....

The first two bytes, $00/FF, tell the drive that this is the last (and only)
block in this VLIR HEADER SECTION (will never be more than 1 block, as was
mentioned earlier). The next pair of bytes, $03/11, points to the first VLIR
record. The next two, $03/05, point to the second record.

You will notice that 5th record contains the values $00/FF. This means that for
this record, there is no picture data. We will get into exactly what the data
held in the records mean in a minute. The $00/FF entries indicate an empty
record. Finally, the 9th entry, $00/00 indicates the end of the GeoPaint file.
There is no more data beyond this point.

One note should be made. GeoPaint is not always consistent in its handling of
the data in a header block. Sometimes, it will show quite a few $00/FF
combinations before finally terminating with a $00/00. When reading the file,
I always read the entire header, as I don't trust the end of file method
mentioned above. Just remember that any track/sector link that does not contain
$00/FF or $00/00 is a valid record, with picture data.


Layout on Screen
----------------

GEOS orients the data in a GeoPaint file slightly differently than in a
PhotoScrap or an Icon. A photoscrap stores the bytes corrosponding to a screen
which looks like this:

001 002 003 004 005 ....0012
013 014 015 016 017 ....0024

Consecutive bytes are placed BESIDE each other. However, if you are at all
familiar with the layout of a C64 hi-res screen, you will know this is very
different from the layout that the VIC chip sees data. GeoPaint uses a format
identical to the VIC chip layout on screen.

GeoPaint pictures are stored in the following format:

001 009 017 025 033 .....  313
002 010 018 026 034 .....  314
003 011 019 027 035 .....  315
004 012 020 028 036 .....  316
005 013 021 029 037 .....  317
006 014 022 030 038 .....  318
007 015 023 031 039 .....  319
008 016 024 032 040 .....  320

321 329 .....
322 330 .....
323 331 .....
324 332 .....
325 333 .....
326 334 .....
327 335 .....
328 336 .....

As you can see, this is very different from the PhotoScrap format. Consecutive
bytes are NOT stored on the screen beside each other. Rather, they are stored
underneath each other into groups of 8 bytes. This makes moving the data from
the disk onto the screen that much faster, as the decompacted bytes can just be
stored on the screen after each other. Of course, this makes porting GEOS pics
to the 128's VDC that much more difficult, as the VDC conforms to the
PhotoScrap format.


Compression Method
------------------

GEOS uses an excellent compression method to store files on disk. You may have
noticed that nearly empty pictures on disk consume very little disk space. This
can be credited to GeoPaint's smart compression techniques.

Basically, the format of the compression has one COMMAND byte followed by one
or more DATA bytes. The COMMAND byte tells GEOS what to do with following DATA
bytes. There are 4 commands for compression:

1) If the COMMAND byte is less than 64, this indicates that there are 'COMMAND'
   many DATA bytes following that are to be taken as individual bytes. This is
   the least effective method of compression, as no compression takes place.

2) If the COMMAND byte ranges from 65 to 127, then this is a special type of
   compression. First of all, the next 8 bytes in the file are read in as DATA.
   This DATA is used to make an 8*8 'stamp'. Secondly, the amount of times to
   'stamp' this 8*8 square is calculated (COMMAND AND 63). Then, the stamping
   is done. 'Stamping' sounds more difficult that it really is. What it boils
   down to, is repeating the 8 byte DATA stamp 'COMMAND AND 63'
   times.

3) If the COMMAND byte is 129 or greater, then the following DATA byte is
   repeated 'COMMAND AND 127' times. This is different from #1, as only 1 DATA
   byte is called in, and simply repeated. #1 called in 'COMMAND' many DATA
   bytes.

4) If the COMMAND byte is ZERO, we have reached the end of the VLIR record for
   the GeoPaint picture.

It should be noted that the COMMAND byte will NEVER be 64 or 128. If it is,
there has been an error.


Format of Data After Decompacting
---------------------------------

After the data has been decompacted, it remains to be placed on the screen. Each
VLIR record holds 16 scanlines of data, or 2 character lines (different ways of
looking at the same thing).

The format of the data is as follows:

     First, there is 640 bytes of picture data, comprising the first character
     line (8 scanlines). Remember, GeoPaint pictures are 640 pixels across. 640
     pixels works out to 80 bytes. A character line is 8 pixels deep, so 80*8
     comes to 640 bytes.

     These bytes are followed by the 640 bytes for the second chacacter line
     (next 8 scanlines). This is followed by 8 garbage bytes that accidentaly
     worked themselves into the original GeoPaint design. They should be set to
     zero.

     Finally, two sets of 80 bytes of colour data follow. The first set
     comprises the colour for the first line, the second 80 bytes for the second
     line. To wrap things up, the VLIR record is terminated by a zero byte.

     The next VLIR record will hold the data for the NEXT 16 scanlines, and so
     on.


Conclusion
----------

That about wraps up this discussion on GeoPaint format for files. We've
discussed the format of VLIR files on disk, layout of picture data on screen,
compression methods used in GeoPaint files, and the format of the data once
decompacted. I hope this information will come in handy for someone.

==============================================================================
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 #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 #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 #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.