********************************************************************
* DDisk - Dragon Floppy driver
*
* $Id$
*
* Edt/Rev  YYYY/MM/DD  Modified by
* Comment
* ------------------------------------------------------------------
*   -      ????/??/??
* Original Dragon Data distribution version
*
* Added Defines for IO ports.
*		   2004/11/09, P.Harvey-Smith
*	
* Dragon Alpha code, 2004-11-09, P.Harvey-Smith.
*	I am not sure of how to disable NMI on the Alpha, it is
*	simulated in software using the NMIFlag.
*
*   The Dragon Alpha/Professional uses the same FDC chip as 
*	DragonDos, however it is mapped between FF2C and FF2F,
*	also the register order is REVERSED, so command/status is at
* 	FF2F.
*
* 	Drive Selects, motor and write precompensation is controled
*	through the IO port of an AY-8912, which itself is connected
*	to a 3rd PIA mapped at FF24 to FF27, this PIA also has it's
*	inturrupt lines connected to the CPU's FIRQ.
*
* 2004-11-15, P.Harvey-Smith.
*	Fixed bug in inturrupt handling code that was making the 
*	Dragon Alpha crash if a disk was accessed with no disk 
*	in the drive. As the Alpha is using a simulated NMI disable
* 	we have to ensure that the NMI enabling routine has completed
*	BEFORE isuing a command to the disk controler, or if the 
* 	inturrupt happens in the middle of the enable routine it 
*	causes the machine to crash !
*
* 2004-11-24, P.Harvey-Smith.
*	Fixed up so that double sided disks now work, origional 
* 	double sided code taken from Jon Bird's pages at :
*	http://www.onastick.clara.net/dragon.html and modified 
* 	for NitrOS9.
* 
* 2004-11-27, P.Harvey-Smith.
* 	Reformatted with tab=8.
*
* 2005-04-22, P.Harvey-Smith.
*	Hopefully fixed a bug that was causing the Dragon 64 target to
*	crash and burn when reading disks, this made a successfull boot 
*	almost imposible ! Fixed by writing disk command before writing
*	disc control register, the Alpha target needs them the other way 
*	around. Still has a problem doing lots of retries.
*
* 2005-04-24, P.Harvey-Smith.
*	Fixed constant lost data errors reading disks, again by slightly 
*	re-ordering the instructions in the read data loop.
*

         nam   DDisk
         ttl   Dragon Floppy driver

* Disassembled 02/04/21 22:37:46 by Disasm v1.6 (C) 1988 by RML

         ifp1
         use   defsfile.dragon
         endc

		IFNE	DragonAlpha

* Dragon Alpha has a third PIA at FF24, this is used for
* Drive select / motor control, and provides FIRQ from the
* disk controler.

DPPIADA		EQU	DPPIA2DA
DPPIACRA	EQU	DPPIA2CRA		
DPPIADB		EQU	DPPIA2DB		
DPPIACRB	EQU	DPPIA2CRB

PIADA		EQU	DPPIADA+IO	; Side A Data/DDR
PIACRA		EQU	DPPIACRA+IO	; Side A Control.
PIADB		EQU	DPPIADB+IO	; Side A Data/DDR
PIACRB		EQU	DPPIACRB+IO	; Side A Control.

;WD2797 Floppy disk controler, used in Alpha Note registers in reverse order !
DPCMDREG	EQU	DPCmdRegA	; command/status			
DPTRKREG	EQU	DPTrkRegA	; Track register
DPSECREG	EQU	DPSecRegA	; Sector register
DPDATAREG	EQU	DPDataRegA	; Data register

CMDREG		EQU	DPCMDREG+IO	; command/status			
TRKREG		EQU	DPTRKREG+IO	; Track register
SECREG		EQU	DPSECREG+IO	; Sector register
DATAREG		EQU	DPDATAREG+IO	; Data register

; Disk IO bitmasks

NMIEn    	EQU	NMIEnA
WPCEn    	EQU   	WPCEnA
SDensEn  	EQU   	SDensEnA
MotorOn  	EQU   	MotorOnA 

; These are the bits that we know the function of on the Alpha interface
KnownBits	EQU	Drive0A+Drive1A+Drive2A+Drive3A+MotorOnA+WPCEnA

		ELSE
		
DPPIADA		EQU	DPPIA1DA
DPPIACRA	EQU	DPPIA1CRA		
DPPIADB		EQU	DPPIA1DB		
DPPIACRB	EQU	DPPIA1CRB

