Author Archives: Allen Huffman

About Allen Huffman

Co-founder of Sub-Etha Software.

There is no substitute for real hardware.

I make use of emulation regularly. While I do still have my original Radio Shack Color Computers from the 1980s, it is usually more convenient to just load up an emulator. For short experiments in BASIC, or casual game playing, this works just fine. However, emulation is not perfect and sometimes you run in to something that stands out.

Consider this…

…and this…

These purple colors were generated on the XRoar 1.0 emulator when emulating a later-model Color Computer 2 with an updated T1 version of the 6874 VGD video chip.

I had never seen such colors, but since I had never had a T1 CoCo I assumed it was just another odd secret that chip held I was just unfamiliar with.

The first sign that this might be an emulation issue was that the purple only showed up when the emulator was using simulated RF output. In that mode, it tries to replicate the fuzzy appearance of TV output. turn that simulation off and the screen changed to expected orangish reddish alternate color.

While I do not know the details on what caused this, XRoar has been updated to no longer produce these purple colors in this mode.

You can pick up the latest XRoar emulator (that adds support for the Color Computer 3 and MC-10) here:

http://www.6809.org.uk/xroar/

String space required. No exceptions.

Revisiting my Color BASIC String Theory series… Here is one to ponder…

CLEAR 0 can be used to reserve 0 bytes for string storage. With no string space, we obviously expect something like this to NOT work:

A$="THIS WON'T WORK WITH CLEAR 0"

Indeed, that would give us “?OS ERROR” — Out of string space.

The exception to this rule are “constant strings” that are embedded inside the BASIC program itself. In a YouTube video I recently posted, I demonstrated how constant strings in a program do not use string space:

You can see twenty nine other short Color BASIC videos I posted to YouTube during the month of #SepTandy 2021.

But I digress…

Here are some other things that won’t work without string space, even if, at first glance, it seems like they would:

It seems you can’t SAVE, LOAD or even PRINT a “constant” string if there is no string space.

But why?

Consider this… The same thing happens with string functions such as LEFT$, RIGHT$, MID$ and even INSTR:

Looking at those, and recalling my String Theory article, we know LEFT$, RIGHT$ and MID$ are trying to create a new string. With no string space, there is no way to create it. That must be why they fail. This makes sense.

But INSTR does not create any strings. It merely returns the position where one string appears inside of another, or 0 if the string is not found. (Or 1 if you give it an empty string, which truly does seem like a bug but other flavors Microsoft BASIC behave the same way with their implementations of INSTR. But I digress…)

And SAVE, LOAD and PRINT are similar. They just print something, not create a string.

That does not make sense.

Let’s speculate a bit.

What’s all this, then?

Without consulting the Color BASIC Unravelled disassembly, my guess is that the ROM code for SAVE, LOAD, PRINT, etc. probably expects some register to be pointing to where the string exists in memory (a location pointer in the variable data). This might be code space, in the case of a constant string embedded in a BASIC line, or string space, in the case of a dynamic string.

But when you are entering a command directly in to BASIC, there is no program memory for that command (even though it seems like it could just point to the keyboard input buffer and then make the ROM call). BASIC needs to put that constant string data somewhere before jumping to the ROM call.

That’s weird, and quite possibly unnecessary, but would, at least, make sense.

I am going to add this to my “look this up in the disassembly to see what’s going on” list for future investigation.

In the meantime … I wonder what else won’t work without string space?

Until next time…

The undocumented syntax of Extended Color BASIC



The other night I was experimenting with Extended Color BASIC and the “GET” and “PUT” commands. I knew that the documentation was incorrect about them, and was trying to figure out how they worked. I also wanted to do this without cheating (i.e., doing a quick web search and finding the results of someone who already did this).

I will share the results of this experimentation later, but I wanted to pass along something I was unaware of until last night. I ended up looking at the Extended Color BASIC disassembly to try to find clarification on why something I was seeing was happening. Reading through it revealed a syntax I was unaware of for the following commands:

  • CIRCLE
  • GET
  • LINE
  • PAINT
  • PUT

Much like the CoCo BASIC “PRINT@” command, the ‘@’ sign is allowed on these commands as well. It does nothing, and is merely skipped:

Extended Color BASIC’s unused @ syntax.

A note in the Unraveled book says:

It is interesting to note that the “@” symbol does not do anything! It is there to make the command syntax consistent with the “PRINT @” concept and to make it compatible with other versions of Microsoft BASIC.

– Extended Color BASIC Unraveled II

Since this was not documented in the manual (as far as I know), I was not aware of this syntax.

Did you know about this?

There are certainly many undocumented secrets in the Color BASIC ROMs, from the unimplemented Easter egg to things like how the PLAY command plays nonexistent notes. Certainly the author of the Unraveled series discovered these items, and anyone who read those books would have known about it, but I wonder how widespread these oddities were back in the 1980s.

Until next time…

6809 assembly request: make this more better?

Updates:

  • 2021-9-17 – Added revisions suggested by William “Lost Wizard” Astle.

Hello, folks who actually know how to program 6809 assembly.

Can you make this more better?

The idea is to look at each byte on the 32-column VDG screen (from 1024 to 1535) and if the high bit is set (>128, thus a semigraphics block character), add 16 to it. That moves it to the next color. If it rolls over, adjust it back to the proper range.

i.e., if the block is 250, and it adds 16, you end up with 10. Setting the high bit again bumps it back to the 128-255 range. Initially, I did this using a compare to #128, then using ADDA of #128. Then I looked up the BIT test. Rather than compare to decide if I need to do something, I just do the OR to set that high bit back. Seems to work. Maybe faster than checking each byte?

