        SUBT    > Sys.File

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;                         F I L E   S T U F F
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; FCB's have form (NB. Reordered by Tutu)

          ^     0
fcbflags  #     4       ; 00 flags
fcbhandle #     4       ; 04 file handle
fcbtfid   #     4       ; 08 temporary filename identifier (= 0 for perm files)
fcbcsz    #     4       ; 0C component size
fcbBV     #     0       ; 10 ( .. n) buffer variable

; Masks for FCB flags (bits 1 and 0 unused)

R_file  *       &0080   ; Read / ~Write flag
V_file  *       &0040   ; Valid buffer variable (BV)
L_file  *       &0020   ; Eoln flag
F_file  *       &0010   ; EOF flag
P_file  *       &0008   ; Physical EOF read
G_file  *       &0004   ; Get pending (on input)

; Usage of the 32 temporary filenames Pas___0..9, A..W is recorded in the
; Temporary File Usage Word (tfuw), and the temporary filename identifier byte
; is a mask to this byte. For permanent files, it has the value 0

; Open files are recorded in the Valid File Table (32 entries possible),
; the Valid File Usage Word (vfuw) keeps track of free entries in this table

; The 'p' flag is used, if necessary, to insert a terminating eoln into
; a text file

; g(et pending) is flagged on INPUT after get, read (char), readln or reset
; to allow normal interactive use

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; +             C O M M O N   I / O   R O U T I N E S                         +
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Test whether a given FCB is defined in valid file usage tables (32 entries)

; In : addr -> FCB to look up

; Out : CC - FCB not valid, count undefined
;       CS - FCB valid, entry index in vF tables = count
; All regs except count preserved

FCBtest ROUT

        STASH   "R0, t1, temp, link"
        LV      R0, vfuw
        MOV     count, #32
        SUB     temp, globalbase, #vFaddr ; Base of FCB addresses table
 [ debfile
 SWI Newline
 STRIM "Testing FCB"
 DREG addr
 STRIM " vfuw ="
 DREG R0
 STRIM " "
 ]

10      SUB     count, count, #1
        ADDS    R0, R0, R0
        BCC     %FT30

