Videopac G7000 BIOS
Last changed: 2003-07-22

VSYNC and line interrupts

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.

VSYNC interrupt

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.

Check if blinking necessary

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

Do the blinking

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 line IRQ

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.

Activating the line 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

The line interrupt

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