Vol 2 Issue 7

 

Advanced raster interrupt programming
or: How do I get more than 16 colours at once
by Udo of TEX

The problem: To get more than 16 colours on the screen at once, you must change the color palette somewhere in the screen. This can be done with Xbios function 6 (setpalette) because this waits for the electron beam to prevent flickering. The operating system waits for the Vertical Blank Interrupt before the palette is set. For example, we'd have to change the colour palette in the middle of the screen to be able to use 16 other colours in the lower half of the screen. And, of course, we'd have to put the previous colour palette after the VBL so that the upper half of the screen uses the first 16 colours again. So that the screensplitting is done with each setup of the screen (50 or 60 times per second) without the main program being harassed with it, we have to use interrupt techniques. The Atari has three possibilities therefore: The level 2 interrupt, the level 4 interrupt and the level 6 interrupt. All three methods have in common that level 4 is used to set the upper colour palette and to initialise the data for the further interrupt procedure.

Installation of a level 4 interrupt
instal4:    
move.l $70,old4+2 ;mark old vector
move.l #new4,$70 ;set new vector
rts    

This routine has to be executed in supervisor mode (e.g. by use of Xbios 38, supexec).

The new level 4 routine
new4:    
;here are the seperate initialise routine
old4:    
jmp $000000 ;after old4+2 comes the old interrupt vector, so that the interrupt routines from the OS are still being executed

All program examples were written for the SEKA assembler, and can be re-written for use with other assemblers without much trouble (a ";" denotes a comment).

 

Raster interrupts with level 2:

This interrupt is usually disabled through interrupt mask $0300 of the processor status register (sr), because it is caused by the electron beam going back to the beginning of a line (15625 times per second). When the interrupt is cleared, is halts the main program (the biggest disadvantage of this method). For raster programming, one installs a level 2 routine, that makes the variegated screen together with the level 4 routine:

instal:   ;should again be executed from supervisor mode
move.l $70,old4+2  
move.l #new4,$70  
move.l $68,old2  
move.l #new2,$68  
rts    
     
wert = 50  
old2: dc.l 0  
zeile: dc.w 0  
pal_o: blk.w 16,0  
pal_u: blk.w 16,0  
new4:    
movem.l d0-d7/a0-a1,-(sp)  
move.w #wert,zeile ;set in which line the palette should be switched
move.l #pal_o,a0 ;upper palette
move.l #$ff8240,a1 ;palette register
movem.l (a0),d0-d7 ;load colors in registers
movem.l d0-d7,(a1) ;set colors
movem.l (sp)+,d0-d7/a0-a1  
old4:    
jmp $00000  
     
new2:    
move.l d0,-(sp)  
move.w zeile,d0  
subq.w #1,d0 ;decrease counter
move.w d0,zeile  
bne no2 ;not yet zero, then nothing
     
movem.l d1-d7/a0-a1,-(sp)  
move.l #pal_u,a0 ;palette below
move.l #$ff8240,a1 ;palette registers
movem.l (a0),d0-d7 ;load colors in registers
movem.l d0-d7,(a1) ;set colors
movem.l (sp)+,d1-d7/a0-a1  
     
no2:    
move.l (sp)+,d0  
rte    

