Amazing, eh? How did they possibly know back in 1980 that COCOFEST would become a thing?

But actually, it’s just a random message, and not a hidden message at all. I learned of this trick from this video by 8-Bit Show and Tell that claims to share a hidden anti-Microsoft Easter egg in Commodore 64 BASIC… and then reveals how the prank works.

A hidden anti-Microsoft Easter egg in Commodore 64 BASIC! Or not…

If you tried to run that program on other flavors of BASIC, it probably would not work. It certainly does not produce the expected results on a CoCo.

This was the first video from 8-Bit Show and Tell I ever saw, and it’s lead me down quick a rabbit hole trying things he demonstrates on the Commodore computers on our beloved CoCo. And it all started with this random video that YouTube randomly showed me.

Monkeys and Shakespeare

“…a monkey hitting keys at random on a typewriter keyboard for an infinite amount of time will almost surely type any given text, such as the complete works of William Shakespeare.”

Wikipedia

We are just using BASIC’s RND() random number generator to simulate a monkey at a typewriter, and using short words instead of the complete works of Shakespeare.

It’s much quicker this way.

As previously discussed, the RND function generates a series of numbers that are not random. Each time you power up a CoCo, for instance, this code will produce the same “random” numbers the first time you run it:

In order to change the series of numbers, you pass a negative value into the RND() function, and that series will be used. If you do X=RND(-1), you will then get the same series of random values every time. If you do X=RND(-42), you get a different set of random numbers every time.

Magic!

Or math. But math is hard, and magic is just frustrating.

The monkey simulator

But how do you find which random seed value will give you the random numbers you want in the order you want them? The original prankster used brute-force trial and error.

A program can be designed that first seeds the RND function with -1, then generates a series of random numbers and tests to see if they are what it is looking for. In the case of the C64 version, it needed to see the numbers that represented the characters of the word followed by a ZERO to terminate the string.

If it did not work, it tries a seed of -2, and so on. This could take hours or days, and there is no guarantee the exact series of numbers will be found.

I decided to write a CoCo version of this monkey typewriter simulator, but I made some changes.

First, I figured looking for “W”+”O”+”R”+”D” was more work than just looking for “W”+”O”+”R”+”D” without a 0 byte at the end. That should speed up the search, but require an extra bit of data in the display program since it now needs to know how many random values to use (the length of the word).

The C64 version looked from A to the highest letter used (“BILL GATES SUCKS” scans A to U, though it doesn’t really need to try to find A since the earliest letter is B.) I figured that looking for A to Z (worst case, 26 choices) would be more work than just looking at the range of letters actually used in the word. For instance, finding “ABC” in a repeating random series of 26 numbers seems less likely than finding “ABC” if you were only using 3 random numbers. I made my generator look for a range covering only the letters being used. “CAT” would need numbers from “C” to “T”. “DOG” would need “D” to “O”. “ALACAZAM” would need “A” to “Z”. This meant my display program also needed to know the starting letter value and range value, in addition to the word length.

My version is not as clean and tidy as the C64 original

Here is the program I came up with. You can type in a word and it will present the range of letters it will look for, and then start searching until it finds it (or, weeks later, it has not and you give up):

I tried to find “COCOFEST” together, but after days and days of running, it still hadn’t. Perhaps it would have found it if I was searching the entire A-Z range versus just C-T. It’s random-ish, after all.

Perhaps one of you will take this concept and recreate the C64 version, looking for A-Z and a zero. Maybe that works better. I did not try.

Perhaps one of you will start compiling a dictionary of random words and we can use this as a secret decoder ring for passing cryptic messages to each other on Facebook.

Perhaps this will just be a passing random thought and we will never speak of it again.

But knowing me and this site, I expect we will speak of it again. Especially if I get any good comments to this post.

My recent return to exploring my old Commodore VIC-20 code has reminded me about the main reason I jumped ship to a Radio Shack TRS-80 Color Computer: Extended Color BASIC. The older CBM BASIC V2 used by the VIC was missing keywords like ELSE, and had no functions for graphics or sounds. While I am having a blast re-learning how to program VIC-20 games, I sure do miss things like ELSE.

But should I?

IF/THEN/ELSE versus IF/THEN versus ON/GOTO

Pop quiz time! Suppose you were trying to determine if you needed to move a game character up, down, left or right. Which is the faster way to handle four choices?

30 IF Z=1 THEN 100 ELSE IF Z=2 THEN 200 ELSE IF Z=3 THEN 400 ELSE IF Z=4 THEN 500

…or…

30 IF Z=1 THEN 100
31 IF Z=2 THEN 200
32 IF Z=3 THEN 400
33 IF Z=4 THEN 500

Of course, if the values were only 1, 2, 3 and 4, you wouldn’t do either. Instead, you might just do:

ON Z GOTO 100,200,300,400

…but for the sake of this question, assume the values are not in any kind of order that allows you to do that.

IF/THEN/Work/ELSE versus IF/THEN/WORK

Suppose you were a junior high kid learning to program and you wanted to update some player X/Y positions based on keyboard input. Which one of these would be faster?

30 IF Z=1 THEN X=X+1 ELSE IF Z=2 THEN X=X-1 IF Z=3 THEN Y=Y+1 IF Z=4 THEN Y=Y-1

…versus…

30 IF Z=1 THEN X=X+1
31 IF Z=2 THEN X=X-1
32 IF Z=3 THEN Y=Y+1
33 IF Z=4 THEN Y=Y-1

All is not fair

I should point out that the speed it takes to run these snippets depends on the value of Z. For the sake of this article, let’s assume no key is pressed, so Z is set to something that is not 1, 2, 3 or 4.

Obviously, when there are four IFs in a row (either in a single line with ELSE, or on separate lines), the order of the checks determines how fast you get to each one. If Z is 1, and that is the first IF check, that happens faster than if Z is 4 and the code has to check against 1, 2 and 3 before finally checking against 4.

The same thing applies in languages that use switch/case type logic, so the things that need to be the fastest or happen most often should be at the top of the list and checked before things that happen less often.

Because of this, to be fair, we should also check best case (Z=1) and worst case (Z=4) and see what that does.

Best versus Worst: FIGHT!

