Category Archives: CoCo

Tandy/Radio Shack TRS-80 Color Computer (CoCo)

Porting a BBC Micro galaxy program to the CoCo

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.

Here is the page:

https://bbcmic.ro/?t=9db8C

Here is the BBC Micro program:

10 MODE1
20 VDU5
30 FORG=RND(-64)TO400
40 t=RND(99):q=RND(99)
50 u=RND(1280):v=RND(1024)
60 A=RND(1)*3
70 R=90/(1+RND(200))
80 Q=1+R*(.5+RND(1)/2)
90 a=1+3*RND(1)^2
100 M=1
110 IFRND(9)<4Q=R:t=0:q=0:A=0:M=PI/3:a=1
120 C=(1+3*RND(1)^2)*R*R
130 FORi=0TOC
140 S=-LNRND(1)
150 T=i*M
160 U=S*R*SINT
170 V=S*Q*COST
180 T=S*A
190 X=U*COST+V*SINT
200 Y=V*COST-U*SINT
210 D=(X*X+Y*Y)/(R*R+Q*Q)
220 Z=99*((2.7^-D)+.1)
230 Z=Z*(RND(1)-.5)^3
240 y=Y*COSt+Z*SINt
250 Z=Z*COSt-Y*SINt
260 x=u+X*COSq+y*SINq
270 y=v-X*SINq+y*COSq
280 P=POINT(x,y)+a
290 IFP>3P=3
300 GCOL0,P
310 PLOT69,x,y
320 NEXT,
330 REM

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

My attempt at a CoCo 1/2 version looks like this:

0 'BBC MICRO - 320X256	4 COLOUR DISPLAY
1 POKE 65395,0
5 PI=3.141592653589793238
10 'MODE1
11 PMODE 4,1:PCLS:SCREEN 1,1:PMODE 3,1
20 'VDU5
30 'FORG=RND(-64)TO400
31 FORG=-64TO400
40 T=RND(99):Q=RND(99)
50 U=RND(1280):V=RND(1024)
60 A=RND(1)*3
70 R=90/(1+RND(200))
80 'Q=1+R*(.5+RND(1)/2)
81 Q=1+R*(.5+RND(0)/2)
90 'A=1+3*RND(1)^2
91 A=1+3*RND(0)^2
100 M=1
110 'IFRND(9)<4Q=R:T=0:Q=0:A=0:M=PI/3:A=1
111 IFRND(9)<4THENQ=R:T=0:Q=0:A=0:M=PI/3:A=1
120 'C=(1+3*RND(1)^2)*R*R
121 C=(1+3*RND(0)^2)*R*R
130 FORI=0TOC
140 'S=-LNRND(1)
141 S=-(LOG(RND(0)) / 0.4342944819)
150 T=I*M
160 'U=S*R*SINT
161 U=S*R*SIN(T)
170 'V=S*Q*COST
171 V=S*Q*COS(T)
180 T=S*A
190 'X=U*COST+V*SINT
191 X=U*COS(T)+V*SIN(T)
200 'Y=V*COST-U*SINT
201 Y=V*COS(T)-U*SIN(T)
210 D=(X*X+Y*Y)/(R*R+Q*Q)
220 Z=99*((2.7^-D)+.1)
230 'Z=Z*(RND(1)-.5)^3
231 Z=Z*(RND(0)-.5)^3
240 'Y=Y*COST+Z*SINT
241 Y=Y*COS(T)+Z*SIN(T)
250 'Z=Z*COST-Y*SINT
251 Z=Z*COS(T)-Y*SIN(T)
260 'X=U+X*COSQ+Y*SINQ
261 X=U+X*COS(Q)+Y*SIN(Q)
270 'Y=V-X*SINQ+Y*COSQ
271 Y=V-X*SIN(Q)+Y*COS(Q)
275 IF X<0 OR X>255 THEN 321
276 IF Y<0 OR Y>191 THEN 321
280 'P=POINT(X,Y)+A
281 P=PPOINT(X,Y)+A
290 'IFP>3P=3
291 IFP>3THENP=3
300 'GCOL0,P
301 COLORP
310 'PLOT69,X,Y
311 PSET(X,Y)
320 'NEXT,
321 NEXT:NEXT
330 REM
340 GOTO 340

