Monthly Archives: May 2020

Random Easter Egg

There is an interesting hidden message embedded in the Color BASIC ROM, and here is the code that reveals it:

10 A=26493:B=66:C=13:GOSUB40
20 A=291227:B=B+2:C=C+3:GOSUB40
30 END

If you run this code, it will display this:

“COCOFEST” easter egg in the Color BASIC ROM!

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.

10 A=125708:GOSUB 20:A=33435700:GOSUB 20:A=17059266:GOSUB 20
20 A=RND(-A)

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

The infinite monkey theerem states that…

“…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.”


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:

You get these same random numbers every time you power up the CoCo.

Try it for yourself using the web-based JS Mocha CoCo emulator.

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.


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.

  1. 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).
  2. 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):

10 REM rndwords.bas
20 POKE 65495,0
40 TL=LEN(T$)
50 IF TL=0 THEN 30
70 LL=255:HL=0
80 FOR P=1 TO LEN(T$)
90 V=ASC(MID$(T$,P,1))
120 ?V,LL;HL
130 NEXT
150 R=HL-LL+1
170 PRINT "LETTER RANGE :";R;"(";CHR$(LL);" - ";CHR$(HL);")"
190 FL=LL-1
200 FOR SD=1 TO 9999999
210 V=RND(-SD):A$=""
220 A$=A$+CHR$(FL+RND(R))
230 IF LEN(A$)

Some words are found almost instantly. “HI” shows up immediately:

Finding random words in the Color BASIC RND function.

The output shows the random seed to start with (-5), the word it was looking for (“HI”), the ASCII character to add to the random numbers it finds, and the range to use in the RND functions.

To display the string back, you would modify my original COCOFEST program with the proper values, or do it manually:


Here are some words I have found:

V=RND(-26493):FOR I=1 TO 4:PRINT CHR$(66+RND(13));:NEXT

1001 V=RND(-291227):FOR I=1 TO 4:PRINT CHR$(68+RND(16));:NEXT

1002 V=RND(-56403):FOR I=1 TO 3:PRINT CHR$(65+RND(20));:NEXT

1003 V=RND(-1049135):FOR I=1 TO 4:PRINT CHR$(64+RND(20));:NEXT

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.

Until next time…

BASIC and ELSE and GOTO and Work – part 2

See also: part 1

There is a time and ELSE for everything

After I dove into ELSE efficiency, and then dove into it a bit deeper, I realized one of the major things that slows down BASIC is having to scan to the end of the line in order to find the next line.

In part 7 of my Optimizing Color BASIC series, I noticed that a GOTO or GOSUB was much slower if there were things on the line after it:


When BASIC is searching for a line number, each line entry has a size which lets it skip ahead to the next line if the line number did not match. But, once BASIC is processing a line, it does not track that. If the parser gets one token in to a line and it has to GOTO somewhere else, it has to scan forward until it finds the end of the line, then it can start scanning line numbers and skipping lines again.

Thus, one should not put comments on the ends of lines. They have to be parsed through. It is much faster to put comments on their own lines, though that takes up a more memory since each new line number takes 5 bytes.

This is one of the reasons this…

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

…can be slower than…

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

In the first example, if Z=1, BASIC still has to scan through all the remaining bytes of the line until it finds the end. In the second example, BASIC gets the line number then is already at the end and can immediately start scanning.

Thus, to add to my previous ELSE discussion, we should stop doing this:

30 IF X=1 THEN X=X+1:GOTO 70

If X is NOT 1, BASIC still has to scan through the rest of the line before it can check whatever happens next. Instead, it ican be much faster to do this:

30 IF X=1 THEN 100
100 X=X+1:GOTO xxx

There is a caveat to this. Things in BASIC are quite predictable. This change makes it faster to get to line 100 to do the actual work (X=X+1). BUT, from line 100 it could be much slower to get back to the top if there were a bunch of lines before the target line. If it’s a GOTO near the top of the program, it’s fast. If it’s a loop around 500, and you try to GOTO 500, that could be much slower if it had to scan through all the lines from 10 to 499 to get there.

