I need to know the real way to do this. BING AI and ChatGPT keep giving me different solutions, and only this last one works. Or seems to work.
I was amused that the BING AI suggested using DEF FN to create this function on a BASIC without LN.
DEF FNLN(x)
=LOG(x) / LOG(2.71828)
Changing the X to uppercase allows this to work on the CoCo. The results are close — are they close enough? Would more decimal places in the LOG value help?
2026-05-06 – This program has a bug where the graphics are flipped vertically. A followup article explaining the bug is being written, but in the meantime you can fix the bug in line 480 by changing the end to “V=V+BT(8-BT)” (or, if you are using the slower method, line 500 to “V=V+2^(8-BT)”).
NOTE: Can anyone tell me how to get the blank lines out of the code listings? I used to be able to copy/paste from MS Visual Studio Code with no problem, but something has changed in WordPress. For the past few weeks, I have to paste in (it looks fine), then when I look at it, it has the line feeds. I then EDIT, and copy/paste it back in to Code (where I see the blank lines) then search/replace those blank lines, then copy/paste it back in to WordPress. That has been working fine, but now that does not work. Maybe it’s a different between PC Code and Mac Code (PC using CR/LF or something?). Anyone know?
Revisiting the PUT from DATA concept… For those who don’t want to figure out the binary values for the characters, here is a routine that lets you define them using strings:
'TANK UP DATA " XX " DATA " XX " DATA "XX XX XX" DATA "XXXXXXXX" DATA "XXXXXXXX" DATA "XXXXXXXX" DATA "XXXXXXXX" DATA "XX XX"
The routine reads these 8-character strings, then goes through them to build a value based on where the X is found.
Here is some code you can play with…
0 'PUTDATA.BAS 1 '2024-01-22 2 'SHOWS HOW TO DO IT USING 3 'STRINGS RATHER THAN #S 4 '
10 'TO SPEED UP STR-TO-BYTE 20 DIM BT(7):FOR BT=0 TO 7:BT(BT)=2^BT:NEXT 30 ' 40 'VARIABLES USED IN THE 50 'POKE-TO-ARRAY ROUTINE MUST 60 'BE PRE-ALLOCATED OR THEY 70 'WILL CAUSE ARRAY MEM TO 80 'SHIFT WHEN IT GET USED. 90 ' 100 DIM L,V,Z,Z$ 110 ' 120 'EACH ARRAY ENTRY CAN STORE 130 '5 BYTES. AN 8X8 SINGLE 140 'COLOR CHARACTER IS 8, SO 150 'WE NEED TWO ARRAY ENTRIES 160 '(10 BYTES) TO FIT THE 8 170 'BYTE CHARACTER. 180 ' 190 DIM TU(1),TD(1),TL(1),TR(1):GOSUB 320
200 ' 210 ' TEST PROGRAM 220 ' 230 PMODE 4,1:PCLS 1:SCREEN 1,1 240 PUT (0,0)-(7,7),TU 250 PUT (16,0)-(16+7,7),TD 260 PUT (32,0)-(32+7,7),TL 270 PUT (48,0)-(48+7,7),TR 280 GOTO 280
380 ' 390 'READ DATA AND POKE AT L 400 ' 410 PRINT 420 'READ STRINGS AND CONVERT 430 'TO BYTES. 440 FOR Z=0 TO 7:V=0 450 READ Z$:PRINT Z$,; 460 FOR BT=1 TO 8 470 'FASTER WAY 480 IF MID$(Z$,BT,1)="X" THEN V=V+BT(BT-1) 490 'SLOW WAY 500 'IF MID$(Z$,BT,1)="X" THEN V=V+2^(BT-1) 510 NEXT:PRINT INT(V);HEX$(V) 520 POKE L+Z,(NOT V)+256:NEXT:RETURN 530 NEXT:PRINT V;HEX$(V):NEXT 540 RETURN
550 ' 560 '8X8 SPRITE CHARACTERS 570 ' 580 'TANK UP 590 DATA " XX " 600 DATA " XX " 610 DATA "XX XX XX" 620 DATA "XXXXXXXX" 630 DATA "XXXXXXXX" 640 DATA "XXXXXXXX" 650 DATA "XXXXXXXX" 660 DATA "XX XX"
670 'TANK DOWN 680 DATA "XX XX" 690 DATA "XXXXXXXX" 700 DATA "XXXXXXXX" 710 DATA "XXXXXXXX" 720 DATA "XXXXXXXX" 730 DATA "XX XX XX" 740 DATA " XX " 750 DATA " XX "
760 'TANK LEFT 770 DATA " XXXXXX" 780 DATA " XXXXXX" 790 DATA " XXXX " 800 DATA "XXXXXXX " 810 DATA "XXXXXXX " 820 DATA " XXXX " 830 DATA " XXXXXX" 840 DATA " XXXXXX"
850 'TANK RIGHT 860 DATA "XXXXXX " 870 DATA "XXXXXX " 880 DATA " XXXX " 890 DATA " XXXXXXX" 900 DATA " XXXXXXX" 910 DATA " XXXX " 920 DATA "XXXXXX " 930 DATA "XXXXXX "
You can take this and change the characters real easily to play with it. Here is a smiling face instead of the UP TANK:
580 'SMILING FACE 590 DATA " XXXXXX " 600 DATA "X X" 610 DATA "X X X X" 620 DATA "X X" 630 DATA "X X X X" 640 DATA "X XX X" 650 DATA " X X " 660 DATA " XXXX "
Have fun… When I get time, I’ll try to post versions of these routines on my Github that handle different size objects as well as 4-color objects for us on PMODE 1 and 4 screens.
When I posted a video of my CoCo Factory TNT test, Chet Simpson suggested I could eliminated flicker by making a double-high sprite, with the top half blank. When I put the falling bomb, it would erase the position after it. L. Curtis Boyle suggested something similar to my tank game.
So I did it. It took a bit to figure out the logic. I have four 8×8 characters of the tank facing each direction, then I have two 16×8 (for left, and right) and two 8×16 (for up and down) versions.
Ericomont mentioned that the tank could leave tracks (I guess, since it seems like my black on white screen looks like snow). I added those too.
And then some sound. When the tank is not moving, the sound is slow — as if the tank is idling. When the taking is moving, it makes a faster sound.
I am having fun playing.
This version does to have the firing. I need to optimize this a bit and then add some bullets to it ;-)
Here is a video:
And here is the current code. There are some commented out lines I was using for debugging – like making a box around where the tank’s current position is (TX/TY) and where the old position was (OX/OY). That was massively helpful in me being able to see the logic to how I should draw and erase the tanks.
Now that I understand it, maybe I can do it better.
And this code can certainly be optimized and be made much faster.
Enjoy…
0 ' 1 ' TANKPUT8X8-SOUND.BAS 2 ' ALLEN C. HUFFMAN 3 ' SUBETHASOFTWARE.COM 4 ' 2023-01-19 5 '
10 ' LOAD SPRITES 20 DIM TU(1),TD(1),TL(1),TR(1),UE(4),DE(4),LE(4),RE(4),Z,L,V:GOSUB 2000
50 ' 256X192 BLACK AND WHITE 60 PMODE 4,1:PCLS 1:SCREEN 1,1
70 ' TANK POSITION AND DIR. 80 TX=8:TY=8:TD=4:OX=8:OY=8:Z=0
90 ' ERASE PREV POSITION 100 LINE(OX,OY)-(OX+7,OY+7),PSET,BF 110 ' PUT TANK ON SCREEN 130 PUT(TX,TY)-(TX+7,TY+7),TU:GOTO 180 140 PUT(TX,TY)-(TX+7,TY+7),TD:GOTO 180 150 PUT(TX,TY)-(TX+7,TY+7),TL:GOTO 180 160 PUT(TX,TY)-(TX+7,TY+7),TR:GOTO 180 165 ' PUT WITH BLANK NEXT TO IT 166 PUT(TX,TY)-(TX+7, TY+15),UE:GOTO 180 167 PUT(OX,OY)-(OX+7, OY+15),DE:GOTO 180 168 PUT(TX,TY)-(TX+15,TY+7),LE:GOTO 180 169 PUT(OX,OY)-(OX+15,OY+7),RE
170 ' READ KEYBOARD (WASD) 180 Z=0:POKE&H155,&HFF:POKE&H156,&HFF:POKE&H157,&HFF:POKE&H158,&HFF 181 'LINE(TX-1,TY-1)-(TX+8,TY+8),PRESET,B 182 'LINE(OX-1,OY-1)-(OX+8,OY+8),PRESET 185 Z=Z-1:IF Z<0 THEN Z=20:EXEC 43345 190 A$=INKEY$:IF A$="" THEN 185 191 'LINE(TX-1,TY-1)-(TX+8,TY+8),PSET,B 192 'LINE(OX-1,OY-1)-(OX+8,OY+8),PSET 200 LN=INSTR(KY$,A$):IF LN=0 THEN 180 210 OX=TX:OY=TY 220 ON LN GOTO 240,270,300,330
230 ' UP 240 IF TD=1 THEN IF TY>0 THEN TY=TY-8:GOTO 166 250 TD=1:GOTO 130 260 ' DOWN 270 IF TD=2 THEN IF TY<184 THEN TY=TY+8:GOTO 167 280 TD=2:GOTO 140 290 ' LEFT 300 IF TD=3 THEN IF TX>0 THEN TX=TX-8:GOTO 168 310 TD=3:GOTO 150 320 ' RIGHT 330 IF TD=4 THEN IF TX<248 THEN TX=TX+8:GOTO 169 340 TD=4:GOTO 160
3000 ' READ DATA AND POKE AT L 3010 PRINT "."; 3020 'FOR Z=L TO L+7:READ V:POKE Z,(NOT V)+256:NEXT:RETURN 3021 FOR Z=L TO L+7:READ V:POKE Z,(NOT V)+256:NEXT:RETURN 3025 PRINT "--"; 3026 FOR Z=L TO L+15:READ V:POKE Z,(NOT V)+256:NEXT:RETURN 3030 ' 8X8 SPRITE CHARACTERS 3040 ' TANK UP 3050 DATA 24,24,219,255,255,255,255,195 3060 ' TANK DOWN 3070 DATA 195,255,255,255,255,219,24,24 3080 ' TANK LEFT 3090 DATA 63,63,30,254,254,30,63,63 3100 ' TANK RIGHT 3110 DATA 252,252,120,127,127,120,252,252
3120 ' TANK UP+EMPTY 3130 DATA 24,24,219,255,255,255,255,195 3131 DATA 0,195,0,0,0,195,0,0 3140 ' TANK EMPTY+DOWN 3150 DATA 0,0,195,0,0,0,195,0 3151 DATA 195,255,255,255,255,219,24,24 3160 ' TANK LEFT+EMPTY 3170 DATA 63,68, 63,68, 30,0, 254,0, 254,0, 30,0, 63,68, 63,68 3180 ' TANK EMPTY+RIGHT 3190 DATA 34,252, 34,252, 0,120, 0,127, 0,127, 0,120, 34,252, 34,252
Over in the Facebook CoCo group, Carlos Comacho shared a link to a cool online emulator for the BBC Micro. The link contains a BASIC program listing that drew a very cool galaxy graphic.
I decided to try converting it to the CoCo. This led me to looking up some BBC Micro Commands to understand what they did. I found a few significant differences:
All variables need to be uppercase to work on the CoCo.
PI is not a constant on the CoCo, so it needs to be defined.
Line 10 – MODE1 sets a 320×256 4 color screen. On the CoCo 1/2, the 4-color screen has a resolution of 128×191.
Line 20 – VDU5 – Not used on the CoCo. It outputs a screen character of 5 for some reason.
Line 30 – RND(-64) would seed the random number generator, but on the BBC Micro it returns the -64. On the CoCo, RND(-64) looks like it does the same as RND(0) on the CoCo, returning a value from 0.0 to .9999. The FOR/NEXT will just have to do -64 for the start number.
Line 80, 90, 120, 140 and 230 – Their RND(1) returns a number from 0.0 to .99999. This will be changed to RND(0) on the CoCo.
Line 110 and 290 – The CoCo needs a THEN between IF X>4 THEN DO SOMETHING.
Line 160,170, 190, 200, 240, 250, 260 and 270 – SIN and COS need parens on the CoCo.
Line 280 – POINT looks like PPOINT on the CoCo.
Line 290 – If the program was written for a 4-color “MODE1” display, I assume their colors are 0-3. On the CoCo, they are 1-4, so that is a minor change.
Line 300 – GCOL0,P sets a color, so probably COLOR P.
Line 310 – PLOT69,X,Y becomes PSET(X,Y)
Line 320 – Their BASIC has an interesting shortcut for NEXT. After a FOR A you need a NEXT A, but you can also just say “NEXT” and it uses the most recent FOR. If you have nested loops, like FOR A :FOR B … you can end with “NEXT:NEXT” or “NEXT B,A”. It looks like their BASIC allows leaving out the variables, but using the comma. I tried this on CoCo, and it didn’t work. That would have been a fun discovery. That just means “NEXT,” becomes “NEXT:NEXT”.
When I did a quick port, I saw it passing negative values to PPOINT and PSET, so I had to add an extra check that skips plotting anything that would be off the screen (X <0 or >255, and Y<0 or >191).
Here is a program that creates four 8×8 “sprites” that can be PUT on the screen. Each sprite represents the tank aimed in a different direction (up, down, left or right). For speed, they are only PUT on a byte boundary.
Use the arrow keys to move the tank around the screen.
The POKEs in line 180 reset the BASIC keyboard rollover table, which lets you hold down an arrow and the tank keeps moving.
Just for fun…
0 ' 1 ' TANKPUT.BAS 2 ' ALLEN C. HUFFMAN 3 ' SUBETHASOFTWARE.COM 4 ' 2023-01-16 5'
10 ' LOAD SPRITES 20 DIM TU(1),TD(1),TL(1),TR(1),Z,L,V:GOSUB 2000
3000 ' READ DATA AND POKE AT L 3010 PRINT "."; 3020 FOR Z=L TO L+7:READ V:POKE Z,(NOT V)+256:NEXT:RETURN 3030 ' 8X8 SPRITE CHARACTERS 3040 ' TANK UP 3050 DATA 24,24,219,255,255,255,255,195 3060 ' TANK DOWN 3070 DATA 195,255,255,255,255,219,24,24 3080 ' TANK LEFT 3090 DATA 63,63,30,254,254,30,63,63 3100 ' TANK RIGHT 3110 DATA 252,252,120,127,127,120,252,252
And here is one using 16×16 sprites:
10 ' 20 ' TANKPUT16.BAS 30 ' ALLEN C. HUFFMAN 40 ' SUBETHASOFTWARE.COM 50 ' 2023-01-16 60'
70 ' LOAD SPRITES 80 DIM TU(6),TD(6),TL(6),TR(6),Z,L,V:GOSUB 2000
110 ' 256X192 BLACK AND WHITE 120 PMODE 4,1:PCLS 1:SCREEN 1,1
130 ' TANK POSITION AND DIR. 140 TX=0:TY=0:TD=4:OX=0:OY=0
150 ' ERASE PREV POSITION 160 LINE(OX,OY)-(OX+15,OY+15),PSET,BF 170 ' PUT TANK ON SCREEN 180 ON TD GOTO 190,200,210,220 190 PUT(TX,TY)-(TX+15,TY+15),TU:GOTO 240 200 PUT(TX,TY)-(TX+15,TY+15),TD:GOTO 240 210 PUT(TX,TY)-(TX+15,TY+15),TL:GOTO 240 220 PUT(TX,TY)-(TX+15,TY+15),TR
230 ' READ KEYBOARD (WASD) 240 POKE&H155,&HFF:POKE&H156,&HFF:POKE&H157,&HFF:POKE&H158,&HFF 250 A$=INKEY$:IF A$="" THEN 250 260 LN=INSTR(KY$,A$):IF LN=0 THEN 240 270 OX=TX:OY=TY 280 ON LN GOTO 300,330,360,390
290 ' UP 300 TD=1:IF TY>0 THEN TY=TY-8:GOTO 160 310 GOTO 180 320 ' DOWN 330 TD=2:IF TY<176 THEN TY=TY+8:GOTO 160 340 GOTO 180 350 ' LEFT 360 TD=3:IF TX>0 THEN TX=TX-8:GOTO 160 370 GOTO 180 380 ' RIGHT 390 TD=4:IF TX<240 THEN TX=TX+8:GOTO 160 400 GOTO 180
2000 ' LOAD SPRITE CHARACTERS 2010 PRINT "LOADING DATA"; 2020 L=VARPTR(TU(0)):GOSUB 3000 2030 L=VARPTR(TD(0)):GOSUB 3000 2040 L=VARPTR(TL(0)):GOSUB 3000 2050 L=VARPTR(TR(0)):GOSUB 3000 2060 RETURN 3000 ' READ DATA AND POKE AT L 3010 PRINT "."; 3020 FOR Z=L TO L+31:READ V:POKE Z,(NOT V)+256:NEXT:RETURN 3030 ' 8X8 SPRITE CHARACTERS 3040 ' TANK UP 3050 DATA 1,128 3060 DATA 1,128 3070 DATA 1,128 3080 DATA 1,128 3090 DATA 225,135 3100 DATA 255,255 3110 DATA 225,135 3120 DATA 227,199 3130 DATA 229,167 3140 DATA 229,167 3150 DATA 228,39 3160 DATA 228,39 3170 DATA 227,199 3180 DATA 224,7 3190 DATA 255,255 3200 DATA 224,7 3210 ' TANK DOWN 3220 DATA 224,7 3230 DATA 255,255 3240 DATA 224,7 3250 DATA 227,199 3260 DATA 228,39 3270 DATA 228,39 3280 DATA 229,167 3290 DATA 229,167 3300 DATA 227,199 3310 DATA 225,135 3320 DATA 255,255 3330 DATA 225,135 3340 DATA 1,128 3350 DATA 1,128 3360 DATA 1,128 3370 DATA 1,128 3380 ' TANK LEFT 3390 DATA 15,255 3400 DATA 15,255 3410 DATA 15,255 3420 DATA 4,2 3430 DATA 4,2 3440 DATA 4,242 3450 DATA 5,10 3460 DATA 255,202 3470 DATA 255,202 3480 DATA 5,10 3490 DATA 4,242 3500 DATA 4,2 3510 DATA 4,2 3520 DATA 15,255 3530 DATA 15,255 3540 DATA 15,255 3550 ' TANK RIGHT 3560 DATA 255,240 3570 DATA 255,240 3580 DATA 255,240 3590 DATA 64,32 3600 DATA 64,32 3610 DATA 79,32 3620 DATA 80,160 3630 DATA 83,255 3640 DATA 83,255 3650 DATA 80,160 3660 DATA 79,32 3670 DATA 64,32 3680 DATA 64,32 3690 DATA 255,240 3700 DATA 255,240 3710 DATA 255,240
A few months back, 8-bit Show and Telltweeted this:
I was curious if the bug existed in Color BASIC, so I tried it and tweeted back confirming ours had the same issue.
Recently, he posted this deep dive video about the bug, mentioning my reply and others (including CoCoist Tim Lindner) that showed this bug on various other systems:
At the time of the original tweet, I wrote a blog post about the bug (that is the screen shot he shows in his video). I thought I’d add a follow-up to my post, with a bit more detail.
The bug explained…
William Astle commented on my original post explaining what was going on, similar to the explanation Robin gave in a tweet reply.
The issue is that VAL puts a NUL at the end of the string then calls the regular number parser, which can then bail out unceremoniously. While VAL itself does restore the original byte after the string, that code path does not execute when an error occurs parsing the number. The NUL is required so the parser knows where to stop interpreting bytes.
– William Astle, 8/17/2023
Let’s dive in a bit more from the CoCo side… First, just in case you aren’t a BASIC programmer, the VAL keyword is designed to convert a string to a numeric variable. For instance, you can do this:
A$="1234" A=VAL(A$)
Above, A$ is a string variable containing the characters “1234” and A is a numeric variable of 1234. I see this often used in Extended Color BASIC when the LINE INPUT command is used with a string, and then converted to a number:
10 LINE INPUT "AGE: ";A$ 20 A=VAL(A$)
But I digress..
LINE INPUT is a better form of INPUT, but it only works with string variables. If you were to type letters in to a LINE INPUT, then run those through VAL, they should evaluate as 0. So type in “42” and VAL(A$) gives you 42. Type in “BACON” and VAL(A$) gives you 0. If you had just used INPUT “AGE”;A and typed non-numbers, it would respond with “?REDO” and go back to the input prompt.
Second, let’s make the bug easier to see by clarifying this “1E39” scientific notation thing. The bug has nothing to do with using scientific notation. It has to do with having a number that is too big causing the Overflow Error and aborting the VAL conversion of a string to a number.
Scientific Notation
“1E39” is a number 1 followed by 39 zeros. It appears BASIC is happy to print out the full number if it is short enough, but at some point starts showing it in scientific notation. I found that 1 followed by 8 zeros (100000000) is fine, but 9 zeros switched over to scientific notation:
And it does that even if you just try to use a number like “1000000000” directly:
I guess I had never used numbers that large during my Color BASIC days. ;-)
You may notice it prints “1E9” back as “1E+09”. You can use “1E+09” or “1E+9” as well, and it does the same thing. If you leave out the “+”, it assumes it. The reason for the plus is because you can also use it to represent fractional numbers. In the case of +9, it is moving the decimal place nine places to the right. “1E5” is taking “1.0” and moving the decimal place five places to the right like “100000.0”
If you use a “-“, you are moving the decimal that many places left. “1E-1” takes “1.0” and moves the decimal one spot left, producing “.1”. It appears you cannot print as many values that way before it turns in to scientific notation:
And, printing those values directly shows something similar:
I guess I had never used numbers that small during my Color BASIC days. ;-)
This made me wonder if the VAL bug would happen if a value was too small, but it seems at some point the number just becomes zero, so no error occurs. (Maybe William will chime in with some more information on this. I was actually expecting a similar “Underflow” error, but I don’t think we have a ?UF ERROR in Color BASCIC ;-)
For fun, I wondered if this was truly considered zero. In C, using floating to compare against specific floating point values can cause issues. For example:
#include <stdio.h> #include <stdlib.h>
int main() { float a = 902.1;
if (a == 902.1) { printf ("a is 902.1\n"); } else { printf ("a is NOT 902.1\n"); }
return EXIT_SUCCESS; }
I have discussed this here in the past, but if you run that, it will print “a is NOT 902.1″. This is because 902.1 is not a value that a 32-bit C floating point variable can exactly represent. I expect this could also be the case in Color BASIC, so I wanted to do a quick check and see if “1E-39” (which shows as zero) really was 0:
IF 0=1E-39 THEN PRINT "YES"
That printed “YES” so I will just assume at a certain point, BASIC floating point values just turn in to zero.
But I digress… Again.
The point is, it’s a bug with the number being too large, so even if you do this, you can cause the overflow:
10 A=VAL("1000000000000000000000000000000000000000"):REM SHOW BUG 20 PRINT A
Above, that 40 character number (1 with the decimal place 39 places to the right) is just too long and it will cause the ?OV ERROR.
Strings in String Memory
In my String Theory series, I dove in to how strings work on the CoCo. The important bit is there is reserved string memory for strings, for things like INPUT/LINE INPUT, and string manipulation like MID$, LEFT$, etc. There are also “constant” strings that exist in the program code itself.
If you assign a string directly from BASIC (not in a line number of a program), it will go in to string memory:
A$="THIS IS IN STRING MEMORY" PRINT A$ THIS IS IN STRING MEMORY
But, if that is in a program, BASIC just makes an entry for “A$” and points it to the spot in the BASIC program where the quoted text exists:
10 A$="THIS IS IN PROGRAM MEMORY" 20 PRINT A$ RUN THIS IS IN PROGRAM MEMORY
That is what causes the problem with VAL. BASIC attempts to modify the closing quote in the BASIC program itself and make it a 0, and never restores it. The BASIC “LIST” command starts showing the line up until it sees a 0, then stops. The rest of the line is still in memory, but is now invisible to LIST. If you try to run the program after it gets “corrupted”, it will error out on the VAL line since it is missing the closing quote:
However, the code is still there. If you know where the BASIC program starts in memory, and ends in memory, you can use PEEK/PRINT to see the contents. Memory locations 25/26 are the start of the BASIC program, and locations 27/28 are the start of variables which are stored directly after the program, so something like this would do it:
Much like what Robin showed in his video using a machine language monitor to dump memory, above we can look for the bytes that would be the “1E39” (quote is 34, “1” is 49, “E” is 69, “3” is 51 and “9” is 57), we can find that byte sequence of 34, 49, 69, 43, 51 and 57 in the second line followed by a zero where the final quote (34) used to be. After that zero is a 41 which is the “)” that used to be in VAL(“1E39”), then a 58 which is a “:” colon, and then a 130 which is the byte for the “REM” token, then a 32 which is a space, and 83, 72, 79 and 38 are “SHOW” followed by a 32 space then 66, 85, 71 which is “BUG” and a real 0 marking the end of the line.
If I knew the byte that is now a 0, I could just POKE it back to 34 and restore the program, just like Robin did on his Commodore 64.
FOR A=PEEK(25)*256+PEEK(26) TO PEEK(27)*256+PEEK(28):PRINT A,PEEK(A):NEXT
That would start printing memory locations and I could quickly BREAK the program when I see the 0 I am looking for show up.
I believe the zero at 9744 is the one after “1E39” and I can do this to restore the program:
Now, if only Color BASIC did that after an ?OV ERROR! Although we did get an updated BASIC in 1986 for the Color Computer 3, it was just patches on top of the old Microsoft BASIC to add new CoCo 3 features.
Avoiding the VAL bug
Which brings me to this… If the string to parse was in string memory, changing that final byte and not changing it back would be no problem because strings all end with a 0 in memory anyway! There is nothing to corrupt.
To force a variable to be in string memory, you can add +”” when you declare it, like this:
10 A$="THIS IS IN STRING MEMORY"+""
Since BASIC has to combine those two strings, it makes a new string in string memory to copy the “THIS IS IN STRING MEMORY” string and the “” string there. (It is not smart enough to check to know that “” is unneeded, which is good because it lets us do things like this.)
10 A=VAL("1E39"+""):REM NO BUG HERE 20 PRINT A
And that is a simple way to work around this bug. Since the bug only affects hard-coded strings in program memory, it should be easy to avoid just by not using values too large for variables :)
And if you are inputting them, the INPUT is going in to string memory and you will still get an ?OV ERROR (crashing the program) but at least the program would not get corrupted:
When I got my VIC-20, that is all I had — just the VIC-20 computer. I did not have the Commodore Datasette (tape recorder). I could not save anything that I typed in. Because of this, I recall leaving my VIC turned on just so I wouldn’t lose the program I had typed in (or was in the process of typing in).
In the back of the book is a section called “Programs to Try.” It contains listings of three games. In the PDF above, these games where called:
Tank Versus UFO by D. Later
Killer Comet by Duane Later
Rocket Command by Duane Later
But, in the book that came with my VIC-20, I believe at least two of them were called something else: Meteor and Rocket Fighter! I am pretty sure I remember Tank versus UFO being called that in my book.
I recall typing these programs in and fixing either bugs in the listing or, more likely, bugs in my typing ability. Much to my surprise, I found my original VIC-20 tapes several years ago and was able to get “most” of what I had on those tapes loaded in to an emulator. Two of the programs I found were ones I had typed in from the VIC-20 book.
When I went to look up these games, I found many videos on YouTube showing different versions than the ones I had. I originally figured some VIC owners just customized the games and they ended up in a collection somewhere on the Internet.
But now I think there might have been multiple versions of these games released by Commodore. The PDF of the book on archive.org says it is the 5th edition. I am now trying to find out how many editions of this book were published. I would like to see if they contain different versions of these programs.
Here are some differences I found between the ones I typed in from my book, and the ones shown in that 5th edition copy I found online.
Killer Comet (PDF) versus Meteor (mine)
VIC-20 “Killer Comet”VIC-20 “Meteor”
The differences between the two versions are mostly cosmetic:
The name: “Meteor” in my book, and “Killer Comet” in the PDF.
The “graphics”: A block for the cannon and ball for the bullet in the PDF, versus a letter “A” for the cannon and vertical bar for the bullet in mine.
Game messages: “Delta base destroyed” in my book, versus “Moon base destroyed” in the PDF. Strangely enough, the “Killer Comet” prints “Meteor destroyed.” This further makes me think I had an earlier version and Meteor was the original name.
Stars on the screen (I may have added those) done through some extra POKEs neat the top of the program:
The 46 is a period. These POKEs are poking the “stars” in the background. As the meteor goes across the screen, it will erase those stars, so I am not sure if this was part of the original program, or something I had added. I know I had a game I was writing that did a similar thing with stars in the background, but mine got restored so they were not erased by other objects:
If I added those stars, perhaps I also changed the name? But if so, it wouldn’t explain why the Killer “Comet” version still calls it a meteor…
Rocket Command (PDF) versus Rocket Fighter (mine)
Again, these versions are very similar except for a few minor differences:
The name: “Rocket Fighter” in my book, versus “Rocket Command” in the PDF.
The “graphics”: Different ASCII (er, PETSCII) characters are used for the game playfield. The PDF uses a letter “X”, and mine used a PETSCII graphics character. For the missile, the original used a text up arrow, and mine uses a card clubs character.
Tank versus UFO
If I saved out Tank versus UFO, I couldn’t find it. Or, it might have been damaged on the ancient cassette tapes I found a few years ago and I couldn’t get it to load. I have no idea what differences there might be between the 5th edition version and whatever I typed in.
Why so different?
When I look up these games on YouTube, I see videos several other variations. I expect some where enhancements done by VIC-20 owners, but others look like just slightly updated versions. Did I do the same thing, and modify these programs to change the names?
Modified versions of Tank-v-Ufo (p. 153-156) appear to have been published as Tank-v-Ufo (VIC Games), Tank Attack (Commodore Computing International, 82-11), Ufo-Jagd (Happy Computer, 83-12), Invasion (Commander, 84-02) and Save The City! (Big K, 84-04).
A slightly different version of Rocket Command (p. 158-159) was published in VIC! (Vol 6).
Modified versions of Rocket Command (p. 158-159) appear to have been published as UFO Target (Commodore Computing International, 83-08) and Rocket! (Big K, 84-04).
And none of those match the names of the versions I have! It does “confirm” that Commodore published different versions of at least two of these – Tank Attack (Tank vs. UFO) and Rocket Command (UFO Target).
Tank vs UFO versions
Tank Attack (Commodore Computing International)
Yet the title screen still reads “Tank versus UFO”…
Invasion (Commander … magazine?)
Save the City (Big K)
Yet the title still reads “Tank versus UFO”…
Rocket Command versions
UFO Target (Commodore Computing International)
Rocket Command
I find this version interesting because it has the PETSCII “X” character used for the base, like my version, but still uses the up arrow character like the PDF version.
Rocket (Big K)
Yet the title still reads “Rocket Command”.
It is clear that games based on the type-in programs were certainly appearing in may publications… Did the version I have actually come from a magazine, rather than the VIC-20 manual? (I remember having Family Computing and Compute, which spun off in to Compute’s Gazette, during my VIC-20 days.)
Can anyone shed some light on this? How many editions to this manual were there, and did the program listings change? I did find a 1981 manual that does not say what edition it is, so it might be the first edition — but the program listings have the same names as the 5th edition.
I’d really like to know where I got “Meteor” and “Rocket Fighter” from.
In BASIC, the “=” works a bit differently because it can be both an assignment (A=5) or a test (IF A=5 THEN). For example:
REM ASSIGNMENT A=5
REM TEST IF A=5 THEN PRINT "A IS 5"
Perhaps this is why there was a keyword LET in early BASIC. When you use LET (it is supported on the CoCo), it tells BASIC you are assigning a variable:
LET A=5
Perhaps if LET was required for an assignment, those BASICs could treat the “=” when seen not after a LET as a test. Anyone know the history of LET?
In C, this is solved by having “=” be an assignment, and “==” being a test:
a = 5; // assignment
if (a == 5) { ... } // test
BASIC could have used “LET” to me “=”, and no LET to mean “==”, but it doesn’t. Saying “the BASIC parser doesn’t work like that” is an accurate answer, but it did bring to mind some things I never knew back when I was learning BASIC.
What is A=B?
You cannot answer that, without seeing more context. “IF A=B THEN” is a test. “LET A=B” or just “A=B” is an assignment.
When is it a test? After IF.
IF A=5 THEN PRINT "A IS 5!"
It is also a test after a PRINT:
PRINT A=5
Or after an assignment:
Z=A=5
To make this cleared, the “test” part (after the initial assignment) could be put in parentheses is like this:
Z=(A=5)
But, the parents are optional in this case, but can matter when doing certain things, such as math.
I am sure I have discussed this in an earlier article, but to recap, when things are a test, they return either -1 if TRUE, or 0 if FALSE. For example:
A=1 B=1 PRINT A=B -1
A=0 B=1 PRINT A=B 0
The result of any test in BASIC is either 0 or -1. BASIC treats 0 as FALSE, and anything else as TRUE:
IF 0 THEN PRINT "FALSE" IF -1 THEN PRINT "TRUE" IF 1 THEN PRINT "TRUE" IF 42 THEN PRINT "TRUE"
Here’s a simple program that shows this:
10 FOR A=-2 TO 2 20 PRINT A,;:IF A THEN PRINT "TRUE" ELSE PRINT "FALSE" 30 NEXT
BASIC returns either -1 (TRUE) or 0 (FALSE) from a comparison, but IF only cares about “not zero” as true, and treats only 0 as false. That logic makes sense. “IF condition is true THEN do something”.
If you were trying to specifically test for 0 or -1, you would have a different result:
IF A=0 THEN PRINT "FALSE" ELSE IF A=-1 THEN PRINT "TRUE"
That would not catch anything that wasn’t a 0 or -1, so you’d really want to add a bit more:
IF A=0 THEN PRINT "FALSE" ELSE IF A=-1 THEN PRINT "TRUE" ELSE PRINT "UNKNOWN"
I’ll mention this again in a moment…
Let’s see what the BASIC parser is trying to do when it sees something like this:
A=B=4
What does that mean? A= tells us there is an assignment, then after that, then B=4 is what we’d like for it to be, and B=4 is a test. In Chet’s case…
A=B=C=D=4
We might feel all variables would come back as 4, but the variables appear to be completely left alone:
10 A=0:B=1:C=2:D=3 20 A=B=C=D=4 30 PRINT A;B;C:D
Running that will print…
0 1 2 3
The key part is “appear to be completely left alone”. There is something happening that we are not seeing. If you change the code to set the variables to 1, 2, 3 and 4, and then try to set all of them to 5, you now see more of what is going on:
10 A=1:B=2:C=3:D=4 20 A=B=C=D=5 30 PRINT A;B;C:D
Running THAT version will print:
0 2 3 4
Now we can see that A, which starts out as 1, is getting set to 0. Something is being assigned after all. If you break it down, here it what you see:
Individually, each of those tests returns 0 for FALSE. Since we see that A is getting set to 0, it is getting assigned the result of the first test after it: B=C. If you try this, you would get the same result in A:
A=1:B=2:C=3 A=(B=C) PRINT A 0
That makes sense. And if the parser is just going through the line, automatically grouping the tests, perhaps something like this:
A=1:B=2:C=3:D=4 A=(B=(C=D))
And THAT looks exactly like what we are getting.
C=D returns 0, false.
B=0 (the result of C=D) returns 0, false.
Then A is assigned 0.
Could it be as simple as that? Somehow BASIC has to know if something is an assignment or a test, and perhaps as it scans forward, finding an additional “=” is enough to start it looking for tests rather than assignments. (I bet William Astle already knows how this works, and maybe he will see this and comment.)
Chet’s code shows BASIC does not work like we might expect, especially if we are used to how things work in languages like C. In C, it would look like this:
#include <stdio.h> #include <stdlib.h>
int main(int argc, char **argv) { int a,b,c,d;
a=1; b=2; c=3; d=4;
a=b==c==d;
printf ("a=%d b=%d c=%d d=%d\n", a, b, c, d);
return EXIT_SUCCESS; }
Notice the use of double equals “==” in the assignment line. If you run that in C, you get the same results as BASIC:
a=0 b=2 c=3 d=4
The thing C does differently is that when you have an assignment, that code block returns the value of the assignment, since it knows it is an assignment:
If BASIC forced the use of LET for an assignment, or used “==” for test instead of “=”, then it could figure out what we mean when we try “A=B=C=D=5″” or whatever. BASIC would know that “D=4” is an assignment, and to return 4, versus “D==4” being a test, and returning true 0 or false -1.
This is a quick follow-up to a post I did back in 2020. I had written this at the time, but never completed my experiments to post the results. Here is a video demo of using eight sets of two programmable characters to represent the 8×8 object at each position. This allows smooth horizontal positioning.
5 GOSUB 500:rem poke 36869,255 10 print"{clear}{reverse on}generating frames:" 20 for c=0 to 7:print "{reverse on}";chr$(65+c);chr$(73+c);" -> {reverse off}";chr$(65+c);chr$(73+c):print:next 25 get a$:if a$="" then 25 28 poke 36869,255 30 rem char row 40 for r=0 to 7 50 read v:v1=v 60 l1 = 7168+8+r 70 l2 = 7168+8+(8*8)+r 80 rem character 90 for c=0 to 7 100 poke l1+(c*8),v1 110 v1=v1/2 120 v2=(v and bt(c)-1) * bt(8-c) 130 poke l2+(c*8),v2 140 next 150 next 200 rem smooth move 210 print "{reverse on}move: a/s quit: q" 220 c=0:f=0 230 print chr$(65+f);chr$(73+f);"{left*2}"; 240 k=peek(197):if k=64 then 240 250 if k=48 then 380 260 if k=17 then 300 270 if k=41 then 340 280 goto 240 290 rem left 300 if f>0 then f=f-1:goto 230 310 if c>0 then c=c-1:f=7:print" {left*3}"; 320 goto 230 330 rem right 340 if f<7 then f=f+1:goto 230 350 if c<20 then c=c+1:f=0:print" "; 360 goto 230 370 rem quit 380 poke 36869,240:poke 198,0 390 end 500 rem initialize 510 for z=0 to 7:bt(z)=2^z:next 520 for z=7168+8 to 7168+8+16*8:poke z,0:next 525 for z=7126+(32*8) to 7168+(32*8)+8:poke z,0:next 530 return 600 rem print bits 610 for bt=0 to 7 620 if v and bt(bt) then print"1";: goto 640 630 print "0"; 640 next:return 5000 rem 8x8 object 5010 rem data 60,126,255,255,255,255,126,60 5020 DATA 24,60,126,60,255,126,255,255