Page 1 of 3

Stable Raster Interrupts

Posted: Tue Sep 15, 2015 7:16 am
by DrVeryEvil
I am looking at some code on how to make a stable raster interrupt on the VIC 20, Commodore Hacker #10, and was wondering if anyone can tell me the purpose of the BIT $24 in the code. Is that just a way to burn some cycles? :?

Re: Stable Raster Interrupts

Posted: Tue Sep 15, 2015 8:00 am
by tokra
Yes :-)

NOP = burn 2 cycles
BIT $xx = burn 3 cycles

Re: Stable Raster Interrupts

Posted: Wed Sep 16, 2015 5:04 am
by DrVeryEvil
Would it be more efficient to syncronise to the high bit of $9003? This could be done with a simple BIT$9003, instead of actually reading the register.

Re: Stable Raster Interrupts

Posted: Wed Sep 16, 2015 6:04 am
by tokra
Not if you want to sync to a specific rasterline. Unlike the C64 the high bit in $9003 is actually the lowest bit of the rasterline-counter, not the highest. So it switches after every rasterline.

Furthermore, regarding the raster-routine: it may be possible that the check for the first raster actually happens WHILE that rasterline is being displayed. Chances should be 1 in 131, but I noticed this behaviour in developing my graphic modes as well. Since then to be safe I do it like this:

Code: Select all

        ldy        #$01        ; make sure next command really STARTS at line #$03 and not SOMEWHERE in it
raster0 cpy        $9004
        bne        raster0
        ldy        #$03        ; wait for this raster line (times 2)
raster1 cpy        $9004
        bne        raster1     ; at this stage, the inaccuracy is 7 clock cycles
