The text scroller. The single most used demo effect of all time. An effect that always fascinated me on the C64 back in the day, but (like with the colour cycle effect) something I could never get my head around all those years ago. I have often thought to revisit coding on the C64 JUST to successfully produce this, but never got around to it. Now is the time.
The time spent diving back into assembly language over the last two weeks has been quite fruitful. The lessons learned and knowledge gained will come in handy here. The goal is to create a simple, 1×1 character scroller. Once I can do that, it shouldn’t be too difficult to expand it further in the future to jazz it up.
The first step was to explore the $d016 register and see what effect it has. There are specifically 3 bits on this register that interest me here – the lower 3 bits. This allows us to generate 8 pixels of scrolling on the horizontal plane. The first interesting thing I found was that the scrolling is only done in 1 direction – to the right. Most text scrollers go from left to right, which means I would have to start with a scroll value of 7 and then decrease it towards 0 to get the left to right scroll.
Since I had not touched the $d016 register before, I figured a quick routine was in order to explore it a little better. To start with, I won’t worry about a single line text scroller, but see what effect modifying those lower 3 bits has on the entire screen. That way, as I learn more, I can transform the code into the text scroller I want.
To begin with, I setup the code to have my routine triggered via a raster interrupt – on the first scan line of the screen (0).
With the interrupt set up, it was time to write the routine to scroll modify the bits on the scroll register. The first step was to implement a delay (or smoothing) to the routine. Since it’s going to be called every 1/60 of a second, that will be too fast. The first few lines perform a delay counter – so the actual update code of the routine is skipped several times which will help to slow it down.
Once the delay has expired, I simply load the current offset value, increment it and then loop it once it hits reaches 8. I’m also careful to retain the state of the higher 5 bits of the $d016 register. This isn’t mandatory here because I know what I want to happen, but in terms of being correct, I will retain their state and just modify the lower 3 bits for the horizontal scrolling.
Compiling the routine and running it gives me a full screen scrolling 8 pixels before it loops back to 0. This is what I was expecting. The register affects the entire screen and scrolls the content to the right.
To use the register to scroll left, it was as simple as setting the offset value from 00 to 07 and then changing the update code to decrease the offset and then loop back to 7 once it passed 0. The result showed the screen starting a full 8 pixels to the right, and then scrolling left.
With those two examples under my belt, it felt time to try and produce a 1×1 text scroller. Before worrying about reading a string of text and looping a message, I figure let’s keep it simple. First step is to get a single row of text scrolling. Best way to test this will be to fill the bottom row of the screen with characters and then write a routine that scrolls it across the screen. If I can get that working, then it’s should be a simple process to get it working with an actual message. Starting with the full screen scroll code, I added an init block of code to output a series of characters to the bottom row of the screen.
Next, I wanted to think about exactly what I’d need to do to achieve this. I want to scroll 1 row and only 1 row and it will be the bottom row on screen. So it seams reasonable I would need chain two interrupts. The first, I’ll trigger at scan line 0. This will clear all 3 lower bits on $d016 – meaning no horizontal scrolling. Once that triggers, it can update the interrupt pointer to point at the scroll routine and also update the scan line to trigger it on. The second interrupt will now trigger on the first scan line of the final row. The scroll routine will set the current scroll offset onto $d016 (a value from 0 to 7). It can then update the interrupt pointer to point back to the original noscroll routine which is set to trigger on line 0. This should then only have the scroll active on the final text row and leave the rest of the screen unaffected.
So, in the init code, I changed the interrupt pointer to point at a new label called noscroll. I added a label for noscroll and it simply retains all but the 3 lower bits on $d016. It then sets a new scan line trigger and updates the interrupt pointer to point at the scroll code.
As with before, the scroll routine reads the current scroll value and applies it to $d016. It also has a delay (smoothing) counter to slow the scroller down.
Once the delay expires, the offset value is decreased. One change here however is now, once the offset value goes below zero (negative flag is set) it will not only reset the offset value back to 7, it will also execute a small routine to copy the characters on the last row left 1 space. This small change is what will cause our text to actually scroll.
The final part of the routine update the interrupt pointer back to noscroll and sets the scan line trigger to 0. Building this code and running it produced a single line of text, scrolling across the bottom of the screen. The last character repeats, but that will be fixed once I add the last piece of this text scroller – an actual message to scroll.
Adding a message is very simple. I create a character offset and message label at the end of the routine like this:
The nextchar variable will point to which character to read next from the message and this updates everything we shift the right to the left. When the character is read, we store it in the final character position on the row. This then scrolls on, once the scroll offset hits 0, all characters on the row are shifted left 1 space, the next character is added to the final position on the row and the scroll offset is reset to 7. Repeat. A last little check is added to detect a character code 255, which indicates to the scroller that it’s the end of the message and it should reset the nextchar offset to 0.
I went a little bit further and added some code to update the colours at the head and tail of the scroller to fade the text in and out of the screen. The colour is read from a colour table and it uses the current scroll offset to pick a colour for the specific character. It uses 4 colours across 2 character locations which gives a subtle fade in and out effect.
..and that’s it. The effect I’ve wanted to do on the C64 for so long took only a few hours to go from nothing to several routines where I explored the horizontal scroll effects on $d016 to produce an actual text scroller.
This is of course a very simple scroller. But it’s the starting point.
The code is available on github here