To switch off the interrupt, you have to put back the old values of the vectors back to $68 and $70. The disadvantage of this method is that both the level 6 and the level 2 interrupt have priority above the level 2 interrupt and thus can block it or even interrupt it. This causes the colours not to be switched on the same lines all the times, which creates a flickering effect (just like in the "Gauntlet" text when there's no titles music to be heard). When you block the interrupt level 6, you cannot use the mouse or the keyboard, and you can't even perform disk operations!

 

Raster interrupts with Level 4:

Because you can create stable raster interrupts with this technique that, however, takes up such a lot of time so that the main program almost stops completely (and the level 6 interrupt has to be blocker as well), I would just like to mention the possibility of this method but not explaining it in more detail (I have discovered this possibility in the 42-Crew demo):

After the switch-off of the level 6 interrupt, the level 4 routine is modified in such a way that it reads memory locations $ff8205/6/7 after being called. In these locations, it is possible to read the current position of the video address pointers (the address that the video chip momentarily uses to fetch its screen data from). It now waits until a certain value is reached. Is it the first value of a line, it is reached at the end of the preceeding line and the colours can be changed while the electron beam is going back to the beginning of the next line. If the colours in line 180 should be changed, the program has to wait until this line is reached, which takes 90% of the available time!

 

Raster interrupts with level 6:

The level 6 interrupt is controlled by a peripheral chip that can recognise 16 different interrupts and gives these to the processor as a level 6 interrupt. In the Atari, the following are used: RS232, keyboard, Centronics busy, monochrome monitor detect and Timer C (one of four timers). Timer B can now count screen lines, where the difference with the level 2 interrupt lies in the fact that only the screen lines are counted that are displayed and not the number of times the electron beam goes back to the beginning of a line! Because of this fact, one cannot use stable rasters in the border (in our "Super-Neo-Demo-Show" there is no lower border for the video chip). But now for practical proceedings; the installations of the Timer B:

instal6:    
move.l #new6,$120 ;interrupt vektor
or.b #1,$fffa07 ;enable Timer B
or.b #1,$fffa13  
move.l $70,old4+2 ;divert level 4
move.l #new4,$70  
move.b #0,$fffa1b ;Timer B stop
rts    

If this routine was called from supervisor mode, than the "new6" routine waits for the first call. This is reached when the level 4 interrupt sets a counter value and thus starts Timer B. Timer B then decreases that value by one each time an end of a screen line is reached, until zero is reached. When that happens, an interrupt is executed (if you change the palette only once, this will happen 50 or 60 times per second instead of 15625 times for the level 2 interrupt).

 

The new level 4 routine...
new4:    
movem.l d0-d7/a0-a1,-(sp)  
move.b #0,$fffa1b ;Timer stop
move.b #wert,$fffa21 ;Counter value
move.b #8,$fffa1b ;Timer start
move.l #pal_o,a0 ;Upper palette
move.l #$ff8240,a1  
movem.l (a0),d0-d7  
movem.l d0-d7,(a1) ;Set colors
movem.l (sp)+,d0-d7/a0-a1  
old4:    
jmp $000000  

 

...and the Timer B routine
new6:    
movem.l d0/d3-d7/a0-a6,-(sp)  
move.w #$fa21,a4 ;Takes $fffa21 => a4
clr.b -6(a4) ;Timer B stop
move.b #240,(a4) ;Pseudo value (see remark 1)
move.b #8,-(a4) ;Timer B start
move.l #pal_u,a6 ;Pointer on colour palette
movem.l 2(a6),d4-d7/a0-a2 ;Load colours in registers
move.w #$8240,a5  
move.w 30(a6),d3  
move.b (a4),d0 ;Trick 1! (See remark 2)
wait:    
cmp.b (a4),d0  
beq wait  
movem.l d4-d7/a0-a2,2(a5) ;Trick 2! (See remark 2)
move.w d3,30(a5)  
move.w (a6),(a5)  
movem.l (sp)+,d0/d3-d7/a0-a6  
bclr #0,$fffa0f ;Clear interrupt again
rte    

Remark 1: In this example, the colour palette is to be changed only once. That's why a value is written to the counter register that is never reached (maximum 200).

Remark 2: So that really nothing starts flickering, the colours have to be set in the border and during the time needed for the electron beam to get back to a beginning of a line. The interrupt is executed at the beginning of the right border, but as soon as the processor reaches the actual command to set the colours the electron beam is already too far to prevent flickering. The colours have to be set at the beginning of the border - and as fast as possible (Trick 2 - the movem.l is the fastest). Trick 1 reads the Timer B counter and waits until it changes - in other words, it waits until the end of a line is reached. Because it now still waits until the next line, this way is only suitable to change the color palette at every second line.

Problematical aspects:

The MFP interrupts are reported to the processor as level 6 interrupts and are not mutually interrupted! If system interrupts are still installed, this can cause quite some problems. To prevent this, there are three possibilities:

  1. One tolerates the flickering and does nothing about it (absolutely out of the question in our demos!)

  2. One switches off the hazardous interrupts (like we did in our TEX Demos 1-3)
    Remark: Hazardous are Timer C, that are simply turned off. When doing that, the keyboard repeat as well as the possibility to load with GEMDOS are blocked. Because keyboard checks through GEMDOS are still possible, the program can still react, turn off the raster interrupts and turn on the other interrupts again before going on with the actual program. Add thereto the complete sample program, that rescues the starting values in "hblon", sets the raster interrupts and then waits for a key to be pressed. After pressing [Esc], the raster interrupt is switched off and the starting values are set back. The distance values specify the distances between the current and the previous raster interrupt. A number of additional raster interrupts can be inserted as long as you make sure the distance values are not larger than 199. Of course, enough color palettes have to be present.

  3. One programs the interrupts in such a way that they can interrupt themselves (be careful!) (This is what we did in our Super-Neo-Demo-Show)
    Remark: To create the possibility to load, it is possible to give priority to an interrupt by software. To give priority to the raster interrupt, for example, you have to take care that Timer C can be interrupted. This can be done the following way (initialising of the other interrupt routines has to be done like I explained earlier):

newtimc:   ;New Timer C routine
tst.w flagtc ;test Timer C flag
bne noirq  
move.w #1,flagtc ;Set flag to prevent second call
move.w #$2500,sr  
bclr #5,$fffa11 ;Clear ISR bit to enable level 6
move.l #hier,-(sp) ;To operating system after "hier:"
move.w sr,-(sp)  
jmp $000000 ;This is where the old Timer C vector has to go
hier:   ;From the OS from here on
clr.w flagtc ;Clear flag
noirq:    
bclr #5,$fffa11 ;Interrupt End
rte    

With this method, flickering can be prevented. The operating system doesn't like this, however, and takes revenge with a lot of read errors (depending on computer, drive and disk, this can vary between 15% and 60%). This is why our "Super-Neo-Demo-Show" keeps on reading until 32128 bytes were able to be read. Because this is not always possible, I would advise the second method to prevent flickering (Quote: "Luckily there are only few raster interrupts to manage in our game.... movem.l.. swap.. rte..aaarghh...")!

P.S.: Because GEM really doesn't like it when many raster interrupts are present on the screen (because of reasons unknown), the program crashed when moving the mouse. Help: Start the program as a .TOS file or send a command to the keyboard processor ($12 = turn off the mouse). Who knows exactly why .PRG files crash, should please write to us on the address mentioned below.

For questions and support you can also contact us through the following address:

-TEX-
Postfach 1322
D-6702 Bad Dürkheim 1
West Germany

Have fun while trying the routines!

Udo (TEX)


This text was published in the Atari ST diskmag "ST News" and is used by kind permission of Richard Karsmakers. Source for this article: http://www.st-news.com