Let’s try some best and worst cases now. For this test, I’ll resolve the jumps to lines 100, 200, 300 and 400 by adding this:

100 GOTO 70
200 GOTO 70
300 GOTO 70
400 GOTO 70

That will greatly slow things down since we have to search forward to the new line, then it has to start back at the top of the program and search forward to find line 70. BUT, it will be consistent from test to test. I’ll add a “6 Z=1” or “6 Z=4” line.

elsetst1.bas (else): Z=1 produces 507. Z=4 produces 1058.

elsetst2.bas (separate): Z=1 produces 390. Z=4 produces 1053.

elsetst3.bas (on/goto): Z=1 produces 317. Z=4 produces 357.

Wow. ON/GOTO is really good at going places, best or worst case.

And what about the “doing work” stuff?

elsetst4.bas (else): Z=1 produces 632. Z=4 produces 633.

elsetst5.bas (separate): Z=1 produces 1171. Z=4 produces 1172.

In conclusion…

If you are using IF to go to some code, ON/GOTO is the fastest, following by separate lines. Even in the worst case, separate lines are still a tiny bit faster, which surprised me. I suspect it’s the time it takes to parse the ELSE versus a new line number. Retesting with all the spaces removed might change the results and make them closer.

But it does look like we need to stop doing “IF X=Y THEN ZZZ ELSE IF X=Y THEN ZZZ ELSE” unless we really need the extra bytes ELSE saves over a new line number.

And if you are trying to do work, ELSE seems substantially faster than separate line numbers. But, in both cases, best and worst case are very close. I believe this is a benchmark issue, since the time to scan a few lines is tiny compared to the time it takes to do something like “X=X+1”, and both best and worst case do the same amount of work. A better test would need to be performed.

Bonus

There is a way to speed up the separate line statements when doing work, especially for better case. Consider this:

By adding the GOTO, if line 30 is satisfied (Z=1), the parser can start searching for line 70 without having to do the check against Z three more times. But, when a case is not satisfied, it now has to parse through the GOTO token and a line number to find the end of the line, meaning that for worst case (Z=4) it should be a bit slower.

Let’s see if this works.

elsetst6.bas (separate/goto): Z=1 produces 544. Z=4 produces 1241.

Compare that to the previous version without the end line GOTOs:

elsetst5.bas (separate): Z=1 produces 1171. Z=4 produces 1172.

It looks like there’s a significant improvement for best case, and a slight decrease in performance for worst case (the overhead of skipping more characters to find the end of the line for the false conditions).

The more you know…

I guess I am learning quite a bit by revisiting the VIC-20 and having to do things without ELSE.

You could also split that to two separate statements. One handling K=17 case, and then do ON K-38 GOTO 50,x,30 where x is just the line following the ON GOTO line.

don’t know about speed but you could also try ON K-16 GOTO 40,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,50,x,x,30 (also where x is the following line)

MiaM

In my example, I was getting keypress values back that represented left (17), right (39) and jump (41). By filling the ON/GOTO/GOSUB with bogus line values where the gaps are, you can now use ON/GOTO for non-sequential values. But, if the first number expected was a 17, that would be 17 dummy values at the start. Mia’s approach is to subtract that starting value, eliminating the need for 16 dummy values. Clever!

Clever, sure. But can it be benchmarked?

So how bad is this with speed? Let’s find out.

First, for the dummy lines we will just put nothing between the commas. That will be parsed as a zero, which is bad if any of those values are hit since going to 0 would restart the program, but since we are just testing and can control the value, it will give us the fastest way to parse a long ON/GOTO/GOSUB. Using real lines numbers will only be slower.

Best case (17) reports 504 and worst case (41) reports 1128. Can there really be that much more overhead to skip two extra IF/THENs? It seems so. In this example, the long ON/GOTO is faster in worst case. Interesting. If worst case is a button not used that often (“smart bomb”), IF/THEN may be the best option, but if all buttons are used equally, there’s probably a point where a long ON/GOTO makes more sense.

But wait … there’s more!

Rob’s idea of using an array to translate the non-sequential values into sequential numbers is a fun one. It uses more memory, and trades the time it takes to do an array lookup for the time it takes to parse a long ON/GOTO/GOSUB line.

Since the largest value we need to check for is 41, I did a DIM K(41). That will allow for values from 0 to 41.

Best case (17) gives us 432! Faster than the manual IF/THEN check!

Worse case (41) gives us 432 … Really? ON/GOTO is really fast with just a few choices. It would be slower if there were dozens and you wanted the one at the end.

The downside of this approach is the memory it took for an array of 42 (0-41) variables. Doing something like this:

NEW:CLEAR
PRINT MEM
DIM K(41)
PRINT MEM

…shows me 22823 and 22606. That’s 217 bytes being taken by the 42 K array entries. (There is an entry reserved for the array itself, then each array element takes 5 bytes, I believe. It’s been awhile since I wrote my String Theory articles which I think looked into how variables are stored.)

This may be the fastest approach if you have a few hundred bytes available to use for this. On a VIC-20 with 3583 bytes free on startup, if I had memory left when I was done with my normal IF/THEN version, I could retrofit it with this approach and use that extra available RAM to speed up my program a tad.

The theory so far…

When we last left off, I had just described my theory about how my prototype Sky-Ape-Er game loaded as just one file which contained a custom character set — without being contained in DATA statements or anywhere in the BASIC code.

My theory was that I modified BASIC’s “start of variables” pointer (which normally points to just past the end of the BASIC code) so it was after the memory where the custom characters were stored. When saved, the file would contain the entire range of memory including those custom characters. When the program was LOADed and ran, the first thing it had to do was set the “start of variables” pointer back to where it needed to be, just after the BASIC code.

Today I want to test that theory by trying to create a standalone BASIC program that contains custom character set data. I am going to use the excellent CBM prg Studio development environment to make a BASIC project that will have three things:

A custom character set. I will use the editor to export the characters out as DATA statements into a BASIC file.

That new file will be turned in to a program that will READ the DATA statements and POKE the values into RAM memory.

Finally, I will have a simple test program that will do the necessary POKEs to enable RAM characters and animate them.