Does this make sense?

Please and thank you.

William Astle Updates

William Astle immediately left these comments:

I’d use “BPL” instead of “BITA/BEQ”. (Bit 7 is the sign bit and LD and ST set flags.) Also, I’d do “LDA ,X+” and then if updating the screen, do “STA -1,X”. That removes the need for the “LEAX”.

William Astle

BPL is “Branch if PLus”. When a register value is used to represent values that can be negative (-127 to 128 versus 0 to 255), the high bit is used to indicate negative. I expect BPL is basically “branch if the high bit is not set.”

When I “LDA ,X”, I am loading register A with whatever is pointed to by X. Adding the plus “LDA ,X” will load A then increment X. Since STA allows an offset, so you can do things like “STA 5,X” to start the value in register A 5 locations past wherever X points. Doing a -1 stores it one byte earlier. That eliminates me needing to use “LEAX” to add one to X.

Here is the update:

References

References I used:

Color BASIC Attract Screen – part 4

See also: part 1, part 2, part 3, part 4, unrelated, part 5 and part 6.

Beyond removing some spaces and a REM statement, here is the smallest I have been able to get my “attract” program:

10 ' ATTRACT4.BAS
20 FOR I=0 TO 3:READ L(I),LD(I),CL(I),CD(I):NEXT:Z=143:CLS 0:PRINT @268,"ATTRACT!";
30 Z=Z+16:IF Z>255 THEN Z=143
40 FOR I=0 TO 3:POKE L(I),Z:L(I)=L(I)+LD(I):FOR C=0 TO 3:IF L(I)=CL(C) THEN LD(I)=CD(C)
50 NEXT:NEXT:GOTO 30
60 DATA 1024,1,1024,1,1047,1,1055,32,1535,-1,1535,-1,1512,-1,1504,-32

(We could reduce it by one line by sticking the DATA statement on the end of line 50, now that I look at it.)

Let’s rewind and look at the original, which used individual variables for each of the moving color blocks:

10 ' ATTRACT.BAS
20 A=1024:B=A+23:C=1535:D=C-23:Z=143
30 AD=1:BD=1:CD=-1:DD=-1
40 CLS 0:PRINT @268,"ATTRACT!";
50 POKE A,Z:POKE B,Z:POKE C,Z:POKE D,Z
60 Z=Z+16:IF Z>255 THEN Z=143
70 A=A+AD
80 IF A=1055 THEN AD=32
90 IF A=1535 THEN AD=-1
100 IF A=1504 THEN AD=-32
110 IF A=1024 THEN AD=1
120 ' 
130 B=B+BD
140 IF B=1055 THEN BD=32
150 IF B=1535 THEN BD=-1
160 IF B=1504 THEN BD=-32
170 IF B=1024 THEN BD=1
180 ' 
190 C=C+CD
200 IF C=1055 THEN CD=32
210 IF C=1535 THEN CD=-1
220 IF C=1504 THEN CD=-32
230 IF C=1024 THEN CD=1
240 ' 
250 D=D+DD
260 IF D=1055 THEN DD=32
270 IF D=1535 THEN DD=-1
280 IF D=1504 THEN DD=-32
290 IF D=1024 THEN DD=1
300 GOTO 50

This was then converted to us an array:

10 ' ATTRACT2.BAS
20 L(0)=1024:L(1)=1024+23:L(2)=1535:L(3)=1535-23
30 Z=143
40 CL(0)=1024:CD(0)=1
50 CL(1)=1055:CD(1)=32
60 CL(2)=1535:CD(2)=-1
70 CL(3)=1504:CD(3)=-32
80 CLS 0:PRINT @268,"ATTRACT!";
90 LD(0)=1:LD(1)=1:LD(2)=-1:LD(3)=-1
100 FOR I=0 TO 3:POKE L(I),Z:NEXT
110 Z=Z+16:IF Z>255 THEN Z=143
120 FOR I=0 TO 3:L(I)=L(I)+LD(I):NEXT
130 FOR L=0 TO 3
140 FOR C=0 TO 3
150 IF L(L)=CL(C) THEN LD(L)=CD(C)
160 NEXT
170 NEXT
180 GOTO 100

And then it was converted to use READ/DATA instead of hard-coding values:

10 ' ATTRACT3.BAS
20 FOR I=0 TO 3
30 READ L(I),LD(I),CL(I),CD(I)
40 NEXT
50 Z=143
60 CLS 0:PRINT @268,"ATTRACT!";
70 Z=Z+16:IF Z>255 THEN Z=143
80 FOR I=0 TO 3
90 POKE L(I),Z
100 L(I)=L(I)+LD(I)
110 FOR C=0 TO 3
120 IF L(I)=CL(C) THEN LD(I)=CD(C)
130 NEXT
140 NEXT
150 GOTO 70
160 ' L,LD,CL,CD
170 DATA 1024,1,1024,1
180 DATA 1047,1,1055,32
190 DATA 1535,-1,1535,-1
200 DATA 1512,-1,1504,-32

Shuffling code around is fun.

But it’s still really slow.

10 PRINT “FASTER”

There are other ways to do similar effects, such as with strings. We could make a string that contained a repeating series of the color block characters, like this:

FOR I=0 TO 7:B$=B$+CHR$(143+16*I):NEXT

Then we could duplicate that 8-character string a few times until we had a string that was twice the length of the 32 column screen:

B$=B$+B$+B$+B$+B$+B$+B$+B$

Then we could make the entire thing move by printing the MID$ of it, like this:

FOR I=1 TO 32
PRINT@0,MID$(B$,33-I,32);
PRINT@480,MID$(B$,I,31);
NEXT

We print one section @0 for the top line, and the other @480 for the bottom line. Unfortunately, using PRINT instead of POKE means if we ever print on the bottom right location, the screen would scroll, so the bottom right block has to be left un-printed (thus, printing 31 characters for the bottom line instead of the full 32). This bothers me so apparently I do have O.C.D. Maybe we can fix that later.

But, it gives the advantage of scrolling ALL the blocks, and is super fast. Check it out:

10 ' ATTRACT5.BAS
20 CLS 0:PRINT @268,"ATTRACT!";
30 FOR I=0 TO 7:B$=B$+CHR$(143+16*I):NEXT
40 B$=B$+B$+B$+B$+B$+B$+B$+B$
50 FOR I=1 TO 32
60 PRINT@0,MID$(B$,33-I,32);
70 PRINT@480,MID$(B$,I,31);
80 NEXT:GOTO 50

That’s not bad, but only gives the top and bottom rows (minus that bottom right location). But, it’s fast!

ATTRACT5.BAS

Since the orders of the colors is the same on the top and bottom, we’d really need to reverse the bottom characters to make it look like it’s rotating versus just reversing. Let’s tweak that:

10 ' ATTRACT6.BAS
20 CLS 0:PRINT @268,"ATTRACT!";
30 FOR I=0 TO 7:B$=B$+CHR$(143+16*I)
35 R$=R$+CHR$(255-16*I):NEXT
40 B$=B$+B$+B$+B$+B$+B$+B$+B$
45 R$=R$+R$+R$+R$+R$+R$+R$+R$
50 FOR I=1 TO 32
60 PRINT@0,MID$(B$,33-I,32);
70 PRINT@480,MID$(R$,I,31);
80 NEXT:GOTO 50

That’s a bit better. But getting the sides to work is a bit more work and it will slow things down quite a bit. But let’s try anyway.

Initially, I tried scanning down the sides of the string using MID$, like this:

FOR J=1 TO 14
PRINT@480-32*J,MID$(R$,39-J+I,1);
PRINT@31+32*J,MID$(R$,33-J+I,1);
NEXT

But that was very, very slow. You could see it “paint” the sides. Each time you use MID$, a new string is created (with data copied from the first string). That’s a bunch of memory shuffling just for one character.

Then I thought, since I can’t get the speed up from a horizontal string being PRINTed, it was probably faster to just use CHR$().

I tried that, and it was still too slow.

Benchmark Digression: POKE vs PRINT

This led me back to an earlier benchmark discussion… Since I cannot get any benefit of using PRINT for a vertical column of characters, I could switch to the faster POKE method. This would also allow me to fill that bottom right character block. My O.C.D. approves.

To prove this to myself, again, I did two quick benchmarks — one using PRINT@ and the other using POKE.

0 ' LRBENCH1.BAS
1 ' 4745
10 C=143+16
20 TIMER=0:FOR A=1 TO 1000
30 FOR P=1024 TO 1535 STEP 32
40 POKEP,C
50 NEXT
60 NEXT:PRINT TIMER

0 ' LRBENCH2.BAS
1 ' 6013
10 C=143+16
20 TIMER=0:FOR A=1 TO 1000
30 FOR P=0 TO 511 STEP 32
40 PRINT@P,CHR$(C);
50 NEXT
60 NEXT:PRINT TIMER

Line 1 has the time that it printed for me in the Xroar emulator.

POKE will be the way.

However, there is still a problem: Math.

It just doesn’t add up…

The CoCo screen is 32×16. There are 8 colors. That means those 8 colors can repeat four times along the top of the screen, and four times along the bottom, leaving only 14 on each side going vertical. 32+32+14+14 is 92, which is not evenly divisible by our 8 colors. If we represent them as numbers, they would look like this:

If you start at the top left corner and go across, repeating 12345678 over and over, you end up back at the top left on 4. We have three colors that won’t fit. This means even if I had a nice fast routine for rotating the colors, they would not be evenly balanced using this format.

However…

…if I leave out the four corners, we get 88, and that divides just fine by our 8 colors!

Thus, the actual O.C.D.-compliant border I want to go for would look like this:

The only problem is … how can this be done fast in BASIC?

To be continued…

Bonus: Show Your Work

Here are the stupid BASIC programs I wrote to make the previous four screens:

0 ' border1.bas
10 CLS:C=113:L=1024
20 ' RIGHT
30 L=1024:D=1:T=31:GOSUB 110
40 ' DOWN
50 L=1087:D=32:T=13:GOSUB 110
60 ' LEFT
70 L=1535:D=-1:T=31:GOSUB 110
80 ' UP
90 L=1472:D=-32:T=13:GOSUB 110
100 GOTO 100
110 ' L=LOC, D=DELTA, T=TIMES
120 POKE L,C
130 C=C+1:IF C>120 THEN C=113
140 IF T=0 THEN RETURN
150 L=L+D:IF L>1023 THEN IF L<1536 THEN 170
160 L=L-D:SOUND 200,1
170 T=T-1:GOTO 120
0 ' border2.bas
10 CLS:C=113:L=1024
20 ' RIGHT
30 L=1025:D=1:T=29:GOSUB 110
40 ' DOWN
50 L=1087:D=32:T=13:GOSUB 110
60 ' LEFT
70 L=1534:D=-1:T=29:GOSUB 110
80 ' UP
90 L=1472:D=-32:T=13:GOSUB 110
100 GOTO 100
110 ' L=LOC, D=DELTA, T=TIMES
120 POKE L,C
130 C=C+1:IF C>120 THEN C=113
140 IF T=0 THEN RETURN
150 L=L+D:IF L>1023 THEN IF L<1536 THEN 170
160 L=L-D:SOUND 200,1
170 T=T-1:GOTO 120
0 ' border3.bas
10 CLS 0:C=143:L=1024
20 ' RIGHT
30 L=1025:D=1:T=29:GOSUB 110
40 ' DOWN
50 L=1087:D=32:T=13:GOSUB 110
60 ' LEFT
70 L=1534:D=-1:T=29:GOSUB 110
80 ' UP
90 L=1472:D=-32:T=13:GOSUB 110
100 GOTO 100
110 ' L=LOC, D=DELTA, T=TIMES
120 POKE L,C
130 C=C+16:IF C>255 THEN C=143
140 IF T=0 THEN RETURN
150 L=L+D:IF L>1023 THEN IF L<1536 THEN 170
160 L=L-D:SOUND 200,1
170 T=T-1:GOTO 120

