Author Archives: Allen Huffman

About Allen Huffman

Co-founder of Sub-Etha Software.

How random is RND in Color BASIC?

Random thought of the day…

Recently, Steve Ostrom posted to the Facebook CoCo group a puzzle he was trying to work out:

It’s been a few years since I turned on my Coco, even though it’s still all set up. I came across an interesting probability problem a few days ago, so I fired up the Coco and wrote a program to calculate the approximate answers using a random number generator. I’ll work on the real probability calculations maybe when I get even more bored. So, here’s the problem: You have 15 marbles in a bag. 5 are red, 4 are blue, 3 are white and 3 are black. You pull out one marble at a time, without putting it back, until you have 3 marbles of the same color. What are the odds of getting 3 red marbles, of getting 3 blue marbles, of getting 3 white marbles or of getting 3 black marbles. I ran 100,000 iterations (took most of the night) and here are the rounded percentages I found. Red = 42.5%, blue = 27.5%, white and black are both 15.0%. Anyone else want to try?  :-) Three cheers for the Coco and the ability to write quick programs !!

Steve Ostrom (March 21, 2020 on the CoCo Facebook Group)

This started a discussion on the CoCo Mailing List about just how random the RND() function is in Color BASIC. This reminds me of one of my earliest computer encounters back around 1979-1980.

I was living in Mequite, Texas (near Dallas) and my next door neighbor’s mom worked for Texas Instruments. They had plenty of TI stuff at home (watches and such) as well as a TI-99 home computer. Due to the year, it must have been the original TI-99/4.

His mom has typed in a BASIC program that played a card game. My friend Kris explained that he knew all the cards it would pick because it did the same ones each time. My memory seems to recall him trying to explain that the computer did not do random numbers, so his mother programmed the card sequence. Looking back, maybe what he was explaining was that it did the same numbers each time.

He was, of course, very good at this card game because of this.

Years later, I was shown an amazing magic trick as a friend talked to me over the phone and had me type in things into my CoCo and he told me what the result would be. Turn on a CoCo (or load an emulator, even the online JS Mocha or Xroar versions), and type this in:

FOR A=1 TO 10:PRINT RND(0):NEXT

What do you get if you try that on a real CoCo 1, 2 or 3?

If I recall, the RND function was meant to be used like this:

CoCo 3 BASIC manual entry on RND.

I am quite sure he just had me type “PRINT RND(100)” and he could tell me the results each time.

The pseudo random number generator in Color BASIC will do the same sequence every time, so after a power up, any game that relied on RND would be as predictable as my friend’s TI-99/4 card game was.

You can “seed” the random number generator by giving it a negative number:

As you can see, the random number generator will reset and generate the same sequence of numbers based on the negative value you give it.

Ever play Mine Sweeper on Windows? It had an option to enter a number and replay the same game. I am guessing this just seeded the Microsoft random number generator in the same way, and allowed the randomly generated playfield to be the same each time you gave it the same seed number.

Seems we could do that on the CoCo too and have a random game that you could let others try, using the same sequence of randomness.

I did not understand this when I was first getting in to the CoCo, but I knew you were supposed to seed the generator doing something like:

A=RND(-TIMER)

In Extended Color BASIC, TIMER was an incrementing variable that started at 0 on power up, and then counted up 60 times a second (the screen refresh interrupt time) until it rolled over at 65535 (about 18 minutes).

Since the time it took to turn on a computer, load a program and type run would vary, using the negative value of the timer gave a different random seed each time. I suppose if you had a BOOT rom that would automatically start up and run something (like RGB-DOS’s AUTOEXEC.BAS), maybe it would be the same amount of time each time, but beyond that, this was a good way to make the predictable RND function a bit less predictable.

As suggested on the mailing list, you can “see” the randomness by plotting out the values, such as doing something like this:

10 PMODE 4,1:SCREEN 1,1:PCLS
20 PSET(RND(256)-1,RND(192)-1):GOTO 20

Or, to make that a tad faster, use HEX in that line 20 loop and add the double speed poke (for the CoCo 3, POKE 65497,0 is even faster):

10 POKE 65495,0:PMODE 4,1:SCREEN 1,1:PCLS
20 PSET(RND(&H100)-&H1,RND(&HC0)-&H1):GOTO 20

…then let it run for awhile. If it was truly random, eventually it should set every single dot on the screen. But, pseudo random number generators have patterns. There is a level of predictability in them which has been used by hackers to exploit “secure” Internet connections for many years.

