home .. products .. mforth .. source code ..
main.asm
; Copyright (c) 2009-2011, Michael Alyn Miller <malyn@strangeGizmo.com>.
; All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions are met:
;
; 1. Redistributions of source code must retain the above copyright notice
;    unmodified, this list of conditions, and the following disclaimer.
; 2. Redistributions in binary form must reproduce the above copyright notice,
;    this list of conditions and the following disclaimer in the documentation
;    and/or other materials provided with the distribution.
; 3. Neither the name of Michael Alyn Miller nor the names of the contributors
;    to this software may be used to endorse or promote products derived from
;    this software without specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY
; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
; DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


; ======================================================================
; MFORTH Version Information
; ======================================================================

MFORTH_MAJOR    .EQU    1
MFORTH_MINOR    .EQU    1
;MFORTH_CHANGE  .EQU    Supplied on the TASM command line.



; ======================================================================
; MFORTH Register Usage
; ======================================================================
;
; HL - Word Pointer (W); only used by the Address Interpreter, available
;      for general use by CODE words.
; DE - Instruction Pointer (IP); must be preserved with SAVEDE/RESTOREDE.
; BC - Return Stack Pointer (RSP); must be preserved if used (usually by
;      pushing BC onto the stack).  B always contains the TASK page.
; SP - Parameter Stack Pointer (PSP).
; A  - Unused.



