; s.Driver

                GET             hdr.Common

                AREA            |$Driver|, PIC, CODE

                EXPORT          Register
                EXPORT          Unregister
                EXPORT          StopSharedSound
                EXPORT          ReleaseAudioBuffer
                EXPORT          AudioBufferSizeInLBA
                EXPORT          AudioBufferSize
                EXPORT          AudioLBAsToRead
                EXPORT          AudioBytesToRead
                EXPORT          StopAllAudio

; Register the driver

Register        SAVE            "r0-r3"
                ADR             r0, InfoWord
                MOV             r1, #31         ; number of commands handled
                MOV             r2, #0          ; usage count
                MOV             r3, #1          ; number of types
                STMIA           r0, {r1-r3}
                ADR             r1, Driver
                MOV             r2, ws
                SWI             XCD_Register
Register_exit
                RETURN_ERR

Unregister      SAVE            "r0-r1"
                ADR             r0, InfoWord
                ADR             r1, Driver
                SWI             XCD_Unregister
                RETURN_ERR

; Track SWI vector, so we can redirect CD 0 to our virtual drive
SWIVector DCD 0
; Driver entry point
;
; R0-
; R6  ==> Arguments from CD_ swi
; R7  ==> Control block
;            0000   SCSI device ID
;            0004   SCSI card number
;            0008   SCSI LUN
;            0012   Drive type
; R11 ==> CD swi number: 0 == CD_ReadData, 1 == CD_SeekTo (top two bits may be set)
; R12 ==> Our workspace pointer
; R13 ==> Full descending stack
; R14 ==> Address to return to
;
; Internally we make R10 point to the drive block for this drive, or 0
; if we can't identify it
;
; R10 ==> drive block or 0

Driver          &               &EE50EE50       ; EESOX

                [               Debug

                SAVE

                DBF             "%r11b "

;                DBF             "\nEnter %0w %1w %2w %3w %4w\n"
;                DBF             "      %5w %6w %7w %bw\n"

                BL              DriverCode

;                DBF             "**** Error ****\n", VS
;                DBF             "Exit  %0w %1w %2w %3w %4w\n"
;                DBF             "      %5w %6w %7w %bw\n\n"

                RETURN_ERR

                ]

DriverCode      MOV             r8, r11, LSL #2  ; Clear retry bits
                CMP             r8, #DrivJmpEnd - DrivJmp
                MOVGE           pc, lr

; Work out which drive it is and call the handler

                STMFD           sp!, {r0-r6}

                LDMIA           r7, {r0-r2}     ; ID, Card & LUN
                DBF             "Drive %0w, %1w, %2w\n"

                ADR             r10, DriveBlock  ; Base of drive block
                ADD             r3, r10, #(MaxDrive * Drive_Size) :AND: &00FF
                ADD             r3, r3,  #(MaxDrive * Drive_Size) :AND: &FF00

Driver1         CMP             r10, r3         ; No more?
                MOVGE           r10, #0
                BGE             Driver2
                LDMIA           r10, {r4-r6}    ; ID, Card & LUN
                DBF             "At %r10w, %4w, %5w, %6w\n"
                TEQ             r0, r4
                TEQEQ           r1, r5
                TEQEQ           r2, r6
                ADDNE           r10, r10, #Drive_Size :AND: &00FF
                ADDNE           r10, r10, #Drive_Size :AND: &FF00
                BNE             Driver1

Driver2         LDMFD           sp!, {r0-r6}
;    B skippy
;nextRow DCB 31, 0, 0, 0
;skippy
;    STMFD r13!, {r0-r2, r14}
;    ADR r0, nextRow
;    MOV r1, #4
;    SWI XOS_WriteN
;    LDRB r0, nextRow+2
;    ADD r0, r0, #1
;    CMP r0, #30
;    MOVHI r0, #0
;    STRB r0, nextRow+2
;    SWI XOS_WriteS
;    DCB 17, 15, 17, 128, 0
;    ALIGN
;    SUB r13, r13, #12
;    MOV r0, r8, LSR #2
;    MOV r1, r13
;    MOV r2, #9
;    SWI XOS_ConvertHex1
;    SWI XOS_Write0
;    SWI XOS_NewLine
;    SWI &100+" "
;    SWI &100+" "
;    ADD r13, r13, #12
;    LDMFD r13!, {r0-r2, r14}

                ADD             pc, pc, r8
IgnoredSWI      MOV             pc, lr          ; Padding
DrivJmp         B               ReadData        ; 00
                B               SeekTo          ; 01
                B               DriveStatus     ; 02
                B               DriveReady      ; 03
                B               GetParameters   ; 04
                B               SetParameters   ; 05
                B               OpenDrawer      ; 06
                B               EjectButton     ; 07
                B               EnquireAddress  ; 08
                B               EnquireDataMode ; 09
                B               PlayAudio       ; 10 0a
                B               PlayTrack       ; 11 0b
                B               AudioPause      ; 12 0c
                B               EnquireTrack    ; 13 0d
                B               ReadSubChannel  ; 14 0e
                B               CheckDrive      ; 15 0f
                B               DiscChanged     ; 16 10
                B               StopDisc        ; 17 11
                B               DiscUsed        ; 18 12
                B               AudioStatus     ; 19 13
                B               Inquiry         ; 20 14
                B               DiscHasChanged  ; 21 15
                B               Control         ; 22 16
                B               Supported       ; 23 17
                B               Prefetch        ; 24 18
                B               Reset           ; 25 19
                B               CloseDrawer     ; 26 1a
                B               IsDrawerLocked  ; 27 1b
                B               AudioControl    ; 28 1c
                B               AudioLevel      ; 29 1d
                B               Identify        ; 30 1e
                B               ReadAudio       ; 31 1f
                B               ReadUserData    ; 32 20
                B               SeekUserData    ; 33 21
                B               GetAudioParams  ; 34 22
                B               SetAudioParams  ; 35 23
DrivJmpEnd

; Handle CD_ReadData
;
; R0  ==> Addressing mode (0 == LBA, 1 = Red Book, 2 = PBA)
; R1  ==> Block number
; R2  ==> Number of blocks
; R3  ==> Address to put data
; R4  ==> Number of bytes from each block to read
; R7  ==> Control block

ReadData        DBF             "\nReadData %0w, %1w, %2w, %3w, %4w, %9w, %aw\n"

;JJA commented as there's no reason to restrict to MODE 0 ISO's
;                CMP             r2, #1
;                CMPLE           r4, #2048
;                BLE             ReadData1
;
;                TEQ             r4, #2048
;                BEQ             ReadData1
;                MOV             r0, #1 << 31
;                ADDS            r0, r0, r0
;                ADR             r0, MustBe2048Msg
;                MOV             pc, lr
;---