PIADA		EQU	DPPIADA+IO	; Side A Data/DDR
PIACRA		EQU	DPPIACRA+IO	; Side A Control.
PIADB		EQU	DPPIADB+IO	; Side A Data/DDR
PIACRB		EQU	DPPIACRB+IO	; Side A Control.

;WD2797 Floppy disk controler, used in DragonDos.
DPCMDREG	EQU	DPCmdRegD	; command/status			
DPTRKREG	EQU	DPTrkRegD	; Track register
DPSECREG	EQU	DPSecRegD	; Sector register
DPDATAREG	EQU	DPDataRegD	; Data register

CMDREG		EQU	DPCMDREG+IO	; command/status			
TRKREG		EQU	DPTRKREG+IO	; Track register
SECREG		EQU	DPSECREG+IO	; Sector register
DATAREG		EQU	DPDATAREG+IO	; Data register

; Disk IO bitmasks

NMIEn    	EQU	NMIEnD
WPCEn    	EQU   	WPCEnD
SDensEn  	EQU   	SDensEnD
MotorOn  	EQU   	MotorOnD

		ENDC

tylg	set   	Drivr+Objct   
atrv    set   	ReEnt+rev
rev     set  	$00
edition set   	3

MaxDrv   set   4

         mod   eom,name,tylg,atrv,start,size
		
		org	DrvBeg
		RMB   	MaxDrv*DrvMem
CDrvTab	  	rmb   	2
DrivSel   	rmb   	1	; Saved drive mask
Settle	 	rmb	1	; Head settle time
SavePIA0CRB	rmb 	1	; Saved copy of PIA0 control reg b
SaveACIACmd	rmb	1	; Saved copy of ACIA command reg
BuffPtr	 	rmb	2	; Buffer pointer
SideSel	 	rmb	1	; Side select.
NMIFlag	 	rmb	1	; Flag for Alpha, should this NMI do an RTI ?

DskError	rmb	1	

VIRQPak  	rmb   	2	; Vi.Cnt word for VIRQ
u00B3    	rmb   	2	; Vi.Rst word for VIRQ
u00B5    	rmb   	1	; Vi.Stat byte for VIRQ (drive motor timeout)

VIRQInstalled	rmb	1	; Is VIRQ Installed yet ?
size     	equ  	.

	fcb   	$FF 
name    equ   	*
        fcs   	/DDisk/
        fcb   	edition

VIRQCnt fdb   	TkPerSec*4     Initial count for VIRQ (4 seconds)

start   lbra  	Init		; Initialise Driver
        lbra  	Read		; Read sector
        lbra  	Write		; Write sector
        lbra 	GetStat		; Get status
        lbra  	SetStat		; Set status
        lbra  	Term		; Terminate device

IRQPkt   fcb   	$00 		; Normal bits (flip byte)
         fcb   	$01		; Bit 1 is interrupt request flag (Mask byte)
         fcb   	10		; Priority byte


* Init
*
* Entry:
*    Y  = address of device descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
DragonDebug	EQU	1
Init    
	IFNE	DragonDebug
	pshs	x		; This is here so I can find disk driver in mess
	ldx	#$AA55		; by setting register breakpoint to X=$AA55 !
	puls	x
	ENDC

	clra
	sta	>D.DskTmr	; Zero motor timer
	
	IFNE	DragonAlpha	; Turn off all drives
	lbsr	AlphaDskCtl
	ELSE
        sta   	>DskCtl
	ENDC
		 
        ldx   	#CmdReg		; Reset controler
        lda  	#FrcInt
        sta   	,x
        lbsr  	Delay
        lda   	,x
        lda   	#$FF
        ldb   	#MaxDrv
        leax  	DrvBeg,u
		 
InitDriveData    
	sta   	DD.Tot,x	; Set total sectors = $FF
        sta   	<V.Trak,x	; Set current track = 0
        leax  	<DrvMem,x	; Skip to next drive
        decb  
        bne   	InitDriveData
         
	leax  	>NMIService,pcr	; Setup NMI handler
        stx   	>D.XNMI+1
        lda   	#$7E		; $7E = JMP
        sta   	>D.XNMI
		 
	clr	VIRQInstalled,u	;flag not installed yet

	pshs  	y              	; save device dsc. ptr
	leay  	>u00B5,u       	; point to Vi.Stat in VIRQ packet
        tfr   	y,d            	; make it the status register ptr for IRQ
        leay  	>IRQSvc,pc     	; point to IRQ service routine
        leax  	>IRQPkt,pc     	; point to IRQ packet
        os9   	F$IRQ          	; install IRQ
        puls  	y              	; Get back device dsc. ptr
		 
        ldd   	#$0100		; Request a page of system ram
        pshs  	u		; used to verify writes
        os9   	F$SRqMem 
        tfr   	u,x
        puls  	u
        bcs   	Return
        stx   	>BuffPtr,u	; Save verify page pointer
        clrb  