Unfortunately, these changes were not enough.

Can you help?

My old (and new) VIC-20 stuff is now on Github

Preservation…

https://github.com/allenhuffman/VIC-20

And so is my *ALL RAM* BBS for the CoCo:

https://github.com/allenhuffman/ALL-RAM-BBS

And various BASIC things from other blog posts:

https://github.com/allenhuffman/BASIC

And a Lites Out game from an upcoming blog series:

https://github.com/allenhuffman/Lites-Out

There is even a VIC-20 port of that one:

https://github.com/allenhuffman/VIC-20/tree/main/my%20new%20programs/lites%20out

Simple 8×8 and 16×16 Tank on 256×192 screen in Color BASIC

Just for fun…

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

30 ' ARROW KEYS
40 KY$=CHR$(94)+CHR$(10)+CHR$(8)+CHR$(9)

50 ' 256X192 BLACK AND WHITE
60 PMODE 4,1:PCLS 1:SCREEN 1,1

70 ' TANK POSITION AND DIR.
80 TX=0:TY=0:TD=4:OX=0:OY=0

90 ' ERASE PREV POSITION
100 LINE(OX,OY)-(OX+7,OY+7),PSET,BF
110 ' PUT TANK ON SCREEN
120 ON TD GOTO 130,140,150,160
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

170 ' READ KEYBOARD (WASD)
180 POKE&H155,&HFF:POKE&H156,&HFF:POKE&H157,&HFF:POKE&H158,&HFF
190 A$=INKEY$:IF A$="" THEN 190
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 TD=1:IF TY>0 THEN TY=TY-8:GOTO 100
250 GOTO 120
260 ' DOWN
270 TD=2:IF TY<184 THEN TY=TY+8:GOTO 100
280 GOTO 120
290 ' LEFT
300 TD=3:IF TX>0 THEN TX=TX-8:GOTO 100
310 GOTO 120
320 ' RIGHT
330 TD=4:IF TX<248 THEN TX=TX+8:GOTO 100
340 GOTO 120

999 GOTO 999

2000 ' LOAD SPRITE CHARACTERS
2010 PRINT "LOADING DATA";
2030 L=VARPTR(TU(0)):GOSUB 3000
2040 L=VARPTR(TD(0)):GOSUB 3000
2050 L=VARPTR(TL(0)):GOSUB 3000
2060 L=VARPTR(TR(0)):GOSUB 3000
2190 RETURN

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

90 ' ARROW KEYS
100 KY$=CHR$(94)+CHR$(10)+CHR$(8)+CHR$(9)

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

The VAL overflow bug and scientific notation

A few months back, 8-bit Show and Tell tweeted 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:

10 PRINT "TYPE 1 AND 39 ZEROS":
20 INPUT A

Have fun…

A=B=C=D in BASIC and C

Chet Simpson posted this in the Color Computer Facebook group recently:

Some folks understand it, some folks are confused by it, and I decided to be inspired by it to write this article.

In the C programming language, you can do that assignment:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
int a,b,c,d;

a = 0;
b = 1;
c = 2;
d = 3;

a = b = c = d = 4;

printf ("a=%d b=%d c=%d d=%d\n", a, b, c, d);

return EXIT_SUCCESS;
}

That program will produce the following output:

a=4 b=4 c=4 d=4

You can try this program online, if you want.

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:

A=1:B=2:C=3:D=4
PRINT A=B
0
PRINT B=C
0
PRINT C=D
0

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:

printf ("a=1 returns: %d\n", a=1);
printf ("b=2 returns: %d\n", b=2);
printf ("c=3 returns: %d\n", c=3);
printf ("d=4 returns: %d\n", d=4);

That would print:

a=1 returns: 1
b=2 returns: 2
c=3 returns: 3
d=4 returns: 4

And in BASIC, the “a=1” if being seen as a test will return either -1 (if false) or 0 (true).

In C, we have “==” to change the behavior from assignment to test:

a=1;
b=2;
c=3;
d=4;

printf ("a==1 returns: %d\n", a==1);
printf ("b==2 returns: %d\n", b==2);
printf ("c==3 returns: %d\n", c==3);
printf ("d==4 returns: %d\n", d==4);

