Jump to content
IGNORED

Created English translation of the Extended Turbo Basic (Turex.com) documentation


markmiller

Recommended Posts

I don't know if I've ever used the String$ function in Microsoft Basic II. Maybe because it wasn't all that useful. But it's defined in the manual as:

 

Returns a string composed of a specified number of replications of A$.

Example: X$ = STRING$(100,"A")

 

But what the heck. If we were looking for strict utility we wouldn't be working on 40-year-old hardware. But let's do it one better. Let's pass a couple of parameters. The first will tell it how many repeats. The second tells it how many spaces to put between repeats. "0" equals no space between repeats. "1" is one space, etc. "-1" means to repeat on consecutive lines (as with a string printed without the trailing semicolon).

 

Because we can't pass strings as a parameter, you need to set the string that you want to repeat to REPEAT$. If we could pass a string as a parameter, we could also add a special string character or characters to print between the repeats. What fun that would be. :)

 

REPEAT$="FastBasic"
EXEC STRINGS 4,-1

PROC STRINGS REPS SPCS
SPC$=" "
  IF SPCS=-1
    FOR Y = 1 TO REPS
      ? REPEAT$
    NEXT Y
    EXIT
  ELSE
    FOR Y = 1 TO SPCS
      REPEAT$ =+ " "
    NEXT Y
    FOR X = 1 TO REPS
      ? REPEAT$;
    NEXT X
  ENDIF
ENDPROC

 

Link to comment
Share on other sites

Okay, maybe this is overkill. But I updated the STRINGS procedure (for FastBasic) to take a third parameter. The third parameter signifies the ATASCII code for a single character. It will be added (after any spaces) between repetitions of the main string (REPEAT$). "-1" will add no coded ATASCII character.

 

Again, the first parameter is the number of repetitions of the main string (that you assign to REPEAT$ somewhere in your program...likely just before you call the procedure). The second parameter is how many spaces between repetitions. (Zero will give you no spaces, -1 means to repeat on consecutive lines as if printing without the trailing semicolon.)

 

This is a way around not being able to pass a string as a parameter to a procedure in FastBasic. Most will certainly remember that you can use ASC("string") to get the ATASCII code for whatever character you put inside the quotes. It will only ever give you the code value for the first character of the string in case your string is several characters long.

 

Edited: Added a more aesthetically pleasing padding of spaces (if any) around the ATASCII character (if any).

 

 

REPEAT$="FastBasic"
EXEC STRINGS 40,2,42

PROC STRINGS REPS SPCS CSA
SPC$=" "
  IF SPCS=-1
    REPEAT$=+CHR$(CSA)
    FOR Y = 1 TO REPS
      ? REPEAT$
    NEXT Y
    EXIT
  ELIF CSA<>-1
    FOR Z = 1 TO SPCS
      REPEAT$ =+ " "
    NEXT Z
    REPEAT$ =+ CHR$(CSA)
    FOR W=1 TO SPCS
      REPEAT$ =+ " "
    NEXT W
    FOR V = 1 TO REPS
      ? REPEAT$;
    NEXT V
    EXIT
  ELSE
    FOR Y = 1 TO SPCS
      REPEAT$ =+ " "
    NEXT Y
    REPEAT$=+CHR$(CSA)
    FOR X = 1 TO REPS
      ? REPEAT$;
    NEXT X
  ENDIF
ENDPROC

 

Edited by Brad Nelson
Link to comment
Share on other sites

Here's one more string procedure for FastBasic. I suppose we could create a library of useful commands for the back-in-the-day BASIC programmer.

 

There could have been two procedures: One for converting to lowercase and one for uppercase. But why not combine them?

 

The parameter of 1 will change to UPPERCASE. The parameter of 2 will change to lowercase. 3 will ask ChatGPT to turn it all into gibberish.

 

CASECHG$="?Fast_Basic*"

EXEC CASE 2
?CASECHG$

PROC CASE C
'C=1 CHANGE TO UC : C=2 CHANGE TO LC
DIM UP(LEN(CASECHG$))
IF C=1
  FOR X = 1 TO LEN(CASECHG$)
    IF ASC(CASECHG$[X,1]) <97 OR ASC(CASECHG$[X,1])>122 
      UP(X)=ASC(CASECHG$[X,1])
    ELSE 
      UP(X)=ASC(CASECHG$[X,1])-32
    ENDIF
    REFORM$ =+ CHR$(UP(X))
  NEXT X
ENDIF

IF C=2
  FOR X = 1 TO LEN(CASECHG$)
    IF ASC(CASECHG$[X,1]) <65 OR ASC(CASECHG$[X,1])>90
      UP(X)=ASC(CASECHG$[X,1])
    ELSE 
      UP(X)=ASC(CASECHG$[X,1])+32
    ENDIF
    REFORM$ =+ CHR$(UP(X))
  NEXT X
ENDIF

CASECHG$=REFORM$
ENDPROC

 

Link to comment
Share on other sites