Return  rts   


* GetStat
*
* Entry:
*    A  = function code
*    Y  = address of path descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
GetStat

* Term
*
* Entry:
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
Term    clrb  
        rts   

* Read
*
* Entry:
*    B  = MSB of the disk's LSN
*    X  = LSB of the disk's LSN
*    Y  = address of path descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
Read     	
	lda   	#$91		; Retry count
        cmpx  	#$0000		; LSN ?
        bne   	ReadDataWithRetry	; No : Just do read,
        bsr   	ReadDataWithRetry	; Yes : do read and copy disk params
        bcs   	ReadDataExit
        ldx   	PD.Buf,y
        pshs  	y,x
        ldy   	>CDrvTab,u
        ldb   	#DD.Siz-1 
L0082   lda   	b,x
        sta   	b,y
        decb  
        bpl   	L0082
        clrb  
        puls  	pc,y,x
ReadDataExit    
	rts   

; Read Retry

ReadDataRetry    

	lbsr	RetryErrorDisplay

	bcc   	ReadDataWithRetry	; Retry entry point
        pshs  	x,b,a
        lbsr  	ResetTrack0	; Reset track 0
        puls  	x,b,a

ReadDataWithRetry    
	pshs  	x,b,a		; Normal entry point
        bsr   	DoReadData
        puls  	x,b,a
        bcc  	ReadDataExit
        lsra  			; Check for retry
        bne   	ReadDataRetry

DoReadData    
	lbsr  	SeekTrack
        bcs   	ReadDataExit
        ldx   	PD.Buf,y	; Target address for data
        pshs  	y,dp,cc
        ldb   	#ReadCmnd	; Read command
        bsr   	PrepDiskRW	; Prepare disk 

DoReadDataLoop    
	lda   	<DPPIACRB	; Is data ready ?
        bmi   	ReadDataReady	; Yes : read it
        leay  	-1,y			
        bne   	DoReadDataLoop
        bsr   	RestoreSavedIO
        puls  	y,dp,cc			
        lbra  	RetReadError	; Return read error to caller

ReadDataWait 
	sync			; Sync to inturrupts, wait for data
	
ReadDataReady
	lda   	<DPDataReg	; Read data from FDC
        ldb   	<DPPIADB	; Clear PIA inturrupt status
        sta   	,x+		; save data in memory
        bra   	ReadDataWait	; do next
	 
PrepDiskRW    

	clr	DskError,u
	
	lda   	#$FF		; Make DP=$FF, to make io easier
        tfr   	a,dp
        lda   	<DPAciaCmd 	; Save ACIA Command reg	
        sta   	>SaveACIACmd,u
        anda  	#$FE		; Disable ACIA inturrupt
        sta   	<DPAciaCmd 	
        bita  	#$40		; Is Tx inturrupt enabled ?
        beq   	L00DE
L00D8   lda   	<DPAciaStat	; Yes, wait for Tx to complete
        bita  	#$10
        beq   	L00D8
		 
L00DE   orcc  	#$50		; Mask inturrupts
        lda   	<DPPia0CRB	; Save PIA0 IRQ Status
        sta   	>SavePIA0CRB,u	
        lda   	#$34		; Disable it.
        sta   	<DPPia0CRB	
	lda   	<DPACIACmd	; Disable ACIA Inturrupt
        anda  	#$FE
        sta   	<DPACIACmd	
        lda   	<DPPIACRB	; Set PIA to generate FIRQ on FDC DRQ
        ora   	#$03
        sta   	<DPPIACRB	
        lda   	<DPPIADB	; Clear any outstanding inturrupt	
        ldy   	#$FFFF
        lda   	#NMIEn+MotorOn	; Enable NMI, and turn motor on
        ora   	>DrivSel,u	; mask in drives
        ORB   	>SideSel,U 	; Set up Side		 
         
	IFNE	DragonAlpha	; Turn on drives & NMI
	lbsr	AlphaDskCtl
        stb   	<DPCmdReg	; issue command to controler 
	ELSE
        stb   	<DPCmdReg	; issue command to controler 
        sta   	<DPDskCtl
	ENDC
		 		 
		 
	rts  
		 
;
; Restore saved iO states of peripherals.
;

