Jump to content
BruceMcF

New community dev tool uploaded: alpha xForth Compiler

Recommended Posts

alpha xForth Compiler

View File

NOTE: v0.1.4 seems to have fixed or reduced the severity of the corrupted dictionary in v1.3. Known Bugs: .S may give spurious results. DOES> is broken, do not attempt to use it.

________________________________________________________________

I just uploaded this to see if it runs in the try it now box. It appears to run fine, including the words that operate programmatically based on the width of the display.

With some work with an effective in system Monitor program, it would be possible to save a copy of xForth including words you have defined on the console, but for practical purposes, this still requires a SAVE word to be able to save an image of the file after extending, and an INCLUDE word to load a script from a .SEQ file. Once those are written, I will be able to test to see how close to American National Standards Forth (ANS Forth) compliance this system actually comes.

While it runs, it is not fully exercised, so I would be very surprised if there were not bugs. "Alpha" in the title means that there ARE bugs, though I fixed two substantial ones since the last release. Bug reports & suggested bug fixes both accepted, the first dutifully and the second gleefully.

About upper and lower case: there is NO ASCII CONVERSION in this system. ANS Forth requires that ANS standard words be recognized in upper case, and its up to the individual Forth what they do about lower case. Most modern Forths are not case sensitive. This one is. So if you are in Graphics mode, do EVERYTHING in upper case. If you switch to Upper/Lower case mode, do EVERYTHING in lower case. If you enter the command WORDS to see all of the words in the dictionary, you will note some that look like /FORTH/ which is what I did when eForth had lower case words that were "platform" words that the standard Forth word in upper case was built upon. You will also see /DO/ /?DO/ and /LOOP/ as the "platform" words that DO ?DO and LOOP are built upon.

No conversion also means the the line comment word \ is entered and shown as the English Pound Sterling, aka GBP, sign, _ is backarrow, and ^ is up arrow. On the command line, any character from $00 to space is treated as whitespace, as is any character from $80 to Non-Break-Space ($A0).

For some brief usage examples and pointer to more about the Forth programming language, see the discussion forum posts attached to this upload.


 

  • Like 2

Share this post


Link to post
Share on other sites
Posted (edited)

If you want to start playing around with it, and don't know any Forth, the first few chapters of Leo Brodie's classic "Starting Forth" might work ... and if it doesn't, would make an excellent bug report. Some of the assumptions made in that edition are not applicable (integers are 16bit in xForth, not 32bit), and the editor that Starting Forth refers to involves elements that are not yet in place ... but still, there should be several chapters that will get you started.

If you just want to see it do something, you can define "dots" to print a string of dots. Enter:

: DOTS ( n -- ) 0 ?DO [CHAR] . EMIT LOOP ;

This will define the word DOTS which will take the number already on the stack as the DO loop limit, start the do loop from 0 and then print the "." character n times. You use ?DO because if you enter "0 0 DO [CHAR] . EMIT LOOP" you get a loop that is 64K long ... "?DO" skips the loop if the start and end are the same. Run it with:

25 DOTS

... and you should see a string of 25 dots.

The word "CHAR" will grab the character from the command line when the word is run, while [CHAR] grabs it when the word is being defined. So to repeat any character, you can "feed" the character on the stack using CHAR ...

: REPEATS ( c n -- ) 0 ?DO DUP EMIT LOOP DROP ;

CHAR ] 25 REPEATS

... and you should see a string of 25 ] characters.

_____________________________

To see conditional structures, you can define ON and OFF to set and reset a variable, and .STATUS (read "print status") to check how a variable is set. Variables just leave their own memory address on the stack, and then following words use that memory address to store and retrieve the integer.

"!" is the word to store data at an address, so to set a variable to "ON" (that is, TRUE or all bits set):

: ON ( a -- ) TRUE SWAP ! ;

You have to "swap" the top two items on the stack because ! puts the SECOND item at the address given by the TOP item. To reset a variable to OFF:

: OFF ( a -- ) FALSE SWAP ! ;