Above, that prints:

a==1 returns: 1
b==2 returns: 1
c==3 returns: 1
d==4 returns: 1

In C, 1 is true, and 0 is false. Had the variables been initialized to something that made the tests for 1, 2, 3 and 4 invalid…

a=42;
b=42;
c=42;
d=42;

printf ("a==1 returns: %d\n", a==1);
printf ("b==2 returns: %d\n", b==2);
printf ("c==3 returns: %d\n", c==3);
printf ("d==4 returns: %d\n", d==4);

…you would see that the value returned from the “a==1” test would be 0 for false.

a==1 returns: 0
b==2 returns: 0
c==3 returns: 0
d==4 returns: 0

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.

But it doesn’t.

Happy new year!

Extended Color BASIC PUT from DATA

Updates:

  • 2024-01-09 – Corrected a PMODE 1 size typo from 8×18 to the correct 8×16, and a 16×17 typo.

Here is a quickie…

I did not know this was possible “back in the day,” and do not think I have ever tried it until now.

In Extended Color BASIC you can use “GET” to get a block of memory from the screen and store it in array memory. You can then “PUT” it back on to the screen.

But, if you know where the array memory is (using the VARPTR function) you can POKE bytes directly in to the array and then just PUT it without ever needing to GET.

10 ' GET/PUT ARRAY (2 ENTRIES)
20 DIM B(1)
30 ' PRE-ALLOCATE VARIABLES
40 DIM A,D,L,V
50 ' 256x192 (32X24 OF 8X8 CHARS)
60 PMODE 4,1:PCLS:SCREEN 1,1
70 ' GET ADDRESS OF ARRAY DATA
80 V=VARPTR(B(0))
90 ' POKE 8X8 DATA IN TO IT
100 FOR L=V TO V+7:READ D:POKE L,D:NEXT
110 ' PUT THE DATA ON THE SCREEN
120 PUT(0,0)-(7,7),B
130 GOTO 130
140 ' BOMB DATA
150 DATA 24,24,60,118,122,126,126,60

This demo puts an 8×8 bomb on the top left of the screen. The bomb was one of the characters from my VIC-20 game Factory TNT.

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

It does not look like you can use a multi-dimensional array got GET/PUT (does anyone know if this is possible?). Pity. If that were possible, I had something I wanted to try.

How much array memory do I need?

To know how many DIM array elements you need for an object, run this program:

0 REM pmodes.bas
5 CLS:INPUT "W,H";MW,MH:PRINT
10 FOR M=0 TO 4:READ M$
20 PRINT "PMODE";M;"(";M$;")"
30 NEXT
40 PRINT
50 PRINT "M W X H W X H PXLS BYT E"
60 PRINT "- -- -- -- -- ---- --- --"
70 FOR M=0 TO 4
80 W=MW:H=MH
90 PRINT USING "# ##X##";M;W;H;
100 PRINT" -> ";
110 IF M<4 THEN W=W/2
120 IF M<2 THEN H=H/2
130 P=W*H
140 B=P/(8-4*(M AND 1))
150 E=INT(B/5+.5)
160 PRINT USING "##X## #### ### ##";W;H;P;B;E
170 NEXT
180 IF INKEY$="" THEN 180
190 GOTO 5
999 GOTO 999
1000 DATA "128 X 96 X 2"
1010 DATA "128 X 96 X 4"
1020 DATA "128 X 192 X 2"
1030 DATA "128 X 192 X 4"
1040 DATA "256 X 192 X 2"

Type in the width/height and it shows you the memory requirements for that object on each PMODE screen. If it says it needs two elements, that is a DIM X(1) because DIM starts at 0. DIM X(3) would give you four — X(0), X(1), X(2) and X(3).

PMODE width and heights

Be aware that all PMODE screens use coordinates of 256×192, and scale to whatever resolution is being used (128×96, 128×192 or 256×192).

On a PMODE 4 screen (256×192), doing LINE(0,0)-(7,7),PSET,B draws a box that is truly 8 pixels wide by 8 pixels tall.

On a PMODE 2 or 3 screen (128×192), the same command would draw a box that was 4 pixels wide and 8 pixels tall.

On a PMODE 0 or 1 screen (128×96) would be drawing a box that was 4 pixels wide by 4 pixels tall.

