One of my readers (Crispin) has kindly requested a simple digital clock program for the Commodore 64 (in BASIC V2). He showed me a C64 BASIC example from Rosetta code and asked if I could make a similar program that would be either fast (at least to be usable) and flicker-free. And possibly, with simple code.
We have two ways to measure time with Commodore 64 BASIC. The system variables TI and TI$ are in fact available.
TI is a jiffy counter that is started as soon as the Commodore 64 is turned on. A jiffy is 1/60 of a second. This counter is reset to 0 every 24 hours at midnight.
TI$ measures time by means of hours, minutes and seconds. This is a special reserved string variable that holds six numbers representing current time in the format HHMMSS (hours, minutes, seconds with two digits each). Each piece of information can be obtained by using LEFT$, MID$ and RIGHT$ string functions.
For instance, hours, minutes and seconds are provided by the following expressions:
ho = left$(TI$,2) mi = mid$(TI$,3,2) se = right$(TI$,2)
Still, I wanted to code something that did not use string functions. In facts, those functions create garbage strings in memory. If we want the clock to be functional as long as the computer is getting power, then we must avoid any possible garbage collection issue.
A radical approach has been used: no string operations during the clock loop.
We just need to sense a difference
We don’t need to get hours, minutes and seconds from TI$. We just need to check TI$ until one second has passed. At that point, we can update our own time variables.
As we don’t want to make any string operation to TI$, we can just print it on the screen, then PEEK its last digit from the screen itself (the rightmost digit). If we take the initial value of the last digit and we store it, we can check this value against the digit currently being shown. As soon as those values get different, we know a second has passed.
We don’t care about the initial value of TI$, as we are using it as a relative time measure (we just need it as a counter for seconds).
Based on these concepts, here are the simple programs I have come up with.
The last program (basic clock simple) is actually the first version. It is a quite simple program but I have included it as it should be better for studying purposes. It makes use of the standard CBM font and prints characters on the standard video matrix.
Digits are not updated too fast. As a result, you can see single characters changing while updating time.
The other programs use characters with double buffering (basic char clock) and sprites (sprite clock v1 and sprite clock v2). Both methods allow for a fast flicker free time update.
Programs basic char clock and sprite clock v2 both print the variable TI$ on memory and not on screen. This way, the whole screen is free to be used and there is no need to hide TI$.
On the programs, time can be set by changing the content of variables H, M and S (line 15 should be the one of interest on all programs). As some initialization time is required by both the sprite clock and the character clock, please take into account an 8 seconds and 5 seconds delay respectively for initial time setting. When entering seconds, add the required figure.
The program “basic char clock” makes use of programmable characters for the clock digits. Actually, the standard CBM character set is used, I have only changed a couple of characters by appending my program tiny font editor. This way, the two digits “4” and “7” look a little better.
Big digits are stored on a bi-dimensional string array. We have two indexes, since one index selects the digit, while the other index selects the digit row to be printed. So, selecting the digit to be shown is only a matter of updating the value of an array index.
The double buffering method is used to re-draw images while they are not shown, and to show them only when they have been fully drawn. So, the old image will be shown while the new image (on the other buffer) is being re-drawn. Once the new image is ready, we can switch from the current buffer (with the old image) to the new buffer (with the new image ready to be shown). So, even if the re-drawing process is slow, we get an instant image update. Of course, we need twice the memory needed to show the image.
Since BASIC is not fast enough to redraw 3 x 5 characters digits, we must use two 1000 bytes video matrixes. The subroutines on line 200 and 210 of the program “basic char clock” handle this.
The subroutine on line 200 flips a variable. This variable is used to switch the buffer we will print information on. Each buffer has its own set of values for the VIC-II registers (please see line 8 of the program).
Once we have chosen the buffer to print information on, we can print current time. Once we have printed it, we can call the subroutine on line 210: it shows on screen the buffer we have just printed information on. Notice that subroutine on line 200 only sets the buffer to print on, but the old buffer is kept being shown. This way, the slow big digits printing from BASIC has been hidden.
As usual, a WAIT 53265,128 statement is used (in line 150) so that the buffer to be shown is being switched only when the rasterbeam is in the not-viewable area.
This is the same double-buffering technique used in my 3D programs.
Double buffering the text screen both requires switching the current start address of the video matrix used by the VIC-II and changing the first video memory page on location 648 decimal. As the NMI handler on ROM fails to reset this location, a small machine language routine has been included in the program, so that it can be properly escaped by pressing RUN/STOP and hitting RESTORE.
The fix is quite easy and only requires to divert the NMI vector to a small routine in RAM that will place a 4 in location 648 decimal (the standard value), then will jump to the standard ROM routine. On the process, the register used to store the default value in location 648 must be preserved, as we are dealing with an interrupt handler. This is done by using the instructions PHA and PLA, that save the A register on the stack and retrieve it from the stack respectively (please see the machine code in line 2000).
The fix is only temporary, as the NMI vector is restored to default values after pressing RUN/STOP + RESTORE, but as we only need this fix to properly exit from our program, we are happy with it.
Sprites allow for a very fast printing of big digits, as long as we are happy with a maximum of 8 digits (sprite multiplexing is generally not an option in BASIC for practical use, although it can be done for demonstration purposes).
Once we have stored the digits graphics data on sprite shapes, showing a particular digit is just a matter of switching a sprite pointer. So, we only need six POKEs to update time on screen.
On the programs “sprite clock v1/v2”, sprite digits are drawn out of the ROM character set. As you can see, digits are stretched in the Y direction by copying the same ROM byte twice in RAM (please see lines 1110, 1115 and 1120). Then, sprites are expanded on both directions so that digits are even bigger.
On the program “sprite clock v1”, TI$ is directly printed on the screen, and it is hidden by setting the cursor colour the same as the background color.
On the program “sprite clock v2” instead, TI$ is only printed in memory, not on screen (so, no hidden characters on screen). As this requires to change the content of location 648 decimal, the same fix as the one in the program “basic char clock” has been added, so that proper program exiting by using the RUN/STOP + RESTORE key combination is possible.