home .. products .. mforth .. source code ..
file.asm
; Copyright (c) 2009-2010, 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.


; ======================================================================
; FILE Constants
; ======================================================================

; ----------------------------------------------------------------------
; I/O Result Codes
;
IOROK:      .EQU    0           ; No error.
IORFNF:     .EQU    1           ; File not found or invalid filename (>6 chars)
IORRDONLY:  .EQU    2           ; Files cannot be opened R/W.
IORBADFILEID:.EQU   3           ; Bad fileid.



; ======================================================================
; FILE Words
; ======================================================================

; ----------------------------------------------------------------------
; BIN [FILE] 11.6.1.0765 ( fam1 -- fam2 )
;
; Modify the implementation-defined file access method fam1 to additionally
; select a "binary", i.e., not line oriented, file access method, giving
; access method fam2.
;
; ---
; No-op in MFORTH as files are not opened in any specific mode.

            LINKTO(LINK_FILE,0,3,'N',"IB")
BIN:        NEXT


; ----------------------------------------------------------------------
; CLOSE-FILE [FILE] 11.6.1.0900 ( fileid -- ior )
;
; Close the file identified by fileid.  ior is the implementation-defined
; I/O result code.
;
; ---
; : CLOSE-FILE ( fileid -- ior)
;   FILEID>FCB? ?DUP IF EXIT THEN
;   FCBADDR +  0 SWAP !  IOROK ;

            LINKTO(BIN,0,10,'E',"LIF-ESOLC")
CLOSEFILE:  JMP     ENTER
            .WORD   FILEIDTOFCBQ,QDUP,zbranch,_closefile1,EXIT
_closefile1:.WORD   LIT,FCBADDR,PLUS,ZERO,SWAP,STORE,LIT,IOROK,EXIT


; ----------------------------------------------------------------------
; CREATE-FILE [FILE] 11.6.1.1010 ( c-addr u fam -- fileid ior )
;
; Create the file named in the character string specified by c-addr and u,
; and open it with file access method fam.  The meaning of values of fam is
; implementation defined.  If a file with the same name already exists,
; recreate it as an empty file.
;
; If the file was successfully created and opened, ior is zero, fileid is
; its identifier, and the file has been positioned to the start of the file.
;
; Otherwise, ior is the implementation-defined I/O result code and fileid is
; undefined.
;
; ---
; Always fails in MFORTH as the file system is read-only.

            LINKTO(CLOSEFILE,0,11,'E',"LIF-ETAERC")
CREATEFILE: JMP     ENTER
            .WORD   DROP,TWODROP,ZERO,LIT,IORRDONLY,EXIT


; ----------------------------------------------------------------------
; DELETE-FILE [FILE] 11.6.1.1190 ( c-addr u -- ior )
;
; Delete the file named in the character string specified by c-addr u.
; ior is the implementation-defined I/O result code.
;
; ---
; Always fails in MFORTH as the file system is read-only.

            LINKTO(CREATEFILE,0,11,'E',"LIF-ETELED")
DELETEFILE: JMP     ENTER
            .WORD   TWODROP,LIT,IORRDONLY,EXIT


; ----------------------------------------------------------------------
; FILE-POSITION [FILE] 11.6.1.1520 ( fileid -- ud ior )
;
; ud is the current file position for the file identified by fileid.  ior
; is the implementation-defined I/O result code.  ud is undefined if ior is
; non-zero.
;
; ---
; : FILE-POSITION ( fileid -- ud ior)
;   FILEID>FCB? ?DUP IF 0 SWAP EXIT THEN
;   DUP FCBPOS + @  SWAP FCBADDR + @  -  IOROK ;

            LINKTO(DELETEFILE,0,13,'N',"OITISOP-ELIF")
FILEPOSITION:JMP    ENTER
            .WORD   FILEIDTOFCBQ,QDUP,zbranch,_filepos1,ZERO,SWAP,EXIT
