The program bitfield.a48 shows another powerful programming technique: how to extend the interrupt routines. As described in the second chapter it is possible to insert code into the standard interrupt routines.
Here I put some code into the VSYNC interrupt which is called at the beginning of every new frame. This code counts the frames and toggles the active grid every 10 frames. It also initialises and enables the line IRQ which I use to change the colours in the middle of the screen.
When entering the VSYNC IRQ the main IRQ has already done some things, RB0
is the active register bank, the VDC is enabled and the old state (A and P1) is
stored. The registers R3-R7 are used by the standard routine I call later,
which leaves only R0-R2 for free use. It is possible to replace the routine
completely but then there is no table, no collision check, no waitvsync, no
clock and no sound, so I don't recommend doing this. But if you do, the
routine should jump to irqend at the end, which restores P1 and A,
selects RB1 and does a retr.
The code explained here starts after the line IRQ part, see below for the first part of the VSYNC. At first I check if I should blink. The blinking can be turned off, because the VSYNC IRQ is always active, even on the "SELECT GAME" screen and I don't want any blinking there. After that I count frames, because I only change state every 10 frames, this is the blink frequency. If this is not the tenth VSYNC, I continue with the original VSYNC.
; do the blinking
mov r0,#iram_ictrl ; control register
mov a,@r0
jb7 myvsynccont ; should we blink ?
jmp vsyncirq ; thats all for now
myvsynccont
mov a,@r0 ; count the frames
inc a
mov @r0,a
anl a,#03Fh ; mask out frame counter
xrl a,#00Ah ; wait more frames ?
jz myvsyncblink
jmp vsyncirq ; thats all for now
Now that I have to blink, I reset my frame counter, leaving the two control
bits intact. Then I have to turn the grid off, because changing the grid while
it is turned on gives interesting results, but not what I want. I can't use the
normal gfxoff, because that switches register banks and I have to
restore vdc_control later. So I take the vdc_control
register, store the old value and turn off the grid. Then I get the grid line
I have to toggle and test its current state, but only to get a pointer to the
corresponding byte in R1 and a bit mask in R2, so I can invert the bit very
easy. The only thing left to do is restore the old vdc_control
value and continue with the built-in routine.
myvsyncblink
mov a,@r0
anl a,#0C0h ; restart frame counter
mov @r0,a
; turn grid gfx off, the bios-routine is not safe
; to use in irq, it leaves with RB1 and EN I
mov r1,#iram_vdcctrl
mov r0,#vdc_control
movx a,@r0
mov @r1,a ; store old vdc_control
anl a,#0F6h
movx @r0,a
mov r0,#iram_work ; get the bit to blink
mov r1,#vdc_gridh8+1
mov a,@r0
call bittest ; test bit to blink
movx a,@r1 ; invert the bit we have
xrl a,r2
movx @r1,a
; restore old VDC status
mov r1,#iram_vdcctrl
mov r0,#vdc_control
mov a,@r1
movx @r0,a
jmp vsyncirq ; continue vsyncirq
The 8048 can count external events and produce a timer interrupt after an
adjustable number of events have occurred. The timer interrupt is enabled with
en tcnti, the event counter is activated with
strt cnt. The timer interrupt occurs when the T register
rolls over from 0FFh to 0. At every external event T is incremented. In the
G7000 the external events are the HSYNC (Horizontal sync, marks the beginning
of a new line) pulses, so T counts the scan-lines on the screen. So if I put
eg. 0F8h into T and start the counting, the timer interrupt routine is
called 8 scan lines later.
Here I use it the change the background and grid color in the middle of the screen. To do this I activate the line interrupt inside the VSYNC interrupt.
The first part of code is called at the beginning of the VSYNC IRQ. I test, if I should activate the line IRQ, because I don't want to change colours on the "SELECT GAME" screen. Then I activate the line IRQ and set the background and grid colours for the top half of the screen.
; start line irq, if needed
mov r0,#iram_ictrl ; control register
mov a,@r0
cpl a
jb6 myvsyncnoline ; should we start line irq ?
mov a,@r0
mov a,#088h ; middle of the screen
mov t,a ; set # of lines to wait
strt cnt ; start line counting
en tcnti ; enable timer irq
myvsyncnoline
; set grid+background color
mov r0,#vdc_color
mov a,#col_grd_white | col_grd_lum
movx @r0,a
This is the timer IRQ part, it is reached via 0404h when T
overflows. At first I select RB0 and store A and P1. Then I enable the VDC and
prepare to set the color register. But this preparations take too long and the
VDC is in the visible section of the scan line. To prevent ugly flickering I
wait until this scan line is completely displayed and change colours before the
next one starts. I found the exact number of nops by try and error
until the flickering stopped. Due to the different timing you need a different
number of nops here for PAL or NTSC machines. The rest is just
register restoring and finishing the IRQ.
timeirq
sel rb0
mov r5,a
stop tcnt
in a,P1
mov r6,a
call vdcenable
; set grid+background color
mov r0,#vdc_color
mov a,#col_grd_yellow | col_grd_lum | col_bck_blue
; wait until end of current scan line to avoid changing
; the background in the middle of the screen
nop
; [... 15 nop deleted ...]
nop
movx @r0,a
mov a,r6
outl P1,a
mov a,r5
retr