RestoreSavedIO
	lda   	>DrivSel,u	; Deselect drives, but leave motor on
        ora   	#MotorOn
	IFNE	DragonAlpha	; Turn off drives & NMI
	lbsr	AlphaDskCtl
	ELSE
        sta   	<DPDskCtl
	ENDC
         
	lda   	>SavePIA0CRB,u	; Recover PIA0 state	
        sta   	<DPPia0CRB	
        lda   	<DPPIACRB	; Recover ACIA state
        anda  	#$FC
        sta   	<DPPIACRB	; Disable Drive FIRQ source.
        lda   	>SaveACIACmd,u
        sta   	<DPAciaCmd	
        rts   

* Write
*
* Entry:
*    B  = MSB of the disk's LSN
*    X  = LSB of the disk's LSN
*    Y  = address of path descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
Write   lda   	#$91		; Retry byte
L0124   pshs  	x,b,a
        bsr   	DoWrite		; Attempt to do write
        puls  	x,b,a
        bcs   	WriteDataRetry	; On error, retry
        tst   	<PD.Vfy,y	; Written, should we verify ?
        bne   	WriteDone	; no : return
        lbsr  	WriteVerify	; yes : verify write
        bcs   	WriteDataRetry	; verify error, retry write
WriteDone    
	clrb  			; Return status ok
        rts   
		 
WriteDataRetry    
	lsra  			; Retry data write
        lbeq  	RetWriteError	; All retries exhausted ?, return error
        bcc   	L0124
        pshs  	x,b,a		; Reset to track 0
        lbsr  	ResetTrack0
        puls  	x,b,a
        bra   	L0124
		 
DoWrite lbsr  	SeekTrack	; Seek to correct track & sector
        lbcs  	ReadDataExit
        ldx   	PD.Buf,y	; Get data buffer in X
        pshs  	y,dp,cc
        ldb   	#WritCmnd	; Write command
		 
WriteTrackCmd    
	lbsr  	PrepDiskRW	; Prepare for disk r/w
        lda   	,x+		; get byte to write
L015A   ldb   	<DPPIACRB	; Ready to write ?
        bmi   	WriteDataReady	; Yes, do it.
        leay  	-1,y
        bne   	L015A
        bsr   	RestoreSavedIO	; Restore saved peripheral states
        puls  	y,dp,cc
        lbra  	RetWriteError	; Return write error

WriteDataWait    
	lda   	,x+		; Get next byte to write
        sync  			; Wait for drive
WriteDataReady    
	sta   	<DPDataReg	; Write data to FDC
        ldb   	<DPPIADB	; Clear pending FDC inturrupt
        bra   	WriteDataWait
	

; The following block of code is needed for the Dragon Alpha, because
; we currently do not know how to disable it's NMI in hardware,
; So we check a flag here, and if set we simply return from inturrupt
; as if nothing happened !

NMIService
	IFNE	DragonAlpha		
	pshs	cc		; Save flags
	tst	>NMIFlag,u	; Check NMI enable flag 
	bne	DoNMI		; if enabled, continue with handler
	puls	cc		; else restore flags and return
	RTI
		
DoNMI	puls	cc		; restore flags
	ENDC
	
RealNMI    
	leas  	R$Size,s	; Drop regs from stack
        bsr   	RestoreSavedIO	; Restore saved IO states
        puls  	y,dp,cc
        ldb   	>CmdReg

	stb	DskError,u

	bitb  	#LostMask	; check for lost record
        lbne  	RetReadError	; yes : return read error
        lbra  	TestForError	; esle test for other errors
	 
; Verify a written sector.
WriteVerify    
	pshs  	x,b,a				
        ldx   	PD.Buf,y	; Swap buffer pointers
        pshs  	x
        ldx   	>BuffPtr,u	
        stx   	PD.Buf,y
        ldx   	4,s
        lbsr  	DoReadData	; Read data back in
        puls  	x
        stx   	PD.Buf,y	; Swab buffer pointers back
        bcs   	VerifyEnd
        lda   	#$20
        pshs  	u,y,a
        ldy   	>BuffPtr,u
        tfr   	x,u
VerifyLoop    
	ldx   	,u		; Compare every 4th word
        cmpx  	,y
        bne   	VerifyErrorEnd
        leau  	8,u
        leay  	8,y		; Increment pointers
        dec   	,s
        bne   	VerifyLoop
        bra   	VerifyEndOk	; Verify succeeded.
VerifyErrorEnd    
	orcc  	#Carry		; Flag error
VerifyEndOk    
	puls  	u,y,a
VerifyEnd    
	puls  	pc,x,b,a
;
; Seek to a track
;
SeekTrack
	CLR   	>Settle,U 	; default no settle
        LBSR  	SelectDrive	; select and start correct d
        TSTB
        BNE   	E.Sect 

	TFR   	X,D 
        LDX   	>CDrvTab,U 
        CMPD  	#0		; Skip calculation of track 0
        BEQ   	SeekT1 
        CMPD  	DD.TOT+1,X      ; Has an illegal LSN been
        BLO   	SeekT2 