If you truly wanted an 8×8 object on a PMODE 0 or 1 screen (128×96) screen, you would have to draw it as 16×16. An 8×8 object on a PMODE 2 or 3 screen (128×192) would be drawn as if it was 8×16.

Here is a short program that draws a 16×16 pixel box on each of the five PMODE screen types. It adjusts the width and height based on the mode to know how big of a box it has to draw which would appear as 16×16:

10 FOR M=0 TO 4
20 W=16:H=16
30 PMODE M,1:PCLS:SCREEN 1,1
40 IF M<4 THEN W=W*2
50 IF M<2 THEN H=H*2
60 LINE(0,0)-(W,H),PSET,B
70 LINE(0,0)-(W,H),PSET
80 LINE(W,0)-(0,H),PSET
90 IF INKEY$="" THEN 90
100 NEXT
999 GOTO 999

And that is not confusing at all ;-) But it did mean that you could draw anything as if it were a PMODE 4 screen and then draw it on any lower resolution screen without changes — you could just lose detail.

But I digress. It looks like I should write a new article series…

Tackling the Logiker 2023 Vintage Computing Christmas Challenge – part 4

See also: part 1, part 2, part 3 and part 4.

If I would have told my teenage-self that one day I would be corresponding with Radio Shack ROM-Pak game programmer, Rick Adams, I would have not believed myself. Rick became a very well known name in the CoCo community, and I remember seeing his name in the title screen of Radio Shack ROM-Paks. Temple of ROM was one I played at Radio Shack, but never owned. I just recall thinking it was like a fancy version of my all-time favorite Atari VCS game, Adventure.

But I digress…

It started with a message containing a screen shot:

Rick had simplified the program by breaking up the pattern in to sections he could GOSUB to and print a few lines. It was simple, and smaller than printing the whole thing.

“Why line 2046?” I asked. Apparently, the PDP-8 BASIC he was using had that as the highest line number. This was one of many restrictions this version of BASIC had. Through our chat, I learned it had a limit to the length of a string, and could not do things like MID$(A$,1,1)=”A” which were methods I had been toying with.

The next thing he sent me was optimizing this pattern further, breaking it up in to just one part of the diamond shape:

1 DIM A$(4)
10 FOR I = 1 TO 4
20 READ A$(I)
30 NEXT I
40 FOR I = 1 TO 4
50 PRINT A$(I);A$(I);A$(I)
60 NEXT I
70 FOR I = 3 TO 1 STEP -1
80 PRINT A$(I);A$(I);A$(I)
90 NEXT I
300 DATA " * "
301 DATA " * * "
302 DATA " * *"
303 DATA "* "
2046 END

This proof-of-concept code worked by loading this pattern in to an array. It can print the top array string three times across the screen, then continue through the pattern. Then, for the reverse diamond, print the array backwards. This version was clever, though it left off the right-most asterisk of the bottom/middle row, which could be handled by adding an extra IF/PRINT for those lines. Neat!

But, even when packing everything together (the PDP-8 BASIC has its own way of combining lines using a backslash), it did not look like it would be the smallest approach to this challenge.

A bit later, I received this screen shot:

This, my friends, is what a programmer comes up with when they are tired of typing in Microsoft BASIC. Rick has a front-end (pre-processor?) that allows him to write nicely readable (for BASIC) code and then have it converted to Microsoft BASIC for loading on a CoCo.

I typed that in (well, not literally) to a CoCo and saw that it worked. But what is it doing? Let’s look at a packed CoCo version of this code he provided (adjusted to work on the 40 column screen):

1 WIDTH40:GOSUB2:FORK=1TO3:FORI=1TO3:GOSUB2:NEXTI:FORI=2TO0STEP-1:GOSUB2:NEXTI:NEXTK:END
2 A$=STRING$(20,32):FORJ=4TO16STEP6:MID$(A$,J-I)="*":MID$(A$,J+I)="*":NEXTJ:PRINTA$:RETURN

This looks like a similar approach that Jason Pittman had mentioned he was working on — drawing the pattern as if it was two asterisks that just get further apart as they move down the line (then closer together as it continues).

