** Stable rasters on the VIC-20

Basic and Machine Language

Moderator: Moderators

Post Reply
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

** Stable rasters on the VIC-20

Post by Mike »

This is a follow-up to an inquiry by SparkyNZ in another thread. Here is the source in question:

Code: Select all

DIM code 511
:
ntsc=TRUE
:
cycles=&4243:line=17:phase=21:delay=21:pos=5
IF NOT ntsc THEN cycles=&5686:line=30:phase=88:delay=23:pos=1
:
aux_extbrd_1=&3C00
bck_intbrd_1=&3D00
aux_extbrd_2=&3E00
bck_intbrd_2=&3F00
:
FOR pass=4 TO 7 STEP 3
P%=&3B00:O%=code
[OPT pass
.Init
 SEI
 LDA #Server MOD 256:STA &0314
 LDA #Server DIV 256:STA &0315
 LDA #cycles MOD 256:STA &9124
 LDY #line
.Init_00
 CPY &9004:BNE Init_00:INY:INY
.Init_01
 CPY &9004:BNE Init_01:JSR Init_06
 INY:CPY &9004:BEQ Init_02:NOP:NOP
.Init_02
 JSR Init_06:NOP
 INY:CPY &9004:BEQ Init_03:BIT &24
.Init_03
 JSR Init_06:NOP
 INY:CPY &9004:BNE Init_04
.Init_04
 LDX #pos
.Init_05
 DEX:BNE Init_05
]
IF ntsc THEN [OPT pass:NOP:NOP:]
[OPT pass
 LDA #cycles DIV 256:STA &9125
 CLI
 RTS
.Init_06
 LDX #delay
.Init_07
 DEX:BNE Init_07
]
IF NOT ntsc THEN [OPT pass:NOP:]
[OPT pass
 RTS

.Server
 CLD:SEC:LDA #phase:SBC &9124
 CMP #10:BCC Server_00:JMP &EABF
.Server_00
 STA Server_01+1
.Server_01
 BCC Server_01
 LDA #&A9
 LDA #&A9
 LDA #&A9
 LDA #&A9
 LDA #&A5
 NOP
 LDA &FB:PHA
 LDX &900F
 LDY #0
.Server_02
 JMP Frame1

.Frame1
 LDA &900E:EOR aux_extbrd_1,Y:AND #&0F:EOR aux_extbrd_1,Y:STA &FB
 LDA aux_extbrd_1,Y:EOR bck_intbrd_1,Y:AND #&0F:STX &900F:EOR bck_intbrd_1,Y:TAX
 LDA &FB:STA &900E:STX &900F:]:IF NOT ntsc THEN [OPT pass:CMP (&00,X):]
[OPT pass:LDA bck_intbrd_1,Y:STA &900F:NOP:INY:CPY #&C1:BCC Frame1
 LDX #Frame2 MOD 256
 LDY #Frame2 DIV 256
 JMP Exit

.Frame2
 LDA &900E:EOR aux_extbrd_2,Y:AND #&0F:EOR aux_extbrd_2,Y:STA &FB
 LDA aux_extbrd_2,Y:EOR bck_intbrd_2,Y:AND #&0F:STX &900F:EOR bck_intbrd_2,Y:TAX
 LDA &FB:STA &900E:STX &900F:]:IF NOT ntsc THEN [OPT pass:CMP (&00,X):]
[OPT pass:LDA bck_intbrd_2,Y:STA &900F:NOP:INY:CPY #&C1:BCC Frame2
 LDX #Frame1 MOD 256
 LDY #Frame1 DIV 256

.Exit
 STX Server_02+1
 STY Server_02+2
 PLA:STA &FB
 JMP &EABF
]
NEXT
I use this raster routine in a small demo to implement a greyscale display for a graphics bitmap.
SparkyNZ wrote:So $9004 is the vertical beam position. Isn't there a high bit for the position, or is there only 256 lines due to the Vic'2 resolution? I know PAL has more scan lines than NTSC but I don't know the differences off hand.
This is not a PAL/NTSC issue. $9004 counts double raster lines. The lowest bit of the raster counter resides in $9003. For the positioning purposes during the init part, the double raster line precision is entirely sufficient.
If I understand correctly, your code waits for line 17 and then 19. Is that to make sure that we're at the top of the display? Do you check 19 to make sure that it isn't wrapping around at near the bottom of the display for example?
The ".Init" part syncs a VIA timer to the electron beam, to a cycle exact position. Note the start value of $9004 (here 17) is parametrized and controls the vertical positioning. This first check for a value in $9004, in ".Init_00", is done to avoid a race condition. The next check in ".Init_01" results in a remaining uncertainty of 7 cycles for the horizontal position. The following checks of $9004 reduce that uncertainty to 3, 1 and 0 cycles, respectively. At ".Init_04", the routine sits on the electron beam and the delay at ".Init_05" does the horizontal positioning before starting the VIA timer.
Then I see there's a delay at Init_06.. and this is called a few times with ever increasing line values in Y.
The delay at ".Init_06" waits until the next change in $9004 is about due.
[...] I am interested in knowing what the bare minimum requirements [...] for a stable raster routine [are.]
You see these requirements here. I doubt the code can be made much more compact.
I am also wondering how tight-loop timing would work within a "game loop" too. A raster routine would need priority to display the right colours at the right time.. so would a game loop do other things once the raster has reached the bottom of the screen.. or reached a line where we don't care about register hacking/changing anymore?
That is what interrupts are for.