Randomly plotting points on a CoCo PMODE 4 screen.

Interesting. I never would have thought to try that back in the 1980s.

Random Benchmarking

And, lastly, benchmarks! How fast is decimal versus hex for plotting on the screen?

10 PMODE 4,1:SCREEN 1,1:PCLS
15 TIMER=0:FOR A=1 TO 1000
20 PSET(RND(256)-1,RND(192)-1):NEXT
30 PRINT TIMER/60

Versus:

10 PMODE 4,1:SCREEN 1,1:PCLS
15 TIMER=0:FOR A=1 TO 1000
20 PSET(RND(&H100)-&H1,RND(&HC0)-&H1):NEXT
30 PRINT TIMER/60

The decimal version reports 23.58 seconds. The hex version reports 20.1 seconds. Changing the numbers to variables can make it a tad faster:

10 PMODE 4,1:SCREEN 1,1:PCLS
11 X=256:Y=192
15 TIMER=0:FOR A=1 TO 1000
20 PSET(RND(X)-&H1,RND(Y)-&H1):NEXT
30 PRINT TIMER/60

19.23 seconds. I wonder if the &H1 is slower than a variable?

10 PMODE 4,1:SCREEN 1,1:PCLS
11 X=256:Y=192:O=1
15 TIMER=0:FOR A=1 TO 1000
20 PSET(RND(X)-O,RND(Y)-O):NEXT
30 PRINT TIMER/60

Hey hey! 18.61 seconds! I sure wish I knew all this back when I was writing BASIC programs in the 80s…

Oh, and FOR/NEXT is faster than a GOTO since it does not have to scan through lines to find where it is going. I think if I was going to let this run for a few hours, I might do this:

10 PMODE 4,1:SCREEN 1,1:PCLS
11 X=256:Y=192:O=1
15 FOR A=1 TO 2 STEP 0
20 PSET(RND(X)-O,RND(Y)-O):NEXT

That should be marginally faster than the GOTO 20 was.

I’ll tell you how it turns out tomorrow.

Tomorrow

Well, it looks like RND does hit every location from 0 to 255. Here is what my screen looked like after running overnight:

Randomly plotting points on a CoCo PMODE 4 screen, the next morning.

I guess I need a better test to show the frequency that each value comes up. Sorta like Steve’s original project of randomly pulling colored marbles from a bag.

Until next time…

Microsoft Color BASIC easter egg.

Updates:

  • 2020-04-01 – Added image of the Color BASIC Unravelled book section where the Easter egg is located.

Over in the Color Computer group on Facebook, there was a post by Phill Harvey-Smith discussing CoCo ROMs and the ROMs of the CoCo clones.

Cathe Cita responded and provided a link to this cool article about the Microsoft BASIC ROM easter eggs:

https://www.pagetable.com/?p=43

I got curious, and wrote this:

10 REM MS-EGG.BAS
20 P$="ABCDEFGHIJKLMNOPQRSTUVWXYZ......!"
30 FOR L=&HBFEF TO &HBFE6 STEP -1
40 V=PEEK(L) AND &H3F
50 C$=MID$(P$,V,1)
60 PRINT HEX$(L);" ";HEX$(PEEK(L));" -> ";HEX$(V),C$
70 NEXT

Now I’ve finally seen it with my own eyes.

Color BASIC Easter Egg

Line 20 is just enough of the PETASCII characters that would get POKEd to the screen memory on the Commodore for our hidden message. (A is POKE x, 1 … B is POKE x,2, etc.)

Line 30 loops through the memory locations in the Color BASIC ROM where the easter egg is stored, just after a numeric table. And stored in reverse.

Like 40 uses PEEK to get the byte at that location, then uses AND to mask off the top 2 bits, which were added (“randomly?”) to obfuscate the message so you couldn’t see in the ROM binary.

Line 50 gets the corresponding character from the P$ that matches the value of the PEEK’d byte.

Line 60 prints out the memory location, then the original value stored there, then the “mask off the top two bits” value, and then the character it represents.

…neat, huh?

BASIC Code

For those curious, here is what those bytes look like in the ROM. This screen shot is from the Color BASIC Unravelled book.

Unused “MICROSOFT!” hidden in the Color BASIC ROMs.

Until next time…

Color BASIC optimization challenge – attempts

See also: part 1

My original “nicer to read, slower to run” version took 25.28 seconds to run. Here are some things that can speed it up.

RENUMber by 1

