        SUBT    > <wini>arm.FileSwitch.FSCtrl2 - Filing System management routines

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; Filing System info block format: What they pass to me
; ======================================================

;     0 +-----------+
;       |  nameptr  | Offset to Filing System name
;       +-----------+
;       | bannerptr | Offset to Filing System banner text, &80000000 -> FSFunc
;       +-----------+
;       |   open    | Offset of routine to open files
;       +-----------+
;       | getbuffer | Offset of routine to fill a file buffer from the FS
;       +-----------+
;       | putbuffer | Offset of routine to put a file buffer to the FS
;       +-----------+
;       |   args    | Offset of routine to control open files
;       +-----------+
;       |   close   | Offset of routine to close open files
;       +-----------+
;       |   file    | Offset of routine to do whole file operations
;       +-----------+
;       |   info    | Word of Filing System type information
;       +-----------+
;       |   func    | Offset of routine to do Filing System functions
;       +-----------+
;       |   gbpb    | Offset of routine to do unbuffered multiple byte transfer
;       +-----------+

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; fscb format
; ===========

;     0 +-----------+
;       |   link    | To next fscb, or Nowt
;       +-----------+
;       | bannerptr | Address of Filing System banner text, &80000000 -> FSFunc
;       +-----------+
;       |   open    | Address of routine to open files
;       +-----------+
;       | getbuffer | Address of routine to fill a file buffer from the FS
;       +-----------+
;       | putbuffer | Address of routine to put a file buffer to the FS
;       +-----------+
;       |   args    | Address of routine to control open files
;       +-----------+
;       |   close   | Address of routine to close open files
;       +-----------+
;       |   file    | Address of routine to do whole file operations
;       +-----------+
;       |   info    | Word of Filing System type information
;       +-----------+
;       |   func    | Address of routine to do Filing System functions
;       +-----------+
;       |   gbpb    | Address of routine to do unbuffered muliple byte transfer
;       +-----------+
;       | modulebase| Address of Filing System module base
;       +-----------+
;       |  privword | Address of module's private word
;       +-----------+
;       |modulebase2| Address of Filing System secondary module base
;       +-----------+
;       | privword2 | Address of module's secondary private word
;       +-----------+
;       |   opt 1   | OPT 1 status on this Filing System (Byte)
;       +-----------+
;       |           |
;       ~   name    ~ LowerCased Filing System name
;       |           |
;       +-----------+

; info word
; =========

; bit31   = 1 -> Filing System supports special fields
;         = 0 -> Filing System does not support special fields

; bit30   = 1 -> Filing System streams are interactive, eg. kbd, vdu
;         = 0 -> Filing System streams are not interactive

; bit29   = 1 -> Filing System supports null length filenames, eg. kbd, vdu
;         = 0 -> Filing System needs non-null filenames

; bit28   = 1 -> Filing System should always be called to open file
;         = 0 -> Filing System must only be called to open file in/up if exist

; bits7-0: BBC type Filing System identifier

;       0  No Filing System             1  1200 Baud cassette   (NS)
;       2  300 Baud cassette    (NS)    3  ROM Filing System    (NS)
;       4  Acorn DFS            (NS)    5  net:
;       6  Teletext/Telesoft    (NS)    7  IEEE Filing System   (NS)
;       8  adfs:                        9  Unknown - but reserved
;       10 Acorn VFS            (NS)    11 Acorn WDFS           (NS)
;       12 netprint:                    13 null:
;       14 vdu:                         15 bbc:
;       16 Acacia RAM Filing System (?) 17 bbcb:
;       18 rawvdu:

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Offsets in a filing system control block

          ^     0
fscb_link #     4
fscb_startuptext # 4 ; Ptr to text to be printed on Filing System startup
fscb_open #     4    ; --\
fscb_get  #     4    ;   |
fscb_put  #     4    ;   } Addresses in same order and offset as FS_xxx
fscb_args #     4    ;   |
fscb_close #    4    ;   |
fscb_file #     4    ; --/
fscb_info #     4
fscb_func #     4    ; --\
fscb_gbpb #     4    ; --/

fscb_modulebase  # 4    ; Ptr to module base (for * decoding primarily)
fscb_privwordptr # 4    ; Ptr to module private word (or whatever Bruce says)

fscb_modulebase2  # 4   ; Ptr to secondary module base
fscb_privwordptr2 # 4   ; Ptr to secondary module private word

fscb_opt1 #     1       ; Current OPT 1 value for Filing System (0 to start)
fscb_name #     1       ; Dynamic sizing of name field, but 1 for 0 terminator

fscb_size *     :INDEX: @

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Notes on numbers and names:

;       Numbers may only be used to select or lookup filing systems
;       Names MUST be used for Add, AddSecondary and Remove ops

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; AddFSEntry. Top level FSControl entry
; ==========
;
; Add a Filing System from a loaded module to those that FileSwitch knows about
; Can be used to update the given fscb too (if it already exists)

; In    r1 -> Filing System module base
;       r2 = Offset of Filing System info block in module
;       r3 = Module private word^ for Filing System to be entered with
;          (conventionally st. LDR wp,[r12] will be executed on all fs entries)

; Out   VC: Added to Filing System chain / Updated fscb
;       VS: fail


UntitledFSString DCB    "<Untitled FS>", 0
                 ALIGN


