; this lot will be relocated into RAM at LowCodeLocation
ALowReadCodeStart ROUT
        MOVS    R2, R1, LSL #31         ;NE <=> bit 0, CS <=> bit 1
        BNE     ARead256OddAddress
        BCS     ARead256HalfWordAddress
ARead256WordAddress ROUT
        MOV     R0, #256-4

        Align16  ALowReadCodeStart
10                              ;DONT REORDER, ALIGNMENT MOD 16 TIME CRITICAL
        SUBS    R0, R0, #7*4
        LDR     R2, HDCdataRead
        LDR     LR, HDCdataRead
        AND     R2, R2, R10,LSR #8

        ORR     R2, R2, LR, LSL #16
        LDR     R3, HDCdataRead
        LDR     LR, HDCdataRead
        AND     R3, R3, R10,LSR #8

        ORR     R3, R3, LR, LSL #16
        LDR     R4, HDCdataRead
        LDR     LR, HDCdataRead
        AND     R4, R4, R10,LSR #8

        ORR     R4, R4, LR, LSL #16
        LDR     R5, HDCdataRead
        LDR     LR, HDCdataRead
        AND     R5, R5, R10,LSR #8

        ORR     R5, R5, LR, LSL #16
        LDR     R6, HDCdataRead
        LDR     LR, HDCdataRead
        AND     R6, R6, R10,LSR #8

        ORR     R6, R6, LR, LSL #16
        LDR     R7, HDCdataRead
        LDR     LR, HDCdataRead
        AND     R7, R7, R10,LSR #8

        ORR     R7, R7, LR, LSL #16
        LDR     R8, HDCdataRead
        LDR     LR, HDCdataRead
        AND     R8, R8, R10,LSR #8

        ORR     R8, R8, LR, LSL #16
        STMIA   R1!,{R2-R8}
        BGT     %BT10
        LDR     R8, HDCdataRead

        AND     R8, R8, R10,LSR #8
        LDREQ   LR, HDCdataRead         ;EQ <=> 4n
        ORREQ   R8, R8, LR, LSL #16
        STREQ   R8, [R1], #4

        LDREQ   PC, RomReturn
        STRB    R8, [R1],#1             ;here if 4n+2
        MOV     R8, R8, LSR #8
        STRB    R8, [R1],#1

        LDR     PC, RomReturn

ARead256HalfWordAddress
        LDR     R8, HDCdataRead
        STRB    R8, [R1],#1
        MOV     R8, R8, LSR #8

        STRB    R8, [R1],#1
        MOV     R0, #256-4-2
        B       %BT10


ARead256OddAddress ROUT
        MOV     R0, #(256-4)/12

        MOV     R4, #&FF
        BCS     ARead256at4Nplus3
; so here we know that the address is 4N + 1
        LDR     R8, HDCdataRead
        STRB    R8, [R1], #1

        MOV     R8, R8, LSR #8
        STRB    R8, [R1], #1
        BL      ARead254odd
        LDR     PC, RomReturn

        Align16  ALowReadCodeStart-12
ARead254odd ROUT                ;corrupts R0,R2,R5-R8,LR
        LDR     R8, HDCdataRead
        STRB    R8, [R1],#1
        AND     R5, R4, R8, LSR #8

10                              ;DONT REORDER, ALIGNMENT MOD 16 TIME CRITICAL
        SUBS    R0, R0, #1              ;R5=next byte
        LDR     R2, HDCdataRead         ;xxxxBhBl
        LDR     R6, HDCdataRead         ;xxxxChCl
        AND     R2, R2, R10,LSR #8      ;0000BhBl

        ORR     R2, R2, R6, LSL #16     ;ChClBhBl
        LDR     R7, HDCdataRead         ;xxxxDhDl
        ORR     R6, R5, R2, LSL #8      ;ClBhBlAh
        LDR     R5, HDCdataRead         ;xxxxEhEl

        AND     R7, R10,R7, LSL #8      ;00DhDl00
        LDR     R8, HDCdataRead         ;xxxxFhFl
        ORR     R7, R7, R5, LSL #24     ;ElDhDl00
        ORR     R7, R7, R2, LSR #24     ;ElDhDlCh

        MOV     R5, R5, LSL #16         ;EhEl0000
        LDR     R2, HDCdataRead         ;xxxxGhGl
        AND     R8, R10,R8, LSL #8      ;00FhFl00
        ORR     R8, R8, R2, LSL #24     ;GlFhFl00

        ORR     R8, R8, R5, LSR #24     ;GlFhFlEh
        STMIA   R1!, {R6-R8}
        AND     R5, R4, R2, LSR #8      ;000000Gh
        BNE     %BT10

        STRB    R5, [R1],#1
        MOV     PC, LR

