Commodore 64 Video — A Guided Tour
Jim Butterfield, Associate Editor
In this, the final installment of our exploration of the Commodore 64's unique video design, we look at a solution to the pesky "hiccup" screen glitch.
Last time, we looked at a simple program to split the screen of the Commodore 64. It was similar, but not identical, to this one:
100 FOR J = 828 TO 862 : READ X 110 T = T + X : POKE J, X 120 NEXT J 130 IF T <> 3929 THEN STOP 200 DATA 173, 25, 208, 41, 1, 240, 25, 141, 25, 208, 162, 146, 160, 6, 173, 18 210 DATA 208, 16, 4, 162, 1, 160, 0, 142, 18, 208, 140, 33, 208, 76, 188, 254, 76, 49, 234 300 POKE 56333, 127 310 POKE 788, 60 : POKE 789, 3 320 POKE 56333, 129 : POKE 53274, 129
Our previous example split the screen into two sections: graphics and text. This one splits the screen into two background color areas. It makes it easier for us to see the glitch — the hiccup that occasionally disturbs our screen split. By the way, it's easier to see the problem when you are using the keyboard.
Why The Problem?
Here's where the problem comes from: the timer interrupt strikes about every 1/60 of a second. The screen display, too, runs at a rate of about 60 times a second. But they are not synchronized. The two processes run at similar, but not identical, speeds.
Now, every once in a while, the timer interrupt hits just before the raster interrupt. The timer interrupt has quite a few jobs to do: update the TI$ clock, check the cassette motor, flash the cursor, and check the keyboard. It takes time to do these jobs, and extra time is required if a key is being pressed.
Suppose we have just started on the timer interrupt, and the raster scan says, "I'm ready!" Sorry, raster, we're already into an interrupt routine, and other interrupts are locked out until we have finished. By that time, the screen scan might have moved along a few lines, and our split screen has crept from its normal position.
Some Possible Fixes
There are several possible approaches to fixing this jitter. The ones that come to mind first are complex; in a moment, we'll move on to an easy one.
When the timer interrupt strikes, we could ask it to look at the raster and see if the scan was close to the interrupt point. If so, we might wait things out, or skip part of the timer interrupt jobs. Messy.
The timer interrupt could "unlock" the interrupt very quickly, using a CLI command. That way, we could interrupt the interrupt program itself to do the split screen job. Better, but some programmers feel it's dangerous to allow this kind of thing to happen.
A Better Way
There is an easier way: shut the timer interrupt off completely, and do its various jobs with our own programs. This seems complex, but it's not. We can call the timer interrupt routines ourselves, whenever it's time.
Let's look a little more closely into the timing of these interrupts. We expect to cause a raster scan interrupt about 120 times a second. That's twice as often as the timer interrupt needs to be handled. So our raster program could occasionally call in the timer interrupt program.
It seems that we could accomplish the task easily by calling the timer interrupt routines every second raster interrupt. That would certainly do the job, but there's a better way.
Even though we've shut off the timer inter-Table 1: 6566 Video Chip C64 Control and Miscellaneous Registers
Table 2: 6566 Video Chip C64 Sprite Registers
rupt, it's still signaling when the time is ready. Let's review: the timer leaves a signal in hex address $DCOD (56333) whenever it counts down to zero. Normally, this signal triggers the interrupt line (IRQ) and causes the processor to be interrupted. But we may "break" the connection between the timer signal and the interrupt line. In this case, the timer will not cause an interrupt, but the signal bit will still flash when the appropriate time has come.
We can see the plan in Figures 1 and 2. We will disconnect the timer from interrupt, and service it ourselves when it flashes. Easier done than said. Let's look at the machine language coding:
033C A9 01 INTR LDA #$01 033E 8D 19 D0 STA $D019
Raster interrupt is now the only game in town, so we don't need to test for it. We must, of course, turn off the raster interrupt flag.
0341 A2 92 LDX #$92 0343 A0 06 LDY #$06
Setup for top of screen. Next interrupt, line 92 hex; new color, number 6.
0345 AD 12 D0 LDA $D012 0348 10 04 BPL MID
If it's really the top of screen, we can skip ahead. Otherwise, we change for mid-screen – line 1, new color, number 0:
034A A2 01 LDX #$01 034C A0 00 LDY #$00
Now we're ready to do the job, wherever the screen is:
034E 8E 12 D0 MID STX $D012 0351 8C 21 D0 STY $D021
The job is done. Now let's see if the timer interrupt is calling for action:
0354 AD 0D DC LDA $DC0D 0357 29 01 AND #$01 0359 F0 03 BEQ SKIP
If we didn't skip, the timer wants attention. Call it in:
035B 4C 31 EA JMP $EA31
If we did skip, the timer isn't needed. Quit with:
035E 4C BC FE SKIP JMP $FEBC
We must remember, of course, to: turn off the timer interrupt; set the IRQ vector to our new code; and turn on the raster interrupt. We'll do all that in BASIC. Speaking of which.…
Here's the same program in BASIC.
100 FOR J=828 TO 864 : READ X 110 T = T+X : POKE J,X 120 NEXT J 130 IF T<>4077 THEN STOP 200 DATA 169, 1, 141, 25, 208, 162, 146, 160, 6, 173, 18, 208, 16, 4, 162, 1 210 DATA 160, 0, 142, 18, 208, 140, 33, 208, 173, 13, 220 220 DATA 41, 1, 240, 3, 76, 49, 234, 76, 188, 254 300 POKE 56333, 127 310 POKE 788, 60 : POKE 789, 3 320 POKE 53274, 129
Now we have a rock-solid color change at the appropriate screen point. No creeping, no jittering, no hiccups.
We've only touched upon the techniques of raster interrupt. A whole host of new possibilities open up with its use.
But we've shown it can be done, and some of the techniques that can be used to do it.
Copyright © 1983 Jim Butterfield.Figure 1: "Conventional" coding requires the program to distinguish between the two live timing sources. It may also cause timing jitter.
Figure 2: Single interrupt coding gives priority to the time-sensitive raster job.