Since I haven’t owned a VIC-20 since 1983, I am going to do all of this in the VICE VIC-20 emulator. To do it like I did it back in 1982, I am going to use a virtual cassette tape for program storage. I could probably do this easier using an emulated disk drive, but never had a disk drive on my VIC-20 and I want to keep this as virtually real as possible.

Except for the whole part of using a Mac and virtual PC for development, of course.

Step 1: Custom characters and loader program.

Using CBM prg Studio’s character set editor, I created a few custom characters:

VIC-20 custom character set test in CBM prg Studio.

I then used the “Character Set -> Export -> To Listing” option to output the DATA statements containing those characters.

I then added the following code to load the DATA statements into memory, and display them to verify they work.

Lines 4 and 6 – These POKEs are used to protect the characters in memory so BASIC will not override them. They set the highest memory location that BASIC and strings can use. I set them to 7168, the address where the custom characters load.

Line 10 to 20 – FOR/NEXT loop of READ and POKE the first 8 bytes where character RAM will be. This is where the “@” symbol is (character 0).

Line 30 to 35 – These POKEs clear out the “space” character in the custom character set. I do this so my DATA statements don’t have to contain all the characters up to space.

Line 40 to 55 – Clear screen then print reverse text (which will still show up even after we switch to RAM character mode) and the custom characters.

Line 60 – Set VIC chip to use RAM starting at 7168 for custom characters. At this point, the screen will show my custom characters, and the reverse video should appear as normal text.

Line 65 and 70 – Wait for key to be pressed.

Line 75 – Set VIC chip to use normal ROM area for characters.

Line 80 – Print the two bytes that represent the last memory location used by the character set. These will be POKEd into 45 and 46 before SAVING the demo program later.

Line 85 – End.

Line 1000 to 1110 – Each line has eight bytes that make up a custom character.

Here is what it looks like when it runs:

VIC-20 custom character set demo.

Then when you press enter, it disables the custom characters and you will see it says “CHAR:” in reverse view with letters a-i and @ where the custom characters were. It then prints two numbers, which I need to write down. Those numbers represent the address of the end of the custom characters my test program uses.

I will build this into a “.prg” file, and then load that into VICE. Next, I will “Create and attach an empty tape image” (I called mine “Custom Char Demo.tap“) and then save this loader program to that virtual tape:

SAVE "CHAR SET LOAD"

Step 2: Program to use the custom characters.

The next part will be a standalone program that will make use of these characters. I am creating a simple demo where spinning bricks fall from the sky and a player character on a sidewalk below has to dodge them. Except nothing happens if a brick hits the player because this is just a demo.

Here is what it is doing… Actually, I’ll skip the demo logic and just mention a few important things:

Line 1000 – This prints the programs’ current end (start of variables). Since I need the program to restore this when it loads (after being saved with the custom characters), I can load this program and “RUN 1000” to get those values. I then change the POKEs in line 2 to match those values. Thus, when the real program is loaded, it will fix those pointers which will get messed up by the SAVE process.

Thus, I would load this program into memory (but NOT run it) and do “RUN 1000” and note those numbers. I changed the POKEs on line 2 to match those values. Then I saved this after the “CHAR SET TEST” program as:

SAVE "CHAR SET TEST"

Step 3: Save the all-in-one test and charset file.

Now I reset the virtual VIC and rewind the virtual tape. Here are the steps:

LOAD and RUN the “CHAR SET LOAD” program to get the character set in memory. I make a note of the two numbers printed out at the end.

LOAD (but DO NOT run) the “CHAR SET TEST” program.

With the TEST program in memory, I do the following POKEs to change the end of BASIC pointer:POKE 45,X:POKE 46,Y…where X is the first number the loader program printed and Y is the second number the loader program printed.

I now can SAVE the test program and it should save all of the BASIC and continue saving until it gets to the end of RAM.

SAVE "CHAR SET DEMO"

Step 4: Test!

After a reboot, and rewind of the virtual tape, I try loading the “CHAR SET DEMO” program and running it…

VIC-20 error when loading my character set demo program.

Oh no! My theory is not correct. Something is still wrong. Running this program produces parts of the custom character, but not all. It’s clear I am off somewhere.

That might be a nice approach if the numbers were relatively close to each other, but at some point, adding a bunch of dummy numbers to the ON/GOTO line would take more time to parse than just using separate IF/THEN statements.

Arbitrary GOTO

My example was based on some VIC-20 code I wrote back in 1983. I was reading which key was currently being held down, and would get back three different values for the keys I was reading:

17 – ‘A’ key is pressed (LEFT)

42 – ‘S’ key is pressed (RIGHT)

39 – ‘F1’ key is pressed (JUMP)

I couldn’t use ON/GOTO for values 17, 42 and 39.

But Rob’s code does just that!

20 ON -(K=41)-2*(K=17)-3*(K=39) GOTO 30,40,50

In BASIC, any comparison returns a -1 if it is TRUE, or a 0 if it is FALSE:

PRINTing the result of a comparison in Color BASIC.

…so in Rob’s example, the checks in parenthesis will be turned in to either a -1 or a 0 based on the value of K.

If K is 41, then (K=42) will be (-1) and (K=17) and (K=39) will both be (0).

If K is 17, then (K=17) will be (-1) and (K=41) and (K=3) will both be (0).

If K is 39, then (K-39) will be (-1) and (K=42) and (K=17) will both be (0).

Arbitrary benchmark

So of course, I now have to see how this compares to separate IF/THEN’s speed-wise. Let’s pull out the trusty benchmark test code and do a version for best case (first choice) and worst case (last choice) for each approach (Rob’s, and IF/THENs).

This brings the time down slightly to 1092. Still not enough to beat the separate IF/THEN/GOSUB (and that could also be sped up slightly using HEX or variables).

Conclusion

This trick is very cool. From my calculations, it looks like it save code space, which could be very important on a low-memory system like a 4K CoCo or the 5K VIC-20. That alone might make this trick worth doing.

But for speed, such as a BASIC game, it looks like brute force IF/THEN may be a better approach.

It’s really nice to have options. I can’t wait for an opportunity to use this technique in something.