This is when organizing code locations comes in very important, but that is a discussion for another time.

But other than situations like that, this is faster way:


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

50 GOTO 10
100 X=X+1:GOTO 10
200 X=X-1:GOTO 10
300 Y=Y+1:GOTO 10
400 Y=Y-1:GOTO 10

If Z=4, the code will get there much faster since there is less for it to scan through after each Z check.

The location of where the work is done (later in the program or back at the top) is the only thing that can make this slower (or faster). Once a program gets large enough, moving things around may help speed up certain things but will slow down other things. I guess, much like real estate, location matters.

I guess we can stop using ELSE, and stop doing work on IF lines. And that goes for things like this:


At 1000 points, we get a new life! However, every time through that loop when SC is not 1000, BASIC has to skip over “THEN LV=LV+1”, wasting cycles. The savings would be huge over the life of that loop to just do:

IF SC=1000 THEN 1000

…and let 1000 handle LV=LV+1 and returning back with a GOTO. GOSUB might be better, but that might be slower for something like this since:

IF SC=1000 THEN GOSUB 1000

…has an extra token (and potentially spaces, if you use them for readability) versus:

IF SC=1000 THEN 1000

WHEN this code runs (SC=1000), it may be faster to GOSUB and RETURN than it would be to GOTO/GOTO. BUT, most of the time the main loop is running that code will not be called and parsing past the GOSUB will just be wasting time.

Until next time…

Rename your way to cleaner code…

Sometimes I see things like this…

if (input(DIGITAL_IN1) == HIGH)

if (input(DIGITAL_IN2) == HIGH)

if (input(DIGITAL_IN3) == HIGH)

This code led me to having to open schematics and figure out what digital input 1, 2 and 3 where.

So I used the free CodeBlocks editor refactor “Rename Symbol” feature to change their names to:

if (input(ROBOT_ON_FIRE_IN1) == HIGH)


if (input(DUST_BIN_FULL_IN3) == HIGH)

…but using names that made sense for my application ;-)

Someone must have thought it was important to include the input number (IN1, IN2 and IN3) — possibly so they would not need to look through defines to see what value goes to what input — so I kept those in my names.

But now, when I find this code months down the line, I’ll immediately know which function is most likely detecting that the robot is on fire.

Pretty easy, isnt’ it?

Until next rename…

The 1987 Max Headroom TV station hijack incident.


  • 05-20-2020: Fixed a few typos, added a few more sentences.

Every since I first learned about the Max Headroom signal hijacking incident in 1987, I’ve been fascinated about it. There has been much coverage of this over the years, including some interesting “recreations” of behind-the-scenes footage.

There has even been a documentary about the incident. Here are some to check out:

  1. Oddity Archive episode 1 (2012) (and commentary version).
  2. Oddity Archive episode 137 (2017).
  3. The Bizarre documentary (2019).
  4. “Leaked Footage” (2019).
  5. …and dozens of others if you just search YouTube.

However, one thing remains consistent when I watch videos that theorize on this, or read the REDDIT threads, etc. Most seem to think that this was an inside job (it probably was; would be the easiest explanation). But, most don’t seem to realize how much you could do with good home equipment back in the early 1980s, let alone towards the end of that decade.

1980s home video was better than folks think.

My first encounter with a home video recorder was one my father had — a huge, hulking machine with giant push buttons and a pop up tray to insert the VHS tape. This was around 1980 or 1981.

Over the years, he had all kinds of cool video equipment. We had an early video camera, which could hook to the VCR using an adapter box that would power the camera and turn it’s output into audio/video cables. This camera was an old-style camera that would leave streaks when you moved it past lights due to the way the image sensor worked. Early, ancient stuff!

Later he had a backpack-sized VHS unit that could be ran off a 12V power supply or battery, and we took it, and the external camera, to Walt Disney World in 1982. As a young teen, it took me and another kid to lug it around (one with the recorder strapped to him, and the other operating the camera). This was all consumer equipment.