Renumbering the program by 1 can save program memory (since “GOTO 77” takes less space than “GOTO 1500”, and if the program is large enough, you get a slight speed increase since it’s also less work to parse shorter line numbers. But this program is too small for this to matter.

Remove comments / avoid comments at the end of lines

Removing comments, in general, is a good way to make a program smaller (and thus, a bit faster since BASIC will have less lines to skip). But, as I found out, comments at the end of lines are a real problem, since BASIC cannot just skip the entire line like it can if the line starts with a comment:

10 X=42:REM SET X TO THE ANSWER.

10 REM SET X TO THE ANSWER.
20 X=42

The second version is faster, since even though BASIC has to scan an extra line, it can immediately skip it, while the first example must be parsed, character-by-character, to find the end of that line. But for this example, this did not help. It would have, if there were more GOTOs to earlier line numbers, since they would have to parse through a few lines with end comments, which would be slower. Since our looping was done with a FOR/NEXT, we didn’t have to do that.

Remove unnecessary spaces

Spaces are great for readability, but bad for code size and speed. There are only a few times when a space is needed, such as when BASIC can’t tell if the next character is a keyword or part of a variable:

10 IF A=B THEN 100

BASIC must have the space between the variable and the keyword THEN, else the parser isn’t sure if the variable is “B” or “BT” followed by the characters HEN. BUT, if it’s a number, it can figure it out:

10 IFA=42THEN100

Removing all other spaces reduces the amount of code BASIC has to parse through. My program had plenty of spaces. Removing them in this tiny program only reduced it to 25.15 seconds, but it can have a bigger impact on larger programs.

Combine lines

This one is easy. The less lines BASIC has to scan through, the quicker it will run. But, there are exceptions to this. Any time BASIC has to do more work at the end of a line, such as checking an ELSE, if that condition is not satisfied, BASIC has to scan forward character-by-character to get to the end of the line:

10 IF X=42 THEN 100 ELSE PRINT "I DON'T KNOW THE ANSWER"

Above, if X is 42, the ELSE code will not be ran. BUT, before the program can find 100, it has to scan through all the rest of the bytes in that line to find the end of it, then it continues scanning a line at a time looking for 100. Based on line lengths, sometime it might be faster to do:

10 IF X=42 THEN 100
20 IF X<>42 THEN PRINT "I DON'T KNOW THE ANSWER"

Combining lines in the program reduced the speed slightly to 25.13 seconds. The more code, the more you save by doing this (and it makes the program smaller, since every line number takes up space).

NEXT without variable

Unless you are doing really weird stuff with FOR/NEXT loops, it’s faster to leave off the variable in the NEXT. For example:

10 FOR A=1 TO 10:NEXT A

…will be slower than:

10 FOR A=1 TO 10:NEXT

NEXT without a variable will go to the most recent FOR anyway, so this works fine:

10 FOR A=1 TO 10
20 FOR B=1 TO 20
30 PRINT A,B
40 NEXT
50 NEXT

For this program, just removing the variables from NEXT A and NEXT Z increased the speed to 24.66 seconds.

Use HEX instead of decimal

It takes much more work to convert base-10 (i.e., “normal”) decimal numbers than it does to convert base-16 (hex) values. Every time a number is encountered, time can be saved by simply changing the number to a HEX — even though a hex digit has to start with “&H”. It will make the program larger, since 15 takes less bytes than &HF. For this example, it reduced time down to 21.66 seconds!

Pre-render strings

If you have to PRINT a generated string more than once, you are better off pre-rendering that string first so it only gets generated one time. This includes using things like STRING$, CHR$, etc. For example:

10 PRINT CHR$(128);"HELLO";CHR$(128)

Each time BASIC has to print that, it allocates a new string a few times and copies things over. See my string theory article for more details. For this program, I use STRING$() to generate a repeating block of characters. But, since it’s in a loop to print the block, it generates the same string over and over for each line of the block. Instead of this:

10 FOR A=1 TO 10
20 PRINT STRING$(10,128)
30 NEXT A

…I could generate that string ahead of time and just print it in the loop:

5 LN$=STRING$(10,128)
10 FOR A=1 TO 10
20 PRINT LN$
30 NEXT A

In this program, doing this trick reduces the time to 20.98 seconds! String are slow, you see…

The time savings so far…

Combining all of these (the ones that matter, at least), reduces time taken down to 18.45 seconds – saving 6.83 seconds overall.

Here is what it looks like, with the stuff outside of the benchmark timing loop left alone:

0 REM scale.bas
10 SW=32/4 ' SCALE WIDTH
20 SH=16/3 ' SCALE HEIGHT
30 SM=.1   ' SCALE INC/DEC
40 S=.5    ' SCALE FACTOR
70 TM=TIMER:FOR Z=1 TO 100
80 W=INT(SW*S):H=INT(SH*S):P=&HF-INT(W/&H2)+(&H7-INT(H/&H2))*&H20:LN$=STRING$(W,&HAF):CLS:FORA=&H1 TOH:PRINT@P+A*&H20,LN$:NEXT:S=S+SM:IFH<&H1 ORH>&HF THEN SM=-SM:S=S+(SM*&H2)
170 NEXT
180 ' 60=NTSC 50=PAL
190 PRINT:PRINT (TIMER-TM)/60;"SECONDS"

If we’d done any GOTO/GOSUBing to earlier lines, that would have to scan through those lines with ending REMarks, so cleaning all that up would be useful. If this was a larger program, RENUMbering by 1s might also squeak out a bit more speed.

But wait! There’s more!

I originally wrote this follow-up the evening that I wrote the original article. However, several folks have contributed their own optimizations. Let’s see what they did…

First, we go to the comments of the original article…

George Phillips – inner loop optimization

My current best effort is going full strength reduction on the inner loop. Replace lines 120 to 170 with:

120 L$=STRING$(W,175):FORA=P+32TOP+H*32STEP32:PRINT@A,L$;:NEXT
150 IFH<1ORH>15THEMSM=-SM
170 S=S+SM:NEXT

The STRING$ requires Extended Color Basic which starts at a baseline of 20.4 seconds and those changes get it down to 13.

George Phillips

George pre-rendered the string, removed spaces, and vastly sped things up by simplifying the math. On Xroar, it reports 16.15 – faster than any of my efforts. Nice job!

I took his updated code and converted the integers to HEX and that dropped it to 15.1 seconds! Even faster.

Jason Pittman – hex and start row calculation

Interestingly, changing the blue square value on line 130 to hex (“&HAF”) saves about 11.5% for me (27.55 to 24.33). Then, moving the row start calculation out of the print statement takes it down to 22.87.

120 FOR A=32 TO H*32 STEP 32
130 PRINT@P+A,STRING$(W,&HAF)
140 NEXT A

Jason Pittman

Ah, the magic of hex! Jason did a similar trick with the row calculation. I tried Jason’s updates, and it was 20.28 on my Xroar. George is still in the lead.

Adam – DIM, reductions and variable substituion

So I was able to cut out 5 seconds doing the following:
– DIM all variables
– Adding variables for constants (2, 32, etc.) for lines 130, 160.
– Removing the math in lines 10, 20.
– Removing the variables after NEXT.
– Removing all remarks.

No change to the original algorithm or PRINT line.

Coco3 (6309): Original code = 27.15s. Optimized code 22.13s.
With high speed poke: 11.05s. :D

I’m intrigued with the post that mentioned using HEX values. I may have to try that.

Adam

Ah! Using a variable rather than a constant can be significantly faster. I didn’t think about that, even though I’ve previously benchmarked and tested that. He adds:

I just put the STRING$ statement into a variable as suggested by George Phillips. That took out another 2 seconds! I also ran RENUM 1,,1. Run time is down from 27.2 s to 20.1 s.

L$=STRING$(W,C)
FORA=1TO H
PRINT@P+A*B,L$
NEXT

Adam

It looks like George has gotten us all thinking. I don’t have Adam’s code to test, but I am going to experiment with replacing the constants with variables and see what that does.

Ciaran Anscomb – uh… not really sure!

The author of Xroar chimes in:

So I cheated and read the comments on your page and combined some of those techniques (and replace /2 with  *1/2, forgot about that…).

13.1s?  Not quite double speed, but not bad :)