I have completed my code walk-through of one of my earliest computer programs, a Donkey Kong-inspired VIC-20 game called Sky-Ape-Er. But, the version I presented was not the only version of the game I created. I have dozens of saved copies of this game in various stages of completion, but one in particular stood out. It used completely different graphics.

Sky-Ape-Er: The Prototype

VIC-20 Sky-Ape-Er version 1, death screen.

VIC-20 Sky-Ape-Er version 1, screen 1.

VIC-20 Sky-Ape-Er version 1, screen 2.

VIC-20 Sky-Ape-Er version 1, screen 3.

As a reminder, here is what the graphics were later changed to in the release version:

VIC-20 Sky-Ape Er, screen 1.

VIC-20 Sky-Ape Er, screen 2.

VIC-20 Sky-Ape-Er, screen 3.

The ape was completely different, and the platform graphics were meant to resemble the ones used in the arcade game Donkey Kong. But why were there pinwheels? I have so many questions for my junior high self.

When I first uncovered these tapes I was unaware of how many variations of my programs were on them. When I started this article on my Sky-Ape-Er game, I discovered that the earlier version with different graphics was also using some different code — most notably in how it read the keyboard input.

I thought it might be fun to look at the programming choices I originally made, and speculate on why I changed them.

Sky-Ape-Er: The Mystery

The first thing I want to discuss is a mystery I am currently trying to solve. My VIC-20 games that used custom character sets seem to come in three forms:

DATA STATEMENTS

The BASIC program reads the character set from DATA statements and POKEs it into memory. This is what the Sky-Ape-Er INSTRUCTIONS program does. I believe these character sets may have been designed by the Eight by Eight Create program I previously mentioned. If true, I don’t envy my junior high self having to manually copy down the numbers to paper and type them in to my own program later. From the documentation:

“Once you have created, designed and examined enough characters, you can copy their associated numbers on paper to be used in any programs you make.”

LOADABLE CHARACTER SET

A standalone binary “program” of the custom character set that can be loaded into memory, presumably at the address of where the character data goes. I have found several programs called things like CHARS and TNTCH that do not have any BASIC code in them. I suspected these were character set data (especially TNTCH which was on the tape after the main program TNT) but I had no idea how to use them. I was finally able to see what was inside by importing them into the CBM prg Studio‘s characters set editor. CBM prg Studio is a Windows integrated development environment (IDE) for making Commodore programs in BASIC or assembly. It has some great features and is worth checking out.

VIC-20 Factory TNT character set in CBM prg Studio.

This let me see that these were indeed character sets, though my first attempt to import them had all the graphics off by a few lines. I needed to use an offset (bytes to skip in the file) of 2 for the characters to load properly. That told me that whatever type of file this was had some 2 byte header at the start (perhaps memory location where to load the data?).

ALL-IN-ONE PROGRAM WITH CHARACTER SET

And this is the mystery! My early prototype version of Sky-Ape-Er was just one program, and it loaded up with the custom character set. There was no font in DATA statements. There was no pre-loader that did it. It just loaded and “just worked.” I have no idea how I created this, nor do I know why, for the release version, I change it to use two programs and DATA statements.

But I have theories.

Dissecting the data

Using a free hex editor, I opened the file and looked for the end of the BASIC program. I could tell it ended around byte 2612 because the last line was a “SYS xxxxx” command that would reboot the VIC-20. The xxxxx numeric value was visible as plain text in the tokenized BASIC file, so it was easy to spot.

VIC-20 .prg file in a HEX editor.

After this was a bunch more data. Somewhere in there must be the character set. But where? I decided to try opening the entire program file in the character set editor and using the 2612 offset where the BASIC program ended.

Doing this showed garbage between the BASIC program and character data, but scrolling down let me visibly see where the font data began.

VIC-20 CBM prg Studio trying to find where character set data is in a .prg file.

I now knew that approximately 58 characters (each character is 8×8, so 8 bytes per) into the file was the start of the font data. A little math (which was hard) and some trial and error (which was easy) and I came up with 3073 as the offset to use from the stat of the .prg to where my custom characters were. I imported using that value and got this:

VIC-20 character set data imported from a .prg file.

Tada!

If I knew what the font data was to begin wish (from DATA statements), I could have just scanned the HEX file looking for those values. But I didn’t, so I couldn’t.

Now I have a BASIC file for the game, as well as a character set file in the CBM prg Studio editor. But how did I combine them together in the first place?

Where does the data go from here?

The clue is in these POKEs found on the first line of the program:

POKE45,56:POKE46,26:POKE51,0:POKE52,28:POKE55,0:POKE56,28:CLR

They reminded me of similar POKEs in Color BASIC that track where the program starts in memory as well as where variables and strings go. I expected CBM BASIC would be similar, so I went searching for a VIC-20 memory map.

The “*” notes “Useful memory locations” in the memory map. I agree. I seem to be changing where variables and strings start, as well as where the end of memory is on startup.

Why was I changing the start of variables, the end of string storage, and limiting the end of BASIC? I have a theory, which parallels something I’ve done on the CoCo.

In Color BASIC, we use the CLEAR command to allocate more string space (“CLEAR 500” for 500 bytes for strings). It looks like CBM BASIC doesn’t do that, and allows strings to use as much memory as is available (the memory between the end of the BASIC program + variable arrays, and the limit of memory).

CLEAR can also limit how much memory BASIC can use (“CLEAR 200,&H3F00”). That’s useful when you are wanting to use some of that memory for machine language and don’t want BASIC to overwrite it. I am betting POKE 51/52 is like CLEAR x,XXXX.

VIC-20 Memory Map

To better visualize this, let’s take a quick look at where the 5K of RAM in the VIC-20 is located.

Hey, look at that! The memory range used by BASIC (4096-7679) is the “3583 BYTES FREE” value shown on the startup screen:

VIC-20 startup screen showing 3583 bytes free.

Notice the 3K gap (1024-4095) which is where the 3K RAM expansion cartridge goes if you have one. I never did, though I did have the Super Expander cartridge which gave extra memory as well as enhanced graphics and sound commands.

Side Note: When memory expansion cartridges are plugged in, more memory becomes available and some things shift around. But for this discussion, we will talk only about the stock 5K VIC-20. It was only in recent years that I learned the VIC-20 was a 5K computer. I’d always thought it was 4K. That now makes the weird 3K memory expansion make more sense, since that would boost it to a nice even 8K. But I digress…

