Thursday, 18 April 2013

In Which We Do Something New (Possibly)


I've got an idea for a new way to do stable raster synchronisation. I've looked around, and I don't think it's been done before - I'm working on the code now, and if it does what I think it will, I'll post it and an explanation of how it works next time.

Here's how I got on to this: as you know from my last post, I'm reworking the display logic to draw the 25-row screen bitmap with the extra line in the middle of the screen instead of at the bottom - this makes things much nicer in terms of memory allocation, speed, colour resolution, and algorithm programming to do the refresh. It does, however, require two IRQ interrupts per frame in order to effect three raster splits; one right at the top of the screen to set the VIC up for the first 12 rows, and one at the end of row 12 to set things up for row 13. The third raster split occurs at the end of row 13 to set the VIC for row 14 onwards, but I don't need an IRQ for that as I'll just wait for that raster line after doing the housekeeping stuff (timers, cursor blink, keyboard scan, etc.) in the 8 lines of row 13 itself.

Now the IRQ at the top could theoretically occur largely anywhere on whichever raster line we choose, because it's in the top border and therefore invisible - if it happens to start halfway along a raster line and cross into the next, it wouldn't matter so long as VIC is ready to go by the time we get to the first raster line of the top display row. But the IRQ at the end of row 12 has to be spot on because we have a very limited amount of time before row 13 starts drawing, and we've also got to have a precise interval value to give the VIA#2 timer so that the top-of-screen IRQ happens at a specific spot after all, because it's another precise number of cycles after that at which the row 12 IRQ has to happen all over again.

What this all boils down to is that the IRQ trigger points have to be absolutely cycle-accurate - and that's traditionally a bit of a bugger to do on a machine with no raster interrupts (on the C64, for example, you can tell the VIC-II to trigger an IRQ at the beginning of a specific raster line). Even though we can watch the raster counter in a tight loop and wait for a target line, there's still up to 7 cycles of inaccuracy between the counter ticking-over to the target value and our code actually being in a position to act on it:

            lda #$_TARGET_LINE  ; [2]   set the target raster line number
.waitline cmp $_RASTER_COUNT ; [4] see if the VIC raster counter matches the target
bne .waitline ; [3/2] loop back and check again if not

You see? If the counter hits our target just before the cmp, it's 4 cycles for cmp + 2 cycles for bne, making 6 cycles after the counter actually was the value we're looking for before we're ready to do something about it. If the counter hits the target just after cmp, it's 3 cycles for bne + 4 for cmp + 2 for bne, making 9 cycles. The best-case scenario is where the counter hits the target just before the end of cmp, in which case we just clock 2 further cycles for bne - but that still means we could be anywhere from 2 to 9 cycles on from the counter hitting the target, with no way to know how much of that 7-cycle variability is in force. And those 7 cycles make a lot of difference when you're trying to get a precise raster effect - it's what gives rise to the 'jitter' you sometimes see at the edges of the screen when playing a game with a complex visual display.

So what to do? Doing a simple wait-loop such as the code above gets you to a specific target line, but at best you've got a 7-cycle unpredictability to deal with somehow. Plus of course that wait-loop sits there eating 100% CPU, and it's hardly appropriate for an OS to spend huge chunks of processor time doing nothing but watch a raster counter. We need a way to eliminate that jitter, and so far I've only found one person who's managed to do it reliably - a hot coder named Marko Makela who some years back wrote a routine to use two timers offset from each other to measure the inaccuracy and compensate for it. His original article is here, and it took me about four passes over it before I grokked what he was doing. :)

All well and good, you might say - so just use his code, and be done with it.

Well, there's two things wrong with that: first, I'm not using anyone else's code in VIC++ (although it is entirely possible that I'll write something at some point that has been done in a similar way before); and secondly, although Markos' code is undoubtedly very cool and gets the job done, I read it and thought it seemed a tiny bit awkward to have to tie-up two VIA timers to make it work. Yeah yeah, like I know any better - but sometimes ignorance can be an asset, and in this case I think I've hit on something that'll guarantee a reliable, stable, cycle-perfect raster sync without needing a second timer.

You may point and laugh next time, when I show you what I've done. ;)

No comments:

Post a Comment