        SUBT    > <wini>arm.FileSwitch.OSFind - Open and Close

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; +                                                                           +
; +                             O P E N    S W I                              +
; +                                                                           +
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; FindEntry. Vectored SWI level entry
; =========

; Corresponds to BBC OSFIND with path + termination extensions

FindEntry ROUT

        TST     r0, #&FF                ; r0b is mode for Open/Close
        BNE     TopOpenFile             ; CLOSE#r1 ?

; .............................................................................
;
; TopCloseFile. SWI level entry
; ============
;
; Close one file or all files open on any Filing System

; In    r1b = file handle to close

; Out   VC: file closed, r0 = 0
;       VS: fail

TopCloseFile NewSwiEntry "r0-r1"

 [ debugosfind
 DREG r1,"OSClose: handle "
 DREG sp, "Entry sp = "
 ]
        AND     r1, r1, #&FF            ; r1b is file handle
        BL      CloseFile

 [ debugosfind
 BVC %FT90
 DREG sp, "OSClose exit sp = "
 LDR r14, globalerror
 DREG r14, "globalerror "
90
 ]
        SwiExit

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; CloseFile
; =========
;
; Close one file or all files open on temp Filing System

; In    r1 =           0 -> close all files on the temporary Filing System
;           1..MaxHandle -> file handle to close

; Out   VC: file closed
;       VS: fail

CloseFile

 [ debugosfind
 DREG r1,"CloseFile: handle "
 ]
        CMP     r1, #0
        BEQ     CloseAllFiles

; .............................................................................
;
; CloseThisFile
; =============
;
; Close file of a given handle. File may be on any Filing System

; In    r1[1..MaxHandle] is FileSwitch handle

; Out   VC: file closed
;       VS: failed to close, or no file on this channel

CloseThisFile ENTRY "scb"

        BL      FindValidStream         ; Returns scb^ if VClear
        BLVC    FlushAndCloseStream     ; Gets streaminfo itself
 [ appendhandle
        BLVS    AppendHandleToError
 ]
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; CloseAllFiles
; =============
;
; Flush and close all files on the temporary Filing System (*CLOSE low-level)

; In    no parms

; Out   VC: all files closed
;       VS: fail, no particular handle faulted

CloseAllFiles ENTRY "fscb"

        LDR     fscb, tempfs            ; If temp invalid, don't shut 'em all
        TEQ     fscb, #0
        BLNE    CloseAllFilesOnThisFS
        EXIT

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

; In    no parms, fp not necessarily valid (called on death)

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

CloseAllFilesEverywhere ENTRY "r1, scb, fscb"

        MOV     fscb, #0

05      MOV     r1, #MaxHandle          ; Loop over handles in stream table

10      BL      FindStream              ; Get scb^ for this handle, VClear
        BEQ     %FT60

        TEQ     fscb, #0                ; fscb = 0 -> match all Filing Systems
        LDRNE   r14, scb_fscb           ; Is handle on this Filing System ?
        TEQNE   r14, fscb
        BLEQ    FlushAndCloseStream     ; Gets stream info itself
 [ appendhandle
        BLVS    AppendHandleToError
 ]

; Ignore any errors that this may give; we catch them on exit

60      SUBS    r1, r1, #1              ; Last valid handle = 1
        BNE     %BT10                   ; VClear from SUBS

        TEQ     fp, #0                  ; Can do bugger all if called in a
        EXIT    EQ                      ; brain damaged state

        LDR     r14, globalerror        ; Need to check this now
        CMP     r14, #0                 ; VClear
        SETV    NE                      ; VSet if there's an error lurking
        EXIT

; .............................................................................
; In    fscb = that to close and flush all files on

CloseAllFilesOnThisFS ALTENTRY ; Does need to preserve registers

 [ debugosfind
 DREG fscb,"Closing all files on fscb ",cc
 Push r1
 ADD r1, fscb, #fscb_name
 DSTRING r1, ", Filing System "
 Pull r1
 ]
        B       %BT05

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; TopOpenFile. SWI level entry
; ===========
;
; Corresponds to current BBC OSFIND ~0 with path + termination extensions

; Open on temporary Filing System (generally current)

; In    r0 =  OSFIND type. Must now have read and/or write request
;       r1 -> space,CR,LF or 0 terminated filename (will be translated)
;       r2 -> optional path variable name or string to use (depending on r0)

; Out   VC: r0 is file handle, 0 if file not opened
;       VS: fail

