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?
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.
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?
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.
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”.
“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.
Sweet! Thanks! I just converted the whole thing to uppercase when I brought it over.
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.
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.
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.
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
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.
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)
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.
On BBC, try LOG(123) versus LN(123) and you get very different answers.
Ahhh…you’re right! I think you’ve got it.
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.
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.
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.
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
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:
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.
Oh I see…”Rate Limit.” I hadn’t even noticed that! Now we’re cooking.
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.
Ah, 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.
F12! Hold down to speed, and there is a toggle like Ctrl F12 or something that locks it.
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”
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.
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+3RND(0)^2)RR
130 FORLI=0TOC
140 S=-(LOG(RND(0)) / LOG(2.718281828459045235))
150 T=LIM
160 U=SRSIN(T)
170 V=SQCOS(T)
180 T=SA
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.
What all did you find? This one actually does colors. My efforts have not :(
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
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.
This is really cool.
For the colors to be right, put XRoar in RGB mode (View->TV Input->RGB)
Ahh. I bet that “G=-RND(64)” the CoCo version of what it’s doing?
Looks like RND(negative) seeds and then returns 0-.99999 like RND(0) does.
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.
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.
In the documentation I found, MODE1 was a 320×256 4 color mode. Is there a /4 already done on the Beeb version?
Oops, yes start the loop at -64 as Allen said.
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.
The G loop starts negative — just for a count loop, not using the value?
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.
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.
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
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.
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.
OK… 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