org #8000
nolist

;
; Digitalised sample loader by roudoudou/Flower Corp.
; First sample loader ever RELEASED on Amstrad CPC first generation!
;
; Sorry Shap, i waited 20 years to see yours, here is mine ;)
; This is a beta release, there will be improvements in a few months
; edouard (.) berge (at) gmail (.) com
;
; documentation:
; SOS programmeurs 1,2,3,4
; Quasar.CPCScene.net
; Amslive (Madram)
; Disc+Ultra notice
;
;
; main functions in the example
;
; FDC_SetSectorSize    (default 2, may use from 0 to 5)
; FDC_SetDrive         (default 0)
; FDC_MotorON
; FDC_MotorOFF
; FDC_PlaySampleInit   ; play a sample at approx 3.9Khz
; FDC_CalibrateSample
; FDC_SetTrackSample
; FDC_ReadSectorSample
;


FDCMainStateDataAvailable         EQU 128
FDCMainStateWay                   EQU  64 ; 1 -> output to Z80 / 0 -> input from Z80
FDCMainStateIsData                EQU  32 ; 1 -> instr in progress 0 -> result to fetch
FDCMainStateIsBusy                EQU  16 ; 1 -> instr in progress / 0 -> FDC Ready
FDCMainStateWaitSeekDriveD        EQU   8
FDCMainStateWaitSeekDriveC        EQU   4
FDCMainStateWaitSeekDriveB        EQU   2
FDCMainStateWaitSeekDriveA        EQU   1
FDCMainStateWaitSeek              EQU   1+2+4+8

FDCET0StateCurrentOperation       EQU 192 ; 00 OK 01 KO ou RW ended 10 illegal instruction 11 no floppy / drive lost
; 11 ejected disk while operating on it
; 01 / 00 -> OK
FDCET0StateNoDisk                 EQU 128 ; 1 -> no disk

FDCET0StateEndOfInstruction       EQU  32 ; 1 -> instruction terminated
FDCET0StateElectronicERROR        EQU  16 ; calibrating failed or electronic error
FDCET0StateDriveUnplugged         EQU   8 ; drive or head unavailable
FDCET0StateSelectedHead           EQU   4
FDCET0StateActiveDrive            EQU   3

FDCET1StateEndOfTrack             EQU 128 ; reached end of track
FDCET1StateCRCError               EQU  32 ; or wrong read IDs
FDCET1StateDataTimingKO           EQU  16 ; respect the 26us specifications!!! Z80 was too slow!
FDCET1StateSectorNotFound         EQU   4 ; or cannot read ID
FDCET1StateProtectedFloppy        EQU   2 ; when trying to write/format
FDCET1StateIDSNotFound            EQU   1 ; wrong format

FDCET2StateErasedSectorFound      EQU 64 
FDCET2StateReadWriteError         EQU 32 ; checksum error in data red
FDCET2StateWrongIdTrack           EQU 16 ; in sector definition
FDCET2StateDataCompareOK          EQU  8 
FDCET2StateDataCompareKO          EQU  4
FDCET2StateWrongIdTrackBis        EQU  2 ; id track == #FF
FDCET2StateDAMNotFound            EQU  1 ; wrong format

FDCET3StateUnknownSpecificDrive   EQU 128 ; drive out of order
FDCET3StateProtectedFloppy        EQU  64 ; 
FDCET3StateInsertedFloppy         EQU  32 ; drive and floppy ready
FDCET3StateHeadInTrack0           EQU  16
FDCET3StateDoubleHeadDisable      EQU   8 ; 1 -> single head / 0 -> double head
FDCET3StateSelectedHead           EQU   4
FDCET3StateSelectedDrive          EQU   3




;--------------------------- sample test ---------------------------------

