I've already mentioned that Fleet Forth's metacompiler has words on the host in a TARGET FORTH vocabulary to serve as handles for the target system's words which are in virtual memory. The 'target words' have a body with three cells. The first cell holds the address of the actual target word in virtual memory. The second cell holds the address of this word's counterpart in the host system to be executed while interpreting. This includes immediate words when compiling. The third cell is for statistical data.
I've rewritten MFIND , the metacompiler's FIND , so that a target word is only found if it is to be compiled. This change will save two bytes (one cell) for each 'target word' in the TARGET FORTH vocabulary.
: MFIND ( ADR1 -- ADR2 F )
COUNT >HERE
TFIND DUP STATE @ =
2PICK ?TW = AND
?DUP ?EXIT
DROP HERE
[META] ['] SHADOW >BODY (FIND)
DUP STATE @ = 0EXIT
2DROP HERE 0 ;
HOST
The string at ADR1 may or may not be at HERE , although it normally is (but not when testing). COUNT >HERE moves the string at ADR1 to HERE to simplify the code. Even if the string is already at HERE , overwriting the string with itself will not be a problem. TFIND only searches the TARGET FORTH vocabulary. The phrase
results in a true flag under two conditions. The state is compiling and the found word is not immediate or the state is interpreting and the word was not found. That phrase followed by this one
will result in a true flag if the word found is to be compiled and it is a 'target word' or the word is to be interpreted and it is not a 'target word'. This flag is ANDed with the flag returned by TFIND .
If the result is a true flag, MFIND exits with the address of the found word and the flag on the stack. If the result is false, the results are dropped and the search resumes with the SHADOW vocabulary and its parent vocabularies, if necessary. At this point any word found is not in the TARGET FORTH vocabulary so the following phrase
will exit if the word is immediate or the state is interpreting, otherwise the results are dropped and the address of the search string and a false flag are placed on the stack.
After preliminary tests of this new version of MFIND , I found it doesn't work in actual use. When assembling, the metacompiler's ASSEMBLER vocabulary doesn't get searched.
Here is a revision which works.
: MFIND ( ADR -- ADR2 F )
CONTEXT @
BEGIN
2DUP VFIND
DUP STATE @ =
2PICK ?TW = AND
DUP 0=
WHILE
2DROP 2+ @ DUP 0=
UNTIL
EXIT
THEN
2NIP ;
This version searches the vocabularies one at a time starting with the CONTEXT vocabulary all the way down to the HOSTFORTH vocabulary. VFIND searches a vocabulary without searching parent vocabularies.
The test
returns a true flag if the found word is a TARGET word. If these two flags are not equal, the flag returned by VFIND is cleared. If they are equal, the flag returned by VFIND is unaffected.
If the resulting flag is false, the search resumes with the parent vocabulary. Each time the search resumes, the old search results are dropped. The search might have been successful, yet this word should not be used if it is a target word which would not be compiled or a non-target word which would be compiled.
: HEADER ( -- )
>IN @ VHEADER >IN !
HEAD ON
CREATE
THERE , 0 ,
[ HERE 2+ >A ]
DOES>
TRUE
ABORT" TARGET WORD EXECUTED" ;
A> CONSTANT TARGET-WORD-CF
Although target words in this version of the metacompiler are not supposed to be executed, the ABORT" is there just in case.
Forth programmers normally prefer minimal error checking. I think a metacompiler should be an exception.
?TCOMP also needs modified to place the statistical data in the correct cell.
: ID. ( NFA -- )
1+
BEGIN
COUNT $7F 2DUP AND QEMIT >
UNTIL
DROP ;
TRACE uses ID. to display a word's name and this version of ID. displays a word's name in place, which leaves PAD and HERE unaffected.
I was able to place a string at pad to test MFIND .
As I mentioned previously, when I added a new CREATE DOES> word to Fleet Forth's kernel it was necessary to add support for it in the metacompiler. Finding a better way to implement CREATE DOES> in the metacompiler is proving to be fiendishly difficult. I may have to settle for how the metacompiler currently handles it.
There is room for improvement in the current method and I will post the results shortly.
I realize it has been a while. I've been busy and couldn't work on this until now.
One deficiency with my metacompiler concerns code words with no body. I use this method in the system loader to create a word with a code field pointing to code elsewhere.
There are two reasons for the difference.
1) The metacompiler's LATEST needed fixed.
2) Variables are defined before CREATE is defined (it's a chicken and egg problem) so all the CFA's for variables are in a chain. When CREATE is defined in the kernel, all the code fields in the chain are resolved to the newly compiled do.variable. The same is done for all colon words, all CONSTANTs, and anything which is not a CODE word. Each CREATE DOES> or CREATE ;CODE defining word has a chain linking the code fields of its child words which gets resolved when that defining word is defined in the kernel.
suppose the variables BLK>IN and CONTEXT are the first three variables to be defined. Here is a rough diagram of what gets compiled. The code fields get resolved when CREATE is defined.
1000: XXXX < Link Field (to previous header)
1002: 3 < Name Field count
1003: B L K < Name Field text
1006: 0 < Code Field (first variable)
1008: ???? < Parameter Field
100A: 1000 < Link Field (to header of BLK)
100C: 3 < Name Field count
100D: > I N < Name Field text
1010: 1006 < Code Field (points to code field of BLK)
1012: ???? < Parameter Field
1014: 100A < Link Field (to header of >IN)
1016: 7 < Name Field count
1017: C O N T E X T < Name Field text
101E: 1010 < Code Field (points to code field of >IN)
1020: ???? < Parameter Field
Using the method shown in example one in the kernel would break the chain for do.create, yet that method seems natural. There is no need to invoke the assembler (meta assembler) since no code is to be assembled.
Here is a prototype form of chaining to allow in the kernel the method shown in example two.
This version of chaining requires two cells. It keeps the address of the latest link and the one prior to that.
If the latest link in the chain is altered, the word MEND-CHAIN used in ADD-CHAIN mends the chain.
The word DUNCE creates variables and chains together their parameter fields for testing purposes.
I neglected to log my test results, but the test was something like this.
0 0 DUMMY 2! OK \ initialize DUMMY variable
DUNCE LARRY OK
DUNCE SHIRLEY OK
SHIRLEY OFF OK \ chain broken
DUNCE CURLY OK \ chain restored -- SHIRLEY removed from chain
DUNCE MOE OK
DUNCE ABBOT OK
DUNCE EDWIN OK
42 EDWIN ! OK \ chain broken
DUNCE COSTELLO OK \ chain restored -- EDWIN removed from chain
TEST
COSTELLO
ABBOT
MOE
CURLY
LARRY OK
At this point COSTELLO is the latest DUNCE created. If I broke the chain at any other DUNCE it would remain broken.
As in the word TEST , the word to resolve the chain would also use MEND-CHAIN to remove the last link if it did not belong.
I know it's been a while. Rest assured that I have not given up on the redesign. I haven't had the large blocks of free time needed to properly redesign the metacompiler's handling of CREATE DOES> .
As an example, here is the source for SARRAY (string array) in the Fleet Forth's kernel.
This would be sufficient if there were no SARRAY child words in the kernel.
The following was added to the metacompiler to support child words of SARRAY in the kernel.
," TOO MANY OPEN FILES"
," FILE ALREADY OPEN"
," FILE NOT OPEN"
," FILE NOT FOUND"
," DEVICE NOT PRESENT"
," NOT INPUT FILE"
," NOT OUTPUT FILE"
," MISSING FILE NAME"
," ILLEGAL DEVICE NUMBER"
2) The child words of SARRAY are not executed while building the kernel.
Here is the source for CONSTANT and VALUE in the kernel.
The two differences with SARRAY are:
1) CONSTANT and VALUE compile the data, it is not added after the fact.
2) The child words of CONSTANT and VALUE can run while the target is being built. Not really, an alias in the shadow vocabulary is created. It is this alias which runs.
The 'system value' LRU and the value #BUF are used to determine the initial value of the 'system value' MRU .
Since MRU gets initialized during cold-start, the ability to run target constants isn't necessary and I've thought about removing the ability to execute them during metacompilation; however, if CREATE DOES> child words can not be executed during metacompilation, that would eliminate the ability to have child words which create other child words in the target. Such words could be added to the target system after it is running. This is not needed in the kernel and I have not used this technique before, but I seem to recall mention of it with object oriented Forth systems.
By the way, I've posted on other subjects recently because they did not require the amount of uninterrupted spare time needed for metacompiler redesign.
I will refer to the metacompiler's version of CREATE DOES> and CREATE ;CODE words as definer words.
I have not found a better way to implement the definer words, so I will use the same idea from my original version of the metacompiler; however, I will try to clean up the code.
Two words from the last post which have not been defined are CF, and DEFINER . HEADER , shown previously, creates a word in the target vocabulary and a header in virtual memory. It does not build the code field.
VARIABLE DEF-LINK DEF-LINK OFF
: CF, ( ADR -- )
DUP @
IF @ V, EXIT THEN
2+ ADD-CHAIN ;
: DEFINER ( CFA -- )
['] ORDER! >BODY >R
ORDER@ META DEFINITIONS
CREATE
, 0 , 0 , 0 ,
DEF-LINK ADD
[ HERE 2+ >A ]
DOES>
DUP 2+ SWAP @ EXECUTE ;
A> CONSTANT DEFINER-WORD
DEFINER is a CREATE DOES> word which defines its child words in the META vocabulary. The body of ORDER! is placed on the return stack so the search and compile orders saved by ORDER@ will be restored when DOES> finishes execution.
The first cell of a definer word is the address of a word to execute to build the header in virtual memory as well as the handle in the TARGET VOCABULARY . This is handled by a separate word because some definer words also perform other actions such as creating an alias in the SHADOW vocabulary or changing STATE .
The second cell serves as a flag and the address of the code to compile in virtual memory for the code field. The value is initially zero and CF, adds the virtual memory version of HERE to a chain of addresses to be resolved when the value for the code field is set.
When the corresponding CREATE DOES> or CREATE ;CODE word is defined in the target's source, this cell is set by the metacompiler version of DOES> or ;CODE and the chain of addresses is resolved so they all point to this value. From this point on, any child word for this definer will have this value compiled for its code field.
The next two cells hold the address for the chain and its previous link. Saving the previous link allows the following usage in the target's source:
Altering LEAVE's CFA breaks the chain; however, the next time a variable is created in the target, or the chain is resolved, the chain is mended. This may not seem like a big deal; however, the former is used in the system loader to remove a dependency on the cell size.
CREATE CS-DUP ( ADR N -- ADR N ADR N )
' 2DUP @ LATEST NAME> !
IMMEDIATE
The final cell is a chain linking all DEFINER words together, similar to how all vocabularies are linked in a chain pointed to by VOC-LINK . This new version of the metacompiler may not need the definer link. If it doesn't, I will remove it.
One of the last actions to perform when metacompiling is to verify that all the definer words were defined in the target's source, or at least to verify that the ones used in the source were defined.
: VERIFY ( -- )
['] ORDER! >BODY >R
ORDER@ META WITH-WORDS
DUP NAME> DUP @ DEFINER-WORD <>
IF 2DROP EXIT THEN
>BODY 2+ @
IF DROP EXIT THEN
CR RON ID.
." . NOT DEFINED IN TARGET" ;
VERIFY will show the name of each definer word which was not defined in the target's source.
Fleet Forth is block based and the source for the kernel spans two C64 single sided disks. When I metacompile a new Fleet Forth kernel I get the following message when the first disk is done:
2CONSTANT NOT DEFINED IN TARGET
This is because 2CONSTANT is defined on the second disk and is not a problem since metacompiling is not done.
The final cell is a chain linking all DEFINER words together, similar to how all vocabularies are linked in a chain pointed to by VOC-LINK . This new version of the metacompiler may not need the definer link. If it doesn't, I will remove it.
The chain linking all definer words together will not be needed. Here is the new source for DEFINER
: DEFINER ( CFA -- )
['] ORDER! >BODY >R
ORDER@ META DEFINITIONS
CREATE
, 0 , 0 , 0 ,
[ HERE 2+ >A ]
DOES>
DUP 2+ SWAP @ EXECUTE ;
A> CONSTANT DEFINER-WORD
The word START , used to start metacompiling, will have another word added to its definition. The word DEF-RESET clears all the DEFINER words prior to a new metacompiling session.
: DEF-RESET ( -- )
['] ORDER! >BODY >R
ORDER@ META WITH-WORDS
NAME> DUP @ DEFINER-WORD <>
IF DROP EXIT THEN
>BODY 2+ 6 ERASE ;
DEF-RESET uses WITH-WORDS on the META vocabulary. The original search order is saved with ORDER@ . The first line saves the PFA of ORDER! to the return stack. When WITH-WORDS is finished with all the words in the META vocabulary, DEF-RESET will exit into ORDER! which will exit into the caller of DEF-RESET .
For each word in the META vocabulary, its NFA will be left on the stack and the following Forth thread will be executed.
NAME> DUP @ DEFINER-WORD <>
IF DROP EXIT THEN
>BODY 2+ 6 ERASE ;
This will clear the last three cells of all the DEFINER words in the META vocabulary without affecting other words.
The metacompiler's version of DOES> compiles the appropriate data in virtual memory as well as patch all the child words of the CREATE DOES> word being defined in the target.
META? returns a flag for the state of metacompiling. The metacompiler's version of DOES> will branch to the beginning of the original DOES> if metacompiling is off.
If metacompiling is on, it performs some housekeeping. ?CDP checks to see if the host dictionary size has changed. If it has, there is a problem. The phrase "DUP TRUE ?PAIRS" checks for any structure mismatches in the code between CREATE and DOES> . This also isolates the code between CREATE and DOES> from the code between DOES> and ; (semicolon). MCOMPILE uses the inline DOES (which is the regular Fleet Forth DOES , but this doesn't matter) for the search string to find the target version of DOES and compile it.
The word which resolves and patches all the code fields for the latest CREATE DOES> word being defined is PATCH-CF .
The phrase "$4C VC," builds a jump instruction in the target and the next two lines find the subroutine DO.DOES and compiles its address into the target.
If there is no chain just exit. This is not an error, it just means the CREATE DOES> word was defined in the target's source before any child words were.
Take apart the chain and patch the code field of each child word.
The metacompiler's ;CODE works similarly for CREATE ;CODE words.
Once this is done any new child words of this CREATE DOES> word will have its code built directly using the value from the second cell of the corresponding DEFINER word.
META DEFINITIONS
: LABEL ( -- )
HERE THERE DEFINE
HERE SWAP - CDP +! ; IMMEDIATE
HOST
!CDP and ?CDP are a 'sanity check' to make certain that the source is built in virtual memory and not in the host memory by mistake. Metacompiling is rather complicated so I may have been a little aggressive with the error checking.
Notice that <M:> smudges the LATEST word. This has to be the handle created in the TARGET vocabulary; therefore, the metacompiler's version of LATEST is defined in the META vocabulary to keep it out of the way until metacompiling.
Here are the corresponding DEFINER words and a few others. DEFINER creates it's child word in the META vocabulary regardless of the values of CURRENT and CONTEXT .
Since HSUBR (headerless subroutine) is defined in the host FORTH vocabulary, the version of SUBR which is compiled is the host version. This is so the source for words to be metacompiled can be tested on the host system.
The metacompiler redesign is almost done. There are still more tests.
I'm still wondering if there is a better way to deal with the CREATE DOES> and CREATE ;CODE words. This really isn't a problem unless a CREATE DOES> or CREATE ;CODE word is defined in the target source which does not yet have support in the metacompiler. An example is when I added string arrays to the Forth kernel.
This case was relatively straight forward. Some of the support for CREATE DOES> words is a bit more complicated.
As I've already mentioned, part of the complexity is because user variables, variables, constants and colon definitions are defined before USER , VARIABLE , CONSTANT or : (colon) are defined.
The other part of the complexity is that the user variables and other child words can not be executed on the target while it is being built. They exist in virtual memory, yet they have to be available.
CODE ]
DEY
STATE STY
STATE 1+ STY
NEXT JMP END-CODE
The variable STATE needs to leave its address while this word is being assembled. This is why there are shadow definitions in the SHADOW vocabulary. The shadow definition for the target system's variable STATE is a constant which holds the virtual memory address for the body of STATE .
0 VALUE USER.AREA
0 VALUE 'THERE
VARIABLE MVOC-LINK
VARIABLE MCURRENT
USER.AREA will be set in the kernel source and holds the address on the target for user variables. It is used by the word <<MUSER>> which is executed by the word <MUSER> which is executed by the DEFINER word USER . 'THERE is used in the kernel source to hold the address of the location for the coldstart dictionary size.
Both of these values are set in the kernel source.
The next two, MVOC-LINK and MCURRENT are metacompiler stand-ins for VOC-LINK and CURRENT and are used to set the values of VOC-LINK and CURRENT when metacompilation is finished.
When ?TFIND was renamed to ?TARGET the flag was changed.
In the word PNAME the 'R' in the string is actually the Commodore 64's reverse print character. It shows on the screen as a reverse 'R' but prints (and print dumps to a text file) as a normal 'R'. A carriage return clears the C64's reverse video mode.
VARIABLE CDP
: !CDP ( -- ) HERE CDP ! ;
: ?CDP ( -- ) HERE CDP @ <>
ABORT" COMPILED TO HOST" ;
I only gave a brief mention of their purpose. Metacompiling is tricky enough that I added error checking to make sure that compiling to the host didn't take place when compiling to virtual memory is intended. These words use an idea from other Forth's. Rather than record and check the stack depth, they record and check the host dictionary size.
Here is the source for the metacompiler's ; and -;
The host system's version of -; would work with the metacompiler; however, there would be no check for a change in the size of the host dictionary.
Metacompiling colon definitions which end with ; would not work without the metacompiler's version. It is needed to compile the target version of EXIT .
' (tick) now uses ?TCOMP to improve the stats on word usage for the new kernel.
: IMMEDIATE
IMMEDIATE
LATEST NAME> ?TW 0EXIT
LATEST NAME> >BODY @ V>NAME
" IBIT" FIND 0=
ABORT" IBIT NOT DEFINED IN SOURCE"
>BODY C@ VTOGGLE ;
This version of IMMEDIATE , like the original, will make the latest word immediate. It will also check the latest word to see if it is a TARGET word. For a TARGET word, it will fetch the address of the target's code field in virtual memory to get to the name field and toggle the immediate bit for this word in virtual memory. IBIT is a definer which is defined in the target's source.
The metacompiler's assembler is loaded before the aliases in the META vocabulary are defined.
The following aliases are not even needed:
and the reason has nothing to do with the chaining of the code fields together until they are resolved. That part works as expected.
So that I can use variables like this:
CODE ] ( -- )
DEY
STATE STY
STATE 1+ STY
NEXT JMP END-CODE
there has to be a word with the same name as the variable which returns the address of the variable. This is why there is a SHADOW vocabulary and why the DEFINER word CREATE creates a shadow constant for each CREATE word. This was also the case before redesigning the metacompiler.
This normally works quite well.
The word 1 is a code word with no body in Fleet Forth. Its code field points to a place in (FIND) which pushes a one on the data stack. This is smaller than a CONSTANT with the name 1 .
At this point there is a VARIABLE named 1 and a SHADOWCONSTANT of the same name. When 1 is found while interpreting, such as assembling a code word with the meta assembler, the address of this VARIABLE will be placed on the stack by the SHADOWCONSTANT1.
The following will change 1 to a code word with no body.
The shadow definition of 1 will still exist and will place the address of what was the VARIABLE1 on the data stack whenever the number 1 is encountered.
no shadow constant will be created since they are not needed for code words. In the following example the host system's 1 will be found or the search string will be converted to a number. Either way a one will still be placed on the stack as intended.
The host system's version of -; would work with the metacompiler; however, there would be no check for a change in the size of the host dictionary.
Metacompiling colon definitions which end with ; would not work without the metacompiler's version. It is needed to compile the target version of EXIT .
The only difference between the metacompiler's version of hyphen semicolon and the host version of hyphen semicolon is the check to see if the host dictionary changed. The metacompiler's hyphen semicolon can be streamlined.
The host version of hyphen semicolon is used in the metacompiler's version of hyphen semicolon.
The metacompiler's version of hyphen semicolon is used in the metacompiler's version of semicolon.
Only a few more words are needed to make the metacompiler functional. SMUDGE-TEST checks the TARGETVOCABULARY for any smudged words. If any are smudged, something went wrong while metacompiling. Smudging a word by setting its smudge-bit cannot hide it from WITH-WORDS .
'THERE (tick there) points to the address in virtual memory the target will use for the coldstart value of DP , Forth's dictionary pointer.
The address in WLINK is stored in the body of the vocabulary FORTH in the target. The target's CURRENT and CONTEXT are also set to point to the target's FORTH . Setting the target's CONTEXT and CURRENT isn't strictly necessary since the target's cold start routine executes FORTH DEFINITIONS as part of the high level startup. I left this in so the new kernel image will be identical to the one created with the older metacompiler.
Finally, the target's VOC-LINK is set to point to the third cell in the target's FORTH .
FINISH-FORTH executes PATCH-FORTH , runs a few checks and switches off metacompiling.
: FINISH-FORTH ( -- )
PATCH-FORTH VERIFY
[TARGET] FORTH [HOST]
SMUDGE-TEST
[COMPILE] FINISH
[COMPILE] META-STATUS
'THERE 0= ABORT" 'THERE IS ZERO"
USER.AREA 0= ABORT" NO USER AREA"
; IMMEDIATE
Unless I overlooked mentioning something in the source, that will be everything needed for metacompilation. I've tested this metacompiler with the source for Fleet Forth to build a new kernel. The new kernel is identical to the original so by that measure the new metacompiler is a success.
Now to start metacompiling by inserting the first source disk.
FH is 'from here' and adds the current blocks number to the number on the stack. Since the load block is block 1, this will load blocks 2 thru 165 inclusive.
When FINISH-FORTH executes, this is displayed:
That's about it. There are two more words for the metacompiler. GLOSSARY displays all the words in a given vocabulary, each on its own line with some statistical data. TLOCATE is used to locate the source for a word in the TARGET vocabulary.
: GLOSSARY ( -- )
SETWIDTH WITH-WORDS
CR DUP ID. TAB NAME>
DUP @ MACRO-CF =
IF >BODY COUNT QTYPE EXIT THEN
DUP @ TARGET-WORD-CF =
IF
>BODY DUP @ 16BITS U.R
." :" 2+ @ U.W EXIT
THEN
DUP @ MUSER-CF =
IF >BODY C@ 8BITS .R EXIT
THEN
>BODY @ U.W ;
GLOSSARY will display a MACRO's internal string.
It will display the CFA of a target word and the count of how many times that word was compiled.
If a word is the shadow of a USER VARIABLE , GLOSSARY will display its offset into the user area.
For all other words, GLOSSARY displays the contents of the first cell of the word's PFA.
This switches to the EDITOR vocabulary and displays the screen with the source for ?STACK .
Those are all the words for the metacompiler. I recently made changes to Fleet Forth's kernel and built the new kernel with the new metacompiler. Everything seems to work fine.
Any questions?
A few more words I glossed over.
I used ORIGIN in some of the examples. It takes a number from the data stack and makes that the value for THERE by setting TDP , target DP. The lowest value used for ORIGIN is retained in (ORIGIN) and used as the start address when saving the target with TSAVE .
I mentioned that the metacompiler has the following vocabulary structure:
FORTH
META
SHADOW
FORTH
ASSEMBLER
EDITOR
ASSEMBLER
I already showed the source for some words to navigate this vocabulary tree. The following seven words are immediate. HOST makes the host FORTH vocabulary the CONTEXT and CURRENT vocabulary. TARGET makes the target FORTH vocabulary the CONTEXT and CURRENT vocabulary.
The following words only set the CONTEXT vocabulary. [HOST] sets it to the host FORTH vocabulary. Note the brackets. [TARGET] sets it to the target FORTH vocabulary. [META] sets it to the META vocabulary. [SHADOW] sets it to the SHADOW vocabulary. [ASSEMBLER] sets it to the metacompiler's ASSEMBLER vocabulary.
These seven words are defined in the host FORTH vocabulary so they can be found from any other vocabulary.
The variable HEADLESS is used to count the number of headerless words. PNAMES is used to count the number of padded words.
The constant DEFINER-WORD is used by the word DEF-RESET to reset the definer words. It is also used by VERIFY to show which definer words were not defined in the target source. It is also used by PATCH-CF to check if the latest word defined in the target is actually a definer word.
The constant TARGET-WORD-CF is used to determine if a word is a target word. TARGET-WORD-CF and the constants MACRO-CF and MUSER-CF are used by GLOSSARY to display appropriate data for target words, macros and the shadows of user variables.
: HSUBR ( -- )
META?
IF
THERE CONSTANT ASSEMBLE EXIT
THEN
SUBR ;
If not metacompiling, it acts as an alias for SUBR , a word in the host system. This is so that even subroutines defined with HSUBR can be tested on the host system.
If metacompiling, it will create a constant in the TARGET vocabulary which holds the current address of THERE , target HERE . It then switches to assembling.
No header or code field is created.
The subroutine DOUBLE.COMPARE is used by four code words. This example just shows one.
DOUBLE.COMPARE has no header and no code field. Its address is found by using the constant DOUBLE.COMPARE in the TARGET vocabulary which is on the host.