When a new numeric variable is added, it goes into the Variables section, which grows larger downward. When a new array is added, it goes into the Arrays area (and likely the entries there point to the Variable) and it grows larger downward. When a string is added, it gets an entry in the Variable section (“A$”) which has a pointer into the actual string content in the Strings section, which grows upwards.

So why was I changing the start of variables, the end of string storage, and limiting the end of BASIC? I believe I was making BASIC think the program was larger than it really was so it would SAVE out (and thus LOAD back later) the program PLUS some custom character data. When the program would run, it would need to reset the pointers to be at the actual end of the BASIC program, and limit memory so BASIC did not write over the character data.

In order to explain this, we need to look at how the VIC-20 custom characters worked.

How the VIC-20 custom characters worked

The VIC-20 character set was 4K of data stored in ROM starting at 0x8000:

Each character was 8 pixels wide (one byte) by 8 pixels tall (8 bytes total). There is room for 512 characters in that 4K. Normal printable ASCII characters are 0-127, so it looks like the Commodore PETASCII was similar, with 128 special Commodore characters per bank (128 characters * 8 bytes per character = 1024 bytes).

On power up, the VIC’s video chip is programmed to use the first of those four 1K blocks of ROM for its character set. There is a register with four bits that can be changed to select which of those four ROM blocks it uses, or point it to four 1K RAM blocks in RAM. By loading a character set in to one of those RAM areas and setting the register, the VIC will now display the custom character set rather than the one built in to the ROM. Here are the important four bits:

The four RAM locations all are within the 4K that is used by BASIC and screen memory:

0x1000 – 0x1dff – 3583 bytes used by BASIC programs.

0x1e00 – 0x1fff – 512 bytes used by screen memory.

We can’t use RAM area #1 for characters because that is where our BASIC program is. If we kept our BASIC program and all its variables very small (1K, 0x1000-0x13FF), we could use area #2. But, it makes more sense to use area #4 and give as much memory as possible to BASIC.

In my programs I see POKE 36869,255 and POKE 36869,240. The first POKE makes the video chip start using characters in RAM starting at 0x1c00 (bit pattern 1111). This means a BASIC program and all its variables can’t be any larger than 3072 bytes (0x1000-0x1bff). The second poke switches the characters back to using the standard ROM location for uppercase and graphics characters (bit pattern 0000).

My Sky-Ape-Er INSTRUCTIONS program would READ character data and then POKE it into memory starting at 0x1c00 (7168). It then did POKE 36869,255 to start using them. To display a normal text screen, or at the end of the program, it would POKE 36869,240 to get back to the ROM character set. (This is the part I actually mostly remembered.)

I do want to point out that the character RAM area #4 overlaps with the screen memory:

0x1c00 – 0x1fff – Character RAM area #4.

0x1e00 – 0x1fff – 512 bytes used by screen memory.

This tells me that you really only have 0x1c00 to 0x1dff (7168-7679) for custom characters. That’s 512 bytes, and at 8 bytes per letter, there is only room for 64 custom characters. Assembly language programs that did not need BASIC could move things around and use one of the other blocks in its entirety, but this last block shares memory with the screen so not all of it can be used for character data.

Character sets

The PETSCII character set starts at zero with an “@” symbol, followed by the alphabet characters “A-Z” (1-26), then various punctuation and symbols (27-47), then numbers (48-57), then more punctuation and symbols (59-63). This covers the basic characters and uppercase alphabet just like standard ASCII does for characters 32-96. This means being limited to just 64 characters is not bad at all.

And, as I was working through this, I discovered why my Factory TNT game is not displaying the score correctly! I did not realize it also contained updated number characters (48-57) that I was not properly loading during my “restoration” of the game:

VIC-20 Factory TNT character set also remaps the numbers.

I’m so glad I am writing this article and figured that out. It was driving me mad!

But I digress…

Stay on target… Stay on target…

This means that you could have a BASIC program from 0x1000 to 0x1bff followed by custom character data from 0x1c00 to 0x1dff. If you had the characters in memory for your BASIC program to use, and you SAVEd your BASIC program, those custom characters would NOT be saved with it since BASIC doesn’t know anything about them.

But … you could lie to BASIC and tell it your BASIC program actually ENDS at 0x1dff, then when you SAVE it should write out the entire range of memory (0x1c00 to 0x1dff) thinking it’s just one large BASIC program…

Then, when you loaded it back it, BASIC would start loading it into memory at 0x1c00 and keep going until it got to the end of the program. You now have loaded memory that is part BASIC, and part character set!

But, if you tried to RUN it, you wouldn’t get very far because BASIC would think there is no memory left for variables. You would need to un-lie to BASIC and tell it where the program really ends, and do so without using varaibles.

In this installment, I will walk through the code for my VIC-20 game Sky-Ape-Er. Thanks to a wonderful utility I mentioned in part one, I have this ASCII program listing. The utility replaces the special PETASCII control characters with {words} so they can be viewed on non-Commodore systems.

Sky-Ape-Er program listing

Initialization

1 POKE 808,100

That poke would “disable the RUN/STOP key, the RESTORE key, and the LIST command” to keep someone from being able to save a copy of it.

Comments, but with no dates. Thanks, me. One of my tapes had the game in various stages of completion, named “SKY-APE-ER 1”, “SKY-APE-ER 2” and “SKY-APE-ER 3”, but all my other tapes just called it SKY-APE-ER, and I’ve found at least two distinct versions of the game so far.

I am not sure what the {$cc} is at the end. From looking at the c64list.exe command I used to make this listing, it seems to be an unknown token. This might have been something I placed in that line to prevent listing, even after a LOAD without the POKE being executed. But, you can LIST 10- and still see it ;-)

10 S=1:POKE36879,26:POKE36878,15:S1=36875:POKE775,200:POKE36869,255

S is the screen (level) to display. Sky-Ape-Er contained three screens, and a “game won” screen.

36879 is the border color, with 26 being red.

36878 is something to do with the music chip.

S1 is being set to the sound note location 36875.

775,200 disables LIST. Or maybe not. There was conflicting information in the magazine I looked that up in.

