Jump to content
IGNORED

Preview - New integer basic interpreter


Recommended Posts

Hi!

 

Over the last months I have been writing a new Basic interpreter for the Atari 8-bit computers.

 

My goals are:

- Integer variables

- Small size (currently less than 6k)

- Fast execution (currently, about 15% faster than compiled TurboBasicXL in the "sieve.bas" benchmark)

- Modern (no line numbers, many control structures)

- "alike" TruboBasicXL.

 

Attached is my current "beta" release. What is missing is an editor, currently it only runs text programs from disk, and possibly more commands. Also, parsing speed is slow, main reason is that the file is read a line at a time, if the file were read from memory the parser would be a lot faster.

 

I ported one of my ten-liners to the interpreter, and included some demonstration programs.

 

Well, bug reports and new ideas are welcomed :)

 

 

----- cheat sheet -----

All statements and variable names can be lower or uppercase,
the parser is case insensitive.

No line numbers are allowed.

Supported integer operators, in order of precedence:
 +  -           : addition, subtraction
 *  /  MOD      : multiplication, division, modulus
 &  !  EXOR     : binary AND, OR and EXOR
 +  -           : unary "+" and "-"

Supported functions:
 TIME           : time in jiffies
 ABS(N)
 SGN(N)
 PEEK(N)
 PADDLE(N)
 PTRIG(N)
 STICK(N)
 STRIG(N)
 RAND(N)        : random from 0 to N
 DPEEK(N)
 FRE()          : free memory
 ERR()          : last I/O error
 ADR(array)     : address of array in memory
 ADR(string)
 LEN(string)
 VAL(string)
 ASC(string)

Supported boolean operators, in order of precedence:
 OR     : Logical OR
 AND    : Logical AND
 NOT    : Logical NOT
 <= >= <> <  >  =       : Integer comparison

Strings:
 String variables don't need DIM, are of max 255 bytes length.
 String constants can have embedded " by duplicating (" my string ""hello"" ")

Arrays:
 Arrays must be DIM before use.
 After array name, you can specify "WORD" or "BYTE" to give a type.
    DIM Array1(10) Word, SmallArray(32) Byte

Variables:
 Variables are always INT (from -32768 to 32767), must be assigned before use.

Statements:
 '              : Comment (REM)
 .              : Comment (REM)
 ?              : PRINT
 BGET #
 BPUT #
 CLOSE #
 COLOR
 DIM
 DO / LOOP
 DPOKE
 DRAWTO
 END            : Terminates parsing, go to command mode
 EXEC
 EXIT           : Exits from DO, REPEAT, WHILE or FOR
 FILLTO
 FOR / TO / NEXT
 FOR / TO / STEP / NEXT
 GET            : Get from Keyboard
 GET #
 GRAPHICS
 IF / ELSE / ENDIF
 IF / THEN
 INC
 INPUT
 MOVE / -MOVE
 OPEN #
 PAUSE
 PLOT
 POKE
 POSITION
 PRINT
 PROC / ENDPROC
 PUT            : Put to E:
 PUT #
 REPEAT / UNTIL
 SETCOLOR
 SOUND          : With no parameters silences all sounds.
 WHILE / WEND
 XIO #

fastbas.atr

  • Like 23
Link to comment
Share on other sites

This is MOST interesting. :-)

 

By looking over the defined instruction set I don't see anything important missing. What's missing would be a deeper documentation but that's definitely nitpicking at this point. ;-)

 

Oh, one thing: You should set up a little website ... with a donation-button! I would be willing to pay for that! :D

Link to comment
Share on other sites

Hi!

 

gtia = $d000

...

dpoke gtia + $02,48 + trackPos / 32

 

Does it mean unsigned INT is also OK but for calculations?

Yes, you can input any number from -65535 to 65535 (or -$FFFF to $FFFF), but the representation is as signed two complement integers. The only places where this is important is in comparisons (<, >, <= and >=), division and printing.

 

You need to be specially careful with FOR loops, as a simple looking "FOR I=0 TO 40000" won't iterate.

 

 

This is MOST interesting. :-) By looking over the defined instruction set I don't see anything important missing. What's missing would be a deeper documentation but that's definitely nitpicking at this point. ;-) Oh, one thing: You should set up a little website ... with a donation-button! I would be willing to pay for that! :D

About the documentation, that is always a weak spot for me ;) , perhaps the community could help with that.

 

I plan to release the source code after I a little cleanup, probably under a GPL license.

 

Nice alternative to AtariBASIC :)

