; 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
		EXPORT		CloseFile
		EXPORT		OpenFile
		EXPORT		CDSectorInfo

; 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}

                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	SAVE            "r0-r5"
		DBF             "\nReadData %0w, %1w, %2w, %3w, %4w, %9w, %aw\n"

                BL              CheckDisc
                BVS             ReadDataX

                SWI             XCD_ConvertToLBA
                BVS             ReadDataX

                BL              EmulationCalc		; Work out how long this should take

                MUL             r3, r2, R4		;bytes to read
                LDR             r2, [sp, #word * 3]
                MOV             r0, #3
                LDR             r4, [r10, #Drive_ImageType]
                TEQ             r4, #Type_BINARY	;is it a BINARY?
                BEQ             BINimage		;YES
                                        		;NO, assume ISO
                MOV             r4, r1, LSL #11
		LDR		r1, [r10, #Drive_FileName]
                BL		OpenFile
                DBF             "OS_GBPB %0w, %1w, %2w, %3w, %4w\n"
                SWI             XOS_GBPB
		MOVVC		r2, #0
		MOVVS		r2, r0
		BL		CloseFile
		TEQ		r2, #0
		BNE		ReadDataX2

                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	STRVS		r0, [sp]
		RETURN_ERR

ReadDataX2	TEQ		r2, #0
		MOVNE		r0, r2
		MOVNE		r2, #1<<31
		ADDNES		r2, r2, r2
		RETURN_ERR

                ;      header / data size / framesize
Type_Details    DCD        0,       2352,       2352, 0   ;AUDIO
                DCD        0,       2048,       2352, 0   ;CD-Graphics
                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, [r4, #TrackInfo_FileName]	;assume Track 1 is the DATA track
                BL		OpenFile
		BVS		BINReadDataX

                LDR             r7, [r4, #TrackInfo_LocalOffset]
                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		CloseFile
                BL              CheckSharedSound
                STREQ           r9, [r10, #Drive_LastPosn]
                LDMFD           sp!, {r4-r9}
                B               ReadData2

BINReadDataX	MOV		r2, r0
		BL		CloseFile
		LDMFD           sp!, {r4-r9}
                B               ReadDataX2


;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"
                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_AudioTrack]
                TEQ             r0, #0
		MOVNE		r0, #2
		MOVNE		pc, lr

		LDR		r0, [r10, #Drive_Flags]
		TST             r0, #Drive_Mounted
                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_Flags]
		TST             r0, #Drive_Mounted
                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
                RESTORE
                ADRVS           r0, LockedMsg
                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
                RETURN          VS
                ADD             r2, r10, #Drive_Tracks-TrackInfo_Size
                MOV             r3, #MaxTracks			;track#
EnquireDataMode_L1
                ADD             r2, r2, #TrackInfo_Size
                SUBS            r3, r3, #1
                MOVEQ           r0, #1                       ;assume MODE1
                RETURN          MI
                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]
                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
                ADD             r2, r2, r0, LSL #TrackInfo_SHL		     	;track details
                LDR             r0, [r2, #TrackInfo_Offset-TrackInfo_Size]	;Track is +1
                LDR             r5, [r2, #TrackInfo_DataType-TrackInfo_Size]	;Track is +1
                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
		STRVS		r0, [sp]
                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
		MOV		r3, #1                       ;TRACK 1
ReadSubChannel_exit
                STR             r6, [r1, #0]                 ;head LBA offset relative to track start
                STRB            r4, [r1, #8]                 ;track type
                STRB            r3, [r1, #9]                 ;TRACK #
                RETURN

ReadSubChannelBIN
                ADD             r2, r10, #Drive_Tracks-TrackInfo_Size
                MOV             r3, #MaxTracks			;track#
ReadSubChannelBIN_L1
                ADD             r2, r2, #TrackInfo_Size
                SUBS            r3, r3, #1
                RETURN		EQ                           ;oops
                LDR             r4, [r2, #TrackInfo_Offset]
                LDR             r5, [r2, #TrackInfo_DataType]
                TEQ             r5, #0                       ;track exist?
                BEQ             ReadSubChannelBIN_L1         ;NO
                CMP             r4, r6                       ;found track?
                BLO             ReadSubChannelBIN_L1         ;NO
		SUBHI		r2, r2, #TrackInfo_Size
                LDRHI           r4, [r2, #TrackInfo_Offset]
                LDRHI           r5, [r2, #TrackInfo_DataType]

                SUB             r6, r6, r4
                TEQ             r5, #Type_AUDIO              ;AUDIO?
                MOVEQ           r4, #2                       ;YES
                MOVNE           r4, #1                       ;NO
		RSB 		r3, r3, #MaxTracks-1         ;correct Track #
		B		ReadSubChannel_exit


; 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        SAVE           "r0-r5"

                BL              CheckDisc
		BVS		DiscUsedX

		MOV		r5, r1
                LDR		r2, [r10, #Drive_DiscUsed]
                MOV             r1, r2, LSR #11
                ADD             r1, r1, #6
                TEQ             r0, #1
                MOVEQ           r0, #0
                SWIEQ           XCD_ConvertToMSF
                MOVVC		r2, #2048
		STMVCIA		r5, {r1, r2}
DiscUsedX	STRVS		r0, [sp]
		RETURN

; 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"
                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
		TEQEQ		r3, #7			;is this ID 7?
		BEQ		CheckID_L1		;YES, skip as its always the host under RISC OS
                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_Flags]
                TST             r0, #Drive_Mounted
                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             r9, r10, #Drive_Tracks-TrackInfo_Size
                MOV             r6, #MaxTracks			;track#
ReadAudio_L1
                ADD             r9, r9, #TrackInfo_Size
                SUBS            r6, r6, #1			;last track
                RETURN          EQ				;YES, exit
                LDR             r5, [r9, #TrackInfo_DataType]
                TEQ             r5, #0				;track exist?
                BEQ             ReadAudio_L1			;NO
                LDR             r0, [r9, #TrackInfo_Offset]
                CMP             r0, r1				;found track?
                BLO             ReadAudio_L1			;NO
		SUBHI		r9, r9, #TrackInfo_Size		;step back if its mid-track
                LDRHI           r0, [r9, #TrackInfo_Offset]	;get the offset within the CD
                LDRHI           r5, [r9, #TrackInfo_DataType]
                TEQ             r5, #Type_AUDIO			;audio track?
                RETURN          NE				;NO
		LDR		r6, [r9, #TrackInfo_LocalOffset]	;get the offset within this FILE
                ADD             r8, r1, r2			;update head pos
		SUB		r1, r1, r0
		ADD		r1, r1, r6			;correct to offset within the FILE
                BL              CheckSharedSound
                STREQ           r8, [r10, #Drive_LastPosn]

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

                LDR             r1, [r9, #TrackInfo_FileName]
		BL		OpenFile
                MOV             r2, r3               ;address
                MOV             r3, r0               ;size
                MOV             r0, #3
                DBF             "OS_GBPB %0w, %1w, %2w, %3w, %4w\n"
                SWI             XOS_GBPB
		BL		CloseFile
                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
                BVS		PlayAudioX

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

                SWI             XCD_ConvertToLBA
                BVS		PlayAudioX
                MOV             r3, r1                       ;r3=start
                MOV             r1, r2
                SWI             XCD_ConvertToLBA
                MOV             r0, r3                       ;r0=start, r1=end
                BL              StartSharedSound
PlayAudioX	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+TrackInfo_DataType  ;first track info
FindLastTrack_L1
                LDR             r5, [r2], #TrackInfo_Size
		TEQ		r3, #MaxTracks		;last track?
                TEQNE           r5, #0                  ;NO, track exist?
		ADDNE		r3, r3, #1
                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, #MaxTracks
                MOVHI           pc, lr                   ;validate start track

                SAVE            "r0-r5"
                BL              CheckDisc
                BVS		PlayTrackX

                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, r3				;beyond last track?
		MOVHI		r1, r3

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

		LDR		r0, [r1, #TrackInfo_DataType]	;end track type
                TEQ             r0, #0				;does track exist?
                MOVEQ           r1, #&FF000			;NO play to end
                LDRNE           r1, [r1, #TrackInfo_Offset]
		LDR		r0, [r2, #TrackInfo_DataType]	;start track type
                TEQ             r0, #Type_AUDIO			;is it an Audio track?
		RETURN          NE				;NO
		LDR		r0, [r2, #TrackInfo_Offset]	
                BL              StartSharedSound
                BVS             PlayTrackX

                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]
PlayTrackX	STRVS           r0, [sp]                 ;return error
                RETURN_ERR


; R0  ==> 0 unpause, 1 pause
; R10  ==> Control block

AudioPause      SAVE            "r1"
                LDR             r1, [r10, #Drive_Flags]
                TEQ             r0, #0
                BICEQ           r1, r1, #Drive_AudioPause
                ORRNE           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]

                LDR             r3, [r10, #Drive_SharedSoundHandle]
                TEQ             r3, #0                   	;audio playing?
;		RETURN		NE				;YES, exit
		BNE		SharedSoundAlreadyActive

                MOV             r6, #MaxTracks			;track#
		ADD		r2, r10, #Drive_Tracks-TrackInfo_Size
StartSharedSound_L1
		ADD		r2, r2, #TrackInfo_Size
                SUBS            r6, r6, #1			;last track we support?
		LDRNE		r4, [r2, #TrackInfo_DataType]
		TEQNE		r4, #0				;NO, last track on disc?
                RETURN          EQ				;YES, exit
		LDR		r3, [r2, #TrackInfo_Offset]
		CMP		r3, r0
		BLO		StartSharedSound_L1
		SUBHI		r2, r2, #TrackInfo_Size
		LDR		r3, [r2, #TrackInfo_Offset]	;get the offset within the CD
		LDR		r6, [r2, #TrackInfo_LocalOffset]	;get the offset within this FILE
		TEQ		r6, r3				;is this a seperate file?
		MOVEQ		r3, #0				;NO
		STR		r3, [r10, #Drive_AudioPosnCorrect]

		LDR		r1, [r2, #TrackInfo_FileName]
		BL		OpenFile			;open the file
		MOVVS		r2, #0
		MOVVS		r1, #0
		STR		r2, [r10, #Drive_AudioTrackInfo]
		STR		r1, [r10, #Drive_AudioTrack]
                STRVS           r0, [sp]                 ;return error
		RETURN		VS

                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, #129
                MOV             r1, #0
                MOV             r2, #&FF
                SWI             XOS_Byte		;OS version
		CMP             r1, #&A5		;RO 3.5 or newer?
                BLO             FallbackToRMA		;NO, fallback to the RMA

                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]
		BVC             BufferAllocated
FallbackToRMA						;Fallback to RMA space if DA fails
                MOV             r0, #6
                LDR             r3, AudioBufferSize
                SWI             XOS_Module               ;claim buffer
                STRVS           r0, [sp]                 ;return error
                RETURN_IF_ERR
BufferAllocated
                STR             r2, [r10, #Drive_BufferStart]
BufferAlreadyAllocated
                STR             r2, [r10, #Drive_BufferPtr]

                LDMIA           sp, {r0, r1}             ;get start/end
SharedSoundAlreadyActive
		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            "r0-r1"
		LDR             r2, [r10, #Drive_Flags]
                ORR             r2, r2, #Drive_AudioPause
                STR             r2, [r10, #Drive_Flags]			;pause audio

		STR		r0, [r10, #Drive_LastPosn]
		STR		r1, [r10, #Drive_TerminateAudio]

                LDR             r0, [r10, #Drive_AudioCallBack]
		TEQ		r0, #0					;is a callback already scheduled?
		MOV		r1, r10
		SWINE		XOS_RemoveCallBack			;YES, cancel it
		ADR		r0, PreCacheAudio_Callback
                STR             r0, [r10, #Drive_AudioCallBack]
		SWI		XOS_AddCallBack
		RETURN

PreCacheAudio_Callback
		SAVE		"r0-r7"
		LDR		r0, [r12, #Drive_LastPosn]
		LDR		r1, [r12, #Drive_TerminateAudio]

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

		LDR		r5, [r12, #Drive_AudioPosnCorrect]
		SUB		r0, r0, r5
                MOV             r5, #Type_AUDIO
                BL              ConvertLBAToBytes
                MOV             r4, r0					;file offset to read from

		MOV		r0, r7
                MOV             r5, #Type_AUDIO
                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, [r12, #Drive_TerminateAudio]

                MOV             r0, #3
		LDR             r1, [r12, #Drive_AudioTrack]
                DBF             "OS_GBPB %0w, %1w, %2w, %3w, %4w\n"
                SWI             XOS_GBPB
                STR             r2, [r12, #Drive_BufferFilled]

                LDR             r1, [r12, #Drive_Flags]
                BIC             r1, r1, #Drive_AudioPause
                STR             r1, [r12, #Drive_Flags]			;unpause audio

		MOV		r0, #0
		STR		r0, [r12, #Drive_AudioCallBack]
                RETURN



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

StopAllAudio1   BL              StopSharedSound
                BL              ReleaseAudioBuffer

		LDR             r1, [r10, #Drive_AudioTrack]
		TEQ		r1, #0
		MOVNE		r0, #0
		STRNE		r0, [r10, #Drive_AudioTrack]
		BLNE		CloseFile

                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		XOS_IntOff
                LDR             r2, [r10, #Drive_BufferPtr]		;terminate the audio
                STR		r2, [r10, #Drive_TerminateAudio]

                LDR             r0, [r10, #Drive_AudioCallBack]
		TEQ		r0, #0					;is a callback already scheduled?
		MOV		r1, r10
		SWINE		XOS_RemoveCallBack			;YES, cancel it

		MOV		r0, #0
		STR		r0, [r10, #Drive_BufferCounter]
		SWI		XOS_IntOn

StopSharedSound_L1
		LDR		r0, [r10, #Drive_BufferCounter]
		CMP		r0, #2
		BLO		StopSharedSound_L1			;wait for two audio buffer fills to ensure silence

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

		LDR		r1, [r10, #Drive_AudioTrack]
		BL		CloseFile
                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:
; CPU in IRQ with IRQ disabled
; 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		r5, [r0, #Drive_BufferCounter]
		ADD		r5, r5, #1
		STR		r5, [r0, #Drive_BufferCounter]

                LDR             r4, [r0, #Drive_BufferPtr]    ;current ptr
                LDR		r5, [r0, #Drive_Flags]
                TST		r5, #Drive_AudioPause         ;paused?
		TSTEQ		r3, #1<<31                    ;silence
		BNE		Audio_Fill_P2

                LDR             r10, [r0, #Drive_BufferStart]
                TEQ             r10, #0                       ;does the audio buffer still exist?
                LDRNE		r12, [r0, #Drive_TerminateAudio]
                TEQNE		r4, r12                       ;audio exhausted?
                BEQ             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]                  
		MOV		r3, r0
                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]
		MOV		r3, r0
Audio_Fill_P2
                CMP             r1, r2                        ;did we fill the buffer
                MOVNE           lr, pc                        ;NO
                LDRNE           pc, [r8, #4]                  ; |+ fill with zeros
Audio_Filled
                LDR             r5, [sp]
                LDR             r1, [r5, #Drive_BufferPtr]
                SUBS            r1, r4, r1
                LDRMI           r2, AudioBufferSize
                ADDMI           r1, r1, r2			;r1=bytes free in buffer
                LDR             r11, [r5, #Drive_AudioPlayed]
                ADD             r11, r11, r1
                STR             r11, [r5, #Drive_AudioPlayed]
                LDR             lr, [r5, #Drive_AudioReadAhead]
                CMP             r11, lr
                BLO             HaveEnoughAudio

                LDR             r1, [r5, #Drive_AudioCallBack]
                TEQ             r1, #0                        ;already scheduled?
                BNE		HaveEnoughAudio               ;YES

                TEQ             pc, pc
                MRSEQ           r2, CPSR
		MOVNE           r2, pc
                ORR             r0, r2, #3
                MSREQ           CPSR_cxsf, r0
                TEQNEP          r0, #0
                NOP

                STR             r14, [r13, #-4]!
                ADR             r0, BufferMoreAudio
                STR             r0, [r5, #Drive_AudioCallBack]
                MOV             r1, r5
                SWI             XOS_AddCallBack
                LDR             r14, [r13], #4

                TEQ             pc, pc
                MSREQ           CPSR_cxsf, r2
		TEQNEP          r2, #0
                NOP
HaveEnoughAudio
                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



BufferMoreAudio SAVE            "r0-r11"
                LDR             r4, [r12, #Drive_BufferStart]
                TEQ             r4, #0				;audio buffer still exists?
                RETURN          EQ				;NO

;JJA 07-03-26	LDR             r4, [r12, #Drive_BufferPtr]
;		LDR             r0, [r12, #Drive_TerminateAudio]
;		TEQ             r0, r4				;music ended?
;		BEQ             Audio_Fill_Terminate

                LDR             r11, [r12, #Drive_AudioPlayed]
                LDR             r1, [r12, #Drive_AudioReadAhead]
                LDR             r2, AudioBytesToRead
                ADD             r1, r1, r2
                STR             r1, [r12, #Drive_AudioReadAhead]

                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				;YES, loop back to start of buffer
                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

		LDR		r5, [r12, #Drive_AudioPosnCorrect]
		SUB		r0, r4, r5			;find head position in file
                MOV             r5, #Type_AUDIO
                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 after this one
                MOV             r0, #3
                LDR             r1, [r12, #Drive_AudioTrack]
		TEQ		r1, #0				;is a file open?
		BEQ		BufferMoreAudioDone		;NO
                SWI		XOS_GBPB			;get first bit
		BLCS		MoveToNextFILE			;C set if we need to move to the next file
                MOVS            r3, r6				;more to get?
		BEQ		BufferMoreAudioDone		;NO
                MOV		r2, r8				;wrap buffer
                SWI		XOS_GBPB			; |+  and read remaining
		BLCS		MoveToNextFILE			;C set if we need to move to the next file
BufferMoreAudioDone
		STR             r2, [r12, #Drive_BufferFilled]	;update buffer ptr

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

;JJA 07-03-26 Audio_Fill_Terminate
;                MOV             r10, r12
;                BL              StopSharedSound
;                RETURN

; exhausted the current FILE, need to move onto the next one if it exists
MoveToNextFILE	SAVE		"r10"
		BL		CloseFile			;close the current file
		MOV		r1, #0
                STR		r1, [r12, #Drive_AudioTrack]	;file closed

		LDR		r4, [r12, #Drive_AudioTrackInfo]	;move to next track
		ADD		r4, r4, #TrackInfo_Size
		STR		r4, [r12, #Drive_AudioTrackInfo]

		LDR		r1, [r4, #TrackInfo_Offset]	;get the offset within the CD
		STR		r1, [r12, #Drive_AudioPosnCorrect]

		LDR		r1, [r4, #TrackInfo_DataType]
		TEQ		r1, #Type_AUDIO			;is it an AUDIO track?
		BNE		MoveToNextFILEX			;NO, return

		LDR		r1, [r4, #TrackInfo_FileName]
		MOV		r10, r12
		BL		OpenFile
		BVS		MoveToNextFILEX
                STR		r1, [r12, #Drive_AudioTrack]	;new file handle
		MOV		r4, #0
		SWI		XOS_GBPB
		RETURN

MoveToNextFILEX	RESTORE
		B		BufferMoreAudioDone

;Entry:
; r1 = filename pointer
OpenFile	SAVE		"r0, r2"
                MOV             r0, #&41             ;open file with path
		ADD             r2, r10, #Drive_FilePath
		SWI             XOS_Find
		MOV		r1, r0
		RETURN

; r1 = file handle
CloseFile	SAVE		"r0"
		TEQ		r1, #0
		MOVNE		r0, #0
		SWINE           XOS_Find
		RETURN

		END