I was looking through some Python string operations and thought, "Hey, why not Title Case?"

 

Fast Basic is fast, but a few of these procedures certainly tax it a bit. Still, it works fairly well. No doubt the code could be optimized. But step one is "Make it work."

 

Now you can pass 3 as the parameter and you get Title Case (1=UPPER CASE, 2 =lower case). Quotations marks and dashes (-) are treated as spaces for purposes of determining initial caps. If you were to insert anything but a space, quotation mark, or dash in front of the initial letter of a word, it won't properly change the case of that word. You can't have everything, I case. But I'm not sure that you'd need more than that. Also,  it does not ignore articles or minor words (a, an, the, in, for, of, and, but, or, etc.). I suppose it could be extended to do so.

 

And as Mark said, these could all be translated into Turbo-BASIC XL easily enough (at least I think "easy" is  the word).

 

CASECHG$="""fast-basic is fast"""

EXEC CASE 3
?CASECHG$

PROC CASE C
'C=1 CHANGE TO UC : C=2 CHANGE TO LC : C=3 CHANGE TO Title Case
DIM UP(LEN(CASECHG$))
IF C=1
  FOR X = 1 TO LEN(CASECHG$)
    IF ASC(CASECHG$[X,1]) <97 OR ASC(CASECHG$[X,1])>122 
      UP(X)=ASC(CASECHG$[X,1])
    ELSE 
      UP(X)=ASC(CASECHG$[X,1])-32
    ENDIF
    REFORM$ =+ CHR$(UP(X))
  NEXT X
ENDIF

IF C=2 OR C=3
  FOR X = 1 TO LEN(CASECHG$)
    IF ASC(CASECHG$[X,1]) <65 OR ASC(CASECHG$[X,1])>90
      UP(X)=ASC(CASECHG$[X,1])
    ELSE 
      UP(X)=ASC(CASECHG$[X,1])+32
    ENDIF
    REFORM$ =+ CHR$(UP(X))
  NEXT X
ENDIF

IF C=3 ' IMPLIMENTATION OF "C=2 OR C=3" PREVIOUSLY CHANGED ALL TO LC TO KEEP MATTERS SIMPLE
REFORM$ = ""
  FOR X = 1 TO LEN(CASECHG$)
    IF X>1 AND (ASC(CASECHG$[X,1]) >96 AND ASC(CASECHG$[X,1]) <123) 'HANDLE ALL BUT 1ST CHAR FOR LC
      IF ASC(CASECHG$[X-1,1]) = 32 OR ASC(CASECHG$[X-1,1]) = 34 OR ASC(CASECHG$[X-1,1]) = 45 'PREV CHAR SPC, ", OR -
        UP(X)=ASC(CASECHG$[X,1])-32
      ELSE
        UP(X)=ASC(CASECHG$[X,1]) ' LEAVE ALONE - CHAR IS NOT ALPHABETIC 
      ENDIF
    ELIF X=1 AND (ASC(CASECHG$[1,1]) >96 AND ASC(CASECHG$[1,1]) <123) 'CONVERT 1ST CHAR IF LC
      UP(X)=ASC(CASECHG$[X,1])-32
    ELSE 
      UP(X)=ASC(CASECHG$[X,1]) ' LEAVE ALONE - 1ST CHAR IS NOT ALPHABETIC
    ENDIF
    REFORM$ =+ CHR$(UP(X))
  NEXT X
ENDIF
CASECHG$=REFORM$
ENDPROC

 

Edited by Brad Nelson
Link to comment
Share on other sites

This is a search-and-replace procedure for FastBasic. Use "1" as the parameter and it will "replace all." Any other value (or no value) will replace just the first instance (if found). If not found, it will leave the string alone. Searches are case-sensitive.

 

I had posted this earlier but found some more bugs. I think it's mostly working now. If you really stress the search-and-replace "all" (for instance, by substituting two "EE's" for one "E" in a long sentence), sometimes (but not always) the very last "E" will be a triple "E." I've found some other repeating-letter anomalies as well. But the single search-and-replace seems (knock on wood) rock sold. Bang around in this and see if you can break it in new and creative ways. :)

 

INPUT "ENTER SOURCE STRING: ";SRC$
INPUT "ENTER SUBSTRING TO FIND: ";FND$
INPUT "REPLACE WITH: ";RPL$
INPUT "REPLACE ALL(1)? ";AMNT

EXEC FIND_REPLACE AMNT
? SRC$

PROC FIND_REPLACE ALL
'EXPECTS 3 STRINGS TO EXIST:
'1) SRC$(THE STRING TO MANIPULATE)
'2) FND$(THE STRING TO FIND)
'3) RPL$(THE STRING TO REPLACE FND$)
'*PARAMETER:(1) REPLACES ALL. ANY OTHER
'VALUE FINDS-AND-REPLACES 1ST INSTANCE ONLY.
LOC1=0:S=1
C=LEN(SRC$):D=LEN(FND$):E=LEN(RPL$)