Color BASIC Attract Screen – part 3

See also: part 1, part 2, part 3, part 4, unrelated, part 5 and part 6.

Previously, I took a target from the attract screen code to talk more on using arrays in BASIC than individual variables. I gave an example of moving ghosts around a screen, and then did modifications to use an array. This let the user select how many ghost they wanted to see randomly display on the screen.

I mentioned that arrays were slower, but allowed flexibility. With that in mind, here is that “wandering ghosts” example turned in to a simple game demo. The player will appear as a yellow block in the top left corner of the screen. The ghosts will randomly appear around the screen as white blocks. The goal is to navigate to the bottom right corner of the screen without hitting a ghost, or being hit by one.

To hopefully make this compatible with the MC-10 computer, it uses the keyboard letters “WASD” — A for left, D for right, W for up and S for down.

You can choose one ghost, and get a single fast moving ghost to avoid. Or you can choose 100 ghosts, and get 100 slow moving ghosts to avoid, making it more like navigating a random maze that slowly moves walls.

Ghost Run in Color BASIC

10 ' ghostrun.bas
15 INPUT "NUMBER OF GHOSTS";G:G=G-1:IF G<0 THEN 15
20 CLS0:DIM G(G):DIM C(G):C=207:B=128:FOR I=0 TO G
30 G(I)=1023+RND(512)
41 NEXT
45 D(0)=1:D(1)=-1:D(2)=-32:D(3)=32
46 PL=1056:PC=159:POKE PL,PC
50 ' DISPLAY GHOSTS
60 FOR I=0 TO G:POKE G(I),C
65 D=INSTR(" DAWS",INKEY$):IF D>1 THEN GOSUB 190
70 ' RANDOM MOVE G(I)
80 NL=G(I)+D(RND(4)-1)
130 IF NL<1024 THEN 170
140 IF NL>1535 THEN 170
150 ' ERASE G(I) AND UPDATE LOCATION
160 POKE G(I),B:POKE NL,C:G(I)=NL
165 IF NL=PL THEN 480
170 NEXT:GOTO 60
180 ' PLAYER MOVED D
190 NL=PL+D(D-2)
200 IF NL<1024 THEN RETURN
210 IF NL>1535 THEN RETURN
220 IF NL=1535 THEN 510
230 IF PEEK(NL)=C THEN 530
240 POKE PL,B:POKE NL,PC:PL=NL
250 RETURN
470 GOTO 60
480 ' GHOST GOT PLAYER
490 PRINT "WE GOT YA!"
500 END
510 PRINT "YOU MADE IT!"
520 END
530 PRINT "YOU HIT A GHOST!"
540 END

To reduce instant death, I made the ghosts spawn no higher than one line below the player. But, with a small amount of faster ghosts, something could spawn then randomly move towards the player quickly. It’s surprisingly challenging (or frustrating).

To make the game responsive to the player, as the code it updating the position of the ghost, the player can move. For example, if drawing 100 ghosts, the player can move as each ghost being drawn. I found this much more fun than doing a turn-by-turn game like the old 1976 CHASE game (also known as Robots for Unix, Daleks for Mac, and a zillion spinoffs in the 70s and 80s). Check out the first published listing Creative Computing 1976 or the wikipedia entry for more details.

But I, as I say, digress.

Maybe we can revisit this in future installments of this series.

Until then, here is another size optimization of the attract screen code, this time removing all the hard-coded array initializations and turning them in to DATA statements loaded by the READ command:

10 ' ATTRACT3.BAS
20 FOR I=0 TO 3
30 READ L(I),LD(I),CL(I),CD(I)
40 NEXT
50 Z=143
60 CLS 0:PRINT @268,"ATTRACT!";
70 Z=Z+16:IF Z>255 THEN Z=143
80 FOR I=0 TO 3
90 POKE L(I),Z
100 L(I)=L(I)+LD(I)
110 FOR C=0 TO 3
120 IF L(I)=CL(C) THEN LD(I)=CD(C)
130 NEXT
140 NEXT
150 GOTO 70
160 ' L,LD,CL,CD
170 DATA 1024,1,1024,1
180 DATA 1047,1,1055,32
190 DATA 1535,-1,1535,-1
200 DATA 1512,-1,1504,-32

We have no changed the original 30 line version in to a 20 line version… but that is actually two lines longer than the previous one due to adding some extra lines for DATA and READ. But, if we were dealing with 50 objects instead of just 4, we’d likely be quite ahead at this point.

Next time, we’ll try to reduce this even further by packing lines together.

To be continued…

Color BASIC Attract Screen – part 2

See also: part 1, part 2, part 3, part 4, unrelated, part 5 and part 6.