; Compare FCB address with corresponding one in vFt

        LDR     t1, [temp, -count, LSL #2]
        CMP     addr, t1

30      BNE     %BT10
 [ debfile
 SWICS WriteI+"C"
 SWICC WriteI+"c"
 STRIM ","
 ]
        GRAB    "R0, R1, temp, pc" ; NB. Not ^ as CC/CS returned

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; BL code &A7 - PrX_Reset. Open permanent (named) stream for input

; In : size = new component size
;      arga = file type (0 = text), temp = filename length if appropriate

iXReset ROUT

        MOV     R0, #perm_file
        B       %FT01


; BL code &A6 - Pr_Reset. Open temporary stream for input

iReset  MOV     R0, #temp_file

01      MOV     t3, #(R_file + V_file + L_file) ; Flags to set when file opened
        MOV     t2, #openin

        STASH   link
        BL      ResRewCommon
        GRAB    link

        LV      R0, filetype
        CMP     R0, #0
        BEQ     LazyGetText
        BNE     AAgetBytes

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; BL code &AB - PrX_Rewrite. Open permanent (named) stream for output

; In : size = new component size
;      arga = file type (0 = text), temp = filename length if appropriate

iXRewrite ROUT

        MOV     R0, #perm_file
        B       %FT01


; BL code &AA - Pr_Rewrite. Open temporary stream for output

iRewrite
        MOV     R0, #temp_file

01      MOV     t3, #(L_file + F_file)  ; Flags to set when file opened
        MOV     t2, #openout

; .............................................................................
; In : R0 = 0 -> temporary file, permanent file
;      t3 = flags to set in FCB
;      t2 = osfind operation needed to open file
;      size = new component size
;      arga = file type (0 = text), temp = filename length if appropriate

ResRewCommon ROUT

 [ debfile
 SWI Newline
 STRIM "*** Reset/rewrite ***"
 SWI Newline
 STRIM "New file's flags :="
 DREG t3
 ]
        STASH   "r0, link"       ; Get round 3um ARM LDM bug

        SV      R0, permflag
        SV      t2, fileop
        SV      arga, filetype
        SV      temp, strlen

        CMP     R0, #temp_file
        LDRNE   R0, [sp, #-4]!  ; varp -> filename
        STRNE   R0, [globalbase, #-varp]
 [ debfile
 BEQ %FT10
 STRIM " - and new file will be permanent"
10
 ]

        POP     addr                    ; addr -> FCB

        STR     t3, [addr, #fcbflags]
        STR     size, [addr, #fcbcsz]   ; New component size

        BL      FCBtest ; If FCB valid (file exists) then close it
 [ debfile
 BCC %FT20
 SWI Newline
 STRIM "FCB already exists - closing old file"
20
 ]
        LDRCS   R1, [addr, #fcbhandle]  ; Don't delete it as we may be opening
        MOVCS   R0, #0                  ; an old temp file for input !
        SWICS   Open

; If now permanent then set tfid = 0 and skip temp file handling stuff

        MOV     R0, #0

        LV      R1, permflag
        CMP     R1, #perm_file
        BEQ     %FT50
 [ debfile
 SWI Newline
 STRIM "New temporary file"
 ]
        BL      FCBtest         ; Allocate new tfid if FCB not already valid
        BCC     %FT25

        LDR     R1, [addr, #fcbtfid]    ; If FCB valid but tfid allocated then
        CMP     R1, #0                  ; don't reallocate !
        BNE     %FT52

25 ; Find first free slot in tfuw
 [ debfile
 STRIM " Allocating new tfid"
 ]

        LV      R1, tfuw
        MOV     R2, R1
        MOV     count, #32
30      SUB     count, count, #1
        ADDS    R2, R2, R2
        BCC     %FT35
        CMP     count, #0
        BNE     %BT30
 [ debfile
 STRIM " - couldn't get a tfid"
 ]

        GRAB    "r0, link"
        B       eTooManyFiles

35      MOV     R2, #1                  ; Calculate new tfid (1 << count)
        MOV     R0, R2, LSL count
        ORR     R1, R0, R1              ; Mark as taken in tfuw
        SV      R1, tfuw

50      STR     R0, [addr, #fcbtfid]    ; Store tfid in FCB (=0 for perm files)
 [ debfile
 SWI Newline
 STRIM "File tfid ="
 DREG R0
 STRIM " - tfuw :="
 DREG R1
 ]

52
        LV      R1, permflag
        CMP     R1, #temp_file
        BEQ     %FT55
 [ debfile
 SWI Newline
 STRIM "Opening perm file"
 ]
        BL      R1fname                 ; If perm then R1 -> 'filename'
 [ debfile
 BNE %FT56
 STRIM " - bound to console"
56
 ]
        BEQ     %FT65                   ; If console then skip file op
        B       %FT60

55
 [ debfile
 SWI Newline
 STRIM "Opening temp file"
 ]
      BL      R1tfname                ; If temp then R1 -> 'pas___<R0>'

60 ; Do operation on real file. R1 -> filename

 [ debfile
 SWI Newline
 STRIM "Filename is '"
 MOV R0, R1
 SWI Write0
 STRIM "'"
 ]
        LV      R0, fileop
        SWI     Open
        CMP     R0, #0
        LDMEQDB hasp!, {r0, link}
        BEQ     eFileNotFound

65      STR     R0, [addr, #fcbhandle]  ; Store file handle in FCB (console=0)
 [ debfile
 STRIM " - handle"
 DREG R0
 ] 
        BL      FCBtest                 ; If FCB exists, just update info
        BCS     %FT80                   ; else add to vF tables
 [ debfile
 SWI Newline
 STRIM "New file, so add to vF tables"
 ]

; Find first free slot in vfuw
        LV      R1, vfuw
        MOV     R2, R1
        MOV     count, #32
70      SUB     count, count, #1
        ADDS    R2, R2, R2
        BCC     %FT75
        CMP     count, #0
        BNE     %BT70
        GRAB    link
        B       eTooManyFiles

75      MOV     R2, #1                  ; Calculate new tfid (1 << count)
        MOV     R0, R2, LSL count
        ORR     R1, R0, R1              ; Mark as taken in vfuw
        SV      R1, vfuw
 [ debfile
 STRIM " - vfuw :="
 DREG R1
 ]

80 ; Update info stored in vF tables, entry = count
 [ debfile
 SWI Newline
 STRIM "Updating vF tables, index"
 DREG count
 ]

        SUB     temp, globalbase, #vFaddr ;  Base of FCB addresses table
        STR     addr, [temp, -count, LSL #2]

        SUB     temp, globalbase, #vFhandle ; Base of handle table
        LDR     R0, [addr, #fcbhandle]
        STR     R0, [temp, -count, LSL #2]

        SUB     temp, globalbase, #vFtfid ; Base of tfid table
        LDR     R0, [addr, #fcbtfid]
        STR     R0, [temp, -count, LSL #2]
 [ debfile
 SWI Newline
 STRIM "*** End of reset/rewrite ***"
 SWI Newline
 ]
        GRABS   "r0, pc"

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; In : varp -> packed array [1..strlen] of char

; Out : EQ - null string (console)
;       NE - R1 -> filename in stracc, with CR appended

R1fname ROUT

        STASH   link
        BL      StrToStracc     ; Out : count = length of string

        LV      R1, straccptr

        CMP     count, #0       ; Null string (console) ?
        LDMNEDB hasp!, {pc}     ; Result flags

        LV      R0, filetype
        CMP     R0, #0          ; Can only use console with text files
        LDMEQDB hasp!, {pc}     ; Result flags

        GRAB    link
        B       eNotText

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; In : R0 = tfid

; Out : R1 -> filename 'pas___<R0>' in stracc, with CR appended
; All other regs preserved

R1tfname ROUT

        CMP     R0, #0
        BEQ     eBadTfid

        STASH   "R0, t2, t3, count, link"

        MOV     R1, #32         ; Generate digit == tfid
10      SUB     R1, R1, #1
        ADDS    R0, R0, R0
        BCC     %BT10

        RSB     t3, R1, #31     ; Start with 31 == pas___0

        CMP     t3, #10
        ADDLT   t3, t3, #"0"
        ADDGE   t3, t3, #"A"-10 ; tf 10 == pas___A
 [ debfile
 STRIM " tf no "
 MOV R0, t3
 SWI WriteC
 ]
        LV      R1, straccptr
        ADR     t2, tfprefixstr
        MOV     count, #9 ; 7
        MOVEBLK t2, R1

        STRB    t3, [R1, #6]            ; Stick digit on end (CR already there)

        GRABS   "R0, t2, t3, count, pc"

tfprefixstr = "Pas___Z", CR, LF, 0
;              0123456   7   8   9 - LF/NUL not essential except for debugging

eBadTfid ERROR  "Bad tfid"

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; In : varp -> packed array [1..strlen] of char

; Out : string copied into stracc, CR appended, count is length
; All other regs preserved

StrToStracc ROUT

        STASH   "R0, t1, t2, t3, link"
 [ debfile
 STRIM " StrToStracc '"
 ]
        LV      count, strlen
        LV      t3, varp        ; t3 -> filename
        LV      t2, straccptr
        MOV     t1, #0
        CMP     count, #0
        BEQ     %FT50

10      LDRB    R0, [t3, t1]
 [ debfile
 SWI WriteC
 ]
        STRB    R0, [t2, t1]
        ADD     t1, t1, #1
        CMP     t1, count
        BLT     %BT10

50      MOV     R0, #CR
        STRB    R0, [t2, t1]!
 [ debfile
 STRIM "'"
 MOV R0, #LF
 STRB R0, [t2, #1]
 MOV R0, #0 ; Termination for Write0
 STRB R0, [t2, #2]
 ]
        GRABS   "R0, t1, t2, t3, pc"

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Close all files at this level

CloseAllFiles ROUT

        MOV     R1, #0
        B       %FT05

ICloseFiles ; close all local files

        MOV     R1, currbase
05
        STASH   "R0, t1, temp, t2, count, link"

        LV      R0, vfuw
        MOV     count, #32
        SUB     temp, globalbase, #vFaddr ; Base of FCB address table

10      SUB     count, count, #1
        ADDS    R0, R0, R0
        BCS     %FT30
        BNE     %BT10

        GRABS   "R0, R1, temp, t2, count, pc"

30      LDR     t2, [temp, -count, LSL #2]      ; Address of FCB to consider
        CMP     t2, R1
        BLGE    ClsNdeleteTemp          ; If this FCB is 'local' then kill it
        B       %BT10

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Close given file, and delete it if it's a temporary. Do nothing if console

; In : count = vF table index. All regs preserved

ClsNdeleteTemp ROUT

        STASH   "R0, R1, temp, link"

        SUB     temp, globalbase, #vFhandle     ; Base of handle table
        LDR     R1, [temp, -count, LSL #2]
        CMP     R1, #0
        LDMEQDB hasp!, {R0, R1, temp, pc}^      ; Do nothing if console
        MOV     R0, #0
        SWI     Open

        SUB     temp, globalbase, #vFtfid       ; Base of tfid table
        LDR     R0, [temp, -count, LSL #2]
        CMP     R0, #0
        LDMEQDB hasp!, {R0, R1, temp, pc}^      ; Exit if permanent

        LV      temp, tfuw
        BIC     temp, temp, R0                  ; Free this entry in tfuw
        SV      temp, tfuw
        BL      R1tfname                        ; Generate 'Pas___<R0>'

        MOV     R0, #6                          ; 'delete' code for osfind
        SWI     File                            ; R1 -> filename

        GRABS   "R0, R1, temp, pc"

        LNK     Sys.Input