ARead256at4Nplus3 ROUT
; here we know the address is 4N + 3
        BL      ARead254odd
        LDR     R8, HDCdataRead
        STRB    R8, [R1],#1             ;here if 4n+2
        MOV     R8, R8, LSR #8
        STRB    R8, [R1],#1

        LDR     PC, RomReturn

ALowReadCodeEnd
ALowReadCodeSize * ALowReadCodeEnd - ALowReadCodeStart





;*********************************************************************

WinIDEIRQHandler        ROUT
;
; Called from an IRQ from the IDE system during a data transfer op.
;
; Entry:
;    SB -> static workspace
;    MODE: IRQ
;    IRQs disabled
;
; Exit:
;    All registers and flags preserved

; Switch to SVC mode so can enable IRQs without R14 being destroyed
; by the first IRQ. Should also allow us to use HostFS debug routines
; once the interrupt has been cleared.

        Push    "$WinIDEIRQRegsA,LR"
        MOV     R0,PC                           ; save mode
        TEQP    PC,#SVC_mode :OR: I_bit         ; keep IRQs disabled
        NOP

; Now in SVC mode - save regs

        Push    "$WinIDEIRQRegsB,LR"

; Set flag to indicate currently in IRQ to prevent timer interfering
; when IRQ being processed.

        MOV     R0,#&FF
        STRB    R0,WinIDEIRQFlag

; Set IDE -> IDE controller

        LDR     IDE,WinIDEPtr           ; IDE -> IDE hardware

; Disable IRQs from the IDE controller so can reenable interrupts
; without risk of interrupt from IDE whilst servicing this one

;        MOV     R0,#bit1                ; -IEN
;        STRB    R0,IDERegDigOutput
        ADD     R4,SB,#:INDEX:WinIDEIRQPtr ; R4 -> address of irq mask reg
        ASSERT  WinIDEIRQBits = WinIDEIRQPtr + 4
        LDMIA   R4,{R4,R5}              ; get mask and bits
        LDR     R1,=DefaultIDE          ; get address of motherboard IDE
        TEQS    R1,IDE                  ; motherboard?
        LDREQB  R1,[R4]                 ; yes, get IOC mask
        BIC     R1,R1,R5                ; clear enable bit
        STRB    R1,[R4]                 ; write to IOC/podule

 [ Debug22
        DLINE   "IDE IRQ incoming..."
 ]

; Now safe for data transfer routines to reenable IRQs to improve
; IRQ latency - they must restore state on exit

; Set R3 -> TmpScatterEntry
;     R4,R5 = TmpScatterEntry
;     R7 = IDE command code of current op

        sbaddr  R3,WinIDETmpScatterEntry ; R3 -> tmp scatter entry
        LDMIA   R3,{R4,R5}              ; get tmp scatter entry
        LDRB    R7,WinIDECommandCode    ; R7 = IDE command code

; Get status (will clear IRQ) and check for error

        LDRB    R8,IDERegStatus
        TSTS    R8,#IDEStatusErrorBits  ; test here for speed
        BNE     %FT80                   ; ...and branch if error

 [ Debug22
        DLINE   "No error - block transfer expected"
 ]

; No error - get blockSize (bytes of this sector to move)

        LDR     R6,WinIDEBlockSize      ; R0 = bytes to move

; R3 -> TmpScatterEntry
; R4,R5 = TmpScatterEntry
; R6 = bytes of this sector we want (blockSize)
; R7 = IDE command code of current command
; R8 = status from controller