REPEAT

FOR A = S TO C
 IF SRC$[A,D]=FND$
  LOC1=A
  S=A+E
  EXIT
 ENDIF
NEXT A

IF LOC1=0 THEN EXIT

PART1$=SRC$[1,LOC1-1]
PART2$=SRC$[LOC1+D]
SRC$=""
SRC$=+PART1$:SRC$=+RPL$:SRC$=+PART2$
C=LEN(SRC$)
IF AMNT <> 1 THEN EXIT
UNTIL A>=C-E'-1

ENDPROC

 

Edited by Brad Nelson
Link to comment
Share on other sites

Speaking of speed…

 

I found that in the Altirra emulator under System>Configure System...>Firmware> that you can choose the AltirraOS 3.33 for XL/XE/XEGS as opposed to the standard Atari XL/XE OS ver. 2.

 

I used a "draw a circle" program that I found here. I have a version attached for both Turbo-BASIC XL and FastBasic. The only difference is the timing routines I've inserted for both.

 

The first three listings were run on the Altirra emulator in Windows 10. That last one was run in the Atari800WinPlus emulator.

 

AltirraOS 3.33 for XL/XE/XEGS

Turbo-BASIC XL: 3.2 seconds (drawing a similar circle in Graphics 8  using the built-in CIRCLE command: 0.5 seconds)

FastBasic: 3.12 seconds

FastBasic (run as a compiled XEX): 3.12 seconds

 

Atari XL/XE OS ver. 2

Turbo-BASIC XL: 3.2 seconds

FastBasic: 6.63 seconds

FastBasic (run as a compiled XEX): 6.63 seconds

 

Atari OS Rev 2 (1983) (Atari) (800XL-65XE-130XE)-FASTFP

Turbo-BASIC XL: 3.33 seconds

FastBasic: 3.35 seconds

FastBasic (run as a compiled XEX): 3.35 seconds

 

Atari800WinPlus 4.1 using updated fast ROMS

Turbo-BASIC XL: 3.33

FastBasic: 3.35

FastBasic (run as a compiled XEX): 3.35

 

I guess the story is that if you don't want to mess around with installing new ROMS in Altirra, you can use their built-in AltirraOS 3.33 for XL/XE/XEGS ROMS.

circle4.fba circle4.lst

Edited by Brad Nelson
Link to comment
Share on other sites

Here's the revised search-and-replace procedure for FastBasic. It seems to be working fine now. It was hard to find the bugs because I'd probably asked the routines and loops to do too much.

 

Whether this is useful or not, I don't know. But it's sort of my science fair project. This is the first time (outside of HyperCard) I've written in BASIC without line numbers. I thus relied on using FastBasic's procedures and various loops.

 