In part 1, I showed a simple but slow way to recreate a classic CoCo game startup screen with color blocks moving around the screen. I recall many early CoCo games had startup screens similar to this, though fancier. The assembly language games would rotate all the blocks around the screen, rather than just moving four blocks like my BASIC demo does. For instance, Steve Bjork‘s port of Clowns and Balloons:

You can check it out in videos on YouTube, or play it in a web browser via the wonderful JS Mocha CoCo emulator.

My BASIC attract screen does not attempt to recreate that one, but is more of an homage to the style of attract screens we had in those early years.

My initial version clocked in at about 30 lines, and I suggested ways to make it smaller, such as using arrays to store screen locations rather than individual variables. Arrays make things smaller, but are slower. i.e., if you wanted to track four ghosts on the screen for a Pac-Man game, you could have variables like G1, G2, G3 and G4 and then have a block of code that handled each one individually. Or, you could have an array such as DIM G(3) and access the four ghost locations using G(0), G(1), G(2) and G(3). This allows handling the ghosts in a FOR/NEXT loop instead of four individual blocks of code using separate variables.

Here is a verbose example of randomly moving around four “ghosts”:

10 ' WANDERING.BAS
20 CLS0
30 G1=1024:G2=1055:G3=1504:G4=1535
40 C1=191:C2=239:C3=223:C4=255
50 ' DISPLAY GHOSTS
60 POKE G1,C1:POKE G2,C2:POKE G3,C3:POKE G4,C4
70 ' RANDOM MOVE G1
80 ON RND(4) GOTO 90,100,110,120
90 NL=G1+1:GOTO 130
100 NL=G1-1:GOTO 130
110 NL=G1-32:GOTO 130
120 NL=G1+32
130 IF NL<1024 THEN 180
140 IF NL>1535 THEN 180
150 ' ERASE G1 AND UPDATE LOCATION
160 POKE G1,128:POKE NL,C1:G1=NL
170 ' RANDOM MOVE G2
180 ON RND(4) GOTO 190,200,210,220
190 NL=G2+1:GOTO 230
200 NL=G2-1:GOTO 230
210 NL=G2-32:GOTO 230
220 NL=G2+32
230 IF NL<1024 THEN 280
240 IF NL>1535 THEN 280
250 ' ERASE G2 AND UPDATE LOCATION
260 POKE G2,128:POKE NL,C2:G2=NL
270 ' RANDOM MOVE G3
280 ON RND(4) GOTO 290,300,310,320
290 NL=G3+1:GOTO 330
300 NL=G3-1:GOTO 330
310 NL=G3-32:GOTO 330
320 NL=G3+32
330 IF NL<1024 THEN 380
340 IF NL>1535 THEN 380
350 ' ERASE G4 AND UPDATE LOCATION
360 POKE G3,128:POKE NL,C3:G3=NL
370 ' RANDOM MOVE G4
380 ON RND(4) GOTO 390,400,410,420
390 NL=G4+1:GOTO 430
400 NL=G4-1:GOTO 430
410 NL=G4-32:GOTO 430
420 NL=G4+32
430 IF NL<1024 THEN 470
440 IF NL>1535 THEN 470
450 ' ERASE G4 AND UPDATE LOCATIOn
460 POKE G4,128:POKE NL,C4:G4=NL
470 GOTO 60

And here is that same program, converted to use arrays for the four ghost locations, four ghost colors, and four directions:

10 ' WANDERING2.BAS
20 CLS0
30 G(0)=1024:G(1)=1055:G(2)=1504:G(3)=1535
40 C(0)=191:C(1)=239:C(2)=223:C(3)=255
45 D(0)=1:D(1)=-1:D(2)=-32:D(3)=32
50 ' DISPLAY GHOSTS
60 FOR I=0 TO 3:POKE G(I),C(I)
70 ' RANDOM MOVE G(I)
80 NL=G(I)+D(RND(4)-1)
130 IF NL<1024 THEN 170
140 IF NL>1535 THEN 170
150 ' ERASE G(I) AND UPDATE LOCATION
160 POKE G(I),128:POKE NL,C(I):G(I)=NL
170 NEXT
470 GOTO 60

I tried to keep common line numbers where I could. 47 lines of code down to 15.

And, now that it is an array, it’s easy to make it handle as many ghosts as you want. By adding one more element to the array, and changing the FOR/NEXT loop to count 0-4 instead of 0-3, we get an extra ghost:

10 ' WANDERING3.BAS
20 CLS0
30 G(0)=1024:G(1)=1055:G(2)=1504:G(3)=1535:G(4)=1263
40 C(0)=191:C(1)=239:C(2)=223:C(3)=255:C(4)=143
45 D(0)=1:D(1)=-1:D(2)=-32:D(3)=32
50 ' DISPLAY GHOSTS
60 FOR I=0 TO 4:POKE G(I),C(I)
70 ' RANDOM MOVE G(I)
80 NL=G(I)+D(RND(4)-1)
130 IF NL<1024 THEN 170
140 IF NL>1535 THEN 170
150 ' ERASE G(I) AND UPDATE LOCATION
160 POKE G(I),128:POKE NL,C(I):G(I)=NL
170 NEXT
470 GOTO 60

Arrays are great for reducing code size, and making it so one routine can handle multiple instances of something (locations, colors, etc.).

But, it is slower. Looking up X(3) is slower than looking up X3 since looking up an array has to first look up the variable, and then index in to it to find the entry.

