I should have included a few examples from my '816 Forth in my post above. As Brad said, the CFA is not part of the header. Whether you use headers or not, every ITC Forth word requires the CFA. What does
not require it is runtimes that Forth words' CFAs might
point to, as in the case of nest, shown below. Note however that
unnest
is a Forth word.
There are a handfull of things the CFA could point to. In the case of primitives, it's just the address immediately following the CFA, because that's where the code to run is. In the case of secondaries, the CFA points to nest, a runtime. Other runtimes include (but are not limited to) const (used by constants, and, in my system, even variables because their
address is constant), 2const (for double-precision constants), or any other machine-language routine (ending in JMP NEXT, not RTS), as in the case of the word -1 below which just points to PUSH_TRUE, and 0 (zero) which just points to PUSH_FALSE, both of which many primitives jump to to finish up. (I put the PUSH_TRUE and PUSH_FALSE code there too.)
I used a GO_NEXT macro to easily change what gets assembled there just by changing a single equate in the assembly code, although in practice I've left it at JMP(NEXTadr) for my high-level Forth interrupt service which slows things down about 3% (because of the indirection) but the speed of NEXT itself is unaffected, except that if there's an interrupt to service in high-level Forth, NEXTadr contains the address of a different version of NEXT which goes into the ISR, and it's actually
faster than the normal NEXT-- almost like a
negative overhead for handling interrupts.
At the end below you'll see DUP>R, a primitive to replace the common occurrence of DUP >R but headers are turned off for that one, yet it still starts with PRIMITIVE which lays down the code field that points to the the code immediately following at $+2. You might ask, "Why not just comment-out the HEADER line if you don't want the header?" but HEADER and NO_HEADERS make it quicker to remove or put back the headers through a
range of Forth words all at once, without having to comment-out or "de-comment" every one individually.
Code:
Selected examples:
PRIMITIVE: MACRO ; The main alternative to PRIMITIVE for
DWL $ + CELL_SIZE ; a code field is DWL nest (or DOCOL ).
ENDM ; CFA is necessary even without headers.
;-------------------
CONSTANT: MACRO number ; This compiles a constant. You still
DWL const, number ; need to use the HEADER macro first if
ENDM ; you want a header. Other assemblers
; might allow you to modify this so the
; assembly-language CFA label name can
; be included in the same macro call
;------------------- ; that sets up the header.
VARIABLE: MACRO ; SF168,174,260,262 FIG F83 ANS_CORE
DWL const, THERE ; THERE refers to the last value left,
THERE: SETL THERE + 2 ; not this new updated value.
ENDM
;-------------------
HEADER "-1", NOT_IMMEDIATE ; -1 is the Forth word's name. MINUS1
MINUS1: DWL PUSH_TRUE ; is the CFA label for the assembler.
HEADER "0", NOT_IMMEDIATE ; Here with -1 , 0 , TRUE , FALSE , and
ZERO: DWL PUSH_FALSE ; DUMMYCELL , making the code field
; point to PUSH_FALSE and PUSH_TRUE is
HEADER "1", NOT_IMMEDIATE ; faster and shorter than an actual
ONE: CONSTANT 1 ; Forth constant.
HEADER "2", NOT_IMMEDIATE ; These numbers are given as Forth words
TWO: CONSTANT 2 ; since these numbers get so much use.
; Every use of a constant in place of a
; literal saves a cell (two bytes).
HEADER "FENCE", NOT_IMMEDIATE ; Address behind which FORGET can't go. Normally
FENCE: VARIABLE ; holds NFA of the first FORGETable word.
; Init'd by COLD.
BASEdata: EQU THERE ; (BASEdata is for primitives' use.)
HEADER "BASE", NOT_IMMEDIATE ; number base for number I/O. Init'd by COLD.
BASE: VARIABLE ; (Use BASE in secondaries where you want CFA.)
STATEdata: EQU THERE ; For primitive STATE_@, which saves time & mem.
HEADER "STATE", NOT_IMMEDIATE ; -1=compile, 0=interpret.
STATE: VARIABLE ; [ in QUIT turns it OFF.
PUSH_FALSE: DEX_DEX
SET_FALSE: STZ 0,X
GO_NEXT
;-------------------
nest: PEI IP ; PEI IP replaces LDA IP , PHA here. nest is the
LDA W ; runtime code of : (often called DOCOL ). It is not
INA_INA ; really a Forth word itself per se-- but it is pointed
STA IP ; to by the CFA of secondaries.
GO_NEXT
;-------------------
HEADER "unnest", NOT_IMMEDIATE ; ( -- )
unnest: PRIMITIVE ; This does the opposite of
PLA ; nest, and the same as EXIT.
STA IP ; It is often called SEMIS
GO_NEXT ; because it's compiled by ;
;-------------------
HEADER "/", NOT_IMMEDIATE ; ( n1 n2 -- quot )
SLASH: DWL nest, sMOD, NIP, unnest
;-------------------
HEADER "1+", NOT_IMMEDIATE ; ( n -- n+1 )
_1ADD: PRIMITIVE
INC 0,X
GO_NEXT
;-------------------
NO_HEADERS
HEADER "DUP>R", NOT_IMMEDIATE ; ( n -- n )
DUP2R: PRIMITIVE
LDA 0,X
PHA
GO_NEXT
HEADERS
;-------------------