And other than the propensity to type "RUN" on the keyboard (I still can't quite buck that habit), this hasn't been a particularly difficult transition. I really do enjoy using Turbo-BASIC XL and will continue to do so. And some skills learned in FastBasic can certainly be transferred to TBXL.

 

I realize that dmsc has set some tight limits on the length of the program. But I would advise him to consider breaking those limits. This is some really good work here and to stop development because of trying to stay within 8k or seems seems unnecessary in this day of emulators and SD cards (or cartridges that can be burned with, I believe, megabytes of ROM).

 

From using it a bit on a truly amateur level (which, I do believe, is something that dmsc particularly wants to facilitate), I have these comments and suggestions.

 

- I really like the method of copying-and-pasting. It's simple and doesn't try to do too much. But it would be nice if there was also a cut-and-paste option. What if (after having marked the line with Control+C as usual) instead of Control+V for copy/paste you hit Option(or Alt)+V for cut-and-paste? That way you wouldn't have to always go back and delete the line(s) you were moving.

 

- It's a small thing, but I would add a little bullet or asterisk at the top (to the left or right of the file name) to mark if there are any unsaved changes to the file.

 

- Include error messages that maybe gave a little more info on what went wrong.

 

- The ability to add strings as parameters to procedures.

 

- Some kind of preferences or batch file where you could, for example, have EFAST load automatically, as well as setting editor colors, margins, key repeat behavior, etc.

 

- When saving, the delete (as opposed to the "backspace") and insert keys don't work for editing the file name. They simply "escape" you out of the save option. It would be useful to be able to use those keys.

 

- auto-indenting of loops?

 

INPUT "ENTER SOURCE STRING: ";SRC$
INPUT "ENTER SUBSTRING TO FIND: ";FND$
INPUT "REPLACE WITH: ";RPL$
INPUT "REPLACE ALL(1)? ";AMNT

EXEC FIND_REPLACE AMNT
? SRC$

PROC FIND_REPLACE ALL
'EXPECTS 3 STRINGS TO EXIST:
'1) SRC$(THE STRING TO MANIPULATE)
'2) FND$(THE STRING TO FIND)
'3) RPL$(THE STRING TO REPLACE FND$)
'*PARAMETER:(1) REPLACES ALL. ANY OTHER
'VALUE FINDS-AND-REPLACES 1ST INSTANCE ONLY.
LOC1=0:S=1:Y=0
C=LEN(SRC$):D=LEN(FND$):E=LEN(RPL$)

REPEAT

FOR A = S TO C
 IF SRC$[A,D]=FND$
  LOC1=A
  S=A+E
  EXIT
 ENDIF
NEXT A

IF LOC1=0 THEN EXIT

PART1$=SRC$[1,A-1]
PART2$=SRC$[A+D]
TEMP=LEN(SRC$):SRC$=""
IF LEN(PART1$) < TEMP
  SRC$=+PART1$:SRC$=+RPL$:SRC$=+PART2$
ELSE
  SRC$=+PART1$
ENDIF
C=LEN(SRC$)
IF AMNT <> 1 THEN EXIT
UNTIL A>=C-E

ENDPROC

 

Edited by Brad Nelson
  • Like 1
Link to comment
Share on other sites

One of the most frustrating problems I'm having with FastBasic is the difficulty in working out parsing errors. Here's an example.

 

FOR ANGLE = 0 TO 6283 STEP 31 ' 6283 IS 2 * PI * 100

    X1 = X + (RADIUS * COS(ANGLE / 100))

    Y1 = Y + (RADIUS * OBLIQUENESS * SIN(ANGLE / 100))

    PLOT X1 / 100, Y1 / 100

NEXT ANGLE

 

I'm playing around with a routine to draw a circle. The above syntax (with the variables, of course, assigned values) works fine in Turbo-BASIC XL.

 

In FastBasic, if I just ask it to print:

 

?(RADIUS * COS(ANGLE / 100))

 

it will do so. But if I try to assign a variable to that:

 

X=(RADIUS * COS(ANGLE / 100))

 

it has a parsing error. I can't find the magic parsing solution. :)

Link to comment
Share on other sites

Hi!

8 hours ago, Brad Nelson said:

One of the most frustrating problems I'm having with FastBasic is the difficulty in working out parsing errors. Here's an example.

 

FOR ANGLE = 0 TO 6283 STEP 31 ' 6283 IS 2 * PI * 100

    X1 = X + (RADIUS * COS(ANGLE / 100))

    Y1 = Y + (RADIUS * OBLIQUENESS * SIN(ANGLE / 100))

    PLOT X1 / 100, Y1 / 100

NEXT ANGLE

 

I'm playing around with a routine to draw a circle. The above syntax (with the variables, of course, assigned values) works fine in Turbo-BASIC XL.

 

In FastBasic, if I just ask it to print:

 

?(RADIUS * COS(ANGLE / 100))

 

it will do so. But if I try to assign a variable to that:

 

X=(RADIUS * COS(ANGLE / 100))

 

it has a parsing error. I can't find the magic parsing solution. :)

This is simple: the expression "RADIUS * COS(ANGLE / 100)" is a floating point expression - because "COS" gives a floating point. And you can't assign a floating point to an integer variable.

 

Also, your program has another bug: the "ANGLE/100" will perform an integer division, as both "ANGLE" and "100" are integers, so it will give incorrect results.

 

This is the fixed program, note the use of "*0.01" instead of "/100" to force floating point and make it faster (multiplication is faster than division):

FOR ANGLE = 0 TO 6283 STEP 31 ' 6283 IS 2 * PI * 100
    X1 = INT(0.01 * (X + RADIUS * COS(ANGLE * 0.01)))
    Y1 = INT(0.01 * (Y + RADIUS * OBLIQUENESS * SIN(ANGLE * 0.01)))
    PLOT X1, Y1
NEXT ANGLE

 

You should use integer variables as much as possible, and try to perform as little FP arithmetic inside loops as possible:

' Pre-multiply values before the loop:
RADX% = 0.01 * RADIUS : RADY% = 0.01 * RADIUS * OBLIQUENESS
XF% = X * 0.01 : YF% = Y * 0.01
' Now, optimized loop:
FOR ANGLE = 0 TO 6283 STEP 31 ' 6283 IS 2 * PI * 100
    ANG% = ANGLE * 0.01
    PLOT INT(XF% + RADX% * COS(ANG%)), INT(YF% + RADY% * SIN(ANG%))
NEXT ANGLE

 

Have Fun!

Edited by dmsc
  • Like 3
Link to comment
Share on other sites

@Brad Nelson - It's the same problem you ran into earlier with assigning a floating-point value to an integer variable. :)

 

Something I immediately noticed with FastBasic is that it reverses the "normal" syntax for "what's an integer" and "what's floating-point." In Microsoft Basic, a variable V is floating-point. If you want an integer variable, you need to use V%. In FastBasic, it's the opposite. A variable V is an integer variable. If you want floating-point, you need to use V%.

  • Like 1
Link to comment
Share on other sites

Thanks for the help, dmsc. It's very much appreciated. My goal is to learn more about FastBasic and I have a vague idea of creating a library of procedures...perhaps including a few graphic primitives. I found a much faster algorithm (code immediately below) for a circle here. I adapted it to FastBasic, including some temporary timing provisions. It works well but has no provision for obliqueness (at least I couldn't figure out a way to add one). And this one is fairly fast at about 0.5 seconds (Graphics 8). I will go with that unless perhaps Mark (please please) can add an obliqueness parameter.

 