_filepos1:  .WORD   DUP,LIT,FCBPOS,PLUS,FETCH,SWAP,LIT,FCBADDR,PLUS,FETCH
            .WORD   MINUS,LIT,IOROK,EXIT


; ----------------------------------------------------------------------
; FILE-SIZE [FILE] 11.6.1.1522 ( fileid -- ud ior )
;
; ud is the size, in characters, of the file identified by fileid.  ior
; is the implementation-defined I/O result code.  This operation does not
; affect the value returned by FILE-POSITION.  ud is undefined if ior is
; non-zero.
;
; ---
; : FILE-SIZE ( fileid -- ud ior)
;   FILEID>FCB? ?DUP IF 0 SWAP EXIT THEN
;   DUP FCBEND + @  SWAP FCBADDR + @  -  IOROK ;

            LINKTO(FILEPOSITION,0,9,'E',"ZIS-ELIF")
FILESIZE:    JMP    ENTER
            .WORD   FILEIDTOFCBQ,QDUP,zbranch,_filesize1,ZERO,SWAP,EXIT
_filesize1: .WORD   DUP,LIT,FCBEND,PLUS,FETCH,SWAP,LIT,FCBADDR,PLUS,FETCH
            .WORD   MINUS,LIT,IOROK,EXIT


; ----------------------------------------------------------------------
; INCLUDE-FILE [FILE] 11.6.1.1717 ( i*x fileid -- j*x )
;
; Remove fileid from the stack.  Save the current input source specification,
; including the current value of SOURCE-ID.  Store fileid in SOURCE-ID.  Make
; the file specified by fileid the input source.  Store zero in BLK.  Other
; stack effects are due to the words INCLUDEd.
;
; Repeat until end of file:  read a line from the file, fill the input buffer
; from the contents of that line, set >IN to zero, and interpret.
;
; Text interpretation begins at the file position where the next file read
; would occur.
;
; When the end of the file is reached, close the file and restore the input
; source specification to its saved value.
;
; An ambiguous condition exists if fileid is invalid, if there is an I/O
; exception reading fileid, or if an I/O exception occurs while closing
; fileid.  When an ambiguous condition exists, the status (open or closed)
; of any files that were being interpreted is implementation-defined.
;
; ---
; \ TODO: Store zero in BLK.
; : INCLUDE-FILE  ( i*x fileid -- j*x)
;   PUSHICB  ICB ICBSOURCEID + !
;   BEGIN  SOURCE-ID FILEID>FCB DUP  2@ <>  WHILE
;       DUP @ ( fcb start) OVER NEXT-LINE ( fcb start end crlfend)
;       -ROT ICB 2! ( fcb crlfend) SWAP !
;       INTERPRET
;   REPEAT
;   DROP  SOURCE-ID CLOSE-FILE DROP  POPICB ;

            LINKTO(FILESIZE,0,12,'E',"LIF-EDULCNI")
INCLUDEFILE:JMP     ENTER
            .WORD   PUSHICB,ICB,LIT,ICBSOURCEID,PLUS,STORE
_includefile1:.WORD SOURCEID,FILEIDTOFCB,DUP
            .WORD       TWOFETCH,NOTEQUALS,zbranch,_includefile2
            .WORD   DUP,FETCH,OVER,NEXTLINE
            .WORD   DASHROT,ICB,TWOSTORE,SWAP,STORE
            .WORD   INTERPRET,branch,_includefile1
_includefile2:.WORD DROP,SOURCEID,CLOSEFILE,DROP,POPICB,EXIT