; ======================================================================
; MFORTH Memory Maps
; ======================================================================
;
; Model 100 Memory Map:
;
;   +--------------------------------+  0000h (32k) or 2000h (24k)
;   |  Files, BASIC variables, etc.  |
;   +--------------------------------+  [FRETOP]
;   |                                |
;   |          Free Memory           |
;   |                                |
;   +--------------------------------+  SP
;   |             Stack              |
;   +--------------------------------+  [STKTOP]
;   |         System variables       |
;   +--------------------------------+  FFFFh
;
;
; MFORTH Memory Map:
;
;   +--------------------------------+  0000h (32k) or 2000h (24k)
;   |  Files, BASIC variables, etc.  |
;   +--------------------------------+  [FRETOP] aka [TICKTIB]
;   |     Terminal Input Buffer      |
;   +--------------------------------+  [TICKTIB] + TIBSIZE aka [DP] aka HERE
;   |           Dictionary           |
;   |  (see below for temp regions)  |
;   |              vvvv              |
;   |                                |
;   |                                |
;   |              ^^^^              |
;   |  Additional Tasks (256b each)  |
;   +--------------------------------+  xx00h (first whole page below BOPSTK)
;   |  Initial MFORTH Task (Task #0) |
;   +--------------------------------+  [BOPSTK]
;   | Original Stack (unused by us)  |
;   +--------------------------------+  [STKTOP]
;   |         System variables       |
;   +--------------------------------+  FFFFh
;
;
; Transient Regions (based on HERE):
;
;   +--------------------------------+  [TICKTIB] + TIBSIZE aka [DP] aka HERE
;   | Empty; space for the header and|
;   | name of the next definition so |
;   | that CREATE can reference the  |
;   | WORD buffer and not clobber it.|
;   +--------------------------------+  [DP] + WORDOFFSET
;   |      Buffer used by WORD       |
;   |              vvvv              |
;   |              ^^^^              |
;   |       Buffer used by HLD       |
;   +--------------------------------+  [DP] + SQOFFSET aka [DP] + HLDEND
;   |           S" buffer            |
;   +--------------------------------+  [DP] + PADOFFSET
;   |     PAD buffer for Task #0     |
;   +--------------------------------+  [DP] + PADOFFSET + tasknum*PADSIZE
;   | Additional per-task PAD buffers|
;   |              vvvv              |
;   +--------------------------------+
;
;
; Task Page (at xx00h in high memory):
;
;   +--------------------------------+  xx00h
;   |         User Variables         |
;   +--------------------------------+  xx40h
;   |              ^^^^              |
;   |          Return Stack          |
;   +--------------------------------+  xx7Fh
;   | Return/Param Stack Guard Cell  |
;   +--------------------------------+  xx81h
;   |              ^^^^              |
;   |        Parameter Stack         |
;   +--------------------------------+  xxFFh
;
;
; Dictionary Header:
;
;   The MFORTH dictionary uses the layout described by Robert L. Smith
;   in his Forth Dimensions I/5 article titled "A Modest Proposal for
;   Dictionary Headers".
;
;   The 00h byte is called the Name Field Address in MFORTH, even though
;   it only points to the name count and flags.  The Link Field Address
;   points to the Name Field Address of the preceding definition.
;
;   The Profiler Execution Count (the dotted cell at 03h) is only used
;   when the profiler is enabled at build time.  That cell is not used,
;   nor is it even allocated (the fixed size of the dictionary header is
;   only three bytes, in other words) when the profiler is disabled.  The
;   Profiler Execution Count stores the number of times that the word
;   was executed by NEXT when the PROFILING variable is non-zero.
;
;   The header below is for the word DUP; note that the word name is
;   stored in reverse order in memory.
;
;   +--------------------------------+ -03h
;   | 1 |          'P'               |
;   +--------------------------------+ -02h
;   | 0 |          'U'               |
;   +--------------------------------+ -01h
;   | 0 |          'D'               |
;   +--------------------------------+  00h
;   | P | S |     Count              |
;   +--------------------------------+  01h
;   |             Link               |
;   |             Field              |
;   +--------------------------------+  03h
;   .            Profiler            .
;   .        Execution Count         .
;   +--------------------------------+  03h/05h
;   |             Code               |
;   |             Field              |
;   |          (JMP $xxxx)           |
;   +--------------------------------+  06h/08h
;   .           Parameter            .
;   .             Field              .
;   . . . . . . . .  . . . . . . . . .
;
;
; Word List:
;
;   An MFORTH wid is a pointer to a word list (HERE, in the case of word
;   lists created by WORDLIST).  A word list is a single cell that is
;   the equivalent of LATEST for that word list.  The value at the word
;   list is updated as new words are added to the word list.



; ======================================================================
; Undocumented 8085 Opcodes
; ======================================================================

;           INSTR   ARGS OPCODE BYTES   MOD CLASS
.ADDINSTR   DSUB    ""   08     1       NOP 1
.ADDINSTR   LDEH    *    28     2       NOP 1
.ADDINSTR   LDES    *    38     2       NOP 1
.ADDINSTR   LHLX    ""   ED     1       NOP 1
.ADDINSTR   RDEL    ""   18     1       NOP 1
.ADDINSTR   SHLX    ""   D9     1       NOP 1



; ======================================================================
; Model 100/102 Constants
; ======================================================================

FRETOP:     .EQU    0FBB6h      ; Beginning of free space on the machine
                                ; (after all files, BASIC vars, etc.)
STKTOP:     .EQU    0F678h      ; Top of the system stack; system stack
                                ; grows downward, although we don't
                                ; use this stack for anything.
ALTBGN:     .EQU    0FCC0h      ; Beginning of the Alternate LCD buffer
ALTEND:     .EQU    0FDFFh      ; End of the Alternate LCD buffer

STACKGUARD: .EQU    076h        ; The guard value used to see if a Task
                                ; has over-/under-flown a stack.  $76
                                ; is "HLT" just in case someone tries
                                ; to execute the stack pointer.



; ======================================================================
; MFORTH Dictionary Constants
; ======================================================================

NFASZ:      .EQU    1           ; Size of the Name Field.
#DEFINE     INXNFATOLFA(r) INX r

LFASZ:      .EQU    2           ; Size of the Link Field.

#IFNDEF PROFILER
NFATOCFASZ: .EQU    NFASZ+LFASZ
#DEFINE     INXNFATOCFA(r) INX r\ INX r\ INX r
#ELSE
NFATOPECSZ: .EQU    NFASZ+LFASZ
PECSZ:      .EQU    2           ; Size of the Profiler Execution Count.
NFATOCFASZ: .EQU    NFASZ+LFASZ+PECSZ
#DEFINE     INXNFATOCFA(r) INX r\ INX r\ INX r\ INX r\ INX r
#ENDIF

CFASZ:      .EQU    3           ; Size of the Code Field.
#DEFINE     INXCFATOPFA(r) INX r\ INX r\ INX r



; ======================================================================
; MFORTH Constants
; ======================================================================

TIBSIZE:    .EQU    80          ; Length of the Terminal Input Buffer.

WORDOFFSET: .EQU    64          ; Offset from HERE to WORD buffer.
WORDSIZE:   .EQU    258         ; Size of WORD buffer.
HLDEND:     .EQU    WORDOFFSET+WORDSIZE ; End (and thus, start) of HLD.
SQOFFSET:   .EQU    HLDEND      ; Offset from HERE to S" buffer.
SQSIZE:     .EQU    80          ; Size of S" buffer.
PADOFFSET:  .EQU    SQOFFSET+SQSIZE ; Offset from HERE to first PAD buffer.
PADSIZE:    .EQU    256         ; Size of each task's PAD buffer.

MAXICBS:    .EQU    8           ; Maximum number of active input sources.
MAXFCBS:    .EQU    8           ; Maximum number of files open at one time.
MAXSOES:    .EQU    8           ; Max. number of word lists in the search order.



; ======================================================================
; Global variables (stored in the Alternate LCD buffer)
; Max of 320 bytes.
;
; OPON and the variables used by OPON take up the first 23 bytes.
; That leaves 297 bytes remaining.
;
; These globals are shared across all of the active tasks, so they must
; either be safe (not used from multiple tasks at a time; DP is such a
; variable as we atomically add new dictionary entries without an
; intervening PAUSE) or be defined in the manual as not being usable from
; multiple tasks.  All of the terminal input variables fall into this
; category as we specifically state that only a single terminal/operator
; is supported.
; ======================================================================

OPON        .EQU    ALTBGN +  0

#IF (OPONLEN > 16)
#ECHO "\t*** ERROR: OPON is too large; Global Variables expect OPONLEN<=16 ***\n"
#ENDIF

HOLDH:      .EQU    ALTBGN + 16
HOLDD:      .EQU    ALTBGN + 18
INTH:       .EQU    ALTBGN + 20
INTD:       .EQU    ALTBGN + 22

DP:         .EQU    ALTBGN + 24 ; Data Pointer.
BOPSTK:     .EQU    ALTBGN + 26 ; Initial value of SP on entry to MFORTH.
TICKCURRENT: .EQU   ALTBGN + 28 ; Pointer to the compilation word list.
TICKSTATE:  .EQU    ALTBGN + 30 ; Compilation-state flag.
TICKTIB:    .EQU    ALTBGN + 32 ; Terminal Input Buffer pointer.
TICKHLD:    .EQU    ALTBGN + 34 ; Pictured Numeric Output hold pointer.
TICKNUMTASKS:.EQU   ALTBGN + 36 ; Number of tasks
TICKFIRSTTASK:.EQU  ALTBGN + 38 ; Address of the first task (always XX00)
TICKNUMUSERVARS:.EQU ALTBGN+ 40 ; Number of USER variables
SAVED:      .EQU    ALTBGN + 42 ; DE is saved here when used as a temporary.
TICKPREVLEAVE:.EQU  ALTBGN + 44 ; Pointer to the previous LEAVE in a DO..LOOP.
TICKPREVENDB:.EQU   ALTBGN + 46 ; Pointer to the previous ?ENDB in a FORB..NEXTB
TICKICB:    .EQU    ALTBGN + 48 ; Address of the current Input Control Block.
PROFILING:  .EQU    ALTBGN + 50 ; Non-zero if the profiler is on.
TICKTICKS:  .EQU    ALTBGN + 52 ; (double) Number of ticks (4ms) since start.
SAVEB:      .EQU    ALTBGN + 56 ; DE is saved here when used as a temporary.
FORTHWL:    .EQU    ALTBGN + 58 ; FORTH word list.
ASSEMBLERWL:.EQU    ALTBGN + 60 ; ASSEMBLER word list.
; 2 bytes free.
ICBSTART:   .EQU    ALTBGN + 64 ; Start of the 8, 8-byte Input Control Blocks.
FCBSTART:   .EQU    ALTBGN + 128; Start of the 8, 8-byte File Control Blocks.
SOESTART:   .EQU    ALTBGN + 192; Start of the 8, 2-byte Search Order Entries.
; 112 bytes free.



; ======================================================================
; System-provided user variables
;
; User variable offsets are the absolute offset into the Task Page and
; are not an index or count or anything like that.  In other words, the
; third User Variable is 4 (0, 2, 4) and not 2 (0, 1, 2).
; ======================================================================

USERSAVEDSP:.EQU    0           ; Saved stack pointer
USERBASE:   .EQU    2           ; Number-conversion radix pointer.
USERB:      .EQU    4           ; "B" register
USERBEND:   .EQU    6           ; "B" register end
NUMUSERVARS:.EQU    4           ; Total number of user variables.



; ======================================================================
; MFORTH Macros
; ======================================================================

; ----------------------------------------------------------------------
; Save and restore DE (corrupts HL).

#DEFINE     SAVEDE      XCHG\ SHLD SAVED
#DEFINE     RESTOREDE   LHLD SAVED\ XCHG


; ----------------------------------------------------------------------
; Save and restore BC (corrupts HL).

#DEFINE     SAVEBC      MOV H,B\ MOV L,C\ SHLD SAVEB
#DEFINE     RESTOREBC   LHLD SAVEB\ MOV B,H\ MOV C,L



; ======================================================================
; Interrupt Vectors
; ======================================================================

RST0:       .ORG    00000h
            INX     SP
            INX     SP
            JMP     INIT

RST1:       .ORG    00008h
            RET

RST2:       .ORG    00010h
            RET

RST3:       .ORG    00018h
            RET

RST4:       .ORG    00020h
            RET

; Power down interrupt.
TRAP:       .ORG    00024h
            DI
            JMP     INTRAP

RST5:       .ORG    00028h
            RET

RST55:      .ORG    0002Ch
            DI
            JMP     INT55

RST6:       .ORG    00030h
            JMP     STDCALL

RST65:      .ORG    00034h
            DI
            JMP     INT65

RST7:       .ORG    00038h
            RET

RST75:      .ORG    0003Ch
            DI
            JMP     INT75



; ======================================================================
; The Main ROM tests the two bytes at 40h to see if they contain the
; values "A" and "B".  If so, then the Main ROM creates an Option ROM
; trigger file with the name of the remaining six bytes at 42h-47h.
;
; Many Option ROMs use these eight bytes to contain OPON, but we use a
; much more thorough (and thus slightly too-long) variant of OPON that
; protects OPON from interrupts.
;
; In any event, the "AB" mechanism causes problems if you are using REX,
; as the Main ROM will detect that the ROM has changed (between MFORTH
; and REX) and cold boot the computer.  We obviously do not want this to
; happen, so we install/overwrite the trigger file ourselves during our
; initialization process.
; ======================================================================

            .ORG    00040h
            HLT\ HLT\ HLT\ HLT
            HLT\ HLT\ HLT\ HLT



; ======================================================================
; Interrupt Handlers
; ======================================================================

; Power down interrupt
INTRAP:     CALL    INTCALL     ; Let the Main ROM turn off the computer
            .WORD   00024h      ; ..and handle the wake-up sequence.
            RET

INT55:      CALL    INTCALL
            .WORD   0002Ch
            RET

INT65:      CALL    INTCALL
            .WORD   00034h
            RET

INT75:      CALL    INTCALL
            .WORD   0003Ch
            PUSH    H
            PUSH    PSW
            CALL    INCTICKS
            POP     PSW
            POP     H
            RET
INCTICKS:   LXI     H,TICKTICKS
            INR     M
            RNZ
            INX     H
            INR     M
            RNZ
            INX     H
            INR     M
            RNZ
            INR     M
            RET


; ======================================================================
; Option ROM Support Code
; ======================================================================

; ----------------------------------------------------------------------
; STDON: Switches to the Main ROM.
;
; Here is how the stack looks on entry into this call:
;
;   TOS: $0363 (STDCALL: Main ROM code that happens to be "EI; RET")
;   T-1: Main ROM routine to call
;   T-2: RAM address to return to after the call (OPON in AltLCD)
;   T-3: Option ROM address to return to after the call through OPON
;
; This call needs to preserve all registers on entry into the Main ROM.
; Some clever manipulation of the stack is done to ensure that this
; happens.  First, we push A+PSW onto the stack since we will use A for
; manipulating the contents of the Cassette Port register at $FF45.  This
; means that we need the Main ROM to call POP PSW, RET in order to first
; POP PSW before RETurning to the Main ROM routine.  Thankfully the Main ROM
; has those exact two instructions at $26C8.
;
; So we need to get $26C8 onto the stack as the initial return address, but
; to do that we need to use HL.  That means that we have to preserve HL
; without pushing it to the stack (since the stack has to be in a specific
; configuration on entry into Main ROM).  To do that we first push HL to
; the stack, put $26C8 in HL, and then swap the top-of-stack with HL, thus
; putting $26C8 on the stack and restoring HL.
;
; Here is how the stack looks on entry into the Main ROM:
;
;   TOS: $26C8 (Main ROM code that happens to be "POP PSW; RET")
;   T-1: PSW
;   T-2: $0363 (STDCALL: Main ROM code that happens to be "EI; RET")
;   T-3: Main ROM routine to call
;   T-4: RAM address to return to after the call (OPON in AltLCD)
;   T-5: Option ROM address to return to after the call through OPON
;
; The final RET statement is supplied here for posterity, but in fact by
; the point that the PC gets to that location we will be in the Main ROM.
; Thankfully the Main ROM (both M100 and T200) has a RET statement at $008E,
; so STDON is ORGed in such a way as to ensure that the final RET statement
; falls at $008E.

STDON:      .ORG    0007Eh
            PUSH    PSW         ; Preserve A+PSW (which $26C8 POPs).
            PUSH    H           ; Get $26C8 onto the stack
            LXI     H,026C8h    ; ..by swapping the value through HL
            XTHL                ; ..and the top-of-stack.
            LDA     0FF45h      ; Get the saved contents of port $E8,
            ANI     0FEh        ; ..clear the low bit to switch to the Main ROM,
            STA     0FF45h      ; ..updated the saved contents of the port,
            OUT     0E8h        ; ..and then actually modify the port.
STDONRET:   RET                 ; Now in Main ROM; return to $26C8.

#IF (STDONRET != $008E)
#ECHO "\t*** ERROR: STDON is not ORGed properly; RET statement not at $008E ***\n"
#ENDIF


; ----------------------------------------------------------------------
; Switches to the Option ROM after a call to the Main ROM.
;
; Here is how the stack looks on entry into this call:
;
;   TOS: Option ROM address to return to after the call through OPON
;
; INIT copies this routine into RAM so that the OPON routine is available to
; the Main ROM (which is where it is used).  STDON sets up the stack in such
; a way as to ensure that the Main ROM calls this routine after the Main ROM
; call has completed.
;
; This code is interrupt-sensitive, so we disable interrupts until after we
; have properly restore the registers (we only use A+PSW here) and enable
; the Option ROM.

OPONIMG:    DI
            PUSH    PSW
            LDA     0FF45h      ; Get the saved contents of port $E8,
            ORI     001h        ; ..set the low bit to switch to the Option ROM,
            STA     0FF45h      ; ..updated the saved contents of the port,
            OUT     0E8h        ; ..and then actually modify the port.
            POP     PSW         ; Now in Option ROM; restore A+PSW.
            EI
            RET                 ; Return to whatever called STDCALL/INTCALL.
OPONLEN:    .EQU    $-OPONIMG   ; Calculate length of OPON for use by INIT.



; ----------------------------------------------------------------------
; Calls a routine in the Main ROM.
;
; This call needs to preserve all registers on entry into the Main ROM
; and cannot use a register to specify the address of the Main ROM routine
; that is being called.  Instead, the Main ROM address is contained in the
; next two bytes of the instruction stream, which are accessed by popping
; the return address from the stack and dereferencing that pointer.  The
; real return address is then computed by skipping over the Main ROM address
; that was embedded in the instruction stream and pushing that value onto
; the stack.
;
; This entire process is interrupt-sensitive, so we disable interrupts here
; and then have the Main ROM enable interrupts before it calls the Main ROM
; routine that we are trying to invoke.  The Main ROM has the instructions
; "EI; RET" at address $0363, so we push that onto the stack as the initial
; address to return to once in Main ROM.  After that we proceed to RET to
; the Main ROM routine that we want to call, then OPON, and then back into
; the Option ROM code that CALLed STDCALL.
;
; Here is the layout of the stack at the point where we JMP STDON:
;
;   TOS: $0363 (Main ROM code that happens to be "EI; RET")
;   T-1: Main ROM routine to call
;   T-2: RAM address to return to after the call (OPON in AltLCD)
;   T-3: Option ROM address to return to after the call through OPON
;
; That stack is obviously clear of all of those items on return from STDCALL.

STDCALL:    DI

            SHLD    HOLDH       ; Preserve HL
            XCHG                ; ..and DE
            SHLD    HOLDD       ; ..since we will need them as temporaries.

            POP     H           ; Get the STDCALL return address,
            MOV     E,M         ; ..which is actually the address of the
            INX     H           ; ..Main ROM routine that we want to call,
            MOV     D,M         ; ..and put that address in DE.  Then point
            INX     H           ; ..HL at the actual Option ROM return address
            PUSH    H           ; ..and push that address (T-3) to the stack.

            LXI     H,OPON      ; Return from Main ROM through OPON, which we
            PUSH    H           ; ..push onto the stack (T-2).

            PUSH    D           ; Push the Main ROM routine to call (T-1).

            LHLD    HOLDD       ; Restore DE
            XCHG                ; ..and HL
            LHLD    HOLDH       ; ..so that they can be used as Main ROM args.

            PUSH    H           ; Get $0363 onto the stack
            LXI     H,00363h    ; ..by swapping the value through HL
            XTHL                ; ..and the top-of-stack.

            JMP     STDON       ; Jump to STDON to get into Main ROM.


; ----------------------------------------------------------------------
; Calls an interrupt handler routine in the Main ROM.
;
; This code is almost identical to STDCALL except that we do not need to
; disable interrupts and we definitely do not want to re-enable them as
; part of the jump into the Main ROM (which STDCALL accomplishes by pushing
; $0363 onto the stack).
;
; Here is the layout of the stack at the point where we JMP STDON:
;
;   TOS: Main ROM routine to call
;   T-1: RAM address to return to after the call (OPON in AltLCD)
;   T-2: Option ROM address to return to after the call through OPON
;
; That stack is obviously clear of all of those items on return from INTCALL.


INTCALL:    SHLD    INTH        ; Preserve HL
            XCHG                ; ..and DE
            SHLD    INTD        ; ..since we will need them as temporaries.

            POP     H           ; Get the INTCALL return address,
            MOV     E,M         ; ..which is actually the address of the
            INX     H           ; ..Main ROM routine that we want to call,
            MOV     D,M         ; ..and put that address in DE.  Then point
            INX     H           ; ..HL at the actual Option ROM return address
            PUSH    H           ; ..and push that address (T-2) to the stack.

            LXI     H,OPON      ; Return from Main ROM through OPON, which we
            PUSH    H           ; ..push onto the stack (T-1).

            PUSH    D           ; Push the Main ROM routine to call (TOS).

            LHLD    INTD        ; Restore DE
            XCHG                ; ..and HL
            LHLD    INTH        ; ..so that they can be used as Main ROM args.

            JMP     STDON       ; Jump to STDON to get into Main ROM.



; ======================================================================
; MFORTH Initialization
; ======================================================================

; ----------------------------------------------------------------------
; Initialize MFORTH.
;
; This routine is called on a MFORTH cold-start each time that MFORTH is
; activated from the Main ROM Menu.  Note that MFORTH does not maintain
; any state between invocations.
;
; We disable interrupts, copy OPON into high memory, call LNKFIL to ensure
; that all of the cached file start addresses are accurate, set up our memory
; map (by figuring out where free space starts and ends), initialize the
; return stack, and JMP to the MFORTH COLD word.

INIT:       DI

            ; Copy OPON into high memory.
            LXI     D,OPONIMG   ; First source byte in DE
            LXI     H,OPON      ; First target byte in HL
            MVI     B,OPONLEN   ; Number of bytes to copy in B
_init1:     LDAX    D           ; Load value from [DE]
            MOV     M,A         ; Save value to [HL]
            INX     D           ; Next source byte
            INX     H           ; Next target byte
            DCR     B           ; Decrement counter
            JNZ     _init1      ; Keep looping until all bytes are copied

            ; Call LNKFIL to ensure that the file addresses are accurate.
            CALL    STDCALL     ; Call the
            .WORD   02146h      ; .."LNKFIL" routine.

            ; Set up our memory map.
            LHLD    FRETOP      ; Store the beginning of free memory to HL
            SHLD    TICKTIB     ; ..and then to the Terminal Input Buffer.
            LXI     B,TIBSIZE   ; Add TIBSIZE
            DAD     B           ; ..to HL
            SHLD    DP          ; ..and then store that in the Data Pointer.
            LXI     H,0         ; Get the location of the
            DAD     SP          ; ..OS stack pointer on entry into MFORTH
            SHLD    BOPSTK      ; ..and store it in in BOPSTK.

            ; Initialize the system word lists.
            LXI     H,_latestFORTH-NFATOCFASZ
            SHLD    FORTHWL
            LXI     H,_latestASSEMBLER-NFATOCFASZ
            SHLD    ASSEMBLERWL

            ; Make the FORTH word list the current compilation word
            ; list; ABORT will initialize the default search order.
            LXI     H,FORTHWL
            SHLD    TICKCURRENT

            ; Four USER variables are currently in use (SavedSP, Base, B, Bend).
            LXI     H,NUMUSERVARS
            SHLD    TICKNUMUSERVARS

            ; Create the first task, which needs to be in place for us
            ; to enter in to the Inner Interpreter (which needs a return
            ; stack, for example).  The first task begins at the page
            ; that is below the page where the SP currently resides.  This
            ; could put us right below the system stack (if SP is at xx00,
            ; for example), but that's okay because we don't actually use
            ; the system stack for anything (each task has its own stack).
            LXI     H,1
            SHLD    TICKNUMTASKS

            LXI     H,0         ; Get SP
            DAD     SP          ; ..into HL,
            LXI     B,0100h     ; ..then subtract 256
            DSUB                ; ..from HL to get into the middle of the page,
            MVI     L,0         ; ..then clear L to get to the start of the page
            SHLD    TICKFIRSTTASK ;.and then store address of the first task.

            MOV     B,H         ; Put the current task's page into its home in B
            MVI     C,07fh      ; ..and initialize the return stack to xx7F.

            MVI     M,STACKGUARD; Set the Saved Stack Pointer to the guard
            INX     H           ; ..value since this task will be running (not
            MVI     M,STACKGUARD; ..resumed by PAUSE) after the JMP to COLD.

            MVI     L,080h      ; Set HL to the parameter stack guard cell
            MVI     M,STACKGUARD; ..then move the guard value
            INX     H           ; ..into both bytes
            MVI     M,STACKGUARD; ..of that cell.

            MVI     L,0ffh      ; Initialize the SP, which begins at xxFF.
            SPHL

            ; Disable the profiler.
            LXI     H,0
            SHLD    PROFILING

            ; Reset the tick counter.
            LXI     H,0
            SHLD    TICKTICKS
            SHLD    TICKTICKS+2

            ; Enable interrupts and jump to COLD (which never returns).
            EI
            LXI     H,COLD      ; Point W at COLD (needed by ENTER).
            LXI     D,COLD      ; Point IP at COLD (needed by NEXT).
            JMP     COLD



; ======================================================================
; MFORTH Kernel and FORTH Word List
; ======================================================================

; Kernel routines and macros.
#include "kernel.asm"

; ANS word sets.
LINK_CORE       .EQU    NFATOCFASZ
#include "answords/core.asm"

LINK_COREEXT    .EQU    LAST_CORE
#include "answords/core-ext.asm"

LINK_DOUBLE     .EQU    LAST_COREEXT
#include "answords/double.asm"

LINK_FACILITY   .EQU    LAST_DOUBLE
#include "answords/facility.asm"

LINK_FACILITYEXT .EQU   LAST_FACILITY
#include "answords/facility-ext.asm"

LINK_FILE       .EQU    LAST_FACILITYEXT
#include "answords/file.asm"

LINK_SEARCH     .EQU    LAST_FILE
#include "answords/search.asm"

LINK_SEARCHEXT  .EQU    LAST_SEARCH
#include "answords/search-ext.asm"

LINK_STRING     .EQU    LAST_SEARCHEXT
#include "answords/string.asm"

LINK_TOOLS      .EQU    LAST_STRING
#include "answords/tools.asm"

LINK_TOOLSEXT   .EQU    LAST_TOOLS
#include "answords/tools-ext.asm"

; MFORTH word sets.
LINK_BREG       .EQU    LAST_TOOLSEXT
#include "mforthwords/breg.asm"

LINK_MFORTH     .EQU    LAST_BREG
#include "mforthwords/mforth.asm"

LINK_TASK       .EQU    LAST_MFORTH
#include "mforthwords/task.asm"

#IFNDEF PROFILER
_latestFORTH    .EQU    LAST_TASK
#ELSE
LINK_PROFILER   .EQU    LAST_TASK
#include "mforthwords/profiler.asm"

_latestFORTH    .EQU    LAST_PROFILER
#ENDIF



; ======================================================================
; Additional Word Lists
; ======================================================================

; ASSEMBLER Word List
LINK_ASSEMBLER  .EQU    NFATOCFASZ
#include "mforthwords/assembler.asm"

_latestASSEMBLER .EQU   LAST_ASSEMBLER



; ======================================================================
; Display ROM statistics.
; ======================================================================

#ECHO "\tEnd of ROM: "
#ECHO $
#ECHO "\n"



; ======================================================================
; Zero-out the rest of the ROM.
; ======================================================================

            .ORG    07FFFh
            .BYTE   0



; ======================================================================
; Perfect Hash of ROM Dictionary
; ======================================================================

#IFNDEF PHASH
; Temporarily store _latestFORTH at 07FFE for use by phashgen.exe.
            .ORG    07FFEh
            .WORD   _latestFORTH-NFATOCFASZ
#ELSE
; phash.asm already generated.
#include "phash.asm"
#ENDIF



; ======================================================================
; End of ROM.
; ======================================================================

            .END