TIMER
 GRAPHICS 8
 COLOR 1
 SETCOLOR 2,0,0
 A=160 ' X COORDINATE FOR CENTER
 B=80 ' Y COORDINATE FOR CENTER
 R=60 ' RADIUS
 R2=0 ' ADD OBLIQUE IF POSSIBLE
 PHI=0 ' DEFAULT WAS 0
 Y1=0
 X1=R

EXEC CIRCLE 160,80,60

ELAPSED%=TIME
?"ELAPSED TIME = ";ELAPSED%/60;" SECONDS"

PROC CIRCLE X Y R
WHILE X1>=Y1
 PHIY=PHI+Y1+Y1+1
 PHIXY=PHIY-X1-X1+1
 PLOT A+X1,B+Y1
 PLOT A-X1,B+Y1
 PLOT A+X1,B-Y1
 PLOT A-X1,B-Y1
 PLOT A+Y1,B+X1
 PLOT A-Y1,B+X1
 PLOT A+Y1,B-X1
 PLOT A-Y1,B-X1
 PHI=PHIY
 Y1=Y1+1
 IF ABS(PHIXY)<ABS(PHIY)
  PHI=PHIXY:X1=X1-1
 ENDIF
WEND
ENDPROC

 

The code for a circle that I did find (that included obliqueness) is the one that you fixed. And I'm not married to this code because, even with your fixes, it's much too slow...even for FastBasic.

 

But just for purposes of learning, the integer/floating-point distinction is still besting me with this one. For instance, in your fixed code below I can't assign a floating point number to the variable, OBLIQUENESS. And if I try to make it floating point (OBLIQUENESS%) I get a parsing error when putting it inside the formula (on the second line of the listing below). That's why I had to hard-wire "0.5" into the formula to make it work (to test if the obliqueness still works...it still does).

 

I realize you are not ChatDMSC. And I apologize in advance for my own obliqueness. But I just wondered if you might have an answer for this. Thanks in advance.

 

PROC DRAWCIRCLE X Y RADIUS OBLIQUENESS
RADX% = 0.01 * RADIUS : RADY% = 0.01 * RADIUS * 0.5 ' OBLIQUENESS
XF% = X * 0.01 : YF% = Y * 0.01
FOR ANGLE = 0 TO 628 STEP 1
    ANG% = ANGLE * 0.01
    PLOT INT(XF% + RADX% * COS(ANG%)), INT(YF% + RADY% * SIN(ANG%))
NEXT ANGLE
ENDPROC

 

Edited by Brad Nelson
Link to comment
Share on other sites

Okay, I figured it out...or at least one way to  do it. I simply multiplied the variable, OBLIQUENESS, by 0.01. Then for user input (and this input is just for testing purposes), the range is 1 to 100. (1) gives an almost flat line (but not quite). (100) is unaltered. (0) draws a flat line and (I think) goes into an error state that requires you to use BREAK. Stuff such as that could easily be handled. It's funny because you can put in numbers over 100 (up to about 190) and simply get vertical obliqueness instead of horizontal. Over 200 you start getting some wacky, even beautiful curves.

 