; ----------------------------------------------------------------------
; INCLUDED [FILE] 11.6.1.1718 ( i*x c-addr u -- j*x )
;
; Remove c-addr u from the stack.  Save the current input source specification,
; including the current value of SOURCE-ID.  Open the file specified by
; c-addr u, store the resulting fileid in SOURCE-ID, and make it the input
; source.  Store zero in BLK.  Other stack effects are due to the words
; included.
;
; Repeat until end of file:  read a line from the file, fill the input buffer
; from the contents of that line, set >IN to zero, and interpret.
;
; Text interpretation begins at the file position where the next file read
; would occur.
;
; When the end of the file is reached, close the file and restore the input
; source specification to its saved value.
;
; An ambiguous condition exists if the named file can not be opened, if an I/O
; exception occurs reading the file, or if an I/O exception occurs while
; closing the file.  When an ambiguous condition exists, the status (open or
; closed) of any files that were being interpreted is implementation-defined.
;
; ---
; : INCLUDED ( i*x c-addr u -- j*x)
;   R/O OPEN-FILE ABORT" Unknown file" INCLUDE-FILE ;

            LINKTO(INCLUDEFILE,0,8,'D',"EDULCNI")
INCLUDED:   JMP     ENTER
            .WORD   RO,OPENFILE,zbranch,_included1
            .WORD   PSQUOTE,12
            .BYTE   "Unknown file"
            .WORD   TYPE,ABORT
_included1: .WORD   INCLUDEFILE,EXIT


; ----------------------------------------------------------------------
; OPEN-FILE [FILE] 11.6.1.1970 ( c-addr u fam -- fileid ior )
;
; Open the file named in the character string specified by c-addr u,
; with file access method indicated by fam.  The meaning of values of
; fam is implementation defined.
;
; If the file is successfully opened, ior is zero, fileid is its identifier,
; and the file has been positioned to the start of the file.
;
; Otherwise, ior is the implementation-defined I/O result code and fileid
; is undefined.
;
; ---
; : OPEN-FILE ( c-addr u fam -- fileid ior)
;   R/O <> IF 2DROP 0 IORRDONLY EXIT THEN
;   FIND-FILE ?DUP IF 0 SWAP EXIT THEN  NEW-FCB >R  ( file-addr file-len  R:fcb)
;   OVER + R@ FCBEND + !
;   DUP R@ FCBADDR + !  R@ FCBPOS + !
;   R@ FCBGENNUM + DUP C@ 1+ SWAP C!
;   R> FCB>FILEID IOROK ;

            LINKTO(INCLUDED,0,9,'E',"LIF-NEPO")
OPENFILE:   JMP     ENTER
            .WORD   RO,NOTEQUALS,zbranch,_openfile1
            .WORD   TWODROP,ZERO,LIT,IORRDONLY,EXIT
_openfile1: .WORD   FINDFILE,QDUP,zbranch,_openfile2
            .WORD   ZERO,SWAP,EXIT
_openfile2: .WORD   NEWFCB,TOR
            .WORD   OVER,PLUS,RFETCH,LIT,FCBEND,PLUS,STORE
            .WORD   DUP,RFETCH,LIT,FCBADDR,PLUS,STORE
            .WORD       RFETCH,LIT,FCBPOS,PLUS,STORE
            .WORD   RFETCH,LIT,FCBGENNUM,PLUS,DUP,CFETCH,ONEPLUS,SWAP,CSTORE
            .WORD   RFROM,FCBTOFILEID,LIT,IOROK,EXIT


; ----------------------------------------------------------------------
; R/O [FILE] 11.6.1.2054 "r-o" ( -- fam )
;
; fam is the implementation-defined value for selecting the "read only"
; file access method.

            LINKTO(OPENFILE,0,3,'O',"/R")
RO:         JMP     ENTER
            .WORD   LIT,00000001b,EXIT


; ----------------------------------------------------------------------
; R/W [FILE] 11.6.1.2056 "r-w" ( -- fam )
;
; fam is the implementation-defined value for selecting the "read/write"
; file access method.

            LINKTO(RO,0,3,'W',"/R")
RW:         JMP     ENTER
            .WORD   LIT,00000011b,EXIT


