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:

Here is the BBC Micro program:

10 MODE1

20 VDU5

30 FORG=RND(-64)TO400

40 t=RND(99):q=RND(99)

50 u=RND(1280):v=RND(1024)

60 A=RND(1)*3

70 R=90/(1+RND(200))

80 Q=1+R*(.5+RND(1)/2)

90 a=1+3*RND(1)^2

100 M=1

110 IFRND(9)<4Q=R:t=0:q=0:A=0:M=PI/3:a=1

120 C=(1+3*RND(1)^2)*R*R

130 FORi=0TOC

140 S=-LNRND(1)

150 T=i*M

160 U=S*R*SINT

170 V=S*Q*COST

180 T=S*A

190 X=U*COST+V*SINT

200 Y=V*COST-U*SINT

210 D=(X*X+Y*Y)/(R*R+Q*Q)

220 Z=99*((2.7^-D)+.1)

230 Z=Z*(RND(1)-.5)^3

240 y=Y*COSt+Z*SINt

250 Z=Z*COSt-Y*SINt

260 x=u+X*COSq+y*SINq

270 y=v-X*SINq+y*COSq

280 P=POINT(x,y)+a

290 IFP>3P=3

300 GCOL0,P

310 PLOT69,x,y

320 NEXT,

330 REM

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

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

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

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

0 'BBC MICRO - 320X256 4 COLOUR DISPLAY

1 POKE 65395,0

5 PI=3.141592653589793238

10 'MODE1

11 PMODE 4,1:PCLS:SCREEN 1,1:PMODE 3,1

20 'VDU5

30 'FORG=RND(-64)TO400

31 FORG=-64TO400

40 T=RND(99):Q=RND(99)

50 U=RND(1280):V=RND(1024)

60 A=RND(1)*3

70 R=90/(1+RND(200))

80 'Q=1+R*(.5+RND(1)/2)

81 Q=1+R*(.5+RND(0)/2)

90 'A=1+3*RND(1)^2

91 A=1+3*RND(0)^2

100 M=1

110 'IFRND(9)<4Q=R:T=0:Q=0:A=0:M=PI/3:A=1

111 IFRND(9)<4THENQ=R:T=0:Q=0:A=0:M=PI/3:A=1

120 'C=(1+3*RND(1)^2)*R*R

121 C=(1+3*RND(0)^2)*R*R

130 FORI=0TOC

140 'S=-LNRND(1)

141 S=-(LOG(RND(0)) / 0.4342944819)

150 T=I*M

160 'U=S*R*SINT

161 U=S*R*SIN(T)

170 'V=S*Q*COST

171 V=S*Q*COS(T)

180 T=S*A

190 'X=U*COST+V*SINT

191 X=U*COS(T)+V*SIN(T)

200 'Y=V*COST-U*SINT

201 Y=V*COS(T)-U*SIN(T)

210 D=(X*X+Y*Y)/(R*R+Q*Q)

220 Z=99*((2.7^-D)+.1)

230 'Z=Z*(RND(1)-.5)^3

231 Z=Z*(RND(0)-.5)^3

240 'Y=Y*COST+Z*SINT

241 Y=Y*COS(T)+Z*SIN(T)

250 'Z=Z*COST-Y*SINT

251 Z=Z*COS(T)-Y*SIN(T)

260 'X=U+X*COSQ+Y*SINQ

261 X=U+X*COS(Q)+Y*SIN(Q)

270 'Y=V-X*SINQ+Y*COSQ

271 Y=V-X*SIN(Q)+Y*COS(Q)

275 IF X<0 OR X>255 THEN 321

276 IF Y<0 OR Y>191 THEN 321

280 'P=POINT(X,Y)+A

281 P=PPOINT(X,Y)+A

290 'IFP>3P=3

291 IFP>3THENP=3

300 'GCOL0,P

301 COLORP

310 'PLOT69,X,Y

311 PSET(X,Y)