36869 enables the custom font characters.

Set up game screen

15 PRINT"{clear}":L=8118:B=7772:M=15

L is the starting memory location for the player.

B is the starting memory location of the chimp (B for barrel, I suppose, as this was inspired by Donkey Kong).

M is the character to POKE to the screen for the player (man). 15 is the letter”O”.

20 FORA=38400TO38905:POKEA,0:NEXTA

This clears the color for each character on the screen. The characters for the screen are stored in one block of memory, and the color of each character is stored in a different block of memory.

Display ape

This clears the screen, then draws the letters that make up the ape character. The VIC-20 has no PRINT@ or LOCATE function, but you could embed cursor movement commands in the PRINT statements. Thus, it would “home” to the top fo the screen, move the cursor {down} one, then {right} one, set the color to {black}, print ABC, then go {down} one and {left} three times (cursor is now back under the A) and print DEF, and repeat for the other lines. The ape was 3×4 characters in size!

Display game screen

30 ONSGOTO35,45,55,500

This goes to the appropriate routine to display the current screen (level) based on the value of S. There were three screens. If S is 4, it goes to a “win” screen.

Main game loop

Line 80 pokes the Man on the screen at its Location. The Barrel is poked to a space, so erased. The S1 sound is stopped. Nothing would be playing the first time this code is ran.

If the Location is 7772 or 7749, we go to 400.

Move the chimp

85 B=B+23:IFPEEK(B)=0THENB=B-22:POKEB,16

This controls the movement of the enemy (chimp). B is the location. The VIC-20 screen is 22 characters per line, so incrementing B by 23 would move the position down and to the right.

The new location of B is checked to see if it is 0 (the brick). If it is, it moves the location back 22 (up one). Thus, the chimp always tries to go down and to the right, and if it can’t it just goes right.

The POKE is what puts the chimp character on the screen at that position.

From looking at this, it seems the chimp is not displayed if it goes down and to the right successfully. I wonder why? Bug? It would get displayed the next time. Or inteitonal? I’ll look into this.

Handle player falling

87 IFPEEK(L+22)=32THENPOKEL,32:L=L+22:POKEL,M

This looks at the block under the player. If it is 32 (empty), it erases the player and moves it down, then displays it at the new location.

This code is what makes the player “fall” when it jumps or, on screen 3, when it falls off the edge. Cool.

Make “tick” sound

90 POKES1,240:POKES1,0

S1 is the address of one of the sound channels (the VIC-20 has three musical channels, plus noise). This turns one on and off, making the “tick” sound.

I did not even consider being able to do background music for a game back then. I bet I could today, memory permitting, even in BASIC.

Collision detection (player and chimp)

95 IFB=LTHEN250

This is the collision detection. If the chimps location (B) is the same as the players Location, we go to 250 (death).

Reset chimp back to top

100 IFB=8118THENPOKEB,32:B=7772:POKEB,12

This checks to see if the chimp is at the bottom right of the screen.

If it is, it gets erased, and the position is reset back to the top left starting position and it is displayed there.

Update and check timer

This homes the cursor, changes the color to blue, turns on reverse video, then displays “TIME:” and the current time value. The reason it goes reverse is because we are using a special character set that replaces the lower alphabet characters. In this mode, there is some memory thing that happens that wraps around the inverse video characters to the standard characters. When using special characters, you have access to the basic letters by using reverse. Apparently.

Keyboard input

115 K=PEEK(197):IFK=64THEN80

Memory location 197 is the current key being pressed. If no key is pressed, 64 is returned and the program goes back to the top and continues. (Move the chimp, update the time, etc.)

120 POKEL,32:POKES1,230

If there was input, then this line will erase the player, and turn on the sound chip to start making a tone.

Those DATA statements contain the note value and length of the tune that plays when you die (funeral march). I did not know anything about music at the time, so I figured it out by ear. The timing is awful.

Time exceeded

285 PRINT"{down:2}{rvrs on}PRESS ANY KEY TO START"300 PRINT"{clear}{black}{rvrs on}{down:3}WELL WHAT DO YOU KNOW?"305 PRINT"{rvrs on}{down:2}WHILE YOU WERE DOWN ONTHE GROUND JUMPING A- ROUND,YOUR TIME CLOCK"310 PRINT"{rvrs on} JUST RAN OUT!"315 PRINT"{rvrs on}{down:2} NEXT TIME {red}WATCH{black} THE CLOCK!"320 GOTO450

This message is displayed when time runs out. It then goes to the “play again?” screen.

Game won – Falling ape animation

This cleans the screen, draws the ape, and then makes it “fall” to the bottom of the screen. Remember how Donkey Kong would fall at the end of the rivets level? Kinda like that. Just not as good. I had forgotten about this!

525 POKE36874,128530 FORX=15TO0STEP-.1:POKE36878,X:NEXTX:POKE36874,0

That plays a sound effect when the ape hits the bottom of the screen.

Game won message

535 PRINT"{clear}{black}{rvrs on}{down:3}CONGRATULATIONS!!!!!!!{down} YOU NOW KNOW WHAT IT FEELS LIKE TO BE ON"540 PRINT"{rvrs on}TOP!{$a0}YOU PLAYED WELL!"545 PRINT"{rvrs on}{down:2}{blue}BET YOU DON'T WIN NEXT TIME!"548 PRINT:GOTO450

This displays the “game won” message, then goes to 450 to ask the player if they wish to play again.

Programmer’s thoughts

I actually expected this code to be far worse than it actually is. If I were writing it today, I see a number of things I could do to make it more efficient. I also have many ideas of ways I could use the VIC-20 features to make the game fancier (animated characters, better music and sounds).

But, overall, I am fairly pleased with how my junior high self programmed this. I was completely self-taught from reading the VIC-20 manual and articles in magazines.

But since I know I could do it better today, I think I might just give that a shot.

Here is the full listing of this version of the program. If you want to run it, I would do two things:

Delete line 1 entirely.

In line 10, remove the POKE772,200.

And I’d get rid of the same POKEs in the INSTRUCTIONS loader.

And if you don’t want to type it in, here is a virtual tape for the VICE Commodore emulator. It has the above changes made, but otherwise is exactly as I last touched it in 1983. Enjoy!