; ----------------------------------------------------------------------
; READ-FILE [FILE] 11.6.1.2080 ( c-addr u1 fileid -- u2 ior )
;
; Read u1 consecutive characters to c-addr from the current position of the
; file identified by fileid.
;
; If u1 characters are read without an exception, ior is zero and u2 is equal
; to u1.
;
; If the end of the file is reached before u1 characters are read, ior is zero
; and u2 is the number of characters actually read.
;
; If the operation is initiated when the value returned by FILE-POSITION is
; equal to the value returned by FILE-SIZE for the file identified by fileid,
; ior is zero and u2 is zero.
;
; If an exception occurs, ior is the implementation-defined I/O result code,
; and u2 is the number of characters transferred to c-addr without an exception.
;
; An ambiguous condition exists if the operation is initiated when the value
; returned by FILE-POSITION is greater than the value returned by FILE-SIZE
; for the file identified by fileid, or if the requested operation attempts
; to read portions of the file not written.
;
; At the conclusion of the operation, FILE-POSITION returns the next file
; position after the last character read.
;
; ---
; \ u1 bytes are copied first, then u2 is determined later.  We might "read"
; \ more bytes than are remaining, but that's not an issue for us given that
; \ all of our files are in memory anyway.
; : READ-FILE ( c-addr u1 fileid -- u2 ior)
;   FILEID>FCB? ?DUP IF NIP NIP 0 SWAP EXIT THEN  ( ca u1 fcb)
;   DUP >R @ ( ca u1 pos R:fcb) -ROT DUP >R MOVE R> ( u1 R:fcb)
;   R@ 2@ - ( u1 rem R:fcb) MIN  DUP R> ( cnt cnt fcb) +!  IOROK ;

            LINKTO(RW,0,9,'E',"LIF-DAER")
READFILE:    JMP    ENTER
            .WORD   FILEIDTOFCBQ,QDUP,zbranch,_readfile1,NIP,NIP,ZERO,SWAP,EXIT
_readfile1: .WORD   DUP,TOR,FETCH,DASHROT,DUP,TOR,MOVE,RFROM
            .WORD   RFETCH,TWOFETCH,MINUS,MIN
            .WORD   DUP,RFROM,PLUSSTORE,LIT,IOROK,EXIT


; ----------------------------------------------------------------------
; READ-LINE [FILE] 11.6.1.2090 ( c-addr u1 fileid -- u2 flag ior )
;
; Read the next line from the file specified by fileid into memory at the
; address c-addr.  At most u1 characters are read.  Up to two implementation-
; defined line-terminating characters may be read into memory at the end of
; the line, but are not included in the count u2.  The line buffer provided
; by c-addr should be at least u1+2 characters long.
;
; If the operation succeeded, flag is true and ior is zero.  If a line
; terminator was received before u1 characters were read, then u2 is the
; number of characters, not including the line terminator, actually read
; (0 <= u2 <= u1).  When u1 = u2, the line terminator has yet to be reached.
;
; If the operation is initiated when the value returned by FILE-POSITION is
; equal to the value returned by FILE-SIZE for the file identified by fileid,
; flag is false, ior is zero, and u2 is zero.  If ior is non-zero, an
; exception occurred during the operation and ior is the implementation-
; defined I/O result code.
;
; An ambiguous condition exists if the operation is initiated when the value
; returned by FILE-POSITION is greater than the value returned by FILE-SIZE
; for the file identified by fileid, or if the requested operation attempts
; to read portions of the file not written.
;
; At the conclusion of the operation, FILE-POSITION returns the next file
; position after the last character read.
;
; ---
; : READ-LINE ( c-addr u1 fileid -- u2 flag ior)
;   FILEID>FCB? ?DUP IF NIP NIP 0 SWAP EXIT THEN  ( ca u1 fcb)
;   DUP 2@ - 0= IF DROP 2DROP 0 0 IOROK EXIT THEN
;   DUP >R @ ( ca u1 pos R:fcb) -ROT COPY-LINE ( u2 cnt R:fcb)
;   R> +! -1 IOROK ;

            LINKTO(READFILE,0,9,'E',"NIL-DAER")
READLINE:    JMP    ENTER
            .WORD   FILEIDTOFCBQ,QDUP,zbranch,_readline1,NIP,NIP,ZERO,SWAP,EXIT