What about USR() function?

I plan to implement USR() and DATA, but I don't really like "USR" as a function, I probably would implement a "SYSTEM" statement instead.

Link to comment
Share on other sites

I plan to implement USR() and DATA, but I don't really like "USR" as a function, I probably would implement a "SYSTEM" statement instead.

USR as a function can return a result - I think it is very flexible, because you can use it in expressions.

If you decide to implement USR I suggest you to add error support. This is very simple and can acts similar to I/O operation error support. If USR user function has changed error variable with error code (AtariBASIC has it at $B9 ERRCOD and initialize it to 0) it is catched by TRAP (if set, otherwise breaks program with error message). Of course you could return it by ERR() function.

 

This is simplest way to extend language function set.

Edited by mono
Link to comment
Share on other sites

Yes, you can input any number from -65535 to 65535 (or -$FFFF to $FFFF), but the representation is as signed two complement integers. The only places where this is important is in comparisons (<, >, <= and >=), division and printing.

 

You need to be specially careful with FOR loops, as a simple looking "FOR I=0 TO 40000" won't iterate.

 

I'm confused. Do you mean you support 17 bit signed or did you mean -32768 to +32767 because 40000<65535.

Link to comment
Share on other sites

Hi!

 

@dmsc:

No, it is not. It is from OSS. It's the fastest Basic for the Atari, when real numbers are not required. Please see the bechmarks and compare.

Further, I highly recommend to take a look at the source code. Further, a cart has been made of it.

Sorry, I tried to say that *my* interpreter is written from scratch.

 

I tested the "sieve" benchmark, in an NTSC Atari800XL, graphics 0, result is:

- OSS Integer Basic: 7107 jiffies ( 3.64x slower)

- TurboBasicXL: 7939 jiffies (4.07x slower)

- Atari Basic: 24556 jiffjes (12.6x slower)

- FastBasic: 1950 jiffies

 

Note that TurboBasicXL and AtariBasic versions use "POKE/PEEK" instead of an array to store result, because you can't define arrays as big.

 

Attached are the benchmark programs.

sieve-bench.zip

  • Like 1
Link to comment
Share on other sites

USR as a function can return a result - I think it is very flexible, because you can use it in expressions.

If you decide to implement USR I suggest you to add error support. This is very simple and can acts similar to I/O operation error support. If USR user function has changed error variable with error code (AtariBASIC has it at $B9 ERRCOD and initialize it to 0) it is catched by TRAP (if set, otherwise breaks program with error message). Of course you could return it by ERR() function.

 

This is simplest way to extend language function set.

What I dislike about "USR" is that side-effects become visible, you need to specify the order of execution in cases like "POSITION USR(A), USR(B)" or worse "USR(A,USR(B))". All this is not possible with a statement.

 

About errors, currently my interpreter don't have a "TRAP" instructions, you need to manually check "ERR()" after each I/O operation, so certainly you could write a ML routine that writes to the error variable.

 

I tough of adding TRAP, but currently I don't have "GOTO", so it does not feel natural. Also, to properly TRAP you should roll-back the execution stack, and my interpreter does not keeps track of the stack (the parser makes sure that all the exits from control-blocks clear the stack properly).

 

I'm confused. Do you mean you support 17 bit signed or did you mean -32768 to +32767 because 40000<65535.

No, all integers are 16bit, but the parser allows numbers up to 65535. This means that if you write 40000, the parser stores the same number as -25536, so this means that 0 is bigger than 40000 (=-25536). Then, a "FOR I=0 to 40000" does no iterations.

 

Do you use RPN for expression parsing and executing?

The interpreter is a stack-machine, so it is like RPN.

 

The parser is a recursive descent parser with no token recognition (parses directly from input), interpreted using a state-machine. This state-machine is generated from a description file similar to a PEG grammar.

  • Like 2
Link to comment
Share on other sites

No, all integers are 16bit, but the parser allows numbers up to 65535. This means that if you write 40000, the parser stores the same number as -25536, so this means that 0 is bigger than 40000 (=-25536). Then, a "FOR I=0 to 40000" does no iterations.

 

Ah, okay. It's up to the user to handle when something is treated as negative.

Link to comment
Share on other sites

The interpreter is a stack-machine, so it is like RPN.

The parser is a recursive descent parser with no token recognition (parses directly from input), interpreted using a state-machine. This state-machine is generated from a description file similar to a PEG grammar.

 

Great! I think RPN speeds-up execution a little bit :)

 

