Porting a BBC Micro galaxy program to the CoCo

Over in the Facebook CoCo group, Carlos Comacho shared a link to a cool online emulator for the BBC Micro. The link contains a BASIC program listing that drew a very cool galaxy graphic.

Here is the page:

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

Here is the BBC Micro program:

`10 MODE120 VDU530 FORG=RND(-64)TO40040 t=RND(99):q=RND(99)50 u=RND(1280):v=RND(1024)60 A=RND(1)*370 R=90/(1+RND(200))80 Q=1+R*(.5+RND(1)/2)90 a=1+3*RND(1)^2100 M=1110 IFRND(9)<4Q=R:t=0:q=0:A=0:M=PI/3:a=1120 C=(1+3*RND(1)^2)*R*R130 FORi=0TOC140 S=-LNRND(1)150 T=i*M160 U=S*R*SINT170 V=S*Q*COST180 T=S*A190 X=U*COST+V*SINT200 Y=V*COST-U*SINT210 D=(X*X+Y*Y)/(R*R+Q*Q)220 Z=99*((2.7^-D)+.1)230 Z=Z*(RND(1)-.5)^3240 y=Y*COSt+Z*SINt250 Z=Z*COSt-Y*SINt260 x=u+X*COSq+y*SINq270 y=v-X*SINq+y*COSq280 P=POINT(x,y)+a290 IFP>3P=3300 GCOL0,P310 PLOT69,x,y320 NEXT,330 REM`

I decided to try converting it to the CoCo. This led me to looking up some BBC Micro Commands to understand what they did. I found a few significant differences:

• All variables need to be uppercase to work on the CoCo.
• PI is not a constant on the CoCo, so it needs to be defined.
• Line 10 – MODE1 sets a 320×256 4 color screen. On the CoCo 1/2, the 4-color screen has a resolution of 128×191.
• Line 20 – VDU5 – Not used on the CoCo. It outputs a screen character of 5 for some reason.
• Line 30 – RND(-64) would seed the random number generator, but on the BBC Micro it returns the -64. On the CoCo, RND(-64) looks like it does the same as RND(0) on the CoCo, returning a value from 0.0 to .9999. The FOR/NEXT will just have to do -64 for the start number.
• Line 80, 90, 120, 140 and 230 – Their RND(1) returns a number from 0.0 to .99999. This will be changed to RND(0) on the CoCo.
• Line 110 and 290 – The CoCo needs a THEN between IF X>4 THEN DO SOMETHING.
• Line 160,170, 190, 200, 240, 250, 260 and 270 – SIN and COS need parens on the CoCo.
• Line 280 – POINT looks like PPOINT on the CoCo.
• Line 290 – If the program was written for a 4-color “MODE1” display, I assume their colors are 0-3. On the CoCo, they are 1-4, so that is a minor change.
• Line 300 – GCOL0,P sets a color, so probably COLOR P.
• Line 310 – PLOT69,X,Y becomes PSET(X,Y)
• Line 320 – Their BASIC has an interesting shortcut for NEXT. After a FOR A you need a NEXT A, but you can also just say “NEXT” and it uses the most recent FOR. If you have nested loops, like FOR A :FOR B … you can end with “NEXT:NEXT” or “NEXT B,A”. It looks like their BASIC allows leaving out the variables, but using the comma. I tried this on CoCo, and it didn’t work. That would have been a fun discovery. That just means “NEXT,” becomes “NEXT:NEXT”.

When I did a quick port, I saw it passing negative values to PPOINT and PSET, so I had to add an extra check that skips plotting anything that would be off the screen (X <0 or >255, and Y<0 or >191).

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