_readline1: .WORD   DUP,TWOFETCH,MINUS,ZEROEQUALS,zbranch,_readline2
            .WORD       DROP,TWODROP,ZERO,ZERO,LIT,IOROK,EXIT
_readline2: .WORD   DUP,TOR,FETCH,DASHROT,COPYLINE
            .WORD   RFROM,PLUSSTORE,LIT,-1,LIT,IOROK,EXIT


; ----------------------------------------------------------------------
; REPOSITION-FILE [FILE] 11.6.1.2142 ( ud fileid -- ior )
;
; Reposition the file identified by fileid to ud.  ior is the implementation-
; defined I/O result code.  An ambiguous condition exists if the file is
; positioned outside the file boundaries.
;
; At the conclusion of the operation, FILE-POSITION returns the value ud.
;
; ---
; : REPOSITION-FILE ( ud fileid -- ior)
;   FILEID>FCB? ?DUP IF DROP EXIT THEN  DUP  FCBADDR + @ ROT +  SWAP !  IOROK ;

            LINKTO(READLINE,0,15,'E',"LIF-NOITISOPER")
REPOSFILE:   JMP    ENTER
            .WORD   FILEIDTOFCBQ,QDUP,zbranch,_reposfile,DROP,EXIT
_reposfile: .WORD   DUP,LIT,FCBADDR,PLUS,FETCH,ROT,PLUS,SWAP,STORE
            .WORD   LIT,IOROK,EXIT


; ----------------------------------------------------------------------
; RESIZE-FILE [FILE] 11.6.1.2147 ( ud fileid -- ior )
;
; Set the size of the file identified by fileid to ud.  ior is the
; implementation-defined I/O result code.
;
; If the resultant file is larger than the file before the operation, the
; portion of the file added as a result of the operation might not have been
; written.
;
; At the conclusion of the operation, FILE-SIZE returns the value ud and
; FILE-POSITION returns an unspecified value.
;
; ---
; Always fails in MFORTH as the file system is read-only.

            LINKTO(REPOSFILE,0,11,'E',"LIF-EZISER")
RESIZEFILE: JMP     ENTER
            .WORD   TWODROP,LIT,IORRDONLY,EXIT


; ----------------------------------------------------------------------
; W/O [FILE] 11.6.1.2425 "w-o" ( -- fam )
;
; fam is the implementation-defined value for selecting the "write only"
; file access method.

            LINKTO(RESIZEFILE,0,3,'O',"/W")
WO:         JMP     ENTER
            .WORD   LIT,00000010b,EXIT


; ----------------------------------------------------------------------
; WRITE-FILE [FILE] 11.6.1.2480 ( c-addr u fileid -- ior )
;
; Write u characters from c-addr to the file identified by fileid starting
; at its current position.  ior is the implementation-defined I/O result code.
;
; At the conclusion of the operation, FILE-POSITION returns the next file
; position after the last character written to the file, and FILE-SIZE returns
; a value greater than or equal to the value returned by FILE-POSITION.
;
; ---
; Always fails in MFORTH as the file system is read-only.

            LINKTO(WO,0,10,'E',"LIF-ETIRW")
WRITEFILE:  JMP     ENTER
            .WORD   DROP,TWODROP,LIT,IORRDONLY,EXIT


; ----------------------------------------------------------------------
; WRITE-LINE [FILE] 11.6.1.2485 ( c-addr u fileid -- ior )
;
; Write u characters from c-addr followed by the implementation-dependent
; line terminator to the file identified by fileid starting at its current
; position.  ior is the implementation-defined I/O result code.
;
; At the conclusion of the operation, FILE-POSITION returns the next file
; position after the last character written to the file, and FILE-SIZE returns
; a value greater than or equal to the value returned by FILE-POSITION.
;
; ---
; Always fails in MFORTH as the file system is read-only.

            LINKTO(WRITEFILE,0,10,'E',"NIL-ETIRW")
WRITELINE:  JMP     ENTER
            .WORD   DROP,TWODROP,LIT,IORRDONLY,EXIT