; If read op, need to read data - check for DRQ first

        TEQS    R7,#IDECmdReadSecs      ; read?
        TSTEQS  R8,#IDEStatusDRQ        ; got DRQ?
        MOVEQ   R0,#WinIDEErrNoDRQ      ; error if not
        BEQ     %FT85

 [ Debug22
        DREG    R7, "Is a DRQ as expected for command "
 ]

; NE => not read or read and DRQ asserted

        TEQS    R7,#IDECmdReadSecs      ; read? (check again)
 [ Debug22
        BNE     %FT01
        LDR     LR, WinIDEReadPtr
        DREG    LR, "ReadSecs with routine at "
01
 ]
        MOVEQ   R1,R4                   ; if so, R1 = buf address
        MOVEQ   R0,R6                   ; ...R0 = bytes we want
        MOVEQ   LR,PC                   ; ...set link
        LDREQ   PC,WinIDEReadPtr        ; ...(R0,R1,IDE->R0,R1)
                                        ; ...returns here
 [ Debug22
        DLINE   "Update scatter list"
 ]

; Note: The read routine should have enabled IRQs whilst we have the
;       scatter list entry sitting in registers. This should be ok as
;       are supposed to disable interrupts only when writing to scatter
;       list and updating process block.
;       If read, IRQs are disabled now until leaving the IRQ routine.
;       Hence IRQs will be disabled from the final write to the scatter
;       list up to updating the process block as required.

; Now update a few things:
; adjust tmp scatter list entry for data transferred

        ADD     R4,R4,R6                ; bump scatter.address
        SUB     R5,R5,R6                ; decrement scatter.length
        STMIA   R3,{R4,R5}              ; write to tmp scatter entry

; adjust lump size

        LDR     R2,WinIDELumpSize       ; decrement bytes to go
        SUB     R2,R2,R6
        STR     R2,WinIDELumpSize

; adjust overall limit

        LDR     R0,WinIDETransferLimit
        SUB     R0,R0,R6
        STR     R0,WinIDETransferLimit

; See if this transfer is complete
; R2 = updated lumpsize

        CMPS    R2,#0
        BLE     %FT30                   ; branch if finished lump

 [ Debug22
        DLINE   "More of lump to do"
 ]
; More of this lump to do
; If we've exhausted this scatter list entry, move to next

        CMPS    R5,#0                   ; exhausted?
        BGT     %FT10                   ; branch if not

; Exhausted this scatter list entry - find next good entry

        LDR     R3,WinIDETmpScatterPtr  ; R3 -> scatter list
        ADD     R3,R3,#8                ; step to next entry
05
        LDMIA   R3,{R4,R5}              ; get the entry
        TEQS    R4,#0                   ; address < 0
        ADDMI   R3,R3,R4                ; add it in if so
        BMI     %BT05                   ; and go again

; Got a scatter list entry with a proper address.
; No need to check length as that was done when we determined the
; lumpsize for this IDE op and it must be non-zero if lump not
; finished.

        STR     R3,WinIDETmpScatterPtr
        sbaddr  R3,WinIDETmpScatterEntry ; save in tmp scatter entry
        STMIA   R3,{R4,R5}

10
; R2 = remaining lump size
; R3 -> TmpScatterEntry
; R4,R5 = TmpScatterEntry
; Set up blocksize for next sector
; This should be the minimum of the sector size and the size of the first
; entry in the scatter list. Since all except the final entry in the list
; must be a multiple of the sector size, it is ok to use the minimum of
; the sector size and the lump size.

        CMPS    R2,#WinIDEBytesPerSector        ; MIN(wanted, bytespersector)
        MOVHI   R2,#WinIDEBytesPerSector
        STR     R2,WinIDEBlockSize

; If write or format op, move data
; Should never be format because only one sector gets written per track
; and only one track at a time can be formatted.
; R7 = IDE command code
; R8 = status from controller

        TEQS    R7,#IDECmdWriteSecs     ; write?
        TEQNES  R7,#IDECmdFormatTrk     ; ...or format ?
        BNE     %FT99                   ; return if not

; It's a write (or format)

        TSTS    R8,#IDEStatusDRQ        ; must have DRQ for write
        MOVEQ   R0,#WinIDEErrNoDRQ
        BEQ     %FT85                   ; error if not

 [ Debug22
        DLINE   "It's a write and DRQ is asserted - good"
 ]