`0 'BBC MICRO - 320X256	4 COLOUR DISPLAY1 POKE 65395,05 PI=3.14159265358979323810 'MODE111 PMODE 4,1:PCLS:SCREEN 1,1:PMODE 3,120 'VDU530 'FORG=RND(-64)TO40031 FORG=-64TO40040 T=RND(99):Q=RND(99)50 U=RND(1280):V=RND(1024)60 A=RND(1)*370 R=90/(1+RND(200))80 'Q=1+R*(.5+RND(1)/2)81 Q=1+R*(.5+RND(0)/2)90 'A=1+3*RND(1)^291 A=1+3*RND(0)^2100 M=1110 'IFRND(9)<4Q=R:T=0:Q=0:A=0:M=PI/3:A=1111 IFRND(9)<4THENQ=R:T=0:Q=0:A=0:M=PI/3:A=1120 'C=(1+3*RND(1)^2)*R*R121 C=(1+3*RND(0)^2)*R*R130 FORI=0TOC140 'S=-LNRND(1)141 S=-(LOG(RND(0)) / 0.4342944819)150 T=I*M160 'U=S*R*SINT161 U=S*R*SIN(T)170 'V=S*Q*COST171 V=S*Q*COS(T)180 T=S*A190 'X=U*COST+V*SINT191 X=U*COS(T)+V*SIN(T)200 'Y=V*COST-U*SINT201 Y=V*COS(T)-U*SIN(T)210 D=(X*X+Y*Y)/(R*R+Q*Q)220 Z=99*((2.7^-D)+.1)230 'Z=Z*(RND(1)-.5)^3231 Z=Z*(RND(0)-.5)^3240 'Y=Y*COST+Z*SINT241 Y=Y*COS(T)+Z*SIN(T)250 'Z=Z*COST-Y*SINT251 Z=Z*COS(T)-Y*SIN(T)260 'X=U+X*COSQ+Y*SINQ261 X=U+X*COS(Q)+Y*SIN(Q)270 'Y=V-X*SINQ+Y*COSQ271 Y=V-X*SIN(Q)+Y*COS(Q)275 IF X<0 OR X>255 THEN 321276 IF Y<0 OR Y>191 THEN 321280 'P=POINT(X,Y)+A281 P=PPOINT(X,Y)+A290 'IFP>3P=3291 IFP>3THENP=3300 'GCOL0,P301 COLORP310 'PLOT69,X,Y311 PSET(X,Y)320 'NEXT,321 NEXT:NEXT330 REM340 GOTO 340`

Unfortunately, these changes were not enough.

Can you help?

47 thoughts on “Porting a BBC Micro galaxy program to the CoCo”

1. William Astle

In case it wasn’t clear, RND(-64) does seed the RNG and it does indeed behave the same as RND(0) for return value. So you might still need to seed the RNG to get the pattern you want.

1. Allen Huffman Post author

The sequence of random numbers on different platforms should generate different images, I expect.

In the BBC Micro code, they always use RND(-64) so I would assume that makes it do the same pattern. Assume.

What do you know about LN versus LOG?

1. Sean Patrick Conner

In mathematics, LOG returns the base-10 logarithm of N, such that 10x = N; LN returns base-e (2.718281828) logarithm of N, such that ex = N. Why choosing one over the other depends upon the nature of the math, which is beyond my pay grade.

1. Joel Ewy

According to the docs I’m reading, the notation is a little different in BASIC09. It uses LOG10() for base 10 log and LOG() for “natural log”.

2. jmurphy

“All variables need to be uppercase to work on the CoCo”
Well, yeah, but you can’t just throw away half your variables.
Uppercase T is a distinct variable, separate from lowercase t.
They were two different variables, until you converted them to uppercase.

1. Allen Huffman Post author

Thanks again for catching this. I know nothing about the BBC Micro and it didn’t even occur to me that there could be different variables for upper and lower.

3. Joel Ewy

I saw Carlos’ post as well, and decided to try and port it to BASIC09 for the MM/1, but couldn’t figure out what LNRND did. This post is already at the top of Google for ‘”lnrnd” bbc basic’. I played a bit with the RND(-64) seed on the BBC emulator and can confirm that if you seed it with different values, such as RND(-26), it will create a different image. I’m still working on the code, and haven’t tried to run anything yet, but the remarks that I put in my working copy indicated that I convinced myself from reading documentation that negative values should work the same way in BASIC09 as in BBC BASIC. We’ll see how it turns out. I’m hoping to make it work in 16 colors in BASIC09, and maybe port it back to the CoCo 3.