To define a variable:

VARIABLE A

And to print the status, if the flag on the top of the stack has any bits set, IF lets execution continue, but if it is 0, IF jumps over the following code to the next ELSE or THEN. ELSE is only reached if IF let execution continue, so it just hops over the following code to the THEN. "@" is the word that grabs the data from the address on the top of stack. ." compiles the text up through the closing " into the word, and prints the text when it executes (if you forget to put the space into the string, the word SPACE can be used to print a space character and avoid your output getting crammed together without spaces):

: .STATUS ( a -- ) @ IF ." ON! " ELSE ." OFF! " THEN ;

Then you can try:

A .STATUS

... because ANS Forth initializes variables when they are created (some 1970s Forths didn't). Then

A ON A .STATUS

A OFF A .STATUS

If you enter "STATUS" without the ".", it won't break anything, the interpreter will just say give a ? mark and the word name to let you know it didn't find the word and couldn't convert it to a number either.

Edited by BruceMcF
  • Like 3

Share this post


Link to post
Share on other sites
Posted (edited)

I will note that one word you will not see in Starting Forth is ">TAB", which goes to the next 8-character tabstop, or does a carriage return ("CR") if it hits the edge of the screen. Note that "DUP" is short for DUPLICATE, it makes a copy of the top of stack value so that I can use up the copy and leave the original in place. Then DROP at then end cleans it up off of the stack. "." prints the number on the top of the stack (and uses it up, so it's gone after . is done).

: TIMES ( N -- ) CR DUP . [CHAR] : EMIT 10 1 DO >TAB DUP I * . LOOP DROP ;

So you can try, 19 TIMES in case you never memorized your multiplication tables through to 20 and need a refresher.

Notice that : . @ ! # * / ( are all defined words in Forth, but any of them can also be used as part of words that you define ... you can even name a word using the expression from the Sunday Funnies !@#$%^& if you want ...  so you cannot smash words together like you can in Basic ... you HAVE TO put a space in between words so the interpreter can tell what you are thinking. If instead of ". LOOP", you type ".LOOP". then the interpreter/compiler will go looking for the word named "dot LOOP". This is why it is conventional to print and read Forth programs in a monospaced font like Courrier.

Edited by BruceMcF

Share this post


Link to post
Share on other sites

I keep hearing that Forth is a language very well suited for tiny 8 bit machines. Why is this? Is it because the language structure/parsing/interpretation is simple? Or is there more to it?    Is it fast to execute perhaps?

Share this post


Link to post
Share on other sites
Posted (edited)
4 hours ago, desertfish said:

I keep hearing that Forth is a language very well suited for tiny 8 bit machines. Why is this? Is it because the language structure/parsing/interpretation is simple? Or is there more to it?    Is it fast to execute perhaps?

Faster to execute than the equivalent Basic, certainly, because the bulk of interpretation overhead is when compiling. Slower than assembler function by function, but more compact than assembler once you get past a certain level of complexity, so if it is running a better algorithm, it can catch up on speed. For the 6502 family in particular, the operations occurring with items at or near the top of the stack avoids the stack frame problem with C or Pascal of needing a function stack bigger than 256 bytes,xwhich is awkward for the 6502. 128-256 bytes is ample for a Forth data stack, so, as in this implementation, the stack can be a pair of byte stacks at $9E00 and $9E80, so, eg, addition is:

PLUS: +code

  LDA SL,X : CLC : ADC SL+1,X : STA SL+1,X : LDA SH,X : ADC SH+1,X : STA SH+1,X : INX : JMP NextStep

Then in a compiled colon definition, the address PLUS in the compiled word will get the top two stack items added.
The other two main concatenative fourth generation languages, lisp and APL can have some of the same benefits, but with lisp coming from academic computer science and Forth coming from scientific instrument control, an efficient Forth tends to execute procedural code faster, while APL is strongest when you can express the problem with matrix and vector operations instead of with procedures.

 

Edited by BruceMcF

Share this post


Link to post
Share on other sites
On 8/29/2020 at 6:26 AM, desertfish said:

I keep hearing that Forth is a language very well suited for tiny 8 bit machines. Why is this? Is it because the language structure/parsing/interpretation is simple? Or is there more to it?    Is it fast to execute perhaps?

In the end, while the overhead of a Basic program is 100%s, the speed overhead a Forth is more like 20%-40%, and well written Forth is more compact.

And like Assembler, Forth gives you as close as possible to total control. And it is much more interactive than Assembler, so you CAN easily write a small word, test it, retest it, test it again to he sure it works, then move on to the next. If you DO write like that, you find most of your bugs early, before it becomes a headache to sort out where the bugs are.

The downside to that is that, like Assembler, it's easy to crash a program with badly written Forth. If you DON'T consistently test as you go, Forth is very unforgiving. "Modern" Forth's have more guardrails attached, but it's still nothing like the guardrails in place in something like Python.

Edited by BruceMcF
  • Like 1

Share this post


Link to post
Share on other sites

I my most recent version, I have fixed the bug in .S ... PICK was implemented incorrectly ... with two mistake that cancelled out when the first PICK was used, as the index used was off by one and there was garbage left on the stack below the answer, and in just a one-off test the index being off by one was not obvious because the garbage made the stack one deeper, but each successive use it was off because of the garbage.

I won't upload the updated version until DOES> is fixed. That is by far the more important of the two bugs.

Share this post


Link to post
Share on other sites

So when I type

:DOTS (N-) 0 ?DO [CHAR] . EMIT LOOP;

It responds with

:DOTS(N-) ?    :DOTS. ?

I think it doesn't understand me.  Do I need spaces in different places?

AH I SEE.  Yes, I needed a space between LOOP and ; .. everywhere really.  This worked:

: DOTS ( N - ) 0 ?DO [CHAR] . EMIT LOOP ;
25 DOTS
	
Edited by rje

Share this post


Link to post
Share on other sites

Yes!
The way Forth works is: get this space delimited token. Is it a word? Do it. Is it a number? Push it onto the stack. Repeat.

Well, that is the interpreter. The compiler is more complex.Get the next token. Is it an immediate word? Do it. Is it a normal word? Compile it. Is it a number? Compile a literal that will be pushed onto the stack.

Everything else is based on what the individual words actually do. The only syntax is the token and the line. And only words that parse text from the input buffer care about the line (because the end of line is when the buffer is empty).

Share this post


Link to post
Share on other sites

A limitation is that you cannot have definitions longer than 79 characters without newlines.

I had:

Quote

: .MATRIX ( R C -- ) SWAP 0 ?DO CR DUP 0 ?DO ." (" J 1 + . ." ," I 1 + . ." ) " LOOP LOOP DROP ; 5 3 .MATRIX

but had to change it to:

Quote

: .M SWAP 0 ?DO CR DUP 0 ?DO ." (" J 1 + . ." ," I 1 + . ." )" LOOP LOOP DROP ;
5 3 .M

But you can have it on several lines:

Quote

: .MATRIX ( R C -- ) SWAP 0 ?DO CR DUP 0 ?DO ." (" J 1 + . ." ," I 1 + . ." ) "
LOOP LOOP DROP ; 5 3 .MATRIX

I pasted in the long lines.

Edited by mobluse

Share this post


Link to post
Share on other sites
3 hours ago, mobluse said:

A limitation is that you cannot have definitions longer than 79 characters without newlines.

Why is that a limitation? Just hit return and keep defining. Because you are not in interpret mode when you do the return, you will not get an "OK" prompt.

Share this post


Link to post
Share on other sites
4 hours ago, mobluse said:

Can you write mutually recursive functions in this Forth? I noticed DEFER, IS, and DEFER@ doesn't exist.

https://rosettacode.org/wiki/Mutual_recursion#Forth

For self-recursion, use RECURSE (and report if it works!)

For mutual recursion, you always COULD do it with variables, ' and EXECUTE ... DEFER IS and DEFER@ are just semantic sugar.
VARIABLE '2NDWORLD 
: 1STWORD ( n1 -- n2 ) ?DUP IF '2NDWORD @EXECUTE THEN ;
: 2NDWORD ( ... -- ... ) ... {whatever 2NDWORD does} ... 1STWORD ;
' 2NDWORD '2NDWORD !

Or, if you prefer:
VARIABLE '1STWORD
: 1STWORD ( n1 -- n2 ) '1STWORD @EXECUTE ;
: 2NDWORD ( ... -- ... ) ... {whatever 2NDWORD does } ... 1STWORD ;
:NONAME ( n1 -- n2 ) ?DUP IF 2NDWORD THEN ; '1STWORD !

Edited by BruceMcF

Share this post


Link to post
Share on other sites
12 minutes ago, BruceMcF said:

Why is that a limitation? Just hit return and keep defining. Because you are not in interpret mode when you do the return, you will not get an "OK" prompt.

It's a limitation since you need to insert line-breaks. The source you paste from doesn't always have line-breaks (CR).

A related topic: In GForth they type "compiled" when they reach new-line (CR) and the definition is not finished, e.g.

Quote

: test 1 2  compiled
3 ;  ok

The "compiled" is useful feedback.

Edited by mobluse

Share this post


Link to post
Share on other sites
2 hours ago, mobluse said:

It's a limitation since you need to insert line-breaks. The source you paste from doesn't always have line-breaks (CR).

A related topic: In GForth they type "compiled" when they reach new-line (CR), e.g.

The "compiled" is useful feedback.

CR is how the CX16 line input mode works. If you are copying and pasting on a 32bit or 64bit system into an emulator of an 8bit system, I guess you are just going to have to get the 32bit or 64bit system to be a bit smarter about how it does things. Might need to copy it into the system text editor, save it in a text format with CR, and INCLUDE it from there.

Mind, xForth is still alpha, including that there is no INCLUDE yet, but that is deferred pending emulator development. Since it is mostly necessary to open the file and made it the default input until EOF, and then make the console the default input again, most of the tools to do it will already be available from the first prototype BLOCK system.

I could also note that copy and pasting from an online source directly into xForth might be putting more faith in an alpha build than is warranted. Copy and pasting line by line and looking for smoke as you do might be a better protocol anyway.

Gforth is a much bigger Forth than xForth, which saves an ongoing log of every line entered if you run it correctly, so it would never have need to edit the text on the screen and run it again. Plus going back, editing it on the screen and hitting return doesn't work on any system that gforth runs on. I've already got OK as a NOP, but I am not going to make a word as potentially useful as "COMPILED" into a NOP too.

xForth is a port of eForth, which is generally used today on smaller microcontrollers, like AVR8's. It IS a lot more space conscious than gforth is. Indeed, 10K is far too big for the final release build of xForth ... once INCLUDE and SAVE4TH are working, some of what is now included in the base image are going to get sent off to a CORE.FS include script to make a less overweight base image size. 7K would be a lot more respectable for an eForth descended Forth.

Edited by BruceMcF

Share this post


Link to post
Share on other sites

Awesome, I've been waiting for a Forth to be available on the x16. Looking forward to finally getting around to learning it.

  • Like 1

Share this post


Link to post
Share on other sites

Excellent. For command line entry, it will already take you through chapters 1 and 2 of Starting Forth.

Starting Forth is (c) Forth Inc., and while they make it freely available at their site, they don't permit free redistribution. So get it online or PDF at:

https://www.forth.com/starting-forth/

Edited by BruceMcF
  • Like 1

Share this post


Link to post
Share on other sites

I've mostly been learning with gforth in the mean time. It seems like eforth (and by extension xforth) doesn't have a greater than word as default, is there any particular reason for that? All the ">" word seems to do is push 7 onto the stack.

Share this post


Link to post
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.

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.


×
×
  • Create New...

Important Information

Please review our Terms of Use