; It IS write or format op and we have DRQ
; R3 still -> TmpScatterEntry
; R4,R5 still hold TmpScatterEntry

        MOV     R1,R4                   ; R1 -> buffer
        MOV     R0,R2                   ; R0 = bytes to move

; R0 = length to transfer (up to 512 bytes will be moved)
; R1 -> buffer
; call data write routine

        MOV     LR,PC                   ; set link
        LDR     PC,WinIDEWritePtr       ; (R0,R1,IDE->R0,R1)
                                        ; returns here

; Do NOT adjust buffer address and lengths as the write may fail
; Note: the data write routine should have enabled IRQs.
;       However, the final write to the scatter list is not done
;       until the NEXT IRQ. Hence IRQs will be disabled from the
;       final write to the scatter list up to updating the process
;       block as required.

; All done for now

        BNE     %FT99

;****** Never fall through

30
; End of lump so must have hit one of
; a) overall limit on transfer
; b) amount in scatter list when op started
; c) WinIDEMaxSectorsPerTransfer sectors
;
; IDE -> IDE controller
; R3 = TmpScatterPtr
; R4 = TmpScatterEntry.addr
; R5 = TmpScatterEntry.len
; R6 = previous block size
; R7 = current op
; R8 = status from controller (= OK - no longer needed)
 [ Debug22
        DLINE   "End of lump"
 ]

 [ IDETrustDrive

; Update real scatter list with amount transferred
; This is ok even if the scatter list was faked as the first update
; will terminate the updating

        MOV     R2,R6                   ; save block size
        LDR     R6,WinIDESaveLumpSize   ; R6 = amount transferred
        LDR     R3,WinIDEScatterPtr     ; R3 -> scatter list at start of op
        BL      WinIDEUpdateScatterList ; (R3,R6->R3-R6,R8)
        MOV     R6,R2                   ; restore R6 = block size

; R3 -> real scatter list
; R4,R5 = scatter list entry (possibly R5=0)
; R6 = size of previous lump

; Convert physical address back to logical address

        BL      WinIDEPhysAddrToLogical         ; (IDE->R1,R2)

; R2 = disc address of current sector - adjust for data just transferred
; If the op is VERIFY, must add just one sector not the whole blocksize
; which, for verify, is the same as the lumpsize.
; If the op is NOT verify, add the bytes out of this sector that we
; actually wanted (blocksize).

        TEQS    R7,#IDECmdVerify                ; if verify...
  [ EngineeringMode
        TEQNES  R7,#IDECmdVerifyEng
  ]
        ADDEQ   R2,R2,#WinIDEBytesPerSector     ; ...add 1 sector...
        ADDNE   R2,R2,R6                        ; ...else add blocksize

 |

; Update disc address by amount transferred

        MOV     R1,R6                   ; save block size
        LDR     R2,WinIDEDiscAddress    ; get disc address
        LDR     R6,WinIDESaveLumpSize   ; R6 = amount transferred
        ADD     R2,R2,R6                ; R2 = updated disc address
        STR     R2,WinIDEDiscAddress    ; save it

 [ Debug20
        Push    "R1,R2"
        BL      WinIDEPhysAddrToLogical ; (IDE->R1,R2)
        Pull    "R1,R2"
        DREG    R2,"Calc "
 ]

; Update real scatter list with amount transferred
; This is ok even if the scatter list was faked as the first update
; will terminate the updating
; R6 = amount transferred

        LDR     R3,WinIDEScatterPtr     ; R3 -> scatter list at start of op
        BL      WinIDEUpdateScatterList ; (R3,R6->R3-R6,R8)
        MOV     R6,R1                   ; restore R6 = block size

; R2 = disc address of next sector
; R3 -> real scatter list
; R4,R5 = scatter list entry (possibly R5=0)
; R6 = size of previous lump

 ]

; Check if hit overall limit
; If no scatter list was provided, limit will be hit at same time as
; fake scatter list is exhausted so no problems

 [ Debug22
        DLINE   "Consider whether to continue"
 ]
        LDR     R0,WinIDETransferLimit  ; check if hit limit
        CMPS    R0,#0
        MOVLE   R0,#0                   ; if done, set completion code...
        BLE     %FT90                   ; ...and branch