0 REM scale.bas
1 GOSUB100:TM=TIMER:FORZ=1TO M:CLS:A$=STRING$(E-INT(W*B),L)+STRING$(W,C):PRINT@(8-INT(H*B))*L,””;:FORA=1TOH:PRINTA$:NEXT:IFH<1ORH>=&H10 THENQ=-Q:R=-R
2 W=W+Q:H=H+R:NEXT
3 ‘ 60=NTSC 50=PAL
4 T=TIMER:PRINT:PRINT (T-TM)/60;”SECONDS”
5 END
100 DIMW,H,A$,A,B,C,L,Q,R,E,M,Z
110 I=32/4 ‘ SCALE WIDTH
120 J=16/3 ‘ SCALE HEIGHT
130 D=.1   ‘ SCALE INC/DEC
140 S=.5   ‘ SCALE FACTOR
150 W=I*S:H=J*S
160 Q=I*D:R=J*D
170 L=32:M=&H64
180 B=1/2:C=&HAF:E=15
190 RETURN

..ciaran

Ciaran Anscomb

Ciaran reorganized the code, placing initialization at the end. This is a habbit I need to get into since GOTO/GOSUB to an earlier line number have to start at the FIRST line of the program and scan forward EVERY TIME. Code that only needs to run once, if placed at the top, slows down every GOTO to an earlier line number.