E.Sect  COMB
        LDB   	#E$Sect 
        RTS
		 
SeekT2  CLR   	,-S            	; Calculate track number 
        SUBD  	PD.T0S,Y 	; subtract no. of sectors in track 0
        BHS   	SeekT4 
        ADDD  	PD.T0S,Y 	; if -ve we are on track 0, so add back on again
	BRA   	SeekT3 
SeekT4  INC   	,S 
        SUBD  	DD.Spt,X 	; sectors per track for rest of disk
        BHS   	SeekT4 		; repeat, until -ve, incrementing track count
        ADDD  	DD.Spt,X 	; re add sectors/track to get sector number on track

; At this point the byte on the top of the stack contains the track
; number, and B contains the sector number on that track.

SeekT3  PULS  	A 		; retrieve track count
        LBSR  	SetWPC         	; set write precompensation
        PSHS  	B 
        LDB   	DD.Fmt,X     	; Is the media double sided ?
        LSRB
        BCC   	SingleSidedDisk	; skip if not
        LDB   	PD.Sid,Y     	; Is the drive double sided ?
        DECB
        BNE   	SetupSideMask 	; yes : deal with it.
        PULS  	B               ; No then its an error
        COMB
        LDB   	#E$BTyp 
        RTS
		 
SetupSideMask	
	BSR   	SetSide		; Media & drive are double sided
	BRA   	SeekT9 

SingleSidedDisk
	clr   	>SideSel,U	; Single sided, make sure sidesel set correctly
	BRA   	SeekT9
		 
SeekT1  clr   	>SideSel,U	; make sure sidesel is always 0 if lsn0
	PSHS  	B 
SeekT9  LDB   	PD.Typ,Y  	; Dragon and Coco disks
        BITB  	#TYP.SBO       	; count sectors from 1 no
        BNE   	SeekT8 
        PULS  	B 
        INCB			; so increment sector number
        BRA   	SeekT11 
SeekT8  PULS  	B		; Count from 0 for other types

SeekT11 STB   	>SecReg 	; Write sector number to controler
        LBSR  	Delay 
        CMPB  	>SecReg 
        BNE   	SeekT11  			

SeekTS  LDB   	<V.Trak,X	; Entry point for SS.WTrk command
        STB   	>TrkReg 
        TST   	>Settle,U	; If settle has been flagged then wait for settle
        BNE   	SeekT5 	 
        CMPA  	<V.Trak,X	; otherwise check if this is  
        BEQ   	SeekT6   	; track number to the last
		 
SeekT5  STA   	<V.Trak,X	; Do the seek
        STA   	>DataReg 	; Write track no to controler
        LDB   	#SeekCmnd 
        ORB   	PD.Stp,Y	; Set Step Rate according to Parameter block
        LBSR  	FDCCommand 
        PSHS  	X 
        LDX   	#$222E		; Wait for head to settle
SeekT7  LEAX  	-1,X 
        BNE   	SeekT7 
        PULS  	X 

SeekT6  CLRB			; return no error to caller
        RTS

; Set Side2 Mask
; A contains the track number on entry
SetSide LSRA			; Get bit 0 into carry & devide track no by 2
	PSHS  	A  
	BCC   	Side0          	; Side 0 if even track no.
        LDA    	#Sid2Sel	; Odd track no. so side 2
	BRA	Side
Side0   CLRA
Side    STA   	>SideSel,U 
        PULS  	A,PC 

;
; Select drive and start drive motors.
; On entry A=drive number.
;

SelectDrive    
	lbsr  	StartMotor	; Start motor
        lda   	<PD.Drv,y	; Check it's a valid drive
        cmpa  	#MaxDrv
        bcs   	SelectDriveValid	; yes : continue
		 
RetErrorBadUnit
        comb  			; Return bad unit error
        ldb   	#E$Unit
        rts   

SelectDriveValid    
	pshs  	x,b,a		; Unit valid so slect it
        sta   	>DrivSel,u	; Get DD table address
        leax  	DrvBeg,u	; Calculate offset into table
        ldb   	#DrvMem
        mul   
        leax  	d,x
        cmpx  	>CDrvTab,u
        beq   	SelectDriveEnd
        stx   	>CDrvTab,u	; Force seek if different drive
        com   	>Settle,u
SelectDriveEnd    
	puls  	pc,x,b,a

;
; Analise device status return.
;