I’m interested to see what you come up with for ECB.

1. Allen Huffman Post author

I couldn’t get it to work. I want to do a follow up with a working version from FB, and yours if you get it going. A comment yesterday says there were both lowercase and uppercase variables that used the same letter. I took the source and uppercase it first so I had no idea. That is yet another problem with my attempt. Big fail.

4. Jason Pittman

Wow, this is a handful. I played around a little and (maybe) got it a little closer?
– Created LI, LX, LY,etc variables for “lower case I, X, Y, etc:
– Changed the “u” and “v” variables on 50 to the coco screen size (255,191) and removed the “/ 0.4342944819” because I thought that might be there to compensate for the resolution difference
– I think line 60 is supposed to be RND(0)*3

```0 'BBC MICRO - 320X256 4 COLOUR DISPLAY 1 POKE 65395,0 5 PI=3.141592653589793238 11 PMODE 4,1:PCLS:SCREEN 1,1:PMODE 3,1 31 FORG=-64TO400 40 LT=RND(99):LQ=RND(99) 50 LU=RND(255):LV=RND(191) 60 A=RND(0)*3 70 R=90/(1+RND(200)) 81 Q=1+R*(.5+RND(0)/2) 91 LA=1+3*RND(0)^2 100 M=1 111 IFRND(9)<4THENQ=R:LT=0:LQ=0:A=0:M=PI/3:LA=1 121 C=(1+3*RND(0)^2)*R*R 130 FORLI=0TOC 141 S=-(LOG(RND(0))) 150 T=LI*M 161 U=S*R*SIN(T) 171 V=S*Q*COS(T) 180 T=S*A 191 X=U*COS(T)+V*SIN(T) 201 Y=V*COS(T)-U*SIN(T) 210 D=(X*X+Y*Y)/(R*R+Q*Q) 220 Z=99*((2.7^-D)+.1) 231 Z=Z*(RND(0)-.5)^3 241 LY=Y*COS(LT)+Z*SIN(LT) 251 Z=Z*COS(LT)-LY*SIN(LT) 261 LX=LU+X*COS(LQ)+LY*SIN(LQ) 271 LY=LV-X*SIN(LQ)+LY*COS(LQ) 275 IF LX<0 OR LX>255 THEN 321 276 IF LY<0 OR LY>191 THEN 321 281 P=PPOINT(LX,LY)+LA 291 IFP>3THENP=3 301 COLORP 311 PSET(LX,LY) 321 NEXT:NEXT 340 GOTO 340 ```

1. Allen Huffman Post author

I looked up the difference between LOG and LN, and was shown something on how to do it using LOG with a divisor of that weird long value.

Funny, I did another pass at it today and added the “L” to any lowercase value, too.

Line 140 is in question in mine, since I don’t know enough about LN versus LOG and the BBC Micro. Mine still produces an interesting star pattern but not what I expect.

1. Jason Pittman

I honestly don’t know either, but if I run “LN(0.73458)” on the BBC and “LOG(0.734598)” on the CoCo, I get the essentially same result (-0.308456372 vs -0.308456371)

1. Allen Huffman Post author

I keep getting different solutions on how to do this. AI is useless. But this one matches — running it on the BBC emulator:

10 INPUT X
20 PRINT LN(X)
30 PRINT LOG(X)/LOG(2.71828)

I tried .5, and 123, and both line up to the far end decimal places.

We need a smart math person. I have no idea what LN is for.

1. Allen Huffman Post author

In my current redo, I do “31 Z=RND(-42):FORG=-42TO400” so it should do the same RND seed every time it runs. But it doesn’t. It gives a different pattern on two XRoars. I wonder if one of the RNDs is using varaibles that produce a negative seed.

