( Variables and User Variables ) : doVAR ( -- a ) R> ; VARIABLE UP ( -- a, Pointer to the user area.) : doUSER ( -- a, Run time routine for user variables.) R> @ \ retrieve user area offset UP @ + ; \ add to user area base addr : doVOC ( -- ) R> CONTEXT ! ; : FORTH ( -- ) doVOC [ 0 , 0 , : doUSER ( -- a ) R> @ UP @ + ; ( User Variables ) SP0 ( -- a, pointer to bottom of the data stack.) RP0 ( -- a, pointer to bottom of the return stack.) '?KEY ( -- a, execution vector of ?KEY. Default to ?rx.) 'EMIT ( -- a, execution vector of EMIT. Default to tx!) 'EXPECT ( -- a, execution vector of EXPECT. Default to 'accept'.) 'TAP ( -- a, execution vector of TAP. Defulat the kTAP.) 'ECHO ( -- a, execution vector of ECHO. Default to tx!.) 'PROMPT ( -- a, execution vector of PROMPT. Default to '.ok'.) BASE ( -- a,.radix base for numeric I/O. Default to 10.) tmp ( -- a, a temporary storage location used in parse and find.) SPAN ( -- a, hold character count received by EXPECT.) >IN ( -- a, hold the character pointer while parsing input stream.) #TIB ( -- a, hold the current count and address of the terminal input buffer. Terminal Input Buffer used one cell after #TIB.) CSP ( -- a, hold the stack pointer for error checking.) 'EVAL ( -- a, execution vector of EVAL. Default to EVAL.) 'NUMBER ( -- a, address of number conversion. Default to NUMBER?.) HLD ( -- a, hold a pointer in building a numeric output string.) HANDLER ( -- a, hold the return stack pointer for error handling.) CONTEXT ( -- a, a area to specify vocabulary search order. Default to FORTH. Vocabulary stack, 8 cells follwing CONTEXT.) CURRENT ( -- a, point to the vocabulary to be extended. Default to FORTH. Vocabulary link uses one cell after CURRENT.) CP ( -- a, point to the top of the code dictionary.) NP ( -- a, point to the bottom of the name dictionary.) LAST ( -- a, point to the last name in the name dictionary.) ( Common Functions ) : ?DUP ( w -- w w | 0 ) DUP IF DUP THEN ; : ROT ( w1 w2 w3 -- w2 w3 w1 ) >R SWAP R> SWAP ; : 2DROP ( w w -- ) DROP DROP ; : 2DUP ( w1 w2 -- w1 w2 w1 w2 ) OVER OVER ; : + ( w w -- w ) UM+ DROP ; : NOT ( w -- w ) -1 XOR ; : NEGATE ( n -- -n ) NOT 1 + ; : DNEGATE ( d -- -d ) NOT >R NOT 1 UM+ R> + ; : D+ ( d d -- d ) >R SWAP >R UM+ R> R> + + ; : - ( w w -- w ) NEGATE + ; : ABS ( n -- +n ) DUP 0< IF NEGATE THEN ; ( Comparison ) : = ( w w -- t ) XOR IF 0 EXIT THEN -1 ; : U< ( u u -- t ) 2DUP XOR 0< IF SWAP DROP 0< EXIT THEN - 0< ; : < ( n n -- t ) 2DUP XOR 0< IF DROP 0< EXIT THEN - 0< ; : MAX ( n n -- n ) 2DUP < IF SWAP THEN DROP ; : MIN ( n n -- n ) 2DUP SWAP < IF SWAP THEN DROP ; : WITHIN ( u ul uh -- t ) \ ul <= u < uh OVER - >R - R> U< ; ( Divide ) : UM/MOD ( ud u -- ur uq ) 2DUP U< IF NEGATE 15 FOR >R DUP UM+ >R >R DUP UM+ R> + DUP R> R@ SWAP >R UM+ R> OR IF >R DROP 1 + R> ELSE DROP THEN R> NEXT DROP SWAP EXIT THEN DROP 2DROP -1 DUP ; : M/MOD ( d n -- r q ) \ floored division DUP 0< DUP >R IF NEGATE >R DNEGATE R> THEN >R DUP 0< IF R@ + THEN R> UM/MOD R> IF SWAP NEGATE SWAP THEN ; : /MOD ( n n -- r q ) OVER 0< SWAP M/MOD ; : MOD ( n n -- r ) /MOD DROP ; : / ( n n -- q ) /MOD SWAP DROP ; ( Multiply ) : UM* ( u u -- ud ) 0 SWAP ( u1 0 u2 ) 15 FOR DUP UM+ >R >R DUP UM+ R> + R> IF >R OVER UM+ R> + THEN NEXT ROT DROP ; : * ( n n -- n ) UM* DROP ; : M* ( n n -- d ) 2DUP XOR 0< >R ABS SWAP ABS UM* R> IF DNEGATE THEN ; : */MOD ( n n n -- r q ) >R M* R> M/MOD ; : */ ( n n n -- q ) */MOD SWAP DROP ; ( Memory Alignment ) : CELL- ( a -- a ) -2 + ; : CELL+ ( a -- a ) 2 + ; : CELLS ( n -- n ) 2 * ; : ALIGNED ( b -- a ) DUP 0 2 UM/MOD DROP DUP IF 2 SWAP - THEN + ; : BL ( -- 32 ) 32 ; : >CHAR ( c -- c ) $7F AND DUP 127 BL WITHIN IF DROP 95 THEN ; : DEPTH ( -- n ) SP@ SP0 @ SWAP - 2 / ; : PICK ( +n -- w ) 1 + CELLS SP@ + @ ; ( Memory Access ) : +! ( n a -- ) SWAP OVER @ + SWAP ! ; : 2! ( d a -- ) SWAP OVER ! CELL+ ! ; : 2@ ( a -- d ) DUP CELL+ @ SWAP @ ; : COUNT ( b -- b +n ) DUP 1 + SWAP C@ ; : HERE ( -- a ) CP @ ; : PAD ( -- a ) HERE 80 + ; : TIB ( -- a ) #TIB CELL+ @ ; : @EXECUTE ( a -- ) @ ?DUP IF EXECUTE THEN ; : CMOVE ( b b u -- ) FOR AFT >R DUP C@ R@ C! 1 + R> 1 + THEN NEXT 2DROP ; : FILL ( b u c -- ) SWAP FOR SWAP AFT 2DUP C! 1 + THEN NEXT 2DROP ; : -TRAILING ( b u -- b u ) FOR AFT BL OVER R@ + C@ < IF R> 1 + EXIT THEN THEN NEXT 0 ; : PACK$ ( b u a -- a ) \ null fill ALIGNED DUP >R OVER DUP 0 2 UM/MOD DROP - OVER + 0 SWAP ! 2DUP C! 1 + SWAP CMOVE R> ; ( Numeric Output ) : DIGIT ( u -- c ) 9 OVER < 7 AND + 48 + ; : EXTRACT ( n base -- n c ) 0 SWAP UM/MOD SWAP DIGIT ; : <# ( -- ) PAD HLD ! ; : HOLD ( c -- ) HLD @ 1 - DUP HLD ! C! ; : # ( u -- u ) BASE @ EXTRACT HOLD ; : #S ( u -- 0 ) BEGIN # DUP WHILE REPEAT ; : SIGN ( n -- ) 0< IF 45 HOLD THEN ; : #> ( w -- b u ) DROP HLD @ PAD OVER - ; : str ( n -- b u ) DUP >R ABS <# #S R> SIGN #> ; : HEX ( -- ) 16 BASE ! ; : DECIMAL ( -- ) 10 BASE ! ; ( Number Output ) : str ( n -- b u ) ( Convert a signed integer to a numeric string.) DUP >R ( save a copy for sign) ABS ( use absolute of n) <# #S ( convert all digits) R> SIGN ( add sign from n) #> ; ( return number string addr and length) : HEX ( -- ) ( Use radix 16 as base for numeric conversions.) 16 BASE ! ; : DECIMAL ( -- ) ( Use radix 10 as base for numeric conversions.) 10 BASE ! ; : .R ( n +n -- ) ( Display an integer in a field of n columns, right justified.) >R str ( convert n to a number string) R> OVER - SPACES ( print leading spaces) TYPE ; ( print number in +n column format) : U.R ( u +n -- ) ( Display an unsigned integer in n column, right justified.) >R ( save column number) <# #S #> R> ( convert unsigned number) OVER - SPACES ( print leading spaces) TYPE ; ( print number in +n columns) : U. ( u -- ) ( Display an unsigned integer in free format.) <# #S #> ( convert unsigned number) SPACE ( print one leading space) TYPE ; ( print number) : . ( w -- ) ( Display an integer in free format, preceeded by a space.) BASE @ 10 XOR ( if not in decimal mode) IF U. EXIT THEN ( print unsigned number) str SPACE TYPE ; ( print signed number if decimal) : ? ( a -- ) ( Display the contents in a memory cell.) @ . ; ( very simple but useful command) ( Number Input ) : DIGIT? ( c base -- u t ) >R 48 - 9 OVER < IF 7 - DUP 10 < OR THEN DUP R> U< ; : NUMBER? ( a -- n T | a F ) BASE @ >R 0 OVER COUNT ( a 0 b n) OVER C@ 36 = IF HEX SWAP 1 + SWAP 1 - THEN ( a 0 b' n') OVER C@ 45 = >R ( a 0 b n) SWAP R@ - SWAP R@ + ( a 0 b" n") ?DUP IF 1 - ( a 0 b n) FOR DUP >R C@ BASE @ DIGIT? WHILE SWAP BASE @ * + R> 1 + NEXT DROP R@ ( b ?sign) IF NEGATE THEN SWAP ELSE R> R> ( b index) 2DROP ( digit number) 2DROP 0 THEN DUP THEN R> ( n ?sign) 2DROP R> BASE ! ; ( Basic I/O ) : ?KEY ( -- c T | F ) '?KEY @EXECUTE ; : KEY ( -- c ) BEGIN ?KEY UNTIL ; : EMIT ( c -- ) 'EMIT @EXECUTE ; : NUF? ( -- f ) ?KEY DUP IF 2DROP KEY 13 = THEN ; : PACE ( -- ) 11 EMIT ; : SPACE ( -- ) BL EMIT ; : CHARS ( +n c -- ) \ ???ANS conflict SWAP 0 MAX FOR AFT DUP EMIT THEN NEXT DROP ; : SPACES ( +n -- ) BL CHARS ; : TYPE ( b u -- ) FOR AFT DUP C@ EMIT 1 + THEN NEXT DROP ; : CR ( -- ) 13 EMIT 10 EMIT ; : do$ ( -- a ) R> R@ R> COUNT + ALIGNED >R SWAP >R ; : $"| ( -- a ) do$ ; : ."| ( -- ) do$ COUNT TYPE ; COMPILE-ONLY : .R ( n +n -- ) >R str R> OVER - SPACES TYPE ; : U.R ( u +n -- ) >R <# #S #> R> OVER - SPACES TYPE ; : U. ( u -- ) <# #S #> SPACE TYPE ; : . ( n -- ) BASE @ 10 XOR IF U. EXIT THEN str SPACE TYPE ; : ? ( a -- ) @ . ; ( Parsing ) : parse ( b u c -- b u delta ; ) tmp ! OVER >R DUP \ b u u IF 1 - tmp @ BL = IF \ b u' \ 'skip' FOR BL OVER C@ - 0< NOT WHILE 1 + NEXT ( b) R> DROP 0 DUP EXIT \ all delim THEN R> THEN OVER SWAP \ b' b' u' \ 'scan' FOR tmp @ OVER C@ - tmp @ BL = IF 0< THEN WHILE 1 + NEXT DUP >R ELSE R> DROP DUP 1 + >R THEN OVER - R> R> - EXIT THEN ( b u) OVER R> - ; : PARSE ( c -- b u ; ) >R TIB >IN @ + #TIB @ >IN @ - R> parse >IN +! ; : .( ( -- ) 41 PARSE TYPE ; IMMEDIATE : ( ( -- ) 41 PARSE 2DROP ; IMMEDIATE : \ ( -- ) #TIB @ >IN ! ; IMMEDIATE : CHAR ( -- c ) BL PARSE DROP C@ ; : TOKEN ( -- a ; ) BL PARSE 31 MIN NP @ OVER - CELL- PACK$ ; : WORD ( c -- a ; ) PARSE HERE PACK$ ; ( Dictionary Search ) : NAME> ( a -- xt ) CELL- CELL- @ ; : SAME? ( a a u -- a a f \ -0+ ) FOR AFT OVER R@ CELLS + @ OVER R@ CELLS + @ - ?DUP IF R> DROP EXIT THEN THEN NEXT 0 ; : find ( a va -- xt na | a F ) SWAP \ va a DUP C@ 2 / tmp ! \ va a \ get cell count DUP @ >R \ va a \ count byte & 1st char CELL+ SWAP \ a' va BEGIN @ DUP \ a' na na IF DUP @ [ =MASK ] LITERAL AND R@ XOR \ ignore lexicon bits IF CELL+ -1 ELSE CELL+ tmp @ SAME? THEN ELSE R> DROP EXIT THEN WHILE CELL- CELL- \ a' la REPEAT R> DROP SWAP DROP CELL- DUP NAME> SWAP ; : NAME? ( a -- xt na | a F ) CONTEXT DUP 2@ XOR IF CELL- THEN >R \ context<>also BEGIN R> CELL+ DUP >R @ ?DUP WHILE find ?DUP UNTIL R> DROP EXIT THEN R> DROP 0 ; ( Terminal ) : ^H ( b b b -- b b b ) \ backspace >R OVER R> SWAP OVER XOR IF 8 'ECHO @EXECUTE 32 'ECHO @EXECUTE \ distructive 8 'ECHO @EXECUTE \ backspace THEN ; : TAP ( bot eot cur c -- bot eot cur ) DUP 'ECHO @EXECUTE OVER C! 1 + ; : kTAP ( bot eot cur c -- bot eot cur ) DUP 13 XOR IF 8 XOR IF BL TAP ELSE ^H THEN EXIT THEN DROP SWAP DROP DUP ; : accept ( b u -- b u ) OVER + OVER BEGIN 2DUP XOR WHILE KEY DUP BL - 95 U< IF TAP ELSE 'TAP @EXECUTE THEN REPEAT DROP OVER - ; : EXPECT ( b u -- ) 'EXPECT @EXECUTE SPAN ! DROP ; : QUERY ( -- ) TIB 80 'EXPECT @EXECUTE #TIB ! DROP 0 >IN ! ; ( Error Handling ) : CATCH ( ca -- err#/0 ) ( Execute word at ca and set up an error frame for it.) SP@ >R ( save current stack pointer on return stack ) HANDLER @ >R ( save the handler pointer on return stack ) RP@ HANDLER ! ( save the handler frame pointer in HANDLER ) ( ca ) EXECUTE ( execute the assigned word over this safety net ) R> HANDLER ! ( normal return from the executed word ) ( restore HANDLER from the return stack ) R> DROP ( discard the saved data stack pointer ) 0 ; ( push a no-error flag on data stack ) : THROW ( err# -- err# ) ( Reset system to current local error frame an update error flag.) HANDLER @ RP! ( expose latest error handler frame on return stack) R> HANDLER ! ( restore previously saved error handler frame ) R> SWAP >R ( retrieve the data stack pointer saved ) SP! ( restore the data stack ) DROP R> ; ( retrived err# ) CREATE NULL$ 0 , $," coyote" : ABORT ( -- ) NULL$ THROW ; : abort" ( f -- ) IF do$ THROW THEN do$ DROP ; ( Text Interpreter ) : $INTERPRET ( a -- ) NAME? ?DUP IF @ $40 AND ABORT" compile ONLY" EXECUTE EXIT THEN 'NUMBER @EXECUTE IF EXIT THEN THROW ; : [ ( -- ) doLIT $INTERPRET 'EVAL ! ; IMMEDIATE : .OK ( -- ) doLIT $INTERPRET 'EVAL @ = IF ." ok" THEN CR ; : ?STACK ( -- ) DEPTH 0< ABORT" underflow" ; : EVAL ( -- ) BEGIN TOKEN DUP C@ WHILE 'EVAL @EXECUTE ?STACK REPEAT DROP 'PROMPT @EXECUTE ; ( Shell ) : PRESET ( -- ) SP0 @ SP! TIB #TIB CELL+ ! ; : xio ( a a a -- ) \ reset 'EXPECT 'TAP 'ECHO 'PROMPT doLIT accept 'EXPECT 2! 'ECHO 2! ; COMPILE-ONLY : FILE ( -- ) doLIT PACE doLIT DROP doLIT kTAP xio ; : HAND ( -- ) doLIT .OK doLIT EMIT [ kTAP xio ; CREATE I/O ' ?RX , ' TX! , \ defaults : CONSOLE ( -- ) I/O 2@ '?KEY 2! HAND ; : QUIT ( -- ) RP0 @ RP! BEGIN [COMPILE] [ BEGIN QUERY doLIT EVAL CATCH ?DUP UNTIL 'PROMPT @ SWAP CONSOLE NULL$ OVER XOR IF CR #TIB 2@ TYPE CR >IN @ 94 CHARS CR COUNT TYPE ." ? " THEN doLIT .OK XOR IF $1B EMIT THEN PRESET AGAIN ; ( Interpreter and Compiler ) : [ ( -- ) [ ' $INTERPRET ] LITERAL 'EVAL ! ( vector EVAL to $INTERPRET ) ; IMMEDIATE ( enter into text interpreter mode ) : ] ( -- ) [ ' $COMPILE ] LITERAL 'EVAL ! ( vector EVAL to $COMPILE ) ; ( Primitive Compiler Words ) : ' ( -- xt ) TOKEN NAME? IF EXIT THEN THROW ; : ALLOT ( n -- ) CP +! ; : , ( w -- ) HERE DUP CELL+ CP ! ! ; \ ???ALIGNED : [COMPILE] ( -- ; ) ' , ; IMMEDIATE : COMPILE ( -- ) R> DUP @ , CELL+ >R ; : LITERAL ( w -- ) COMPILE doLIT , ; IMMEDIATE : $," ( -- ) 34 WORD COUNT ALIGNED CP ! ; : RECURSE ( -- ) LAST @ NAME> , ; IMMEDIATE ( Structures ) : MARK ( -- A ) HERE 0 , ; : >RESOLVE ( A -- ) R MARK ; IMMEDIATE : AHEAD ( -- A ) COMPILE branch >MARK ; IMMEDIATE : REPEAT ( A a -- ) [COMPILE] AGAIN >RESOLVE ; IMMEDIATE : THEN ( A -- ) >RESOLVE ; IMMEDIATE : AFT ( a -- a A ) DROP [COMPILE] AHEAD [COMPILE] BEGIN SWAP ; IMMEDIATE : ELSE ( A -- A ) [COMPILE] AHEAD SWAP [COMPILE] THEN ; IMMEDIATE : WHEN ( a A -- a A a ) [COMPILE] IF OVER ; IMMEDIATE : WHILE ( a -- A a ) [COMPILE] IF SWAP ; IMMEDIATE : ABORT" ( -- ; ) COMPILE abort" $," ; IMMEDIATE : $" ( -- ; ) COMPILE $"| $," ; IMMEDIATE : ." ( -- ; ) COMPILE ."| $," ; IMMEDIATE ( Compiler ) : ?UNIQUE ( a -- a ) DUP NAME? IF ." reDef " OVER COUNT TYPE THEN DROP ; : $,n ( a -- ) DUP C@ IF ?UNIQUE ( na) DUP LAST ! \ for OVERT ( na) HERE ALIGNED SWAP ( cp na) CELL- ( cp la) CURRENT @ @ ( cp la na') OVER ! ( cp la) CELL- DUP NP ! ( ptr) ! EXIT THEN $" name" THROW ; .( FORTH Compiler ) : $COMPILE ( a -- ) NAME? ?DUP IF @ $80 AND IF EXECUTE ELSE , THEN EXIT THEN 'NUMBER @EXECUTE IF [COMPILE] LITERAL EXIT THEN THROW ; : OVERT ( -- ) LAST @ CURRENT @ ! ; : ; ( -- ) COMPILE EXIT [COMPILE] [ OVERT ; IMMEDIATE : ] ( -- ) doLIT $COMPILE 'EVAL ! ; : call, ( xt -- ) \ DTC 8086 relative call $E890 , HERE CELL+ - , ; : : ( -- ; ) TOKEN $,n doLIT doLIST call, ] ; : IMMEDIATE ( -- ) $80 LAST @ @ OR LAST @ ! ; ( Defining Words ) : USER ( n -- ; ) TOKEN $,n OVERT doLIT doLIST COMPILE doUSER , ; : CREATE ( -- ; ) TOKEN $,n OVERT doLIT doLIST COMPILE doVAR ; : VARIABLE ( -- ; ) CREATE 0 , ; ( Memory Dump ) : _TYPE ( b u -- ) FOR AFT DUP C@ >CHAR EMIT 1 + THEN NEXT DROP ; : dm+ ( b u -- b ) OVER 4 U.R SPACE FOR AFT DUP C@ 3 U.R 1 + THEN NEXT ; : DUMP ( b u -- ) BASE @ >R HEX 16 / FOR CR 16 2DUP dm+ ROT ROT 2 SPACES _TYPE NUF? NOT WHILE NEXT ELSE R> DROP THEN DROP R> BASE ! ; ( Stack Tools ) : .S ( -- ) CR DEPTH FOR AFT R@ PICK . THEN NEXT ." NAME ( xt -- na | F ) CURRENT BEGIN CELL+ @ ?DUP WHILE 2DUP BEGIN @ DUP WHILE 2DUP NAME> XOR WHILE CELL- REPEAT THEN SWAP DROP ?DUP UNTIL SWAP DROP SWAP DROP EXIT THEN DROP 0 ; : .ID ( a -- ) ?DUP IF COUNT $01F AND _TYPE EXIT THEN ." {noName}" ; : SEE ( -- ; ) ' CR CELL+ BEGIN CELL+ DUP @ DUP IF >NAME THEN ?DUP IF SPACE .ID ELSE DUP @ U. THEN NUF? UNTIL DROP ; : WORDS ( -- ) CR CONTEXT @ BEGIN @ ?DUP WHILE DUP SPACE .ID CELL- NUF? UNTIL DROP THEN ; ( Startup ) : VER ( -- u ) $101 ; : hi ( -- ) !IO BASE @ HEX \ initialize IO device & sign on CR ." eFORTH V" VER <# # # 46 HOLD # #> TYPE CR ; : EMPTY ( -- ) FORTH CONTEXT @ DUP CURRENT 2! 6 CP 3 MOVE OVERT ; CREATE 'BOOT ' hi , \ application vector : COLD ( -- ) BEGIN U0 UP 74 CMOVE PRESET 'BOOT @EXECUTE FORTH CONTEXT @ DUP CURRENT 2! OVERT QUIT AGAIN ;