New community dev tool uploaded: alpha xForth Compiler

All aspects of programming on the Commander X16.
BruceMcF
Posts: 1336
Joined: Fri Jul 03, 2020 4:27 am

New community dev tool uploaded: alpha xForth Compiler

Post by BruceMcF »




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.






 
BruceMcF
Posts: 1336
Joined: Fri Jul 03, 2020 4:27 am

New community dev tool uploaded: alpha xForth Compiler

Post by BruceMcF »


Assembly source of the above, acme assembler format.

 


EFC02_DT.ASM
BruceMcF
Posts: 1336
Joined: Fri Jul 03, 2020 4:27 am

New community dev tool uploaded: alpha xForth Compiler

Post by BruceMcF »


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.

BruceMcF
Posts: 1336
Joined: Fri Jul 03, 2020 4:27 am

New community dev tool uploaded: alpha xForth Compiler

Post by BruceMcF »


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.

User avatar
desertfish
Posts: 1097
Joined: Tue Aug 25, 2020 8:27 pm
Location: Netherlands

New community dev tool uploaded: alpha xForth Compiler

Post by desertfish »


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?

BruceMcF
Posts: 1336
Joined: Fri Jul 03, 2020 4:27 am

New community dev tool uploaded: alpha xForth Compiler

Post by BruceMcF »



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.

 

BruceMcF
Posts: 1336
Joined: Fri Jul 03, 2020 4:27 am

New community dev tool uploaded: alpha xForth Compiler

Post by BruceMcF »



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.

BruceMcF
Posts: 1336
Joined: Fri Jul 03, 2020 4:27 am

New community dev tool uploaded: alpha xForth Compiler

Post by BruceMcF »


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.

rje
Posts: 1263
Joined: Mon Apr 27, 2020 10:00 pm
Location: Dallas Area

New community dev tool uploaded: alpha xForth Compiler

Post by rje »


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

BruceMcF
Posts: 1336
Joined: Fri Jul 03, 2020 4:27 am

New community dev tool uploaded: alpha xForth Compiler

Post by BruceMcF »


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

Post Reply