Rick’s version appears to work by starting with an empty string – generated using the STRING$(20,32) for a string of 20 CHR$(32) spaces. As it goes forward through the loop, it changes the character in the string at position J+I to be an asterisk, then the character at position J-I to be an asterisk . At the top/bottom of each diamond, the offset is 0, so it is just putting the asterisk on top of itself.

Let me break this apart, closer to his original pre-preprocessed source. I will put some extra spaces in to space out the loops:

1 GOSUB 100
2 FOR K=1 TO 3
3 FOR I=1 TO 3
4 GOSUB 100
5 NEXT I
6 FOR I=2 TO 0 STEP -1
7 GOSUB 100
8 NEXT I
9 NEXT K
10 END
100 A$=STRING$(20,32)
101 FOR J=4 TO 16 STEP 6
102 MID$(A$,J-I)="*"
103 MID$(A$,J+I)="*"
104 NEXT J
105 PRINT A$
106 RETURN

Line 100 – The subroutine creates an A$ of 20 space characters.

Line 101 – The FOR loop of 4 TO 16 STEP 6 will produce values of 4, 10 and 16. That matches the top spacing of the peak of the diamond shapes in the pattern:

.........11111111112
12345678901234567890
* * *
* * * * * *
* * * * * *
* * * *

Line 102 – at position J-I (I is set by a FOR loop before it GOSUBs to this routine) will be places an asterisk at that location inside the A$. The outer FOR I loop is 1 TO 3, calling this routine each time, so it would look like this:

I=1, J=4, 10, 16 (producing 4-1, 10-1 and 16-1 ... 3, 9 and 15).
"..*.....*.....*....."

I=2, J=4, 10, 16 (producing 4-2, 10-2 and 16-2 ... 2, 8 and 14).
".*.....*.....*......"

I=3, J=4, 10, 16 (producing 4-3, 10-3 and 16-3 ... 1, 7 and 13).
"*.....*.....*......."

Line 103 – this line does the same thing, but uses the position of J+I so the asterisk moves to the right each time:

I=1, J=4, 10, 16 (producing 4+1, 10+1 and 16+1 ... 5, 11 and 17)
"....*.....*.....*..."

I=2, J=4, 10, 16 (producing 4+2, 10+2 and 16+2 ... 6, 12 and 18)
".....*.....*.....*.."

I=3, J=4, 10, 16 (producing 4+3, 10+3 and 16+3 ... 7, 13 and 19)
"......*.....*.....*."

Since these two lines are adding asterisks to the same A$, the results actually look like this:

"..*.*...*.*...*.*..."
".*...*.*...*.*...*.."
"*.....*.....*.....*."

Line 104 – is the NEXT for the FOR/J, so it goes through all three entries (4, 10 and 16).

Line 105 – prints the modified string.

Line 106 – returns back to the main code.

You will notice that calling this routine like this misses the top line. Let’s look at the start of the program to see how it gets there:

Line 1 – This initial GOSUB to 100 is what draws that top line. When the program starts, all variables are zero. So the routine enteres with I set to 0, which would make J+I and J-I just be the J value, putting an asterisk at the 4, 10, and 16 positions:

"...*.....*.....*...."

Well. That’s clever.

Line 2 – This is just a FOR loop to draw the pattern three times.

Line 3 – This is the FOR loop that draws the top part of the diamond, but since I always starts at 0, it doesn’t draw the “top” row — that was done by LINE 1, and then when it draws the reverse/bottom of the diamond it will finish that pattern, which is the last row and start of the next diamond. (Confused yet?)

Line 4 – Draw the line for the top part of the diamond (inside the I loop of 1 to 3).

Line 5 – This is the NEXT for the top-to-bottom I loop.

Line 6 – This is a second I loop, that goes from 2 to 0.

Line 7 – Calling the GOSUB routine, so now it will be drawing like this:

I=2, J=4, 10, 16 (producing 4+2, 10+2 and 16+2 ... 6, 12 and 18)
".....*.....*.....*.."

I=1, J=4, 10, 16 (producing 4+1, 10+1 and 16+1 ... 5, 11 and 17)
"....*.....*.....*..."

I=0, J=4, 10, 16 (producing 4+0, 10+0 and 16+0 ... 4, 10 and 16)
"...*.....*.....*...."

And the second part of the GOSUB doing the right side of the pyramid shape:

I=2, J=4, 10, 16 (producing 4-2, 10-2 and 16-2 ... 2, 8 and 14)
".*.....*.....*......"

I=1, J=4, 10, 16 (producing 4-1, 10-1 and 16-1 ... 3, 9 and 15)
"..*.....*.....*....."

I=0, J=4, 10, 16 (producing 4-0, 10-0 and 16-0 ... 4, 10 and 16)
"...*.....*.....*...."

Together, the strings end up as:

".*...*.*...*.*...*.."
"..*.*...*.*...*.*..."
"...*.....*.....*...."

And that draws the bottom of the diamond!

Line 8 – The NEXT for the I (bottom of diamond) loop.

Line 9 – The NEXT for the “do it three times” loop.

Line 10 – END, so it won’t try to run the subroutine again.

Wow, that’s cool.

What do you think?

More to come…?

Tackling the Logiker 2023 Vintage Computing Christmas Challenge – part 3

See also: part 1, part 2, part 3 and part 4.

I have to admit, this year’s Logiker challenge stumped me. I had a few “clever” ideas on how to reproduce the pattern, but the code was larger than just printing out the pattern from arrays of strings.

Meanwhile, Jason Pittman kept posting revisions of his concept in the comments. In a response to part 2, he shared this:

This is the last attempt I came up with. It uses the same “-3 to 2 … ABS()” from the other example I sent on part 1. I think one potential trick here is to treat it as if you are holding a rubber stamp that stamps out three asterisks that are six spaces apart. You want to make this pattern by stamping it twice on each line. You’re keeping track of a starting position on each row (1027 + 32 for each row) and an offset to add and subtract to the starting position for each row. On the first line, the offset is zero, so you stamp it twice on top of itself at the starting position. On the next line, the offset is 1, so you stamp it twice, but one time you add -1 and the other time you add +1 to the starting position. Does this make any sense?

1 CLS:P=1027:FORC=1TO3:FORX=-3TO2:FORS=0TO12STEP6:POKEP-3+ABS(X)+S,106:POKEP+3-ABS(X)+S,106:NEXT:P=P+32:NEXT:NEXT:GOTO1

– Jason Pittman

This was an optimization of his original approach, but it had one limitation that might prevent it from solving the challenge: The CoCo’s 32×16 screen is too small to display the entire image, and POKEing characters on the screen would be limited to just 16 lines of text. He was aware of this, and his program does POKE the full image, but it is POKEing past the end of the visible screen. Would this count? RUNning the program displays the pattern over and over again (which was done to avoid having a new line with an endless loop GOTO):

POKEing past the visible screen works here because I am emulating a Disk Extended BASIC CoCo, and the memory after the text screen is reserved for four pages of PMODE high resolution graphics. But, I suspect, if I ran this on a cassette based CoCo, it might be POKEing in to memory used for the BASIC program itself.

Perhaps the CoCo 3 could help, since it has 40×25 and 80×25 text modes? Jason tried that:

I may play around with LPOKE. This should get it on the 40 column screen. I bet there is a crafty way to (a) not do the last line manually outside of the loops (b) remove one of the FOR loops (c) Shoot, there’s probably some crafty wizard way to do it in one FOR loop with logical operators, but I wouldn’t ever find it.

1 WIDTH40:FORZ=0TO12STEP6:FORX=-3TO2:FORS=0TO12STEP6:LOCATEABS(X)+S,Z+X+4:PRINT”*”;:LOCATE6-ABS(X)+S,Z+X+4:PRINT”*”;:NEXT:NEXT:NEXT:FORX=3TO15STEP6:LOCATE X,19:PRINT”*”;:NEXT

– Jason Pittman

In this version, Jason uses LOCATE(x,y) to position the cursor. That is what the CoCo 3 used instead of PRINT@ for text positioning. And it works!

It also feels better to use built-in BASIC text commands versus POKEing in to memory.

But he wasn’t done! He added this version:

10 WIDTH 40
20 FOR S = -15 TO 33 STEP 6
30 FOR X = 0 TO 18
40 IF S+X < 19 AND S+X >= 0 THEN LOCATE S+X,X:PRINT "*";
41 IF S-X >= 0 AND S-X < 19 THEN LOCATE S-X,X:PRINT "*";
50 NEXT X
60 NEXT S
70 GOTO 70

This one draws the same pattern, but in a very different way. It draws diagonal lines going down from the points at the top. Try it! It’s cool!

And, then this odd one, which creates the pattern by drawing the asterisks RANDOMLY, eventually getting them all on the screen.

0 WIDTH40
1 X=RND(3)*6:Y=RND(3)*6:RX=RND(7)-4:RY=ABS(RX)-3:D=RND(2)*2-3:LOCATE X+RX,Y+RY*D:PRINT"*";:GOTO 1

Nice job, Jason!

But it is going to make my brain hurt to understand how this works…

Meanwhile, I received a message from Rick Adams on Facebook with an implementation he was working on for a (much more limited) PDP-8 BASIC.

To be continued…

Tackling the Logiker 2023 Vintage Computing Christmas Challenge – part 2

See also: part 1, part 2, part 3 and part 4.

As I write this, I have no idea how to make this work. Producing this pattern:

   *     *     *
* * * * * *
* * * * * *
* * * *
* * * * * *
* * * * * *
* * *
* * * * * *
* * * * * *
* * * *
* * * * * *
* * * * * *
* * *
* * * * * *
* * * * * *
* * * *
* * * * * *
* * * * * *
* * *

…seems like it should be simple. Three asterisks, then six, then six, then four, then six, then six, then three… Spaces that go five, three, one, zero and then back up. Unhelpful.

But, it’s also just one pattern repeated across the screen three times…

   *
* *
* *
* *

And then it’s reversed, so I think if we can do the above, we can do the whole pattern. We see spaces of three, two, one, zero on the left, and zero, one, three, and five in the inside.

Color BASIC does not have a SPC() option (my VIC-20 did, I think) for outputting spaces, but TAB() will go to a specific column. Maybe we can figure out which column the asterisks should be in:

.........1111111111
1234567890123456789
* * *
* * * * * *
* * * * * *
* * * *

This gives us 4, 10 and 16. Then 3 and 5, 9 and 11, and 15 and 17. Then 2 and 6, 8 and 12, and 14 and 18. Finally, 1 and 7 and 13 and 19. I don’t know why, but I kind of like thinking about it as tab positions.

10 FOR SP=3 TO 0 STEP-1
20 PRINT TAB(SP);"*";TAB(6-SP);
30 IF SP<3 THEN PRINT "*";
40 PRINT
50 NEXT

That would give us one of the pyramid shapes. To complete the bottom, we’d do another FOR/NEXT loop. At least, that’s what I would do. BUT, in a comment to part 1, Jason Pittman had a smarter idea:

Awesome! I’ve got an idea on this one but I’m not going to jump ahead this year and I’m just going to follow along.One thought here is that you could combine the two print loops on 100 and 110 by coming up with a series that goes “0 1 2 3 2 1”. I did it by replacing 100 and 110 with this: “100 FOR A=-3 TO 2:PRINT A$(ABS(ABS(A)-3)):NEXT”

Or, you could shorten that a little if you reverse the direction of the array (so that it looks like “VVV”) and use “100 FOR A=-3 TO 2:PRINT A$(ABS(A)):NEXT” – Jason Pittman

I could print one diamond like this:

10 FOR A=-3 TO 3:SP=ABS(A)
20 PRINT TAB(SP);"*";TAB(6-SP);
30 IF SP<3 THEN PRINT "*";
40 PRINT
50 NEXT

That prints almost the entire diamond, except for the final asterisk. Because, if I wanted to print three of them, I’d do this in a loop, then print the final asterisk row at the end.

Unfortunately, as I start going down this rabbit hole, I find the code of loops and such ends up looking larger than some much simpler approaches, like one shown to my by Rick Adams. His code was written for a PDP-8 BASIC, which lacks things like ELSE and MID$. His technique was to have strings representing parts of the pyramid:

"   *  "
" * * "
" * *"
"* "

…then to print each string three times across the screen. This produced:

"   *     *     *  "
" * *  * *  * * "
" * * * * * *"
"* * * "

…and then do it backwards. There is a missing “*” on the right, that gets printed with an “IF”. In a chat, we bounced around some ideas to shrink the code, but looking at his approach, it seems everything I try to do gets larger:

  • Try “run length encode” where DATA statements represent the spaces. Print that many spaces, then an asterisk, and repeat.
  • Try DATA statements showing the positions of the asterisks. DATA is large than a string.
  • Try simulating a “SET(x,y)” to draw it, but using PRINT@ on the CoCo. Alas, the CoCo 32×16 screen is too small to fit the whole pattern, so even if this was smaller, it would still require extra code at the end to scroll the screen and print the final few lines (as the top portion scrolls off). BUT, using a CoCo 3 40/80 column screen would work using LOCATE x,y instead. But still, larger.

Is there an elegant solution to this challenge that doesn’t involve just PRINTing strings?

We shall continue… Next time…

Rest in peace, Steve Bjork

Glenside found out the details

In Memoriam: Steve Bjork, Pioneer and trailblazer of Personal Computing and the Tandy Color Computer

Legendary Color Computer game programmer, Steve Bjork, is no longer with us.

I have much to say about Steve. I considered him good friend, and alwauys enjoyed meeting up with him when I would visit Southern California. I had not been in touch since the Covid-era, and I wish I had. This news comes as quite a shock.

I hope to share some fun stories about hanging out with Steve. Though many know him from his video games on TRS-80, the Color Computer, Gameboy, Genesis, Super Nintendo, and I think even something on Playstation, I knew him as my Disneyland/theme park friend.

As a local, Steve grew up around Disneyland, and even worked there in the 1970s as a Jungle Cruise skipper.

I spent many days hanging out with him at Disneyland, and early on he would tell me, “there’s more to see in California than Disneyland.” He would become my tour guide during these trips — taking me to Magic Mountain (where he worked in the 1970s), Knott’s Berry Farms, and places like Fry’s Electronics and In and Out Burger. Many of the things I love about visiting California are directly traced back to Steve suggestions.

My finances prevented me for visiting for many years, but I did meet up with him again in 2017. Then he introduced us to Rock and Brews – a restaurant connected to KISS members Gene Simmons and Paul Stanley.

http://misc.disneyfans.com/OtherPlaces/California/RockAndBrews2017/index.html

There was always a new experience I would never have discovered on my own as an ignorant tourist ;-)

There are fun trivia things about Steve… He was an extra in The Goonies (if I have the movie correct), as a reporter. He was an extra in the movie Rollercoaster, I believe loading folks on a coaster. He has an Internet Movie Database entry through his work on One On One (basketball) and The Mask (Super Nintendo). He was a magician at some point — a tidbit that came up during an early theme park visit, complete with him presenting a “Hot Rod” trick he had with him (a trick I knew well from my younger days as an amateur magician).

There are many tales about his time in the software industry – such as a story about folks working on the TRON movie visiting computer places to ask questions. I think he worked for Datasoft at the time, which was “just down the road” from the Disney Studios.

If you look at the startup screen for The Rocketeer on Super Nintendo, I believe I heard Steve was the one wearing the suit for the photo (1992). Apparently props and such were loaned out so they could be used for reference for the game.

Steve also had a haunted house effects company — creating custom hardware and software to do things like make animated skulls talk to audio. An article about one of his products (“Wee Little Talker”) was featured in Nuts and Volts magazine (September 2017). You can see a photo of a circuit board with “Copyright 2017 Steve Bjork” on it. The article was co-written by his haunt partner, Steve Koci, who also passed away in recent years. Their business, Haunt Hackers, last updated in 2021 with a “closed due to Covid” message on the now-defunct website.

And people knew him. Once I was at Disneyland with him for some special opening event. A Disneyland manager type walked up to him and said “Steve Bjork, you’re my hero!” This person had known him through computer clubs back in the 80s. And that wasn’t the only time I saw something like that happen.

So while he popped up in CoCo discussions frequently (and got re-engaged in later years via the CoCoTalk YouTube show), it was also no surprise to see him pop up in an electronics publication or a haunted house message forum. I even remember reading a Disneyland fan site report about some event out there, and Steve Bjork was there presenting some kind of plaque to someone involved with a restaurant.

He did so much, and was still designing and programming new things right up to his final years.

He will be greatly missed.

I am sad.