While writing a simple C64 BASIC “game”, I came across some little issues dealing with formatting numbers and updating information on the screen. As info must be kept updated with no screen flickering and with no potential garbage collection issues, some simple special techniques were needed.
Commodore 64 BASIC V2 doesn’t provide any means of formatting numbers. For instance, there is no function that allows you to force an integer number to be four digits (using leading zeros for numbers with less than four digits, and omitting surplus digits for numbers with more than four digits).
So, C64 built-in BASIC prints numbers with no special formatting. But, a space is always put at the beginning of any number. And when converting an integer or real number to string, that space is kept.
Suppose we must print score (or time) using a fixed length of four digits.
If we are happy with leading zeroes, we can accept a thing such as:
Then, we could code like this:
10 input "value (integer)";a 20 a$=right$(str$(a),len(str$(a))-1) 25 nz=5-len(str$(a)):ifnz<=0thenz$="":goto35 30 fort=1tonz:z$=z$+"0":next 35 a$=z$+a$ 40 print "formatted number: ";right$(a$,4)
Although functional, the above code is quite inefficient. Of course, we could make some improvements (for instance, evaluating the STR$ function only once, using a variable), but the point is, the above code is not quite straightforward.
But, we can use a trick. If we want the score to be four digits at max, we can just set initial score to 10000 instead of zero. If we only print four digits from the right, the initial score will be just zero. And as long as we add points, everything will work just nicely. For instance, we will print the value 100, but in memory we will have the value 10100. If the number printed on screen exceeds 9999, it will wrap around to zero on the screen, but in memory it will be 20000. Here is how we may code it:
10 input "value (integer)";a 20 a=a+10000 30 a$=right$(str$(a),4) 40 print "formatted number:";a$
The above code works fine, but we still have a problem. The use of the function STR$ may cause garbage collection issues. For instance, if we are printing the value of a timer, it will be updated quite often, and that increases the risk of slow garbage collection troubles. Even if no slow garbage collection happens, many garbage strings will be created on memory.
As far as I can see, we should use another approach. Suppose we are not interested on leading zeros. We just want the number to be printed starting from the same position. Taking advantage of Commodore BASIC control characters seems a better solution in this case.
Let’s imagine our score keeps growing. Then, the following code will print score always starting from the same character position on the screen:
20 printchr$(147)" score:";sc 25 sc=sc+500:if sc>10000 then end 30 printchr$(19)chr$(29)chr$(29)chr$(29)chr$(29)chr$(29)chr$(29)chr$(29);sc 40 fort=1to200:next 50 goto 25
Things work fine because the number is always increasing. So, new digits are always guaranteed to overwrite old numbers. As a result, numbers being printed are always correct and there is no visible flickering as well.
Things will not work, however, if we are printing a timer going towards zero:
10 printchr$(147):sc=100 20 printchr$(19)" time";sc 25 sc=sc-1:if sc<0 then end 30 printchr$(19)chr$(29)chr$(29)chr$(29)chr$(29)chr$(29)chr$(29);sc 40 fort=1to100:next 50 goto 25
Some old digits are not updated, so printed numbers at the end are bigger than the actual values. To fix the problem, we can use spaces:
Now, printed numbers are correct, but we see some flickering. This is due to the fact that numbers are deleted with spaces, then printed again.
But, we know from my BASIC chessboard scrollers and other routines presented here, that we can use the instruction WAIT to update information while the rasterbeam is out of the viewable area, avoiding flickers. The following code will do:
As the program is short, a little pause between WAIT and PRINT statements is required. Otherwise, flickering will happen. However, bigger BASIC programs will run slower, so that pause may be not needed on them.
Missing the leading zeros? It may be the case, as they are used on most games for scores and time. If we still want to avoid using string functions, the fix is not straight, but it can be done. We just have to change the starting character position according to the size of the number. And, as we don’t want any potential slow garbage collection issue, we will create a little array for control characters, then update an index, thus avoiding string variables assignments.
The above code is quite complex. Instead, I like the simplicity of the solution truncating a number which is bigger than needed (the second program presented on this article). But, I don’t like the fact that it creates garbage strings. Still, we can play around with the string pointer and avoid producing garbage strings. The following program will actually format a big amount of numbers without producing garbage strings:
10 fork=1to9999 20 a=k+10000 25 p1=peek(51):p2=peek(52) 30 a$=right$(str$(a),4) 40 print "formatted number:";a$ 45 poke51,p1:poke52,p2 50 next
You can directly paste the program in VICE. After you run the program, you will see that the string pointer will have the original value. So, there is no memory waste and there are no potentially slow garbage collection issues either. Of course, the content of variable A$ will NOT be preserved. As long as other strings will be created, its content will get corrupted. But, since we are regarding it as a temporary string (we just need to print it once), it just doesn’t matter.
So, the following version offers simple code with no garbage collection concerns:
10 input "value (integer)";a 20 a=a+10000 25 p1=peek(51):p2=peek(52) 30 a$=right$(str$(a),4) 35 poke51,p1:poke52,p2 40 print "formatted number:";a$