Here is the attract code, converted to use arrays for the block positions and movement directions. As you can see, I’m basically handling the blocks like I did the ghosts above — as objects that can be moved around the screen. Instead of making their movement random, they follow a pattern around the outline of the screen. Instead of having a set color, they just cycle through the seven available non-black VDG colors:

10 ' ATTRACT2.BAS
20 L(0)=1024:L(1)=1024+23:L(2)=1535:L(3)=1535-23
30 Z=143
40 CL(0)=1024:CD(0)=1
50 CL(1)=1055:CD(1)=32
60 CL(2)=1535:CD(2)=-1
70 CL(3)=1504:CD(3)=-32
80 CLS 0:PRINT @268,"ATTRACT!";
90 LD(0)=1:LD(1)=1:LD(2)=-1:LD(3)=-1
100 FOR I=0 TO 3:POKE L(I),Z:NEXT
110 Z=Z+16:IF Z>255 THEN Z=143
120 FOR I=0 TO 3:L(I)=L(I)+LD(I):NEXT
130 FOR L=0 TO 3
140 FOR C=0 TO 3
150 IF L(L)=CL(C) THEN LD(L)=CD(C)
160 NEXT
170 NEXT
180 GOTO 100

30 lines of the originally down to 18 by using arrays.

When time isn’t as important as code size (or convenience), arrays are a great thing.

I have two more iterations of this attract screen to share, so I’ll end with…

To be continue…

Uploaded Facebook videos not playing sound (but do in preview).

I thought I’d share the solution to a puzzling issue I encountered recently with videos uploaded to Facebook. A recorded video (screen capture) had audio that played fine in my local .mov file, and played fine in the Facebook video preview, but once published, it was silent. No sound.

I tried multiple uploads and several videos, and they all did the same thing. Yet, earlier videos recorded with the same screen capture software, the same way (or so I thought) played audio just fine.

It turns out, Facebook does not support multiple tracks of audio in video files. It uses the first set of audio. I’m my case, the change was using a USB microphone to record my narration instead of the built in laptop mic. The capture software was placing the app audio (built in sound from the computer) on the first set of stereo tracks, and then my USB microphone on the second set. Facebook does not honor any tracks beyond the first set.

Had what I was recording had any app audio, I might have noticed this … “hey, my voice is gone but the beeps are still there.”

But my demo was silent, and it took some web searching to figure this out.

Initially, I got it working simply by taking my captured .mov file and importing it into Final Cut Pro X and then exporting it from there. FCP mixes the multiple audio tracks down to stereo for me, per the output file format.

But, a much easier solution (since I was not using audio on the first track) was to use a utility that would just delete the first unused audio track.

My screen capture software does have an option on how it prioritizes audio recording, so there may be an easy fix I can make there.

I thought I’d post this here in case someone else stumbles upon it.

Color BASIC Attract Screen – part 1

See also: part 1, part 2, part 3, part 4, unrelated, part 5 and part 6.

Inspired by some of the title screens on early TRS-80 Color Computer games, tonight I wrote this:

10 ' ATTRACT.BAS
20 A=1024:B=A+23:C=1535:D=C-23:Z=143
30 AD=1:BD=1:CD=-1:DD=-1
40 CLS 0:PRINT @268,"ATTRACT!";
50 POKE A,Z:POKE B,Z:POKE C,Z:POKE D,Z
60 Z=Z+16:IF Z>255 THEN Z=143
70 A=A+AD
80 IF A=1055 THEN AD=32
90 IF A=1535 THEN AD=-1
100 IF A=1504 THEN AD=-32
110 IF A=1024 THEN AD=1
120 ' 
130 B=B+BD
140 IF B=1055 THEN BD=32
150 IF B=1535 THEN BD=-1
160 IF B=1504 THEN BD=-32
170 IF B=1024 THEN BD=1
180 ' 
190 C=C+CD
200 IF C=1055 THEN CD=32
210 IF C=1535 THEN CD=-1
220 IF C=1504 THEN CD=-32
230 IF C=1024 THEN CD=1
240 ' 
250 D=D+DD
260 IF D=1055 THEN DD=32
270 IF D=1535 THEN DD=-1
280 IF D=1504 THEN DD=-32
290 IF D=1024 THEN DD=1
300 GOTO 50

It produces color changing blocks that chase around the screen:

It works by assigning screen POKE memory locations to the four moving blocks. A (1024) is the top left corner, and C (1535) is the bottom right. I then made B halfway between A and C, and D halfway between C and A. See? (I initially started with them in each of the four corners, but since the screen is 32 wide and 16 tall, the pattern did not look very good.)

I assign Z as the graphical block I will be POKEing on to the screen, using 143 (the first solid color block).

20 A=1024:B=A+23:C=1535:D=C-23:Z=143

I then assign four directional (delta) variables for each position — AD is the direction A should move (1 to add one and move it right, -1 to subtract one and move it left, 32 to move it down, or -32 to move it up). A and B are not he top row so they start with deltas of 1 (right). C and D are on the bottom row, so they start with deltas of -1 (left).

30 AD=1:BD=1:CD=-1:DD=-1

I clear the screen to black and print a message in the center of the screen.

40 CLS 0:PRINT @268,"ATTRACT!";

Next, I use POKE to place the graphic block (Z) on to locations A, B, C and D.

50 POKE A,Z:POKE B,Z:POKE C,Z:POKE D,Z

To change the color to the next color block, I add 16 to Z. If it’s larger than 255, I reset it back to the 143 value of the first block. (Solid blocks are 16 apart in the character set).

60 Z=Z+16:IF Z>255 THEN Z=143

Then I just do A=A+AD to get the new position (and the same for B, C and D), then check to see if the new location is one of the four corners, and adjust the delta (movement) variable accordingly.

