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 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?

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.

    Reply
    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?

      Reply
      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.

        Reply
        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”.

          Reply
  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.

    Reply
    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.

      Reply
  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.

    Reply
    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.

      Reply
  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

    Reply
    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.

      Reply
      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)

        Reply
        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.

          Reply
          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.

      Reply
    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.

      Reply
      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

        Reply
        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:

          Reply
          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.

          Reply
          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.

    Reply
  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.

    Reply
  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.

    Reply
  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.

    Reply
  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.

    Reply
    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

      Reply
  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.

    Reply
    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.

      Reply

Leave a Reply

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