The real power of [ and ]
- GARTHWILSON
- Forum Moderator
- Posts: 8775
- Joined: 30 Aug 2002
- Location: Southern California
- Contact:
Re: The real power of [ and ]
I'm used to DOES> being for defining the child words' runtime behavior in Forth, and ;CODE being for defining their behavior in assembly language.
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
Re: The real power of [ and ]
Exactly! That is why I was trying to figure out IamRob's logic.
- GARTHWILSON
- Forum Moderator
- Posts: 8775
- Joined: 30 Aug 2002
- Location: Southern California
- Contact:
Re: The real power of [ and ]
JimBoyd wrote:
GARTHWILSON wrote:
You'll always have a column of mnemonics
Code: Select all
CODE BOUNDS ( N1 N2 -- N1+N2 N1 )
CLC,
0 ,X LDA, 2 ,X ADC, 0 ,X STA,
1 ,X LDA, 3 ,X ADC, 1 ,X STA,
' SWAP @ JMP, END-CODECode: Select all
CODE FOOBAR2 ( a b -- c )
<operand> MNE
<operand> MNE
BEGIN
<operand> MNE
<long operand calculation messes stuff up> MNE
MNE \ (with no operand)
Z_flag_set
IF
<do stuff>
THEN
CS
UNTIL
NEXT JMP
END-CODEThe line with a mnemonic (like TAY) could be pushed way out under the previous line's mnemonic, but then the left margin's indentation no longer shows the structures.
However, individuality and being free-thinkers is part of being a Forth enthusiast. We do things our own way. The fact that Forth allows it is one of its strengths. It's also one of the things that makes others turn their noses up at it, but we don't care, because another of Forth's strengths is that we can carry on without the support of an industry.
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
Re: The real power of [ and ]
IamRob wrote:
I created a Forth word call BLOAD. Which can load a binary file to a buffer. All we have to do is designate where that buffer is.
Quote:
If TEST is the latest word in the CURRENT vocabulary when it is executed, it will change it's own code field so it is no longer a colon definition. it's code field will point to the code following (;CODE) ( or DOES ) in it's body.
I agree that [ and ] are handy, when defining a colon definition. When assembling a code word or the assembly part of a CREATE ;CODE word, the state is interpret so they aren't even needed.
Does ProForth's ;CODE work differently because there is no Forth assembler?
I agree that [ and ] are handy, when defining a colon definition. When assembling a code word or the assembly part of a CREATE ;CODE word, the state is interpret so they aren't even needed.
Does ProForth's ;CODE work differently because there is no Forth assembler?
As far as I can see, TEST is still a colon word. The CFA points to indirect address for DOCOLON and the indirect address for (;CODE) is in the PFA that ends the interpretation of indirect addresses and starts the execution of ML code, and as far as I can see, nothing points to the ML code that follows (;CODE).
Re: The real power of [ and ]
IamRob wrote:
The Forth assembler is in a new vocabulary, correct? I have the ability to create new vocabularies, but I have not tested that yet. I am still working in the Forth Vocabulary.
Yes, it can be but not necessarily. Fleet Forth's assembler, as well as that of 64Forth and Blazin' Forth, is in a vocabulary named, appropriately enough, ASSEMBLER . If memory serves, Garth's Forth assembler is not in it's own vocabulary. His instruction names avoid redefining other Forth words.
Quote:
As far as I can see, TEST is still a colon word. The CFA points to indirect address for DOCOLON and the indirect address for (;CODE) is in the PFA that ends the interpretation of indirect addresses and starts the execution of ML code, and as far as I can see, nothing points to the ML code that follows (;CODE).
TEST is a colon word. It stops execution at (;CODE) . (;CODE) pulls the address of the following location from the return stack and stores it in the code field of the latest word defined ( at least in Forth-83 but I believe FIG Forth is similar). When (;CODE) exits, it returns control to the word that called TEST .
When TEST is executed, it patches the code field of the latest word and exits. If TEST is the latest word, it patches it's own code field so the next time it is executed, it performs the assembly code after (;CODE) in the body of TEST .
I ran some tests with ;CODE on three systems: 64Forth, Fleet Forth, and Gforth. On all three systems, when ;CODE executes ( it is immediate) the state is changed to interpreting so the leading [ is not needed.
Like Garth, I too am used to DOES> being for defining the child words' runtime behavior in Forth, and ;CODE for defining it in assembly language.
From my Forth-83 system:
Code: Select all
: DEFER ( -- )
CREATE ['] -SET ,
;CODE
2 # LDY,
W )Y LDA, PHA, INY, W )Y LDA,
0 # LDY,
W 1+ STA, PLA, W STA,
W 1- JMP,
END-CODE
The Forth assembler used for the assembly code between ;CODE and END-CODE does not have to be RPN. Fleet Forth's is, but that's my preference.
I have not seen any other topics specifically about ;CODE but there is information on CREATE DOES> here and here.
Re: The real power of [ and ]
GARTHWILSON wrote:
The RPN thing becomes more of a problem when you have for example FOOBAR1 6 + @ E0 AND # LDA or even longer, pushing that LDA way out to the right. If you're trying to vertically align mnemonics for visual factoring, you have to push them all way out. If the section is already indented for a loop, or nested structures, it gets worse. So you have things like (where "MNE" stands for "mnemonic"):
Code: Select all
CODE FOOBAR2 ( a b -- c )
<operand> MNE
<operand> MNE
BEGIN
<operands MNE
<long operand calculation messes stuff up> MNE
MNE \ (with no operand)
Z_flag_set
IF
<do stuff>
THEN
CS
UNTIL
NEXT JMP
END-CODEThe line with a mnemonic (like TAY) could be pushed way out under the previous line's mnemonic, but then the left margin's indentation no longer shows the structures.
Like this code fragment?
Code: Select all
BEGIN,
0 # LDY,
N )Y LDA, TAX, INY,
N )Y LDA,
0= NOT WHILE,
N 1+ STA, N STX, INY,
N )Y LDA,
SBIT 1F OR # AND,
N 2+ )Y EOR,
CS-DUP 0= UNTIL, // CONTINUE
BEGIN,
INY,
N 2+ )Y LDA,
N )Y EOR,
0= NOT UNTIL,
7F # AND,
0= UNTIL,
Or this one without control flow structures?
Code: Select all
HSUBR WARM
FF # LDX, TXS,
'THERE USER.DATA - 1- # LDY,
(WARM) JSR,
>FORTH
FORTH DEFINITIONS
." WARM START"
ABORT ; -2 ALLOT
Even though the mnemonics don't line up and there is that transition to high level, I don't have a problem with it. I suppose that could be because I had very little assembly language experience before finding out about Forth. The first few Forths I've used had RPN assemblers and that is what I am comfortable with. Win32Forth's assembler could be set for either RPN or non RPN mode, if memory serves. It's been a while since I've done anything on Windows.
Quote:
However, individuality and being free-thinkers is part of being a Forth enthusiast. We do things our own way. The fact that Forth allows it is one of its strengths. It's also one of the things that makes others turn their noses up at it, but we don't care, because another of Forth's strengths is that we can carry on without the support of an industry.
I couldn't have said it better.
Re: The real power of [ and ]
JimBoyd wrote:
TEST is a colon word. It stops execution at (;CODE) . (;CODE) pulls the address of the following location from the return stack and stores it in the code field of the latest word defined ( at least in Forth-83 but I believe FIG Forth is similar). When (;CODE) exits, it returns control to the word that called TEST .
When TEST is executed, it patches the code field of the latest word and exits. If TEST is the latest word, it patches it's own code field so the next time it is executed, it performs the assembly code after (;CODE) in the body of TEST .
When TEST is executed, it patches the code field of the latest word and exits. If TEST is the latest word, it patches it's own code field so the next time it is executed, it performs the assembly code after (;CODE) in the body of TEST .
When TEST is compiled using ;CODE, the CFA holds the address to start indirect interpretation. The PFA is an indirect address that calls a routine that basically does a JSR to the code that follows the PFA, then upon return of executing the ML code, control is returned to the routine at the PFA address. Which in turn pops the TEST address off the stack and continues on with the next word that follows [b[]TESTr one could say, control returns to the word that called [b[]TEST. But nothing gets patched that I can see.
Re: The real power of [ and ]
JimBoyd wrote:
Exactly! That is why I was trying to figure out IamRob's logic.
If I am not mistaken, only the Forth vocabulary can be saved as part of the Forth system. and the Assembler and Editor have to be re-compiled every time Forth is started for them to be in their own Vocabularies.
But if my logic is correct in what I believe can be done, then one can load the entire Assembler or Editor into their own vocabularies without having to compile them. Not only would this be a time saver, but also a space saver as the binary of the compiled vocabularies will be much smaller than the text one typed in to create the Assembler or Editor.
And looking down the road, when applications or games are created, the Forth system is always part of every program. This takes up a lot of extra space having duplicate Forth systems. But what if one only wants one Forth system on their hard drive, but has the ability to load utilities like the Assembler and Editor, games, applications, ...etc from just that one Forth system?
Re: The real power of [ and ]
GARTHWILSON wrote:
I'm used to DOES> being for defining the child words' runtime behavior in Forth, and ;CODE being for defining their behavior in assembly language.
The diversity of Forth seems to be endless.
Re: The real power of [ and ]
SamCoVT wrote:
IamRob wrote:
Currently I am writing some ProDOS commands for ProForth on the Apple II.
Code: Select all
S" AFILE.FS" INCLUDEDCode: Select all
INCLUDE AFILE.FShttps://github.com/aik/SLOF/blob/master ... jhBLqZ8Yzs
Re: The real power of [ and ]
IamRob wrote:
On Facebooks Programming Language 21st Century someone posted this link for accessing FAT16 files in Forth.
Back to your original topic (what?? that never happens....), I use [ and ] to compute things while compiling (as mentioned already by Garth). I do cycle testing on words as part of TaliForth2's test suite and use the following code. This test code predates Tali2 having an assembler, so you also get to see the pretendo-assembler using C, (note that Tali is an STC Forth, so all words are essentially CODE words and nothing special needs to be done to switch between assembly and regular Forth code):
Code: Select all
hex
\ The location of the result
F008 constant cycles
\ direct byte compiled
\ lda $f006
\ lda $f007
: cycles_overhead [ AD c, 06 c, F0 c, AD c, 07 c, F0 c, ] cycles 2@ ;
\ direct byte compiled
\ lda $F006
\ jsr (xt on stack goes here)
\ lda $f007
\ then forth code to fetch and print results.
: cycle_test_runtime
[ AD c, 06 c, F0 c, \ lda $F006
20 c, 0000 , \ jsr (address to be filled in)
AD c, 07 c, F0 c, ] \ lda $F007
cycles 2@ \ fetch result
cycles_overhead d- \ subtract overhead
." CYCLES: " 6 ud.r \ print results
;
\ cycle_test updates the address of the given xt in cycle_test_runtime
\ then it runs the test.
\ To test a word, put any arguments it needs on the stack, use tick
\ (') on the word to get it's execution token (xt) and then put
\ cycle_test, then any stack cleanup.
\ eg. 5 ' dup cycle_test 2drop
: cycle_test ( xt -- )
[ ' cycle_test_runtime 4 + ] literal ! cycle_test_runtime ;
Re: The real power of [ and ]
IamRob wrote:
In ProForth, there is no patching when using ;CODE. Are you confusing it with DOES>?
I haven't confused anything.
You said the original ProForth followed the fig Forth standard. Here is the definition of ;CODE from 64Forth, a superset of fig Forth
Code: Select all
: ;CODE
?CSP COMPILE (;CODE)
[COMPILE] [ SMUDGE ; IMMEDIATE
Here is the definition of (;CODE)
Code: Select all
: (;CODE)
R> LATEST PFA CFA ! ;
These definitions are also in agreement with the information in the publication "fig-FORTH 6502 ASSEMBLY SOURCE LISTING" Release 1.1 .
As you can see, ;CODE is immediate and compiles (;CODE) into the definition of the word being compiled. When this word is later executed, an address is removed from the return stack by R> and stored in the code field of the latest word by
Code: Select all
LATEST PFA CFA !
Perhaps it is ProForth that is different.
Forth-79 and Forth-83 are similar. DOES> works differently than in fig-Forth. This excerpt is from the book "All About Forth" :
Quote:
Comment: This implementation uses the Forth-79 CREATE ...
DOES> technique which differs internally from fig-forth's
now obsolete <BUILDS ... DOES> . DOES> is immediate. it
compiles <;CODE> and a machine language call to the low level
routine labeled "DODOES". By embedding that one machine
instruction in the body of the defining word, two bytes are saved
from every one of its generated offspring.
DOES> technique which differs internally from fig-forth's
now obsolete <BUILDS ... DOES> . DOES> is immediate. it
compiles <;CODE> and a machine language call to the low level
routine labeled "DODOES". By embedding that one machine
instruction in the body of the defining word, two bytes are saved
from every one of its generated offspring.
MVP-FORTH uses < and > instead of parenthesis in names to avoid problems with comments.
Here are two excerpts from the Forth-79 Standard. The first is from the assembler word set.
Quote:
;CODE C,I,206
Used in the form:
: <name> . . . ;code
Stop compilation and terminate a defining word <name>. ASSEMBLER becomes
the CONTEXT vocabulary. When <name> is executed in the form:
<name> <namex>
to define the new <namex>, the execution address of <namex> will contain
the address of the code sequence following the ;CODE in <name>. Execution
of any <namex> will cause this machine code sequence to be executed.
"semi-colon-code"
Used in the form:
: <name> . . . ;code
Stop compilation and terminate a defining word <name>. ASSEMBLER becomes
the CONTEXT vocabulary. When <name> is executed in the form:
<name> <namex>
to define the new <namex>, the execution address of <namex> will contain
the address of the code sequence following the ;CODE in <name>. Execution
of any <namex> will cause this machine code sequence to be executed.
"semi-colon-code"
and this is from the required word set.
Quote:
DOES> I,C,168
Define the run-time action of a word created by a high-level defining
word. Used in the form:
: <name> . . . CREATE . . . DOES> . . . ;
and then <name> <namex>
Marks the termination of the defining part of the defining word <name> and
begins the definition of the run time action for words that will later be
defined by <name>. On execution of <namex> the sequence of words between
DOES> and ; will be executed, with the address of <namex>'s parameter
field on the stack. "does"
Define the run-time action of a word created by a high-level defining
word. Used in the form:
: <name> . . . CREATE . . . DOES> . . . ;
and then <name> <namex>
Marks the termination of the defining part of the defining word <name> and
begins the definition of the run time action for words that will later be
defined by <name>. On execution of <namex> the sequence of words between
DOES> and ; will be executed, with the address of <namex>'s parameter
field on the stack. "does"
The following excerpts are from the Forth-83 standard, the one my own Fleet Forth follows. From the assembler extension word set.
Quote:
;CODE -- C,I,79 "semi-colon-
sys1 -- sys2 (compiling) code"
Used in the form:
: <namex> ... <create> ... ;CODE ... END-CODE
_____ ______
Stops compilation, terminates the defining word <namex> and
_____
executes ASSEMBLER. When <namex> is executed in the form:
_____
<namex> <name>
_____ ____
to define the new <name>, the execution address of <name>
____ ____
will contain the address of the code sequence following the
;CODE in <namex>. Execution of any <name> will cause this
_____ ____
machine code sequence to be executed. sys1 is balanced with
its corresponding : . sys2 is balanced with its
corresponding END-CODE . See: CODE DOES>
sys1 -- sys2 (compiling) code"
Used in the form:
: <namex> ... <create> ... ;CODE ... END-CODE
_____ ______
Stops compilation, terminates the defining word <namex> and
_____
executes ASSEMBLER. When <namex> is executed in the form:
_____
<namex> <name>
_____ ____
to define the new <name>, the execution address of <name>
____ ____
will contain the address of the code sequence following the
;CODE in <namex>. Execution of any <name> will cause this
_____ ____
machine code sequence to be executed. sys1 is balanced with
its corresponding : . sys2 is balanced with its
corresponding END-CODE . See: CODE DOES>
and the required wordset.
Quote:
DOES> -- addr C,I,83 "does"
-- (compiling)
Defines the execution-time action of a word created by a
high-level defining word. Used in the form:
: <namex> ... <create> ... DOES> ... ;
and then
<namex> <name>
____
where <create> is CREATE or any user defined word which
executes CREATE .
Marks the termination of the defining part of the defining
word <namex> and then begins the definition of the
execution-time action for words that will later be defined
by <namex>. When <name> is later executed, the address of
____
<name>'s parameter field is placed on the stack and then the
____
sequence of words between DOES> and ; are executed.
-- (compiling)
Defines the execution-time action of a word created by a
high-level defining word. Used in the form:
: <namex> ... <create> ... DOES> ... ;
and then
<namex> <name>
____
where <create> is CREATE or any user defined word which
executes CREATE .
Marks the termination of the defining part of the defining
word <namex> and then begins the definition of the
execution-time action for words that will later be defined
by <namex>. When <name> is later executed, the address of
____
<name>'s parameter field is placed on the stack and then the
____
sequence of words between DOES> and ; are executed.
Re: The real power of [ and ]
IamRob wrote:
If I am not mistaken, only the Forth vocabulary can be saved as part of the Forth system. and the Assembler and Editor have to be re-compiled every time Forth is started for them to be in their own Vocabularies.
You are mistaken.
Even with 64Forth for the C64, a system on a cartridge, there is a word to save the user's dictionary and a word to load a user's dictionary. I can testify from personal experience that all vocabularies and their definitions that are added to the system are saved.
My own Fleet Forth has the command FSAVE to save the Forth system. All vocabularies and their words are saved.
The Forth word FSAVE does not read a name from the text stream. It prompts for a name and reads it in with EXPECT . Here is a session log of saving Forth.
Code: Select all
OK
FSAVE
FILENAME? HMS HEXAPUMA OK
and checking the directory.
Code: Select all
DIR
0 "VICE " 01 2A
81 "HMS HEXAPUMA" PRG
583 BLOCKS FREE. OK
CONSOLE
I intentionally did not call this new system 'FORTH' to show that any filename can be used.
When I built my metacompiler I saved it as a separate Forth system. My metacompiler has seven vocabularies and I'm glad everything in the dictionary gets saved. When I power up the C64, or the VICE simulation of the C64, if I want to build a new Forth system, I insert the disk with the metacompiler and load it like so:
Code: Select all
LOAD"METACOMPILER",8,1
And the entire metacompiler with all seven vocabularies, is loaded into memory.
Re: The real power of [ and ]
JimBoyd wrote:
IamRob wrote:
I created a Forth word call BLOAD. Which can load a binary file to a buffer. All we have to do is designate where that buffer is.
In ProForth one creates a code word like this:
: TEST ;CODE [ HEX 20 C, FC58 , A0 , ... etc ] ;
As you can see, it takes a bit of typing to type in the code by hand. Then it finally dawned on me that since all code between [ and ] are immediately executed I should also be able to use disk commands.
The final result is:
: TEST ;CODE [ HERE BLOAD BINARY.PRGRM ] ;
HERE designates the address after the word TEST and the binary program is loaded right there.
Remember that all words between the brackets are executed immediately (just as if typing at the OK prompt) and does not get compiled into the word definition.
In ProForth one creates a code word like this:
: TEST ;CODE [ HEX 20 C, FC58 , A0 , ... etc ] ;
As you can see, it takes a bit of typing to type in the code by hand. Then it finally dawned on me that since all code between [ and ] are immediately executed I should also be able to use disk commands.
The final result is:
: TEST ;CODE [ HERE BLOAD BINARY.PRGRM ] ;
HERE designates the address after the word TEST and the binary program is loaded right there.
Remember that all words between the brackets are executed immediately (just as if typing at the OK prompt) and does not get compiled into the word definition.
I have looked at ;CODE on three Forth implementations. 64Forth is a superset of FIG Forth for the C64. My Fleet Forth is a Forth-83 Forth for the C64. Gforth is an Ansi Standard Forth. In all three ;CODE compiles a word named (;CODE) on some systems, and DOES on others, and changes state to interpret. When executed, TEST will change the code field of the latest word in the CURRENT vocabulary to point to the code that follows ;CODE. If TEST is the latest word in the CURRENT vocabulary when it is executed, it will change it's own code field so it is no longer a colon definition. it's code field will point to the code following (;CODE) ( or DOES ) in it's body.
I agree that [ and ] are handy, when defining a colon definition. When assembling a code word or the assembly part of a CREATE ;CODE word, the state is interpret so they aren't even needed.
Does ProForth's ;CODE work differently because there is no Forth assembler?
Re: The real power of [ and ]
I realize this thread is a little old.
Getting back to IamRob's original topic, the real power of [ and ] is that you can do just about anything after [ .
Here is an example from my redefinition of Fleet Forth's LOAD .
Note that BRANCH is NOT immediate. It is normally compiled by immediate words such as AHEAD , ELSE , AGAIN , and REPEAT .
Code: Select all
: LINELOAD ( LINE# BLK# -- )
DUP 0=
ABORT" CAN'T LOAD 0"
RB DECIMAL
BLK 2@ 2>R
BLK ! C/L * >IN !
INTERPRET 2R>
BRANCH [ BLK.2! , ] ; -2 ALLOT
: LOAD ( U -- )
0 SWAP BRANCH
[ ' LINELOAD >BODY , ] ; -2 ALLOT
But that's not all. As long as you don't disturb the definition in progress by altering the contents of the data stack (compiler security) or adding to the dictionary (unless what is added is meant to be part of the definition in progress) you can play with the Forth system after [ . Examine the source of words, list words in a vocabulary, crunch some numbers. As long as any compiler security data is as it was and the dictionary is as you intend it for the definition in progress, you can resume where you left off after ] .