70 A=A+AD
80 IF A=1055 THEN AD=32
90 IF A=1535 THEN AD=-1
100 IF A=1504 THEN AD=-32
110 IF A=1024 THEN AD=1

This is duplicated for B, C and D, then back to the top to do it all over…

120 ' 
130 B=B+BD
140 IF B=1055 THEN BD=32
150 IF B=1535 THEN BD=-1
160 IF B=1504 THEN BD=-32
170 IF B=1024 THEN BD=1
180 ' 
190 C=C+CD
200 IF C=1055 THEN CD=32
210 IF C=1535 THEN CD=-1
220 IF C=1504 THEN CD=-32
230 IF C=1024 THEN CD=1
240 ' 
250 D=D+DD
260 IF D=1055 THEN DD=32
270 IF D=1535 THEN DD=-1
280 IF D=1504 THEN DD=-32
290 IF D=1024 THEN DD=1
300 GOTO 50

It works!

But it seems to go very, very slowly.

And it’s quite large.

I wonder if you might want to try to make the smallest version that does the same thing (even if it’s slower), and the fastest version (even if it’s larger).

Some Size-Reducing Ideas

For example, to make is small, perhaps using a multidimensional array of POKE locations, like DIM L(3) (it is base zero, so 0-3 would be the elements). That would let all the positions be POKEd in a loop like:

L(0)=1024:L(1)=1024+23:L(2)=1535:L(3)=1535-23
FOR I=0 TO 3:POKE L(I),Z:NEXT

For movement, there could be a location delta array like DIM LD(3) that could have the direction each location is meant to be heading.

LD(0)=1:LD(1)=1:LD(2)=-1:LD(3)=-1

To move each location, code like this could be used:

FOR I=0 TO 3:L(I)=L(I)+LD(I):NEXT

Then, perhaps the screen corners could be in an array, such as DIM C(3), and another corresponding array of delta directions could be used like DIM D(3) set up like this:

CL(0)=1024:CD(0)=1
CL(1)=1055:CD(1)=32
CL(2)=1535:CD(2)=-1
CL(3)=1504:CD(3)=-32

Then, you could check for each block being in a corner and adjust it’s delta using the array as a lookup table:

FOR L=0 TO 3 ' Check each Location
   FOR C=0 TO 3 ' Check each Corner
      IF L(L)=CL(C) THEN LD(L)=CD(L)
   NEXT
NEXT

And instead of hard-coding all these values, maybe they could be read in via READ/DATA statements.

That might make it smaller, but probably slower.

I’ll share three iterations of this program, each one making it smaller than the previous. Then, if we aren’t bored by then, we can look at ways to make it faster.

As always, leave your thoughts in the comments. I bet you know a better way to do this.

Until next time…

Revisiting 10 PRINT RACER

Awhile back I ported 8-Bit Show and Tell‘s “10 PRINT RACER” from Commodore PET to CoCo. I tried to make it a literal port, keeping the code as close as I could to the original. I did, however, mention a few things that could make it faster, taking advantage of things like Extended Color BASIC’s hex values (&H2 is faster to parse than 2, for instance).

The other day, MiaM left a comment on the original article:

It might be faster to use A=ASC(INKEY$) and IF A=4 instead of IF A$=CHR$(4)

– MiaM

Intriguing. The original Commodore version, the direction was read by using GET A$, and I simply converted that over to A$=INKEY$ for Color BASIC. Here is a look at Robin’s Commodore PET original:

1 REM 10 PRINT RACER: 8-BIT SHOW & TELL
5 R$="":PRINT"{CLR}INIT:";:FORX=1TO75:M$=CHR$(205.5+RND(.)):R$=R$+M$:PRINTM$;:NEXT
10 PRINT"{CLR}":C=20:R=13:W=15:D=0:S=32768
20 L=0:FORZ=0TO1STEP0:X=RND(.)*10
30 IFX<4THENR=R-1:IFR<1THENR=1
40 IFX>6THENR=R+1:IFR+W>37THENR=37-W
50 RN=RND(.)*35+1:PRINTMID$(R$,RN,R);SPC(W);MID$(R$,RN,39-R-W)
60 D=D+1:L=L+1:IFL>49THENL=0:W=W-1:IFW<3THENW=3
70 IFD<25THENNEXT
75 GETA$:IFA$="4"THENC=C-1
80 IFA$="6"THENC=C+1
90 P=PEEK(S+C):IFP<>32THEN200
100 POKES+C,42:NEXT
200 PRINTSPC(17)"CRASH!":IFD>HTHENH=D
205 PRINT,"SCORE:"D"  HIGH:"H
210 FORX=1TO2000:NEXT:POKE158,0
220 GETA$:IFA$=""THEN220
230 GOTO10

And here is my Color BASIC conversion:

0 ' 10 PRINT RACER
1 ' BY WWW.8BITSHOWANDTELL.COM
2 '
3 ' PORTED FROM PET TO COCO
4 ' BY SUBETHASOFTWARE.COM
5 R$="":CLS:PRINT"INIT:";:FORX=1TO75:M$=CHR$(47+45*(RND(2)-1)):R$=R$+M$:PRINTM$;:NEXT
6 S$=STRING$(32," ")
10 CLS:C=16:R=10:W=12:D=0:S=1024
20 L=0:FORZ=0TO1STEP0:X=RND(.)*10
30 IFX<4THENR=R-1:IFR<1THENR=1
40 IFX>5THENR=R+1:IFR+W>29THENR=29-W
50 RN=RND(.)*28+1:PRINTMID$(R$,RN,R);MID$(S$,1,W);MID$(R$,RN,31-R-W)
60 D=D+1:L=L+1:IFL>49THENL=0:W=W-1:IFW<3THENW=3
70 IFD<16THENNEXT
75 A$=INKEY$:IFA$=CHR$(8)THENC=C-1
80 IFA$=CHR$(9)THENC=C+1
90 P=PEEK(S+C):IFP<>96THEN200
100 POKES+C,106:NEXT
200 PRINTTAB(13)"CRASH!":IFD>H THENH=D
205 PRINTTAB(6)"SCORE:"D"  HIGH:"H
210 FORX=1TO2000:NEXT:A$=INKEY$
220 A$=INKEY$:IFA$=""THEN220
230 GOTO10