TestForError    
	bitb  	#ErrMask
        beq   	TestErrorEnd
        bitb  	#NotRMask	; not ready
        bne   	RetErrorNotReady
        bitb  	#WPMask		; Write protect
        bne   	RetErrorWP
        bitb  	#RTypMask	; Wrong type ?
        bne   	RetWriteError
        bitb  	#RNFMask	; Record Not found
        bne   	RetErrorSeek
        bitb  	#CRCMask
        bne   	RetErrorCRC
TestErrorEnd   
	clrb  
        rts   
;		 
; Return error code
;

RetErrorNotReady    
	comb  
        ldb   	#E$NotRdy
        rts   
RetErrorWP    
	comb  
        ldb   	#E$WP
        rts   
RetWriteError    	
	comb  
        ldb   	#E$Write
        rts   
RetErrorSeek    
	comb  
        ldb   	#E$Seek
        rts   
RetErrorCRC    
	comb  
        ldb   	#E$CRC
         rts   
RetReadError
	comb  
        ldb   	#E$Read
        rts   
;		 
; Issue a command to FDC and wait till it's ready
;

FDCCommand    
	bsr   	FDCCmd
FDCCmdWait    
	ldb   	>CmdReg		; Poll until not busy
        bitb  	#$01
        beq   	Delay3
	bra	FDCCmdWait

FDCCmdMotorOn    
 	lda   	#MotorOn	; Turn on motor
        ora   	>DrivSel,u

	IFNE	DragonAlpha
	lbsr	AlphaDskCtl
	ELSE
        sta   	>DskCtl
	ENDC

	bsr	SetupVIRQ
		
        stb   	>CmdReg		; Send Command to FDC
        rts   

SetupVIRQ
	pshs	D
        ldd   	>VIRQCnt,pc    	; Get VIRQ initial count value
        std   	>VIRQPak,u      ; Save it
	lda	VIRQInstalled,u	; Installed yet ?
	beq   	DoInsVIRQ       ; Not installed yet, try installing it
SetupVIRQExit
	PULS	D
	rts
	
DoInsVIRQ
	bsr	InsVIRQ
	bra	SetupVIRQExit
		
InsVIRQ pshs	D,y,x
	lda   	#$01           	; Flag drive motor is up to speed
        IFEQ  	Level-1
        sta   	>D.DskTmr
        ELSE
        sta   	<D.MotOn
        ENDC

	ldx   	#$0001         	; Install VIRQ entry
        leay  	>VIRQPak,u      ; Point to packet
        clr   	Vi.Stat,y      	; Reset Status byte
        ldd   	>VIRQCnt,pc    	; Get initial VIRQ count value
        os9   	F$VIRQ         	; Install VIRQ
        bcs  	VIRQOut        	; Error, exit
	inc	VIRQInstalled,u	; Flag it's installed
VIRQOut puls	X,Y,D
        rts   

* IRQ service routine for VIRQ (drive motor time)
* Entry: U=Ptr to VIRQ memory area
IRQSvc  pshs  	a
	lda   	<D.DMAReq
        beq   	L0509
        bsr   	InsVIRQ
        bra   	IRQOut
L0509    
	IFNE	DragonAlpha
	lbsr	AlphaDskCtl
	ELSE
        sta   	>DskCtl
	ENDC

        clr   	u00B5,u
	clr	VIRQInstalled,u
        IFEQ  	Level-1
        clr   	>D.DskTmr
        ELSE
        clr   	<D.MotOn
        ENDC
IRQOut  puls  	pc,a

		 
FDCCmd
	bsr   	FDCCmdMotorOn

; Delay routine !
Delay   lbsr  	Delay2
Delay2  lbsr  	Delay3
Delay3  rts   

* SetStat
*
* Entry:
*    A  = function code
*    Y  = address of path descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
SetStat ldx   	PD.Rgs,y	; Retrieve request
        ldb   	R$B,x
		 
        cmpb  	#SS.Reset	; Restore to track 0.
        beq   	ResetTrack0
        cmpb  	#SS.Wtrk	; Write (format) a track
        beq   	DoWriteTrack
        comb  
        ldb   	#E$UnkSvc
SetStatEnd    
	rts   
;
; Write (format) a track
;

DoWriteTrack    
	lbsr  	SelectDrive	; Select drive
        lda   	R$Y+1,x
        LBSR  	SetSide		; Set Side 2 if appropriate
        LDA   	R$U+1,X 
        BSR   	SetWPC         	; Set WPC by disk type

