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.
But Shuan mentioned a way of using ON to simulate an ELSE. A quick search led me to a forum post discussing this very thing.
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?
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:
10 INPUT "VALUE";A 20 ON -(A=42) GOTO 30:GOTO 10 30 PRINT "YOU KNOW THE ANSWER!"
…would be like…
10 INPUT "VALUE";A 20 IF A=42 THEN 30 ELSE 10 30 PRINT "YOU KNOW THE ANSWER!"
Interesting! I have not benchmarked this to see if it’s faster than using GOTO to skip lines, but it might be smaller due to not needing another few bytes for each line number.
Would this have helped my “left, right, jump” code? Maybe not. You can string these together like this:
124 ON -(K=17) GOTO 125:ON -(K=42) GOTO 130:ON -(K=39) GOTO 180 125 L=L-1:M=14:GOTO 140 130 L=L+1:M=13:GOTO 140
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:
125 IF K=17 THEN 200 130 IF K=41 THEN 300 135 IF K=39 THEN 400 ... 200 REM Handle LEFT ... 300 REM Handle RIGHT ... 400 REM Handle JUMP ...
Those three lines could be combined into one like this:
124 ON -(K=17) GOTO 200:ON -(K=42) GOTO 300:ON -(K=39) GOTO 400
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.
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:
10 GET A$:IF A$="" THEN 10 20 IF K$="U" THEN Y=Y-1:GOTO 70 30 IF K$="D" THEN Y=Y+1:GOTO 70 40 IF K$="L" THEN X=X-1:GOTO 70 50 IF K$="R" THEN X=X+1:GOTO 70 60 GOTO 10 70 REM DRAW X,Y... 80 GOTO 10
…which on the CoCo’s Extended Color BASIC might look like:
10 GET A$:IF A$="" THEN 10 20 IF K$="U" THEN Y=Y-1 ELSE IF K$="D" THEN Y=Y+1 ELSE IF K$="L" THEN X=X-1 ELSE IF K$="R" THEN X=X+1 ELSE 10 70 REM DRAW X,Y... 80 GOTO 10
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.
Until next time, ELSE maybe sooner…