2. Allen Huffman Post author

I just checked BING AI, and it suggests:

(LOG(x)/LOG(10) as a conversion, so…

S=-(LOG(RND(0))/LOG(10))

Maybe? That change immediately started making a “star” pattern.

3. Allen Huffman Post author

I ran this through ALEX EVANS’ basic_util and it turns it in to 6 lines, and runs faster. He has a RE-ID feature that handles renaming long variables, and if it works on upper and lower (treating “i” different than “I”) I bet I could just pass it through that and it would do the work.

1. Jason Pittman

That is awesome! I need to start using it more. Here’s a funky CoCo3 version that is painfully slow but fun to watch. I may just have to leave it running and check on it tomorrow to know if it’s even really working…

```10 PALETTE0,0:PALETTE1,36:PALETTE2,63:PALETTE3,6 20 HSCREEN2:HCLS0 30 PI=3.141592653589793238 40 FORG=RND(-64)TO400 50 LT=RND(99):LQ=RND(99) 60 LU=RND(320):LV=RND(192) 70 A=RND(0)*4 80 R=90/(1+RND(200)) 90 Q=1+R*(.5+RND(0)/2) 100 LA=3*RND(0)^2 110 M=1 120 IF3<4THENQ=R:LT=0:LQ=0:A=0:M=PI/3:LA=1 130 C=(1+3*RND(0)^2)*R*R 140 FORLI=0TOC 150 S=-(LOG(RND(0)) / LOG(10)) 160 T=LI*M 170 U=S*R*SIN(T) 180 V=S*Q*COS(T) 190 T=S*A 200 X=U*COS(T)+V*SIN(T) 210 Y=V*COS(T)-U*SIN(T) 220 D=(X*X+Y*Y)/(R*R+Q*Q) 230 Z=99*((2.7^-D)+.1) 240 Z=Z*((RND(0))-.5)^3 250 LY=Y*COS(LT)+Z*SIN(LT) 260 Z=Z*COS(LT)-Y*SIN(LT) 270 LX=LU+X*COS(LQ)+LY*SIN(LQ) 280 LY=LV-X*SIN(LQ)+LY*COS(LQ) 290 IF LX<0 OR LX>320 THEN 350 300 IF LY<0 OR LY>192 THEN 350 310 P=PPOINT(LX,LY)+LA 320 IFP>3THENP=3 330 HCOLORP 340 HSET(LX,LY) 350 NEXT:NEXT 360 GOTO 360 ```

1. Allen Huffman Post author

The BBC manual I found says MODE 1 is a 320×256 4 color mode. The program checks for >3 so I suppose 0-3. But I don’t see anything that’s not greater. How does your do? I’ll turbo it in Xroar:

1. Jason Pittman

Is there a way to do that with xroar? I knew you could do “–no-throttle” or something like that in mame, but I didn’t see an option for it with xroar.

And I made a mistake in the code I posted on line 120. I’ve got “IF 3<4” from some test I was trying, but it’s supposed to be “IF RND(9) < 4”, so I guess that line is a decision to make something happen 33% of the time. I’m going to let it cook for a while with that corrected and see what it looks like.

2. Allen Huffman Post author

I only learned of that in the past year I think. I’m kickin’ my self because I ran some really long long long programs in that thing!

Also, check the comment from jmurphy. CoCo’s LOG is the BBC’s LN. SO, yeah. We are doing it backwards.

3. Jason Pittman

There’s probably more, but I know I didn’t change PPOINT to HPOINT and I left the “1+” off in “LA = 1+3*RND…”. This looks just like it now. Whoever wrote this wizardry should be doing those “Christmas Challenges”

2. Jason Pittman

There’s a little something off in it, but I can’t even imagine how to troubleshoot “too many stars and not enough nebulas” in this code.

1. Blair Leduc

This should be closer, I corrected all the errors I could see:

5 RGB
10 PALETTE0,0:PALETTE1,36:PALETTE2,54:PALETTE3,63
15 HSCREEN1:HCLS0
20 PI=3.141592653589793238
25 G=RND(-64)
30 FORG=1TO400
40 LT=RND(99):LQ=RND(99)
50 LU=RND(320):LV=RND(192)
60 A=RND(0)3
70 R=90/(1+RND(200))
80 Q=1+R
(.5+RND(0)/2)
90 LA=1+3RND(0)^2
100 M=1
110 IFRND(9)<4THENQ=R:LT=0:LQ=0:A=0:M=PI/3:LA=1
120 C=(1+3
RND(0)^2)RR
130 FORLI=0TOC
140 S=-(LOG(RND(0)) / LOG(2.718281828459045235))
150 T=LIM
160 U=S
RSIN(T)
170 V=S
QCOS(T)
180 T=S
A
190 X=UCOS(T)+VSIN(T)
200 Y=VCOS(T)-USIN(T)
210 D=(XX+YY)/(RR+QQ)
220 Z=99((2.7^-D)+.1)
230 Z=Z
(RND(0)-.5)^3
240 LY=YCOS(LT)+ZSIN(LT)
250 Z=ZCOS(LT)-YSIN(LT)
260 LX=LU+XCOS(LQ)+LYSIN(LQ)
270 LY=LV-XSIN(LQ)+LYCOS(LQ)
272 IF LX320 THEN 350
274 IF LY192 THEN 350
280 P=HPOINT(LX,LY)+LA
320 IFP>3THENP=3
330 HCOLORP
340 HSET(LX,LY)
350 NEXT:NEXT
360 GOTO 360

You should be able to play with line 25 to choose different seeds (something other than -64) to get different images.

Of course, if you are not using an RGB monitor, you will need to change line 5 and 10. The BBC colours are black, red, yellow, and white in mode 1.

Blair.

2. Jason Pittman

Thanks Blair…that’s it! I found a couple of those things, but there was still something wrong that I couldn’t find. It somewhat butchered it when you pasted it so I’ll paste it again in a code block. I think that’s a close as this will get on a CoCo.

```10 RGB 20 PALETTE0,0:PALETTE1,36:PALETTE2,54:PALETTE3,63 30 HSCREEN1:HCLS0 40 PI=3.141592653589793238 50 G=RND(-64) 60 FORG=GTO400 70 LT=RND(99):LQ=RND(99) 80 LU=RND(320):LV=RND(192) 90 A=RND(0)*3 100 R=90/(1+RND(200)) 110 Q=1+R(.5+RND(0)/2) 120 LA=1+3*RND(0)^2 130 M=1 140 IFRND(9)<4THENQ=R:LT=0:LQ=0:A=0:M=PI/3:LA=1 150 C=(1+3*RND(0)^2)*R*R 160 FORLI=0TOC 170 S=-(LOG(RND(0))) 180 T=LI*M 190 U=S*R*SIN(T) 200 V=S*Q*COS(T) 210 T=S*A 220 X=U*COS(T)+V*SIN(T) 230 Y=V*COS(T)-U*SIN(T) 240 D=(X*X+Y*Y)/(R*R+Q*Q) 250 Z=99*((2.7^-D)+.1) 260 Z=Z*(RND(0)-.5)^3 270 LY=Y*COS(LT)+Z*SIN(LT) 280 Z=Z*COS(LT)-Y*SIN(LT) 290 LX=LU+X*COS(LQ)+LY*SIN(LQ) 300 LY=LV-X*SIN(LQ)+LY*COS(LQ) 310 IF LX<0ORLX>320ORLY<0ORLY>192THEN 370 330 P=HPOINT(LX,LY)+LA 340 IFP>3THENP=3 350 HCOLORP 360 HSET(LX,LY) 370 NEXT:NEXT 380 GOTO 380 ```

3. Allen Huffman Post author

Line 60 will have to be FOR G=-64 TO 400 … the negative RND on CoCo does not return the value. On the BBC, if you do:

PRINT RND(-64)
-64

Interestingly different.

5. Blair Leduc

Yes, that’s right and there is no need to start that loop at G since G is not used and it will be a number from 0 to 1 inclusive so you can just hard code it to 0 or 1.

I believe what they were doing here was calling RND to seed, but you always need to assign the results of a function to a variable(?), they did this for code size efficiency.

6. Blair Leduc

I should add that to more accurately emulate the beeb, line 80 should use 1280 and 768. Then lines 290-300 should divide by 4 to get LX and LY into the correct range.

7. Blair Leduc

Yes, making the changes above I suggested does produce a nicer output. Also, I found -12 is a good seed on the CoCo 3.

And the “G” loop sets the number of objects to plot, so if you find that the image is too crowded, reduce that count.

Blair.

8. Blair Leduc

Yes, Allen, the value of the G variable is not used in the calculation. That loop controls the number of objects drawn so the start and end value do not matter. Set the number of times the loop repeats to something that is ascetically pleasing.

9. Blair Leduc

Hmm, I can’t let this go. You can also remove line:
280 Z=ZCOS(LT)-YSIN(LT)

It is unnecessary and you will get a small performance improvement. This bug(?) exists in the original program as well.

Blair.

1. Jason Pittman

Wow, you’re right. Removing that line doesn’t make any difference in the output at all. Here’s an attempt at a 16 color version:

```10 RGB 20 TM=TIMER 30 FORI=0TO15:READZ:PALETTE I,Z:NEXT 40 HSCREEN2:HCLS0 50 PI=3.141592653589793238 60 FORG=1TO400 70 LT=RND(99):LQ=RND(99) 80 LU=RND(320):LV=RND(192) 90 A=RND(0)*3 100 R=90/(1+RND(200)) 110 Q=1+R(.5+RND(0)/2) 120 LA=1+3*RND(0)^2 130 M=1 140 IFRND(9)<4THENQ=R:LT=0:LQ=0:A=0:M=PI/3:LA=1 150 C=(1+3*RND(0)^2)*R*R 160 FORLI=0TOC 170 S=-(LOG(RND(0))) 180 T=LI*M 190 U=S*R*SIN(T) 200 V=S*Q*COS(T) 210 T=S*A 220 X=U*COS(T)+V*SIN(T) 230 Y=V*COS(T)-U*SIN(T) 240 D=(X*X+Y*Y)/(R*R+Q*Q) 250 Z=99*((2.7^-D)+.1) 260 Z=Z*(RND(0)-.5)^3 270 LY=Y*COS(LT)+Z*SIN(LT) 280 LX=LU+X*COS(LQ)+LY*SIN(LQ) 290 IF LX<0ORLX>320THEN 370 300 LY=LV-X*SIN(LQ)+LY*COS(LQ) 310 IF LY<0ORLY>192THEN 370 320 P=HPOINT(LX,LY)+LA 330 P=15*((P-1)/2)+1 340 IFP>15THENP=15 350 HCOLORP 360 HSET(LX,LY) 370 NEXT:NEXT 380 HCOLOR1:HPRINT (2,22),"Done! Time=" + STR\$(TIMER-TM) 390 GOTO 390 400 DATA 0,40,47,45,41,61,18,26,50,58,62,38,33,37,36,63 ```

10. James Jones

Wow. No wonder it takes so long. Lots of redundant calculation, especially of SIN() and COS(). It’s doing a lot of rotating of vectors; in a BASIC09 version I’d make that its own routine, with the 2D rotation matrix (cos θ, sin θ on first row, -sin θ, cos θ on the second) a parameter to avoid recalculation. Do strength reduction to get the multiples of M. You’d want to trace back to whatever the original program was based on to have a chance of giving the variables meaningful names.

1. Allen Huffman Post author

BASIC09 has all the stuff that would take to do this, doesn’t it? Natural log, sin, cos, set pixel of color… hmm this uses POINT to peek a pixel color. Not sure about that one.

This site uses Akismet to reduce spam. Learn how your comment data is processed.