; Not hit overall limit

        CMPS    R5,#0                   ; end of scatter entry?
        BGT     %FT40                   ; branch if not

; Exhausted this scatter list entry - find next good entry

        ADD     R3,R3,#8                ; step to next entry
35
        LDMIA   R3,{R4,R5}              ; get the entry
        TEQS    R4,#0                   ; address < 0
        ADDMI   R3,R3,R4                ; add it in if so
        BMI     %BT35                   ; and go again

; Got a scatter list entry with a proper address - check length

        TEQS    R5,#0                   ; zero length?
        MOVEQ   R0,#0                   ; if so, set completion code...
        BEQ     %FT90                   ; ...and branch

40
; More to do
; R2 = updated disc address
; R3 -> real scatter list
; R4,R5 hold scatter list entry

	LDR	LR,WinIDEFileCoreOp
	CMP     LR,#WinIDEFileCoreOpFormat

        BEQ	%FT50

        BL      WinIDESetPhysAddress    ; set up for next xfer (R2,IDE->R0)
	B	%FT60

50
	BL	WinIDESetPhysAddressCHS
60

; if ok, start the next transfer

        LDR     R4,WinIDETransferLimit  ; R4 = limit on transfer
 [ Debug21
        DLINE   "Start transfer..."
 ]
        BL      WinIDEStartTransfer     ; start next transfer
 [ Debug21
        DLINE   "Transfer start returned"
 ]
        BVC     %FT99                   ; all done if started ok
        B       %FT90                   ; branch if error

;****** Never fall through

80
; Drive error - decode status
;
; R3 -> TmpScatterPtr
; R4,R5 = TmpScatterEntry
; R7 = IDE command code
; R8 = contents of drive status register
; IDE -> IDE controller
 [ Debug22
        DREG    r8, "Drive error "
 ]

        MOV     R0,R8                   ; decode error
        BL      WinIDEDecodeDriveStatus ; (R0->R0)

; Set R3 -> real scatter list (start point of transfer)

        LDR     R3,WinIDEScatterPtr

; If the command was verify or write, we must adjust the amount remaining
; from the contents of the IDE registers: verify only gets an interrupt at
; the end or after error, not after each sector; write interrupts do not
; indicate whether the previous sector has been written to disc but only
; that the controller is ready to accept more data - the final interrupt
; indicates that all data has been written.
; SaveLumpSize tells us how much we WOULD have updated the transfer by
; if the entire op had succeeded. The sector register says how many sectors
; were not verified/written.

        TEQS    R7,#IDECmdVerify        ; if not verify...
 [ EngineeringMode
        TEQNES  R7,#IDECmdVerifyEng
 ]
        TEQNES  R7,#IDECmdWriteSecs     ; ...or write...
        BNE     %FT85                   ; ...branch

; Op was verify or write

        LDRB    R2,IDERegSecCount       ; get sectors not done
        LDR     R6,WinIDESaveLumpSize   ; and what we SHOULD have done
        ASSERT  WinIDEBytesPerSector = 512
        SUBS    R6,R6,R2,LSL #9         ; R6 = amount done

; R6 = amount verified/written before the error occurred.
; If >0, update the scatter list entry - this is all that needs to be updated
; as the op is now aborting.
; If <=0, do NOT update scatter list - controller is misbehaving

        BLGT    WinIDEUpdateScatterList ; (R3,R6->R3-R6,R8)

85
; Error:
;    a) status wrong
;    b) read but no DRQ
;    c) write but no DRQ

 [ Debug22
        DLINE   "Transfer expected, but DRQ not asserted"
 ]

 [ IDETrustDrive

; convert physical address back to logical address

        BL      WinIDEPhysAddrToLogical ; (IDE->R1,R2)

 |

; Convert physical address back to logical address
; Do this by reading drive registers as, even though they may be unreliable
; in no error conditions, they *must* be valid in error conditions.

        BL      WinIDEPhysAddrToLogical ; (IDE->R1,R2)
 ]


