; >FileCore15

; *********************************
; ***  CHANGE LIST AND HISTORY  ***
; *********************************
;
; 16-Jun-94   AMcC  Replaced ScratchSpaceSize with ?ScratchSpace
;
; 09-Dec-94   SBP   Made discop call to parent for small FSs check
;                   for disc address overflow and give BadParmsErr
;                   if address to big.

 [ Dev
PHEX
 [ DebugSwitch
        MOV     R3, #0
        LDR     R3, [R3,#&FFC]
        TEQS    R3, #0
        MOVEQS  PC, LR
 ]
 [ :LNOT: IrqDebug
        MOV     R3, #IRQsema
        LDR     R3, [R3]
        TEQS    R3, #0
        MOVNES  PC, LR          ;return if IRQ thread
 ]
        MOV     R3, LR
        ASSERT  SVC_mode=3
        ORR     R0, LR, #SVC_mode :OR: I_bit
        TEQP    PC, R0          ;go to SVC mode preserving flags
        nop
        Push    "LR"
 [ SpoolOff
        BL      SpoolOff
 ]
        MOV     R1, #32-4
PHLOOP
        MOV     R0, R2, LSR R1
        AND     R0, R0, #&F
        CMPS    R0, #10
        ADDCC   R0, R0, #"0"
        ADDCS   R0, R0, #"A"-10
        SWI     OS_WriteC
        SUBS    R1, R1, #4
        BPL     PHLOOP
        MOV     R0, #" "
        SWI     OS_WriteC
 [ SpoolOff
        BL      SpoolOn
 ]
        Pull    "LR"
        MOVS    PC, R3

 [ SpoolOff
SpoolOff
        Push    "R0-R2,LR"
        MOV     R0, #3
        MOV     R1, #&10
        MOV     R2, #&EF
        SWI     OS_Byte
        Pull    "R0-R2,PC",,^

SpoolOn
        Push    "R0-R2,LR"
        MOV     R0, #3
        MOV     R1, #0
        MOV     R2, #&EF
        SWI     OS_Byte
        Pull    "R0-R2,PC",,^
 ]

 [ ExceptionTrap
Exception
        B       ThroughZero
        B       Undefined
        nop                     ;dont trap SWI vector
        B       PrefetchAbort
        B       DataAbort
        B       AddressException

ThroughZero
        TEQP    PC, #F_bit :OR: I_bit :OR: SVC_mode
        nop
        STR     R13,[R0,-R0]
        MOV     R13,#ScratchSpace+?ScratchSpace
        STMDB   R13!,{R0-R14}
        LDR     R14,[R0,-R0]
        STR     R14,[R13,#13*4]
        MOV     R14,#IOC
        ASSERT  IOC :MOD: &100 = 0
        STRB    R14,[R14,#IOCFIQMSK]
        STRB    R14,[R14,#IOCIRQMSKA]
        STRB    R14,[R14,#IOCIRQMSKB]
        mess    ,"Through zero",NL
        B       ExceptionCommon

Undefined
        TEQP    PC, #F_bit :OR: I_bit :OR: SVC_mode
        nop
        STR     R13,[R0,-R0]
        MOV     R13,#ScratchSpace+?ScratchSpace
        STMDB   R13!,{R0-R14}
        LDR     R14,[R0,-R0]
        STR     R14,[R13,#13*4]
        MOV     R14,#IOC
        ASSERT  IOC :MOD: &100 = 0
        STRB    R14,[R14,#IOCFIQMSK]
        STRB    R14,[R14,#IOCIRQMSKA]
        STRB    R14,[R14,#IOCIRQMSKB]
        mess    ,"Undefined instruction",NL
        B       ExceptionCommon

PrefetchAbort
        TEQP    PC, #F_bit :OR: I_bit :OR: SVC_mode
        nop
        STR     R13,[R0,-R0]
        MOV     R13,#ScratchSpace+?ScratchSpace
        STMDB   R13!,{R0-R14}
        LDR     R14,[R0,-R0]
        STR     R14,[R13,#13*4]
        MOV     R14,#IOC
        ASSERT  IOC :MOD: &100 = 0
        STRB    R14,[R14,#IOCFIQMSK]
        STRB    R14,[R14,#IOCIRQMSKA]
        STRB    R14,[R14,#IOCIRQMSKB]
        mess    ,"Prefetch Abort",NL
        B       ExceptionCommon

DataAbort
        TEQP    PC, #F_bit :OR: I_bit :OR: SVC_mode
        nop
        STR     R13,[R0,-R0]
        MOV     R13,#ScratchSpace+?ScratchSpace
        STMDB   R13!,{R0-R14}
        LDR     R14,[R0,-R0]
        STR     R14,[R13,#13*4]
        MOV     R14,#IOC
        ASSERT  IOC :MOD: &100 = 0
        STRB    R14,[R14,#IOCFIQMSK]
        STRB    R14,[R14,#IOCIRQMSKA]
        STRB    R14,[R14,#IOCIRQMSKB]
        mess    ,"Data Abort",NL
        B       ExceptionCommon

AddressException
        TEQP    PC, #F_bit :OR: I_bit :OR: SVC_mode
        nop
        STR     R13,[R0,-R0]
        MOV     R13,#ScratchSpace+?ScratchSpace
        STMDB   R13!,{R0-R14}
        LDR     R14,[R0,-R0]
        STR     R14,[R13,#13*4]
        MOV     R14,#IOC
        ASSERT  IOC :MOD: &100 = 0
        STRB    R14,[R14,#IOCFIQMSK]
        STRB    R14,[R14,#IOCIRQMSKA]
        STRB    R14,[R14,#IOCIRQMSKB]
        mess    ,"Address Exception",NL

ExceptionCommon
        mess    ,"R0       R1       R2       R3       R4       R5       R6       R7",NL
        wrhex   R0
        wrhex   R1
        wrhex   R2
        wrhex   R3
        wrhex   R4
        wrhex   R5
        wrhex   R6
        wrhex   R7
        mess    ,NL,"R8       R9       R10      R11      R12      R13      R14",NL
        wrhex   R8
        wrhex   R9
        wrhex   R10
        wrhex   R11
        wrhex   R12
        LDR     LR, [R0,-R0]
        wrhex   LR
        LDR     LR, [R13,#14*4]
        wrhex   LR

        wrhex   R13

        mess    ,NL
        MOV     LR, #IRQsema
        LDR     LR, [LR]
        wrhex   LR
        mess    ,"IRQsema",NL
EHANG
        BAL  EHANG

 ]

Mess1                   ;R0,R1,LR stacked
        MOV     R0, PC          ;save PSR
        MOV     R1, LR          ;save link
 [ DebugSwitch
        MOV     LR, #0
        LDR     LR, [LR, #&FFC]
        TEQS    LR, #0
        MOV     LR, PC
        TSTS    LR, #Z_bit      ;toggle Z
        MOVNE   PC, R1
 ]
 [ :LNOT: IrqDebug
        MOV     LR, #IRQsema
        LDR     LR, [LR]
        TEQS    LR, #0
        MOVNE   PC, R1          ;skip if IRQ thread, return NE
 ]

        BIC     LR, R0, #3
        ORR     LR, LR, #SVC_mode :OR: Z_bit :OR: I_bit
        TEQP    PC, LR          ;go to supervisor mode, preserve FI, set EQ
        nop                     ;delay for mode change
        Push    "LR"
 [ SpoolOff
        BL      SpoolOff
 ]

        MOV     PC, R1

TubeWrHexWord ROUT
        MOV     R3, LR
        MOV     R1, #32-4
05
        MOV     R0, R2, LSR R1
        AND     R0, R0, #&F
        CMPS    R0, #10
        ADDCC   R0, R0, #"0"
        ADDCS   R0, R0, #"A"-10
        BL      TubeChar        ;(R0)
        SUBS    R1, R1, #4
        BPL     %BT05
        MOV     R0, #" "
        BL      TubeChar        ;(R0)
        MOVS    PC, R3

           ^ 0, R1
R1_status  # 4
R1_data    # 4
TubeAddress * &3344000

TubeChar ROUT
        Push    "R1,LR"
        LDR     R1, =TubeAddress
10
        LDRB    LR, R1_status
        TSTS    LR, #&40
        BEQ     %BT10
        STRB    R0, R1_data
        Pull    "R1,PC",,^
        LTORG
 ]


; =========
; IndDiscOp
; =========

; as GenIndDiscOp, but known to start at beginning of file

IndDiscOp
        MOV     R5,#0
;fall into GenIndDiscOp

; ============
; GenIndDiscOp
; ============

; read/write bytes from/to disc
; this should be used for all file operations, so that they may be stored
; non-contiguously

; entry:
;  R1 = disc op
;  R2 = ind disc address of file
;  R3 = RAM start
;  R4 = length
;  R5 = start offset in file

; exit: flags except V preserved
; IF error V set, R0 = result
;  R3,R5 incremented by amount transferred
;  R4    decremented by amount transferred

GenIndDiscOp
 [ Debug8

        DLINE   "disc op |disc add|RAM ptr |length  |File ptr - enter GenIndDiscOp"
        DREG   R1," ",cc
        DREG   R2," ",cc
        DREG   R3," ",cc
        DREG   R4," ",cc
        DREG   R5," "
 ]

 [ NewFs
  [ FileCache
        Push    "R1,R2,R6-R9,LR"
  |
        Push    "R2,R6-R9,LR"
  ]
        MOV     R6, R3			;save RAM ptr
        MOV     R3, R2
        BL      TestMap			;(R3->Z)
        BEQ     %FT50			;if new map

        ; For old map disc address is (ind disc address of file + offset)
	; this remains true on 'big' discs, but we have to squash the disc
	; address into a sector address

 [ BigDisc
	MOV	R3,R6			; restore RAM ptr

; if we have an old map disc, the indirect disc addr is simply the
; byte address of the file.  Simply shift to sector address.

        MOV	R6,R2,LSR #(32-3)	; get drive bits
	DiscRecPtr R7,R6		; get disc record
	LDRB	R7,[R7,#SectorSize]	; get the sector size
	BIC	R2,R2,#DiscBits		; disc address bits
	MOV	R2,R2,LSR R7		; change to sector address
	ADD	R2,R2,R5, LSR R7	; disc address to start at
	ORR	R2,R2,R6,LSL #(32-3)	; drive number back in

	MOV	R6,R4			; length

        BL      DoDiscOp                ;(R1-R4->R0-R4,V)

        SUB     R6, R6, R4		;amount transferred
        ADD     R5, R5, R6		;increment start offset
        B       %FT95
 |
        MOV     R3, R6			;restore RAM ptr
        ADD     R2, R2, R5		;disc address to start at.
        MOV     R6, R4
        BL      DoDiscOp		;(R1-R4->R0-R4,V)

        SUB     R6, R6, R4		;amount transferred
        ADD     R5, R5, R6		;increment start offset
        B       %FT95
 ]

50
        ; For new map disc address isn't ind disc address - have to go via map
        LDRB    R8, LockedDrive
        CMPS    R8, #8          ;V=0
        BHS     %FT52

        ; There's a locked disc, check its ours
        LDRB    lr, LockedDisc
 [ DebugL
        Push    "r0"
        MOV     r0, lr
        DREG    r0,"Locked disc is ",cc
        DREG    r3," disc address is "
        Pull    "r0"
 ]
        TEQ     lr, r3, LSR #(32-3)
        BEQ     %FT52

        ; Generate error and restore RAM ptr
        MOV     r0, #InUseErr
        BL      SetV
        MOV     r3, r6
        B       %FT95

52
        BLHS    BeforeReadFsMap ;(R3->R0,V)
        MOV     R3, R6          ;restore RAM ptr
        BVS     %FT95
        MOV     R9, #-1         ;init pre gap unknown
        MOVS    R7, R4          ;amount left to do
        BEQ     %FT90
55
  [ FileCache
        LDR     R2, [SP,#4]     ; Ind disc address
  |
        LDR     R2, [SP]	; what is this?
  ]
        BL      FindFileFragment;(R2,R5,R9->R2,R4,R9,LR)
        CMPS    R4, R7		;(R4 is the amount left in the fragment)
  [ FileCache
        BICLS   R1, R1, #BackgroundOp
  ]
        MOVHI   R4, R7          ;min (amount left to do,amount left in chunk)
        MOV     R6, R4
        BL      DoDiscOp        ;(R1-R4->R0-R4,V)
        SUB     R6, R6, R4      ;amount transferred
        ADD     R5, R5, R6      ;increment start offset
        SUB     R7, R7, R6      ;decrement amount left
 [ FileCache
        EOR     R6, R1, #BackgroundOp
        LDR     R1, [SP]
        AND     R6, R1, R6
        BLVS    %FT97
 ]
        BVS     %FT90
        TEQS    R7, #0
        BNE     %BT55           ;loop until error or done
 [ FileCache                    ;IF op is background op
        TSTS    R6, #BackgroundOp       ;AND last sub op not background
        BEQ     %FT90                   ;THEN need to do backround part if any
        TEQP    PC, #I_bit :OR: SVC_mode
        LDMIA   R3, {R6,R7}
        TEQS    R6, #0
        ADDMI   R3, R3, R6
        LDMMIIA R3, {R6,R7}
        TEQS    R7, #0          ;is background part zero length ?
        BLEQ    %FT99           ;V=0, R7=0
        TEQNEP  PC, #SVC_mode
        BLNE    DoDiscOp        ;(R1-R4->R0-R4,V) preserves Z
        TEQP    PC, #SVC_mode
 ]
90
        LDRB    LR, LockedDrive
        TEQS    LR, R8
        BLNE    UnlockMap
95
  [ FileCache
        Pull    "R1,R2,R6-R9"
  |
        Pull    "R2,R6-R9"
  ]
  [ Debug8

        DLINE   "disc op |disc add|RAM ptr |remains |file ptr|result - leave GenIndDiscOp"
        DREG   R1," ",cc
        DREG   R2," ",cc
        DREG   R3," ",cc
        DREG   R4," ",cc
        DREG   R5," ",cc
        DREG   R0," "
  ]

        B       PullLinkKeepV

 [ FileCache
97
        ; Nothing to do if not background op
        TSTS    R6, #BackgroundOp
        MOVEQS  PC, LR
99
        ; Background op - update process block
        Push    "LR"
 [ Module_Version >= 205
        ; Test for Winnie/Floppy disc now has to test drive number not disc number
        ; which is what should have been done anyway
        MOV     R9, R2, LSR #(32-3)
        DiscRecPtr R9, R9
        LDRB    R9, [R9, #DiscFlags]
        TST     R9, #FloppyFlag
        LDREQ   R9, WinnieProcessBlk
        LDRNE   R9, FloppyProcessBlk
 |
        LDRB    R9, Winnies
        CMPS    R9, R2, LSR #(32-3)
        LDRHI   R9, WinnieProcessBlk
        LDRLS   R9, FloppyProcessBlk
 ]
        ASSERT  ReadSecsOp=1
        ASSERT  WriteSecsOp=2
        TSTS    R1, #WriteSecsOp
        MOVNE   LR, #WriteBehind
        MOVEQ   LR, #ReadAhead
 [ DebugP
        DREG    r9, "Process ",cc
        Push    "r0"
        MOV     r0, lr
        DREG    r0, " going active with "
        Pull    "r0"
 ]
        STRB    LR, [R9, #Process]
        Pull    "LR"
        TSTS    LR, #V_bit
        STREQ   R7, [R9, #ProcessStatus]
        ADDNE   R9, R9, #ProcessError
        ASSERT  ProcessStatus = ProcessError + 4
        STMNEIA R9, {R0,R3}
        MOVS    PC, LR
 ]

 |

        Push    "R2,R6,LR"
        ADD     R2, R2, R5      ;disc address to start at
        MOV     R6, R4
        BL      DoDiscOp        ;(R1-R4->R0-R4,V)
        SUB     R6, R6, R4      ;amount transferred
        ADD     R5, R5, R6      ;increment start offset
  [ Debug8

        DLINE   "disc op |disc add|RAM ptr |remains |file ptr|result - leave GenIndDiscOp"
        DREG   R1," ",cc
        DREG   R2," ",cc
        DREG   R3," ",cc
        DREG   R4," ",cc
        DREG   R5," ",cc
        DREG   R0," "
  ]
        Pull    "R2,R6"
        B        PullLinkKeepV

 ]


; ========
; DoDiscOp
; ========

; As RetryDiscOp except
;  a) first ensures correct disc is present
;  b) disc must be specified by disc address, not by disc rec ptr in disc op

; entry: R1 disc op
;        R2 disc address
;        R3 RAM start
;        R4 length

; exit: flags except V preserved
; IF error V set, R0 result
;        R2,R3 incremented
;        R4 decremented

DoDiscOp ROUT
 [ Debug3

        DLINE   "disc op  disc add start    length - enter DoDiscOp"
        DREG    R1,,cc
        DREG    R2," ",cc
        DREG    R3," ",cc
        DREG    R4," "
 ]
 [ FileCache
        Push    "R0-R3,LR"
        MOV     R3, R2
        BL      FindDisc        ;(R3->R0,R1,V)
        STRVS   R0, [SP]

 |

        Push    "R0-R7,LR"
        MOV     R5, R2, LSR #(32-3)	; get disc address
        DiscRecPtr  R6, R5		; and disc record from it
        LDRB    LR, [R6,#DiscUsage]	; get usage count of disc
        ADD     LR, LR, #1              ;   and increase it so disc cant be forgotten by FindDiscRec
        STRB    LR, [R6,#DiscUsage]     ;
        MOV     R4, #0
00
;IF we think disc is in drive
        LDRB    R1, [R6,#DiscsDrv]
        CMPS    R1, #8                  ;V=0

;AND certain of drive contents
        MOVCC   R0, R1
        BLCC    PollChange              ;(R0->LR)
        CMPCCS  LR, #8
                                ;V=0
;THEN no need to search for disc
 [ Debug4
        BCS     %FT01

        DLINE   "certain where disc is"
01
 ]
        BCC     %FT60
; uncertain (must be floppy)

;first consider old drive
        LDRB    R1, [R6,#DiscsDrv]      ;may have been altered by PollChange
        CMPS    R1, #8          ;if disc is probably still in drive, try it first
 [ Debug4

        DLINE   "disc probably not in drive"
 ]
        BCS     %FT05
        BL      WhatDisc        ;(R1->R0-R2,R3,V)
        BVS     %FT05
        CMPS    R2, R5          ;also V=0
        BEQ     %FT60           ;disc is still in that drive
05

        MOV     R7, #-1         ;must be at least one floppy if here
        LDRB    R0, Floppies    ;identify uncertain drives
        ADD     R0, R0, #4
15
        SUB     R0, R0, #1
        CMPS    R0, R1
        BLNE    PollChange      ;(R0->LR)
        RSBNES  LR, LR, #7
        ADC     R7, R7, R7      ;bit 0 set <=> certain
        CMPS    R0, #4
        BHI     %BT15

;now check other drives in turn
 [ Debug4

        DREG    r7,"need to check drives in turn ",cc
        DLINE   " uncertain bits"
 ]
        MOV     R1, #4
20
        MOVS    R7, R7, ASR #1
        BCS     %FT40
        BL      WhatDisc        ;(R1->R0-R3,V)
        BVS     %FT40
                        ;If successfully examined disc
        TEQS    R2, R5          ;and it was the one we wanted
        BEQ     %FT60           ;then dont need to look any further

40
        ADD     R1, R1, #1
        CMPS    R7, #-1
        BNE     %BT20           ;loop until all uncertain drives tried

        MOV     R0, #UpCall_MediaNotPresent
        ADD     R1, R6, #DiscName
        BL      UpCall          ;(R0,R1,R4->R2-R4,C)
        BCS     %BT00           ;try again if upcall claimed
        MOV     R0, #DiscNotPresentErr
        BL      SetV
60
; V=1 <=> disc not found
        STRVS   R0, [SP]
        TEQS    R4, #0
        MOV     R4, PC
        MOVNE   R0, #UpCall_MediaSearchEnd
        BLNE    OnlyXOS_UpCall
        TEQP    PC, R4
        LDRB    LR, [R6,#DiscUsage]
        SUB     LR, LR, #1        ;protection from FreeDiscRec off
        STRB    LR, [R6,#DiscUsage]
 ]

 [ FileCache
        Pull    "R0-R3"         ;restore disc op params
 |
        Pull    "R0-R4"         ;restore disc op params
 ]
        BLVC    RetryDiscOp     ;(R1-R4->R0-R4,V) do the disc op

 [ Debug3

        DLINE   "disc op  disc add start    length - leave DoDiscOp"
        DREG    R1,,cc
        DREG    R2," ",cc
        DREG    R3," ",cc
        DREG    R4," "
 ]

 [ :LNOT: FileCache
        Pull    "R5-R7"
 ]
        B       PullLinkKeepV

; ===========
; RetryDiscOp
; ===========

; Do a disc operation with retries

; On entry:           On Exit
;  R0                  IF error V set, R0 result
;  R1 = DiscOp 0-&FF
;  R2 = Disc Address   End Disc Address         TOP 3 BITS R2 = DISC
;  R3 = RAM start      Ram End
;  R4 = Length         Untransferred

RetryDiscOp ROUT
        Push    "R0,R1,R5,R6,LR"
 [ Debug3
	DLINE	"RetryDiscOp"
 ]
        MOV     R6, R2, LSR #(32-3)     ;save disc bits
        DiscRecPtr  R5,R6
        LDRB    LR, [R5,#DiscsDrv]
        BIC     R2, R2, #DiscBits       ;replace disc bits by drive bits
        ORR     R2, R2, LR, LSL #(32-3)
        BL      RetryDriveOp
        BIC     R2, R2, #DiscBits
        ORR     R2, R2, R6, LSL #(32-3)
        MOVVC   R0, R6
        BLVC    UpdatePriority
        STRVS   R0, [SP]
        Pull    "R0,R1,R5,R6,PC"

; ============
; RetryDriveOp
; ============

; Do a disc operation with retries

; On entry:           On Exit
;  R0                  IF error V set, R0 result
;  R1 = DiscOp 0-&FF
;  R2 = Disc Address   End Disc Address         TOP 3 BITS INTERNAL DRIVE NUMBER
;  R3 = RAM start      Ram End
;  R4 = Length         Untransferred
;  R5 ->disc rec
;  R6 ->sector cache   extended sector cache

 [ Module_Version >= 209
; Remove all version and other conditionals from this operation
; Store in the drive record details of the last format operation
 ]

RetryDriveOp ROUT
        Push    "R0,R6-R8,LR"
 [ Debug3

        DLINE   "disc op  disc add start    length   disc rec  cache    >RetryDriveOp"
        DREG    R1," ",cc
        DREG    R2," ",cc
        DREG    R3," ",cc
        DREG    R4," ",cc
        DREG    R5," ",cc
        DREG    R6

        TSTS    R1, #ScatterBit		; check for scatter list
        BEQ     %FT01			; if not, skip code to handle it...
        Push    "r4,R5,r6,r7,lr"

        MOV     LR, R4			; take copy of length to count down thru scatter list
 [ BigDisc
	LDRB	R4,[R5,#SectorSize]	; get sector size
 ]
        MOV     R0, R3			; take copy of scatter list ptr
        MOV     R5, R2                  ; take copy of disc start addr to build end addr

00
        LDMIA   R0!,{R6,R7}		; get a scatter list entry
        CMPS    R7, LR                  ; if scatter list entry length greater than transfer length...
        MOVHI   R7, LR			; ...make it same size
        DREG    R5," ",cc
        DREG    R6," ",cc
        DREG    R7,,cc

        DLINE   " disc ram len"
 [ BigDisc
        ADD     R5, R5, R7,LSR R4       ; adjust end disc address (sectors)
 |
        ADD     R5, R5, R7              ; adjust end disc address (bytes)
 ]
        SUBS    LR, LR, R7		; count down length and if still more left...
        BNE     %BT00			; ...back to start of loop
        Pull    "r4,R5,r6,r7,lr"
01
 ]

        LDRB    R8, Interlocks

        ; Check the drive number - this covers fix_2 completely
        MOV     r7, r1
        MOV     r1, r2, LSR #(32-3)
        BL      CheckDriveNumber
        BVS     %FT95
        DrvRecPtr r0, r1
        MOV     r1, r7

        ; Process handling of previous format details
        AND     lr, r1, #OpMask
        TEQ     lr, #VerifyOp                   ; Verify doesn't affect LastDiscOpWasFormat
        BEQ     %FT40
        TEQ     lr, #WriteTrkOp
        LDRB    lr, [r0, #DrvFlags]
        BICNE   lr, lr, #LastDiscOpWasFormat
        STRB    lr, [r0, #DrvFlags]
        BNE     %FT40

        ; Dismount the disc attached to the drive
        Push    "r0,r1"
        LDRB    r1, [r0, #DrvsDisc]
        BIC     r1, r1, #Uncertain
        CMP     r1, #7
        BLLS    ActiveDismountDisc
        Pull    "r0,r1"
        CLRV

        LDRB    lr, [r0, #DrvFlags]
        ORR     lr, lr, #LastDiscOpWasFormat
        STRB    lr, [r0, #DrvFlags]

        ; Disc record specifies information for WriteTrkOp
        LDRB    lr, [r5, #SectorSize]
        STRB    lr, [r0, #PrevFormSectorSize]
        LDRB    lr, [r5, #SecsPerTrk]
        STRB    lr, [r0, #PrevFormSecsPerTrk]
        LDRB    lr, [r5, #Heads]
        STRB    lr, [r0, #PrevFormHeads]
        LDRB    lr, [r5, #Density]
        STRB    lr, [r0, #PrevFormDensity]
        LDRB    lr, [r5, #LowSector]
        STRB    lr, [r0, #PrevFormLowSector]
        LDR     lr, [r5, #DiscSize]
        STR     lr, [r0, #PrevFormDiscSize]
 [ BigDisc
        LDR     lr, [r5, #DiscSize2]
        STR     lr, [r0, #PrevFormDiscSize2]
 ]

40

        ; Check for cached read sectors and escape if so
        AND     LR, R1, #OpMask
        TEQ     LR, #CachedReadSecsOp
        BEQ     DoCachedReadSecs

        ; C=Background bit set
        ; Load r0 with relevant process block
        TEQS    R1, R1, LSR #BackgroundBitNo + 1        ;C=1 <=> BackgroundOp
        MOV     R7, R1
        MOV     R1, R2, LSR #(32-3)
        TSTS    R1, #4
        LDRNE   R0, FloppyProcessBlk
        LDREQ   R0, WinnieProcessBlk
        BCC     %FT50

        ; If doing background stuff (C set), then set process to
        ; read ahead or write behind as appropriate and store the
        ; drive. RetryDriveOp with background set only gets called
        ; when a process is finished and the lower level driver is idle
        ; so forcing the process to read/write is not a problem.
        ASSERT  ReadSecsOp=1
        ASSERT  WriteSecsOp=2
        TSTS    R7, #WriteSecsOp
        MOVNE   LR, #WriteBehind
        MOVEQ   LR, #ReadAhead
        STRB    R1, [R0, #ProcessDrive]
 [ DebugP
        DREG    r0, "Process ",cc
        Push    "r0"
        MOV     r0, lr
        DREG    r0, " going active with "
        Pull    "r0"
 ]
        STRB    LR, [R0, #Process]
        MOV     R1, R7
        ; Addresses generated by FileCore (background) are safe (probably)
        B       %FT60

50
        ; If doing foreground stuff then:

        ; If doing a transfer op and transfering 0 bytes then finished now
        AND     LR, R7, #OpMask
        CMP     LR, #WriteSecsOp
        CMPLS   R4, #0
        BEQ     %FT99

        ; if on floppy wait for the write-behind to finish.
        TST     r1, #4
        BLNE    DriveWriteBehindWait

        ; Lock the relevant controller from having more background stuff lumped
        ; onto it
        ORRNE   LR, R8, #FloppyLock
        ORREQ   LR, R8, #WinnieLock
        STRB    LR, Interlocks

        ; Wait for the controller to become free
        BL      WaitForControllerFree   ;(R0)
        AND     LR, R7, #OpMask

        MOV     R1, R7

        ; Check length/end of read track or write track
        TEQ     lr, #ReadTrkOp
        TEQNE   lr, #WriteTrkOp
        BNE     %FT60

        ; Check track start is within the disc
        BIC     R6, R2, #DiscBits
        LDR     LR, [R5,#DiscSize]

 [ BigDisc

	Push	"R6,R7"			; convert disc size to sectors
	LDRB	R6,[R5,#SectorSize]
	MOV	LR, LR, LSR R6
	LDR	R7, [R5,#DiscSize2]
        RSB     R6, R6, #32
        ORR     LR, LR, R7, LSL R6
	Pull	"R6,R7"

        CMPS    R6, LR                  ;reject beyond disc end
        MOVHI   R0, #BadParmsErr
        BHI     %FT95
 |
        CMPS    R6, LR                  ;reject beyond disc end
        MOVHI   R0, #BadParmsErr
        BHI     %FT95
 ]

60
        ; Determine the location of the defect list:
        ; if AltDefectBit then On the end of the disc record
        ; else if HasDefectList then In DefectSpace
        ; else Use DummyDefectList
        TST     R1, #AltDefectBit
        ADDNE   R6, R5, #MaxStruc
        BNE     %FT70
        MOV     LR, R2, LSR #(32-3)
        DrvRecPtr LR, LR
        LDRB    LR, [LR, #DrvFlags]
        TST     LR, #HasDefectList
        MOVEQ   R6, #NIL                ; Bad address to indicate 'no defect list'
        BEQ     %FT80
        LDRNE   LR, DefectSpace
        ADDNE   LR, SB, LR
        ASSERT  SzDefectList=(1 :SHL: 9)
        MOVNE   R6, R2, LSR #(32-3)
        ADDNE   R6, LR, R6, LSL #9      ;->defect list for this drive
70
        ; Defect list pointed at by R6 - check it
        MOV     R7, #0
        MOV     R0, R6
75
        LDR     LR, [R0],#4
        CMPS    LR, #DefectEndMark
        EORCC   R7, LR, R7, ROR #13
        BCC     %BT75
        EOR     R7, R7, R7, LSR #16
        EOR     R7, R7, R7, LSR #8
        EOR     R7, R7, LR
        ANDS    R7, R7, #&FF
        MOVNE   R0, #BadDefectListErr
        BNE     %F95

80

        ; Test for whether Fiq is needed
        TSTS    R2, #bit31
        MOVEQ   R0, #WinnieNeedsFiq
        MOVNE   R0, #FloppyNeedsFiq
        LDR     LR, FS_Flags
        TST     LR, R0

 [ DebugI
        BEQ     %FT01

        DLINE   " C1 ",cc
01
 ]
        ; Claim Fiq if it's needed
        BLNE    ClaimFiq
        EOR     R2, R2, #bit31          ;convert to external drive numbering
        MOV     R7, SB                  ;save static base
        BL      OnlyExternal
        LDR     SB, ParentPrivate
 [ Debug3; :LOR: DebugI :LOR: DebugBA :LOR: Debug3L

        DLINE   "D",cc
        DREG    r0,"Regs into child:",cc
        DREG    r1,,cc
        DREG    r2,,cc
        DREG    r3,,cc
        DREG    r4,,cc
        DREG    r5
 ]
 [ BigDisc
; determine whether FileSystem supports BigDisc
	LDR	LR, [R7,#:INDEX:FS_Flags]	; get the flags for filing system
	TST	LR, #BigDiscSupport		; does this FS use big addrs?
	BNE	%FT10				; skip code to adjust addrs

 [ Debug3
        DLINE   "Doing address conversion"
 ]

; convert disc addr to a byte addr

	Push	"R3,R6"

	LDRB	R6, [R5,#SectorSize]		; sector size
	AND	LR, R2, #DiscBits		; drive in LR
	BIC	R2, R2, #DiscBits		; disc offset (sector) in R2
	MOV	R3, R2, LSL R6			; convert to byte addr
        CMP     R2, R3, LSR R6                  ; check for disc addr overflow
        TSTEQ   R3, #DiscBits                   ; check for >512M
	ORREQ	R2, R3, LR			; recombine with drive no
	Pull	"R3,R6"
        MOVNE	R0,#BadParmsErr			; error
	BNE	%FT95

 [ Debug3 :LOR: DebugI :LOR: DebugBA :LOR: Debug3L
        DREG    r0,"Actual regs into child:",cc
        DREG    r1,,cc
        DREG    r2,,cc
        DREG    r3,,cc
        DREG    r4,,cc
        DREG    r5
 ]
        MOV     LR, PC                  	; set up link for return to MOV SB,R7
        LDR     PC, [R7,#:INDEX:FS_LowLevel] 	; do the low level op

	LDRB	R6, [R5, #SectorSize]		; get sector size
	AND	LR, R2, #DiscBits		; disc bits in LR
	BIC	R2, R2, #DiscBits		; offset in R2
	MOV	R2, R2, LSR R6			; back to sectors
	ORR	R2, R2, LR  			; put drive back in

	B	%FT20				;

10
; do the operation without conversion of addresses
 [ Debug3
        DLINE   "No address conversion"
 ]
	MOV	LR, PC				; set up link for return to MOV SB,R7
        LDR     PC, [R7,#:INDEX:FS_LowLevel]	; do the low level op

20

 |
        MOV     LR, PC                  ;set up link for return to MOV SB,R7
        LDR     PC, [R7,#:INDEX:FS_LowLevel] ; do the low level op
 ]
 [ Debug3; :LOR: DebugBA :LOR: Debug3L

        DLINE   "d",cc
        DREG    r0,"Regs out of child:",cc
        DREG    r1,,cc
        DREG    r2,,cc
        DREG    r3,,cc
        DREG    r4,,cc
        DREG    r5
 ]
        MOV     SB, R7
        BL      InternalFromParent
        EOR     R2, R2, #bit31          ;convert to internal drive numbering
 [ Debug3

        DLINE   "disc op  disc add start    length   disc rec  cache    >RetryDriveOp"
        DREG    R1," ",cc
        DREG    R2," ",cc
        DREG    R3," ",cc
        DREG    R4," ",cc
        DREG    R5," ",cc
        DREG    R6
 ]
        BVC     %FT99
95
        BL      SetVOnR0
        STRVS   R0, [SP]
99
        STRB    R8, Interlocks          ;restore entry interlocks
        Pull    "R0,R6-R8,PC"

; These emergency values are for the case where these fields are 0
Emergency_SectorSize * 10
Emergency_Heads * 1
Emergency_SecsPerTrk * 1

; =================
; DoChachedReadSecs
; =================

; Perform a read of a number of sectors, via a cache.  When using BigDisc,
; will only work with sector aligned addresses.  Otherwise works with
; non-aligned addresses, though the PRM defines it not to work non-aligned.

DoCachedReadSecs ROUT
        ; R7 = Amount left
        MOV     R7, R4
        LDRB    R8, [R5, #SectorSize]
        TEQ     R8, #0
        MOVEQ   R8, #Emergency_SectorSize
10
 [ BigDisc
	; calculate length to transfer upper bounded by next
	; sector boundary.
	MOV	LR, #1
        MOV     LR, LR, ASL R8		; length of single sector
        CMP     R7, LR
        MOVHS   R4, LR
        MOVLO   R4, R7
 |
        ; Calculate length to transfer upper bounded by
        ; the next sector boundary
        MOV     LR, R2, LSR R8
        ADD     LR, LR, #1
        RSB     LR, R2, LR, ASL R8      ; start of next sector - start of Tx in this sector
        CMP     R7, LR
        MOVHS   R4, LR
        MOVLO   R4, R7
 ]

        ; Remove that from the amount left and do the readsector
        SUB     R7, R7, R4
        BL      DoCachedReadSector
        BVS     %FT20

        ; If there's more go around for another go
        TEQ     R7, #0
        BNE     %BT10

20
        ; All done, let's return
        ADD     R4, R7, R4
        STRVS   R0, [SP]
        STR     R6, [SP, #1*4]
        Pull    "R0,R6-R8,PC"

; ------------------
; DoCachedReadSector
; ------------------
;
; On entry:           On Exit
;  R0                  IF error V set, R0 result
;  R1 = Op with flags
;  R2 = Disc Address   End Disc Address         TOP 3 BITS DRIVE
;  R3 = RAM start      Ram End
;  R4 = Length         Untransferred
;  R5 ->disc rec
;  R6 ->sector cache   extended sector cache
;
; Note that the transfer must be within one sector.
;
; As above, when we're using BigDisc, the addresses have
; to be sector aligned as the addrs are now sector addrs
; instead of byte addrs.

 [ BigDisc

; sector addressed version of DoCachedReadSector

DoCachedReadSector ROUT
        Push    "r0-r1,r7-r11,LR"
 [ DebugMt
        DREG    r1,"CacheReadSector(",cc
        DREG    r2,",",cc
        DREG    r3,",",cc
        DREG    r4,",",cc
        DREG    r5,",",cc
        DREG    r6,",",cc
 ]

        ; Convert from disc address to cache disc address
        ; which involves extracting side, track information and
        ; putting it back together as a disc address assuming
        ; one particular encoding.

        ; Extract the sector offset within a track

        ; R9 = SectorsPerTrack
        LDRB    R9, [R5, #SecsPerTrk]
        TEQ     R9, #0
        MOVEQ   R9, #Emergency_SecsPerTrk

 [ DebugM
        DREG    r9, "Secs per trk="
 ]

        ; R7 = Track (in user's track space)
        ; R8 = Offset into track
        BIC     R8, R2, #DiscBits
 [ DebugM
        DREG    r9, "Sector per track into DivRem = "
 ]
        DivRem  R7, R8, R9, LR

        LDRB    LR, [R5, #LowSector]
        TST     LR, #SequenceSides
        BEQ     %FT05

        ; Split the track into cylander and side if sides are sequenced in user's space

        ; R10 = cylinders per disc
        LDRB    LR, [R5, #Heads]
        TEQ     LR, #0
        MOVEQ   LR, #Emergency_Heads
        ADD     LR, LR, #1              ; Adjust due to sequenced sides
        MUL     R9, LR, R9              ; sectors per cylinder
        LDR     R11, [R5, #DiscSize]
	LDR	LR, [R5, #SectorSize]
	TEQ	LR, #0
	MOVEQ	LR, #Emergency_SectorSize
 [ BigDisc
	MOV	R11, R11, LSR LR	; sectors per disc
	RSB	LR, LR, #32
	Push	"R12"
	LDR	R12, [R5, #DiscSize2]
	ORR	R11, R11, R12, LSL LR
	Pull	"R12"
 ]

 [ DebugM
        DREG    r9, "DiscSize/sectors per cylinder into DivRem = "
 ]
        DivRem  R10, R11, R9, LR

        ; R9 = side
        ; R7 = cylinder
 [ DebugM
        DREG    r10, "Cylinders into DivRem = "
 ]
        DivRem  R9, R7, R10, LR

        B       %FT10
05
        ; split the track into cylander and side if the sides are interleaved in user's space

        ; R9 = side (remainder after division by #sides (= #Heads)
        ; R7 = cylinder
        MOV     R9, R7
        LDRB    R10, [R5, #Heads]
        TEQ     R10, #0
        MOVEQ   R10, #Emergency_Heads
 [ DebugM
        DREG    r10, "Heads to DivRem = "
 ]
        DivRem  R7, R9, R10, LR


10
        ; R7 = cylinder# in user's space
        ; R8 = Offset into track
        ; R9 = side

        ; Double the cylinder number if the user's asked for double step
        LDRB    LR, [R5, #LowSector]
        TST     LR, #DoubleStep
        MOVNE   r7, r7, ASL #1

        ; R7 = cylinder number in real, internal, space
        ; R8 = Offset into track
        ; R9 = Track's side

        ; Combine the above into an internal sector number, which is a normal
        ; disc address assuming:
        ; *  Interleaved sides
        ; *  Single stepping
        LDRB    R10, [R5, #Heads]
        TEQ     R10, #0
        MOVEQ   R10, #Emergency_Heads
        LDRB    LR, [R5, #LowSector]
        TST     LR, #SequenceSides
        ADDNE   R10, R10, #1
        MLA     R7, R10, R7, R9
        LDRB    R9, [R5, #SecsPerTrk]
        TEQ     R9, #0
        MOVEQ   R9, #Emergency_SecsPerTrk
        MUL     R7, R9, R7
        LDRB    R9, [R5, #SectorSize]
        TEQ     R9, #0
        MOVEQ   R9, #Emergency_SectorSize

        ADD     R7, R7, R8
        AND     LR, R2, #DiscBits
        ORR     R7, R7, LR

        ; R7 = Disc address of start of sector (internal disc addressing)
        ; R8 = Used to be Offset into sector of start of transfer, now nothing
 [ DebugM
        DREG    r7, "ISector is ",cc
 ]

        ; Scan the cache for the required sector
        MOV     R9, R6
        B       %FT20

15
        LDR     R10, [R9, #SectorCache_Address]
        TEQ     R10, R7
        BEQ     %FT45
        LDR     R9, [R9, #SectorCache_Next]
20
        TEQ     R9, #0
        BNE     %BT15

        ; We've run out of sector cache, so let's get the sector off
        ; disc into the sector cache (if possible)
        Push    "r2,r3"
        MOV     R3, #1
        LDRB    LR, [R5, #SectorSize]
        TEQ     LR, #0
        MOVEQ   LR, #Emergency_SectorSize
        MOV     R3, R3, ASL LR
        ADD     R3, R3, #SectorCache_HeaderSize
        MOV     R0, #ModHandReason_Claim
        BL      OnlyXOS_Module
        Pull    "r2, r3", VS
        BVS     %FT25

        ; Link the new block into the cache chain
        STR     R6, [R2, #SectorCache_Next]
        MOV     R6, R2

        ; Pass the address of the new cache block forward for reading into
        MOV     R9, R2
        Pull    "r2, r3"
        B       %FT40

25
        ; Claim didn't work - check for any sector cache and use some if there is
        CLRV
        MOV     R0, #0          ; pointer to candidate
        MOV     R1, #&ffffffff  ; rank of candidate (lots)
        MOV     R9, R6
        B       %FT35

30
        ; Record this if it's used less than candidate
        LDR     R8, [R9, #SectorCache_Usage]
        CMP     R8, R1
        MOVLO   R0, R9
        MOVLO   R1, R8

        ; Move to next
        LDR     R9, [R9, #SectorCache_Next]

35
        ; Check if list's run out
        TEQ     R9, #0
        BNE     %BT30

        ; Lists's run out - check for candidate
        MOVS    R9, R0
        BEQ     %FT50

40
        ; R2 = disc address to ultimately read from
        ; R3 = RAM destination for read
        ; R4 = Length of transfer
        ; R5 = Disc record^
        ; R6 = Cache chain^
        ; R7 = Internal disc address of sector start
        ; R9 -> block in sector cache to fill in
 [ DebugM
        DREG    r2, "ESector ",cc
        DREG    r7, ", ISector ",cc
        DREG    r3, ", RAM ",cc
        DREG    r4, ", Len ",cc
        DREG    r5, ", rec ",cc
        DREG    r6, ", cache ",cc
        DREG    r9, ", cacheblock "
 ]

        ; Read a sector to cache block at R9
        Push    "R3, R4"
        LDRB    LR, [R5, #SectorSize]
        TEQ     LR, #0
        MOVEQ   LR, #Emergency_SectorSize
        MOV     R4, #1
        MOV     R4, R4, ASL LR
        MOV     R8, R2
        ADD     R3, R9, #SectorCache_Contents
        LDRB    R1, [sp, #1*4 + 2*4]
        AND     R1, R1, #AltDefectBit :OR: NoEscape :OR: NoTimeOut
        ORR     R1, R1, #ReadSecsOp
        BL      RetryDriveOp
        Pull    "R3,R4"
        MOVVC   R0, #0
        STR     R0, [R9, #SectorCache_Error]
        MOV     R0, #0
        STR     R0, [R9, #SectorCache_Usage]
        STR     R7, [R9, #SectorCache_Address]

        ; Restore the real disc address
        MOV     R2, R8

        CLRV

45
        ; R2 = disc address to ultimately read from
        ; R3 = RAM destination for read
        ; R4 = Length of transfer
        ; R5 = Disc record^
        ; R6 = Cache chain^
        ; R9 -> block in sector cache which is filled in

        ; Found the sector, and its cache address is in R9
        LDR     R0, [R9, #SectorCache_Error]
        TEQ     R0, #0
        BNE     %FT90

        ; Found sector and it's error free, so let's copy it
        Push    "R2"
        LDR     R0, [R9, #SectorCache_Usage]
        ADD     R0, R0, #1
        STR     R0, [R9, #SectorCache_Usage]
        ADD     R0, R9, #SectorCache_Contents
        MOV     R1, R3
        MOV     R2, R4
 [ DebugM
	DREG	R3, "RAM dest "
	DREG	R4, "Transfer length "

 ]
        BL      BlockMove
        Pull    "R2"
	LDRB	LR, [R5,#SectorSize]
	TEQ	LR, #0
	MOVEQ	LR,#Emergency_SectorSize
        ADD     R2, R2, R4, LSR LR
        ADD     R3, R3, R4
        MOV     R4, #0
        B       %FT95

50
        ; R2 = disc address to ultimately read from
        ; R3 = RAM destination for read
        ; R4 = Length of transfer
        ; R5 = Disc record^
        ; R6 = Cache chain^

        ; No available cache entry to use -
        ; Read to stack, then copy to user's memory

        ; Get DiscOp together before drop stack pointer
        LDR     R1, [sp, #1*4]
        AND     R1, R1, #AltDefectBit :OR: NoEscape :OR: NoTimeOut
        ORR     R1, R1, #ReadSecsOp

        ; Get a stack frame one sector big
        MOV     LR, #1
        LDRB    R10, [R5, #SectorSize]
        TEQ     R10, #0
        MOVEQ   R10, #Emergency_SectorSize
        MOV     R10, LR, ASL R10
        SUB     SP, SP, R10

        ; R8 = RAM destination of transfer
        ; R9 = length of transfer
        MOV     R8, R3
        MOV     R9, R4

        ; Read sector to stack frame
        MOV     R3, SP
        MOV     R4, R10
 [ DebugM

        DREG    r1, "RetryDriveOpNoRMA(",cc
        DREG    r2,",",cc
        DREG    r3,",",cc
        DREG    r4,",",cc
        DREG    r5,",",cc
        DLINE   ")"
 ]
        BL      RetryDriveOp
 [ DebugM
        BVC     %FT01
        DREG    r0, "Gives error:"
01
 ]
        BVS     %FT60

        ; Copy part of frame of interest to RAM
        MOV     R0, SP
        MOV     R1, R8
        Push    "r2"
        MOV     R2, R9
        BL      BlockMove
        Pull    "R2"

        ADD     R3, R8, R9      ; construct RAM end
        MOV     R4, #0          ; bytes left

60
        ; Loose stack frame
        ADD     SP, SP, R10

        B       %FT95

90
        BL      SetV
95
        STRVS   R0, [SP]
 [ DebugMt

        DREG    r2,")->(",cc
        DREG    r3,",",cc
        DREG    r4,",",cc
        DREG    r5,",",cc
        DREG    r6,",",cc
        BVC     %FT01
        DREG    r0,",VS:",cc
        B       %FT02
01
        DLINE   ",VC",cc
02
        DLINE   ")"
 ]
        Pull    "R0-R1,R7-R11,PC"



; end of sector addressed version


 |


; OLD VERSION OF DoCachedReadSector - byte addressing


DoCachedReadSector ROUT
        Push    "r0-r1,r7-r11,LR"
 [ DebugMt
        DREG    r1,"CacheReadSector(",cc
        DREG    r2,",",cc
        DREG    r3,",",cc
        DREG    r4,",",cc
        DREG    r5,",",cc
        DREG    r6,",",cc
 ]

        ; Convert from disc address to cache disc address
        ; which involves extracting side, track information and
        ; putting it back together as a disc address assuming
        ; one particular encoding.

        ; Extract the byte offset within a track

        ; R9 = BytesPerTrack
        LDRB    LR, [R5, #SectorSize]
        TEQ     LR, #0
        MOVEQ   LR, #Emergency_SectorSize
        LDRB    R7, [R5, #SecsPerTrk]
        TEQ     R7, #0
        MOVEQ   R7, #Emergency_SecsPerTrk
        MOV     R9, R7, ASL LR
 [ DebugM
        DREG    r7, "Secs per trk="
        MOV     r7, lr
        DREG    r7, "Sector size="
        DREG    r9, "Bytes per trk ="
 ]

        ; R7 = Track (in user's track space)
        ; R8 = Offset into track
        BIC     R8, R2, #DiscBits
 [ DebugM
        DREG    r9, "Bytes per track into DivRem = "
 ]
        DivRem  R7, R8, R9, LR

        LDRB    LR, [R5, #LowSector]
        TST     LR, #SequenceSides
        BEQ     %FT05

        ; Split the track into cylander and side if sides are sequenced in user's space

        ; R10 = cylinders per disc
        LDRB    LR, [R5, #Heads]
        TEQ     LR, #0
        MOVEQ   LR, #Emergency_Heads
        ADD     LR, LR, #1              ; Adjust due to sequenced sides
        MUL     R9, LR, R9              ; bytes per cylinder
        LDR     R11, [R5, #DiscSize]
 [ DebugM
        DREG    r9, "DiscSize/bytes per cylinder into DivRem = "
 ]
        DivRem  R10, R11, R9, LR

        ; R9 = side
        ; R7 = cylinder
 [ DebugM
        DREG    r10, "Cylinders into DivRem = "
 ]
        DivRem  R9, R7, R10, LR

        B       %FT10
05
        ; split the track into cylander and side if the sides are interleaved in user's space

        ; R9 = side (remainder after division by #sides (= #Heads)
        ; R7 = cylinder
        MOV     R9, R7
        LDRB    R10, [R5, #Heads]
        TEQ     R10, #0
        MOVEQ   R10, #Emergency_Heads
 [ DebugM
        DREG    r10, "Heads to DivRem = "
 ]
        DivRem  R7, R9, R10, LR


10
        ; R7 = cylinder# in user's space
        ; R8 = Offset into track
        ; R9 = side

        ; Double the cylinder number if the user's asked for double step
        LDRB    LR, [R5, #LowSector]
        TST     LR, #DoubleStep
        MOVNE   r7, r7, ASL #1

        ; R7 = cylinder number in real, internal, space
        ; R8 = Offset into track
        ; R9 = Track's side

        ; Combine the above into an internal sector number, which is a normal
        ; disc address assuming:
        ; *  Interleaved sides
        ; *  Single stepping
        LDRB    R10, [R5, #Heads]
        TEQ     R10, #0
        MOVEQ   R10, #Emergency_Heads
        LDRB    LR, [R5, #LowSector]
        TST     LR, #SequenceSides
        ADDNE   R10, R10, #1
        MLA     R7, R10, R7, R9
        LDRB    R9, [R5, #SecsPerTrk]
        TEQ     R9, #0
        MOVEQ   R9, #Emergency_SecsPerTrk
        MUL     R7, R9, R7
        LDRB    R9, [R5, #SectorSize]
        TEQ     R9, #0
        MOVEQ   R9, #Emergency_SectorSize
        ADD     R7, R7, R8, LSR R9
        MOV     R7, R7, ASL R9
        RSB     R9, R9, #32
        MOV     R8, R8, ASL R9
        MOV     R8, R8, LSR R9
        AND     LR, R2, #DiscBits
        ORR     R7, R7, LR

        ; R7 = Disc address of start of sector (internal disc addressing)
        ; R8 = Offset into sector of start of transfer
 [ DebugM
        DREG    r7, "ISector is ",cc
        DREG    r8, ", byte offset is "
 ]

        ; Scan the cache for the required sector
        MOV     R9, R6
        B       %FT20

15
        LDR     R10, [R9, #SectorCache_Address]
        TEQ     R10, R7
        BEQ     %FT45
        LDR     R9, [R9, #SectorCache_Next]
20
        TEQ     R9, #0
        BNE     %BT15

        ; We've run out of sector cache, so let's get the sector off
        ; disc into the sector cache (if possible)
        Push    "r2,r3"
        MOV     R3, #1
        LDRB    LR, [R5, #SectorSize]
        TEQ     LR, #0
        MOVEQ   LR, #Emergency_SectorSize
        MOV     R3, R3, ASL LR
        ADD     R3, R3, #SectorCache_HeaderSize
        MOV     R0, #ModHandReason_Claim
        BL      OnlyXOS_Module
        Pull    "r2, r3", VS
        BVS     %FT25

        ; Link the new block into the cache chain
        STR     R6, [R2, #SectorCache_Next]
        MOV     R6, R2

        ; Pass the address of the new cache block forward for reading into
        MOV     R9, R2
        Pull    "r2, r3"
        B       %FT40

25
        ; Claim didn't work - check for any sector cache and use some if there is
        CLRV
        MOV     R0, #0          ; pointer to candidate
        MOV     R1, #&ffffffff  ; rank of candidate (lots)
        MOV     R9, R6
        B       %FT35

30
        ; Record this if it's used less than candidate
        LDR     R8, [R9, #SectorCache_Usage]
        CMP     R8, R1
        MOVLO   R0, R9
        MOVLO   R1, R8

        ; Move to next
        LDR     R9, [R9, #SectorCache_Next]

35
        ; Check if list's run out
        TEQ     R9, #0
        BNE     %BT30

        ; Lists's run out - check for candidate
        MOVS    R9, R0
        BEQ     %FT50

40
        ; R2 = disc address to ultimately read from
        ; R3 = RAM destination for read
        ; R4 = Length of transfer
        ; R5 = Disc record^
        ; R6 = Cache chain^
        ; R7 = Internal disc address of sector start
        ; R9 -> block in sector cache to fill in
 [ DebugM
        DREG    r2, "ESector ",cc
        DREG    r7, ", ISector ",cc
        DREG    r3, ", RAM ",cc
        DREG    r4, ", Len ",cc
        DREG    r5, ", rec ",cc
        DREG    r6, ", cache ",cc
        DREG    r9, ", cacheblock "
 ]

        ; Read a sector to cache block at R9
        Push    "R3, R4"
        LDRB    LR, [R5, #SectorSize]
        TEQ     LR, #0
        MOVEQ   LR, #Emergency_SectorSize
        MOV     R4, #1
        MOV     R4, R4, ASL LR
        MOV     R8, R2
        MOV     R2, R2, LSR LR
        MOV     R2, R2, ASL LR
        ADD     R3, R9, #SectorCache_Contents
        LDRB    R1, [sp, #1*4 + 2*4]
        AND     R1, R1, #AltDefectBit :OR: NoEscape :OR: NoTimeOut
        ORR     R1, R1, #ReadSecsOp
        BL      RetryDriveOp
        Pull    "R3,R4"
        MOVVC   R0, #0
        STR     R0, [R9, #SectorCache_Error]
        MOV     R0, #0
        STR     R0, [R9, #SectorCache_Usage]
        STR     R7, [R9, #SectorCache_Address]

        ; Restore the real disc address
        MOV     R2, R8

        ; Get the offset into the block into R8
        LDRB    LR, [R5, #SectorSize]
        TEQ     LR, #0
        MOVEQ   LR, #Emergency_SectorSize
        MOV     R8, R8, LSR LR
        MOV     R8, R8, ASL LR
        BIC     R8, R2, R8

        CLRV

45
        ; R2 = disc address to ultimately read from
        ; R3 = RAM destination for read
        ; R4 = Length of transfer
        ; R5 = Disc record^
        ; R6 = Cache chain^
        ; R8 = Offset into sector to start transfer from
        ; R9 -> block in sector cache which is filled in

        ; Found the sector, and its cache address is in R9
        LDR     R0, [R9, #SectorCache_Error]
        TEQ     R0, #0
        BNE     %FT90

        ; Found sector and it's error free, so let's copy it
        Push    "R2"
        LDR     R0, [R9, #SectorCache_Usage]
        ADD     R0, R0, #1
        STR     R0, [R9, #SectorCache_Usage]
        ADD     R0, R9, #SectorCache_Contents
        ADD     R0, R0, R8
        MOV     R1, R3
        MOV     R2, R4
        BL      BlockMove
        Pull    "R2"
        ADD     R2, R2, R4
        ADD     R3, R3, R4
        MOV     R4, #0
        B       %FT95

50
        ; R2 = disc address to ultimately read from
        ; R3 = RAM destination for read
        ; R4 = Length of transfer
        ; R5 = Disc record^
        ; R6 = Cache chain^

        ; No available cache entry to use -
        ; Read to stack, then copy to user's memory

        ; Get DiscOp together before drop stack pointer
        LDR     R1, [sp, #1*4]
        AND     R1, R1, #AltDefectBit :OR: NoEscape :OR: NoTimeOut
        ORR     R1, R1, #ReadSecsOp

        ; Get a stack frame one sector big
        MOV     LR, #1
        LDRB    R10, [R5, #SectorSize]
        TEQ     R10, #0
        MOVEQ   R10, #Emergency_SectorSize
        MOV     R10, LR, ASL R10
        SUB     SP, SP, R10

        ; R7 = offset into stack frame of transfer
        ; R8 = RAM destination of transfer
        ; R9 = length of transfer
        SUB     LR, R10, #1
        AND     R7, R2, LR
        MOV     R8, R3
        MOV     R9, R4

        ; Read sector to stack frame
        BIC     R2, R2, LR
        MOV     R3, SP
        MOV     R4, R10
 [ DebugM

        DREG    r1, "RetryDriveOpNoRMA(",cc
        DREG    r2,",",cc
        DREG    r3,",",cc
        DREG    r4,",",cc
        DREG    r5,",",cc
        DLINE   ")"
 ]
        BL      RetryDriveOp
 [ DebugM
        BVC     %FT01
        DREG    r0, "Gives error:"
01
 ]
        BVS     %FT60

        ; Copy part of frame of interest to RAM
        ADD     R0, SP, R7
        MOV     R1, R8
        Push    "r2"
        MOV     R2, R9
        BL      BlockMove
        Pull    "R2"
        SUB     R2, R2, R10     ; Original start of sector
        ADD     R2, R2, R7      ; +offset into sector
        ADD     R2, R2, R9      ; +bytes transfered
        ADD     R3, R8, R9      ; construct RAM end
        MOV     R4, #0          ; bytes left

60
        ; Loose stack frame
        ADD     SP, SP, R10

        B       %FT95

90
        BL      SetV
95
        STRVS   R0, [SP]
 [ DebugMt

        DREG    r2,")->(",cc
        DREG    r3,",",cc
        DREG    r4,",",cc
        DREG    r5,",",cc
        DREG    r6,",",cc
        BVC     %FT01
        DREG    r0,",VS:",cc
        B       %FT02
01
        DLINE   ",VC",cc
02
        DLINE   ")"
 ]
        Pull    "R0-R1,R7-R11,PC"

 ]

; ==============
; GetDriveStatus
; ==============

; used to read low-level status info that cannot
; be read using something such as PollChange
; because PollChange cannot be called from
; background

; entry:
;
; R1=(internal) drive number
;
; exit:
;
; R2=drive status
; LR=0 <-> status not supported

GetDriveStatus ROUT
        Push    "R0,LR"
        LDR     LR, FS_Flags                    ; get status flags
        ANDS    LR, LR, #DriveStatusWorks       ; check for status supported
 [ DebugDL
        BNE     %FT01
        DLINE   "GetDriveStatus: Not supported"
01
 ]
        Pull    "R0,PC", EQ                     ; not supported - LR will be 0
        MOV     R0, #Misc_DriveStatus           ; do the miscop
        BL      Parent_Misc
        STRVS   R0, [SP]                        ; if error then return it
        MOV     LR, #1
        Pull    "R0, PC"

; ========
; ClaimFiq
; ========

ClaimFiq ROUT
 [ Debug3

        DLINE   "ClaimFiq"
 ]
        Push    "R0-R2,LR"
        TEQP    PC, #I_bit :OR: SVC_mode        ;disable IRQ to prevent re-entrance
        LDRB    R2, FiqOwnership
        TEQS    R2, #0
        BNE     %FT90                   ;already claimed

        TEQP    PC, #SVC_mode           ;can re-enable IRQs as cannot get background
        MOV     R1, #Service_ClaimFIQ   ;release unless at least one claim
        SWI     XOS_ServiceCall         ;(R1)
        MOV     R1, #IOC
        ASSERT  IOC    :MOD: 256 = 0
        MOV     LR, #0
        STRB    LR, [R1,#IOCFIQMSK]     ;Disable all FIQs
90
        ADD     R2, R2, #1
        STRB    R2, FiqOwnership
 [ DebugI
        DREG    R2,,cc
 ]
 [ Debug3

        DLINE   "FIQ claimed"
 ]
        Pull    "R0-R2,PC",,^

; ==========
; ReleaseFiq
; ==========

ReleaseFiq ROUT
 [ Debug3

        DLINE   "*ReleaseFiq"
 ]
        Push    "R0-R2,LR"
        TEQP    PC, #I_bit :OR: SVC_mode        ;disable IRQ to prevent re-entrance
        LDRB    R2, FiqOwnership
        SUBS    R2, R2, #1
        STRB    R2, FiqOwnership
        BNE     %FT90

        TEQP    PC, #SVC_mode           ;can reenable IRQs as background claim can't
        MOV     R1, #IOC                ;succeed until after release
        STRB    R2, [R1,#IOCFIQMSK]     ;Disable all FIQs
        MOV     R1, #Service_ReleaseFIQ
        SWI     XOS_ServiceCall         ;(R1)
90
 [ DebugI
        DREG    R2,,cc
 ]
 [ Debug3

        DLINE   "FIQ Released"
 ]
        Pull    "R0-R2,PC",,^

 [ FileCache

; ==================
; BackgroundClaimFiq
; ==================

;Exit EQ <=> succeeded

BackgroundClaimFiq ROUT
 [ Debug3 :LOR: DebugG

        DLINE   "BackgroundClaimFiq"
 ]
        Push    "R0-R2,LR"
        LDRB    LR, BackgroundFiqLock
        TEQS    LR, #0
        Pull    "R0-R2,PC",NE

        TEQP    PC, #I_bit :OR: SVC_mode        ;disable IRQ to prevent re-entrance
        LDRB    R2, FiqOwnership
        TEQS    R2, #0
        BNE     %FT90                   ;already claimed

        MOV     R1, #IRQsema
        LDR     R1, [R1]
        TEQS    R1, #0
        MOVEQ   R1, #Service_ClaimFIQ
        MOVNE   R1, #Service_ClaimFIQinBackground
        SWI     XOS_ServiceCall         ;(R1)
        TEQS    R1, #0
        TEQNES  R1, #Service_ClaimFIQ
        Pull    "R0-R2,LR",NE
        BICNES  PC, LR, #Z_bit          ;if claim failed return NE

        MOV     R1, #IOC
        MOV     LR, #0
        STRB    LR, [R1,#IOCFIQMSK]    ;Disable all FIQs
90
        ADD     R2, R2, #1
        STRB    R2, FiqOwnership
 [ DebugI

        DLINE   " C0 ",cc
        DREG    R2,,cc
 ]
 [ Debug3 :LOR: DebugG

        DLINE   "FIQ claimed"
 ]
        Pull    "R0-R2,LR"
        ORRS    PC, LR, #Z_bit          ;if claim succeeded return EQ

 ]

        LTORG

        END
