Text scrollers are quite common on the Commodore 64, due to the ability of the VIC-II chip to perform fine scrolling. The VIC-II can scroll the entire screen horizontally or vertically by one character, in eight steps. So, on each step it is possible to make a one hi-res pixel scroll.
Coarse scrolling is also needed at a certain point (that is, a scroll by one character). This must be coded on software, and it allows to make room for the new data to be shown. Then, the fine scrolling from the VIC-II chip takes place, again.
The final result of combining fine scrolling and coarse scrolling is a smooth scrolling. In order not to have any flickering, this movement must be synchronized with the rasterbeam, so that information is updated only when it is not currently shown (that is, when the rasterbeam is away from that information).
Using machine language, coarse scrolling of a text is not a problem. It is not difficult to figure out a fast enough routine. But, if we want to use BASIC, obtaining a fast enough coarse scrolling routine may be a challenge.
Years ago, I remember I managed to code a horizontal text scrolling routine in BASIC. But, it was a bit limited. The coarse scrolling was not fast enough and in order not to have any flicker, I was forced only to scroll a fraction of a text line, not the entire line. I think I was able to scroll a 30 / 32 characters text line. As smooth scroll requires shrinking the borders (both sides), I had to use a sprite to act as a “masking border” for the new upcoming information. Also, that scroll routine used string manipulation BASIC functions, and that brought to a slow routine (25 fps I think it was) and it was not slow garbage collection issues safe.
So, I decided to use another approach. My idea is: you can take a string variable, change its pointer to the String Storage Area each time you have to do a coarse scroll, and print it on and on. The pointer will be updated for each scroll so that it will somehow “move” the text.
Of course, the result may be not worth the effort, but since I am currently studying Commodore BASIC a bit, I decided to take this route to realize some aspects of its inner workings. If you are not willing to read about all of these details, you may just go at the last paragraph of this article, which explains a bit the general algorithm for horizontal scroll. Maybe you may use those hints to code your own machine language text scroller.
Here is the code I came up with. This program is intended for PAL machines only and requires a stock C64 with no cartridges plugged-in.
There are many things inside this simple little program. In order to explain this code, we need to have a look at BASIC pointers and at the way string variables are stored in memory.
Commodore 64 BASIC pointers
On Commodore 64 BASIC, locations such as the address where a BASIC program begins and the address where BASIC variables are stored from are not fixed. There are some pointers containing the address of those locations. A pointer consists of two locations (on this case, on page zero) containing the low byte and the high byte of the memory address being referenced.
The Start of BASIC pointer is on locations 43 and 44 decimal. It contains the starting address of the BASIC program code stored in memory. On startup, those locations contain the values 1 and 8 respectively. As the Commodore 64 is an eight bit machine, each location can have numbers not greater than 255. 256 different values can be stored on each location (from 0 to 255). So, to store a value such as 1000, for instance, we need two locations. One location (the high byte), will tell you how many times the value 256 stays on the given number. The other location (the low byte), will contain the “remainder”.
So, to store the value 1000, we need to calculate high byte and low byte:
Hi byte = INT (1000/256) = 3 Low byte = 1000 - (Hi byte * 256) = 232
Remember that the low byte must come first in memory, so to represent the value 1000 on the Commodore 64 memory, we need to poke the value 232 on a location, and the value 3 on the following location (low byte / high byte format). This is because the 6510 processor is designed to “encode” the addresses this way to gain speed.
Now, let’s go back to the Start of BASIC initial value. As location 43 holds the value 1 and location 44 holds the value 8, the start of BASIC address is:
1 (low byte) + 8 (high byte) * 256 = 2049
2049 is actually the address (in decimal) BASIC programs usually start from ($801 hex).
Please note that if you want to increment by one the address a given pointer makes reference too, you just need to increment by one the low byte of that pointer, as long as that byte is less than 255. Otherwise, if the low byte is 255, you need to increment the high byte of the pointer by one, and you also need to set the low byte equal to zero.
The Start of Variables pointer is on locations 45 and 46 decimal. It contains the address variables are stored from. On startup, it contains the same address as the Start of BASIC plus two, as no BASIC code is currently in memory. The two additional bytes only contain a zero value each. Those zeroes are used to mark the end of the BASIC program.
This pointer is very important for the above program. It will allow us to see where string variables are so that we can directly manipulate them by changing the content of RAM.
The start of array pointer is on locations 47 and 48 decimal. The end of array pointer can be found on locations 49 and 50. Those pointers delimit the area numeric arrays and string arrays descriptors are stored. However, we will use no arrays on this program.
The Bottom of Strings pointer, on locations 51 and 52 decimal, is very important at this moment because it tells us where strings can be found in memory. As you will see, we will create string variables and we will search for the strings those variables make reference to in the String Storage Area. The Bottom of Strings pointer will just tell us where the text to scroll is stored in memory. At startup, it makes reference to the address 40960 decimal, and values contained on locations 51 and 52 are 0 (low byte) and 160 (high byte). As long as strings are created, those will be stored from 40960 on in a backward way. So, as long as strings are created, this pointer will be moving towards the End of Arrays address.
Finally, the Top of Basic pointer can be found on locations 55 and 56 decimal. It contains the last address available to BASIC. On startup, this pointer makes reference to the same address as the Bottom of strings pointer. Usually, the Top of BASIC stays the same all the time. It can be changed however, in particular situations, along with the Bottom of strings pointer, to reserve some free RAM for data, safe from the BASIC which may otherwise overwrite it.
How variables are stored in memory
Let’s RUN our simple scroll program and let’s stop it right after the string variable D$ has been created (to do this, you can temporary replace the assignment R=53265 on line 14 with a STOP instruction.
Let’s have a look at the Start of Variables pointer and let’s see how variables are stored. Please see the following picture.
As you can see, variables start from location 2611 decimal. With a simple FOR… NEXT cycle in direct mode, we can print on the screen the memory content from this address. If you look at program code, six string variables are assigned. Each string variable takes 7 bytes. So, that’s why the FOR… NEXT cycle is for T from 2611 to 2611 + 7 * 6 – 1.
String variables appear in memory this way:
- the first two bytes, to store the variable name (only two characters are important for variables names, in facts);
- on the second byte, bit 7 is set, to tell that the variable is a string;
- the third byte, containing the length of the string (hence the 255 characters limit for strings);
- the fourth and fifth bytes, containing a pointer to the address where the string assigned to the variable can be found, in the usual low byte / high byte format.
- the sixth and seventh byte are unused, so they are just zeroes.
The string assigned to a variable may be found:
- on the BASIC program, if it is assigned on a BASIC program line with an instruction such as A$ = “HELLO”;
- starting from a certain location in the String Storage Area, if the characters that make up the string cannot be found inside the BASIC program.
If you look at the beginning of the program, you will find instructions such as:
8 a$=b$+" stay tuned and have fun with the c64!! " 10 c$=b$+"hello all! i am glad you follow my blog!"
B$ is just a null string, but it has a function. It serves to make a dummy concatenation between strings. This way, the string to be assigned to A$ will be found on the String Storage area, and not inside the program. The same can be said for the variable C$ and for other string variables in the program.
This way, the text to be scrolled will be available on adjacent locations in RAM.
You may notice on the code that we have created a variable called D$, containing the string “DUMMY”. The starting content of this variable is useless, hence the name. However, we will be changing the string pointer of this variable in order to perform the coarse scrolling of the text.
As the PETASCII code for “D” is 68, if you look at the numbers on the previous picture you can easily find where the variable D$ is. The numbers following 68 and 128 are of great importance for us.
- number 5 tells us that the variable D$ is 5 characters long. That’s correct, it holds the word “DUMMY”;
- the numbers 91 and 159 represent the address on the String Storage Area the string “DUMMY” can be found starting from.
Now, we are going to change those three values. We will change 5 to 40, since we will be printing always 40 characters strings on the screen (that’s our scrolling message), and we will change the pointer of the variable D$ on each frame, so that the coarse scrolling can be performed. We will change the low byte only of this pointer to gain speed, as our text is short.
Notice the order each part of the text is assigned to variables. This is the only way to have all the words of the text properly adjacent to one another in the String Storage Area.
After the string variables are assigned, the Bottom of Strings pointer just tells us where the text to scroll starts from. Please seee the following picture.
Line 16 of the program, starting from the Start of Variables pointer, calculates were the pointer of the variable D$ is, so that it is possible to change it. D$ string length is on the location before the calculated one. This length is set to 40 by the instruction POKE L-1,40 on line 16.
On line 20, the coarse scrolling is performed. The pointer of the variable D$ is updated on each iteration. It grows by one each time. Only the low byte of the pointer is updated to gain speed, as the text is short. The scrolling text is being shown by printing D$ again and again.
Line 22 performs the fine scrolling, by changing the horizontal scrolling register.
Notice the instructions WAIT R,W (equivalent to WAIT 53265,128) used to move the text when it is not shown on the screen (or, that is, when it is far from the rasterbeam), in order to avoid flickering.
Well, this is definetely an odd way to do a text scroll, but I wanted to come up with something fast even with BASIC. I tried this program on a real C64 with a CRT TV set, and the scroll runs smoothly with no jitters, at least on my experiments.
Since the scroll doesn’t make use of string manipulations with BASIC functions, there is definetely no problem with slow garbage collection.
I had some troubles to get this scroll flicker-free however, and I had to shape the code so that there is no flicker and no jitters. I had to perform a step of the fine scrolling outside the iteration on line 22, otherwise the scroll would jitter noticeably.
Generic horizontal scroll algorithm
I had some problems remembering how smooth scrolling worked, so before coding the above program, I made a very simple program to figure out how scrolling could be done.
Please refer to the Commodore 64 Programmer’s Reference Guide for a simple explanation of scroll registers (Chapter 3). On that program, I have used the same conventions used in that guide for your convinience (and mine too).
The program is “unrolled”. That way, it was easier for me to work out the algorithm. You know, since the last scroll I coded, many years have passed.
As you can see from the code, the procedure for a horizontal scroll from right to left is the following:
- set the scrolling register X position to maximum value;
- print the text
- by using an iteration, change gradually the X position of the scrolling register from maximum to minimum. This way, you have a smooth motion.
- set the scrolling X register to maximum again, and
- quickly perform coarse scrolling. As you can see, you have to remove the first character to the left of the text and you have to add one new character at the end of the text (on this case, the new characters are spaces);
- go to point 3 and repeat, until all the text has been scrolled.
Borders must be shrinked so that the new character that appears can be hidden. Also, while doing smooth scrolling, thanks to the shrinked border the text will go to the left, “inside” the border.
The program asks to shrink the border or not, at the beginning, so that you can see what happens during the scrolling on the normally hidden columns of the screen.