;L02D5
	ldx   	>CDrvTab,u
        lbsr  	SeekTS		; Move to correct track
        bcs   	SetStatEnd
        ldx   	PD.Rgs,y
        ldx   	R$X,x		
        ldb   	#WtTkCmnd
        pshs  	y,dp,cc
        lbra  	WriteTrackCmd
		 
; Reset track 0
ResetTrack0
	lbsr  	SelectDrive	; Select drive
        ldx   	>CDrvTab,u
        clr   	<V.Trak,x	; Set current track as 0
        lda   	#$05
ResetTrack0Loop    
	ldb   	#StpICmnd	; Step away from track 0 5 times
        pshs  	a
        lbsr  	FDCCommand
        puls  	a
        deca  
        bne   	ResetTrack0Loop
        ldb   	#RestCmnd	; Now issue a restore
        lbra  	FDCCommand
;
;Start drive motors
;

StartMotor    
	pshs  	x,b,a
        lda   	>D.DskTmr	; if timer <> 0 then skip as motor already on
        bne   	MotorsRunning				
        lda   	#MotorOn	; else spin up
		 
        IFNE	DragonAlpha
	bsr	AlphaDskCtl
	ELSE
        sta   	>DskCtl
	ENDC
	 
	ldx   	#$A000		; Wait a little while for motors to get up to speed
StartMotorLoop    
	nop   
        nop   
        leax  	-1,x
        bne   	StartMotorLoop
		 
MotorsRunning
	lbsr	SetupVIRQ
;	lda   	#$F0		; Start external motor timer
;        sta   	>D.DskTmr	; external to driver
        puls  	pc,x,b,a
	
;
; Set Write Precompensation according to media type
;

SetWPC  PSHS  	A,B 
        LDB   	PD.DNS,Y 
        BITB  	#T80Mask      	; Is it 96 tpi drive
        BNE   	SetWP1 
        ASLA                    ; no then double
SetWP1  CMPA  	#32		; WPC needed ?
        BLS   	SetWP2 
        LDA   	>DrivSel,U 
        ORA   	#WPCEn 
        STA   	>DrivSel,U 
SetWP2  PULS  	A,B,PC 


	IFNE	DragonAlpha

; Translate DragonDos Drive select mechinisim to work on Alpha 
; Takes byte that would be output to $FF48, reformats it and 
; outputs to Alpha AY-8912's IO port, which is connected to 
; Drive selects, motor on and enable precomp.
; This code now expects Alpha NMI/DDEN etc codes, as defined
; at top of this file (and dgndefs). The exception to this is
; the drive number is still passed in the bottom 2 bits and
; converted with a lookup table.
; We do not need to preserve the ROM select bit as this code
; operates in RAM only mode.
; Also sets NMIFlag.


DrvTab	FCB		Drive0A,Drive1A,Drive2A,Drive3A

AlphaDskCtl	
	PSHS	x,A,B,CC

	PSHS	A	
	ANDA	#NMIEn		; mask out nmi enable bit
	sta	>NMIFlag,u	; Save it for later use
		
	lda	,s		; Convert drives
	anda	#%00000011	; mask out drive number
	leax	DrvTab,pcr	; point at table
	lda	a,x		; get bitmap
	ldb	,s
	andb	#%11111100	; mask out drive number
	stb	,s
	ora	,s		; recombine
	sta	,s

	bita	#MotorOn	; test motor on ?
	bne	MotorRunning

	clra			; No, turn off other bits.
MotorRunning
	anda	#KnownBits	; Mask out bits we do not know the function of
	sta	,s
		
		
	lda	#AYIOREG	; AY-8912 IO register
	sta	PIA2DB		; Output to PIA
	ldb	#AYREGLatch	; Latch register to modify
	stb	PIA2DA
		
	clr	PIA2DA		; Idle AY
		
	lda	,s+		; Fetch saved Drive Selects
	sta	PIA2DB		; output to PIA
	ldb	#AYWriteReg	; Write value to latched register
	stb	PIA2DA		; Set register

	clr	PIA2DA		; Idle AY
			
	PULS	x,A,B,CC
	RTS

	ENDC
	

ShowReg
	pshs	d,x,y,cc,dp,u
	pshs	x
	leax	RegBuffD,pcr
	bsr	RegDToHex
	
	puls	d
	leax	RegBuffX,pcr
	bsr	RegDToHex
	
	tfr	Y,D
	leax	RegBuffY,pcr
	bsr	RegDToHex

	tfr	u,d
	leax	RegBuffU,pcr
	bsr	RegDToHex

	tfr	s,d
	leax	RegBuffS,pcr
	bsr	RegDToHex
	
	tfr	cc,a
	leax	RegBuffCC,pcr
	bsr	RegAToHex

	tfr	dp,a
	leax	RegBuffDP,pcr
	bsr	RegAToHex

	lda	#1			* stdout
	leax	RegBuff,pcr
	ldy	#RegBuffEnd-RegBuff
	os9	I$write
	
