Jump to content

Photo

Preview - New integer basic interpreter

basic compiler interpreter

59 replies to this topic

#1 dmsc OFFLINE  

dmsc

    Chopper Commander

  • 249 posts
  • Location:Viņa del Mar, Chile

Posted Wed Jun 28, 2017 11:40 PM

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 #

Attached Files



#2 pirx OFFLINE  

pirx

    Moonsweeper

  • 366 posts
  • Location:Poland

Posted Thu Jun 29, 2017 7:26 AM

WOW!!!



#3 pirx OFFLINE  

pirx

    Moonsweeper

  • 366 posts
  • Location:Poland

Posted Thu Jun 29, 2017 7:36 AM

gtia = $d000

...

dpoke gtia + $02,48 + trackPos / 32

 

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



#4 Island2Live OFFLINE  

Island2Live

    Star Raider

  • 80 posts
  • Location:Germany

Posted Thu Jun 29, 2017 7:49 AM

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

#5 mono OFFLINE  

mono

    Star Raider

  • 70 posts

Posted Thu Jun 29, 2017 8:38 AM

Nice alternative to AtariBASIC :)

What about USR() function?



#6 dmsc OFFLINE  

dmsc

    Chopper Commander

  • Topic Starter
  • 249 posts
  • Location:Viņa del Mar, Chile

Posted Thu Jun 29, 2017 11:05 AM

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.

#7 luckybuck OFFLINE  

luckybuck

    Dragonstomper

  • 696 posts

Posted Thu Jun 29, 2017 11:10 AM

Did you all took a look at OSS Integer Basic including source code and benchmarks:

https://atariwiki.or...S Integer Basic

?



#8 dmsc OFFLINE  

dmsc

    Chopper Commander

  • Topic Starter
  • 249 posts
  • Location:Viņa del Mar, Chile

Posted Thu Jun 29, 2017 11:20 AM

Hi!
 

Did you all took a look at OSS Integer Basic including source code and benchmarks:
https://atariwiki.or...S Integer Basic
?


No, this interpreter is not based on any existing BASIC, it is written from scratch. But I expect my interpreter to be about 5 times faster than the OSS one.

#9 flashjazzcat ONLINE  

flashjazzcat

    Quadrunner

  • 12,580 posts
  • Location:United Kingdom

Posted Thu Jun 29, 2017 11:24 AM

24-bit and 32-bit ints would be very nice (and would make one completely forget about FP). It's an interesting project, for sure. :)

#10 luckybuck OFFLINE  

luckybuck

    Dragonstomper

  • 696 posts

Posted Thu Jun 29, 2017 11:38 AM

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



#11 mono OFFLINE  

mono

    Star Raider

  • 70 posts

Posted Thu Jun 29, 2017 12:01 PM

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, Thu Jun 29, 2017 12:02 PM.


#12 Bryan ONLINE  

Bryan

    Quadrunner

  • 10,591 posts
  • Cruise Elroy = 4DB7
  • Location:Chesaning, MI

Posted Thu Jun 29, 2017 12:13 PM

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.



#13 mono OFFLINE  

mono

    Star Raider

  • 70 posts

Posted Thu Jun 29, 2017 12:56 PM

Do you use RPN for expression parsing and executing?



#14 dmsc OFFLINE  

dmsc

    Chopper Commander

  • Topic Starter
  • 249 posts
  • Location:Viņa del Mar, Chile

Posted Thu Jun 29, 2017 1:39 PM

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.

Attached Files



#15 luckybuck OFFLINE  

luckybuck

    Dragonstomper

  • 696 posts

Posted Thu Jun 29, 2017 1:48 PM

Sorry, did misunderstand, maybe I am too old. ;-)

 

Wow! That sounds fantastic. Will take a closer look at. Thank you very much! :-)))



#16 dmsc OFFLINE  

dmsc

    Chopper Commander

  • Topic Starter
  • 249 posts
  • Location:Viņa del Mar, Chile

Posted Thu Jun 29, 2017 2:10 PM

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.

#17 Bryan ONLINE  

Bryan

    Quadrunner

  • 10,591 posts
  • Cruise Elroy = 4DB7
  • Location:Chesaning, MI

Posted Thu Jun 29, 2017 2:18 PM

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.



#18 mono OFFLINE  

mono

    Star Raider

  • 70 posts

Posted Thu Jun 29, 2017 3:07 PM

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, Thu Jun 29, 2017 3:08 PM.


#19 Philsan ONLINE  

Philsan

    River Patroller

  • 3,364 posts
  • New Orleans Saints Super Bowl XLIV Champions
  • Location:Switzerland

Posted Thu Jun 29, 2017 3:22 PM

Very nice!

 

Are sprites commands out of question?

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



#20 dmsc OFFLINE  

dmsc

    Chopper Commander

  • Topic Starter
  • 249 posts
  • Location:Viņa del Mar, Chile

Posted Thu Jun 29, 2017 7:12 PM

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:
atari002.png

- Use "PAUSE 0" to wait for the VBI (remember that VBI is at VCOUNT = 124)
atari003.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):
atari004.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


#21 Rybags OFFLINE  

Rybags

    Quadrunner

  • 15,106 posts
  • Location:Australia

Posted Thu Jun 29, 2017 10:15 PM

I had a quick look yesterday - looks good.

 

I'm thinking I might try and port one of my games over though the one I most had in mind is very heavy on the FP and would be a big job.



#22 dmsc OFFLINE  

dmsc

    Chopper Commander

  • Topic Starter
  • 249 posts
  • Location:Viņa del Mar, Chile

Posted Thu Jun 29, 2017 10:39 PM

Hi!

I finally uploaded the full sources at https://github.com/dmsc/fastbasic

#23 CharlieChaplin ONLINE  

CharlieChaplin

    River Patroller

  • 2,534 posts

Posted Fri Jun 30, 2017 1:01 AM

But errmmm,

 

Fast Basic is not a good name, since there already exists a Basic with that name for approx. 20 or more years - Fast Basic by Tom Hunt (has no extra commands, it is just about 10%-30% faster than Atari Basic)...



#24 pirx OFFLINE  

pirx

    Moonsweeper

  • 366 posts
  • Location:Poland

Posted Fri Jun 30, 2017 1:58 AM

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, Fri Jun 30, 2017 1:59 AM.


#25 baktra OFFLINE  

baktra

    Moonsweeper

  • 353 posts
  • Location:Czech republic

Posted Fri Jun 30, 2017 3:41 AM

I believe that this project deserves a unique name.

Flying Basic, FlyBasic, DMSC Basic, FI Basic, Jet Basic, Citus Basic...







Also tagged with one or more of these keywords: basic, compiler, interpreter

0 user(s) are browsing this forum

0 members, 0 guests, 0 anonymous users