; ======================================================================
; FILE Constants (implementation details)
; ======================================================================

; ----------------------------------------------------------------------
; File Control Block
;
; Stores information about an open file.  The elements are ordered such
; that 2@ on an FCB will put END and POS on the stack in that order.  You
; can then calculate the remaining bytes in the file (the most common
; operation on an FCB if you assume that READ-* is the most commonly-called
; FILE word) with "2@ -" and without any indexing into the FCB.  This is
; also the reason that we store the end address of the file instead of the
; length of the file; we only need the length for FILE-SIZE, but we need
; the end address for every READ-* (to ensure that we do not read past the
; end of the file).  Finally, storing POS first means that you can access
; the absolute file position without having to adjust the FCB address.  In
; other words, an FCB address is also the address of the POS cell for that
; FCB.

FCBPOS:     .EQU    0           ; Offset from FCB to position in file.
FCBEND:     .EQU    2           ; Offset to end address of file.
FCBADDR:    .EQU    4           ; Offset to address of file.
FCBGENNUM:  .EQU    6           ; Offset to generation number.


; ======================================================================
; FILE Words (implementation details)
; ======================================================================

; ----------------------------------------------------------------------
; FCB>FILEID [MFORTH] "fcb-to-fileid" ( fcb-addr -- fileid )
;
; Return fileid given a valid File Control Block address (fcb-addr).
;
; Note that we add one to the zero-based file address in order to support
; the semantics of SOURCE-ID, which requires that zero refer to the user
; input device.  The first FCB, when its generation number is zero, would
; otherwise produce a fileid of zero.
;
; ---
; : FCB>FILEID ( fcb-addr -- fileid)
;   DUP FCBGENNUM + C@ 8 LSHIFT  SWAP FCBSTART - 1+  OR ;

            LINKTO(WRITELINE,0,10,'D',"IELIF>BCF")
FCBTOFILEID:JMP     ENTER
            .WORD   DUP,LIT,FCBGENNUM,PLUS,CFETCH,LIT,8,LSHIFT
            .WORD   SWAP,LIT,FCBSTART,MINUS,ONEPLUS,OR,EXIT


; ----------------------------------------------------------------------
; FILEID>FCB [MFORTH] "fileid-to-fcb" ( fileid -- fcb-addr )
;
; Return the address of the File Control Block for the given fileid.  An
; ambiguous condition exists if fileid is invalid.
;
; ---
; : FILEID>FCB ( fileid -- fcb-addr)   255 AND 1- FCBSTART + ;

            LINKTO(FCBTOFILEID,0,10,'B',"CF>DIELIF")
FILEIDTOFCB: JMP    ENTER
            .WORD   LIT,255,AND,ONEMINUS,LIT,FCBSTART,PLUS,EXIT


; ----------------------------------------------------------------------
; FILEID>FCB? [MFORTH] "fileid-to-fcb-question" ( fileid -- ior | fcb-addr 0 )
;
; Return the address of the File Control Block for the given fileid, or
; ior if the fileid is invalid.
;
; ---
; : FILEID>FCB? ( fileid -- ior | fcb-addr 0 )
;   DUP 8 RSHIFT  SWAP 255 AND ( gen fcboff)
;   DUP 0=  OVER [ MAXFCBS 2* 2* 2* ] >  OR IF 2DROP IORBADFILEID EXIT THEN
;   1- FCBSTART +  DUP FCBGENNUM + C@ ROT <> IF DROP IORBADFILEID EXIT THEN
;   DUP FCBADDR + @ 0= IF DROP IORBADFILEID EXIT THEN
;   IOROK ;

            LINKTO(FILEIDTOFCB,0,11,'?',"BCF>DIELIF")
FILEIDTOFCBQ:JMP    ENTER
            .WORD   DUP,LIT,8,RSHIFT,SWAP,LIT,255,AND
            .WORD   DUP,ZEROEQUALS,OVER,LIT,MAXFCBS*8,GREATERTHAN
            .WORD       OR,zbranch,_fileidtofcbq1
            .WORD   TWODROP,LIT,IORBADFILEID,EXIT