ShowRegEnd
	puls	d,x,y,cc,dp,u
	rts
	
RegDtoHex
	pshs	d
	bsr	RegAToHex
	tfr	b,a
	bsr	RegAToHex
	puls	d
	rts
	
RegAToHex
	pshs	d,y
	
	leay	HexTable,pcr
	tfr	a,b
	anda	#$f0
	lsra	
	lsra
	lsra
	lsra
	lda	a,y
	sta	,x+
	tfr	b,a
	anda	#$0f
	lda	a,y
	sta	,x+
		
	puls	d,y
	rts

RetryErrorDisplay
	pshs	a,x,cc
	
	lda	DskError,u
	leax	ErrCode,pcr
	bsr	RegAToHex
	
	leax	RetryMess,pcr
	bsr	PrintStdOut
	puls	a,x,cc,pc


PrintDot
	pshs	x,cc
	leax	DotMess,pcr
	bsr	PrintStdOut
	puls	x,cc,pc
	

PrintReadDone
	pshs	x,cc
	leax	ReadDoneMess,pcr
	bsr	PrintStdOut
	puls	x,cc,pc

PrintReadDoneFail
	pshs	x,cc
	leax	ReadDoneFailMess,pcr
	bsr	PrintStdOut
	puls	x,cc,pc


PrintStdOut
	pshs	d,x,y,u,cc,dp
	leax	-2,x
	ldy	,x++
	lda	#1
	os9	I$write
	puls	d,x,y,u,cc,dp,pc

PrintCtrlByte
	pshs	a,cc,x
	
	leax	CtrlCode,pcr
	bsr	RegAToHex
	
	leax	CtrlMess,pcr
	ldy	#CtrlMessLen
	bsr	PrintStdOut
	puls	a,cc,x,pc

PrintCmdByte
	pshs	a,cc,x
	
	leax	CmdCode,pcr
	bsr	RegAToHex
	
	leax	CmdMess,pcr
	bsr	PrintStdOut
	puls	a,cc,x,pc
		
PrintNMIMess
	pshs	x,cc
	leax	NMIMess,pcr
	bsr	PrintStdOut
	puls	x,cc,pc


PrintTimeout
	pshs	x,cc
	leax	TimeoutMess,pcr
	bsr	PrintStdOut
	puls	x,cc,pc

RegBuff	
	fcc	"A B  X    Y    U   "
	fcb	C$CR,C$LF
RegBuffD
	fcc	"0000 "
RegBuffX
	fcc	"0000 "
RegBuffY
	fcc	"0000 "
RegBuffU
	fcc	"0000 "
RegBuffS
	fcc	"0000 "
RegBuffCC
	fcc	"00 "
RegBuffDP
	fcc	"00 "
	
	fcb	C$CR,C$LF,0
RegBuffEnd

HexTable
	fcc	"0123456789ABCDEF"
	

	fdb	RetryMessLen
RetryMess
	fcc	"Read error: retry ("
ErrCode fcc	"00)"	
	fcb	C$CR,C$LF,0

RetryMessLen	EQU	*-RetryMess


	fdb	CtrlMessLen
CtrlMess
	fcc	"Control byte ("
CtrlCode fcc	"00)"	
	fcb	C$CR,C$LF,0
CtrlMessLen	EQU	*-CtrlMess



	fdb	CtrlMessLen
CmdMess
	fcc	"Command byte ("
CmdCode fcc	"00)"	
	fcb	C$CR,C$LF,0
CmdMessLen	EQU	*-CmdMess

	fdb	1
DotMess	fcc	"."
	fcb	0
	

	fdb	ReadDoneMessLen
ReadDoneMess
	fcc	"Read Succeded"
	fcb	C$CR,C$LF,0
ReadDoneMessLen	EQU	*-ReadDoneMess

	fdb	ReadDoneFailMessLen
ReadDoneFailMess
	fcc	"Read Failed"
	fcb	C$CR,C$LF,0
ReadDoneFailMessLen	EQU	*-ReadDoneFailMess


	fdb	NMIMessLen
NMIMess
	fcc	"In NMI"
	fcb	C$CR,C$LF,0
NMIMessLen	EQU	*-NMIMess

	fdb	TimeoutMessLen
TimeoutMess
	fcc	"Timout !"
	fcb	C$CR,C$LF,0
TimeoutMessLen	EQU	*-TimeoutMess

        emod
eom     equ   *
        end