But this routine is quite slow. I really want to use the one posted just above and somehow add the ability to change the obliqueness of the circle (of the ellipse, of course...blah blah blah...if it was somewhat oblique, it wouldn't be a circle anymore).

 

EXEC MAIN

PROC DRAWCIRCLE X Y RADIUS OBLIQUENESS
  RADX% = 0.01 * RADIUS : RADY% = 0.01 * RADIUS * (0.01*OBLIQUENESS)
  XF% = X * 0.01 : YF% = Y * 0.01
  FOR ANGLE = 0 TO 628 STEP 1
    ANG% = ANGLE * 0.01
    PLOT INT(XF% + RADX% * COS(ANG%)), INT(YF% + RADY% * SIN(ANG%))
  NEXT ANGLE
ENDPROC

PROC MAIN
  GRAPHICS 0
  SETCOLOR 2,0,0:COLOR 1
  INPUT  "ENTER OBLIQUENESS (1-100): ";OBLIQUENESS
  GRAPHICS 8+16
  EXEC DRAWCIRCLE 16000, 9600, 8000, OBLIQUENESS
ENDPROC

 

Edited by Brad Nelson
Link to comment
Share on other sites

@Brad Nelson - Looking at your code more closely, I can see you're trying to work around the fact that you're limited to integer parameters for the procedure call. In that case, I find it's easier to just skip the explicit parameters. I reworked your program a little. Hopefully, I didn't mangle your logic. I gave it a test run with obliqueness of 1 (what you were calling 100), and it seemed to work out.

 

GRAPHICS 0
SETCOLOR 2,0,0:COLOR 1
INPUT "INPUT X (1-318) (ACCOUNT FOR RADIUS) ";X%
INPUT "INPUT Y (1-190) (ACCOUNT FOR RADIUS) ";Y%
INPUT "INPUT RADIUS ";RAD%
INPUT "INPUT OBLIQUENESS (0.01-1.0) ";OBLIQUENESS%
GRAPHICS 8+16
EXEC DRAWCIRCLE
END

PROC DRAWCIRCLE
  RADX%=RAD%:RADY%=RAD%*OBLIQUENESS%
  FOR ANGLE=0 TO 628
    ANG%=ANGLE*0.01
    PLOT INT(X%+RADX%*COS(ANG%)),INT(Y%+RADY%*SIN(ANG%))
  NEXT ANGLE
ENDPROC

 

I know basic Trig., but I get lost being asked about something like obliqueness. So, I don't have a better algorithm for you.

Edited by markmiller
Link to comment
Share on other sites

I can see you're trying to work around the fact that you're limited to integer parameters for the procedure call. In that case, I find it's easier to just skip the explicit parameters.

 

Thanks for doing your version, Mark. Yes, not trying to pass obliqueness as a parameter works great. Add that to my requests for improvements to FastBasic: The ability for floating-point parameters. :D

 

What I eventually hope to do (if only for my own uses) is turn these procedures (including the ones I've posted previously...at least the versions that worked) into a library or collection. They would obviously be stripped of all inputs, timing data, etc. etc., but with appropriate comments for the input values expected. Thus the more one can use parameters, perhaps the easier they will be to use.

 

Note (again) none of these al-gore-rhythms for circles is of my own creation. They come from that Atari Archives page. And as Stephen noted, this particular one is much too slow, although that other one I posted (which comes from the same Atari Archives page) is pretty fast. In fact, a similar sized Graphics 8 circle drawn in Turbo-BASIC XL via its built-in CIRCLE command is only slightly faster than the code below (about 0.5 seconds in TBXL compared to about 0.6 for theFastBasic version below).

 

I just haven't been able to find a way to make the code below handle obliqueness. And I'm certainly no trig guru. In fact, quite the opposite. So, the following code is pretty fast but has no built-in means for obliqueness. I've been messing with it but haven't been able to make anything work as I'd like. Sort of stumbling around in the dark. Maybe someone here has an idea.

 

 TIMER
 GRAPHICS 8:COLOR 1:SETCOLOR 2,0,0
 A=160 ' X COORDINATE FOR CENTER
 B=80 ' Y COORDINATE FOR CENTER
 R=60 ' RADIUS
 R2=0 ' ADD OBLIQUE IF POSSIBLE

EXEC CIRCLE 160,80,60

ELAPSED%=TIME
?"ELAPSED TIME = ";ELAPSED%/60;" SECONDS"

PROC CIRCLE A B R
PHI=0:Y1=0:X1=R
WHILE X1>=Y1
 PHIY=PHI+Y1+Y1+1
 PHIXY=PHIY-X1-X1+1
 PLOT A+X1,B+Y1
 PLOT A-X1,B+Y1
 PLOT A+X1,B-Y1
 PLOT A-X1,B-Y1
 PLOT A+Y1,B+X1
 PLOT A-Y1,B+X1
 PLOT A+Y1,B-X1
 PLOT A-Y1,B-X1
 PHI=PHIY
 Y1=Y1+1
 IF ABS(PHIXY)<ABS(PHIY)
  PHI=PHIXY:X1=X1-1
 ENDIF
WEND
ENDPROC

 

Link to comment
Share on other sites

This is one of those times "by hook or by crook" has meaning. Not that I didn't have some vague idea that I had to shrink the x- and expand the y-axis where the points were plotted (or vice versa). But I admit completely stumbling into this solution with about zero understanding of trig (which this method doesn't use anyway, so that was actually a help).

 

I did the same trick as previously to get around the inability to pass floating-point variables as parameters. You can therefore choose obliqueness from 1 to 100 in whole number increments. No trapping is in place yet to handle illegal values.

 

The extra code slows the circle down a bit. But for my purposes...say...inventing the first FastBasic LOGO...it's fine. And no doubt clever people out there can speed it up a bit. And I did certainly take to heart dmsc's advice to do as much math as I could outside the loops. Thank you again for that advice.

 

The obliqueness expands the circle in the direction of the y-axis. It should be simple enough to reverse this behavior with another parameter or using values from the existing one (such as 101-200 to stretch the circle in the x-axis).

 

TIMER
GRAPHICS 8
COLOR 1
SETCOLOR 2,0,0
' A= X COORDINATE FOR CENTER
' B= Y COORDINATE FOR CENTER
' R= RADIUS
' OBL= OBLIQUENESS

EXEC CIRCLE 160,80,60,30
  ELAPSED%=TIME
  ?"ELAPSED TIME = ";ELAPSED%/60;" SECONDS"

PROC CIRCLE A B R OBL

PHI=0:Y1=0:X1=R:H=0
R2%=(.01*OBL)
W=INT(X1*R2%)

WHILE X1>=Y1
 PHIY=PHI+Y1+Y1+1
 PHIXY=PHIY-X1-X1+1
 PLOT A+W,B+Y1
 PLOT A-W,B+Y1
 PLOT A+W,B-Y1
 PLOT A-W,B-Y1
 PLOT A+H,B+X1
 PLOT A-H,B+X1
 PLOT A+H,B-X1
 PLOT A-H,B-X1
 PHI=PHIY
 Y1=Y1+1
 IF ABS(PHIXY)<ABS(PHIY)
  PHI=PHIXY:X1=X1-1
 ENDIF
W=INT(X1*R2%):H=INT(Y1*R2%)
WEND

ENDPROC

 

Edited by Brad Nelson
Link to comment
Share on other sites

@Brad Nelson - I got some ideas for how to speed up your circle+obliqueness algorithm. I noticed the symmetry, and thought I could speed it up by having it do some less expensive calculations, but mostly fewer iterations. I divided the circle into quadrants, and I have each draw a part of the circle "simultaneously."

 

I had to do more trig. to make this work, but I got the time down to a little more than half the time of your algorithm.

 

GRAPHICS 0
SETCOLOR 2,0,0:COLOR 1
INPUT "INPUT X (1-318) (ACCOUNT FOR RADIUS) ";X%
INPUT "INPUT Y (1-190) (ACCOUNT FOR RADIUS) ";Y%
INPUT "INPUT RADIUS ";RAD%
INPUT "INPUT OBLIQUENESS (0.01-2.0) ";OBLIQUENESS%
GR.8+16
EXEC DRAWCIRCLE
END

PROC DRAWCIRCLE
  'HPI="Half of Pi" (90 degrees)
  RADX%=RAD%:RADY%=RAD%*OBLIQUENESS%:HPI%=1.571
  FOR ANGLE=0 TO 157
    ANG%=ANGLE*0.01:BASEX%=RADX%*COS(ANG%):BASEY%=RADY%*SIN(ANG%)
    ROT90X%=RADX%*COS(ANG%+HPI%):ROT90Y%=RADY%*SIN(ANG%+HPI%)
    PL.INT(X%+BASEX%),INT(Y%+BASEY%)
    PL.INT(X%-BASEX%),INT(Y%-BASEY%)
    PL.INT(X%+ROT90X%),INT(Y%+ROT90Y%)
    PL.INT(X%-ROT90X%),INT(Y%-ROT90Y%)
  N. ANGLE
ENDPROC

 

I timed yours and mine in FastBasic. Yours took 86 seconds. By dividing it up into quadrants, I got it down to 44.5 seconds.

 

I had the thought that doing the extra trig. might be slowing it down more than necessary. So, I tried cutting it out, and just drawing the circle in a mirror image (two halves), and that was actually a bit slower, 45.1 seconds. Though, this illustrates how expensive the extra trig. is, because cutting the halves in half again, the way I did it, takes almost as long as doing two halves.

 

Also, I can see now how the obliqueness algorithm works. It adds a "warp" factor to the trig., but only on one of the axes. So, rather than scaling the whole circle, it just scales one of the axes, creating something that looks elliptical.

Edited by markmiller
  • Like 1
Link to comment
Share on other sites

Thanks, Mark. That's pretty cool. In the meantime I was able to add not only obliqueness to that other and faster version (the one that doesn't use trig), but also was able (in a somewhat brute force way) to add the option to stretch the ellipse along either the x- or y-axis. That is listed below. And any ideas for speeding it up will be appreciated. You sound as if you have some great and bold ideas (cutting up a circle...ouch..but it if works!)

 