Once the VIA timer has been synced, it triggers each frame at exactly the same vertical and horizontal position, which I defined in the example to suit the needs of the raster effect. However, the CPU itself has to finish the current instruction (and in very rare cases, also the next one!) before commencing the interrupt processing and this so-called "IRQ jitter" needs to be compensated once again. The example code does this at the beginning of the ".Server" part: the low byte of the VIA timer is read out at $9124 and then used to execute a variable "NOP slide" delay. After the final NOP instruction, the routine once again sits on the electron beam, everything else is just about counting cycles to place (colour) register changes at the intended positions.

Should the user intend to run other code with the interrupt, like a tracker routine, that can be appended after the raster effects part. Finally, the IRQ routine may set a workspace variable to inform the foreground process about the IRQ had taken place. The foreground process can then synchronize the animation by first clearing that workspace variable and then polling for it being set by the IRQ routine.
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: ** Stable rasters on the VIC-20

Post by Mike »

The raster routine in the start post can be assembled to work for either PAL or NTSC systems. The VIA timer needs different periods for each:

PAL: 312 rasters, 71 cycles/raster
NTSC: 261 rasters, 65 cycles/raster

The NTSC values assume a non-interlaced display. The timings for interlaced displays on NTSC have been covered elsewhere.

The VIA timer counts down and underflows from $0000 to $FFFF before reloading the latch value. This is the reason the latch value is the interrupt period minus 2:

PAL: $5686 = 22150 = 312 x 71 - 2
NTSC: $4243 = 16963 = 261 x 65 - 2

This is reflected in the conditional assignment to the 'cycles' variable in the first lines of the source.
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: ** Stable rasters on the VIC-20

Post by Mike »

The compensation of the IRQ jitter warrants a closer look how the interrupt processing commences:

As the VIA timer underflows to $FFFF, it asserts the IRQ signal. The CPU finishes the currently executed instruction (in very rare cases, also the next one, see below), then reads the opcode byte of the next instruction (but discards it) and then 'injects' a BRK instruction into the instruction decoder:

Code: Select all

                                               Bxx        3  $00, $FF, $86
                                    ..$FF      RMW ABS,X  7  $85..$7F
       00        BRK      (1+)7  $86..$7F                    $7E..$77
.FF72  48        PHA          3  $7E..$7C                    $76..$74
.FF73  8A        TXA          2  $7B..$7A                    $73..$72
.FF74  48        PHA          3  $79..$77                    $71..$6F
.FF75  98        TYA          2  $76..$75                    $6E..$6D
.FF76  48        PHA          3  $74..$72                    $6C..$6A
.FF77  BA        TSX          2  $71..$70                    $69..$68
.FF78  BD 04 01  LDA $0104,X  4  $6F..$6C                    $67..$64
.FF7B  29 10     AND #$10     2  $6B..$6A                    $63..$62
.FF7D  F0 03     BEQ $FF82    3  $69..$67                    $61..$5F
.FF82  6C 14 03  JMP ($0314)  5  $66..$62                    $5E..$5A
       D8        CLD          2  $61..$60                    $59..$58
       38        SEC          2  $5F..$5E                    $57..$56
       A9 xx     LDA #$xx     2  $5D..$5C                    $55..$54
       ED 24 91  SBC $9124    4  $5B..$58                    $53..$50
The BRK instruction normally takes 7 cycles, but is noted with 8 cycles here to account for the discarded opcode fetch.