TopOpenFile NewSwiEntry "r1, scb"

 [ debugosfind
 DREG r0,"OSFind: mode ",cc
 DSTRING r1,", filename "
 ]
        AND     r0, r0, #&FF            ; r0b is mode for Open
        BL      TransNameIntoBuffer     ; Translate into PassedFilename
        BVS     %FA80 ; SwiExit

        BL      OpenFile                ; Resultis r0
        BVS     %FA97                   ; Deallocate PassedFilename

        BL      SFreePassedFilename
        BVS     %FA98                   ; Deallocate stream

80      SwiExit

97      BL      SFreePassedFilename
        B       %BA80 ; SwiExit

98      ADR     r14, streamtable
        LDR     scb, [r14, r0, LSL #2]
        BL      DeallocateStream
        B       %BA80 ; SwiExit

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; OpenFile
; ========
;
; Attempt to open file using temporary Filing System

; In    r0 = open mode
;       r1 -> filename (no translation performed, fsforthisop set)
;       r2 -> optional path variable or string (depending on r0)

; Out   VC: r0 = FileSwitch handle, 0 if not found
;       VS: naff

OpenFile ENTRY "r0-r7, scb" ; We poke the result into the stack

 [ debugosfind
 STRIM "OpenFile: mode "
 TST r0, #open_write
 SWINE OS_WriteI+"W"
 SWIEQ OS_WriteI+"w"
 TST r0, #open_read
 SWINE OS_WriteI+"R"
 SWIEQ OS_WriteI+"r"
 TST r0, #open_nodir
 SWINE OS_WriteI+"F"
 TST r0, #open_mustopen
 SWINE OS_WriteI+"M"
 Push r1
 AND r1, r0, #open_pathbits
 TEQ r1, #open_pathstring
 SWIEQ OS_WriteI+"S"
 TEQ r1, #open_pathvar
 SWIEQ OS_WriteI+"V"
 TEQ r1, #open_nopath
 SWIEQ OS_WriteI+"N"
 Pull r1
 DSTRING r1,", filename "
 ]
        TST     r0, #open_unused        ; Any duff bits set ?
        BNE     %FA80

        MOV     r7, r0                               ; Save Open mode bits
        AND     r6, r0, #(open_write :OR: open_read) ; Save read/write bits

; Decide where to try and open the file (OpenIn/OpenUp)

        TST     r7, #open_read          ; Can't look on any path if we don't
        ANDNE   r14, r7, #open_pathbits ; have a read component
        ADDNE   pc, pc, r14, LSL #2

        B       OpenUsingNoPath         ; Also serves to get addressing right

 assert open_pathbits = 2_11 ; For jump table

        B       OpenUsingDefaultPath
        B       OpenUsingPathString
        B       OpenUsingPathVariable
        B       OpenUsingNoPath


80      addr    r0, ErrorBlock_BadModeForOSFind
        BL      CopyError
        EXIT

; .............................................................................

OpenUsingNoPath ; Try to open this file

        addr    r2, anull               ; Null path string

OpenUsingPathString ; Try to open using given path string

        BL      ExPathString
        B       DoTheOpen

OpenUsingPathVariable ; Try to open using given path variable

        BL      ExPathVariable
        B       DoTheOpen

OpenUsingDefaultPath ; Try to open using File$Path

        addr    r0, FilePathVariableNameCR
        ADD     r2, r0, #(FilePathVariableDefault-FilePathVariableNameCR)
        BL      ExPathWithDefault

; .............................................................................
; In    fsforthisop set up, r1 -> stripped filename to use
;       FullFilename and SpecialField allocated if object exists and no error

DoTheOpen

        EXIT    VS                      ; No deallocation required here

        STRB    r0, osfind_objecttype   ; Unlike load, type 0 reasonable here
        CMP     r0, #object_nothing     ; Need special frig to reallocate !
        BNE     %FT50

        LDR     r1, [sp, #4]     ; Get name back again
        ADR     r0, FullFilename ; Copy filename with fs prefix (and special)
        BL      SGetLinkedString_excludingspaces ; Won't have spaces, but
        EXIT    VS               ; may as well ensure that it doesn't.

        BL      SetFSForOp      ; Set fs and police wrt. this new string
        BLVC    PoliceName      ; No error possible, just setting up stripped
        BLVC    SCopySpecialField ; Setup SpecialField
        BVS     %FT99           ; Deallocate FullFilename

        MOV     r0, #object_nothing     ; Remember that it didn't exist

50      LDR     r2, SpecialField
        STR     r2, osfind_specialptr   ; Fujj for Copy

        LDR     r2, [sp, #4]            ; Filename to use if error in open
        BL      DoTheOpen_Common
        BVS     %FT90

        BL      Open_FreeNames          ; Deallocate names if they exist

        STRVC   r0, [sp]                ; Return handle to stacked r0
        EXIT    VC

        BL      DeallocateStream        ; Only needs scb^ valid
        EXIT

90      BL      Open_FreeNames          ; Deallocate names if they exist
        EXIT


99      BL      SFreeFullFilename
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Exiting from DoTheOpen; need to free FullFilename and SpecialField

; Out   V := V(in) OR error in freeing

Open_FreeNames ENTRY "r1"

        BL      SFreeFullFilename
        BL      SFreeSpecialField
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; In    r1 = filename
;       r2 = filename to use on error
;       r6, r7 = open modes (exact, +bits)

; Out   r0 = new FileSwitch handle

; Requested op  NoFile  IsDir   LWR lWR LwR lwR LWr lWr Xwr
; ============  ======  =====   === === === === === === ===

; OpenIn        han 0   han x   wR  wR  wR  wR  err err err

; OpenUp        han 0   error   WR  WR  wR  wR  err Wr  err

; OpenOut       han x   error   err WR  err wR  err Wr  err
;              created

; Notes:
;       OpenIn fails if no read permission
;       OpenOut fails if file is locked (silly to set 0 extent; equiv. delete)
;       OpenOut preserves file load/exec/attr. Always sets scb_modified
;       All fail if no read or write permission

DoTheOpen_Common ENTRY "r0" ; Result poked into stacked r0

        BL      AllocateStream          ; Allocate an external handle to r3
        EXIT    VS

        STR     r3, [sp]                ; Result in stacked r0
        BL      TryToOpenFile           ; (FS need this passed in r3 on Open)
        BVS     %FT75                   ; Deallocate stream if failed
        STREQ   r1, [sp]                ; If not found. NB. This is FS handle 0
        BEQ     %FT75                   ; Deallocate stream if not opened

; Succesfully opened the file !

        TEQ     r6, #open_read          ; If opening for reading, don't permit
        BICEQ   r0, r0, #fsopen_WritePermission ; writing to file

        BL      StuffTheStreamInfo      ; Wobble the bits into place, deallocs
        EXIT    VS                      ; if error

        TEQ     r6, #open_write         ; If OpenOut, set scb_modified
        LDREQ   r14, scb_status         ; so we will stamp on closing
        ORREQ   r14, r14, #scb_modified
        STREQ   r14, scb_status
        EXIT


75      MOV     r0, psr                 ; Deallocate exit
        BL      DeallocateStream
        TEQVCP  r0, #0
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; In    r1, r6, fscb set
;       r0 = exact OSFind op

; Out   r0 = new FileSwitch handle

OpenFileForCopy ENTRY "r1-r7" ; Untrusting of what goes in in the depths ...

        STR     r6, osfind_specialptr
        STR     fscb, fsforthisop

        MOV     r6, r0                  ; Open modes
        ORR     r7, r0, #open_mustopen  ; Mustn't fail now !
        MOV     r0, #object_file        ; Object type is a file. This we know
        MOV     r2, r1                  ; Error filename^
        BL      DoTheOpen_Common
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; TryToOpenFile
; =============
;
; In    r0  = file type from OSFile 5 committed in ExPath routine
;       r1 -> filename to use for open
;       r2 -> filename to use if dir error
;       r3  = external handle
;       r6  = low level mode for open. Either &40, &80 or &C0
;       r7  = full bits for open

; Out   VS: failed
;       VC: r1 = Filing System handle (NE), or 0 (EQ - not found)

; If opened:
; r0 is the attributes + device/system identifier for this file - normally zero
; r1 is a valid Filing System handle to stuff in our scb
; r2 is the buffer size to use with this file
; r3 is the size of the file
; r4 is the space allocated to the file

TryToOpenFile ENTRY "r2, r7" ; r2 needs to be kept around

        LDR     r14, fsforthisop        ; Should we always ask for open
        LDR     r14, [r14, #fscb_info]  ; regardless of 'existence' ?
        TST     r14, #fsinfo_alwaysopen ; eg. null:, kbd:
        BNE     %FT50                   ; Try to open then !

        CMP     r0, #object_directory   ; Is it a directory ?
        BEQ     %FT90                   ; Deal with separately

        TEQ     r6, #open_write         ; OpenOut ?
        BEQ     %FT80

; OpenIn/OpenUp

        CMP     r0, #object_nothing     ; Not found ? VClear
        MOVEQ   r1, #0                  ; If so, we'll return handle 0
        BEQ     %FT60                   ; if that's ok by the punter

50      MOVVC   r0, r6                  ; Real mode for open. Must trap VSet
        BLVC    CallFSOpen              ; from below
        EXIT    VS

        TEQ     r1, #0                  ; Got a decent Filing System handle ?
        STRNE   r2, [sp]                ; Best return this !
        EXIT    NE                      ; NE -> opened file

; File not found / couldn't be opened

60      TST     r7, #open_mustopen      ; Is this unacceptable ?
        EXIT    EQ                      ; EQ -> not found. VClear

        TEQ     r6, #open_write         ; Only give 'Can't open' for OpenOut
        BNE     %FT95                   ; Otherwise 'File not found'

        addr    r0, MagicErrorBlock_CantOpenFile
        B       %FT85


; OpenOut. Mustn't be wild ! If exists, why don't we OpenUp + fudge extent ?

80      ORR     r7, r7, #open_mustopen  ; Always need to open if OpenOut

        LDRB    r14, wildleafname
        CMP     r14, #0
        addr    r0, MagicErrorBlock_WildCards, NE
        BNE     %FT85

        CMP     r0, #object_nothing     ; Not found ?
        LDRNE   r14, fileblock_attr     ; Is the file locked ?
        TSTNE   r14, #locked_attribute  ; If so, we must forbid OpenOut
        BEQ     %BT50                   ; Try open if not locked or not found

        addr    r0, MagicErrorBlock_ObjectLocked

85      LDR     r1, [sp]                ; Use that name, not the one from path
        BL      MagicCopyError
        EXIT


; It's a directory. Can only OpenIn, and punter may wish to winge

90      TEQ     r6, #open_read          ; If openin and we're
        TSTEQ   r7, #open_nodir         ; not wingeing, try to open it
        BEQ     %BT50

        LDR     r1, [sp]                ; Use that name, not the one from path
        BL      SetMagicIsADirectory
        EXIT


95      LDR     r1, [sp]                ; Use that name, not the one from path
        BL      SetMagicFileNotFound
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; StuffTheStreamInfo
; ==================

; In    scb^ valid and:

; r0 is the attributes + device/system identifier for this file - normally zero
; r1 is a valid Filing System handle to stuff in our scb
; r2 is the buffer size to use with this file
; r3 is the size of the file
; r4 is the space allocated to the file

; Out   stream deallocated if error

StuffTheStreamInfo ENTRY "r1"

        STR     r1, scb_fshandle
        STR     r3, scb_extent
        STR     r4, scb_allocsize

        SUB     r14, r2, #1             ; Store the mask we will be using
        STR     r14, scb_bufmask

        MOV     r1, #0                  ; Calculate log2(buffersize)
10      MOVS    r14, r14, LSR #1        ; Can't be 0
        ADD     r1, r1, #1
        BNE     %BT10
        STR     r1, scb_shift

        MOV     r14, #0                 ; Initial PTR of file is 0
        STR     r14, scb_fileptr

        LDR     r14, fsforthisop        ; Associate with the fs used to open it
        STR     r14, scb_fscb

        AND     r14, r0, #fsopen_DeviceIdentity
        STR     r14, scb_devid

        MOV     r14, #0                 ; Set funky bits, not all in order
        TST     r0, #fsopen_WritePermission
        ORRNE   r14, r14, #scb_write
        TST     r0, #fsopen_ReadPermission
        ORRNE   r14, r14, #scb_read
        TST     r0, #fsopen_IsDirectory
        ORRNE   r14, r14, #scb_directory
        TST     r0, #fsopen_UnbufferedGBPB
        ORRNE   r14, r14, #scb_unbuffgbpb
        TST     r0, #fsopen_Interactive
        ORRNE   r14, r14, #scb_interactive

; Now have converted returned access + status into scb status

        TEQ     r2, #0          ; Keep buffered/unbuffered state too
        ORREQ   r14, r14, #scb_unbuffered
        STR     r14, scb_status ; Store access mode, buffer state, EOF clear

        TEQNE   r2, #1024       ; Limited range of sensible buffer sizes
        TEQNE   r2, #512
        TEQNE   r2, #256
        TEQNE   r2, #128
        TEQNE   r2, #64         ; Below this seems senseless !
        BNE     %FA85           ; Deallocate stream if failed

; Lastly, put the scb into the stream table now that it's kosher

        ADR     r14, streamtable
        LDR     r1, scb_exthandle
        STR     scb, [r14, r1, LSL #2] ; Kapow !
 [ debugosfind
 DREG r1,"Opened file; exthandle "
 ]
        EXIT


85      addr    r0, ErrorBlock_BadBufferSizeForStream
        BL      CopyError
        BL      DeallocateStream        ; Only needs scb^ valid to do this
        SETV
        EXIT

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

        LTORG

        LNK     $fileprefix.OSBGetBPut