TIMER
GRAPHICS 8
COLOR 1
SETCOLOR 2,0,0
' A= X COORDINATE FOR CENTER
' B= Y COORDINATE FOR CENTER
' R= RADIUS
' OBL= OBLIQUENESS
'  (1-100 STRETCH ON Y-AXIS, 101-200 ON X-AXIS)

EXEC CIRCLE 160,80,60,150
  ELAPSED%=TIME
  ?"ELAPSED TIME = ";ELAPSED%/60;" SECONDS"

PROC CIRCLE A B R OBL
  IF OBL < 1 OR OBL  > 200 THEN EXIT
  PHI=0:Y1=0:X1=R:H=0
  R2%=(.01*OBL)
  W=INT(X1*R2%)

IF OBL>100 
TEMP1=A:TEMP2=B:A=TEMP2:B=TEMP1
OBL=OBL-100
R2%=(.01*OBL)
W=INT(X1*R2%)

WHILE X1>=Y1
 PHIY=PHI+Y1+Y1+1
 PHIXY=PHIY-X1-X1+1
 PLOT B+Y1,A+W
 PLOT B+Y1,A-W
 PLOT B-Y1,A+W
 PLOT B-Y1,A-W
 PLOT B+X1,A+H
 PLOT B+X1,A-H
 PLOT B-X1,A+H
 PLOT B-X1,A-H
 PHI=PHIY
 Y1=Y1+1
 IF ABS(PHIXY)<ABS(PHIY)
  PHI=PHIXY:X1=X1-1
 ENDIF