320 'NEXT,

321 NEXT:NEXT

330 REM

340 GOTO 340

Unfortunately, these changes were not enough.

Can you help?

William AstleIn 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.

Allen HuffmanPost authorThe 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?

Sean Patrick ConnerIn mathematics, LOG returns the base-10 logarithm of N, such that 10

x = 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.Joel EwyAccording 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”.

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.

Allen HuffmanPost authorSweet! Thanks! I just converted the whole thing to uppercase when I brought it over.

Allen HuffmanPost authorThanks 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.

Joel EwyI 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.

Allen HuffmanPost authorI 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.

Jason PittmanWow, 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

Allen HuffmanPost authorI 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.

Jason PittmanI 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)

Allen HuffmanPost authorI 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.

Allen HuffmanPost authorOn BBC, try LOG(123) versus LN(123) and you get very different answers.

Jason PittmanAhhh…you’re right! I think you’ve got it.

Allen HuffmanPost authorIn 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.

Allen HuffmanPost authorI 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.

Allen HuffmanPost authorI 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.

Jason PittmanThat 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

Allen HuffmanPost authorThe 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:

Jason PittmanIs 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.

Jason PittmanOh I see…”Rate Limit.” I hadn’t even noticed that! Now we’re cooking.

Allen HuffmanPost authorI 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.

Allen HuffmanPost authorAh, YOU were right — there is only one LN that I can find in the BBC code, and you changed it to LOG, which is correct.

Allen HuffmanPost authorF12! Hold down to speed, and there is a toggle like Ctrl F12 or something that locks it.

Jason PittmanThere’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”

Jason PittmanThere’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.

Blair LeducThis 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(.5+RND(0)/2)70 R=90/(1+RND(200))

80 Q=1+R

90 LA=1+3

RND(0)^2RND(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

RR130 FORLI=0TOC

140 S=-(LOG(RND(0)) / LOG(2.718281828459045235))

150 T=LI

MR160 U=S

SIN(T)Q170 V=S

COS(T)A180 T=S

190 X=U

COS(T)+VSIN(T)200 Y=V

COS(T)-USIN(T)210 D=(X

X+YY)/(RR+QQ)220 Z=99

((2.7^-D)+.1)(RND(0)-.5)^3230 Z=Z

240 LY=Y

COS(LT)+ZSIN(LT)250 Z=Z

COS(LT)-YSIN(LT)260 LX=LU+X

COS(LQ)+LYSIN(LQ)270 LY=LV-X

SIN(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.

Allen HuffmanPost authorWhat all did you find? This one actually does colors. My efforts have not :(

Jason PittmanThanks 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

Allen HuffmanPost authorLine 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.

Allen HuffmanPost authorThis is really cool.

Jason PittmanFor the colors to be right, put XRoar in RGB mode (View->TV Input->RGB)

Jason PittmanAhh. I bet that “G=-RND(64)” the CoCo version of what it’s doing?

Allen HuffmanPost authorLooks like RND(negative) seeds and then returns 0-.99999 like RND(0) does.

Blair LeducYes, 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.

Blair LeducI 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.

Allen HuffmanPost authorIn the documentation I found, MODE1 was a 320×256 4 color mode. Is there a /4 already done on the Beeb version?

Blair LeducOops, yes start the loop at -64 as Allen said.

Blair LeducYes, 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.

Allen HuffmanPost authorThe G loop starts negative — just for a count loop, not using the value?

Blair LeducYes, 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.

Blair LeducHmm, I can’t let this go. You can also remove line:

280 Z=Z

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

Blair.

Jason PittmanWow, 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

James JonesWow. 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.

Allen HuffmanPost authorBASIC09 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.

James JonesOK… I added comments to the YouTube video of the CoCo Nation episode that mentioned this blog entry, giving some more info about ways to cut down the number of trig function invocations even for the M=1 case. https://www.youtube.com/watch?v=kAeTnuJmvjY&t=11632s