I just ran this and got 13.13 seconds!

There is alot to dissect here. Numbers are replaced with variables. Stuff is pre-calculated. And he’s using a variable for the end of a FOR/NEXT, for some reason. I’m going to have to dig in to that one.

Twice as fast, indeed!

I feel like there were a few more responses, but I can’t locate them at the moment, so maybe there will be a second attempt article down the line.

Until next time, kudos to all of you for giving me new things to consider — especially Ciaran with that amazing improvement. Very impressive.

Adding gravity to a BASIC bouncing ball, follow-up.

See also: part 1.

“Let’s build a worksheet to calculate these values.”

This is a quote that will stay with me. It comes from a video response from Paul Fiscarelli that demonstrates his solution for adding gravity to the bouncing ball demo. He suggests pre-calculating the positions so nothing has to be calculated at run-time.

In part 3 of my bouncing ball series, I discussed the idea of using DATA for the positions rather than doing math simply because it would be faster. But, the math still has to be done somewhere to generate the values for those DATA statements.

But math is hard.

Fortunately, Paul provides the math.

And, he does it with style, using real equations that calculate an object in free-fall. Impressive! His video explains how the formulas work, and then he uses an Excel spreadsheet to do all the math, and then fills it up with locations representing each Y position of the ball, all designed to perfectly fit the 32 column screen. That’s something my brute-force simulated gravity attempt did not do.

He then uses some fancy Excel formulas to make it generate all the data together as numbers separated by commas, ready to be copy-and-pasted into an editor with the rest of the BASIC program.

Screen shot from Paul Viscarellis’ YouTube video showing turning Excel spreadsheet data into BASIC DATA statements.

Well done, Paul! I’m impressed!

Your demo sent me back to my benchmarking to find out which was faster:

Array or DATA?

Paul’s recalculated data is stored in DATA statements, such as:

DATA 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,-1

During the initialization code, he loads the data up in an array, such as:

FOR I=0 TO 20:READ Y(I):NEXT

He then has all his Y positions available just by using an index counter (he re-purposes my YM Y-movement variable) that loops through them. In his example, he places a -1 at the end of the DATA statement, and uses that as a dynamic way to indicate the end of the data. When he’s retrieving his Y value from the array, he checks for that to know when he needs to start back at the beginning of the array:

YM=YM+1:Y=Y(YM):IF Y<0 THEN YM=0:Y=Y(YM)

In part 8 of my Optimizing Color BASIC series, I looked into arrays and discovered they were significantly slower than non-array variables. Surely an array is faster than the calculating that gravity equation real-time, but it it faster than just READing each item each time?

READ/RESTORE versus Arrays

I present a new benchmark… which is faster? Using READ/RESTORE, or using pre-loaded arrays? Let’s fine out!

First, the test program is going to load 20 values into an array, then in the benchmark loop it’s going to just assign the next array value to a variable and increment an index variable until it gets to the end of the data. It will then reset the index to 0 and start over. This is a scaled-down version of Paul’s demo looping through the array values.

0 REM ARRAYS.BAS
6 DIMZ(20),Y,X:FORA=0TO19:READZ(A):NEXT:X=0
5 DIM TE,TM,B,A,TT
10 FORA=0TO3:TIMER=0:TM=TIMER
20 FORB=0TO1000
40 Y=Z(X):X=X+&H1:IFX>&H14 THENX=0:GOTO40
70 NEXT
80 TE=TIMER-TM:PRINTA,TE
90 TT=TT+TE:NEXT:PRINTTT/A/60:END
100 DATA 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,-1

Running this in the Xroar emulator gives 12.65 seconds.

Next I removed the array and just do a READ each time, looking for a -1 which indicates we need to RESTORE back to the start of the data. It looks like this:

0 REM RESTORE.BAS
5 DIM TE,TM,B,A,TT
10 FORA=0TO3:TIMER=0:TM=TIMER
20 FORB=0TO1000
30 READ Z:IFZ=-1THENRESTORE:GOTO30
40 Y=Z
70 NEXT
80 TE=TIMER-TM:PRINTA,TE
90 TT=TT+TE:NEXT:PRINTTT/A/60:END
100 DATA 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,-1

This produces 10.51 seconds! A bit faster. But it’s not quite the same.

In the READ/RESTORE example, I have to keep checking for -1. In the array version, there is an index value (needed for the array) that can be checked. Is it faster to do that, rather than looking for -1?

0 REM RESTIDX.BAS
5 DIM TE,TM,B,A,TT
10 FORA=0TO3:TIMER=0:TM=TIMER
20 FORB=0TO1000
30 READ Z:X=X+&H1:IFX>&H14 THENRESTORE:X=&H0:GOTO30
40 Y=Z
70 NEXT
80 TE=TIMER-TM:PRINTA,TE
90 TT=TT+TE:NEXT:PRINTTT/A/60:END
100 DATA 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,-1

Yipes! The one slows down to 14.15 seconds. It seems incrementing a variable, comparing it, then resetting it adds a lot of overhead. It seems just checking for -1 is the faster way.

Let’s make it a tad bit faster.

In part 6 of my BASIC series, I looked at using READ on integers, hex, and strings. I found HEX to be almost twice as fast. I’ll also replace the -1 with some HEX value we won’t be using. Let’s try that here:

0 REM DATAHEX.BAS
10 FORA=0TO3:TIMER=0:TM=TIMER
20 FORB=0TO1000
30 READ Z:IFZ=&HFF THENRESTORE:GOTO30
40 Y=Z
70 NEXT
80 TE=TIMER-TM:PRINTA,TE
90 TT=TT+TE:NEXT:PRINTTT/A/60:END
100 DATA &1,&H2,&H3,&H4,&H5,&H6,&H7,&H8,&H9,&HA,&HB,&HC,&HD,&HE,&HF,&H10,&H11,&H12,&H13,&H14,&HFF

9.27 seconds!!!

It looks like using Paul’s technique to pre-calculate all the gravity values, and then reading them dynamically from DATA statements in the demo, should give us a great speed boost even over my simple attempt to do things like “Y=Y+.25”.

Thanks, Paul, for providing us with a way to do great “real” gravity without having to do a bit of math…on the CoCo. Hopefully I can dissect those equations and replicate it.

Paul’s Video

This is the first time I have ever enjoyed watching a presentation on Excel. I hope you enjoy it, too. Here’s Paul’s video:

Until next time…

Jerry Stratton’s SuperBASIC for the CoCo

Well, this is a mind bender.

loop
  loop
    %key$=inkey$
  endloop unless (%key$="")
  print asc(%key$)
endloop

…can be processed by a script to turn into:

10 KE$=INKEY$:IF KE$="" THEN 10
20 PRINT ASC(KE$)
30 GOTO 10

Mind blown. Jerry Stratton has created SuperBASIC for the TRS-80 Color Computer. It processes a text file written in a more modern-looking scripting language and turns it into Color BASIC.

It adds keywords like loop, switch/case, sub, etc. that then get turned into BASIC that does the same thing.

Check it out and see what you think:

https://www.hoboes.com/Mimsy/hacks/coco/superbasic-trs-80-color-computer/

It would be really cool if we could get all these tools integrated into a cross platform editor (such as Microsoft’s VS Code).

Until next time…

Floating point and 902.1 follow-up

Yesterday, I wrote a short bit about how I wasted a work day trying to figure out why we would tell our hardware to go to “902.1 Mhz” but it would not.

The root cause was a limitation in the floating point used in the C program I was working with. A float cannot represent every value, and it turns out values we were using were some of them. I showed a sample like this:

#include <stdio.h>
#include <stdlib.h>
 
int main()
{
   float f = 902.1;
   printf ("float 902.1  = %f\r\n", f);
 
   double d = 902.1;
   printf ("double 902.1 = %f\r\n", d);
 
   return EXIT_SUCCESS;
}

The float representation of 902.1 would display as 902.099976, and this caused a problem when we multiplied it by one million to turn it into hertz and send it to the hardware.

I played WHAC-A-MOLE trying to find all that places in the code that needed to change floats to doubles, and then played more WHAC-A-MOLE to update the user interface to change fields that use floats to use doubles…

