INTERRUPTS, RASTERS, BORDERS AND OTHER STUFF by Stefan Posthuma
With my hands still shaking a little from a heavy Andes Attack
session (I reached level 14 with 350000 or so points!), I feel
ready to tell you something about the things that make life on
the ST so colorful. In fact, they are the essential element of
every super-duper demo that is being created nowadays. They are
called: rasters.
First of all, let me tell you what rasters are and before that,
I will inform you about the basis of a raster: the interrupt.
Let's say you write a program and you want to do something which
has to be done with specific time-intervals, like blinking a
cursor or playing some music. You can go through a lot of
trouble and call your routines very often and time them or
something to they will be executed regularly, or you can program
them on interrupt.
When you move your mouse, the keyboard processor will notice it,
process the movement and then send the information to the 68000.
It sounds simple, but how does one processor 'send' something to
the other? Well, it simply 'interrupts' the processor it wants to
tell something. In case of the mouse movement, the keyboard
processor will generate an interrupt which will put the 68000 in
interrupt mode.
Now the 68000 has 7 interrupt levels. This means that there are
7 different interrupts possible. Also this means that an
interrupt can interrupt an interrupt when it has a higher level!
The different levels are used for the keyboard-processor, the
vertical blank interrupt etc. More on this later.
Immediately after it has received the signal, the 68000 will
first complete its current instruction. After that, the status
register and the program counter will be put on stack, the
processor will enter supervisor mode and will jump to a certain
memory location. These locations are set on fixed addresses
somewhere in the first 500 bytes of memory or so. The keyboard
processor will create a level 6 interrupt. The 68000 will fetch
the value of memory location $118 and jump to the address formed
by that value. Then it will execute the instructions normally
from that address. When it reaches an RTE instruction, the status
register and program counter will be fetched from stack and the
processor goes back to user mode and program execution will
continue just like nothing happened. As long as the interrupt
routine does not corrupt the stack or changes any registers used
by the normal program, the normal program will not 'know' that
there has been an interrupt.
Still with me? If you're not, don't worry too much about it, it
is not totally essential for the understanding of the rest of
this article.
Now TOS has a nice feature if you want to use an interrupt-
driven routine. The level 4 interrupt is the VBL. As you might
know, the screen is painted by an electron beam. On a color
monitor in 50Hz mode, the screen is drawn 50 times per second by
this electron beam. It starts at the top left-hand corner of the
screen and draws from left to right. When it has reached the
right edge of the screen, it will jump back to the left and draw
the next line. When it has reached the bottom of the screen, it
will jump back to the top of the screen and start all over again.
At this point, the level 4 interrupt will be generated. The time
needed for the electron beam to go back to the top of the screen
is long enough for the 68000 to do some work. When you are in
50Hz mode, the level 4 interrupt will be created 50 times a
second!
At location $4CE (a legal one), is a list of 8 addresses. The
first few ones are used by TOS, but there are always a couple of
them free to use. (They will be zero). When you have a routine,
just put it's address in one of the free locations and it will be
called 50 times a second! Make sure your routine is short because
it will slow down other operations.
Back to the story about the electron beam. Let's say you can
track it and you can do something at specific places on the
screen. If you can somehow see when the beam is in the middle of
the screen and you switch color palette just then, the rest
of the screen will be painted using the new palette and you will
have 32 colors on the screen! (This is the basic principle, make
sure you understand it!!)
So how can we track the electron beam then?
Well, it all comes down to timer B. Timer B is a nice feature
from the MFP 68901 processor and it counts screen lines! Each
time a new screen line is drawn, the timer B data register will
be increased so it will contain the number of the scanline drawn,
starting at the first one after the upper border. Even better
about timer B is that when you put a value in it, let's say 10,
10 scanlines later an interrupt will be generated! Well, this
means that we have found the way. This is the way it works!
Ok, practical now. Let's create a raster.
First of all, we need a safe way to trigger timer B. Why not use
the level 4 interrupt which occurs each time the electron beam
reaches the bottom of the screen. Here we set the value for timer
B and in that scanline, the interrupt will occur! So if we want a
raster on scanline 100, we activate timer B with a value of 100
in the level 4 interrupt and wait for the timer B interrupt.
Uh oh....strange things are happening....
MFP 68901: Hi guys! Interrupt time!
68000 : No way, I'm tired...leave me alone!
MFP 68901: Ah, come one, just one little interrupt...
68000 : Forget it, go away!
MFP 68901: Ok, have it your way.
68000 : Fine.
MFP 68901: OVERRIDE!!!!!!
68000 : OUCH!!!
MMU : Hey? What's going on? Who is overriding there without
filing the proper request at my address bus?
MFP 68901: I am and you can't stop me.
68000 : Groan...
MMU : Stop it right now or I'll have to get involved here.
68000 : Yeah, stop this idiot!
MFP 68901: Fools!
MMU : SYNCHRONISE!!
Shifter : AAAAAAHHHHH! My borders!!!
MMU : Ooops, wrong interrupt level.
MFP 68901: Wow! no borders, let's throw some rasters in there!
Shifter : NO!!! AAAAAAAAAAAAAGGGRRLLL!!
68000 : Hey guys, what about me?
MMU, MFP 68901: We're having fun. In fact, we haven't had this
much fun since the Union Demo.
Shifter : WHAT???!! UNION DEMO??!!!? AAAAAAAHAHAAAAAHAAHAAA!!!
68000 : Nurse!!
MMU : Yeah, border here, synchro there.....I'm having a
blast.
MFP 68901: Ok, what does this address do?
Shifter : AHAHAAAAAAAAAGGRRRLGGALAAAHAAHAHHH!!!!
68000 : Hey, look out!!
Gemdos critial error handler: PANIC!
Exception vector: ZAP!
68000, MMU, MFP 68901: Uh oh.....
Shifter : UNION DEMO!!! RAAAAAAHHH! LOST BOYS!! GROOOOOWWWLL!!
CAREBEARS!!! AAAAAAAAAAHHAHAHAHAHHAHAHGGGGGLLLlllpp..
ST : CRASH!!
Stefan : F.CK!!
Good thing I press CONTROL-V regularly. I was about to tell you
exactly how to program a Timer B raster interrupt. First thing we
do is to install the new vectors so the 68000 will jump to our
routines when interrupted. The VBL vector is $70 and the Timer B
vector is $120. Now before we start messing around with the
vectors, we have to put them in a safe place so we can restore
them to their normal values at the end of the program. Also, we
must make sure that any other VBL routines (e.g. music) are still
called so we must jump to the old value of the level 4 vector
after our level 4 interrupt. The following source fragments are
originally by TEX. All I know about rasters I learned from them.
Thanks guys!
Note: supervisor mode must be on before you can mess with any of
these registers.
hblon: move.l $120,oldtb ; save old timer B vector
move.l $70,old4 ; save old VBL vector
move.l $70,new4b+2 ; now, the new VBL routine will
jump to the old VBL after it
is done.
move.b $fffffa07,old07 ; Timer B enable
move.b $fffffa09,old09 ; Timer C enable
move.b $fffffa0f,old0f ; Timer B in-service
move.b $fffffa11,old11 ; Timer C in-service
move.b $fffffa1b,old1b ; Timer B control
and.b #$df,$fffa09 ; disable Timer C
and.b #$fe,$fffa07 ; disable Timer B
move.l #newtb,$120 ; new Timer B vector
move.l #new4,$70 ; new VBL vector
or.b #1,$fffffa07 ; enable Timer B
or.b #1,$fffffa13 ; set Timber B mask
rts
You can see that timer C will be switched off. This to prevent
it from messing things up. Now we have installed two vectors for
the VBL and the Timer B and we have set some registers of the MFP
68901 to allow Timer B interrupts and to disable Timer C.
The thing we need now are the two routines to handle the VBL and
the Timer B we installed.
new4: clr.b $fffffa1b ; disable Timer B
move.b 100,$fffffa21 ; set Timber B offset
move.b #8,$fffffa1b ; enable Timer B
move.w #$0,$ff8240 ; background = black
new4b: jmp $12345678 ; the old VBL vector will
have replaced the
$12345678 so control
will be passed on to
the old VBL routine.
The important value here is the '100'. This will mean that in
the 100th scanline of the screen, a Timer B interrupt occurs and
the vector at $120 will be called.
newtb: clr.b $fffffa1b.w ; timer stop
movem.l a0/d0,-(sp) ; save A0 and D0
move.w #$fa21,a0 ; timer B data
move.b (a0),d0 ; get value of Timer B
wait: cmp.b (a0),d0 ; wait one scanline
beq.s wait
move.w #$700,$ff8240 ; background red
movem.l (sp)+,a0/d0 ; get registers back
bclr #0,$fffffa0f.w ; end of interrupt
rte
First of all, you have to stop the timer to prevent any other
interrupts from it. After that, you HAVE to push any registers
you use on the stack. This is of vital importance, else somewhere
in the middle of the normal program, registers will be changed
and things go utterly wrong. Then we load the Timer B data
register into A0. Reading this register will give you the current
value of Timber B, i.e. the current scanline. After that we read
the value of it in D0 and wait until it changes. In other words,
we wait for the beginning of the next scanline.
Why?
Well, when an interrupt occurs, the 68000 has to complete its
current instruction. After that, registers (PC and ST) have to
pushed on stack etc. All this takes a little while and when the
actual interrupt routine is called, the electron beam is already
in the left border, drawing visible screen area. If you change
colors now, this will be visible. Also, the interrupt will not
always occur at exactly the same place, so you will see some
nervous flickering which is of course not tolerable if you want
to write a demo.
So we wait until the beginning of the next scanline and
immediately after, change screen color. The electron beam in then
still drawing the non-visible parts of the left border and your
raster will be nice and stable.
To restore the vectors, use the following code:
hbloff: move.w sr,-(sp) ; save status register
move.w #$2700,sr ; disable all interrupts
move.b old07(pc),$fffffa07 ; restore all registers
move.b old09(pc),$fffffa09
move.b old0f(pc),$fffffa0f
move.b old11(pc),$fffffa11
move.b old1b(pc),$fffffa1b
move.l oldtb,$120 ; old Timer B back
move.l old4,$70 ; old VBL back
move.w (sp)+,sr ; restore status register
rts
OK, these are the raw basics for raster programming. Now you
know how to create a raster, let you imagination run free and
create demo's!
I will give you one more example, let's say we want another
raster at screen line 150. This means we will have two Timer B
interrupts, one at line 100 and another one at line 150. In the
VBL, you set the one for line 100 and in the Timer B interrupt at
line 100, you set the one for line 150. Easy!
So in the new4 routine, you add 1 line of code that sets the
vector for the first Timer B.
move.l newtb1,$120
In the newtb1 routine, you have to do three extra things:
- set the new value for Timer B
- set the new Timer B vector
- enable Timer B again.
The new value is set in scanlines from the current one. So if
you want a raster at scanline 150, you have to set the Timer B
value to 150-100 = 50.
move.l #$fa21,a0
move.b #50,(a0) ; this sets the new Timer B value
move.l #newtb2,$120 ; set the new Timer B vector
move.b #8,$fffffa1b ; allow Timer B again
Now you have to create a routine called newtb2 that handles the
next raster. It is identical to the newtb1 routine (without the
new changes).
Of course, you can do more things with rasters than just change
background color. By changing more colors, and changing not only
one scanline, but some more, you can create the typical 'round'
rasters that are to be found in so many demos. Just take a look
at the color demo in this issue. There are three rasters that
seem to revolve around each other. It is done with a clever
trick, think about it!
Also, if you use a table of values for timer B instead of a
fixed number, you can create moving rasters.
The last purpose of raster interrupts is to get rid of borders.
Now it has been explained already in the wizard series, but I
will tell you once again. This is how border-obliteration works:
The MMU (Memory Management Unit or GLUE chip) tells the Shifter
(the video chip responsible for drawing the screen) exactly when
to display screen memory and when to display border. There is one
thing that the MMU does not really like and that is the changing
of screen frequency (register $ff820a). When you do this, it will
'forget' the Shifter for a tiny while.
Now if you change the frequency at a certain point in the last
scanline of the screen, the MMU will forget to tell the Shifter
that the lower border has started and the Shifter will happily
continue displaying screen memory instead of border.
The only thing special about the new screen memory is that it is
shifted one plane to the left. There is 16 bytes missing at the
lower-right corner of the screen. But that is not annoying at
all.
So you simply generate a Timer B interrupt at scanline 199, wait
a while, change screen frequency (50-60Hz), wait for the next
scanline, wait some more and change the frequency back (60-50Hz)
and the border will be gone! Timer B will also happily continue
counting scanlines, so rasters in the lower border are no trouble
or whatsoever! (Check out the color demo!). For source material
about this, check ST NEWS 4.2
Removing the upper border is achieved in the same way. But there
is a serious problem here. There are two versions of the MMU
around and they both require a different position where you have
to change frequency. It wouldn't be that much of a problem (just
change frequency at both points and you are home free) if the
screen address wouldn't start at different points on the screen
then. This would mean that your graphics would be all misplaced
on one ST while it works perfectly well on another.
There must be a way to check this. I think it can be done by
generating a Timer B interrupt at a certain position and
examining the video address counter ($ff8205-$ff8209) but I
haven't tried this yet.
Removing the right and the left border is a little (a LOT more)
harder. It still has to do with changing frequency, but other
problems arise here. Now getting rid of the right border is not
too difficult (I already managed it). You have to create a
raster, wait a while until the electron beam it at a precise
point and change frequency. Do it at exactly the right spot and
the right border will be gone. The problem lies in the exactly.
Remember the story about the flicker? Well, even if you wait for
the next scanline, you never can be sure of the exact position.
So you need a way to synchronise the raster so it happens at the
exact spot every time. There is a way but it is a little hard to
explain.
With this technique, you can also create 'scrolling' rasters. I
did this in the 4.4 demo (really nice one...I'm still working on
it) and there is an ST NEWS logo in the lower, left and right
border which scrolls and bounces and is made up entirely out of
rasters. It is REALLY nice.
Also, you have open the border over and over again for every
scanline. This means you spend a lot of time waiting for the
right border and if you open the entire right border, you won't
have any time left.
The left border is still a mystery to me. After a lot of trying,
I finally gave up. The screen did the most crazy things but the
left border just remained.
One final remark about the full-screen demo by Level 16 (UNION
DEMO!!!). Andreas told me how he did it and it is really
brilliantly solved. I mean you send all time waiting for the
border so there is no time left for the things he does (like
music, a sprite, color animation and rasters). But he did it.
It has to do with filling in the code for the music etc. in the
'gaps' (normally a lot of NOP's) where he waits for the next
border. It must have been one hell of a job to get it all
synchronised. Also, it is just one big list of instructions, no
loops or whatever (one jump I guess to the beginning). So this is
why it is so big and this is why the assembler source is 2Mb
large. Now this is just guessing (but I think I am right) so
Andreas might laugh a lot after reading this.
Check out the source in the PROGRAMS folder. 'RASTERS.S' it is
called.
Disclaimer
The text of the articles is identical to the originals like they appeared
in old ST NEWS issues. Please take into consideration that the author(s)
was (were) a lot younger and less responsible back then. So bad jokes,
bad English, youthful arrogance, insults, bravura, over-crediting and
tastelessness should be taken with at least a grain of salt. Any contact
and/or payment information, as well as deadlines/release dates of any
kind should be regarded as outdated. Due to the fact that these pages are
not actually contained in an Atari executable here, references to scroll
texts, featured demo screens and hidden articles may also be irrelevant.