ReadData1       SAVE            "r0-r5"
                BL              CheckDisc
                BVS             ReadDataX

                SWI             XCD_ConvertToLBA
                BVS             ReadDataX

; Work out how long this should take

                BL              EmulationCalc

                MUL             r3, r2, r4            ;bytes to read
                LDR             r2, [sp, #word * 3]
                MOV             r0, #3
                LDR             r4, [r10, #Drive_ImageType]
                TEQ             r4, #1                ;is it a BINARY?
                BEQ             BINimage              ;YES
                                                      ;NO, assume ISO
                MOV             r4, r1, LSL #11
                LDR             r1, [r10, #Drive_Handle]

                DBF             "OS_GBPB %0w, %1w, %2w, %3w, %4w\n"

                SWI             XOS_GBPB
                BVS             ReadDataX

                MOV             r4, r4, LSR #11
                BL              CheckSharedSound
                STREQ           r4, [r10, #Drive_LastPosn]

; Now wait until the current time is >= R5

ReadData2       SWI             XOS_ReadMonotonicTime
                CMP             r0, r5
                BLT             ReadData2

ReadDataX       RETURN_ERR

                ;      header / data size / framesize
Type_Details    DCD        0,       2352,       2352, 0   ;AUDIO
                DCD        0,       2448,       2352, 0   ;CDG
                DCD     12+4,       2048,       2352, 0   ;MODE1/2048
                DCD     12+4,       2048,       2352, 0   ;MODE1/2352
                DCD   12+4+8,       2324,       2352, 0   ;MODE2/2336 Form 2
                DCD   12+4+8,       2048,       2352, 0   ;MODE2/2352 Form 1
                DCD   12+4+8,       2324,       2352, 0   ;CDI/2336
                DCD   12+4+8,       2048,       2352, 0   ;CDI/2352

;Entry:
; r5 = TrackInfo_DataType
;
;Exit:
; r5 = Offset in sector to data
; r7 = Framesize
; r8 = Data size

CDSectorInfo
                ADR             r7, Type_Details - 16
                LDR             r5, [r7, r5, LSL #4]! ;r5=offset in sector
                LDR             r8, [r7, #4]          ;r8=data size
                LDR             r6, [r7, #8]          ;r6=framesize
                MOV             pc, lr

;Entry:
; r0 = Frame
; r5 = TrackInfo_DataType
;
;Exit:
; r0 = Address
ConvertLBAToBytes
                SAVE            "r5-r8"
                BL              CDSectorInfo
                MUL             r0, r6, r0
                RETURN

;Entry:
; r1 = sector
; r2 = memory address
; r3 = bytes to read

BINimage
                STMFD           sp!, {r4-r9}
                MOV             r9, r1               ;r9=sector#
                ADD             r4, r10, #Drive_Tracks
                LDR             r5, [r4, #TrackInfo_DataType]
                BL              CDSectorInfo
                LDR             r1, [r10, #Drive_Handle]
                LDR             r7, [r4, #TrackInfo_Offset]
                MLA             r5, r7, r6, r5       ;r5=offset to data
                MOV             r7, r3               ;r7=data remaining
BINimage_L1     MLA             r4, r9, r6, r5       ;r4=sector address

                CMP             r7, r8               ; cap to 1 sector
                MOVHI           r3, r8
                MOVLS           r3, r7

                DBF             "OS_GBPB %0w, %1w, %2w, %3w, %4w\n"

                SWI             XOS_GBPB
                BVS             BINReadDataX

                SUBS            r7, r7, r8
                ADD             r9, r9, #1
                BGT             BINimage_L1

                BL              CheckSharedSound
                STREQ           r9, [r10, #Drive_LastPosn]
                LDMFD           sp!, {r4-r9}
                B               ReadData2

BINReadDataX
                LDMFD           sp!, {r4-r9}
                B               ReadDataX


;MustBe2048Msg   MKERR           MustBe2048

; Work out how long the specified transfer should take
;
; R1  ==> Block address to read from
; R2  ==> Number of blocks
;
; On exit
;
; R5  ==> End time for this transfer

EmulationCalc   SAVE            "r0-r4, r6-r8"
                SWI             XOS_ReadMonotonicTime
                
                LDR             r3, [r10, #Drive_Emulation]
                TEQ             r3, #0
                MOVEQ           r5, r0
                
                BEQ             EmulationCalc3

; Calculate delay for distance

                LDR             r4, [r10, #Drive_LastPosn]
                SUBS            r1, r1, r4
                RSBMI           r1, r1, #0

                DBF             "Seek distance is %1#w blocks\n"

                ADD             r4, r3, #Emulation_Size
                LDR             r5, [r3, #Emulation_Used]
                MOV             r8, #0        ; default time

EmulationCalc1  SUBS            r5, r5, #1
                BMI             EmulationCalc2
                LDMIA           r4!, {r6, r7}  ; length, time taken
                CMP             r1, r6
                MOVGT           r8, r7
                BGT             EmulationCalc1
EmulationCalc2  LDR             r6, [r3, #Emulation_Latency]
                DBF             "Seek time %8#w, latency %6#w\n"
                ADD             r7, r8, r6

; Multiply R2 by 2000 to convert from K per second to blocks per ms

                MOV             r4, #2000
                MUL             r4, r2, r4

                LDR             r2, [r3, #Emulation_Speed]
                DivRem          r5, r4, r2, lr      ; R5 is transfer time in ms    
                ADD             r7, r7, r5          ; R7 is total command time (ms)
                DBF             "Transfer time %5#w\n"
                MOV             r2, #10
                DivRem          r5, r7, r2, lr      ; Now cs
                ADD             r5, r5, r0          ; End time

EmulationCalc3  RETURN

; Seek to the given address. We only bother with this if we're playing audio, in which
; case its affect is identical to PlayAudio with the end address duplicated from the
; most recent call to PlayAudio / PlayTrack
;
; R0  ==> Addressing mode
; R1  ==> Address to seek to

SeekTo          SAVE            "r0-r2"
;                BL              StopSharedSound
                SWI             XCD_ConvertToLBA
                STRVC           r1, [r10, #Drive_LastPosn]
                RETURN_ERR

; Check drive status
;
; R7  ==> Control block
;
; On exit
;
; R0  ==> 1 == OK, 2 == BUSY, 4 == NOTREADY, 8 == UNAVAILABLE

DriveStatus     LDR             r0, [r10, #Drive_Handle]
                TEQ             r0, #0
                MOVEQ           r0, #4
                MOVNE           r0, #1
                MOV             pc, lr

; Check if drive is ready
;
; R7  ==> Control block
;
; On exit
;
; R0  ==> 0 if ok, else 1

DriveReady      LDR             r0, [r10, #Drive_Handle]
                TEQ             r0, #0
                MOVEQ           r0, #1
                MOVNE           r0, #0
                MOV             pc, lr

; Read the current parameters for the drive
;
; R0  ==> Pointer to block to receive information
; R7  ==> Control block
;
; On exit
;
; R0  ==> Filled with
;            0000   Inactivity timer multiplier
;            0004   Retry count
;            0008   Data mode (0, 1 or 2 I suppose)
;            0012   Speed selector

GetParameters   SAVE            "r0-r4"
                ADR             r1, FakeBlock
                LDMIA           r1, {r1-r4}
                STMIA           r0, {r1-r4}
                RETURN

FakeBlock       &               1, 3, 1, 1

; Set the parameters for the drive
;
; R0  ==> Pointer to block as above
;            0000   Inactivity timer multiplier
;            0004   Retry count
;            0008   Data mode (0, 1 or 2 I suppose)
;            0012   Speed selector
; R7  ==> Control block

SetParameters   MOV             pc, lr

; Spin the disc down and open the drawer
;
; R7  ==> Control block

OpenDrawer      SAVE            "r0"
                LDR             r0, [r10, #Drive_Flags]
                TST             r0, #Drive_Locked
                BLEQ            StopSharedSound
                TST             r0, #Drive_Locked
                ORREQ           r0, r0, #Drive_DiscChanged
                STREQ           r0, [r10, #Drive_Flags]
                MOVEQ           r0, #0                    ;clear V
                MOVNE           r0, #1 << 31              ;set V
                ADDS            r0, r0, r0
                ADRVS           r0, LockedMsg
                RESTORE
                MOV             pc, lr

LockedMsg       MKERR           Locked

; Enable or disable the eject button
;
; R0  ==> 0 to enable, 1 to disable
; R7  ==> Control block

EjectButton     SAVE            "r1"
                LDR             r1, [r10, #Drive_Flags]
                TEQ             r0, #0
                ORRNE           r1, r1, #Drive_Locked
                BICEQ           r1, r1, #Drive_Locked
                STR             r1, [r10, #Drive_Flags]
                RETURN

; Return the current head position
;
; R0  ==> Addressing mode to return data in
; R7  ==> Control block
;
; On exit
;
; R0  ==> Current address

EnquireAddress  SAVE            "r1"
                LDR             r1, [r10, #Drive_LastPosn]
                TEQ             r0, #1
                MOVEQ           r0, #0
                SWIEQ           XCD_ConvertToMSF
                MOV             r0, r1
                RETURN

; Find out what the data mode for the given block is
;
; R0  ==> Addressing mode for block
; R1  ==> Block address
; R7  ==> Control block
;
; On exit
;
; R0  ==> Addressing mode (1 == mode 1, 2 == mode 2 form 1, 3 == mode 2 form 2)

EnquireDataMode
                SAVE            "r1-r5"
                LDR             r3, [r10, #Drive_ImageType]
                TEQ             r3, #0                       ;ISO?
                MOVEQ           r0, #1                       ;YES
                RETURN          EQ

                SWI             XCD_ConvertToLBA
                MOVVS           pc, lr
                ADD             r2, r10, #Drive_Tracks+(TrackInfo_Size*100)
                MOV             r3, #100                     ;track#
EnquireDataMode_L1
                SUBS            r3, r3, #1
                MOVMI           r0, #1                       ;assume MODE1
                BMI             EnquireDataModeUnknown
                SUB             r2, r2, #TrackInfo_Size
                LDR             r0, [r2, #TrackInfo_Offset]
                LDR             r5, [r2, #TrackInfo_DataType]
                TEQ             r5, #0                       ;track exist?
                BEQ             EnquireDataMode_L1           ;NO
                CMP             r0, r1                       ;found track?
                BHI             EnquireDataMode_L1           ;NO

                ADR             r0, EnquireDateModeTable-1
                LDRB            r0, [r0, r5]

EnquireDataModeUnknown
                RETURN

EnquireDateModeTable
                DCB 0, 1, 1, 1, 3, 2, 3, 2


; Return information about the tracks on a CD, or about an individual track
;
; R0  ==> 0 to return track range or track number to return information
;         about a particular track
; R1  ==> 5 byte return block
; R7  ==> Control block
;
; On exit
;
; R1  ==> 5 byte block filled in like this (R0 == 0)
;            0000   First track on CD (1-99)
;            0001   Last track on CD (1-99)
;            0002   Corrupted
;            0003   Corrupted
;            0004   Corrupted
;         (R0 == track number)
;            0000   Start block
;            0004   Control byte

EnquireTrack
                SAVE            "r2-r5"
                BL              CheckDisc
                RETURN_IF_ERR

                LDR             r2, [r10, #Drive_ImageType]
                TEQ             r2, #0                       ;ISO?
                BNE             EnquireTrackBIN

                TEQ             r0, #0
                MOVEQ           r0, #1
                STREQB          r0, [r1, #0]
                STREQB          r0, [r1, #1]

                MOVNE           r0, #0
                STRNE           r0, [r1, #0]
                MOVNE           r0, #1
                STRNEB          r0, [r1, #4]

                RETURN

EnquireTrackBIN TEQ             r0, #0                 ;range?
                BNE             EnquireTrackBIN_Track

                MOV             r3, #1
                STRB            r3, [r1, #0]           ;first track
                BL              FindLastTrack
                STRB            r3, [r1, #1]           ;last track
                RETURN

EnquireTrackBIN_Track
                ADD             r2, r10, #Drive_Tracks
                SUB             r0, r0, #1             ;correct Track #
                ADD             r2, r2, r0, LSL #4     ;track details
                LDR             r0, [r2, #TrackInfo_Offset]
                LDR             r5, [r2, #TrackInfo_DataType]
                STR             r0, [r1, #0]
                TEQ             r5, #Type_AUDIO
                MOVEQ           r0, #2                 ;stereo audio
                MOVNE           r0, #1                 ;data
                STR             r0, [r1, #4]
                RETURN


; R1 = SubChannel block
;        +0 = LBA address of current head position relative to the track
;        +4 = Absolute LBA address
;        +8 = Audio flags
;              Bit 0: 0=Audio, 1=Data
;              Bit 1: 0=4 channels, 1=2 channels
;        +9 = Track number
;        +10= Index number
; R7 = Control block

ReadSubChannel  SAVE            "r0-r6"
                BL              CheckDisc
                RETURN_IF_ERR

                LDR             r6, [r10, #Drive_LastPosn]
                STR             r6, [r1, #4]                 ;head position
                MOV             r4, #1
                STRB            r4, [r1, #10]                ;INDEX 1

                LDR             r2, [r10, #Drive_ImageType]
                TEQ             r2, #0                       ;ISO?
                BNE             ReadSubChannelBIN            ;NO
ReadSubChannelBIN_Unknown
                STR             r6, [r1, #0]                 ;return head LBA
                STRB            r4, [r1, #8]                 ;DATA
                STRB            r4, [r1, #9]                 ;TRACK 1
                RETURN

ReadSubChannelBIN
                ADD             r2, r10, #Drive_Tracks+(TrackInfo_Size*100)
                MOV             r3, #100                     ;track#
ReadSubChannelBIN_L1
                SUBS            r3, r3, #1
                BMI             ReadSubChannelBIN_Unknown    ;oops
                SUB             r2, r2, #TrackInfo_Size
                LDR             r4, [r2, #TrackInfo_Offset]
                LDR             r5, [r2, #TrackInfo_DataType]
                TEQ             r5, #0                       ;track exist?
                BEQ             ReadSubChannelBIN_L1         ;NO
                CMP             r4, r6                       ;found track?
                BHI             ReadSubChannelBIN_L1         ;NO

                SUB             r6, r6, r4
                STR             r6, [r1, #0]                 ;head LBA offset relative to track start
                TEQ             r5, #Type_AUDIO              ;AUDIO?
                MOVEQ           r4, #2                       ;YES
                MOVNE           r4, #1                       ;NO
                STRB            r4, [r1, #8]
                ADD             r3, r3, #1                   ;correct Track #
                STRB            r3, [r1, #9]                 ;Track #
                RETURN



; Read drive status bits
;
; R7  ==> Control block
;
; On exit
;
; R0  ==> Drive status bits? Could be same as SCSI_Status which is
;
;             1 => Idle
;             2 => Busy
;             4 => Error
;            16 => not available

CheckDrive
                LDR             r0, [r10, #Drive_SharedSoundHandle]
                TEQ             r0, #0                   ;audio playing?
                MOVEQ           r0, #2                   ;Busy
                MOVNE           r0, #1                   ;Idle
                MOV             pc, lr

; Find out whether the disc has changed
;
; R7  ==> Control block
;
; On exit
;
; R0  ==> 0 if not changed, else 1

DiscChanged     SAVE
                LDR             r0, [r10, #Drive_Flags]
                TST             r0, #Drive_DiscChanged
                BIC             r0, r0, #Drive_DiscChanged
                STR             r0, [r10, #Drive_Flags]
                MOVNE           r0, #1
                MOVEQ           r0, #0
                DBF             "DiscChanged returning %0w\n"
DiscChangedX    RETURN_ERR

StopDisc        SAVE            "r0-r2"

                BL              StopSharedSound
                SUBS            r0, r0, r0            ;clear V
                RETURN


; Find out how much of the disc is used
;
; R0  ==> Addressing mode for stored data
; R1  ==> Place to store
;            0000   Size of CD in format indicated by R0
;            0004   Size of blocks in bytes

DiscUsed        SAVEs           "r0-r2"

                BL              CheckDisc
                BVS             DiscUsedX

                MOV             r0, #2
                LDR             r1, [r10, #Drive_Handle]
                SWI             XOS_Args
                BVS             DiscUsedX

                LDR             r0, [sp, #word * 0]
                MOV             r1, r2, LSR #11
                ADD             r1, r1, #6
                TEQ             r0, #1
                MOVEQ           r0, #0
                SWIEQ           XCD_ConvertToMSF
                BVS             DiscUsedX

                LDR             r2, [sp, #word * 1]
                STR             r1, [r2, #0]
                MOV             r1, #2048
                STR             r1, [r2, #4]

DiscUsedX       RETURN_ERR

; Simulate a SCSI inquiry
;
; R0  ==> Address for Inquiry data

Inquiry         SAVE            "r0-r3"
                ADR             r1, InqData
                MOV             r2, #36
Inquiry1        LDR             r3, [r1], #4
                STR             r3, [r0], #4
                SUBS            r2, r2, #4
                BNE             Inquiry1
                RETURN

InqData         =               &05, &80, &02, &02, &1F, &00, &00, &00
                =               "AW      "      ; 8
                =               "CDFaker         "
                =               "0" + (ThisVersion / 100) :MOD: 10, "."
                =               "0" + (ThisVersion / 10) :MOD: 10
                =               "0" + ThisVersion :MOD: 10

; Pretend the disc has changed

DiscHasChanged  SAVE            "r0"
                LDR             r0, [r10, #Drive_Flags]
                ORR             r0, r0, #Drive_DiscChanged
                STR             r0, [r10, #Drive_Flags]
                RETURN


; Returns a bitfield of the things this driver supports
; Currently only CD_ReadAudio, CD_GetAudioParams and CD_SeekAudioParams

Supported       MOV             r0, #&C000
                MOV             pc, lr

; Does a SCSI_Control for this drive. We're not interested but its not an
; error

Control

; Prefetch a block. Not supported

Prefetch

; Reset the drive and set the disc changed flag

Reset
CloseDrawer     MOV             pc, lr

; R0 ==> 0 not locked, 1 locked

IsDrawerLocked  LDR             r0, [r10, #Drive_Flags]
                ANDS            r0, r0, #Drive_Locked
                MOVNE           r0, #1
                MOV             pc, lr


; restrict which SCSI ID's we can use based on CDFaker$SCSIID

CheckSCSIID     SAVE            "r0-r6"
; SWI &100 + "+"
                ADR             r0, SCSIID_Env          ;read CDFaker$SCSIID
                MOV             r2, #1 << 31            ;find size of variable
                MOV             r3, #0
                MOV             r4, #0
                SWI             XOS_ReadVarVal          ;PRM1-314
                TEQ             r2, #0                  ;does it exist?
                BEQ             CheckCARD3              ;NO

                MVN             r0, #0
                EOR             r6, r2, r0              ;r6=length

                MOV             r0, #6
                MOV             r3, r6
                SWI             XOS_Module              ;claim some space
                BVS             CheckCARD3              ;failed, fallback to card 3
                MOV             r5, r2                  ;r5=address allocated

                ADR             r0, SCSIID_Env          ;read CDFaker$SCSIID
                MOV             r1, r5                  ;address
                MOV             r2, r6                  ;size
                MOV             r3, #0
                MOV             r4, #0
                SWI             XOS_ReadVarVal          ;PRM1-314
                BVS             Release_CheckCARD3

                MOV             r4, r5
                ADD             lr, r5, r6              ;end of string
CheckID_L1      CMP             r4, lr
                BHS             CheckSCSIID_Release
                LDRB            r0, [r4], #2
                SUB             r0, r0, #"0"            ;r0=ID
                LDRB            r1, [r4], #2
                SUB             r1, r1, #"0"            ;r1=CARD
                LDRB            r2, [r4], #2
                SUB             r2, r2, #"0"            ;r2=LUN

CheckID_L2      LDRB            r3, [r4]                ;skip spaces
                TEQ             r3, #" "
                ADDEQ           r4, r4, #1
                BEQ             CheckID_L2

                TEQ             r0, #"?"-"0"            ;ID wildcarded?
                LDR             r3, [r7, #Drive_ID]
                MOVEQ           r0, r3                  ;YES
                TEQ             r0, r3                  ;match?
                BNE             CheckID_L1              ;NO

                TEQ             r1, #"?"-"0"            ;Card wildcarded?
                LDR             r3, [r7, #Drive_Card]
                MOVEQ           r1, r3                  ;YES
                TEQ             r1, r3                  ;match?
                BNE             CheckID_L1              ;NO

                TEQ             r2, #"?"-"0"            ;LUN wildcarded?
                LDR             r3, [r7, #Drive_LUN]
                MOVEQ           r2, r3                  ;YES
                TEQ             r2, r3                  ;match?
                BNE             CheckID_L1              ;NO

                MOV             r0, #7
                MOV             r2, r5
                SWI             XOS_Module              ;release block
                TEQ             r0, r0                  ;return EQ
                RETURN                                  ;YES

CheckSCSIID_Release
                MOV             r0, #7
                MOV             r2, r5
                SWI             XOS_Module              ;release block
                CMP             r0, r13                 ;return NE
                RETURN

Release_CheckCARD3
                MOV             r0, #7
                MOV             r2, r5
                SWI             XOS_Module              ;release block

CheckCARD3      LDR             r0, [r7, #Drive_Card]
                TEQ             r0, #3                  ;only use card 3
                RETURN

SCSIID_Env      DCB "CDFaker$SCSIID"


; Identify entry point allows us to insert ourself for an unknown drive type
;
; R0  ==> 36 bytes of SCSI Inquiry data or 0
; R1  ==> If R0 == 0 then this is an error block and we can use this ID
; R7  ==> Control block
;
; On exit
;
; R2  ==> -1 if not recognised (i.e. its a SCSI device or we don't have any
;         more to do), otherwise our internal drive type.

Identify        SAVE            "r0-r1, r3-r5"

                DBF             "Identify %0w, %1w, %7w, %aw\n"

                LDR             lr, Flags
                ORR             lr, lr, #CDFSSeen
                STR             lr, Flags

                CMP             r10, #0
                MOVNE           r2, #0
                BNE             IdentifyX

                TEQ             r0, #0
                MOVNE           r2, #-1
                BNE             IdentifyX

                LDR             r0, Drives
                DBF             "%0w drives left to allocate\n"
                CMP             r0, #0
                MOVLE           r2, #-1
                BLE             IdentifyX

                BL              CheckSCSIID         ;is this is an ID we can use?
                MOVNE           r2, #-1             ;NO
                RETURN          NE                  ; |+ exit
 
                ADR             r2, DriveBlock
                MOV             r3, #MaxDrive

Identify1       LDR             r4, [r2, #Drive_ID]
                CMP             r4, #-1
                BEQ             Identify2
                ADD             r2, r2, #Drive_Size :AND: &00FF
                ADD             r2, r2, #Drive_Size :AND: &FF00
                SUBS            r3, r3, #1
                BNE             Identify1
                DBF             "Can't find available drive\n"
                MOV             r2, #-1
                B               IdentifyX

Identify2       DBF             "Allocating drive at %2w\n"
                LDMIA           r7, {r3, r4, r5}
                STMIA           r2, {r3, r4, r5}
                DBF             "%3w, %4w, %5w\n"

                SUB             r0, r0, #1
                STR             r0, Drives

; Say its a type 0 drive
                MOV             r2, #0
IdentifyX       RETURN

CheckDisc       SAVE            "r0"
                LDR             r0, [r10, #Drive_Handle]
                TEQ             r0, #0
                MOVNE           r0, #0                    ;clear V
                MOVEQ           r0, #1 << 31              ;set V
                ADDS            r0, r0, r0
                RESTORE
                ADRVS           r0, DriveEmptyMsg
                MOV             pc, lr

DriveEmptyMsg   MKERR           DriveEmpty


;Entry:
; r0 = Addressing mode
; r1 = Start address
; r2 = length of section
; r3 = Pointer to destination buffer
; r7 = CDFS Control Block

ReadAudio
                SAVE            "r1-r9"
                BL              CheckDisc
                RETURN_IF_ERR

                LDR             r4, [r10, #Drive_ImageType]
                TEQ             r4, #0                       ;ISO?
                RETURN          EQ                           ;YES, exit

                SWI             XCD_ConvertToLBA
                RETURN_IF_ERR
                ADD             r4, r10, #Drive_Tracks+(TrackInfo_Size*100)
                MOV             r6, #100                     ;track#
ReadAudio_L1
                SUBS            r6, r6, #1                   ;last track
                RETURN          MI                           ;YES, exit
                SUB             r4, r4, #TrackInfo_Size
                LDR             r5, [r4, #TrackInfo_DataType]
                TEQ             r5, #0                       ;track exist?
                BEQ             ReadAudio_L1                 ;NO
                LDR             r0, [r4, #TrackInfo_Offset]
                CMP             r0, r1                       ;found track?
                BHI             ReadAudio_L1                 ;NO

                TEQ             r5, #Type_AUDIO              ;audio track?
                RETURN          NE                           ;NO

                ADD             r4, r1, r2                   ;update head pos
                BL              CheckSharedSound
                STREQ           r4, [r10, #Drive_LastPosn]

                MOV             r0, r1
                BL              ConvertLBAToBytes
                MOV             r4, r0                       ;r4=address

                MOV             r0, r2
                BL              ConvertLBAToBytes            ;r0=size

                LDR             r1, [r10, #Drive_Handle]
                MOV             r2, r3               ;address
                MOV             r3, r0               ;size
                MOV             r0, #3
                DBF             "OS_GBPB %0w, %1w, %2w, %3w, %4w\n"

                SWI             XOS_GBPB
                RETURN


;Entry:
; R0    = reason
; R1-R7 = dependent on reason
;
; Reason
; 0 - Volume
;      R1 pointer to two words to store left/right volume level (0-&ffff)

GetAudioParams  SAVE           "r0-r6"
                TEQ            r0, #0
                RETURN         NE

                LDR            r0, [r10, #Drive_SharedSoundHandle]
                TEQ            r0, #0
                MOV            r6, r1
                MVNEQ          r5, #0           ;Audio not started
                SWINE          XSharedSound_HandlerInfo

                MOV            r2, r5, LSR #16
                EOR            r1, r5, r2, LSL #16
                STR            r1, [r6, #0]     ;left volume
                STR            r2, [r6, #4]     ;right volume
                RETURN


;Entry:
; R0    = reason
; R1-R7 = dependent on reason
;
; Reason
; 0 - Volume
;      R1 pointer to two words to store left/right volume level (0-&ffff)

SetAudioParams  SAVE           "r0-r2"
                TEQ            r0, #0
                RETURN         NE

                LDR            r0, [r10, #Drive_SharedSoundHandle]
                TEQ            r0, #0
                RETURN         EQ               ;Audio not started

                LDR            r2, [r1, #0]     ;left volume
                LDR            r1, [r1, #4]     ;right volume
                ORR            r1, r2, r1, LSL #16
                SWI            XSharedSound_HandlerVolume
                RETURN

; No documentation currently for these

ReadUserData
SeekUserData
AudioControl

; This SWI appears to be INTERNAL only and cannot be claimed

AudioLevel      MOV             pc, lr


; R0  ==> Addressing Mode
; R1  ==> Start address of section (-150 / -00:02:00)
; R2  ==> End address of section (-150 / -00:02:00)
; R7  ==> Control block

PlayAudio       SAVE            "r0-r3"
                BL              CheckDisc
                RETURN_IF_ERR

                LDR             r3, [r10, #Drive_ImageType]
                TEQ             r3, #0                       ;ISO?
                RETURN          EQ                           ;YES, exit

                SWI             XCD_ConvertToLBA
                RETURN_IF_ERR
;                ADD             r3, r1, #150                 ;r3=start
                MOV             r3, r1                       ;r3=start
                MOV             r1, r2
                SWI             XCD_ConvertToLBA
;                ADD             r1, r1, #150
                MOV             r0, r3                       ;r0=start, r1=end
                BL              StartSharedSound
                STRVS           r0, [sp]                     ;return error
                RETURN_ERR


;Exit:
; R3 = last track number

FindLastTrack   SAVE            "r2,r4-r5"
                MOV             r3, #0
                ADD             r2, r10, #Drive_Tracks  ;first track info
FindLastTrack_L1
                LDR             r5, [r2, #TrackInfo_DataType]
                TEQ             r5, #0                  ;track exist?
                RETURN          EQ                      ;no
                ADD             r2, r2, #TrackInfo_Size
                ADD             r3, r3, #1
                CMP             r3, #100
                BNE             FindLastTrack_L1
                RETURN


; R0  ==> Track to play
; R1  ==> Track to finish at, or 255 to play all, or 254 just this track
; R7  ==> Control block

PlayTrack       CMP             r0, #100
                MOVHI           pc, lr                   ;validate start track

                SAVE            "r0-r5"
                BL              CheckDisc
                RETURN_IF_ERR

                TEQ             r1, #254                 ;one track?
                MOVEQ           r1, r0                   ;YES
                SUBNE           r1, r1, #1               ;r1=end track#
                SUB             r2, r0, #1               ;r2=start track#

                BL              FindLastTrack
                TEQ             r1, #255-1               ;play all tracks?
                MOVEQ           r1, r3                   ;YES
                CMP             r1, #99
                RETURN          HI                       ;validate end track

                ADD             r4, r10, #Drive_Tracks
                ADD             r2, r4, r2, LSL #4       ;r2=start track ptr
                ADD             r1, r4, r1, LSL #4       ;r1=end track ptr

                LDR             r0, [r2, #TrackInfo_Offset]
                TEQ             r0, #0                   ;does track exist?
                RETURN          EQ                       ;NO
                LDR             r1, [r1, #TrackInfo_Offset]
                TEQ             r1, #0                   ;does track exist?
                MOVEQ           r1, #&FF000              ;NO play to end
                BL              StartSharedSound

                LDR             r1, [r10, #Drive_Flags]
                TEQ             r0, #0
                BICEQ           r1, r1, #Drive_AudioPause
                TEQ             r0, #1
                ORREQ           r1, r1, #Drive_AudioPause
                STR             r1, [r10, #Drive_Flags]
                STRVS           r0, [sp]                 ;return error
                RETURN_ERR


; R0  ==> 0 off, 1 on
; R7  ==> Control block

AudioPause      SAVE            "r1"
                LDR             r1, [r10, #Drive_Flags]
                TEQ             r0, #0
                BICEQ           r1, r1, #Drive_AudioPause
                TEQ             r0, #1
                ORREQ           r1, r1, #Drive_AudioPause
                STR             r1, [r10, #Drive_Flags]
                RETURN


; Returns the current audio status of the drive
;
; R7  ==> Control block
; 
; On exit
; 
; R0  ==> Audio status:
;            0       Currently playing audio
;            1       Currently paused
;            2       Reserved (has no meaning at present)
;            3       Audio play successfully completed
;            4       Error occurred during last audio play
;            5       Audio play has not been requested     

AudioStatus     SAVE            "r1"
                LDR             r1, [r10, #Drive_SharedSoundHandle]
                TEQ             r1, #0                   ;audio playing?
                MOVEQ           r0, #5                   ;No playing audio
                RETURN          EQ

                LDR             r1, [r10, #Drive_Flags]
                TST             r1, #Drive_AudioPause    ;audio paused?
                MOVEQ           r0, #0                   ;Playing audio
                MOVNE           r0, #1                   ;Audio paused
                RETURN


;Entry:
; R0 = Start offset
; R1 = End offset

CDAudioDAname   DCB "CD-Audio Buffer", 0
                ALIGN

StartSharedSound
                SAVE            "r0-r8"
                STR             r0, [r10, #Drive_LastPosn]
                STR             r1, [r10, #Drive_AudioEnd]
                MOV             r3, #0                   ;allow callbacks
                STR             r3, [r10, #Drive_AudioCallBack]
                STR             r3, [r10, #Drive_AudioPlayed]
                LDR             r3, AudioBytesToRead
                STR             r3, [r10, #Drive_AudioReadAhead]

                LDR             r2, [r10, #Drive_BufferStart]
                TEQ             r2, #0                   ;buffer allocated?
                BNE             BufferAlreadyAllocated
                MOV             r0, #0                   ;Create DA PRM5a-55
                MOV             r1, #-1                  ;area number
                LDR             r2, AudioBufferSize      ;initial size
                MOV             r3, #-1                  ;logical base of area
;                                      +-------- requires specific physical pages
;                                      |+------- area can't be dragged in task bar
;                                      ||+------ area is doubly mapped
;                                      |||+----- area is not cacheable
;                                      ||||+---- area is not bufferable
;                                      |||||++++ access privileges (2=supervisor only)
                MOV             r4, #2_010000000
                MOV             r5, r2                   ;max area size
                MOV             r6, #0                   ;DA handler pointer
                MOV             r7, #0                   ;workspace pointer
                ADR             r8, CDAudioDAname
                SWI             XOS_DynamicArea          ;PRM5a-55
                MOVVC           r2, r3
                STRVC           r1, [r10, #Drive_DAnumber]
; Fallback to RMA space if DA fails
                MOVVS           r0, #6
                LDRVS           r3, AudioBufferSize
                SWIVS           XOS_Module               ;claim buffer
                STRVS           r0, [sp]                 ;return error
                RETURN_IF_ERR
                STR             r2, [r10, #Drive_BufferStart]
BufferAlreadyAllocated
                STR             r2, [r10, #Drive_BufferPtr]

                LDMIA           sp, {r0, r1}             ;get start/end
                BL              PreCacheAudio

                LDR             r3, [r10, #Drive_SharedSoundHandle]
                TEQ             r3, #0                   ;audio playing?
                BNE             StartSharedSoundExit     ;YES

                ADR             r0, Audio_Fill
                MOV             r1, r10                  ;parameter
                MOV             r2, #1                   ;type in R4
                ADR             r3, Audio_Fill_Title
                MOV             r4, #0                   ;immediate handler
                SWI             XSharedSound_InstallHandler
                BVS             StartSharedSoundError
                STR             r0, [r10, #Drive_SharedSoundHandle]
                     
                MOV             r1,     #(44100 :AND: &00FF)*1024
                ORR             r1, r1, #(44100 :AND: &FF00)*1024
                SWI             XSharedSound_SampleRate

StartSharedSoundExit
                LDR             r1, [r10, #Drive_Flags]
                BIC             r1, r1, #Drive_AudioPause
                STR             r1, [r10, #Drive_Flags]

                SUBS            r0, r0, r0               ;clear V
                RETURN

StartSharedSoundError
                BL              ReleaseAudioBuffer
                B               StartSharedSoundExit


;Entry:
; R0 = Start offset
; R1 = End offset

PreCacheAudio   SAVE            "r2-r7"
                LDR             r6, AudioBufferSizeInLBA
                LDR             r2, [r10, #Drive_BufferStart]
                ADD             r6, r0, r6                 ;r6=updated head
                CMP             r6, r1                     ;past the end?
                MOVHI           r6, r1                     ;YES
                STR             r6, [r10, #Drive_LastPosn] ;update head position
                SUB             r7, r6, r0                 ; |+ r7=LBA to cache

                MOV             r5, #Type_AUDIO
                BL              ConvertLBAToBytes
                MOV             r4, r0                     ;file offset to read from

                MOV             r0, r7
                BL              ConvertLBAToBytes
                MOV             r3, r0                     ;bytes to read

                CMP             r6, r1                     ;past the end?
                ADDHS           r0, r2, r3                 ;YES
                MOVLO           r0, #0                     ;NO
                STR             r0, [r10, #Drive_TerminateAudio]
                LDR             r1, [r10, #Drive_Handle]
                MOV             r0, #3
                DBF             "OS_GBPB %0w, %1w, %2w, %3w, %4w\n"

                SWI             XOS_GBPB
                STR             r2, [r10, #Drive_BufferFilled]

                RETURN


StopAllAudio    SAVE            "r4, r10"
                ADR             r10, DriveBlock
                MOV             r4, #MaxDrive

StopAllAudio1   BL              StopSharedSound
                BL              ReleaseAudioBuffer

                ADD             r10, r10, #Drive_Size :AND: &00FF
                ADD             r10, r10, #Drive_Size :AND: &FF00
                SUBS            r4, r4, #1
                BNE             StopAllAudio1

                RETURN


ReleaseAudioBuffer
                SAVE            "r0-r2"
                LDR             r1, [r10, #Drive_DAnumber]
                TEQ             r1, #0
                MOVNE           r0, #1
                SWINE           XOS_DynamicArea          ;release DA
                TEQ             r1, #0                   ;using a DA?
                MOV             r1, #0
                STRNE           r1, [r10, #Drive_DAnumber]
                STRNE           r1, [r10, #Drive_BufferStart]
                RETURN          NE                       ;YES
                                                         ;NO
                LDR             r2, [r10, #Drive_BufferStart]
                TEQ             r2, #0                   ;RMA allocation
                RETURN          EQ                       ;NO, exit
                MOV             r0, #7
                SWI             XOS_Module               ;free buffer
                STR             r1, [r10, #Drive_BufferStart]
                SUBS            r0, r0, r0               ;clear V
                RETURN


StopSharedSound
                SAVE            "r0-r2"
                LDR             r0, [r10, #Drive_SharedSoundHandle]
                TEQ             r0, #0                   ;audio playing?
                RETURN          EQ                       ;NO

                SWI             XSharedSound_RemoveHandler
                MOV             r0, #0
                STR             r0, [r10, #Drive_SharedSoundHandle]

                ADR             r0, BufferMoreAudio
                MOV             r1, r10
                SWI             XOS_RemoveTickerEvent

                RETURN

Audio_Fill_Title = "CDFaker", 0


AudioBufferSizeInLBA DCD           156                   ;2 sec of audio
AudioBufferSize      DCD           156*2352
AudioLBAsToRead      DCD           26                    ;LBA's to read
AudioBytesToRead     DCD           26*2352               ;~64k (61152 bytes)



CheckSharedSound
                SAVE            "r0"
                LDR             r0, [r10, #Drive_SharedSoundHandle]
                TEQ             r0, #0                   ;audio playing?
                RETURN

;Entry:
; R0 = Drive Block pointer
; R1 = Buffer to fill
; R2 = End of buffer
; R3 = Flags
;       Bit  0: Data must be mixed
;       Bit 30: Set if volume in R7<>-1
;       Bit 31: Set if volume in R7=0
; R4 = Sample frequency
; R5 = Sample period
; R6 = Fractional step
; R7 = LR volume
; R8 = Pointer to Helpful fill routines
;       +0 Flags word
;       +4 Silence
;       +8 Data fill

Audio_Fill      SAVE            "r0-r2,r4-r12"

                LDR             r4, [r0, #Drive_BufferPtr]    ;current ptr
                LDR             r10, [r0, #Drive_BufferStart] ;buffer start
                TEQ             r10, #0                       ;buffer exits?
                BEQ             Audio_Fill_P2                 ;NO
                LDR             r12, [r0, #Drive_TerminateAudio]
                TEQ             r4, r12                       ;audio exhausted?
                BEQ             Audio_Fill_P2                 ;YES

                LDR             r5, [r0, #Drive_Flags]
                TST             r5, #Drive_AudioPause         ;paused?
                BNE             Audio_Fill_P2                 ;YES

                LDR             lr, AudioBufferSize
                TEQ             r12, #0                       ;audio exhausted?
                ADDEQ           r5, r10, lr                   ; |  buffer end
                BEQ             Audio_Fill_P1                 ; |+ fill

                MOV             r5, r12                       ;YES
                CMP             r12, r4                       ;are we going to wrap?
                ADDLS           r5, r10, lr                   ;YES
Audio_Fill_P1   MOV             lr, pc                        ;fill buffer
                LDR             pc, [r8, #8]                  
                CMP             r1, r2                        ;did we fill the buffer
                BEQ             Audio_Filled

                CMP             r4, r5                        ;did we hit end of buffer?
                BEQ             Audio_Fill_P2                 ;NO, fill with silence
                LDR             r5, [sp]                      ;YES
                LDR             r4, [r5, #Drive_BufferStart]
                LDR             lr, AudioBufferSize
                ADD             r5, r4, lr                    ;YES
                MOV             lr, pc                        ;fill buffer
                LDR             pc, [r8, #8]
Audio_Fill_P2
                CMP             r1, r2                        ;did we fill the buffer
                MOVNE           lr, pc                        ;NO
                LDRNE           pc, [r8, #4]                  ; |+ fill with zeros
Audio_Filled    MOV             r3, r0

                LDR             r5, [sp]
                LDR             r1, [r5, #Drive_BufferPtr]
                SUBS            r1, r4, r1
                LDRMI           r2, AudioBufferSize
                ADDMI           r1, r1, r2
                LDR             r11, [r5, #Drive_AudioPlayed]
                ADD             r11, r11, r1
                STR             r11, [r5, #Drive_AudioPlayed]
                LDR             lr, [r5, #Drive_AudioReadAhead]
                CMP             r11, lr
                BLHS            NeedMoreAudio

                LDR             r11, [r5, #Drive_BufferStart]
                LDR             lr, AudioBufferSize
                ADD             lr, r11, lr
                TEQ             r4, lr
                STREQ           r11, [r5, #Drive_BufferPtr]
                STRNE           r4, [r5, #Drive_BufferPtr]
                RETURN


NeedMoreAudio   SAVE            "r0-r1,r4-r5"
                LDR             r1, [r5, #Drive_AudioCallBack]
                TEQ             r1, #0                        ;already scheduled?
                RETURN          NE                            ;YES

                MRS             r4, CPSR
                ORR             r0, r4, #3
                MSR             CPSR_cxsf, r0
                MOV             r0, r0
                STR             r14, [r13, #-4]!
                STR             r5, [r5, #Drive_AudioCallBack]
                MOV             r1, r5
                ADR             r0, BufferMoreAudio
                SWI             XOS_AddCallBack
                LDR             r14, [r13], #4
                MSR             CPSR_cxsf, r4
                MOV             r0, r0
                RETURN


BufferMoreAudio SAVE            "r0-r11"
                LDR             r11, [r12, #Drive_AudioPlayed]
                LDR             r1, [r12, #Drive_AudioReadAhead]
                LDR             r2, AudioBytesToRead
                ADD             r1, r1, r2
                STR             r1, [r12, #Drive_AudioReadAhead]

                LDR             r4, [r12, #Drive_BufferStart]
                TEQ             r4, #0                        ;still exist
                RETURN          EQ                            ;NO
                LDR             r4, [r12, #Drive_BufferPtr]
                LDR             r0, [r12, #Drive_TerminateAudio]
                TEQ             r0, r4                        ;music ended?
                BEQ             Audio_Fill_Terminate

                LDR             r2, [r12, #Drive_BufferFilled]
                LDR             r8, [r12, #Drive_BufferStart]
                LDR             r11, AudioBufferSize
                ADD             r11, r8, r11               ;r11=end of buffer
                TEQ             r2, r11                    ;end of buffer
                MOVEQ           r2, r8
                LDR             r4, [r12, #Drive_LastPosn]
                LDR             r9, [r12, #Drive_AudioEnd]
                TEQ             r4, r9                     ;read everything?
                RETURN          EQ                         ;YES
                LDR             r0, AudioLBAsToRead
                ADD             r10, r4, r0
                CMP             r10, r9                    ;hit end yet?
                SUBHI           r0, r9, r4                 ;YES, read remaining

                ADD             r5, r4, r0
                STR             r5, [r12, #Drive_LastPosn] ;update head position
                MOV             r5, #Type_AUDIO
                BL              ConvertLBAToBytes
                MOV             r6, r0                     ;r6=total bytes to read
                MOV             r0, r4                     ;find head position in file
                BL              ConvertLBAToBytes
                MOV             r4, r0                     ;r4=file offset

                TEQ             r2, r11                    ;buffer wrap?
                MOVEQ           r2, r8                     ;YES
                ADD             r7, r2, r6                 ;r7=end address
                CMP             r7, r11                    ;buffer overflow?
                SUBHI           r3, r11, r2                ;YES, read to end of buffer
                MOVLS           r3, r6                     ;NO,  read in one go
                SUB             r6, r6, r3                 ;r6=bytes left to read
                MOV             r0, #3
                LDR             r1, [r12, #Drive_Handle]
                SWI             XOS_GBPB                   ;get first bit
                BVS             TrapGBPBErrors
                MOVS            r3, r6                     ;more to get?
                MOVNE           r2, r8                     ;YES, wrap buffer
                SWINE           XOS_GBPB                   ; |+  and read remaining
TrapGBPBErrors  STR             r2, [r12, #Drive_BufferFilled] ;update buffer ptr

                CMP             r3, #0                     ;EOF?
                CMPEQ           r10, r9                    ;hit end yet?
                STRHS           r2, [r12, #Drive_TerminateAudio] ;YES
                SUBS            r0, r0, r0                 ;allow callbacks
                STR             r0, [r12, #Drive_AudioCallBack]
                RETURN

Audio_Fill_Terminate
                MOV             r10, r12
                BL              StopSharedSound
                RETURN

                END