; disable system
di
ld hl,#C9FB
ld (#38),hl
ld sp,#200

; B=video mode
; C=screen width in bytes
ld bc,#0150
call BB5A_Setup

; HL=sample adress
; DE=sample length
ld hl,#2000
ld de,6903
call FDC_PlaySampleInit

; Setup FDC library
ld a,2
call FDC_SetSectorSize
xor a
call FDC_SetDrive
call FDC_MotorON

;
; THOSE TWO FUNCTIONS ARE NOT AVAILABLE IN THE V035 RELEASE
;
;call FDC_CheckConnectedDrive
;call FDC_TestHardwareIsOK    ; check hardware conflicts
;

; force drive A without hardware check / for testing purpose!!!
ld hl,FDC_DriveEnabled
ld (hl),1
inc hl

call FDC_CalibrateSample

;------------------------- infinite test loop -----
ReTrack
ld a,1
; read track from 1 to 38
TestSetTrack
push af
call FDC_SetTrackSample
or a
jr z,FDC_LibError

ld de,#C1C9
ld hl,#6000
call FDC_ReadSectorSample
or a
jr z,FDC_LibError

pop af
inc a
cp 39
jr c,TestSetTrack


TestSetTrackDec
push af
call FDC_SetTrackSample
or a
jr z,FDC_LibError

ld de,#C1C9
ld hl,#6000
call FDC_ReadSectorSample
or a
jr z,FDC_LibError

pop af
dec a
jr nz,TestSetTrackDec

jr ReTrack
;------------------------- end of infinite test loop -----

; brutal stop at first error!
FDC_LibError
call FDC_DisplayError
call FDC_MotorOFF
jr $

;-------------------- FDC LIBRARY / LOGICAL CORE --------------------------------------------

;
; define maximum track for current drive
; may be use to allow track greater than 41
; on 3.5' drive
;
FDC_SetMaxTrack
push hl
cp 82
jr c,FDC_SetMaxTrackOK
ld hl,MSG_InvalidSetMaxTrack
jp FDC_LibError
FDC_SetMaxTrackOK
push af
ld hl,FDC_MaxTrack ; get MaxTrack adress of selected drive
ld a,(FDC_Drive)
add l
ld l,a
pop af
ld (hl),a
pop hl
ret

;
; Set library sector size from 0 to 5
;
FDC_SetSectorSize
cp 6
jr nc,FDC_SetSectorSizeError
ld (FDC_SectorSize),a
ret
FDC_SetSectorSizeError
ld hl,MSG_SectorSizeError
jp FDC_LibError

FDC_GetSectorSize
ld a,(FDC_SectorSize)
ret

FDC_SetHead
and #1
ld (FDC_Head),a
ret

FDC_GetHead
ld a,(FDC_Head)
ret

FDC_SetDrive
and #3
ld (FDC_Drive),a
; @TODO return an error if not 0->3
ret
FDC_GetDrive
ld a,(FDC_Drive)
ret

FDC_DisableCurrentDrive
push hl
push af
ld hl,FDC_DriveEnabled
ld a,(FDC_Drive)
add l
ld l,a
ld (hl),0 ; disable drive
pop af
pop hl
ret

FDC_EnableCurrentDrive
push hl
push af
ld hl,FDC_DriveEnabled
ld a,(FDC_Drive)
add l
ld l,a
ld (hl),1
pop af
pop hl
ret

FDC_GetCurrentTrack
push hl
ld hl,FDC_CurrentTrack
ld a,(FDC_Drive)
add l
ld l,a
ld a,(hl)
pop hl
ret

; FDC_CheckAllowedDrive
; if the drive is disabled then this function
; will return back to previous function call
FDC_CheckAllowedDrive
push af
push hl
ld a,(FDC_Drive)
ld hl,FDC_DriveEnabled
add l
ld l,a
ld a,(hl)
or a
jr nz,FDC_CheckAllowedDriveOK
pop hl
pop af
ld (FDC_CheckAllowedDriveHLBack+1),hl
pop hl
FDC_CheckAllowedDriveHLBack: ld hl,#1234 ; restore HL
ret
FDC_CheckAllowedDriveOK
pop hl
pop af
ret


; Compute ending value of HL after a read/write command
; then call FDC_ReadWriteCheckHL after read/write to check
; the operation was successfull
;
; HL=destination buffer
; D=start sector
; E=end sector
;
FDC_ReadWriteComputeFinalHL
push af
push de
push hl

; compute sector size in bytes
ld hl,64
ld a,(FDC_SectorSize)
inc a
FDC_RWCSS
add hl,hl
dec a
jr nz,FDC_RWCSS

ld a,e
sub d
inc a ; number of sector to read/write

FDC_RWCSTR
ex hl,de
ld hl,0
FDC_RWCSTR2Loop
add hl,de
dec a
jr nz,FDC_RWCSTR2Loop
ex hl,de ; de=total size to read/write
pop hl
push hl
add hl,de ; final value of HL after a successfull read/write
ld (FDC_RWSectorCompare+1),hl

pop hl
pop de
pop af
ret

; Check HL value after read/write command
; return
; A=1 if OK
; A=0 if KO
; HL=error message NULL terminated
;
FDC_ReadWriteCheckHL
push de
push hl
ex hl,de
FDC_RWSectorCompare: ld hl,#1234
xor a
sbc hl,de
ld a,h
or l
jr z,FDC_ReadWriteCheckHLOK

ld hl,MSG_IncompleteRW
xor a
pop de
pop de
ret

FDC_ReadWriteCheckHLOK
ld a,1
pop hl
pop de
ret

;---------- ERROR DISPLAY --------------

FDC_DisplayError
push af
push hl

display_max:ld a,24
dec a
ld (display_max+1),a
or a
jr z,$

call FDC_DisplayErrorLoop
ld a,13
call BB5A
push bc
ld b,#f5
in a,(c)
rra
jr nc,$-3
pop bc
pop hl
pop af
ret
;
FDC_DisplayErrorLoop
ld a,(hl)
or a
ret z
call BB5A ; simple display
inc hl
jr FDC_DisplayErrorLoop


;-------------- TIMEOUT MANAGEMENT --------------------
;
; Init a maximum value of a timeout counter
;
FDC_InitTimeOutFull
push hl
ld hl,0
ld (FDC_TimeOutValue+1),hl
pop hl
ret
;
; Fine tuning for a timeout counter
; A=approx 1/10s of delay wanted
;
FDC_InitTimeOut
push hl
push de
ld hl,0
ld de,390
FDC_InitTimeOutSimpleMUL
add hl,de
dec a
jr nz,FDC_InitTimeOutSimpleMUL
ld (FDC_TimeOutValue+1),hl
pop de
pop hl
ret

;
; decrement timer then set A
; A=1 -> time out!
; A=0 -> no time out
FDC_CheckTimeOut
push hl
FDC_TimeOutValue: ld hl,#1234
dec hl
ld (FDC_TimeOutValue+1),hl
ld a,l
or h
ld a,0
jr nz,FDC_CheckTimeOutIsOK
ld hl,MSG_OperationTimeOut
call FDC_DisplayError
ld a,1
FDC_CheckTimeOutIsOK
pop hl
ret


;-------------------- FDC LIBRARY / HARDWARE CORE --------------------------------------------

FDC_MotorON
push af
push bc
ld a,(FDC_MotorState)
or a
jr nz,FDC_MotorSkipDelay
inc a
FDC_MotorGeneric
ld bc,#FA7E
out (c),a
ld (FDC_MotorState),a
or a
jr z,FDC_MotorSkipDelay
ld b,200
ei
halt
djnz $-1
di
FDC_MotorSkipDelay
pop bc
pop af
ret

FDC_MotorOFF
push af
push bc
xor a
jr FDC_MotorGeneric
FDC_MotorState defb 0 ; first byte


;
; look for connected but turned off additionnal drives
; may be used instead of only calibrating!
;
;
; NOT FINISHED !!!
;
;

FDC_CheckConnectedDrive
push af
push bc
push hl
call FDC_GetDrive
push af

xor a
call FDC_SetDrive
call FDC_CalibrateSample
ld hl,FDC_DriveEnabled
ld (hl),1 ; et si il etait KO ce lecteur hein?
inc hl

FDC_CheckConnectedDriveLoop
ld a,1
call FDC_SetDrive
call FDC_CalibrateSample
ld a,(FDCET0State)
and FDCET0StateCurrentOperation
cp #80 ; INVALID COMMAND
jr nz,FDC_CheckConnectedDriveOK
ld hl,MSG_PlugDriveB
call FDC_DisplayError
ld (hl),0
jr FDC_CheckConnectedDriveKO
FDC_CheckConnectedDriveOK
ld (hl),1
FDC_CheckConnectedDriveKO

pop af
call FDC_SetDrive ; set last drive used
pop hl
pop bc
pop af
ret


;
;
; NOT FINISHED !!!
;
;
; test ET0 and ET3
FDC_TestHardwareIsOK
push af
push hl
call FDC_GetInterruptStateSample
;
ld a,(FDCET0State)
and FDCET0StateElectronicERROR
jr z,FDC_TestHardwareIsOKNext01
ld hl,MSG_ElectronicError
call FDC_DisplayError
call FDC_DisableCurrentDrive
jr FDC_TestHardwareIsOKEND

FDC_TestHardwareIsOKNext01
ld a,(FDCET0State)
and FDCET0StateDriveUnplugged
jr z,FDC_TestHardwareIsOKNext02
ld hl,MSG_DriveUnplugged
call FDC_DisplayError
call FDC_DisableCurrentDrive
jr FDC_TestHardwareIsOKEND

FDC_TestHardwareIsOKNext02
; check ET3 if IntState succeed
call FDC_GetDriveStateSample
ld a,(FDCET3State)
or FDCET3StateInsertedFloppy
jr nz,FDC_TestHardwareIsOKNext03
ld hl,MSG_InsertFloppy
call FDC_DisplayError

FDC_TestHardwareIsOKNext03

FDC_TestHardwareIsOKEND
pop hl
pop af
ret


FDC_ReadWriteManageError
push hl

ld a,(FDCET0State)
and FDCET0StateNoDisk
jr z,$+2+3+2
ld hl,MSG_InsertFloppy
jr FDC_ReadWriteManageErrorKO
;
ld a,(FDCET0State)
and FDCET0StateElectronicERROR
jr z,$+2+3+2
ld hl,MSG_ElectronicError
jr FDC_ReadWriteManageErrorKO
;
ld a,(FDCET0State)
and FDCET0StateDriveUnplugged
jr z,$+2+3+2
ld hl,MSG_DriveUnplugged
jr FDC_ReadWriteManageErrorKO
;

ld a,(FDCET1State)
and FDCET1StateCRCError
jr z,$+2+3+2
ld hl,MSG_CRCErrorWrongID
jr FDC_ReadWriteManageErrorKO
;
ld a,(FDCET1State)
and FDCET1StateDataTimingKO
jr z,$+2+3+2
ld hl,MSG_YourTooSlow
jr FDC_ReadWriteManageErrorKO
;
ld a,(FDCET1State)
and FDCET1StateSectorNotFound
jr z,$+2+3+2
ld hl,MSG_SectorNotFound
jr FDC_ReadWriteManageErrorKO

; test protected floppy? for what?

;
ld a,(FDCET1State)
and FDCET1StateIDSNotFound
jr z,$+2+3+2
ld hl,MSG_WrongFormat
jr FDC_ReadWriteManageErrorKO

;
ld a,(FDCET2State)
and FDCET2StateReadWriteError
jr z,$+2+3+2
ld hl,MSG_ChecksumError
jr FDC_ReadWriteManageErrorKO

;
ld a,(FDCET2State)
and FDCET2StateWrongIdTrack
jr z,$+2+3+2
ld hl,MSG_WrongIdTrack
jr FDC_ReadWriteManageErrorKO
;
ld a,(FDCET2State)
and FDCET2StateWrongIdTrackBis
jr z,$+2+3+2
ld hl,MSG_WrongIdTrack
jr FDC_ReadWriteManageErrorKO
;
ld a,(FDCET2State)
and FDCET2StateDAMNotFound
jr z,$+2+3+2
ld hl,MSG_WrongDAMFormat
jr FDC_ReadWriteManageErrorKO
pop hl
ld a,1
ret

FDC_ReadWriteManageErrorKO
pop af ; remove HL from stack
xor a
ret


;------ FDC GET --------

;
; HL=adress to store result, will be incremented
; returns
; A=1 if OK / HL=HL+1
; A=0 if cannot read
FDC_GetByte
push bc
ld bc,#FB7E
FDC_GetByteWaitForIt
in a,(c)
jp p,FDC_GetByteWaitForIt  ; test bit 7
and FDCMainStateIsBusy
jr z,FDC_GetByteBusy
inc c
in a,(c)
ld (hl),a
inc hl
pop bc
ld a,1
ret
FDC_GetByteBusy
pop bc
xor a
ret


;------ FDC SEND --------


FDC_FastSendByte
push bc
push af
ld bc,#FB7E
FDC_FastSendByteWaitFDC
in a,(c)
jp p,FDC_FastSendByteWaitFDC ; test bit 7
inc c
pop af
out (c),a
ld b,4
djnz $
pop bc
ret

FDC_SendByte
push bc
push af
ld bc,#FB7E
FDC_SendByteWaitFDC
in a,(c)
add a
jr nc,FDC_SendByteWaitFDC
add a
jr nc,FDC_SendByteExpected
; put value in the message
pop af
ld hl,MSG_FDCDoNotWantData+1
call BB5A_PutDigit
dec hl
call FDC_DisplayError ; cannot send byte to FDC!
pop bc
ret

FDC_SendByteExpected
pop af
inc c
out (c),a
ld b,4
djnz $
pop bc
ret

;
; HL=command bytes
; B=command length
;
FDC_SendCommand
ld a,(hl)
call FDC_SendByte
inc hl
djnz FDC_SendCommand
ret



;**********************************************************************************************
;            FDC FUNCTIONS THAT CAN PLAY A LOOPED SAMPLE DURING OPERATIONS...
;**********************************************************************************************

;
; command #4 returns ET3
;
FDC_GetDriveStateSample
call FDC_WaitToBeReadySample
push af
push hl
ld a,#4
call FDC_SendByte
ld a,(FDC_Drive)
call FDC_SendByte
ld hl,FDCET3State
call FDC_GetByte
pop hl
pop af
ret


FDC_WaitToBeReadySample
push hl
push bc
push af
call FDC_InitTimeOutFull
FDC_IsNotReadySample
call FDC_PutPSGOneSample ; play sample, maybe too fast?
ld b,15
djnz $
call FDC_CheckTimeOut
or a
jr nz,FDC_IsReadyTimeoutSample
ld bc,#FB7E
in a,(c)
jp p,FDC_IsNotReadySample
;ld c,a
;and FDCMainStateWaitSeek ; seek still in progress?
;jr nz,FDC_IsNotReadySample
;ld a,c
and FDCMainStateIsBusy
jr nz,FDC_IsNotReadySample ; Wait FDC is Ready

FDC_IsReadyExitSample
pop af
pop bc
pop hl
ret
FDC_IsReadyTimeoutSample
call FDC_DisableCurrentDrive
jr FDC_IsReadyExitSample


FDC_GetInterruptStateSample
call FDC_WaitToBeReadySample
push af
push de
push hl

call FDC_InitTimeOutFull

FDC_GetInterruptStateSampleLoop
call FDC_PutPSGOneSample
call FDC_CheckTimeOut
or a
jr nz,FDC_GetInterruptStateOKSample ; timeout

ld a,#8
call FDC_SendByte
;
ld hl,FDCET0State
call FDC_GetByte
ld hl,FDC_CurrentTrack
ld a,(FDC_Drive)
ld d,0
ld e,a
add hl,de ; hl=currenttrack of currentdrive
call FDC_GetByte ; may return nothing if seeking
or a
jr z,FDC_GetInterruptStateSampleLoop

ld a,(FDCET0State)
and FDCET0StateEndOfInstruction
jr z,FDC_GetInterruptStateSampleLoop ; wait until current operation end

ld a,(FDCET0State)
and FDCET0StateCurrentOperation
cp #80
jr nz,FDC_GetInterruptStateTestInsertedFloppySample
ld hl,MSG_InvalidCommand
call FDC_DisplayError
call FDC_DisableCurrentDrive
jr FDC_GetInterruptStateOKSample ; exit on first error

FDC_GetInterruptStateTestInsertedFloppySample
ld a,(FDCET0State)
and FDCET0StateDriveUnplugged
jr z,FDC_GetInterruptStateOKSample
ld hl,MSG_DriveUnplugged
call FDC_DisplayError
call FDC_DisableCurrentDrive
FDC_GetInterruptStateOKSample

pop hl
pop de
pop af
ret

FDC_CalibrateSample
call FDC_WaitToBeReadySample
push af
push bc
push hl
ld a,#7
call FDC_SendByte
ld a,(FDC_Drive)
call FDC_SendByte ; ID-Drive without useless head
;
call FDC_GetInterruptStateSample
; check piste 0
ld hl,FDC_CurrentTrack
ld d,0
ld e,a
add hl,de ; hl=currenttrack of currentdrive
ld a,(hl)
or a
jr z,FDC_CalibrateOKSample
ld hl,MSG_CannotCalibrate
call FDC_DisplayError
jr $
FDC_CalibrateOKSample
pop hl
pop bc
pop af
ret

;
; SET TRACK WHILE PLAYING SAMPLE
;
; input
; A=desired track
;
; output
; A=1 -> OK
;
; A=0 -> KO
; HL=error message NULL terminated
;
FDC_SetTrackSample

call FDC_CheckAllowedDrive
call FDC_WaitToBeReadySample
push de
push hl
call FDC_PutPSGOneSample ; PLAY SAMPLE
ld d,a
ld (FDC_SetTrackTargetSample+1),a
ld a,2
ld (FDC_SetTrackRetrySample+1),a          ; retry 2 times max
dec a
ld (FDC_SetTrackRetryCalibrateSample+1),a ; calibrate one time then error
ld hl,FDC_MaxTrack ; get MaxTrack of selected drive
ld a,(FDC_Drive)
ld e,a
ld a,d
ld d,0
add hl,de
cp (hl)
jr c,FDC_SetTrackNextSample
ld hl,MSG_InvalidTrackNumber
xor a
jr FDC_SetTrackEndSample
;
FDC_SetTrackNextRedoSample
ld a,2
ld (FDC_SetTrackRetrySample+1),a

FDC_SetTrackNextSample ; entry loop
ld a,#F                           ; command
call FDC_SendByte
ld a,(FDC_Drive)                  ; drive
call FDC_SendByte
ld a,(FDC_SetTrackTargetSample+1) ; track
call FDC_SendByte
;
call FDC_GetInterruptStateSample
; check piste bien celle demandee
ld a,(FDC_CurrentTrack)
FDC_SetTrackTargetSample: cp #12
jr z,FDC_SetTrackOKSample

; RETRY
FDC_SetTrackRetrySample: ld a,#12
dec a
ld (FDC_SetTrackRetrySample+1),a
jr nz,FDC_SetTrackNextSample
; RETRIES FAILED CALIBRATE!
call FDC_CalibrateSample
FDC_SetTrackRetryCalibrateSample: ld a,#12
dec a
ld (FDC_SetTrackRetryCalibrateSample+1),a
jr nz,FDC_SetTrackNextRedoSample

ld hl,MSG_CannotSetTrack
xor a
jr FDC_SetTrackEndSample
;
FDC_SetTrackOKSample
ld a,1
FDC_SetTrackEndSample
pop hl
pop de
ret

;
; Get 7 bytes after executing general command
;
; output
; A=1 OK
; A=0 KO
; HL=error message NULL terminated
;
FDC_GetResult7BytesSample
push hl
push de
call FDC_PutPSGOneSample ; PLAY SAMPLE
; result 7 bytes
ld hl,FDCET0State
call FDC_GetByte ; ET0
call FDC_GetByte ; ET1
call FDC_GetByte ; ET2
ld hl,FDC_CurrentTrack
ld a,(FDC_Drive)
ld d,0
ld e,a
add hl,de
call FDC_GetByte ; get current track for current drive
ld hl,FDC_Head
call FDC_GetByte
ld hl,FDC_ResultSectorID
call FDC_GetByte ; 'unused'
ld hl,FDC_ResultSectorSize
call FDC_GetByte ; 'unused'
;
pop de
pop hl
call FDC_ReadWriteManageError
ret


; READ SECTOR WHILE PLAYING SAMPLE
;
; input
; HL=destination buffer
; D=start sector
; E=end sector
;
; output
; A=1 OK
; HL=destination buffer+size read
; or
; A=0 KO
; HL=error message NULL terminated
;
FDC_ReadSectorSample

call FDC_CheckAllowedDrive
call FDC_WaitToBeReadySample
call FDC_ReadWriteComputeFinalHL
ld (ReadBufferSample+1),hl
;
call FDC_PutPSGOneSample ; PLAY SAMPLE
; command
ld a,#46
call FDC_SendByte
ld a,(FDC_Drive)
call FDC_SendByte
ld hl,FDC_CurrentTrack
add l
ld l,a
ld a,(hl) ; track
call FDC_SendByte
ld a,(FDC_Head)
call FDC_SendByte
ld a,d
call FDC_SendByte
ld a,(FDC_SectorSize)
call FDC_SendByte
ld a,e
call FDC_SendByte
ld a,#20
call FDC_SendByte ; GAP unused?
xor a
call FDC_SendByte ; length unused?

; execution
ReadBufferSample: ld hl,#1234
call FDC_GenericReadDataSAMPLE
call FDC_PlaySampleResync ; MANDATORY after executing state machine!
call FDC_GetResult7BytesSample
or a
ret z
call FDC_ReadWriteCheckHL
or a
ret z
ret


;*************** FDC STATE MACHINE TO READ DATA ****************************************************************
;
; input HL start of destination array
; returns HL end of data red
;
FDC_GenericReadDataSAMPLE
push bc
push de
ld bc,#FB7E
ld d,FDCMainStateIsData
call FDC_GenericReadDataLoopSample
pop de
pop bc
ret


;
; play a sample routine algo (QuasarCPC)
; out #F4xx,9   ; channel B volume register
; out #F6xx,#C0 ; PSG must read register
; out #F6xx,0   ; PSG inactive
; out #F4xx,v   ; value to write to the register
; out #F6xx,#80 ; PSG must read value
; out #F6xx,0   ; PSG inactive
;
; assuming those registers before calling
; D=9
; E=0
; C=#C0 -> res 6,C -> #80 -> set 6,C -> #C0
; B=#F4 -> set 1,B -> #F6 -> res 1,B -> #F4
;

;
; Init secondary registers for sample replay
;
; HL=sample adress
; DE=sample length
FDC_PlaySampleInit
push hl
add hl,de
ld a,h
ld (FDC_PutPSG_HEND+1),a
ld (FDC_PutPSG_OS_HEND+1),a
pop hl
ld a,h
ld (FDC_PutPSG_LoopReinit+1),a
ld (FDC_PutPSG_OS_LoopReinit+1),a
ld de,#900
ld bc,#F4C0
exx
ld bc,#F700
ld a,#82
out (c),a ; Port A en sortie
xor a
dec b
out (c),a  ; reset command
;
; configure default PSG output
;
ld hl,#73F ;%00111111 init channel mixer/noise
call PSGPutValue
ld hl,#800
call PSGPutValue
ld hl,#A00
call PSGPutValue
ret

; H=psg register
; L=value
PSGPutValue
push bc
push de
ld de,#C080
ld bc,#F400
out (c),h
out (c),d
out (c),c
out (c),l
out (c),e
out (c),c
pop de
pop bc
ret

;
; After reading data we do not know which state we were
; so we need to reinit values! Except HL
;
FDC_PlaySampleResync
exx
ld de,#0900
ld bc,#F6C0
out (c),e ; PPI inactive for sure
res 1,b
exx
ret

;------------- unrolled FDC read + SAMPLE replay -------------

FDC_GenericReadDataLoopSample
in a,(c)
jp p,$+11
and d
ret z
inc c
in a,(c)
dec c
ld (hl),a
inc hl
FDC_GenericReadDataLoopSamplePSG1
exx
out (c),d ; #09 channel B volume register
set 1,b
out (c),c ; #C0
exx
;
in a,(c)
jp p,$+11
and d
ret z
inc c
in a,(c)
dec c
ld (hl),a
inc hl
;
in a,(c)
jp p,$+11
and d
ret z
inc c
in a,(c)
dec c
ld (hl),a
inc hl
exx
xor a
out (c),a  ; #00 (ACK)
res 1,b
res 6,c ; #C0 -> #80
exx
;
in a,(c)
jp p,$+11
and d
ret z
inc c
in a,(c)
dec c
ld (hl),a
inc hl
;
in a,(c)
jp p,$+11
and d
ret z
inc c
in a,(c)
dec c
ld (hl),a
inc hl
exx
ld e,(hl)
inc hl
exx
;
in a,(c)
jp p,$+11
and d
ret z
inc c
in a,(c)
dec c
ld (hl),a
inc hl
;
in a,(c)
jp p,$+11
and d
ret z
inc c
in a,(c)
dec c
ld (hl),a
inc hl
exx
out (c),e ; sample value
set 1,b
out (c),c ; #80
exx
;
in a,(c)
jp p,$+11
and d
ret z
inc c
in a,(c)
dec c
ld (hl),a
inc hl
;
in a,(c)
jp p,$+11
and d
ret z
inc c
in a,(c)
dec c
ld (hl),a
inc hl
exx
out (c),e ; #00 (ACK)
res 1,b
set 6,c
ld a,h
exx
ex af,af'
;
in a,(c)
jp p,$+11
and d
ret z
inc c
in a,(c)
dec c
ld (hl),a
inc hl
;
in a,(c)
jp p,$+11
and d
ret z
inc c
in a,(c)
dec c
ld (hl),a
inc hl
ex af,af'
FDC_PutPSG_HEND: cp #12 ; H-end
jr nz,FDC_PutPSG_GOK
exx
FDC_PutPSG_LoopReinit: ld h,#12
exx
FDC_PutPSG_GOK
; before we loop, wait a little
in a,(c)
jp p,$+11
and d
ret z
inc c
in a,(c)
dec c
ld (hl),a
inc hl
;
in a,(c)
jp p,$+11
and d
ret z
inc c
in a,(c)
dec c
ld (hl),a
inc hl

in a,(c)
jp p,FDC_GenericReadDataLoopSamplePSG1
and d
ret z
inc c
in a,(c)
dec c
ld (hl),a
inc hl
jp FDC_GenericReadDataLoopSample

;---------- all in one PLAY SAMPLE subroutine

FDC_PutPSGOneSample
exx
ex af,af'
xor a
out (c),d  ; #09 channel B volume register
set 1,b
out (c),c  ; #C0
out (c),a  ; #00
dec b
res 6,c    ; #C0 -> #80
outi       ; dec b ; out (c),(hl) ; inc hl
set 1,b
out (c),c  ; #80
out (c),a  ; #00
res 1,b
set 6,c
ld a,h
FDC_PutPSG_OS_HEND: cp #12 ; H-end
jr nz,FDC_PutPSG_OS_OK
FDC_PutPSG_OS_LoopReinit: ld h,#12
FDC_PutPSG_OS_OK
ex af,af'
exx
ret

;
; standard routines, read,write,format,advanced format
; do not need to be published...
;
; read 'FDC_library_extension_v001.asm'
;


; FDC_Advanced_Command (not used)
FDC_multitrack         defb 0 ; bit 7
FDC_density            defb 1 ; bit 6
FDC_ignore_ds          defb 0 ; bit 5

; FDC_Advanced_Drive_Settings
FDC_Head               defb 0   ; bit 2
FDC_Drive              defb 0  ; bit 0/1
FDC_SectorSize         defb 2
FDC_SectorNumber       defb 9   ; Amsdos default 9
FDC_SectorFirstID      defb #C0 ; Amsdos default #C0
FDC_SectorGap          defb #52 ; Amsdos default #52
FDC_FillValue          defb #5A ; Amsdos default = #E5
FDC_ResultSectorID     defb 0
FDC_ResultSectorSize   defb 0

align 4
FDC_MaxTrack           defb 41,41,41,41 ; software limitation, maybe raised up!
FDC_CurrentTrack       defb -1,-1,-1,-1
FDC_DriveEnabled       defb 0,0,0,0

; FDC_State mirror
FDCMainState           defb 0
FDCET0State            defb 0
FDCET1State            defb 0
FDCET2State            defb 0
FDCET3State            defb 0

; Library messages
MSG_OperationTimeOut   defb 'FDC OPERATION TIMEOUT',0
MSG_CannotSetTrack     defb 'CANNOT SET TRACK',0
MSG_CannotCalibrate    defb 'CANNOT CALIBRATE DRIVE',0
MSG_InvalidCommand     defb 'INVALID FDC COMMAND',0
MSG_SectorSizeError    defb 'INVALID SECTOR SIZE',0
MSG_DisableDrive       defb 'DRIVE DISABLED',0
MSG_InvalidTrackNumber defb 'REQUESTED TRACK TOO HIGH',0
MSG_InvalidSetMaxTrack defb 'MAXTRACK VALUE TOO HIGH',0
MSG_IncompleteRW       defb 'READ/WRITE ABORTED DURING OPERATION',0
MSG_WTBR_Purge         defb '(  ) PURGE STACK DURING WAIT TO BE READY',0

; Main State specific messages
MSG_FDCDoNotWantData   defb '(  ) FDC IS NOT EXPECTING DATA',0

; ET0 specific messages
MSG_ElectronicError    defb 'DRIVE IS REPORTING INTERNAL FAILURE',0
MSG_DriveUnplugged     defb 'DRIVE OR HEAD UNAVAILABLE',0
MSG_PlugDriveB         defb 'TURN ON DRIVE B OR DISCONNECT IT',0
MSG_InsertFloppy       defb 'INSERT FLOPPY',0
; ET1 specific messages
MSG_CRCErrorWrongID    defb 'CRC ERROR',0
MSG_YourTooSlow        defb 'Z80 WAS TOO SLOW TO GET DATA',0
MSG_SectorNotFound     defb 'SECTOR NOT FOUND',0
MSG_WrongFormat        defb 'WRONG FORMAT',0
; ET2 specific messages
MSG_ChecksumError      defb 'CHECKSUM ERROR IN DATA',0
MSG_WrongIdTrack       defb 'WRONG ID TRACK IN SECTOR DEF',0
MSG_WrongDAMFormat     defb 'WRONG DAM FORMAT',0
; ET3 specific messages
MSG_DriveOutOfOrder    defb 'DRIVE OUT OF ORDER',0
MSG_ProtectedFloppy    defb 'FLOPPY IS PROTECTED',0



read 'BB5A_replacement_v001.asm'

read 'sampletest_unpacked_6903.asm'