_fileidtofcbq1:.WORD ONEMINUS,LIT,FCBSTART,PLUS,DUP,LIT,FCBGENNUM,PLUS
            .WORD       CFETCH,ROT,NOTEQUALS,zbranch,_fileidtofcbq2
            .WORD   DROP,LIT,IORBADFILEID,EXIT
_fileidtofcbq2:.WORD DUP,LIT,FCBADDR,PLUS,FETCH,ZEROEQUALS,zbranch,_fileidtofcbq3
            .WORD       DROP,LIT,IORBADFILEID,EXIT
_fileidtofcbq3:.WORD LIT,IOROK,EXIT


; ----------------------------------------------------------------------
; FIND-FILE [MFORTH] "find-file" ( c-addr u -- ior | file-addr file-len 0 )
;
; Find the file named in the character string specified by c-addr u.  No
; extension is expected, FIND-FILE will append the ".DO" extension.  If
; the file is found the address and length of the file and 0 will be
; returned, otherwise an ior will be returned.
;
; ---
; : FIND-FILE ( ca u -- ior | fa fl 0)
;   DUP 6 > IF 2DROP IORFNF EXIT THEN
;   TUCK  FILNAME SWAP MOVE
;   6 SWAP ( 6 u) ?DO BL FILNAME I + C! LOOP
;   FILNAME 6 +  [CHAR] D OVER C!  [CHAR] O SWAP 1+ C!
;   SRCNAM ;

            LINKTO(FILEIDTOFCBQ,0,9,'E',"LIF-DNIF")
FINDFILE:   JMP     ENTER
            .WORD   DUP,LIT,6,GREATERTHAN,zbranch,_findfile1
            .WORD   TWODROP,LIT,IORFNF,EXIT
_findfile1: .WORD   TUCK,LIT,0FC93h,SWAP,MOVE
            .WORD   LIT,6,SWAP,pqdo,_findfile3
_findfile2: .WORD   BL,LIT,0FC93h,I,PLUS,CSTORE,ploop,_findfile2
_findfile3: .WORD   LIT,0FC93h,LIT,6,PLUS
            .WORD   LIT,'D',OVER,CSTORE,LIT,'O',SWAP,ONEPLUS,CSTORE
            .WORD   SRCNAM,EXIT


; ----------------------------------------------------------------------
; INIT-FCBS [MFORTH] "init-fcbs" ( -- )
;
; Initialize all of the File Control Blocks.  This has the effect of
; "closing" any open files.
;
; ---
; : INIT-FCBS ( -- )   FCBSTART [ MAXFCBS 2* 2* 2* ] 0 FILL ;

            LINKTO(FINDFILE,0,9,'S',"BCF-TINI")
INITFCBS:   JMP     ENTER
            .WORD   LIT,FCBSTART,LIT,MAXFCBS*8,ZERO,FILL,EXIT


; ----------------------------------------------------------------------
; NEW-FCB [MFORTH] "new-fcb" ( -- 0 | fcb-addr )
;
; Find and return the address of an unused (and uninitialized) File
; Control Block.  Return 0 if the system has run out of File Control
; Blocks.
;
; ---
; : NEWFCB ( -- fcb-addr)
;   [ MAXFCBS 2* 2* 2* ] LITERAL 0 DO
;   FCBSTART I +  DUP FCBADDR + @ 0= IF UNLOOP EXIT THEN  DROP  8 +LOOP
;   0 ;

            LINKTO(INITFCBS,0,7,'B',"CF-WEN")
NEWFCB:     JMP     ENTER
            .WORD   LIT,MAXFCBS*8,ZERO,pdo
_newfcb1:   .WORD   LIT,FCBSTART,I,PLUS,DUP,LIT,FCBADDR,PLUS,FETCH
            .WORD   ZEROEQUALS,zbranch,_newfcb2,UNLOOP,EXIT