AddFSEntry NewSwiEntry "r0-r8, fscb"

        MOV     r5, r1                  ; r5 -> Module base
        ADD     r6, r1, r2              ; r6 -> Filing System info block
        MOV     r7, r3                  ; r7 -> Module workspace

        ADR     r14, UntitledFSString
        LDR     r1, [r6, #FS_name]      ; Module offset to Filing System name
        TEQ     r1, #0                  ; Is it duff ?
        MOVEQ   r1, r14
        ADDNE   r1, r1, r5              ; Add in module base
        TST     r1, #&FC000000          ; Real ARM address (byte aligned) ?
        MOVNE   r1, r14
        MOV     r4, r1                  ; Save name^ for copying later
 [ debugcontrol
 LDRB r14,[r6,#FS_info]
 DREG r14,"Adding Filing System number ",cc,Byte
 DSTRING r1," "
 DREG r6, "Module base      "
 DREG r7, "Module workspace "
 DREG r6, "FS info block    "
 ]
        MOV     r0, #1                  ; <> 0 -> only CtrlChar terminators
        BL      LookupFSName            ; Does FS (r1 -> name) already exist ?
        BVS     %FA99 ; SwiExit         ; And as an aside, is it actually valid

        TEQ     fscb, #Nowt             ; fscb^ ~= Nowt if found
        MOVNE   r2, fscb                ; Update existing FS block if found
        BNE     %FT30

        BL      strlen                  ; How long am the name ?
        ADD     r3, r3, #fscb_size      ; Get space for the fscb + name
        BL      SMustGetArea            ; 0 allowed for already in fscb_size
        BVS     %FA99 ; SwiExit


30 ; r2 now points to a block we can fill with the Filing System entry points

        STR     r5, [r2, #fscb_modulebase]  ; Must store module base
                                            ; For Filing System *cmd decoder
        STR     r7, [r2, #fscb_privwordptr] ; Store Filing System word^

        MOV     r14, #0                 ; These not present yet
        STR     r14, [r2, #fscb_modulebase2]
        STR     r14, [r2, #fscb_privwordptr2]

; Maybe load OPT 1 default from CMOS ?

        MOV     r14, #0                 ; Starting OPT 1 setting is 0
        STRB    r14, [r2, #fscb_opt1]

; Slapped wrists for people who give me a naff banner string too

        addr    r14, UntitledFSString
        LDR     r0, [r6, #FS_startuptext]
        CMP     r0, #-1                 ; -> call FSFunc to print it
        BEQ     %FT13
        TEQ     r0, #0
        ADDNE   r0, r0, r5              ; If not zero, add in module base
        MOVEQ   r0, r14
        TST     r0, #&FC000000          ; Real ARM address (byte aligned )?
        MOVNE   r0, r14
13      STR     r0, [r2, #fscb_startuptext]

; FS_open to FS_gbpb are module offsets to add to module base in the new fscb
; Calculate addresses that FileSwitch will be calling in this Filing System
; Those that are invalid (ie offset of 0 or final address invalid) are given
; the 'naffup' default so that LowLevel isn't blown away

        addr    r7, Lowlevel_UnsupportedFSEntry
        ADD     r8, r7, #(Lowlevel_UnalignedFSEntry-Lowlevel_UnsupportedFSEntry)

 assert FS_open = fscb_open ; Must be in the same order at the same offset !

        MOV     r1, #FS_open            ; First entry to copy
15      LDR     r0, [r6, r1]            ; Src = entry point offset
        TEQ     r0, #0                  ; No entry point for this routine ?
        MOVEQ   r0, r7                  ; Give default 'dunno' entry if so
        ADDNE   r0, r0, r5
        TST     r0, #ARM_CC_Mask        ; Formed a real ARM address ?
                                        ; This MUST be word aligned (code)
        MOVNE   r0, r8                  ; Give default 'hate' entry if not
        STR     r0, [r2, r1]            ; Dest = real address in module
        CMP     r1, #FS_gbpb            ; Last entry to copy
        ADDNE   r1, r1, #4
        BNE     %BT15

        LDR     r0, [r6, #FS_info]      ; Copy FS information word
        STR     r0, [r2, #fscb_info]    ; Don't put before table copy !

        ADD     r3, r2, #fscb_name      ; r3 := r2+fscb_name -> space for name
20      LDRB    r0, [r4], #1            ; LowerCase FS name into fscb
        LowerCase r0, r1                ; (Having preserved r4 down to here !)
        CMP     r0, #space              ; CtrlChar or space terminates
        MOVLS   r0, #0                  ; 0 terminator when stored in fscb
        STRB    r0, [r3], #1
        BHI     %BT20

        TEQ     fscb, #Nowt             ; New block to link to head of chain ?
        LDREQ   r14, fschain
        STREQ   r14, [r2, #fscb_link]
        STREQ   r2, fschain             ; Splat ! It's now on the chain

        LDR     r14, currentfs          ; Are we in a no sel fs state ?
        TEQ     r14, #Nowt
        LDREQB  r0, [r2, #fscb_info]
        LDREQB  r14, fsnumjustkilled    ; Restarting the old currentfs ?
        CMPEQ   r0, r14
        STREQ   r2, currentfs           ; Make current again
        STREQ   r2, tempfs

99      SwiExit

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; AddSecondaryFSEntry. Top level FSControl entry
; ===================
;
; Add secondary module base and wsptr to existing Filing System entry

; In    r1 -> Filing System name
;       r2 = Secondary module base
;       r3 = Secondary module wsptr

; Out   VC: added
;       VS: UK FS error or Bad FS name

AddSecondaryFSEntry NewSwiEntry "r0, r1, r3, fscb"

 [ debugcontrol
 DSTRING r1, "Adding secondary FS for "
 DREG r2, "Module base 2      "
 DREG r3, "Module workspace 2 "
 ]
        MOV     r0, #1                  ; <> 0 -> only CtrlChar terminators
        BL      LookupFSName
        BVS     %FA50 ; SwiExit

        CMP     fscb, #Nowt             ; fscb^ ~= Nowt if found. VClear
        addr    r14, MagicErrorBlock_UnknownFilingSystem, EQ
        BLEQ    MagicCopyErrorSpan      ; needs r0,r1,r3

                                        ; Update existing FS block if found
        STRVC   r2, [fscb, #fscb_modulebase2]
        LDRVC   r3, [fp, #4*2]
        STRVC   r3, [fscb, #fscb_privwordptr2]

50      SwiExit

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; LookupFSEntry. Top level FSControl entry
; =============
;
; Search Filing System chain for matching Filing System name/number

; In    r1 > 255 -> Filing System name, term (see r2)
;          < 256  = Filing System number
;       r2  <> 0 -> only CtrlChar terms allowed
;            = 0 -> CtrlChar and '#',':','-' terms allowed

; Out   VC      r1 = filing system number
;               r2 = valid fscb^ corresponding to name
;                    0 if not found
;       VS      Bad Filing System name error set if bad term or duff chars

LookupFSEntry NewSwiEntry "r0, fscb"

        BICS    r14, r1, #&FF           ; Valid address ?
        BNE     %FT10
        BL      LookupFSNumber
        B       %FT20

10      MOV     r0, r2                  ; <> 0 only allow CtrlChar terminators
        BL      LookupFSName            ; 0 allows '#',':','-' extra terms

20      BVS     %FA30 ; SwiExit
        TEQ     fscb, #Nowt             ; NB. Leave V unperturbed
        MOVEQ   fscb, #0                ; Wot the punter expects, 'spose
        LDRNEB  r1, [fscb, #fscb_info]  ; Return filing system number
        MOV     r2, fscb                ; and fscb^. Yuk!

30      SwiExit

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; LookupFSNumber
; ==============
;
; Search Filing System chain for matching Filing System number

; In    r1 = Filing System number

; Out   fscb^ valid or Nowt if not found. VClear always

LookupFSNumber ENTRY

        LDR     fscb, fschain           ; Point to first fscb

10      CMP     fscb, #Nowt             ; End of fs chain ?
        LDRNEB  r14, [fscb, #fscb_info]
        CMPNE   r1, r14                 ; If matched, fscb valid
        EXIT    EQ                      ; VClear

        LDR     fscb, [fscb, #fscb_link] ; Try next fscb then
        B       %BT10

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; LookupFSName
; ============
;
; Search Filing System chain for matching Filing System name

; Allows '#', '-', ':' terminators for internal porpoises
; Externals may have to check terms and present stripped

; In    r1 -> Filing System name

; Out   if found: r1 -> terminating char, fscb^ valid
;                 r3 = span
;       if not  : r1 unchanged, fscb = Nowt
;                 r3 = span

LookupFSName ENTRY "r2-r5"

 [ debugcontrol
 DSTRING r1,"Searching for Filing System "
 ]
        MOV     r4, r0                  ; Remember validity indicator
        MOV     r3, #0                  ; Validate passed Filing System name
01      LDRB    r0, [r1, r3]            ; Is it terminated correctly ?
        BL      IsAlphanumeric          ; Good char ?
        ADDCS   r3, r3, #1
        BCS     %BT01                   ; Loop if so
        STR     r3, [sp, #4*1]
        TEQ     r3, #0                  ; Zero length name ?
        BEQ     %FA90
        BL      IsFSNameTerm            ; Time for termination check
        BNE     %FA90

        LDR     fscb, fschain           ; Point to first fscb

10      CMP     fscb, #Nowt             ; End of Filing System chain ?
        EXIT    EQ                      ; Leave if so, fscb = Nowt, VClear

        MOV     r2, r1                  ; r2 -> Filing System name to test
        ADD     r5, fscb, #fscb_name    ; r5 -> fscb name field
 [ debugcontrol
 DSTRING r5, "Comparing with "
 ]

15      LDRB    r3, [r5], #1            ; Char from fscb
        LDRB    r0, [r2], #1
        CMP     r3, #0                  ; Ended fscb name ?
        BNE     %FA18
        BL      IsFSNameTerm            ; Ended supplied name ?
        SUBEQ   r1, r2, #1              ; r1 -> term char
        EXIT    EQ

18      LowerCase r0, r14               ; Filing System name stored lowercased
        CMP     r0, r3                  ; Loop if still matching
        BEQ     %BT15

        LDR     fscb, [fscb, #fscb_link] ; Try next fscb then
        B       %BT10


; Are we allowing these terminators ? EQ if term ok. r4 = 0 -> also #:-

IsFSNameTerm

        CMP     r0, #space              ; CtrlChar is always term
        BICLO   lr, lr, #V_bit          ; VClear
        ORRLOS  pc, lr, #Z_bit          ; EQ

        TEQ     r0, #":"                ; For fsname:
        TEQNE   r0, #"-"                ; For -fsname-
        TEQNE   r0, #"#"                ; For -fsname#special-
        MOVNE   pc, lr                  ;  or fsname#special:

        TEQ     r4, #0                  ; Are these allowed in the context ?
        MOVEQ   pc, lr                  ; else drop through ...


90      addr    r0, ErrorBlock_BadFilingSystemName
        BL      CopyError
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; SelectFSEntry. Top level FSControl entry
; =============
;
; Find a Filing System by name / number and select it as current

; In    r1  > 255 -> Filing System name terminated by '#', '-' or CtrlChar
;          <= 255: Filing System number
;           =   0: Go into no selected Filing System state

; Out   VC: Selected by name or number, or failed to select by number
;       VS: Failed to select by name only

SelectFSEntry NewSwiEntry "r0, r1, r3, fscb"

        BICS    r14, r1, #&FF           ; Valid address ?
        BNE     %FT50

        TEQ     r1, #0                  ; No sel fs ?
        MOVEQ   fscb, #Nowt
        BEQ     %FT10

        BL      LookupFSNumber
        TEQ     fscb, #Nowt             ; Found it ?
        BEQ     %FA20 ; SwiExit         ; No error if selecting by number

10      STR     fscb, currentfs         ; Select it if so
        STR     fscb, tempfs

20      SwiExit


50      MOV     r0, #0                  ; 0 -> any old crap terminators
        BL      LookupFSName
        BVS     %BA20 ; SwiExit

        TEQ     fscb, #Nowt             ; Found it ?
        BNE     %BT10

        addr    r0, MagicErrorBlock_FSDoesntExist ; Error if selecting by name
        BL      MagicCopyErrorSpan      ; needs r0,r1,r3
        B       %BA20 ; SwiExit

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; In    r1 -> fs name

; Out   VSet

SetMagicFSDoesntExist ENTRY

        addr    r0, MagicErrorBlock_FSDoesntExist ; Error if fs not found
        BL      MagicCopyError
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; PrintFilingSystemText
; =====================
;
; Output the startup text string associated with the current Filing System

; NB. AddFS has already defaulted null or bad entries to a sensible string

; In   fscb^ valid or Nowt

PrintFilingSystemText ENTRY "r0-r5, r6, fscb" ; FSFunc only preserves r6 up

 [ debugcontrol
 DLINE "FS startup banner: ",cc
 ]
        LDR     fscb, currentfs
        CMP     fscb, #Nowt             ; No msg if no sel fs
        EXIT    EQ                      ; VClear

        LDR     r0, [fscb, #fscb_startuptext]
        CMP     r0, #-1                 ; Code to call ?
        BEQ     %FT90

        SWI     XOS_Write0              ; Always a string, even
        SWIVC   XOS_NewLine             ; if it is '<Untitled>' !
50      SWIVC   XOS_NewLine
        BLVS    CopyError
        EXIT

90      MOV     r0, #fsfunc_PrintBanner
        MOV     r6, #0                  ; Use current context
        BL      CallFSFunc_Given        ; Use fscb^
        EXIT    VS
        B       %BT50                   ; Print another newline

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; BootupFSEntry. Top level FSControl entry
; =============
;
; Ask current Filing System to perform autoboot action, eg. '%LOGON Boot'

; Out   VC: Booted ok (or no sel fs)
;       VS: fail

BootupFSEntry NewSwiEntry "r0-r5, r6, fscb" ; FSFunc only preserves r6 up

        LDR     fscb, currentfs
        CMP     fscb, #Nowt             ; VClear
 [ debugcontrol
 BEQ %FT01
 Push r1
 ADD r1, fscb, #fscb_name ; r1 -> fscb name field
 DSTRING r1,"Booting on "
 Pull r1
01
 ]
        MOVNE   r0, #fsfunc_Bootup
        MOVNE   r6, #0                  ; Use current context
        BLNE    CallFSFunc_Given        ; Use fscb^
        SwiExit                         ; Not an error if no current FS

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; RemoveFSEntry. Top level FSControl entry
; =============
;
; Remove a Filing System from from Filing System chain by name only

; Mustn't call FS eg. to close files; it may not be reentrant (Nick wants this)

; In    r1 > 255 -> Filing System name, CtrlChar
;          < 256 = Filing System number - which will fail; stops accidental rem

; Out   VC: Removed, all registers preserved
;       VS: fail

RemoveFSEntry NewSwiEntry "r0, r1, r3, fscb"

        BICS    r14, r1, #&FF           ; Valid address ?
        addr    r14, ErrorBlock_CantRemoveFSByNumber, EQ
        BEQ     %FA9999 ; SwiExit with r14 -> error

        MOV     r0, #0                  ; 0 -> any old crap terminators
        BL      LookupFSName
        BVS     %FA70 ; SwiExit

        TEQ     fscb, #Nowt             ; Error if named FS not found
        addr    r0, MagicErrorBlock_FSDoesntExist, EQ
        BLEQ    MagicCopyErrorSpan      ; needs r0,r1,r3

        BLVC    RemoveFS                ; Of given fscb

70      SwiExit

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; RemoveFS
; ========
;
; Remove a Filing System from Filing System chain, and return space

; In    fscb^

; Out   VC: Removed or not found
;       VS: failed to free block

RemoveFS ENTRY "r0, r2"

 [ debugcontrol
 Push r1
 ADD r1, fscb, #fscb_name
 DSTRING r1,"Removing Filing System "
 Pull r1
 ]
        ADR     r0, (fschain-fscb_link) ; Find your fscb first

; r0 -> fscb before the one to try

10      LDR     r14, [r0, #fscb_link]
        CMP     r14, #Nowt              ; End of chain ?
        EXIT    EQ
        CMP     r14, fscb
        MOVNE   r0, r14
        BNE     %BT10

        LDR     r14, [fscb, #fscb_link] ; Put our link back in his
        STR     r14, [r0,   #fscb_link]

        LDR     r0, currentfs           ; If we were current, then deselect
        TEQ     r0, fscb                ; -> 'No Filing System Selected' state
        LDREQB  r0, [fscb, #fscb_info]  ; Keep note of this for possible resel
        STREQB  r0, fsnumjustkilled     ; on AddFS
        MOVEQ   r0, #Nowt
        STREQ   r0, currentfs
        LDR     r0, tempfs              ; If we were temporary, then reselect
        TEQ     r0, fscb                ; current as temporary
        LDREQ   r0, currentfs
        STREQ   r0, tempfs

        MOV     r2, fscb                ; Free the space
        BL      SFreeArea
        EXIT                            ; V from freeing block

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; ReadFSNameEntry. Top level FSControl entry
; ===============
;
; Decode the given fs number into a filename

; In    r1 = fs number
;       r2 -> core
;       r3 = size of core

; Out   name in core (zero terminated), else Buffer overflow

ReadFSNameEntry NewSwiEntry "r1-r3, fscb"

 [ debugcontrol
 DREG r1, "Read FSName for fs number "
 ]
        BL      LookupFSNumber
        TEQ     fscb, #Nowt             ; Found it ?
        ADDNE   r1, fscb, #fscb_name
        addr    r1, anull, EQ
 [ debugcontrol
 DSTRING r1, "Found filing system "
 ]

10      SUBS    r3, r3, #1
        addr    r14, ErrorBlock_BuffOverflow, MI ; [buffer too small]
        BMI     %FA9999 ; SwiExit with r14 -> error

        LDRB    r0, [r1], #1
        STRB    r0, [r2], #1
        CMP     r0, #0
        BNE     %BT10

90      SwiExit

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; FileTypeFromStringEntry. Top level FSControl entry
; =======================
;
; Decode the given string into a filetype

; In    r1 -> string (ctrl char terminated, trailing spaces skipped)

; Out   r2 = file type (bottom 12 bits)

             ^  0, sp
fts_result   #  256                     ; Can't read a variable into ScrSpace
                                        ; as it might be a macro variable
fts_size     *  :INDEX: @

ftslocalframesize * localframesize + fts_size


WildFileType
        DCB     "File$Type_"
FileTypePrefixOff * .-WildFileType
        DCB     "*"                     ; Shared zero with ...
fsw_Null ; Can't be local, unfortunately
        DCB     0
        ALIGN


FileTypeFromStringEntry NewSwiEntry "r0-r1, r3-r7",fts ; extra local frame

        BL      FS_SkipSpaces
        BCC     %FT40                   ; Fault with 'Bad number'

        MOV     r7, r1                  ; keep for later

        BL      strlen                  ; Strip trailing spaces orf
        ADD     r5, r1, r3              ; r5 -> end of string (non-null)
05      LDRB    r14, [r5, #-1]!
        CMP     r14, #space
        BEQ     %BT05
        ADD     r5, r5, #1              ; r5 -> end of string (space or CtrlCh)
        SUB     r5, r5, r1              ; r5 = length of bit to compare
 [ debugcontrol
 DREG r5, "length of source string "
 ]

        ADR     r6, fsw_Null            ; Null string (shared)-> read first vbl

10 ; Loop over variables (quicker than doing all 4096, he claims)

        addr    r0, WildFileType        ; Template to match
        ADR     r1, fts_result          ; Lookup that variable please
        MOV     r2, #?fts_result        ; Read into stack frame
        MOV     r3, r6                  ; last matched variable
        MOV     r4, #VarType_Expanded
        SWI     XOS_ReadVarVal          ; r3 -> next one to try
        BVS     %FT40                   ; try number instead (ended)
        MOV     r6, r3                  ; remember variable
        CMP     r2, #0
        BEQ     %BT10
 [ debugcontrol
 MOV r14, #0
 STRB r14, [r1, r2]
 DSTRING r1, "read variable "
 ]

        
        ADD     r3, r1, r2              ; r3 -> end of string (non-null),unterm
15      LDRB    r14, [r3, #-1]!
        CMP     r14, #space
        BEQ     %BT15
        ADD     r3, r3, #1              ; r3 -> end of string (space or unterm)
        SUB     r3, r3, r1              ; r3 = length of bit to compare
 [ debugcontrol
 DREG r3, "length of comparison string "
 ]

        CMP     r3, r5                  ; Same lengths ?
        BNE     %BT10

        MOV     r2, r7                  ; Same as our string (r1in sp-skipped)?
        BL      strncmp                 ; strings unterminated, so use count
 [ debugcontrol
 BEQ %FT9900
 DLINE " NE"
 B %FT9901
9900
 DLINE " EQ"
9901
 ]
        BNE     %BT10                   ; [nope, loop]

        LDR     r0, =(2_101 :SHL: 29) + 16 ; Default base 16, no bad terms
        ADD     r1, r6, #FileTypePrefixOff ; Skip prefix, read number
        LDR     r2, =&FFF               ; Restrict range 000..FFF
        SWI     XOS_ReadUnsigned
        BVS     %BT10                   ; loop if number no good

        B       %FA90 ; SwiExit

        LTORG


; Try number then

40      LDR     r0, =(2_101 :SHL: 29) + 16 ; Default base 16, no bad terms
        MOV     r1, r7
        LDR     r2, =&FFF               ; Restrict range 000..FFF
 [ debugcontrol
 DSTRING r1, "Try reading as number "
 ]
        SWI     XOS_ReadUnsigned        ; Read type
 [ debugcontrol
 BVS %FT00
 DREG r2, "file type "
00
 ]
        addr    r0, ErrorBlock_BadFileType, VS ; Make new error
        BLVS    CopyError

90      SwiExit

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; ReadFileTypeEntry. Top level FSControl entry
; =================
;
; Decode the given filetype into a 4 char sequence

; In    r2 = file type (bottom 12 bits)

; Out   r2, r3 = text form as chars in registers

ReadFileTypeEntry NewSwiEntry

        BL      DecodeFileType
        SwiExit

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Decode the given filetype into a 4 char sequence

; In    r2 = file type (bottom 12 bits)

; Out   r2, r3 = text form as chars in registers

             ^  0, sp
dft_varname  #  32                      ; Variable name is small ~13 currently
dft_result   #  256                     ; Can't read a variable into ScrSpace
                                        ; as it might be a macro variable
dft_size     *  :INDEX: @

DecodeFileType ENTRY "r0-r2, r4" ; Result r2 poked into stack

        SUB     sp, sp, #dft_size       ; Room for the variable string + result

 [ debugcontrol
 DREG r2,"File type is &",,Word
 ]
        MOV     r2, r2, LSL #(32-12)    ; Mask out any crap
        MOV     r2, r2, LSR #(32-12)
        STR     r2, [sp, #dft_size + 4*2] ; Note clean value

        ADR     r3, dft_varname
        ADR     r4, dft_VariablePrefix
05      LDRB    r14, [r4], #1           ; Copy variable name in
        TEQ     r14, #0
        STRNEB  r14, [r3], #1           ; No terminator; leave r3 -> next byte
        BNE     %BT05

        BL      ConvertHex3

        ADR     r0, dft_varname         ; Point at string again
        ADR     r1, dft_result          ; Lookup that variable please
        MOV     r2, #?dft_result        ; Read into stack frame
        ADR     r3, %FT66               ; Null string (shared)
        BL      SReadVariable           ; VS -> exists, buffer overflow. Tough
        BVS     %FT99                   ; Get out
        BEQ     %FT50                   ; [null expansion -> Unknown file type]

        MOV     r0, #8
        MOV     r2, #&FF
30      LDRB    r14, [r1], #1
        TST     r14, r2                 ; If we've had zero, then pretend zero
        MOVEQ   r2, #0                  ; all the time, so we pad with spaces
        MOVEQ   r14, #space
        STREQB  r14, [r1, #-1]
        SUBS    r0, r0, #1
        BNE     %BT30

 [ debugcontrol
 Push "r1, lr"
 MOV r14, #0
 STRB r14, dft_result+8+(4*2) ; Correct for having stacked r1, lr
 ADR r1, dft_result+(4*2)
 DSTRING r1, "File type "
 Pull "r1, lr"
 ]
        ADR     r14, dft_result
        LDMIA   r14, {r2, r3}
        B       %FT98


50      LDR     r2, [sp, #dft_size + 4*2]
        MOV     r1, #Service_LookupFileType
        SWI     XOS_ServiceCall         ; Anybody want to claim it ?
        CMP     r1, #Service_Serviced
        BEQ     %FT98

        MOV     r14, #"&"               ; Build '&xyz' in r2
        STRB    r14, dft_result
        ADR     r3, dft_result+1
        BL      ConvertHex3
        LDR     r2, dft_result
        LDR     r3, FourSpaces          ; '    ' in r3

98      STR     r2, [sp, #dft_size + 4*2]

99      ADD     sp, sp, #dft_size
        EXIT


dft_VariablePrefix
        DCB     "File$Type_"            ; Shared zero with ...
66
        DCB     0
        ALIGN

FourSpaces DCB  "    "                  ; Picked up as a word

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; In    r0 = 4 chars, LSB first

; Out   VC: ok
;       VS: error in r0, not copied

PrintR0Chars ENTRY

        SWI     XOS_WriteC
        MOVVC   r0, r0, ROR #8
        SWIVC   XOS_WriteC
        MOVVC   r0, r0, ROR #8
        SWIVC   XOS_WriteC
        MOVVC   r0, r0, ROR #8
        SWIVC   XOS_WriteC
        MOVVC   r0, r0, ROR #8          ; Back to the start
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; RestoreCurrEntry. Top level FSControl entry
; ================
;
; In    currentfs is valid fscb^ to reselect

RestoreCurrEntry ROUT ; NB. No local frame, just dealing with global state

        LDR     r14, currentfs
        STR     r14, tempfs
        Pull    pc,,^                   ; Claim vector. Assumes VClear in lr

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; ReadTempModEntry. Top level FSControl entry
; ================
;
; Tells us where the temporary (may be current) selected Filing System is

; Out   r1 = module base of Filing System, 0 if none selected
;       r2 = ptr to private word

ReadTempModEntry ROUT ; NB. No local frame, just dealing with global state

        LDR     r2, tempfs
        CMP     r2, #Nowt
        MOVEQ   r1, #0
        LDRNE   r1, [r2, #fscb_modulebase]
        LDRNE   r2, [r2, #fscb_privwordptr]
        Pull    pc,,^                   ; Claim vector. Assumes VClear in lr

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; ReadSecModEntry. Top level FSControl entry
; ===============
;
; Tells us where the temporary (may be current) selected Filing System
; secondary module is

; Out   r1 = secondary module base, 0 if no fs selected
;       r2 = ptr to private word

ReadSecModEntry ROUT ; NB. No local frame, just dealing with global state

        LDR     r2, tempfs
        CMP     r2, #Nowt
        MOVEQ   r1, #0
        LDRNE   r1, [r2, #fscb_modulebase2]
        LDRNE   r2, [r2, #fscb_privwordptr2]
        Pull    pc,,^                   ; Claim vector. Assumes VClear in lr

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; ReadFSHandle. Top level FSControl entry
; ============
;
; Convert the given FileSwitch handle into a 32 bit Filing System handle

; If handle invalid, a Filing System handle of 0 is returned (not valid too)

; In    r1 = FileSwitch handle

; Out   r1 = Filing System handle
;       r2 = Filing System information word

ReadFSHandle ROUT ; NB. No local frame, just dealing with global state

        SUB     r2, r1, #1              ; Only 1..MaxHandle valid
        CMP     r2, #MaxHandle
        ADRLO   r14, streamtable
        LDRLO   r2, [r14, r1, LSL #2]   ; Get scb^
        ADRHSL  r2, UnallocatedStream   ; No stream if invalid
        LDR     r14, [r2, #:INDEX: scb_status]
        TST     r14, #scb_unallocated   ; Valid stream ?
        MOVNE   r1, #0                  ; No FS handle then, is there ?
        MOVNE   r2, #0                  ; Zero information word if not
        LDREQ   r1, [r2, #:INDEX: scb_fshandle] ; Get scb^.fshandle
        LDREQ   r2, [r2, #:INDEX: scb_fscb]     ; Get scb^.fscb
        LDREQ   r2, [r2, #fscb_info]            ; Get fscb^.info
        Pull    pc,,^                   ; Claim vector. Assumes VClear in lr

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; ShutFilesEntry
; ==============
;
; Flush and close all files on all Filing Systems (*SHUT equivalent)

; In    no parms

; Out   VC: all files closed
;       VS: failed to close one or more files

ShutFilesEntry NewSwiEntry

        BL      CloseAllFilesEverywhere
        SwiExit

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; ShutDownEntry
; =============
;
; Flush and close all files on all Filing Systems (*SHUT equivalent)
; then make the little bastards park. Ignore errors until they're all dead !

; In    no parms

; Out   VC: all files closed, everyone asleep
;       VS: failed to close one or more files, or sleep failed

ShutDownEntry NewSwiEntry "r0-r5, r6, fscb" ; FSFunc only preserves r6 up

        BL      CloseAllFilesEverywhere ; So all they have to do is park

        LDR     fscb, fschain           ; Point to first fscb

10      CMP     fscb, #Nowt             ; End of fs chain ?
        SwiExit EQ                      ; Errors picked up by exit check

 [ debugcontrol
 BEQ %FT01
 Push r1
 ADD r1, fscb, #fscb_name ; r1 -> fscb name field
 DSTRING r1, "Shutting down "
 Pull r1
01
 ]
        MOV     r0, #fsfunc_ShutDown
        MOV     r6, #0                  ; Use current context
        BL      CallFSFunc_Given        ; Use fscb^

        LDR     fscb, [fscb, #fscb_link] ; Do next fs then
        B       %BT10

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; 
; CreateHandleEntry
; =================
;
; Allow system extensions ie. ADFS/NetFS to create handles eg. for dir swap

; In    r1 = filing system handle
;       r2 = buffer size to use with file
;       r3 = size of file
;       r4 = space allocated to file
;       r5 = attributes to set in file + device/system identifier
;       r6 = filing system number

; Out   r1 = external handle

CreateHandleEntry NewSwiEntry "r0-r6, scb, fscb"

        MOV     r1, r6
        BL      LookupFSNumber
        CMP     fscb, #Nowt
        MOVEQ   r3, #0                  ; Can't open if duff filing system
        STREQ   r3, [sp, #4*1]          ; Return handle 0
        BEQ     %FA90 ; SwiExit

        STR     fscb, fsforthisop

        MOV     r6, r3
        BL      AllocateStream          ; r3 := new exthandle
        MOVVS   r3, #0

        LDRVC   r0, [sp, #4*5]
        LDRVC   r1, [sp, #4*1]
        STR     r3, [sp, #4*1]          ; Must return a handle to caller
        MOVVC   r3, r6
        BLVC    StuffTheStreamInfo      ; Deallocates if error

90      SwiExit

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; 
; SetContextsEntry
; ================
;
; Allow system extensions ie. ADFS/NetFS to do dir swap

; In    r1 = URD FileSwitch handles (use 0 to read current handle)
;       r2 = CSD
;       r3 = LIB

; Out   r1..r3 = old handle set (FileSwitch handles)

SetContextsEntry NewSwiEntry "r0-r6,r7,r8,scb,fscb" ; fsfunc only preserves r6 up

        TEQ     r1, #0                  ; If all zero, read handle set from
        TEQEQ   r2, #0                  ; tempfs
        TEQEQ   r3, #0
        LDREQ   fscb, tempfs
        BEQ     %FT50

        CMP     r1, #0                  ; Otherwise, ensure all on same fs. VC
        MOVEQ   r5, #0
        BLNE    FindValidStream         ; Preserves flags
        EXIT    VS
 assert r8 <> scb
 assert r8 <> fscb
        LDRNE   r8, scb_fshandle        ; NB. NOT r1!
        LDRNE   r5, scb_fscb
        MOVNE   fscb, r5

        CMP     r2, #0
        MOVEQ   r6, #0
        MOVNE   r1, r2
        BLNE    FindValidStream
        EXIT    VS
        LDRNE   r2, scb_fshandle
        LDRNE   r6, scb_fscb
        MOVNE   fscb, r6

        CMP     r3, #0
        MOVEQ   r7, #0
        MOVNE   r1, r3
        BLNE    FindValidStream
        EXIT    VS
        LDRNE   r3, scb_fshandle
        LDRNE   r7, scb_fscb
        MOVNE   fscb, r7

30      TEQ     r5, #0
        MOVEQ   r5, fscb

        TEQ     r6, #0
        MOVEQ   r6, fscb

        TEQ     r7, #0
        MOVEQ   r7, fscb

        TEQ     r5, r6
        TEQEQ   r5, r7
        BNE     %FA90                   ; Nob off.

50      LDR     r14, [fscb, #fscb_info] ; Can we cope ?
        TST     r14, #fsinfo_setcontexts
        BEQ     %FA95

        MOV     r0, #fsfunc_SetContexts
        MOV     r1, r8                  ; Restore stream 1 fs handle
        MOV     r6, #0                  ; Use current context
        BL      CallFSFunc_Given

        STMVCIB fp, {r1-r3}             ; Poke r1..r3 into stack
        SwiExit


90      addr    r14, ErrorBlock_InconsistentHandleSet
        B       %BA9999 ; SwiExit with r14 -> error

95      addr    r14, ErrorBlock_UnsupportedFSEntry
        B       %BA9999 ; SwiExit with r14 -> error

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; TransNameIntoBuffer
; ===================
;
; GSTrans the filename we've been given from punter into a static buffer

; The passed filename has leading spaces stripped by GSTrans, and translation
; of this name stops on a space or CtrlChar. Quotes are stripped, but solidus
; is not treated as a special character.

; The translated filename is terminated on a CtrlChar.
; Spaces and other nasties in expansion are trapped here.

; In    r1 -> filename to translate

; Out   VC: r1 -> translated name, allocated in PassedFilename
;              Not at all stripped, eg. 'net#40.:bugs.0-30'
;       VS: fail giving 'Bad file name' with translated name

TransNameIntoBuffer ENTRY "r0, r2-r6"

        MOV     r0, r1                  ; -> filename
 [ debugname
 DSTRING r0, "Translating filename "
 ]

        MOV     r4, sp, LSR #20         ; Find stack base
        SUB     r4, sp, r4, LSL #20     ; Amount of stack left
 [ debugpath
 DREG r4, "amount of stack = "
 ]
        SUB     r4, r4, #2*1024
        CMP     r4, #StaticName_length  ; Never less than old compatible
        MOVMI   r4, #StaticName_length  ; length object

        SUB     sp, sp, r4
        MOV     r1, sp
        SUB     r2, r4, #1              ; Need one space for putting term char

 [ debugpath
 DREG r4, "amount of stack used = "
 DREG r1, "translating to buffer ",cc
 DREG r2, ", length "
 ]
        ORR     r2, r2, #2_011 :SHL: 29 ; Space terminator. Don't do
        SWI     XOS_GSTrans             ; | expansion. Strip quotes
        BLVS    CopyError               ; Bad String etc ?
        BVS     %FT99
        BCS     %FT95                   ; Overflowed ? -> Bad name

; r2 = nchars read. Strip trailing spaces, leave terminated

        ADD     r2, r1, r2              ; r2 -> past end of translated name
10      CMP     r2, r1                  ; Back to start ?
        BEQ     %FT20
        LDRB    r14, [r2, #-1]!         ; Loop back till not space
        CMP     r14, #space
        BEQ     %BT10
        MOV     r14, #0                 ; Terminate (r2 -> last char in string)
        STRB    r14, [r2, #1]

20      BL      FS_SkipSpaces           ; If we have leading spaces, copy
        CMP     r1, sp                  ; string down so it has none
        MOVNE   r2, r1
        MOVNE   r1, sp
        BLNE    strcpy
 [ debugname
 DSTRING r1, "Translated to ",cc
 BL strlen
 DREG r3, ", length (no term) "
 ]

        MOV     r3, r1
60      LDRB    r0, [r3], #1            ; Loop checking expanded name
                                        ; Add always - terminator included
        CMP     r0, #delete
        CMPNE   r0, #space-1            ; Terminate on CtrlChar
        BLS     %FT70
        CMP     r0, #space
        CMPNE   r0, #quote
        CMPNE   r0, #solidus
        BNE     %BT60

95      BL      SetMagicBadFileName     ; VSet too
        B       %FT99


70      MOV     r0, #0
        STRB    r0, [r3, #-1]

        SUB     r3, r3, r1              ; r3 = length including term char
 [ debugname
 DREG r3, "Length of space stripped name is "
 ]

; Now see if this name is xxx:filename

        MOV     r0, #-1
        STR     r0, PathStringOffset

        MOV     r2, #0
71      LDRB    r0, [r1, r2]
        CMP     r0, #0                  ; Nope, skip totally
        BEQ     %FT90
        CMP     r0, #":"
        ADDNE   r2, r2, #1
        BNE     %BT71

; Has a colon, so look up xxxx$Path

 [ debugname
 DREG r2, "Colon at position "
 ]
        TEQ     r2, #0                  ; ':0.fred' etc.
        BEQ     %FT90

        ADD     r6, r2, #1              ; r6 = offset to filename past colon

        ADD     r0, r2, r3              ; Have we enough space on stack
        ADD     r0, r0, #5+1            ; to look up the path variable ?
 [ debugname
 DREG r0, "Need so much total room "
 ]
        CMP     r0, r4
        BHI     %FT90                   ; [no]

        ADD     r14, r1, r3             ; r14 -> past zero of filename
        MOV     r2, #0

72      LDRB    r0, [r1, r2]
        CMP     r0, #":"
        STRNEB  r0, [r14], #1
        ADDNE   r2, r2, #1
        BNE     %BT72

        ADR     r2, %FT97
73      LDRB    r0, [r2], #1
        STRB    r0, [r14], #1
        TEQ     r0, #0
        BNE     %BT73

        ADD     r0, r1, r3              ; r0 -> 'xxxx$Path'
        Push    "r0-r4"
        MOV     r1, r0                  ; Expand in situ, replacing var name
        SUB     r2, r4, r3              ; r2 = amount left for expansion
 [ debugname
 DSTRING r0, "Path varname "
 DREG r2, "Amount of buffer for expansion "
 ]
        MOV     r3, #0                  ; We know what the name is
        MOV     r4, #VarType_Expanded   ; Expand macros in variable
        SWI     XOS_ReadVarVal
        MOVVC   r14, #0                 ; Terminate string in buffer
        STRVCB  r14, [r1, r2]!
        ADDVC   r5, r1, #1              ; r5 -> past terminator
        Pull    "r0-r4"
        BVS     %FT90                   ; Doesn't exist (or we ran out of room)

 [ debugname
 DSTRING r0, "Path variable expanded to "
 ]

        STR     r3, PathStringOffset
        SUB     r3, r5, sp              ; Allocate room for both strings

        MOV     r1, sp                  ; Ok, copy filename down to cover xxxx:
        ADD     r2, r1, r6              ; r2 -> filename past colon
        BL      strcpy

90
 [ debugname
 DLINE "Getting space for PassedFilename + PathString"
 ]
        ADR     r0, PassedFilename      ; Allocate space for it
        BL      SGetLinkedArea          ; r2 := new block^
        BVS     %FT99

98      SUBS    r3, r3, #1              ; Copy string(s) into allocated block
        LDRPLB  r0, [r1, r3]
        STRPLB  r0, [r2, r3]
        BPL     %BT98

        MOV     r1, r2
 [ debugname
 DSTRING r1, "Name is now "
 ]

99      ADD     sp, sp, r4              ; Restore stack after GSTrans
        EXIT


97
        DCB     "$Path", 0
        ALIGN

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; TransNameSetFSAndPolice
; =======================
;
; Translate a filename, set up the Filing System and validate the name (Phew !)

; Leaves PassedFilename and SpecialField stacked

; In    r1 -> filename to use for this operation

; Out   VC: r1 -> part of filename (fsname stripped) to use
;       VS: Error down below !

TransNameSetFSAndPolice ENTRY

        BL      TransNameIntoBuffer
        BLVC    SetFSAndPolice
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; SetFSAndPolice
; ==============
;
; Set up the Filing System and validate the name

; Leaves SpecialField stacked if no error

; In    PassedFilename to be used for this operation
;       PassedFilename to be deallocated on error

; Out   VC: r1 -> part of filename (fsname stripped) to use
;       VS: Error down below !

SetFSAndPolice ENTRY

        LDR     r1, PassedFilename
        BL      SetFSForOp
        BVS     %FT50

        BL      PoliceName
        LDRVS   r1, PassedFilename      ; Give as much name as we can here
        BLVS    SetMagicBadFileName     ; VSet too
 [ debugname
 BVS %FT00
 DLINE "Name ok: ",cc
 LDRB r14, pathnailed
 TEQ r14, #0
 SWINE XOS_WriteI+"A"
 SWINE XOS_WriteI+" "
 LDRB r14, wildleafname
 TEQ r14, #0
 SWINE XOS_WriteI+"*"
 SWINE XOS_WriteI+" "
 Push r1
 LDR r1,leafnameptr
 DSTRING r1, "; leafname "
 Pull r1
00
 ]
        BLVC    SCopySpecialField

50      BLVS    SFreePassedFilename
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; SetFSForOp
; ==========
;
; Setup fsforthisop as to whether the object has a fs name prefix
; Setup specialptr -> special field if present, length in speciallength
; Filing System lookup stops on CtrlChar or space.
; NB. filenames like -help are now valid again (We're not lazy !)

; In    r1 -> name (not necessarily a filename), CtrlChar terminated
;       r0 = 0 if not fudging, 1 if fudging colon terminator

; Out   r1 -> part of name past the fsname if present, else unchanged
;       r3 -> special field
;       r4 = length of special field
;       fscb^ set

SetFSForOpFudge ENTRY "r0-r2, r5"

 [ debugname
 DSTRING r1,"Setting FS from "
 ]
        LDR     fscb, tempfs    ; Use temp Filing System unless specified
        MOV     r3, #0          ; No special field as yet
        MOV     r4, #0          ; Of zero length
        STRB    r3, hadfsprefix ; No fs prefix as yet

        LDRB    r0, [r1]        ; What have we got here ?
        TEQ     r0, #":"        ; Colon at start precludes fsname: though
        EXIT    EQ              ; Use that then (must be a drive specifier)
        TEQ     r0, #"-"        ; Could it be a -fsname- job ?
        ADDEQ   r1, r1, #1      ; r1 -> past '-' for possible lookup if term'd
        BEQ     %FT10           ; r0 = "-" to search for
        TEQ     r0, #"#"        ; Could it be a #special: job ? (ie no fs spec)
        BEQ     %FT05
        BL      IsAlphanumeric
        EXIT    CC              ; Use what we've got already

; Is it a -fsname- or fsname: name ?

05      MOV     r0, #":"        ; Search for colon term (wasn't minus start)

10      MOV     r2, r1          ; Start^ for search
        BL      LookForAGoodTerm
        EXIT    NE              ; If name not terminated -fs- or fs:
        MOV     r5, r0          ; Remember what we got for colon fudging

        LDRB    r14, [r1]       ; Is fsname null, but with a special ?
        CMP     r14, #"#"
        ADDEQ   r1, r1, #1      ; Skip the hash
        MOVEQ   r14, #1         ; Yes, we've had a good fs prefix !
        STREQB  r14, hadfsprefix
        BEQ     %FT30           ; And use the tempfs we got

; There is a fsname, so look it up

        MOV     r0, #0          ; Allow '#', '-' and ':' as terminators
        BL      LookupFSName    ; Do we know about this Filing System ?
        EXIT    VS              ; Crap name ?
        TEQ     fscb, #Nowt     ; If not found, error (eg. -sillyfs-file)
        BEQ     %FT96

        MOV     r14, #1         ; Yes, we've had a good fs prefix !
        STRB    r14, hadfsprefix ; and fscb is valid

; r1 -> term now; Filing System will be that of the found fscb

        LDRB    r14, [r1], #1   ; Look at term char (skipping it)
        CMP     r14, #"#"       ; Special field present ?
        BNE     %FT85           ; No specials to consider, r1 -> rest of name

; Ok, specials !

30      LDR     r14, [fscb, #fscb_info] ; Does fs support special field ?
        TST     r14, #fsinfo_special
        BEQ     %FA95           ; It's an error

        MOV     r3, r1          ; r3 -> special field

60      LDRB    r14, [r1], #1   ; Skip special field
 [ True
        CMP     r14, r5         ; Terminate on appropriate char. VClear
 |
        CMP     r14, #"-"       ; Terminate on '-' or ':'. VClear
        CMPNE   r14, #":"
 ]
        ADDNE   r4, r4, #1
        BNE     %BT60

; Good exit. r1 -> rest of name

85      STR     r1, [sp, #4]    ; Update passed name^
        EXIT


; Errors

95      addr    r0, ErrorBlock_FSNotSpecial
        BL      CopyError
        EXIT


96      LDR     r14, [sp]       ; Fudging for set fs from name SWI entry ?
        CMP     r14, #1         ; 1 if so
        CMPEQ   r5, #":"        ; sillyfs: will be picked up as 'Bad file name'
        EXIT    EQ              ; VClear, name unmodified
                                ; Means we can do show:fred without wingeing
                                ; -sillyfs- DOES give error though !

        addr    r0, MagicErrorBlock_UnknownFilingSystem
        BL      MagicCopyErrorSpan
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

SetFSForOp ENTRY "r0, r3-r4, fscb"

        MOV     r0, #0                  ; No fudge please
        BL      SetFSForOpFudge
        EXIT    VS

 [ debugname
 DREG r1,"Stripped name is ",cc
 DREG fscb,", fscb ",cc
 DREG r3,", special "
 ]
        STR     r1, strippedfilenameptr ; Remember this ptr too
        STR     fscb, fsforthisop

        STR     r3, specialptr
        STR     r4, speciallength
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; LookForAGoodTerm
; ================

; In    r2 -> name to parse; must only have alphanumerics before the given term
;             or any chars accepted after possible special
;       r0 = term to match against; either ':' or '-'

; Out   EQ name ok; term found before end of name
;       NE term not found before end of name, or non-alphas before # char

LookForAGoodTerm ENTRY "r0, r2, r4"

        MOV     r4, r0                  ; IsAlphanumeric uses r0

10      LDRB    r0, [r2], #1
        CMP     r0, r4                  ; Finished match ?
        BEQ     %FT80
        CMP     r0, #"#"                ; Just look for term after specials
        BEQ     %FT20
        BL      IsAlphanumeric          ; Catches end of name too !
        BCS     %BT10
        EXIT                            ; CC -> NE

20      LDRB    r0, [r2], #1
        CMP     r0, #space+1            ; Need explicit end of name check
        EXIT    LO                      ; LO -> NE
        CMP     r0, r4
        BNE     %BT20

80 ; Found term - did we have any characters before it though ?

        LDR     r14, [sp, #4]           ; r2in
        SUB     r14, r2, r14
        CMP     r14, #1+1               ; LO -> term found immediately (NE)
        CMPHS   r14, r14                ; EQ -> term found ok
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; SetFromNameEntry. Top level FSControl entry
; ================
;
; Allow setting of tempfs by Filing System name eg. for XOS_CLI

; In    r1 -> name; allow #:- terms + strip 'em

; Out   r1 -> part of name past the fsspec if there was one
;       r2 = -1: no fs specified, tempfs unmolested
;          >= 0: old Filing System to be reselected at end of OSCLI etc.
;       r3 =  specialptr or 0
;       tempfs defaults to old tempfs and may be modified by the SetFSForOp

SetFromNameEntry NewSwiEntry "r0, r4, fscb"

        MOV     r0, #1                  ; Requires colon fudging
        BL      SetFSForOpFudge         ; Sets r1-r4
        CMP     r4, #0                  ; No special there ?
        MOVEQ   r3, #0
        LDRB    r14, hadfsprefix        ; Did we set up fs from a prefix ?
        CMP     r14, #0                 ; 0 -> no prefix
        MOVEQ   r2, #-1
        MOVNE   r2, fscb
        STRNE   r2, tempfs              ; Rewrite tempfs from fscb^ set up
        SwiExit

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; <name term>           ::=     CtrlChar | space

; <digit>               ::=     0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

; <letter>              ::=     A | B | C | D | E | F | G | H | I | J | K | L
;                             | M | N | O | P | Q | R | S | T | U | V | W | X
;                             | Y | Z | a | b | c | d | e | f | g | h | i | j
;                             | k | l | m | n | o | p | q | r | s | t | u | v
;                             | w | x | y | z | <any top bit set char>

; <alphanumeric>        ::=     <letter> | <digit> | _

; <allowed char>        ::=     <alphanumeric>
;                             | <left angle bracket> | <right angle bracket>
;                             | <left curly bracket> | <right curly bracket>
;                             | <left round bracket> | <right round bracket>
;                             | ! | ' | = | ~ | ` | + | / | ? | ;

; <wild char>           ::=     # | *

; <allowed/wild char>   ::=     <allowed char> | <wild char>

; <absolute char>       ::=     $ | & | % | @ | \

; <passed name>         ::=     <filename> <name term>
;                             | " <filename> "

; <filename>            ::=     [<file system spec>] <pathname>

; <file system spec>    ::=     <file system name> [# <special cpt>] :
;                             | # <special cpt> :
;                             | - <file system name> [# <special cpt>] -
;                             | - # <special cpt> -

; <file system name>    ::=     <letter> [<letter>]n

; <special cpt>         ::=     [<allowed/wild char> | . ]n

; <pathname>            ::=     <null>
;                             | [<absolute char> .] <media name>
;                             | [<devicespec> [. $ .]] <media name>

; <device spec>         ::=     : [<allowed/wild char> | - | <absolute char>]n

; <media name>          ::=     {<filename cpt> .} <filename cpt>

; <filename cpt>        ::=     [<allowed char> | <wild char>]n
;                             | ^

; Where { }    means 0 or more ...
;       [ ](n) means optionally one (or 1..n) ...
;
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; PoliceName
; ==========
;
; Given a pathname, make sure it conforms to general rules

; In    r1 -> <pathname>, 0 terminated

; Out   VC -> ok, things set up
;       VS -> fail, no error set

PoliceName ENTRY "r0-r1"

 [ debugname
 DSTRING r1,"Policing name "
 ]
        MOV     r14, #0
        STRB    r14, pathnailed         ; Path not nailed yet
        STRB    r14, wildleafname       ; And leafname isn't wild
        STR     r1, leafnameptr         ; This is the leafname so far

        LDRB    r0, [r1], #1            ; Get first char of name
        CMP     r0, #0                  ; Completely null ?
        EXIT    EQ                      ; Ok by me (FS dependent), VClear

        TEQ     r0, #":"                ; Device name / drive number ?
        BNE     %FT20                   ; Nope, try absolute spec then
        STREQB  r0, pathnailed          ; ~= 0 -> path nailed by device name

10      LDRB    r0, [r1], #1            ; Get more chars from device name
        TEQ     r0, #0                  ; Device name only ?
        EXIT    EQ
        TEQ     r0, #"."                ; Dot also ends device name
        BLNE    IsBad                   ; Loop over allowed chars
        BNE     %BT10

        TEQ     r0, #"."                ; Must now have dot (null done above)
        BNE     %FA90                   ; Error if not

        STR     r1, leafnameptr         ; Next bit might be leaf
        LDRB    r0, [r1], #1            ; Get next char after dot


20      BL      IsAbsolute              ; Absolute spec ?
        STREQB  r0, pathnailed          ; ~= 0 -> path nailed by prefix
        BEQ     %FT80

; If not, then drop through then with NE so we don't load the next char ...


; Parse a filename component. Must not have a leading special in it

30      LDREQB  r0, [r1], #1            ; Get first char of new component
        TEQ     r0, #"^"                ; Relative symbol (parent) ?
        BEQ     %FT80                   ; Can have many of these
        TEQ     r0, #0                  ; Can't be a null component
        TEQNE   r0, #"."                ; ie. end of name or another dot
        TEQNE   r0, #"$"
        BLNE    IsBad
        BEQ     %FA90

40      TEQ     r0, #"*"                ; Wild char ?
        TEQNE   r0, #"#"
        STREQB  r0, wildleafname

        LDRB    r0, [r1], #1            ; Get more chars from component
        CMP     r0, #0                  ; Finished name ?
        EXIT    EQ                      ; VClear
        TEQ     r0, #"."                ; Finished component ?
        MOVEQ   r14, #0                 ; Reset flag if we've finished this
        STREQB  r14, wildleafname
        STREQ   r1, leafnameptr
        BEQ     %BT30                   ; Go for another component, EQ (load)

        TEQ     r0, #"$"                ; Valid char in cpt ?
        BLNE    IsBad                   ; Only $ and crap disallowed now
        BNE     %BT40                   ; Loop over filename component

90      SETV
        EXIT


; Got absolute (or relative) path prefix
; Must now have a dot (+ more) or end of name

80      LDRB    r0, [r1], #1            ; Got a dot ?
        TEQ     r0, #"."                ; Looping or end of name ?
        STREQ   r1, leafnameptr
        BEQ     %BT30                   ; Go for another component, EQ (load)
        CMP     r0, #0                  ; Finished name ? VClear
        SETV    NE                      ; Anything else is bad
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Most of the time we want to give an error

PoliceNameWithError ENTRY

        BL      PoliceName
        BLVS    SetMagicBadFileName     ; VSet too
 [ debugname
 EXIT VS
 DLINE "Name ok: ",cc
 LDRB r14, pathnailed
 TEQ r14, #0
 SWINE XOS_WriteI+"A"
 SWINE XOS_WriteI+" "
 LDRB r14, wildleafname
 TEQ r14, #0
 SWINE XOS_WriteI+"*"
 SWINE XOS_WriteI+" "
 Push r1
 LDR r1,leafnameptr
 DSTRING r1, "; leafname "
 Pull r1
 ]
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Returns EQ if not allowed in a filename component or device name, NE if ok

IsBad   CMP     r0, #space              ; Space or CtrlChar is bad for you
        CMPLS   r0, r0                  ; EQ if one of those
        TEQNE   r0, #delete             ; Delete is pretty shit too
        TEQNE   r0, #":"                ; Colon is drive specifier
 [ False ; Bruce likes commas
        TEQNE   r0, #","                ; Comma is path separator
 ]
        TEQNE   r0, #solidus            ; Solidus and quotes may be confusing
        TEQNE   r0, #quote              ; to file servers and the like
        MOV     pc, lr


IsAbsolute
        TEQ     r0, #"$"                ; Device root
        TEQNE   r0, #"&"                ; URD
        TEQNE   r0, #"@"                ; CSD
        TEQNE   r0, #"%"                ; LIB. New symbol
        TEQNE   r0, #"\"                ; PSD. New symbol
        MOV     pc, lr

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; In    r0 = char

; Out   CS -> alphanumeric (a-z,0-9,_); CC otherwise

IsAlphanumeric

        CMP     r0, #"_"                ; Must check first to prevent > '_' in
        MOVEQ   pc, lr                  ; CS

        ENTRY                           ; Need r14 temp
        CMP     r0, #"0"
        RSBCSS  r14, r0, #"9"
        EXIT    CS
        CMP     r0, #"A"
        RSBCSS  r14, r0, #"Z"
        EXIT    CS
        CMP     r0, #"a"
        RSBCSS  r14, r0, #"z"
        EXIT

; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        LTORG

        LNK     $fileprefix.StreamBits
