- .data
- .align 4096
-buffer:
- // Multi-line constant gives 'Warning: unterminated string; newline inserted' messages which you can ignore.
- .ascii "\
-\\ Define some character constants
-: '\\n' 10 ;
-: 'SPACE' 32 ;
-: '\"' 34 ;
-: ':' 58 ;
-
-\\ CR prints a carriage return
-: CR '\\n' EMIT ;
-
-\\ SPACE prints a space
-: SPACE 'SPACE' EMIT ;
-
-\\ Primitive . (DOT) function doesn't follow with a blank, so redefine it to behave like FORTH.
-\\ Notice how we can trivially redefine existing functions.
-: . . SPACE ;
-
-\\ DUP, DROP are defined in assembly for speed, but this is how you might define them
-\\ in FORTH. Notice use of the scratch variables _X and _Y.
-\\ : DUP _X ! _X @ _X @ ;
-\\ : DROP _X ! ;
-
-\\ The 2... versions of the standard operators work on pairs of stack entries. They're not used
-\\ very commonly so not really worth writing in assembler. Here is how they are defined in FORTH.
-: 2DUP OVER OVER ;
-: 2DROP DROP DROP ;
-
-\\ More standard FORTH words.
-: 2* 2 * ;
-: 2/ 2 / ;
-
-\\ [ and ] allow you to break into immediate mode while compiling a word.
-: [ IMMEDIATE \\ define [ as an immediate word
- 0 STATE ! \\ go into immediate mode
- ;
-
-: ]
- 1 STATE ! \\ go back to compile mode
- ;
-
-\\ LITERAL takes whatever is on the stack and compiles LIT <foo>
-: LITERAL IMMEDIATE
- ' LIT , \\ compile LIT
- , \\ compile the literal itself (from the stack)
- ;
-
-\\ condition IF true-part THEN rest
-\\ compiles to:
-\\ condition 0BRANCH OFFSET true-part rest
-\\ where OFFSET is the offset of 'rest'
-\\ condition IF true-part ELSE false-part THEN
-\\ compiles to:
-\\ condition 0BRANCH OFFSET true-part BRANCH OFFSET2 false-part rest
-\\ where OFFSET if the offset of false-part and OFFSET2 is the offset of rest
-
-\\ IF is an IMMEDIATE word which compiles 0BRANCH followed by a dummy offset, and places
-\\ the address of the 0BRANCH on the stack. Later when we see THEN, we pop that address
-\\ off the stack, calculate the offset, and back-fill the offset.
-: IF IMMEDIATE
- ' 0BRANCH , \\ compile 0BRANCH
- HERE @ \\ save location of the offset on the stack
- 0 , \\ compile a dummy offset
-;
-
-: THEN IMMEDIATE
- DUP
- HERE @ SWAP - \\ calculate the offset from the address saved on the stack
- SWAP ! \\ store the offset in the back-filled location
-;
-
-: ELSE IMMEDIATE
- ' BRANCH , \\ definite branch to just over the false-part
- HERE @ \\ save location of the offset on the stack
- 0 , \\ compile a dummy offset
- SWAP \\ now back-fill the original (IF) offset
- DUP \\ same as for THEN word above
- HERE @ SWAP -
- SWAP !
-;
-
-\\ BEGIN loop-part condition UNTIL
-\\ compiles to:
-\\ loop-part condition 0BRANCH OFFSET
-\\ where OFFSET points back to the loop-part
-\\ This is like do { loop-part } while (condition) in the C language
-: BEGIN IMMEDIATE
- HERE @ \\ save location on the stack
-;
-
-: UNTIL IMMEDIATE
- ' 0BRANCH , \\ compile 0BRANCH
- HERE @ - \\ calculate the offset from the address saved on the stack
- , \\ compile the offset here
-;
-
-\\ BEGIN loop-part AGAIN
-\\ compiles to:
-\\ loop-part BRANCH OFFSET
-\\ where OFFSET points back to the loop-part
-\\ In other words, an infinite loop which can only be returned from with EXIT
-: AGAIN IMMEDIATE
- ' BRANCH , \\ compile BRANCH
- HERE @ - \\ calculate the offset back
- , \\ compile the offset here
-;
-
-\\ BEGIN condition WHILE loop-part REPEAT
-\\ compiles to:
-\\ condition 0BRANCH OFFSET2 loop-part BRANCH OFFSET
-\\ where OFFSET points back to condition (the beginning) and OFFSET2 points to after the whole piece of code
-\\ So this is like a while (condition) { loop-part } loop in the C language
-: WHILE IMMEDIATE
- ' 0BRANCH , \\ compile 0BRANCH
- HERE @ \\ save location of the offset2 on the stack
- 0 , \\ compile a dummy offset2
-;
-
-: REPEAT IMMEDIATE
- ' BRANCH , \\ compile BRANCH
- SWAP \\ get the original offset (from BEGIN)
- HERE @ - , \\ and compile it after BRANCH
- DUP
- HERE @ SWAP - \\ calculate the offset2
- SWAP ! \\ and back-fill it in the original location
-;
-
-\\ With the looping constructs, we can now write SPACES, which writes n spaces to stdout.
-: SPACES
- BEGIN
- SPACE \\ print a space
- 1- \\ until we count down to 0
- DUP 0=
- UNTIL
-;
-
-\\ .S prints the contents of the stack. Very useful for debugging.
-: .S
- DSP@ \\ get current stack pointer
- BEGIN
- DUP @ . \\ print the stack element
- 4+ \\ move up
- DUP S0 @ 4- = \\ stop when we get to the top
- UNTIL
- DROP
-;
-
-\\ DEPTH returns the depth of the stack.
-: DEPTH S0 @ DSP@ - ;
-
-\\ .\" is the print string operator in FORTH. Example: .\" Something to print\"
-\\ The space after the operator is the ordinary space required between words.
-\\ This is tricky to define because it has to do different things depending on whether
-\\ we are compiling or in immediate mode. (Thus the word is marked IMMEDIATE so it can
-\\ detect this and do different things).
-\\ In immediate mode we just keep reading characters and printing them until we get to
-\\ the next double quote.
-\\ In compile mode we have the problem of where we're going to store the string (remember
-\\ that the input buffer where the string comes from may be overwritten by the time we
-\\ come round to running the function). We store the string in the compiled function
-\\ like this:
-\\ ..., LITSTRING, string length, string rounded up to 4 bytes, EMITSTRING, ...
-: .\" IMMEDIATE
- STATE @ \\ compiling?
- IF
- ' LITSTRING , \\ compile LITSTRING
- HERE @ \\ save the address of the length word on the stack
- 0 , \\ dummy length - we don't know what it is yet
- BEGIN
- KEY \\ get next character of the string
- DUP '\"' <>
- WHILE
- HERE @ !b \\ store the character in the compiled image
- 1 HERE +! \\ increment HERE pointer by 1 byte
- REPEAT
- DROP \\ drop the double quote character at the end
- DUP \\ get the saved address of the length word
- HERE @ SWAP - \\ calculate the length
- 4- \\ subtract 4 (because we measured from the start of the length word)
- SWAP ! \\ and back-fill the length location
- HERE @ \\ round up to next multiple of 4 bytes for the remaining code
- 3 +
- 3 INVERT AND
- HERE !
- ' EMITSTRING , \\ compile the final EMITSTRING
- ELSE
- \\ In immediate mode, just read characters and print them until we get
- \\ to the ending double quote. Much simpler than the above code!
- BEGIN
- KEY
- DUP '\"' = IF EXIT THEN
- EMIT
- AGAIN
- THEN
-;
-
-\\ While compiling, [COMPILE] WORD compiles WORD if it would otherwise be IMMEDIATE.
-: [COMPILE] IMMEDIATE
- WORD \\ get the next word
- FIND \\ find it in the dictionary
- >CFA \\ get its codeword
- , \\ and compile that
-;
-
-\\ RECURSE makes a recursive call to the current word that is being compiled.
-\\ Normally while a word is being compiled, it is marked HIDDEN so that references to the
-\\ same word within are calls to the previous definition of the word.
-: RECURSE IMMEDIATE
- LATEST @ >CFA \\ LATEST points to the word being compiled at the moment
- , \\ compile it
-;
-
-\\ ALLOT is used to allocate (static) memory when compiling. It increases HERE by
-\\ the amount given on the stack.
-\\: ALLOT HERE +! ;
-
-
-\\ Finally print the welcome prompt.
-.\" JONESFORTH VERSION \" VERSION @ . CR
-.\" OK \"
-"
-
-_initbufftop:
- .align 4096
-buffend:
-
-currkey:
- .int buffer
-bufftop:
- .int _initbufftop
-