He also had Betamax (then later a SuperBeta) equipment as well. Folks commenting don’t seem to remember that Beta was widespread for awhile — early video rental stores had both VHS and BETA movies available to rent.


Before the FCC put and end to it, we had in-home TV stations! You could buy a box that would transmit video to a nearby TV. And by nearby, I mean down the block. My father would broadcast movies in the evenings and let the neighbors know so they could tune in and watch. And that TV transmitter box could be ran on batteries. I remember one Thanksgiving (? or maybe it was a Christmas ?) where I was walking around the festivities with the luggable VHS unit and camera, recording stuff while others watched what I was doing on the TV in the living room. I guess that was really cool, but it was all just normal stuff to me, having grown up around it.

And it kept getting better…

Each time my dad upgraded, the new equipment was even batter. I still have the full size SuperVHS camcorder my father gave me after he upgraded to 8mm video (and it still works!).

Back to Max

Back to the Max Headroom incident… a few things I want to say:

  1. Often you say people say Max was autistic, or drunk, or just nervous. But why? It does not seem to have been a live recording. There is an edit in the middle of the video! At best, the first half could be live then they switch to a tape, or the first part could be a tape and they switch to live, but it would be much easier to just pre-record and hit PLAY on a VCR. Max may have been drunk, but the evidence suggests he wanted it that way or he could have just re-recorded everything.
  2. The edit is often pointed out as being proof that he was a TV station insider because the edit is perfect. Look up “flying erase head.” You could buy VCRs that had this, and they would make seamless cuts from one recording to the next. They cost more, but you could buy a consumer recorder that had one. It was not anything magical or special. BUT, you didn’t even need one. That luggable portable VCR we had could often do really clean edits — even without a flying erase head! My dad edited so many productions using two of those units (including videos that ran at booths at boat shows, that I did computer graphics for, and even a travel video for the island of Belize, which I’d never heard of back then). We had another unit after that one which was not portable, but did edits so well I did STOP MOTION animation with it. Just a nice consumer VCR! You did NOT need professional equipment to get a clean edit.
  3. As to how they hijacked the signal, TV and radio stations commonly had (and still do) their studios at one place, and beamed their broadcast signal to the remote transmitter. Theories say they were up on a tall building. But why? Every radio station remote with a MARTI unit could broadcast from car dealerships. Heck, the run down AM radio station I worked at in 1987 had one, and it was ancient. And TV stations would go “live” from remote events all the time from their news van. I think the folks who talked about a vehicle (why a van?) being used probably make more sense than climbing up a building. It would be far easier to just park somewhere near the receiving dish and beam a low power signal to it, but I only have experience with doing that with radio stations. (There would be a lot of other issues, since I believe the remote news van would be beaming a different type of signal to a special receiver, and would NOT be capable of sending in the broadcast signal.) But, getting a signal in between the studio and the remote transmitter location could be done from the ground. (Or from a building; but it seems far riskier to climb a building and set equipment up.)

In a city the size of Chicago, I have no doubt that cameras and transmitters and all kinds of video things were readily available to those who wanted them. The only magic part here is the equipment that was used to overpower the TV station’s broadcast signal. An insider would have information, but folks could buy a lot of used equipment like this even back then (before eBay). The requirement to have need a license to operate it did not prevent you from buying it. (Anyone could by a HAM radio, but it was illegal to use it without a license, for example, and we had a place where I grew up that sold police radios and such.)

So who knows. Insider (or at least someone from the industry) makes sense. HAM radio/electronics hobbyists? Sure, why not. But I wish folks would drop the claims of the clean edit as proof it was someone with professional equipment. At least the Oddity guy talked about it looking like it was on a VHS unit (though that was just because of the poor picture quality — the fact that it did such a lean edit shows it would have been a higher quality machine).

I sure hope one day we hear the story behind this event.

Until next time…

BASIC and ELSE and GOTO and Work – part 1

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?


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


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.


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
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.

Benchmarking going to a line

In my benchmark program, this one counted to 954:

0 REM elsetst1.bas '954
20 FORB=0TO1000
30 IF Z=1 THEN 100 ELSE IF Z=2 THEN 200 ELSE IF Z=3 THEN 400 ELSE IF Z=4 THEN 500

And this one was a tiny bit faster. It counted to 942:

0 REM elsetst1.bas '942
20 FORB=0TO1000
30 IF Z=1 THEN 100
31 IF Z=2 THEN 200
32 IF Z=3 THEN 300
33 IF Z=4 THEN 400

Thus, using ELSE was a bit worse if none of the conditions were true.

IF we could have used ON/GOTO, that would be blazing at 253!

0 REM elsetst3.bas '253
20 FORB=0TO1000
30 ON Z GOTO 100,200,300,400

But I said we couldn’t, because I changed the rules to “do work” rather than “go to a line.”

Benchmarking doing work

Doing work with ELSE clocked in at 601:

0 REM elsetst4.bas '601
20 FORB=0TO1000

Since ELSE was slower to go to a line, I thought maybe it would be here, too, but instead, splitting the statements was slower. This one reports 963:

0 REM elsetst5.bas '963
20 FORB=0TO1000
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

It seems like ELSE has its place, but not for just going to a line.

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.


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

0 REM elsetst6.bas '1034
20 FORB=0TO1000
30 IF Z=1 THEN X=X+1:GOTO 70
31 IF Z=2 THEN X=X-1:GOTO 70
32 IF Z=3 THEN Y=Y+1:GOTO 70
33 IF Z=4 THEN Y=Y-1

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.

What do you think? Did I miss anything?

Until next time…

VIC-20 “smooth move”.

I got stuck on my multi-part Sky-Ape-Er dissection tangent, so I thought I’d do something different for today’s VIC-20 Tuesday.

The VIC-20 uses programmable character based graphics, You can change the pixels that make up a letter “A” to be a small 8×8 icon of a spaceship, for instance. But, when you move that letter A to different spots on the screen, it jumps 8 pixels at a time making movement quite jerky (as demonstrated by all my VIC-20 programs):

Since I don’t have time to write a full article at the moment, I’ll share this small VIC-20 program and come back to discuss what it is doing, and why it is doing it, later.

30 print"{clear}{reverse on}frames:"
60 for c=0 to 7:print chr$(65+c);chr$(73+c):print:next
70 gosub 950
100 poke 36869,255
105 rem
108 rem go thru each row of the character
109 rem
110 for ln=0 to 7
115 rem
118 rem read value, multiply by 256 to make 16-bits
119 rem
120 read v:v=v*256
125 rem
128 rem go thru each frame character
129 rem
130 for ch=0 to 7
135 rem
138 rem split 16-bit value into 8-bit values
139 rem
140 b1=int(v/256)
150 b2=v-(b1*256)
155 rem
158 rem poke shifted value in each charater
159 rem
160 poke 7176+ch*8+ln,b1
170 poke 7176+ch*8+ln+64,b2
175 rem
178 rem shift 16-bit value to the right one bit
179 rem
180 v=v/2
190 next
200 next
210 gosub 950
900 poke 36869,240:poke 198,0
910 end
950 get a$:if a$="" then 950
960 return
1000 DATA 60,126,255,255,255,255,126,60

Until then…


More comments from the first ELSE article… First, MiaM chimes in:


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)


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.

20 FORB=0TO1000
30 ON K-16 GOTO 100,,,,,,,,,,,,,,,,,,,,,,200,,300
110 GOTO70
210 GOTO70
310 GOTO70

Best case for the first target gives me 590. Not bad!

Trying again with “K=41” for worst case gives us 664. Still not terrible.

How does this rank against manual IF/THENs like this?

20 FORB=0TO1000
30 IF K=17 THEN 100:GOTO 70
40 IF K=39 THEN 200:GOTO 70
50 IF K=41 THEN 300
110 GOTO70
210 GOTO70
310 GOTO70

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 provided a suggestion about using an array:

Yep, could also do something like
Dim C(256)

ON C(K) GOSUB 20,30,40
But that’s probably a bit memory hungry.


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.

Let’s try:

7 DIMK(41):K(17)=1:K(39)=2:K(41)=41
20 FORB=0TO1000
30 ON K(K) GOTO 100,200,300
110 GOTO70
210 GOTO70
310 GOTO70

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:

DIM K(41)

…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.

Very cool.

Thanks to MiaM and Rob for these interesting ideas.

Until next time…

VIC-20: Sky-Ape-Er code dissection – part 5

See also: part 1, part 2, part 3, part 4 or part 5 (with more coming).

Is it really VIC-20 Tuesday again? Okay, then. Let’s get started…

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:

  1. A custom character set. I will use the editor to export the characters out as DATA statements into a BASIC file.
  2. That new file will be turned in to a program that will READ the DATA statements and POKE the values into RAM memory.
  3. 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.

0 rem custom charset
1 rem protect chars
2 rem from basic.
3 REM set string end
4 POKE51,0:POKE52,28
5 REM set memory end
6 POKE55,0:POKE56,28
7 REM clear vars
10 for l=7168 to 7168+12*8-1
15 read v:poke l,v
20 next
25 rem clear 'space'
30 for l=7424 to 7432
35 poke l,0:next
40 print"{clear}{reverse on}charset:{reverse off}"
45 print" a b c d"
50 print" e g i"
55 print"@f@h@j@k@"
60 poke 36869,255
65 get a$
70 if a$="" then 65
75 poke 36869,240
80 print l and 255;int(l/256)
85 end
1000 DATA 255,2,4,8,16,32,255,255
1010 DATA 0,28,46,71,142,92,56,0
1020 DATA 16,40,68,98,118,62,28,8
1030 DATA 0,28,58,113,226,116,56,0
1040 DATA 16,56,124,110,70,34,20,8
1050 DATA 96,240,96,62,185,89,28,30
1060 DATA 189,68,132,66,33,0,255,255
1070 DATA 24,60,24,126,189,189,189,60
1080 DATA 189,36,36,36,102,0,255,255
1090 DATA 6,15,6,124,157,154,56,120
1100 DATA 189,34,33,66,132,0,255,255
1110 DATA 255,102,129,66,138,150,223,255

Here is what it is doing:

  • 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:


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 my demo program:

0 rem charset demo
1 REM set vars start
2 POKE45,104:POKE46,19
3 REM set string end
4 POKE51,0:POKE52,28
5 REM set memory end
6 POKE55,0:POKE56,28
7 REM clear vars
9 for l=7424 to 7432
10 poke l,0:next
11 REM charset in RAM
12 POKE 36869,255
13 rem
100 print "{clear}{down*20}";
105 print "@@@@@@@@@@@@@@@@@@@@@@";
110 for l=38400 to 38911:poke l,0:next
115 rem init bricks
120 for b=0 to 3:bl(b)=7680+rnd(1)*22+88*b:bc(b)=1+b:next
125 rem init player
130 p1=8109:p2=8131:pt=7:pb=8
135 rem main loop
140 pokep1,pt:pokep2,pb
145 for b=0 to 3:poke bl(b),32
150 bl(b)=bl(b)+22:if bl(b)>8120 then bl(b)=7680+rnd(1)*22
155 bc(b)=bc(b)+1:if bc(b)>4 then bc(b)=1
160 poke bl(b),bc(b)
165 next
170 get a$
175 if a$="a" then if p2>8119 then pokep1,32:pokep2,0:p1=p1-1:p2=p2-1:pt=5:pb=6:goto 140
180 if a$="s" then if p2<8141 then pokep1,32:pokep2,0:p1=p1+1:p2=p2+1:pt=9:pb=10:goto 140
185 if a$="q" then 510
190 pt=7:pb=8
195 goto 140
500 REM charset in ROM
510 POKE 36869,240
520 END
1000 PRINT PEEK(45);PEEK(46)

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:


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:

  1. 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.
  2. LOAD (but DO NOT run) the “CHAR SET TEST” program.
  3. 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.
  4. 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.

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.

What am I doing wrong? I guess I’m gonna need a part 6. . .

Until next time…