NOTE: I have a follow-up to this ELSE thing already written which “changes everything” for me. There’s stuff about ELSE I never knew, which makes me want to never use it. More on that in the next installment. And now back to your regularly scheduled blog post…

First, a quick BASIC memory saving tidbit.

Shaun Bebbington left a comment about my VIC-20 dissection article asking if I knew you could omit zeros from DATA statements like this:

100 DATA 128,0,0,50,239,0,123,42,0,4

If you leave them out (comma comma), READ will still get a zero! It saves a byte each time you do that, which could be important on a machine with 3584 bytes free on startup like my VIC-20.

100 DATA 128,,,50,239,,123,42,,4

Good tip, Shaun! Thanks!

Is there anything ELSE I can help you with?

And while I had his attention, I asked him a few Commodore BASIC questions. I was wondering how you did IF/THEN/ELSE without ELSE! One of the many things I liked about the BASIC in the CoCo was it had ELSE and commands like PLAY, DRAW, CIRICLE, etc. My VIC-20 had no ELSE. In my Sky-Ape-Er game, I’d see my young self doing things like this:

125 IF K=17 THEN L=L-1:M=14
130 IF K=41 THEN L=L+1:M=13
135 IF K=39 THEN 180

In that code snippet, K represented the key value that was pressed. If it was 17 (left), I’d move the player left one and change its graphic character. If it was 41 (right), I’d move the player right one and change its graphics character. If it was 39 (F1, jump), I’d go to a routine that would handle the jump.

Without ELSE, if K was 17, it would do that, then check K two more times, even though it didn’t need to. Maybe I did not “see” it back then, but If I had the bytes to spare, I probably should have done something like this:

125 IF K=17 THEN L=L-1:M=14:GOTO 140
130 IF K=41 THEN L=L+1:M=13:GOTO 140
135 IF K=39 THEN 180

That way, if K=17 was true, it would do it’s thing, then GOTO would skip over the next two lines. This could be a huge time saver if there were a bunch of conditions (up, down, left, right, diagonals, jump, fire, punch, windshield wipers, etc.)

Someone has already suggested that I may have done it my original way to get consistent timing in the game loop. Somehow I doubt my junior high self was that clever. But I digress…

With ELSE, it could have been written like this:

125 IF K=17 THEN L=L-1:M=14 ELSE IF K=41 THEN L=L+1:M=13 ELSE IF K=39 THEN 180

Less code (the ELSE token takes up less memory than an addition line number and/or the extra GOTO to skip lines), and faster execution, maybe.

Side Note: Maybe? There is still the issue of, when completing a successful IF, BASIC’s parser still having to run through the rest of the line characters to find the end and the next line. Adding a “GOTO” wouldn’t help, either, since it would have to parse that and then STILL have to scan to the end of the line. (At least on Color BASIC, which does not remember line pointers once it is parsing a line.) It may actually be faster to break up a long set of IF/ELSE into small lines with a GOTO.

It supposedly works like this… since a compare (K=42, A$=”RED”, G>100) will return either -1 (false) or 0 (true), you can do an ON GOTO based on the result of that compare. Since it’s a -1 or 0, you can just make the result negative (thus, -1 becomes 1, and 0 becomes -0 which is still 0):

10 ON -(A=42) GOTO 20:PRINT "NOT 42":END
20 PRINT "42!"

Er… what? I thought BASIC did not execute anything after a GOTO. It doesn’t, does it?

Nothing on a line after GOTO will be executed. WIll it?

But… but… How can ON/GOTO with something after it work, then? It turns out, ON/GOTO is special since the conditions of the “ON” may not be met, and thus the GOTO part may not get executed and BASIC will continue.

ON A GOTO 100,200,300:PRINT "A WAS NOT 1, 2 or 3"

Looking at it like that makes perfect sense to me. If A is not 1, 2 or 3, it won’t GOTO anywhere and the line continues to be processed.

Thus, this odd code serves as a simple “ELSE” when you don’t have ELSE:

Since I was doing code in response to the IF, the ON/GOTO approach would just let me GOTO a line that does that code, which then still needs a GOTO to skip the lines after it that shouldn’t be executed. Not great for that use.

But, if I were dispatching to routines based on direction (like I do with the “jump” check to line 180), it would have worked just fine. Instead of this:

But, doing a quick test typing in JUST those lines on a VIC-20 emulator, I got 3530 bytes free after each approach. No penalty, but no savings, and all the parsing with parens and the negatives is probably slower.

Interesting, for sure. Useful? I guess I’ll find out if I get around to updating my VIC-20 code.

Bonus offer

I also saw this example in the Commodore forum:

10 rem if then else demo
20 input a
30 if a = 1 then goto 60
40 print "this is the else part"
50 goto 70
60 print "this is the if (true) part"
70 end

This is how I’d write that with ELSE:

20 INPUT A
30 IF A=1 THEN PRINT "THIS IS THE IF (TRUE) PART" ELSE PRINT "THIS IS THE ELSE PART"

So we could change the Commodore example to match this a bit closer:

20 input a
30 if a = 1 then print "this is the if (true) part":goto 70
40 print "this is the else part"
70 end

…which leads us back to just adding a GOTO at the end of each separate IF:

Ultimately, we are just trading “ELSE” with “GOTO xx” and a new line, with ELSE being smaller due it it just being a token, verses the overhead of a GOTO token and the line number characters after it, AND a new line for each additional “else” condition.

Previously, I began to dissect my old VIC-20 game Sky-Ape-Er. It was made up of two BASIC programs:

INSTRUCTIONS – Display instructions, set up custom character set, then load and run the main game.

SKY-APE-ER – The main game.

VIC-20 custom fonts

The VIC-20 did not have a true graphics screen. Instead, it used font characters that could be dynamically changed. Each character on the 22×20 screen was 8×8. You could create custom 8×8 characters to represent anything you wanted.

I believe I used a program called Eight by Eight Create by Robert Spahitz from the January 1983 issue of Creative Computing (Vol 9, Number 1). See page 270 (or 272 of this PDF scan):

This also helps me confirm that I got my VIC-20 in 1982 since I initially did not have a Commodore Datasette tape deck for it and had no way to save programs until later. By the time this issue came out, I had the tape deck.