What I dislike about "USR" is that side-effects become visible, you need to specify the order of execution in cases like "POSITION USR(A), USR(B)" or worse "USR(A,USR(B))". All this is not possible with a statement.

 

Of course, but because USR is a function, the order is specified exactly as for each other built in functions of language. If you can stack it correctly, then it will be work in correct order (as built in functions).

 

About errors, currently my interpreter don't have a "TRAP" instructions, you need to manually check "ERR()" after each I/O operation, so certainly you could write a ML routine that writes to the error variable.

I tough of adding TRAP, but currently I don't have "GOTO", so it does not feel natural. Also, to properly TRAP you should roll-back the execution stack, and my interpreter does not keeps track of the stack (the parser makes sure that all the exits from control-blocks clear the stack properly).

There is no problem there is no TRAP in FB - ERR() function is enough. My suggestion is to publish address of error code variable for USR programmers to enable support of errors returned from user ML code :). AtariBASIC hasn't this functionality.

 

Maybe you would extend your BASIC about TRY/CATCH instead of TRAP ? :)

Edited by mono
Link to comment
Share on other sites

Hi!

 

There is no problem there is no TRAP in FB - ERR() function is enough. My suggestion is to publish address of error code variable for USR programmers to enable support of errors returned from user ML code :). AtariBASIC hasn't this functionality.

 

Maybe you would extend your BASIC about TRY/CATCH instead of TRAP ? :)

Well, try/catch is actually easier than a simple "TRAP", you simply *save* the stack position on "TRY" and restore it in the "CATCH". It get's tricky if you allow nested try/catch, but I can simply ignore that.

 

Very nice!

 

Are sprites commands out of question?

A command to move all sprites at the same time would be useful for multicolor sprites...

As you know, there is no way to move P/M "simultaneously", because you can only write *one* Antic register at a time. The recommended way to do that (even on ML programs) is to wait for VCONT > some line and then write all registers.

 

There are two ways to do that in FastBasic:

- Simply wait for the correct VCOUNT:

post-18634-0-50980400-1498780450.png

 

- Use "PAUSE 0" to wait for the VBI (remember that VBI is at VCOUNT = 124)

post-18634-0-60275000-1498780540.png

 

I don't really like the PMMOVE statement in BasicXE, because it always moves all the PM data, so it's slower than posible. Using "MOVE" and "-MOVE" it's easy to do sprites.

 

This is a P/M testing program that shows that with a simple PROC to move P/M you can have PM support, as you see in the picture there is many frame time still available (purple lines):

post-18634-0-58316500-1498784970.png

 

This is the program source:

' P/M test program
RAMTOP = $6A   : SDMCTL = $22F  : PCOLR0 = $2C0
HPOSP0 = $D000 : GRACTL = $D01D : PMBASE = $D407
' Reserve memory at TOP
MemTop = Peek(RAMTOP) - 4
P0Mem  = $100 * MemTop + $200
oldPos = P0Mem
poke RAMTOP, MemTop

' Activate and configure P/M data
graphics 0
poke P0Mem, 0 : move P0Mem, P0Mem+1, 127 : ' Clears Memory
poke PCOLR0, $1F
poke SDMCTL, Peek(SDMCTL) ! 8
poke PMBASE, MemTop
poke GRACTL, 2

' P/M data and blank as strings
PMdata = 1 + Adr("8DTD8") : PMclear= 1 + Adr("\00\00\00\00\00")
' Initial Conditions
xPos = 6400 : yPos = 2560
xSpd =   64 : ySpd =    0

do
 xPos = xPos + xSpd : yPos = yPos + ySpd
 ySpd = ySpd + 2
 if (ySpd > 0) and (yPos > 12800)
   ySpd = -ySpd
   xSpd = Rand(512) - 256
 endif
 if xSpd > 0
  if xPos > 25600 Then xSpd = -xSpd
 else
  if xPos <  6400 Then xSpd = -xSpd
 endif
 exec MovePm : ' Move P/M Graphics
loop

proc MovePm
 x = xPos / 128 : y = P0Mem + yPos / 128
 poke $D01A,$74 : ' Change background color
 pause 0
 poke HPOSP0, x
 move PMclear, oldPos, 5
 move PMdata,  y,      5
 oldPos = y
endproc
  • Like 1
Link to comment
Share on other sites

I guess the confusion will be absolutely negligible thanks to TBXL that completely overshadowed Tom Hunt's FB that shall be known from now on as the Tom Hunt's Fast Basic :]]]]]

Edited by pirx
  • 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...