Then, I realized we don’t actually care about precision past one decimal point. Here is the workaround I decided to go with, which means I can stop whacking moles.

float MHz;
uint32_t Hertz;
uint32_t Hertz2;
for (MHz=902.0; MHz < 902.9; MHz+=.1)
{
    Hertz = (MHz * 1000000);

    // Round to nearest 10000th of Hertz.
    Hertz2 = (((Hertz + 50000) / 100000)) * 100000;

    printf ("%f * 1000000 = %u -> %u\n", MHz, Hertz, Hertz2);
}

I kept the existing conversion (which would be off by quite a bit after being multiplied by one million) and then did a quick-and-dirty hack to round that Hertz value to the closest decimal point.

Here are the results, showing the MHz float, the converted Hertz, and the new rounded Hertz we actually use:

902.000000 * 1000000 = 902000000 -> 902000000
902.099976 * 1000000 = 902099975 -> 902100000
902.199951 * 1000000 = 902199951 -> 902200000
902.299927 * 1000000 = 902299926 -> 902300000
902.399902 * 1000000 = 902399902 -> 902400000
902.499878 * 1000000 = 902499877 -> 902500000
902.599854 * 1000000 = 902599853 -> 902600000
902.699829 * 1000000 = 902699829 -> 902700000
902.799805 * 1000000 = 902799804 -> 902800000
902.899780 * 1000000 = 902899780 -> 902900000

There. Much nicer. It’s not perfect, but it’s better.

Now I can get back to my real project.

Until next time…

VIC-20 Super Expander and the CHUG logo

My first computer was a Commodore VIC-20. I used it to do TV titles for my dad’s fishing videos (he shot and edited video that would run at trade shows and such). I can’t find the tape of those VIC-20 graphics, but I did find this:

VIC-20 Super Expander: CHUG Logo

This was my version of the CHUG (Commodore Houston User’s Group) logo, done on the VIC-20 Super Expander cartridge. That cartridge added 3K of extra RAM, and had a ROM that gave new commands to do things like draw lines, play music, etc.

I also found a few other things, but I don’t think I had anything to do with them. (Unless I did the face graphic. That one, and as second version of it I found later, seem very familiar.)

VIC-20 Super Expander: Kangaroo
VIC-20 Super Expander: String
VIC-20 Super Expander: Blinky

The face one would draw the circles around the eyes, then un-draw them, over and over. Not really “blinking” but…

My quest for recovering my early VIC-20 games continues…

Floating point and 902.1

I wasted most of my work day trying to figure out why some hardware was not going to the proper frequency. In my case, it looked fine going to 902.0 mHz, but failed on various odd values such as 902.1 mHz, 902.3 mHz, etc. But, I was told, there was an internal “frequency sweep” function that went through all those frequencies and it worked fine.

I finally ended up looking at the difference between what our host system sent (“Go to frequency X”) and what the internal function was doing (“Scan from frequency X to frequency Y”).

Then I saw it.

The frequency was being represented in hertz as a large 32-bit value, such as 902000000 for 902 mHz, or 902100000 for 902.1 mHz. The host program was taking its 902.1 floating point value and converting it to a 32-bit integer by multiplying that by 1,000,000… which resulted it it sending 902099975… which was then fed into some formula and resulted in enough drift due to being slightly off that the end results was also off.

902099975 is not what I expected from “multiply 902.1 by 1,000,000”.

I keep forgetting how bad floating point is. Try this:

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

int main()
{
   float f = 902.1;
   printf ("float 902.1  = %f\r\n", f);

   double d = 902.1;
   printf ("double 902.1 = %f\r\n", d);

   return EXIT_SUCCESS;
}

It prints:

float 902.1 = 902.099976
double 902.1 = 902.100000

A double precision floating point can correctly represent 902.1, but a single precision float cannot.

The Windows GUI was correctly showing “902.1”, though, probably because it was taking the actual value and rounding it to one decimal place. Thank you, GUI, for hiding the problem.

I guess now I have to go through and change all those floats to doubles so the user gets what the user wants.

Until next time…

Hitchhiker’s Guide to the Galaxy is 42 years old?

I just heard that the late Douglas AdamsHitchhiker’s Guide to the Galaxy radio play is turning 42 years old this year! According to the always-accurate Wikipedia, it was first broadcast on March 8, 1978.

I did not learn about Hitchhiker’s Guide until a few years later.

