        SUBT    > <wini>arm.FileSwitch.OSBGetBPut - BGet, BPut

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; +                                                                           +
; +                         B G E T   S W I                                   +
; +                                                                           +
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; BGetEntry. Vectored SWI level entry
; =========
;
; Get a byte from the given file. Optimised for speed in the buffered case

; In    r1b = file handle

; NB. Neither the BGet or BPut caches are guaranteed to work if a file handle
;     of DeadHandle is passed in as that is the handle used to invalidate the
;     cache: if it matches this, it then assumes the cached scb^ is valid ...

DeadHandle * &DEAD2300 ; LSB can't be valid handle

; Out   VC: ok, r0 = char
;       VS: fail, handle given with error

 [ :LNOT: AssemblingArthur ; BGet cache section in kernel now, so watch out ...

BGetEntry ROUT ; Make do without a frame in cache section

 [ debugosbget
 DREG r1,"OSBGet: handle "
 ]

Proc_RegList SETS "scb, bcb, $streaminfo, fp"
Proc_LocalStack SETA localframesize

 assert bcb = r8
 assert scb > r8
        Push    "r8, scb, fp"                   ; Note the nastiness

 assert :INDEX: BGet_shiftedbase = 0
 assert :INDEX: BGet_bufferdata = 4
 assert :INDEX: BGet_scb        = 8
 assert :INDEX: BGet_shift      = 12
 assert :INDEX: BGet_handle     = 16
        LDMIA   wp, {r0, r8, scb, fp, r14}      ; Get cached data
        TEQ     r14, r1                         ; BGet on cached handle ?
        LDREQ   r14, scb_fileptr                ; Check fileptr in that buffer
        TEQEQ   r0, r14, LSR fp
        LDREQB  r0, [r8, r14]                   ; Get byte from buffer
        ADDEQ   r14, r14, #1
        STREQ   r14, scb_fileptr                ; Increment fileptr
        Pull    "r8, scb, fp, lr", EQ           ; Destack all, and punter lr
        BICEQS  pc, lr, #V_bit + C_bit + I_bit  ; Whew ! Allow IRQ here


 [ debugosbgetcache
 DLINE "BGet cache miss"
 ]
        Push    "status, fileptr, extent, bufmask"
                                                ; So we can reload $streaminfo
        InitialiseFrame

 | ; cache in kernel

BGetEntry NewSwiEntry "scb, bcb, $streaminfo"

 [ debugosbget
 DREG r1,"OSBGet: handle "
 ]
 ]

        AND     bcb, r1, #&FF           ; r1b is file handle
        ADR     r14, streamtable
        LDR     scb, [r14, bcb, LSL #2] ; Get scb^

        ReadStreamInfo bcb              ; bcb invalid for unbuffered streams,
                                        ; but who cares ?

        EOR     r14, status, #scb_read  ; Must be set
        TST     r14, #scb_read :OR: scb_directory :OR: scb_unbuffered :OR: scb_EOF_pending :OR: scb_unallocated
        TSTEQ   r14, #scb_datalost :OR: scb_critical
        BNE     %FT85                   ; Any of these set ?

; Buffered BGet. Don't bother to get valid buffer if at EOF !
 [ debugosbgetcache
 DLINE "Valid BGet buffered file"
 ]

        CMP     fileptr, extent         ; Reading at EOF ? CSet if true
        BEQ     %FT90                   ; NB. Order of checks !

        CMP     bcb, #Nowt              ; No buffer present ?
        BEQ     %FT50

        BIC     r0, fileptr, bufmask    ; Where buffer base needs to be
        LDR     r14, bcb_bufferbase
        CMP     r14, r0
        BNE     %FT50

        SUB     r14, r14, #:INDEX: bcb_bufferdata ; Form offset into bcb
                                        ; if in the correct buffer
 [ debugosbgetcache
 DLINE "BGet: have correct buffer"
 ]

40      SUB     r14, fileptr, r14
        LDRB    r0, [bcb, r14]          ; Get a byte from buffer%offset
        ADD     fileptr, fileptr, #1
        STR     fileptr, scb_fileptr    ; Update stream fileptr

 [ True ; Set up cache data
        BIC     r14, fileptr, bufmask   ; Cache if not now in end sector
        BIC     r4, extent, bufmask
        CMP     r4, r14
 assert fileptr = r4 ; Not needed anymore
 [ debugosbgetcache
 BEQ %FT66
 DLINE "Setting up BGet cache"
 B %FT67
66
 DLINE "In end sector: not doing BGet cache"
67
 ]
        STRNE   r1, BGet_handle         ; Cache the file data to use next time
        STRNE   scb, BGet_scb

        LDRNE   r14, scb_shift
        STRNE   r14, BGet_shift
        LDRNE   bcb, scb_bcb            ; Get real bcb^
        LDRNE   r4, bcb_bufferbase      ; Need real bcb^ so we can load this
        MOVNE   r14, r4, LSR r14        ; bufferbase >> shift
        STRNE   r14, BGet_shiftedbase
        ADDNE   r14, bcb, #:INDEX: bcb_bufferdata
        SUBNE   r14, r14, r4            ; bcb^ +offset +(fileptr -basefileptr)
        STRNE   r14, BGet_bufferdata    ; Add fileptr to this to get data addr
 ]

 [ debugosbget
 BNE %FT00
 DREG r0,"OSBGet: got ",cc,Byte
 DREG r1,", handle "
00
 ]

45      ADD     sp, sp, #localframesize
        Pull    "$Proc_RegList, lr"             ; Destack all, and punter lr
        BICS    pc, lr, #V_bit + C_bit + I_bit  ; Allow an interrupt here


; End of simple BGet case. Now for the boring crap
; .............................................................................

50 ; Not got the right buffer. Get it and try again !

        BL      InvalidateBGetCache

        BL      FindFileBuffer          ; May as well still be general
        BNE     %FT55

        BL      GetFileBuffer
        BVS     %FA99

55      LDR     r14, bcb_dataoffset     ; Form offset into bcb
        B       %BT40



90 ; fileptr = extent. Set EOF error flag, return CSet

        BL      InvalidateBGetCache

91      LDR     r0, =EOF_char
        ORR     status, status, #scb_EOF_pending ; Next read will cause EOFerr
        STR     status, scb_status
 [ debugosbget
 DLINE "EOF detected"
 ]
        ADD     sp, sp, #localframesize
        Pull    "$Proc_RegList, lr"     ; Destack all, and punter lr
        BIC     lr, lr, #V_bit + I_bit  ; Allow IRQ real soon now
        ORRS    pc, lr, #C_bit



85 ; Complicated BGet - some flags set, so check 'em

 [ debugosbget
 DLINE "Complicated BGet"
 ]
        TST     status, #scb_unallocated ; Not a stream ?
        BNE     %FT92

        TST     status, #scb_critical   ; Return NULL
 [ debugosbget
 BEQ %FT00
 DLINE "scb_critical set"
00
 ]
        MOVNE   r0, #0
        BNE     %BA45                   ; Exit sets CClear

        TST     status, #scb_datalost   ; Can't read from dead file
        BNE     %FT93

        TST     status, #scb_directory  ; Can't read from directory
        BNE     %FT94

        TST     status, #scb_read       ; Can't read if no permission
        BEQ     %FT96

        TST     status, #scb_EOF_pending ; Already read EOF on this handle ?
        BNE     %FA98                   ; Give error if so

; Must have branched because unbuffered then

        BL      CallFSBGet              ; Get single byte from file
        BVS     %FA99
        BCC     %BA45                   ; Exit sets CClear
        B       %BT91                   ; Returning EOF exit


98      addr    r0, ErrorBlock_EndOfFile
        BL      CopyError

 [ appendhandle
99      BL      AppendHandleToError      ; For all errors in BGet
        SwiExit
 |
99      SwiExit
 ]

92      BL      SetErrorChannel
        B       %BA99

93      BL      SetErrorDataLost
        B       %BA99

94      BL      SetErrorStreamIsDirectory
        B       %BA99

96      BL      SetErrorNotOpenForReading
        B       %BA99

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; +                                                                           +
; +                         B P U T   S W I                                   +
; +                                                                           +
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; BPutEntry. Vectored SWI level entry
; =========
;
; Put a byte to the given file

; In    r0b = byte, r1b = file handle

; Out   VC: ok, all registers preserved
;       VS: fail, handle given with error

 [ :LNOT: AssemblingArthur ; BGet cache section in kernel now, so watch out ...

BPutEntry ROUT ; Make do without a frame in cache section

Proc_RegList    SETS "$streaminfo, r7, bcb, scb, fp" ; When we have frame set
Proc_LocalStack SETA localframesize

 [ debugosbput
 DREG r0,"OSBPut: byte ",cc
 DREG r1,", handle ",cc
 DREG sp,", sp = ",cc
 LDR lr, [sp]
 DREG lr,", stacked lr = "
 ]

; r7 used as temp anyway in BPut, so it's free to abuse here !
 assert bcb = r8
 assert scb > r8
        Push    "r7, bcb, scb, fp"              ; Note the nastiness

;       BPut_shiftedbase                        ; r7
 assert BPut_bufferdata = BPut_shiftedbase + 4  ; r8
 assert BPut_scb        = BPut_shiftedbase + 8  ; scb
 assert BPut_shift      = BPut_shiftedbase + 12 ; fp
 assert BPut_handle     = BPut_shiftedbase + 16 ; r14
        ADR     r14, BPut_shiftedbase           ; Can't be as nasty as BGet
        LDMIA   r14, {r7, r8, scb, fp, r14}     ; Get cached data
        TEQ     r14, r1                         ; BPut on cached handle ?
        LDREQ   r14, scb_fileptr                ; Check fileptr in that buffer
        TEQEQ   r7, r14, LSR fp
        STREQB  r0, [r8, r14]                   ; Put byte to buffer
        ADDEQ   r14, r14, #1
        STREQ   r14, scb_fileptr                ; Increment fileptr
        Pull    "r7, bcb, scb, fp, lr", EQ      ; Destack all, and punter lr
        BICEQS  pc, lr, #V_bit + I_bit          ; Whew ! Allow IRQ here

; No need to set modified flag as that will have been done by the BPut that
; set up the BPut cache

 [ debugosbgetcache
 DLINE "BPut Cache miss"
 ]

        Push    "status, fileptr, extent, bufmask"
                                                ; So we can reload $streaminfo
        InitialiseFrame

 | ; cache in kernel

BPutEntry NewSwiEntry "r7, scb, bcb, $streaminfo"

 [ debugosbput
 DREG r0,"OSBPut: byte ",cc
 DREG r1,", handle ",cc
 ]
        SUB     wp, wp, #:INDEX: BPut_shiftedbase ; Gosh, we are SO nasty !
 ]

        AND     bcb, r1, #&FF           ; r1b is file handle
        ADR     r14, streamtable
        LDR     scb, [r14, bcb, LSL #2] ; Get scb^

        ReadStreamInfo bcb              ; bcb invalid for unbuffered streams,
                                        ; but who cares ?

10      EOR     r14, status, #scb_write :OR: scb_modified ; reenter from below
                                        ; Must be set/normally set
        TST     r14, #scb_write :OR: scb_directory :OR: scb_unbuffered :OR: scb_unallocated :OR: scb_EOF_pending
        TSTEQ   r14, #scb_datalost :OR: scb_critical
        BNE     %FT85                   ; Any of these set ?

; Buffered BPut

        CMP     bcb, #Nowt              ; No buffer present ?
        BEQ     %FT60

        BIC     r7, fileptr, bufmask    ; Where buffer base needs to be
        LDR     r14, bcb_bufferbase     ; Think of a register, any register ...
        CMP     r14, r7
        BNE     %FT60                   ; Wrong buffer ?

        SUB     r14, r14, #:INDEX: bcb_bufferdata ; Form offset into buffer

40      SUB     r14, fileptr, r14
        STRB    r0, [bcb, r14]          ; Put the byte at buffer%offset
        ADD     fileptr, fileptr, #1    ; Increment file pointer

        STMIA   scb, {status, fileptr}  ; These two always get updated

 [ True ; Always set up cache data! This is reeeeely dangerous
  [ debugosbputcache
 DLINE "Setting up BPut cache"
  ]
        STR     r1, BPut_handle         ; Cache the file data to use next time
        STR     scb, BPut_scb

        LDR     r14, scb_shift
        STR     r14, BPut_shift
        LDR     bcb, scb_bcb            ; Put real bcb^
        LDR     r4, bcb_bufferbase      ; Need real bcb^ so we can load this
        MOV     r14, r4, LSR r14        ; bufferbase >> shift
        STR     r14, BPut_shiftedbase
        ADD     r14, bcb, #:INDEX: bcb_bufferdata
        SUB     r14, r14, r4            ; bcb^ +offset +(fileptr -basefileptr)
        STR     r14, BPut_bufferdata    ; Add fileptr to this to get data addr
 ]

        LDRB    r14, bcb_status         ; Buffer modified
        ORR     r14, r14, #bcb_modified
        STRB    r14, bcb_status

 [ False ; no need to bother now, as checks needed for cache version
        CMP     fileptr, extent         ; Writing past current eof ?
        STRHI   fileptr, scb_extent     ; Write new file extent if so
 ]

45      ADD     sp, sp, #localframesize
        Pull    "$Proc_RegList, pc",,^  ; No need to SwiExit as no error


; End of simple BPut case. Now for the boring crap
; .............................................................................

60      BL      InvalidateBGetCache

        BL      EnsureBufferValidForWrite ; Get bcb^ valid for this fileptr
        LDRVC   r14, bcb_dataoffset     ; Form offset into bcb
        BVC     %BT40                   ; And try again
        B       %FA99


85 ; Complicated BPut - some flags set (or clear), so check 'em

 [ debugosbput
 DLINE "Complicated BPut"
 ]
        TST     status, #scb_unallocated ; Not a stream ?
        BNE     %FT92

        TST     status, #scb_critical   ; Trash the char !
 [ debugosbput
 BEQ %FT00
 DLINE "scb_critical set"
00
 ]
        BNE     %BT45                   ; quick exit

        TST     status, #scb_datalost   ; Can't write to dead file
        BNE     %FT93

        TST     status, #scb_modified   ; Set modified if not already
 [ debugosbput
 BNE %FT00
 DLINE "Not previously modified; retry after setting"
00
 ]
        ORREQ   status, status, #scb_modified ; NB. Don't do this to UnallocStr
        BEQ     %BT10                   ; And try again

        TST     status, #scb_EOF_pending ; Clear EOF if set
 [ debugosbput
 BEQ %FT00
 DLINE "EOF was set; retry after clearing"
00
 ]
        BICNE   status, status, #scb_EOF_pending ; NB. Don't do this to Unalloc
        BNE     %BT10                   ; And try again

        TST     status, #scb_directory  ; Can't write to directory
        BNE     %FT94

        TST     status, #scb_write      ; Can't write if no permission
        BEQ     %FT96

; Must have branched because unbuffered then

        BL      CallFSBPut              ; Send single byte to file
        STRVC   status, scb_status      ; and exit, writing status if ok

 [ appendhandle
95      SwiExit


99      BL      AppendHandleToError     ; For all errors in BPut
        B       %BA95 ; SwiExit
 |
99      SwiExit
 ]


92      BL      SetErrorChannel
        B       %BA99

93      BL      SetErrorDataLost
        B       %BA99

94      BL      SetErrorStreamIsDirectory
        B       %BA99

96      BL      SetErrorNotOpenForUpdate
        B       %BA99

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; InvalidateBGetBPutCache
; =======================

; Invalidate cache(s) if handle corresponds

; In    scb^ valid

InvalidateBGetCache ROUT

        Push    "r1, lr"
 [ debugosbget
 DLINE "Invalidate BGet and/or BPut cache"
 ]
        LDRB    r1, scb_exthandle

        LDR     r14, BPut_handle ; As quick as reasonably possible if neither
        CMP     r14, r1
        BEQ     %FA50           ; Need to kill BPut cache
        LDR     r14, BGet_handle
        CMP     r14, r1
        Pull    "r1, pc",NE,^   ; No more to kill


; Only BGet cache to kill if drop thru to here; BPut cache already killed if
; we're branching back to label 20


20      ADR     r1, BGet_shiftedbase

 assert :INDEX: BGet_shiftedbase = 0 ; Don't have to fart around with expr's

30      LDR     r14, =DeadHandle
        STR     r14, [r1, #:INDEX: BGet_handle] ; Invalidate handle
 [ False ; This is totally unnecessary given invalid handle
        MOV     r14, #-1
        STR     r14, [r1, #:INDEX: BGet_shiftedbase] ; Will give NE on TEQ
        MOV     r14, #32
        STR     r14, [r1, #:INDEX: BGet_shift] ; fileptr shifted out of skull
        addr    r14, UnallocatedStream
        STR     r14, [r1, #:INDEX: BGet_scb]
 ]
        Pull    "r1, pc",,^             ; Return, either to caller, or below


50      Push    "r1, pc"                ; Make routine return to .+12
        ADR     r1, BPut_shiftedbase    ; Order means we don't need NOP
        B       %BT30

        LDR     r14, BGet_handle        ; r1 preserved over call
        CMP     r14, r1
        Pull    "r1, pc",NE,^           ; No more to kill

        B       %BT20

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Ensure both caches invalid

Init_InvalidateBGetCache ENTRY "r1"

        LDR     r14, =DeadHandle
        STR     r14, BGet_handle        ; Invalidate handles
        STR     r14, BPut_handle
        MOV     r14, #-1
        STR     r14, BGet_shiftedbase   ; Will give NE on TEQ
        STR     r14, BPut_shiftedbase
 [ False ; No need to initialise this as -1 shiftedbase must always give NE
        MOV     r14, #32
        STR     r14, BGet_shift         ; fileptr shifted out of skull
        STR     r14, BPut_shift
 ]
        addr    r14, UnallocatedStream
        STR     r14, BGet_scb
        STR     r14, BPut_scb
        EXITS

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

        LTORG

        LNK     $fileprefix.OSGBPB
