Last week I said I was going to work on a smooth scrolling routine for the C64. But I’m as prone to creeping featurism as any other hacker, and at the last minute I decided to spruce it up a bit. Oh, let’s make it scroll at different speeds! I told myself, and Why not have it scroll in two different directions! Luckily, adding these features was the easy part. Getting the scroller to work at all was the tricky bit, but the video below shows what I accomplished.
The VIC chip has hardware support for smoothly scrolling the screen. Now, what that actually means is that there are three bits on one of the registers that you can set to scroll the entire screen as much as 7 pixels to the right. Hey, at least it’s a start.
So what happens when you want to scroll farther than that? Last time, I described how the screen is arranged as a 40×25 grid of characters. If you want to scroll 8 pixels to the right, you set the hardware scroll to 0, and then you go through each row of the grid and rotate each character one spot to the right (In other words, a row like “ABCDE” becomes “EABCD”). That’s 1000 copy operations, and another 1000 if you want it to work in color.
If you want to scroll continuously, then you just use the hardware scroll register to move from 0 to 7 pixels, and when it rolls over you move the contents of screen memory and start again. By the way, there’ll be a flickering column on the left side of the screen where the contents has been scrolled out of the way, but the VIC-II has you covered: Another bit in the scroll register will put you into 38-column mode. The video chip pushes the borders in by eight pixels on either side so that you can draw there without anyone noticing.
Now, actually implementing this is pretty tricky. It turns out that moving 1000 or 2000 characters on a C64 is a lot of work. My first, fairly naive attempt produced a flickering mess – it took me longer to rotate character memory around than it did for the video chip to display a frame. Aha! you’re thinking if you know something about graphics. He needs to use double-buffering! Well, that’s part of the solution, but remember that special memory attached to the VIC-II that’s used to store the color of each character? That color memory can’t be moved. You can’t double-buffer it. You’ve got to update that memory while the VIC-II is reading it and spitting out a video signal. You are – to borrow a phrase from Atari 2600 hackers – racing the beam.
The VIC-II outputs its video signal row-by-row, from the upper-left to the lower-right. You’ve got to make sure your changes are in place when the VIC gets to that part of the display. To get ready for that, I had to do two things. First, I optimized my code for rotating the characters around by using loop unrolling. That was surprisingly effective. The second thing was that I put all my drawing code in an interrupt handler (which is a topic worthy of its own post, some day), and I hooked up my interrupt so that it was triggered in sync with the VIC chip.
Even then, it takes me four frames to get ready for that one magic frame where I have to make all the characters appear one spot over from where they were. I start when the scroll register is at 5 (when I’m scrolling to the right). I copy the first 12 rows of character data into a second screen buffer, rotating each row by one character. During the next frame, I copy the rest of the character memory.
The tricky part happens when I copy the color memory. My interrupt triggers when the VIC has output the last row of the screen, so I have some free time while the chip draws the bottom border, and then the top border of the next frame. The scroll register is updated to 7 – as far to the right as it can go. I wait a little longer – until the VIC has drawn the first couple of rows of characters. Then I start rotating the characters in the top rows of color memory, while the chip is still drawing the bottom rows.
Then, we finally wrap the hardware scroll counter back to zero. While the VIC is drawing the borders, we tell it to start reading character data from the other character buffer, where the rotated data has been waiting for it. As the VIC starts drawing this frame, with the new character data and the freshly-rotated color memory, our code still has work to do – it needs to rotate the bottom rows of color memory before the VIC needs to look at them!
Whew! That’s a lot of work spread out over four frames. Of course, it could be compacted farther. I’m sure I could do all that in three frames; probably in two. There are techniques like speed code that are considerably faster than the copy/rotate routines I’m using. But doing it this way, I still have a fair amount of time left each frame that could be used for program logic, or sound effects, or whatever. This graphics hack is a long way from being part of a game, but it is a step in that direction.
All in all, I adore the intricacy of what’s going on here, and I enjoyed puzzling out how the timing and all the pieces fit together. I deliberately didn’t look at any existing solutions, so this one is all me – and, yeah, it’s probably terribly suboptimal. But it was fun, and that’s the goal. If you do want to see the code in all its gory detail (the labels, at least, are a complete mess), the source code and a .d64 disk image are here.