WinIDEIRQError
;
; An error has occurred during processing of the IRQ.
; (Also branched to when a command times out.)
; Possibles are
;    a) status wrong
;    b) read but no DRQ
;    c) write but no DRQ
;    d) timeout
;
; R0 = error code
; R2 = disc address of error
; R3 -> real scatter list
; R7 = IDE command code
; IDE -> IDE controller
;
; Read:
;    Must update the scatter list to indicate the amount read before
;    the error occurred.
; Verify:
;    If timeout, IDE registers are probably trashed so report the error
;    as occuring at the start of the verify. If not timeout, we have
;    already sorted out the error address and amount not verified.
; Write and Format:
;    If timeout, we cannot tell how much the drive actually wrote to the
;    disc so must return disc address = disc address at start of op and
;    not update the scatter list. If not timeout, we have already sorted
;    out the error address and updated the scatter list (for write).

 [ Debug22
        DLINE   "IDEIRQError - tidy up"
 ]

        TEQS    R7,#IDECmdReadSecs      ; read?
        LDREQ   R6,WinIDESaveLumpSize   ; if so, R6=intended xfer size
        LDREQ   R4,WinIDELumpSize       ; ...R4=amount not transferred
        SUBEQ   R6,R6,R4                ; ...R6=amount actually transferred
        BLEQ    WinIDEUpdateScatterList ; (R3,R6->R3-R6,R8)

 [ IDEResetOnError

; Reset the drives to switch the access lights off
; Note: this also enables the interrupt in the drive controller

        BL      WinIDEResetDrives       ; preserves all regs
 ]

90
; Either:
;    a) found entry in scatter list with 0 length field or have hit
;       overall limit on op - we have finished
; or:
;    b) some error has occurred
; R0 = completion code
; R2 = disc address to return
; R3 -> scatter list

; Disable IRQs from IDE
; For podule, assumes that register is write-only and that bits other
; than the enable bit are safe to alter

        ADD     R4,SB,#:INDEX:WinIDEIRQPtr ; R4 -> address of irq mask reg
        ASSERT  WinIDEIRQBits = WinIDEIRQPtr + 4
        LDMIA   R4,{R4,R5}              ; get mask and bits
        LDR     R1,=DefaultIDE          ; get address of motherboard IDE
        TEQS    R1,IDE                  ; motherboard?
        LDREQB  R1,[R4]                 ; yes, get IOC mask
        BIC     R1,R1,R5                ; clear enable bit
        STRB    R1,[R4]                 ; write to IOC/podule

; R0 = completion code
; R2 = disc address
; R3 -> scatter list
; Call callback

 [ Debug22
        DLINE   "Finished - call callback"
 ]
        MOV     LR,PC                   ; save link
        LDR     PC,WinIDECallbackPtr    ; branch to callback routine

; Callback returns here

	B	%FT100

        MOV     R0,#0
        STRB    R0,WinIDEIRQFlag

99

; Common exit from IRQ routine
; Switches back to mode saved on stack in LR, clears "in irq" flag
; and reenables IRQ in IDE digital output register.

; Clear "in IRQ" flag


        MOV     R0,#0
        STRB    R0,WinIDEIRQFlag

        ADD     R4,SB,#:INDEX:WinIDEIRQPtr ; R4 -> address of irq mask reg
        ASSERT  WinIDEIRQBits = WinIDEIRQPtr + 4
        LDMIA   R4,{R4,R5}              ; get mask and bits
        LDR     R1,=DefaultIDE          ; get address of motherboard IDE
        TEQS    R1,IDE                  ; motherboard?
        LDREQB  R1,[R4]                 ; yes, get IOC mask
        ORR     R1,R1,R5                ; set enable bit
        STRB    R1,[R4]                 ; write to IOC/podule

100

; Reenable IRQ in IDE controller.
; This happens even when there is nothing further to do as that
; latch is only used to kill interrupts during the IRQ routine. At
; all other times, it stays enabled.

;        MOV     R0,#0                   ; IEN
;        STRB    R0,IDERegDigOutput

 [ Debug22
        DLINE   "End of IRQ - return"
 ]

; Return from interrupt
; First restore SVC regs and switch back to original mode

        Pull    "$WinIDEIRQRegsB,LR"

        TEQP    PC,R0
        NOP

; Now back in original mode

        Pull    "$WinIDEIRQRegsA,PC",,^

        LTORG
