_newfcb2:   .WORD   DROP,LIT,8,pplusloop,_newfcb1
            .WORD   ZERO,EXIT


; ----------------------------------------------------------------------
; NEXT-LINE [MFORTH] ( fcb -- addr1 addr2 )
;
; Read forward from the current position in the file identified by fcb
; until either a CRLF sequence is found or the end of file is reached.
; addr1 is the address of the end of the current line in the file (ignoring
; the CRLF sequence, if any).  addr2 is the address of the end of the current
; line, including the CRLF sequence.  addr2 will normally be addr1+2 if the
; line was terminated with a CRLF.
;
; ---
; \ The loop in this method takes advantage of the fact that all M100 files
; \ end with an EOF and so we can always read the next two characters in the
; \ file, even if this is the last character in the file.  We will never read
; \ a byte from another file in this situation, because the second character
; \ will be EOF.
; : NEXT-LINE ( fcb -- addr1 addr2)
;   2@ TUCK - 2>B FORB B @ 0x0A0D = IF B DUP 1+ 1+ EXIT THEN NEXTB B DUP ;

            LINKTO(NEWFCB,0,9,'E',"NIL-TXEN")
NEXTLINE:   POP     H           ; Get FCB into HL.
            MOV     A,M         ; Get the FCBPOS[l] into A,
            INX     H           ; ..increment to FCBPOS[h],
            MOV     H,M         ; ..put FCBPOS[h] into H,
            MOV     L,A         ; ..then put FCBPOS[l] into L.
_nextline1: MOV     A,M         ; Get the next byte into A,
            CPI     01Ah        ; ..see if it is EOF,
            JZ      _nextlineEOF; ..and then exit if so.
            CPI     00Dh        ; See if it is CR,
            INX     H           ; ..move to the next byte,
            JNZ     _nextline1  ; ..and continue looping if not CR.
            MOV     A,M         ; See if the byte after CR
            CPI     00Ah        ; ..is LF,
            JNZ     _nextline1  ; ..and if not then continue looping.
            DCX     H           ; Otherwise decrement HL to before the CR,
            PUSH    H           ; ..push addr1,
            INX     H           ; ..increment past the CR
            INX     H           ; ..and LF,
            PUSH    H           ; ..then push addr2.
            JMP     _nextlineDONE;We're done.
_nextlineEOF:PUSH   H           ; Push addr1.
            PUSH    H           ; Push addr2.
_nextlineDONE:NEXT


; ----------------------------------------------------------------------
; SRCNAM [MFORTH] ( -- ior | file-addr file-len 0 )
;
; Call the Main ROM's SRCNAM routine.  FILNAM has already been populated
; by the caller.

            LINKTO(NEXTLINE,0,6,'M',"ANCRS")
LAST_FILE:
SRCNAM:     SAVEDE              ; Save DE
            PUSH    B           ; ..and BC, both of which are corrupted.
            CALL    STDCALL     ; Call the
            .WORD   20AFh       ; .."SRCNAM" routine.
            POP     B           ; Restore BC.
            JZ      _srcnamFAIL ; Zero indicates not found.
            PUSH    D           ; Push file-addr to the stack.
            XCHG                ; Get file-addr in HL.
            LXI     D,0         ; Initialize file-len to zero.
_srcnam1:   MOV     A,M         ; Get the next byte of the file into A,
            CPI     01Ah        ; ..see if it is EOF,
            JZ      _srcnam2    ; ..and exit the loop if so.
            INX     D           ; Increment the file-len,
            INX     H           ; ..increment the file pointer,
            JMP     _srcnam1    ; ..and continue looping.
_srcnam2:   PUSH    D           ; Push file-len onto the stack.
            LXI     H,IOROK     ; Put IOROK in HL.
            JMP     _srcnamDONE ; We're done.
_srcnamFAIL:LXI     H,IORFNF    ; Put the IOR in HL.
_srcnamDONE:PUSH    H           ; Push the flag to the stack.
            RESTOREDE
            NEXT