$FF72..$FF82 show the instruction cycles spent in the KERNAL IRQ handler. The left "$xx..$xx" column shows how the values in the low byte of the VIA timer count down in the case of minimum delay. For PAL, this ends up with $58 in the last cycle of the SBC $9124 instruction, which is exactly the value that SBC then uses to subtract it from the 'phase' value. The accumulator then contains a 0 for the minimal delay until the IRQ processing commenced.

A similar analysis leads to $15 as 'phase' value for NTSC: using the low bytes of the respective latch values (PAL: $86, NTSC: $43), $86 - $58 = $43 - $15 = $2E = constant.

The right "$xx..$xx" column shows the VIA timer values for maximum delay: when the interrupt is asserted during the second cycle of a taken 3 cycle branch instruction, the next instruction is also executed! In the worst case (assuming only the documented NMOS instructions are used and there are no prolonged periods of disabled interrupts) a RMW ABS,X instruction follows, which then takes 7 cycles. This results in 8 cycles for the maximum delay after the SBC $9124 instruction.

These 9 cases (0 to 8 cycles delay) are then compensated by aforementioned "NOP slide": the branch length of the BCC instruction at ".Server_01" is modified with the value in the accumulator after SBC $9124 to skip a variable amount of the slide. The instructions in the slide are carefully crafted so 1 cycle less is waited for every increase of the accumulator value by 1.

When the NOP instruction is reached, the IRQ jitter has been fully compensated.
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: ** Stable rasters on the VIC-20

Post by Mike »

Here is a collection of some more demonstration programs that use this method:
  • the display routine of the 208x256 VFLI mode,
  • the panning viewer for VFLI images on unmodified VIC-20s (showing a 72x256 window into the picture),
  • a rendition of New Order's "Music Complete" album cover as MINIGRAFIK picture, using in-line splits (see third post),
  • Two new PETs for your VIC-20s (actually, three) and
  • a demo crafted on and for Vintage Computer Festival Europe in 2018.
Many of the new graphic modes crafted by tokra also use stable raster routines to ensure cycle exact VIC register writes and updates of the internal RAM. See for example '168160-$a000.asm' in 'hfli.zip', lines 73..112 and 1422..1436:

Code: Select all

[...]
setupirq
        lda     #$86    ; 22150 MOD 256
        sta     $9124
        ldy     #$10
i0      cpy     $9004
        bne     i0
        iny
        iny
i1      cpy     $9004
        bne     i1
        jsr     i6
        iny
        cpy     $9004
        beq     i2
        nop
        nop
i2      jsr     i6
        nop
        iny
        cpy     $9004
        beq     i3
        bit     $24
i3      jsr     i6
        nop
        iny
        cpy     $9004
        bne     i4
i4      ldx     #$06    ; position
i5      dex
        bne     i5
        lda     #$56
        sta     $9125
        cli
        rts

i6      ldx     #$17    ; delay
i7      dex
        bne     i7
        nop
        rts
[...]
        
[...]
irq     cld
        sec
        lda     #$58
        sbc     $9124
        cmp     #$0a
        bcc     s0
        jmp     $eabf
s0      sta     s1+1
s1      bcc     s1
        lda     #$a9
        lda     #$a9
        lda     #$a9
        lda     #$a9
        lda     #$a5
        nop
[...]
(the setup of the IRQ vector happens during initialization of the alternate screen editor)
SparkyNZ
Vic 20 Enthusiast
Posts: 153
Joined: Tue Jan 18, 2011 2:23 am

Re: ** Stable rasters on the VIC-20

Post by SparkyNZ »

Mike wrote: Fri Sep 22, 2023 8:21 pm The lowest bit of the raster counter resides in $9003
I wouldn't have figured this out without your help or Compute's Programming the Vic book. The Programmer's Reference Guide is pretty poor :-)

Image

I found some time and managed to get a stable raster interrupt built. Really useful to have that explanation - thanks Mike.
User avatar
pixel
Vic 20 Scientist
Posts: 1358
Joined: Fri Feb 28, 2014 3:56 am
Website: http://hugbox.org/
Location: Berlin, Germany
Occupation: Pan–galactic shaman

Re: ** Stable rasters on the VIC-20

Post by pixel »

I use this raster routine in a small demo to implement a greyscale display for a graphics bitmap.
Almost dropped my coffee pot.You should fire your PR manager and make a home page with a list of your amazing achievements.
A man without talent or ambition is most easily pleased. Others set his path and he is content.
https://github.com/SvenMichaelKlose
User avatar
huffelduff
Vic 20 Hobbyist
Posts: 118
Joined: Sat Sep 05, 2020 9:14 am