Note: you must do the second check at least 2 values later (like here #$01 then #$03), since you may be at the very end of #$01 when you check and this could again lead to incorrect timing.

Re: Stable Raster Interrupts

Posted: Wed Sep 16, 2015 8:03 am
by DrVeryEvil
I see the bit instruction is of no use in this, because the value needs to be stored for the compare that comes later, and that makes the code take up more bytes.

Code: Select all

		ldy #9
		bit $24
1$	 ldx $9004
		txa
		bit $24
		bit $24
		ldx #21
		dex
		bne *-1
		cmp $9004
		bcs *+2
		dey
		bne 1$ 
I am still curious why the bit instruction is needed after .Y is loaded with 9. There is no need to waste 3 cycles at that point, is there? In your code above, wouldn't there be 2 cycles added to the 7 for the last branch? Ok, scratch that last question. :lol:

Re: Stable Raster Interrupts

Posted: Thu Sep 17, 2015 9:16 am
by DrVeryEvil
I have also found an error, I think, in the refresh rate calculation that Marko does, in claims that Commodore does not meet the video standard of the time. Marko gives 261 as the number of raster lines drawn by the NTSC 6560, which does give quite a bit of error on the refresh rate. I believe he is forgetting that the raster values in $9004 go from zero to 130, not one to 130. If you count zero to 130, you have 131 values. Multiply times two and you get 262. Then add one for the low bit value in $9003, gives you 263 raster lines. The NTSC standard used to be 262.5 lines, before high def digital TV. Even if the 6560 doesn't account for that half line, the calculation using 262 or 263 comes very close to a refresh rate of 60hz. Am I doing this correctly?

Re: Stable Raster Interrupts

Posted: Thu Sep 17, 2015 10:18 am
by tokra
No, you really do have only 261 lines in NTSC. Rasterlines 0-129 have two values each in $9003 (0 and 1), while rasterline 130 has only 0 in $9003 = 130*2+1=261 rasterlines.

In Interlace-mode you really have 525 rasterlines: Rasterlines 0-130 have two values each in $9003 for both half-frames = 524 lines. Plus you have rasterline 131 in one half-frame (forgot which) which has only 0 in $9003 for a total of 525 rasterlines. That means in NTSC-interlace you have to stabilize to line 131.

Re: Stable Raster Interrupts

Posted: Fri Sep 18, 2015 6:05 am
by Kakemoms
Yea, its quite confusing with respect to the 6561-101 not supporting interlaced either. This is a good source: http://tinyvga.com/6561

Chip 6560-101 6561-101
System NTSC-M PAL-B
Cycles/line 65 71
Lines/frame 261 312
- interlaced 525 N/A
Crystal 14318181 Hz 4433618 Hz
Bus clock crystal/14 crystal/4
Screen width 210 233
Screen height 233 284

So, like me, if you design for PAL-B and want to go to the other.. well, not really a good idea I guess.

Re: Stable Raster Interrupts

Posted: Fri Sep 18, 2015 11:00 am
by FD22

Re: Stable Raster Interrupts

Posted: Sun Sep 20, 2015 7:47 am
by Mike
DrVeryEvil wrote:

Code: Select all

[...]
I am still curious why the bit instruction is needed after .Y is loaded with 9. There is no need to waste 3 cycles at that point, is there?
The answer lies in the bit of code you left out, namely before the sync loop you posted:

At first, the start of a certain line is waited for in the way tokra already described: wait for (double-)line X-2, then wait for (double-)line X.

The raster register changes at one certain point during horizontal retrace. As long as the waited-for value doesn't appear, the wait loop has a jitter of 7 cycles: 4 because of the CMP instruction, 3 because of the branch when it is executed. The CMP instruction reads the register in its fourth cycle and compares it directly; on success the not executed branch needs 2 cycles. Now, depending on all of the preceding code ...

... you may be 'lucky', and just are spot on the transition (in 'cycle' 0), then BNE executes for cycles 1 and 2, and the instruction after BNE starts execution in cycle 3.

... or you have 'bad luck', and CMP just sees the value of the preceding (double-)line for the last time it's there (i.e., in 'cycle' -1) - then the loop executes BNE once in cycles 0, 1 and 2; another CMP (which succeeds) in cycles 3, 4, 5 and 6; and finally the non-executed BNE in cycles 7 and 8. Thus, the instruction after BNE starts in cycle 9.

Thats means, with the possible positions 3, 4, 5, 6, 7, 8 and 9 there are 7 different positions, where the simple wait loop can come out, and for this reason, this still isn't stable.

Now, the sync loop works by wasting 129 cycles per double-line (for NTSC), when CMP $9004 returns the value of the next double-line, and wasting 130 cycles per double-line when both LDX and CMP instructions return the same value. In the former case, the start of the loop clocks in one cycle earlier, and thus 'drifts' to the left. In the latter case, that iteration and all following ones are kept in lock with the raster beam.

The BIT instruction now makes sure this final sync loop doesn't already start too early with possibly matching LDX/CMP values! Ideally, the LDX load fetch and CMP compare fetch should be 129 cycles apart. But this isn't the case here, so the sync could report a false positive, when the LDX is little bit too far left. Rather a little more time is wasted (but not too much), to ensure there can't be a false positive. Then, within 9 iterations, the sync loop locks in.

...

The technique described here is mainly used to define a exact position to start the timer.

The interrupt processing itself requires another compensation, as it is only started when the current instruction has finished, which will introduce another jitter of up to 7 cycles. The interrupt service routine has then to execute a variable delay to counteract that jitter and have the rest of the ISR execute (once again) in sync with the raster beam. The low-byte of the timer is read to derive that variable delay.

Please take a look at the following threads for examples: VIC 20 in Black and White mode and ** New Frontiers in VIC-Hires-Graphics, Part 10. Actually, I use another technique for the initial sync, which uses a binary decision tree and syncs in faster, but needs more code.

Re: Stable Raster Interrupts

Posted: Sun Sep 20, 2015 4:59 pm
by groepaz
iirc marko makäla described all this stuff in much detail in some C=Hacking issue ("stable interrupts with auxiliary timer" or sth like that).

Re: Stable Raster Interrupts

Posted: Mon Sep 21, 2015 12:03 pm
by Mike
Well, yes. The OP referred to C= Hacking #10, indeed. :)

At the time I found that article (years ago), I actually couldn't make much sense of Marko's method. For this reason I devised my own method: as said, a binary decision tree, which syncs in faster. I also need only one timer. In the ISR, the compensation of the interrupt delay then is done by branching over a chain of LDA #$A9 instructions. :mrgreen:

Re: Stable Raster Interrupts

Posted: Mon Sep 21, 2015 1:17 pm
by groepaz
ooops =)

my favourite became the "ninja method", which puts a jmp opcode into one (unused) CIA register and then jumps to it which automagically compensates for the interrupt delay. wastes 7 pages (or whatever amount of delay you need to compensate) though - not sure if it could be adapted to VIA too.

Re: Stable Raster Interrupts

Posted: Mon Sep 21, 2015 4:27 pm
by tokra
Yeah, that's pretty f*cked up. He explains it nicely here:

https://www.youtube.com/watch?v=po9IY5Kf0Mo#t=1021

Re: Stable Raster Interrupts

Posted: Wed Sep 23, 2015 2:50 pm
by Mike
Could go along: put $4C, $00 into $91x2, $91x3; let T1 count down from 71(-2) or 65(-2) so it repeats every raster, etc. - up to the point the read of T1-L as high-byte of the JMP instruction target auto-acknowledges the IRQ or NMI.

Unfortunately, the hardware interrupt vectors are fixed on the VIC-20. The standard NMI handler at $FEA9 is reasonably short (just a SEI - for whatever reason - and an indirect JMP), but the IRQ handler at $FF72 goes through the whole lengths to determine whether it was a BRK or a hardware interrupt. :(