W=INT(X1*R2%):H=INT(Y1*R2%)
WEND

ELSE

WHILE X1>=Y1
 PHIY=PHI+Y1+Y1+1
 PHIXY=PHIY-X1-X1+1
 PLOT A+W,B+Y1
 PLOT A-W,B+Y1
 PLOT A+W,B-Y1
 PLOT A-W,B-Y1
 PLOT A+H,B+X1
 PLOT A-H,B+X1
 PLOT A+H,B-X1
 PLOT A-H,B-X1
 PHI=PHIY
 Y1=Y1+1
 IF ABS(PHIXY)<ABS(PHIY)
  PHI=PHIXY:X1=X1-1
 ENDIF
W=INT(X1*R2%):H=INT(Y1*R2%)
WEND
ENDIF

ENDPROC

 

Edited by Brad Nelson
Link to comment
Share on other sites

@Brad Nelson - Okay, my trig. from school is coming back to me. :) I realized there's yet a faster way, by using the fact that cos(a1 + a2) = cos(a1) cos(a2) − sin(a1) sin(a2), and sin(a1 + a2) = sin(a1) cos(a2) + cos(a1) sin(a2).

 

GR.0
SET.2,0,0:C.1
INPUT "INPUT X (1-318) (ACCOUNT FOR RADIUS) ";X%
INPUT "INPUT Y (1-190) (ACCOUNT FOR RADIUS) ";Y%
INPUT "INPUT RADIUS ";RAD%
INPUT "INPUT OBLIQUENESS (0.01-2.0) ";OBLIQUENESS%
GR.8+16
T%=TIME
EXEC DRAWCIRCLE
GR.0
?"TIME: ";(TIME-T%)/60.0;" SEC."
END

PROC DRAWCIRCLE
  RADX%=RAD%:RADY%=RAD%*OBLIQUENESS%:HPI%=1.571
  CHPI%=COS(HPI%):SHPI%=SIN(HPI%)
  FOR ANGLE=0 TO 157
    ANG%=ANGLE*0.01:CANG%=COS(ANG%):SANG%=SIN(ANG%)
    BASEX%=RADX%*CANG%:BASEY%=RADY%*SANG%
    ROT90X%=RADX%*(CANG%*CHPI%-SANG%*SHPI%):ROT90Y%=RADY%*(SANG%*CHPI%+CANG%*SHPI%)
    PL.INT(X%+BASEX%),INT(Y%+BASEY%)
    PL.INT(X%-BASEX%),INT(Y%-BASEY%)
    PL.INT(X%+ROT90X%),INT(Y%+ROT90Y%)
    PL.INT(X%-ROT90X%),INT(Y%-ROT90Y%)
  N. ANGLE
ENDPROC

 

I got the time down to 32.1 seconds, now less than half the original, because I'm doing the same trig. calculations as you did in your original, and getting the rest in less time, because the sin() and cos() for the 2nd angle I'm using is only calculated once, before entering the loop. There's more multiplying inside the loop (expensive, but less expensive than what I had before), but less trig. than I had before, and I'm still doing quadrants.

 

BTW, I tried this in TB, 12.1 seconds.

Edited by markmiller
  • Like 1
Link to comment
Share on other sites

Thanks for the link, Stephen. I had also found similar info here. And at that link you can download the programs and try them out. I tried out the "fastest" one there (Program 7), the "BASIC call to machine language." And once it all gets loaded in, it is indeed fast. The first run is slow as it loads in all that data. But the second run (load in Program 6 for that) was 0.018 seconds in Turbo-BASIC XL. The expected run time listed for it in the program listing is 0.1833. I expect that's the time when run in Atari Basic. I've attached that BASIC file below.

P153P7A.BAS

Edited by Brad Nelson
  • Like 1
  • Thanks 1
Link to comment
Share on other sites

1 hour ago, Brad Nelson said:

Thanks for the link, Stephen. I had also found similar info here. And at that link you can download the programs and try them out. I tried out the "fastest" one there (Program 7), the "BASIC call to machine language." And once it all gets loaded in, it is indeed fast. The first run is slow as it loads in all that data. But the second run (load in Program 6 for that) was 0.018 seconds in Turbo-BASIC XL. The expected run time listed for it in the program listing is 0.1833. I expect that's the time when run in Atari Basic. I've attached that BASIC file below.

P153P7A.BAS 3.42 kB · 1 download

I typed in the program, but there were so many errors in the assembly, I haven't yet been able to debug it.  The errors are all from bad OCR.  I'd love to try it though, I definitely have a use for it in mind.

  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...