Re: ** Stable rasters on the VIC-20

Post by huffelduff »

Hi Mike, Pixel and SparkyNZ,

I'm working on 2 games which need some stable raster routines, so this thread is really helpful.

Thanks a bunch

H
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: ** Stable rasters on the VIC-20

Post by Mike »

I should add a few points which could be taken for granted, but still need to be accounted for reliable operation:

1. Both the initial sync routine and the interrupt server routine are extremely sensitive to timing. No interrupt must be allowed in their tracks. The initial sync does a SEI/CLI bracket, and the interrupt server anyhow executes with IRQs disabled. The two routines are not especially hardened against an NMI though, as for most times on the VIC-20 only the Restore key acts as an NMI source. An NMI during the initial sync may permanently shift the position of the stable raster, any NMIs happening while the raster effect is done will shift the timing for that frame, but this corrects itself on the next interrupt execution.

2. The routines assume that all the branch instructions do not leave the page for their branch target and thus execute in either 2 or 3 cycles (taken branches that leave the page need 4 cycles). If this is not honoured, the initial sync may fail in some circumstances, but work in others, depending upon where the second raster counter check bailed out. During the interrupt server, the timing might permanently shift in unexpected ways. It is recommended to keep the code of each of the two parts - initial sync and interrupt server - within a single page. This is already the case for the initial sync, and for the example, also for the interrupt server, but you will have to be wary about the 4 cycle branches for any own implementation of the interrupt server.

3. The very first check of the raster counter at 'line' (with the next check following at 'line'+2) is important to avoid a possible race condition at 'line'+2. It must not be left out!

4. The horizontal fine positioning should be done in the initial sync (with the loop before the final write to $9125 to (re-)start the timer), not with a delay loop after the NOP slide in the interrupt server. Doing so like the latter permanently wastes cycles each frame which were otherwise available for the main program.

5. If your actual demand for the horizontal fine positioning seems to ask for a negative delay in the initial sync routine, decrement the 'line' value by 1 and use a long delay instead.
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: ** Stable rasters on the VIC-20

Post by Mike »

In another thread, I documented the workings of the inner loop in the example of this thread's start post a little more in detail:

Code: Select all

[...]
.Frame1
 LDA &900E                                ;    Load current aux. colour/volume register,
 EOR aux_extbrd_1,Y                       ;    change the aux. colour,
 AND #&0F                                 ;    but keep the volume
 EOR aux_extbrd_1,Y                       ;    and
 STA &FB                                  ;    store in $FB for later use.
 LDA aux_extbrd_1,Y                       ;    Calculate next combination
 EOR bck_intbrd_1,Y                       ;    of exterior border colour
 AND #&0F                                 ;    plus background colour from ...
 STX &900F                                ; ** re-instate exterior border colour at right edge of display window (keeping the current background colour)
 EOR bck_intbrd_1,Y                       ;    ... the table data and 
 TAX                                      ;    keep in X for later use.
 LDA &FB                                  ;    Load $FB and
 STA &900E                                ; ** write $FB as new value of aux. colour/volume register (immediately before horizontal retrace).
 STX &900F                                ; ** Change to new exterior border colour and background colour during horizontal retrace.
 ]
 IF NOT ntsc THEN [OPT pass:CMP (&00,X):] ;    6 cycles extra delay for PAL
[OPT pass
 LDA bck_intbrd_1,Y                       ;    Load combination of background colour and 'interior border colour' for the %01 multicolour pixels and
 STA &900F                                ; ** write background/border register at left edge of display window.
 NOP                                      ;    Not much leeway here.
 INY                                      ;    Count ...
 CPY #&C1                                 ;    ... 192+1 ...
 BCC Frame1                               ;    ... lines.
 [...]
The "**" markers show where the stores to the VIC registers happen. "Frame2" works the same on another set of tables in alternate frames. The demo thus combines time and raster line interlacing for a better definition of the greyscales. This technique works for both NTSC and PAL.

Attached (download) you find a PDF file which shows above code arranged in a FridgeGrid-like manner. The column "horizontal display window" shows the timing of the MINIGRAFIK display. The stores to the VIC colour registers are supposed to take effect on the next cycle ... on real hardware, an extra hires pixel is still displayed in the old colours though, and nothing can be done about that.
Post Reply