The block of code MiaM refers to is this:

75 GETA$:IFA$="4"THENC=C-1
80 IFA$="6"THENC=C+1

75 A$=INKEY$:IFA$=CHR$(8)THENC=C-1
80 IFA$=CHR$(9)THENC=C+1

On the Commodore PET, without arrow keys, it used “4” and “6” on the numeric keypad for Left and Right. On the CoCo, I changed that to the Left Arrow key and the Right Arrow key.

The Commodore PET has much less work to do looking for A$=”4″ versus A$=CHR$(8) not he CoCo (due to all the parsing). I could have made the CoCo use letter keys like “A” for left and “S” for right to get similar performance.

But what MiaM suggests may be faster. Instead of comparing strings like A$=CHR$(8), the suggestion is to use BASIC’s ASC() keyword to return the numeric value of the character, then compare a numeric value rather than a string compare.

Which is faster? A one character string compare, or ASC() and a number compare?

Let’s find out.

Comparing a String to a String

For this, I dug out my old BENCH.BAS benchmarking code and inserted the first method I wanted to test — the way the Commodore PET did it:

5 DIM TE,TM,B,A,TT
10 FORA=0TO3:TIMER=0:TM=TIMER
20 FORB=0TO1000
30 A$=INKEY$:IF A$="4" THEN REM

70 NEXT
80 TE=TIMER-TM:PRINTA,TE
90 TT=TT+TE:NEXT:PRINTTT/A:END

Comparing A$ to a quoted value in this loop produces 515.

Comparing a String to a CHR$

My conversion changed this to comparing to a CHR$(8) value, like this:

0 REM ascvsstringcompare.BAS
5 DIM TE,TM,B,A,TT
10 FORA=0TO3:TIMER=0:TM=TIMER
20 FORB=0TO1000
30 A$=INKEY$:IF A$="4" THEN REM
30 A$=INKEY$:IF A$=CHR$(8) THEN REM

70 NEXT
80 TE=TIMER-TM:PRINTA,TE
90 TT=TT+TE:NEXT:PRINTTT/A:END

This produces a slower 628. No surprise, due to having to parse CHR$() and the number. I could easily speed up the CoCo port by using quoted characters like “A” for Left and “S” for Right.

But I really wanted to use the arrow keys.

ASC and you shall receive…

The new suggestion is to use ASC. ASC will convert a character to its ASCII value (or PETASCII on a Commodore, I would suppose). For example:

PRINT ASC("A")
65

The cool suggestion was to try using INKEY$ as the parameter inside of ASC(), and skipping the use of a variable entirely. Unfortunately, when I tried it, I received:

?FC ERROR

Function Call error. Because, if no key is pressed, INKEY$ returns nothing, which I suppose would be like trying to do:

PRINT ASC("")

We have been able to use INKEY$ directly in other functions, such as INSTR (looking up a character inside a string), and that works even when passing in “”:

PRINT INSTR("","ABCDE")
0

But ASC() won’t work without a character, at least not in Color BASIC. And, even if we used A$=INKEY$, we can’t pass A$ in to ASC() if it is empty (no key pressed) which means we’d need an extra check like:

30 A$=INKEY$:IF A$<>"" THEN IF ASC(A$)=4 THEN ..

The more parsing, the slower. This produced 539, which isn’t as slow as I expected. It’s slower than doing IF A$=”4″ but faster than IF A$=CHR$(8). Thus, it would be faster in my CoCo port than my original.

This did give me another thing to try. ASC() allows you to pass in a string that contains more than one character, but it only acts upon the first letter. You can do this:

PRINT ASC("ALLEN TRIED THIS")
65

This means I could always pad the return of INKEY$ with another character so it would either be whatever keys he user pressed, or my other character if nothing was pressed. Like this:

30 IF ASC(INKEY$+".")=8 THEN REM

If no key has been pressed, this would try to parse “”+”.”, and give me the ASCII of “.”.

If a key had been pressed, this would parse that character (like “4.” if I pressed a 4).

As I learned when I first stated my benchmarking BASIC series, string manipulation is slow. Very slow. So I expect this to be very slow.

To my surprise, it returns 520! Just a smidge slower than the original IF A$=”4″ string compare! I’m actually quite surprised.

Now, in the actual 10 PRINT RACER game, which is doing lots of string manipulations to generate the game maze, this could end up being much slower if it had to move around other larger strings. But, still worth a shot.

Thank you, MiaM! Neat idea, even if Color BASIC wouldn’t let me do it the cool way you suggested.

Until next time…

Bonus

Numbers verses string compares:

30 IF Z=4 THEN REM

That gives me 350. Even though decimal values are much slower to parse than HEX values, they are still faster than strings.

But, in pure Color BASIC, there is no way to get input from a keypress to a number other than ASC. BUT, you could PEEK some BASIC RAM value that is the key being held down, and do it that way (which is something I have discussed earlier).

Any more ideas?