I was reading the TV Guide and saw a listing for something called “The Hitchhiker’s Guide to the Galaxy” on PBS channel 8 in Houston, Texas. I watched it, and learned it was a British sci-fi comedy series with production quality (ahem) similar to Doctor Who. PBS had introduced me to Tom Baker‘s Doctor years earlier.

The next day, in (I think) my 7th grade English class, I quoted one of the lines from the TV series. A boy sitting in front of me turned around and said something like “you saw that too?” That boy was Jimmy, and at that point the direction of my life changed forever.

If memory is correct, Jimmy loaned me his Hitchhikers’ book. Little did I know the impact that being introduced to the humor of Douglas Adams would have on me. I was an instant fan.

Jimmy and I became friends, and he introduced me to computers and BASIC programming. (Though the first time I ever “saw” a computer in someone’s home was in 1979 or 80, when I lived in Dallas, Texas, and my next door neighbor had a TI-99 because him mom worked for Texas Instruments. But I digress.)

We’d go down to the local Radio Shack and type in programs on their TRS-80 Model 3. I’d been inside Radio Shacks many times with my dad during the 1970s, but this is what got me going there on my own.

Being exposed to computers is what led me to getting my first home computer, a Commodore VIC-20.

Being exposed to Radio Shack (I would hang out there any time my grandmother took me shopping near one) is what led me to get rid of my VIC-20 and get my first TRS-80 Color Computer.

Getting my first “CoCo” led me to getting a second one, and a third, and then starting my first software company, Sub-Etha Software.

Starting that company got me to go to my first CoCoFest in Atlanta in 1990.

Going to that CoCoFest where so many people asked “does it run under OS-9” got me to learn the OS-9 operating system.

Learning the OS-9 operating system is what led me to getting my first “real” job at Microware in Des Moines, Iowa.

And so on…

If it had not been for seeing that listing in TV Guide, and then quoting a line from it that was head by Jimmy, I think I would have had a far different life.

Thank you, TV Guide, for introducing me to the Hitchhiker’s TV show. And thank you, Jimmy, for introducing me to Douglas Adams, computers, and phone phreaking.

And happy birthday, Hitchhiker’s Guide to the Galaxy! I’ve listened to your radio shows, read your books, played your computer game, watched your movie, and look forward to seeing what HULU does with you in their new 2021 TV series.

Don’t panic!

Color BASIC optimization challenge – scaling

Prerequisite: Optimizing Color BASIC series

Here is a simple Color BASIC program that will scale blue box on the screen from small to large then back, going through the scaling processes 100 times.

0 REM scale.bas
10 SW=32/4 ' SCALE WIDTH
20 SH=16/3 ' SCALE HEIGHT
30 SM=.1   ' SCALE INC/DEC
40 S=.5    ' SCALE FACTOR
70 TM=TIMER:FOR Z=1 TO 100
80 W=INT(SW*S)
90 H=INT(SH*S)
100 P=15-INT(W/2)+(7-INT(H/2))*32
110 CLS
120 FOR A=1 TO H
130 PRINT@P+A*32,STRING$(W,175)
140 NEXT A
150 S=S+SM
160 IF H<1 OR H>15 THEN SM=-SM:S=S+(SM*2)
170 NEXT Z
180 ' 60=NTSC 50=PAL
190 PRINT:PRINT (TIMER-TM)/60;"SECONDS"

After this runs, it will report the approximate number of seconds it took. It does this by resetting the TIMER at the start, then printing the current TIMER value divided by 60 (since the CoCo timer is based on the NTSC video interrupt that happens 60 times a second).

NOTE: If you run this on a PAL system, you will need to change the 60 to a 50 in line 190. (edit: thanks, George P., for catching my typo.)

On the Xroar emulator running on my Mac it reports 25.25 seconds.

Color BASIC scaling demo.

Your challenge, should you decide to accept it, is to take this code and make it run faster.

Rules

  1. You must leave the basic algorithm intact (the SW, SH, S and SH stuff with all the math). You can rename variables, change the representation of values, speed up PRINTing, etc. but the core program flow should remain the same.
  2. For bonus points, you are welcome to rewrite the program (in BASIC) to improve upon the algorithm in any way that makes sense, provided it achieves the same results (including the 1 to 100 benchmark loop).

There are some very (very!) simple things that can be done to dramatically improve the speed to his code.

Feel free to share your efforts in the comments. If you post your code, be sure to post the resulting time, too.

Good luck!