Category Archives: Color BASIC

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…

Tackling the Logiker 2023 Vintage Computing Christmas Challenge – part 1

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

Special thanks to Jason Pittman for mentioning this year’s challenge in a comment…

Logiker is at it again, with a 2023 retro-programming Christmas challenge:

???? Vintage Computing Christmas Challenge (VC³) 2023 ???? – Logiker

This year, the pattern looks like this:

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

This image is 19×19, so while it will fit on a Radio Shack Color Computer 1/2 screen width-wise, it’s a bit too tall to fit height-wise. The challenge allows for it to scroll off the screen, which is something we had to do for past challenges.

Logiker 2023 Challenge pattern.

I can think of a number of ways to approach this.

The pattern is made up of only four unique lines, so you could print them A B C D B C A B C D and so on. There’s probably a simple way to do that with a FOR/NEXT loop and an array of those four lines.

10 CLS
50 A$(0)=" * * *
60 A$(1)=" * * * * * *
70 A$(2)=" * * * * * *
80 A$(3)="* * * *
90 FOR I=1 TO 3
100 FOR A=0 TO 3:PRINT A$(A):NEXT
110 FOR A=2 TO 1 STEP-1:PRINT A$(A):NEXT
120 NEXT
130 PRINT A$(0)
333 GOTO 333

If we had a larger screen (like the 40 or 80 column screens on the Color Computer 3), we could use LOCATE x,y to plot the pattern using some line drawing type math.

We could try the RLE (run length encoding) compression from past years to see if we could compress it down to spaces and characters.

We could try using math to figure out a pattern.

These all seem fun.

I hope to find some time to experiment. I don’t plan to “enter,” since one of the asks for the challenge is to not share your work until after the challenge ends.

More to come…

Color BASIC overflow bug – same as Commodore’s?

I just saw a tweet from Robin @ 8-Bit Show And Tell concerning a bug in Commodore BASIC that existed in the PET, C64 and VIC-20.

VAL() takes a string and converts it in to a floating point numerical variable. The value of “1E39” is a number in scientific notation, and this appears to cause a problem.

In Microsoft BASIC, the notation “1E39” represents the number 1 multiplied by 10 raised to the power of 39. This is also known as scientific notation, where the “E” indicates the exponent to which the base (10 in this case) is raised. So, “1E39” is equal to 1 * 10^39, which is an extremely large number:

1E39 = 1 * 10^39 = 1000000000000000000000000000000000000000

This number has 39 zeros after the 1, making it a very large value.

– ChatGPT

I was curious to see what the CoCo’s Color BASIC did, so I tried it…

It appears that port of BASIC to the 6809 also ported over this bug. Anyone want to take a look at the source and see what the issue is?

…to be continued, maybe…

10 PRINT CHR$(205.5+RND(1));GOTO 10 simplified by Jim Gerrie

There is a classic Commodore one-liner program that prints a maze. I have discussed it on this site several times in the past:

10 PRINT CHR$(205.5+RND(1));GOTO 10

I converted it to use CoCo ASCII “/” and “\” characters:

10 PRINT CHR$(47+(RND(2)-1)*45); : GOTO 10

I have since noticed that the 10 PRINT book even has a version for the CoCo in it:

https://10print.org/10_PRINT_121114.pdf page 55

Recently, Jim Gerrie posted a video of a new one-liner MC-10 version of the maze that truly fit on one 32-column line of the MC-10:

https://www.youtube.com/watch?v=7FQ_ht5u2y4

His approach of using MID$() looked much simpler than the CHR$ version of the Commodore original. I am pretty sure my VIC-20 had MID$ so surely the C64 had it as well. Why wasn’t it used? Perhaps this was an example of code obfuscation. “Type in this mysterious code and see what you get!”

Indeed, in a quick test, using MID$() is smaller and faster:

0 PRINTMID$("/\",RND(2));:GOTO

0 PRINTCHR$(47+(RND(2)-1)*45);:GOTO

Now, doing the MC-10 version with CHR$() would be longer on the CoCo since we can’t type those characters like the MC-10 allows. We’d have to use CHR$(140)+CHR$(138) embedded in the MID$ to make that work (and be much slower since it would parse those numbers for every byte of the maze it prints). But…

0 PRINTMID$(CHR$(140)+CHR$(138),RND(2));:GOTO

…does work.

To make it faster, we’d need two lines and a string:

0 A$=CHR$(140)+CHR$(138)
1 PRINTMID$(A$,RND(2));:GOTO1

Even with that, the GOTO is slower than the MC-10 version since it has to parse a number, and then has to skip a line each time. You could get around that by doing it like this, and starting it with “RUN 1”:

0 PRINTMID$(A$,RND(2));:GOTO
1 A$=CHR$(140)+CHR$(138):GOTO

How would you do it? Let me know in the comments.

Until next time…

DATA problems revisited

Awhile back, I posted a note about some weird behavior with DATA statements in Color BASIC. The issue was that you really couldn’t have anything else on a line after the DATA keyword other than data.

I recently mentioned this to Alex Evans so he could make sure his BASIC utilities were not combining DATA statements together. He mentioned something to me that demonstrated this issue even more:

100 DATA 1,2,3:PRINT "FOO"
110 DATA -1

If you RUN that program, it should print “FOO” and do nothing else, proving that the interpreter does scan the entire line looking for things after a DATA statement.

BUT, if you try to USE that DATA, it will continue reading after the 3 and error out, since the data it finds is not an ASCII number:

Above, the “?SN ERROR” is caused by trying to read a numeric variable from what is NOT a string (the token value for PRINT followed by FOO in quotes). Altering the program to use A$ instead of A (and print one item read per line) shows this a bit better:

Alex explained it like this:

Basically, for the purposes of READ everything after the DATA keyword is part of the data statements, but the interpreter executes the line properly.

– Alex Evans

You can see it appears to treat the colon as a DATA separator, since it does not appear as part of the read DATA.

This is a quirk I do not expect many of us ever encountered, since most of us probable grouped all our DATA together, without mixing commands in with them:

100 DATA 1,2,3,-1

I bet all of this was discovered and covered back in the 1980s in various CoCo magazines and newsletters, but the behavior surprised me when I stumbled upon it so I guess I never saw it.

Until next time…

Inverse/reverse text on the CoCo

The later model CoCo 2s used an upgrade MC6847 video display generator chip. This chip provided an updated font with true lowercase, as well as the ability to change the border color and inverse the video.

The original 6847 had a hardware pin that could inverse video, and I recall taking the VDG out of my CoCo 1 to pull up a pin then re-insert it. That gave me the nice inverted display I often use in the XRoar emulator:

In the nearly-forty years since I did that modification I had forgotten that it could only be done in hardware. I thought there was probably a POKE or something, since many programs I had used the inverted video mode.

I couldn’t figure it out, and the bits that made the updated 6847T1 show inverse video did not work on the original 6847.

So I asked on the CoCo mailing list

I wrote a program to go through the 6847 T1 VDG text modes, which includes lowercase, changing the border, and inverting the screen.

I’ve seen programs on the CoCo (like GRABBER) that invert/reverse the screen in software, but the bits to do that on the T1 don’t seem to do anything on the 6847.

Does anyone know of a reference that would show me how to do this? Thanks, much!

– Me, 2/15/2023

L. Curtis Boyle replied:

On the regular VDG, there is no reverse video bit. So you have to use the inverted characters in the character set (ie POKE 1024+loc,0-63).

L. Curtis Boyle, 2/15/2023

It was Tim Lindner that reminded me of the hardware pin:

There is an inverse video pin on the chip. But it is not hooked up. I think.

– tim lindner, 2/15/2023

With that in mind, I decided to do some quick tests. This test PRINTs every possible visible character on the screen:

10 CLS:PRINT
20 FOR C=32 TO 255:PRINT CHR$(C);:NEXT
30 GOTO 30

I skipped the first line and I started printing with CHR$(32) (a space) because the values below that are non-visible characters.

BUT, if you POKE to the screen, you get more characters. Here is an update that will POKE the values 0-255 to the bottom half of the screen:

10 CLS:PRINT
20 FOR C=32 TO 255:PRINT CHR$(C);:NEXT
30 FOR C=0 TO 255:POKE 1280+C,C:NEXT
40 GOTO 40

Comparing the PRINT versus POKE characters, you can see PRINT has 64 ASCII-printable characters starting at 32-96. After that are 32 inverted characters. There are 32 characters that you cannot print in inverse.

When you POKE, values 0-63 represent the inverted full ASCII character set, and 64-127 are the uppercase.

 PRINT   POKE
------- -------
32-96   64-127  Space to Left Arrow
96-127  0-31    Inverse "@" to Inverse Left Arrow
        32-63   Inverse Space to Inverse "Left Arrow"?"

From that table, you can see it’s impossible to PRINT thirty two of the available inverse characters in the 6847, but you can POKE them.

I thought it might be fun to write a routine that would PEEK through the screen memory and invert all the characters. Since there are 32 non-inverted characters from 32-96, and 32 inverted characters from 96-127, it seems all I needed to do is PEEK each location and if it was from 96-127, subtract 64 and POKE it back.

We start with this slow program, complete with some benchmarking timing code:

10 ' FILL SCREEN WITH STUFF
20 CLS:FOR C=32 TO 255:PRINT CHR$(C);:NEXT
30 TIMER=0
40 FOR L=1024 TO 1536
50 V=PEEK(L):IF V>63 AND V<128 THEN POKE L,V-64
60 NEXT
70 PRINT TIMER

This prints 631 for me. One of the first speedups we can do is change the decimal numbers in line 50 to hex.

10 ' FILL SCREEN WITH STUFF
20 CLS:FOR C=32 TO 255:PRINT CHR$(C);:NEXT
30 TIMER=0
40 FOR L=1024 TO 1536
50 V=PEEK(L):IF V>&H3F AND V<&H80 THEN POKE L,V-&H40
60 NEXT
70 PRINT TIMER

That simple change takes the time down to 490. And, since variable lookups (when there aren’t a ton of variables) is faster than parsing a hex number in source code, we can change those values to variables and make it even faster:

10 ' FILL SCREEN WITH STUFF
20 CLS:FOR C=32 TO 255:PRINT CHR$(C);:NEXT
25 X=&H3F:Y=&H80:Z=&H40
30 TIMER=0
40 FOR L=1024 TO 1536
50 V=PEEK(L):IF V>X AND V<Y THEN POKE L,V-Z
60 NEXT
70 PRINT TIMER

This drops to 475.

And, any time you use “IF this AND that”, it can be sped up by doing “IF this THEN IF that”. Let’s try that…

10 ' FILL SCREEN WITH STUFF
20 CLS:FOR C=32 TO 255:PRINT CHR$(C);:NEXT
25 X=&H3F:Y=&H80:Z=&H40
30 TIMER=0
40 FOR L=1024 TO 1536
50 V=PEEK(L):IF V>X THEN IF V<Y THEN POKE L,V-Z
60 NEXT
70 PRINT TIMER

In this case, it really did not change anything — I see 474. Okay, that was a fail. I guess it doesn’t always help.

Since the math we are doing is a subtract 64, we should see if we can do that by using AND to remove the bit that represents 64. We don’t want to do that if it is a graphics character (128-255) so we’ll need to retain on IF:

10 ' FILL SCREEN WITH STUFF
20 CLS:FOR C=32 TO 255:PRINT CHR$(C);:NEXT
25 X=&H3F:Y=&H80:Z=&H40
30 TIMER=0
40 FOR L=1024 TO 1536

50 V=PEEK(L):IF V<Y THEN POKE L,V AND &H3F
60 NEXT
70 PRINT TIMER

That shows 367 for me. Better!

I did test to see if checking for the 8th bit (V AND 128) was faster than just comparing a value (A > 127) and the “>” check was faster, so we’ll stick with that.

At this point, the routine to inverse the screen while leaving graphics characters alone is getting close to twice as fast as the original version.

Can we do better? At least a bit, by removing spaces and combining lines (and even though it only parses the FOR values once, I’ll switch them to hex):

10 ' FILL SCREEN WITH STUFF
20 CLS:FOR C=32 TO 255:PRINT CHR$(C);:NEXT
25 X=&H3F:Y=&H80
30 TIMER=0
40 FORL=&H400 TO&H5FF:V=PEEK(L):IFV<Y THENPOKEL,V AND&H3F
50 NEXT
70 PRINT TIMER

That prints 356. (There were a few spaces that could not be removed due to needing them so the BASIC parser knows where a variable ends and a keyword begins.)

Now we could make a subroutine that would invert the screen, slowly.

1000 ' INVERT SCREEN
1010 X=&H3F:Y=&H80:FORL=&H400 TO&H5FF:V=PEEK(L):IFV<Y THENPOKEL,V AND&H3F
1020 NEXT:RETURN

While it might be a neat “effect” for a title screen to watch it painting the screen, it’s not fast enough to use in a program that you want the output to always be inverted.

Some thoughts…

If no semigraphics characters were being used, the check for them could be removed:

10 ' FILL SCREEN WITH STUFF
20 CLS:FOR C=32 TO 127:PRINT CHR$(C);:NEXT
25 X=&H3F:Y=&H80
30 TIMER=0
40 FORL=&H400 TO&H5FF:V=PEEK(L):POKEL,V AND&H3F
50 NEXT
70 PRINT TIMER

That prints 304 — twice as fast as the original, though it will corrupt semigraphics blocks by changing them.

Another approach might be to only do the portion of the screen that has been PRINTed to by changing the range that the L loop scans. For example, you could call a routine to invert just the last line of the screen every time something is PRINTed there.

Or, perhaps a custom “print reverse” routine might make more sense. We’d also want a special CLS routine that cleared the screen to inverse spaces.

10 CLS
20 GOSUB 1100
30 P=0:A$="This is in REVERSE video!":GOSUB 1000
40 P=32:A$="And this one is, too.":GOSUB 1000
50 P=480:A$="Hello, bottom line!":GOSUB 1000

999 GOTO 999

1000 ' PRINT@ REVERSE
1010 L=&H3FF+P:FOR I=1 TO LEN(A$)
1020 V=ASC(MID$(A$,I,1)):IF V>95 THEN V=V-96 ELSE IF V>64 THEN V=V-64
1030 POKE L+I,V:NEXT:RETURN

1100 ' CLS REVERSE
1110 P=0:FOR L=&H400 TO &H5FF:POKE L,&H20:NEXT:RETURN

To use the function set P to the PRINT@ location, assign the string to A$, then GOSUB 1000. Not much to it.

Handling scrolling the screen would be more work (and slower), but could also be done. Perhaps the routine could track the PRINT@ position internally, so every time you print it increases P by 32 (next line) and if it gets to the end of the screen, it could scroll everything up… slowly…

10 CLS
20 GOSUB 1200
30 A$="This is in REVERSE video!":GOSUB 1000
40 A$="And this one is, too.":GOSUB 1000
50 A$="PRINT;":GOSUB 1100
60 A$="NEXT TEXT":GOSUB 1100
70 P=480:A$="BOTTOM LINE":GOSUB 1000
80 A$="AND THIS":GOSUB 1000

999 GOTO 999

1000 ' PRINT@ REVERSE
1010 GOSUB 1100:P=P+32-I+1:IF P<512 THEN 1050
1020 ' SCROLL
1030 FOR L=&H400 TO &H5E0:POKE L,PEEK(L+&H20):NEXT
1040 FOR L=&H5E0 TO &H5FF:POKE L,&H20:NEXT:P=P-32
1050 RETURN

1100 ' PRINT@; REVERSE
1110 L=&H3FF+P:FOR I=1 TO LEN(A$)
1120 V=ASC(MID$(A$,I,1)):IF V>95 THEN V=V-96 ELSE IF V>64 THEN V=V-64
1130 POKE L+I,V:NEXT:P=P+I-1
1140 RETURN

1200 ' CLS REVERSE
1210 P=0:FOR L=&H400 TO &H5FF:POKE L,&H20:NEXT:RETURN

To clear the screen, GOSUB 1200.

To do a normal PRINT, set the string in A$ and GOSUB 1000.

To do a PRINT with a semicolon at the end, set the string in A$ and GOSUB 1100.

To simulate a PRINT@, set P to the screen position then GOSUB 1000 or 1100.

This program does NOT handle printing past position 512 at the bottom right of the screen.

IT CAN POKE PAST SCREEN MEMORY AND CAUSE A CRASH!

A simple check could be done before line 1130 to see if L+I is going to be past the end of screen memory (1536) and adjust accordingly.

If there is any interest (leave a comment), we can look at how we might do that in a future article.

Is this useful?

If we really wanted to use inverse video in BASIC, I guess it would be useful. Can you make it faster in BASIC? What would you suggest? Leave a comment.

But, doing inverse text would be a simple thing for some assembly language routines that could be called easily from BASIC.

OR, perhaps there would be a simple way to just hook in to the output vector used by Color BASIC and do it there.

To be continue… Maybe.