But I digress…

VIC-20 custom fonts on the CoCo

I have screen shots of what the custom characters looked like in the VIC-20 game, but thought it would be fun to take those DATA statements and display them on my CoCo. Using lines 100 to 125 from the INSTRUCTIONS program, I created this Color BASIC program:

NOTE: This article was originally started on February 29, 2016, shortly after I discovered my cigar box of VIC-20 cassette. When I say “recently,” that now means “four years ago…”

Welcome to VIC-20 Tuesday!

I have slowly been importing my old VIC-20 programs in to the VICE emulator. This has been a tricky process. The tapes are over 30 years old and I no longer posses any real hardware to read them on. (I let my dad take my VIC-20 in exchange for getting me a Radio Shack TRS-80 Color Computer.) I played them on a higher end late 1990s Radio Shack dual cassette deck, and record them at 96khz stereo in Audacity on my Mac. The original tapes were mono, but some are stronger in one channel so I have been preserving both tracks as stereo audio files. I then use wav-prg to convert them to image files I can load in the VICE Commodore emulator.

My Sky-Ape-Er game has caught my attention. On the tape was a program called INSRTUCTIONS (with that typo I apparently never noticed) that will display instructions, wait for a keypress, then load the game as a second program called SKY-APE-ER. There were actually three copies of the main program, and at least two were different versions (oh, if only I had added version numbers to the file name). I also found several other copies of the main game program on other tapes in various forms of completion. I am hoping that this one is the “final” complete version.

The loading process looks like this:

VIC-20 loading a program from tape.

…then you would type “RUN” (assuming you didn’t use the shortcut “RUN” key, which would do the “LOAD” and “RUN” automatically):

VIC-20 Sky-Ape-Er loader (instructions).

Then you press any key…

VIC-20 Sky-Ape-Er loader, loading second part.

…and the program will have you “PLEASE WAIT” for a moment, then will erase itself with “NEW” and the “LOAD” the actual game.

I scanned the code to see how it did this, but I saw nothing obvious. No “NEW” or “LOAD” commands or anything. How did it work? Thanks to the Internet, I believe I figured it out.

First, let’s look at the source code of the program:

Sky-Ape-Er loader, lines -25.

Lines 5-6 – REMarks (comments).

Line 10 – POKE stores a value at a memory location. But what is 36879, 808 and 775? A quick search lead me to some answers. 36879 ($900f) is “Screen color / Reverse mode / Border color”. In the VIC-20 manual, Appendix E shows that 26 would be red border, and white screen. Another search led me to a Compute magazine article showing that 808 ($328) is used to disable STOP, RESTORE and LIST (to prevent the user from BREAKing out of the program). 775 ($307) showed up in a correction article from the same magazine, stating that it is what disabled LIST. (Not sure about 808, then. Maybe there were two POKEs required and the first article had a typo or left the second one out by mistake.)

Line 15-25 – The Commodores allowed embedded special control characters, and the inverted heart is clear screen. The others are likely color codes and cursor movements, to position the text where I wanted it to be.

Sky-Ape-Er loader, lines 25-55.

Line 25-50 – More things that print the text on the screen.

Line 55 – Gets a keypress then, if the keypress is nothing (“”), go back and get another keypress. This causes the program to spin in a loop until a key is pressed on the keyboard.

Sky-Ape-Er loader, lines 60-105.

Line 60 – Please wait … for what? It looks like what happens next may take a few seconds.

Line 65 – This is clearing out memory locations 7424 to 7432. But why? All I can tell so far is this is some memory location in RAM with other places saying it’s in the User BASIC area. A helpful forum post gave me a hint. This is actually clearing out the 8 bytes that will make up the “space” character so once I customize the character set with game graphics, that character will still be a space. More on this next…

Line 70 – This walks through memory locations 7168 to 7303 and POKEs them with the values read from DATA statements below. This is storing values in that range of memory. But what is 7168? According to this page, that is the start of “half RAM, half ROM of upper case normal characters since the 14-bit address space of the VIC wraps around”. I believe this is the font/character set, and the DATA statements are the custom graphics for the Sky-Ape-Er program (such as the bricks for the levels, the pieces that make up the ape, the chimps, and the player).

Line 80 – This is the magic! According to this page, memory location 198 is the number of characters in the keyboard buffer. The VIC-20 has a keyboard buffer from 631-640 (9 bytes?). I am stuffing the buffer with 78 (“N”), 69 (“E”), 87 (“W”), 13 (“ENTER”) and then 131 (the “RUN” key). Thus, it’s like I typed “NEW” and pressed ENTER (clearing out the BASIC memory), followed by hitting the “RUN” key, which does a “LOAD/RUN” sequence, loading the next file from tape and running it. Huzzah! The autostart with done by stuffing the keyboard buffer as if the user typed commands. This tells me just putting in “LOAD:RUN” in a line of BASIC wouldn’t have worked (probably because it would overwrite the program, and you could never get to the RUN part). Cool

Sky-Ape-Er loader, lines 110-END.

Line 110-END – The rest of the code are the DATA statements that make up the custom character set. I will have to dissect them so I can see what the bitmaps look like.

I have no recollection of how this works, so I assume these POKE values were things I found in the Commodore VIC-20 manual or read in magazines such as Compute’s Gazette. There was very little information around back then. In fact, when I got this computer, I knew of only one store in all of Houston that sold software for it (other than Commodore cartridges at some places that sold the computer). I had my grandmother drive me across town to go there once, and that is the store where I bought the Krazy Kong game that inspired me to write Sky-Ape-Er.

There was a Commodore Houston’s User’s Group (CHUG) that I attended a meeting of, but that was the only time I ever met other VIC-20 users (other than going to meet the publishers of the FOX-20 cassette magazine at their house). It was a whole different world back then, and it’s amazing that I can go from from “what does this do?” to writing this article in just a few web searches.

Up next: A look at the actual Sky-Ape-Er game itself, including how it uses this custom character set for game graphics.

Converted Source Code

Here is the INSTRUCTIONS program converted from PETASCII to ASCII. I found a utility that does this thanks to this blog post. The utility is found here: