view level1/modules/ddisk.asm @ 3246:be3446f758ca

Updated dwread.asm and dwwrite.asm so that the 6551 routines could have specially defined flags so that some of the addresses could be overridden by the make files if needed.
author David Ladd <drencor-xeen@users.sourceforge.net>
date Sun, 11 Mar 2018 01:30:30 -0600
parents 58bb6cac8939
children c505ae3120c4
line wrap: on
line source

********************************************************************
* 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.
*	
* 2005-04-24, P.Harvey-Smith.
*	Removed debugging code/messages.
*
* 2005-05-31, P.Harvey-Smith.
*	Added ability to read, write and format standard OS-9 format disks 
*	including single denity, and disks with track 0 single denisity, but
*	all other tracks double density.
*	
*	Added code to make step rates work as on the rb1773 driver, they where
*	previously working back to front.
*
* 2005-06-06, P.Harvey-Smith.
*	Verified as working on a real Alpha, by Richard Harding.
*
* 2005-06-16, P.Harvey-Smith.
*	Added NMI enable/disable code, as I know know how to enable/disable
*	NMI on the Alpha, having disasembled the Alpha's OS9's ddisk.
*
* 2005-06-17, P.Harvey-Smith.
*	Ok, this'll teach me to submit code before testing on the real hardware !
*	Seperated NMI disable/drive select code on alpha, as above patches
*	worked fine on Mess, but not on real hardware (timing problems).
*
* 2006-01-08, P.Harvey-Smith.
* 	Added support for Dragon 32, that has had a memory upgraded to 64K,
* 	this is treated like the Dragon 64 EXCEPT that the code to manipulate
*	the ACIA is not included. This is required due to the incomplete 
*	address decoding, writes to the non-existant ACIA would hit the PIA 
*	at FF00, and cause a crash.
*
* 2006-01-08, P.Harvey-Smith.
*	Since I now have a genuine Dragon 5.25" drive, found that nitros format
* 	fell over accessing it, this was due to the step rate not being set 
*	correctly in the drive recalibrate routine, I ahve corrected this to
*	use the value in the descriptor.
*

         nam   DDisk
         ttl   Dragon Floppy driver

* Disassembled 02/04/21 22:37:46 by Disasm v1.6 (C) 1988 by RML

         ifp1
         use   defsfile
         endc

		IFNE	dalpha

* 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+SDensEnA+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
DrvTab		RMB   	MaxDrv*DrvMem	; Drive tables, 1 per drive 
CDrvTab	  	rmb   	2	; Pointer to current drive table entry above
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.
Density		rmb	1	; Density 0=double, %00001000=single D64, %00100000=single Alpha

DskError	rmb	1	; hardware disk error	

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


	nop
	nop
	nop
	lbsr	ResetTrack0
* Init
*
* Entry:
*    Y  = address of device descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
DragonDebug	EQU	0
Init    
	IFNE	DragonDebug
	pshs	y		; This is here so I can find disk driver in mess
	ldy	#$AA55		; by setting register breakpoint to y=$AA55 !
	sty	$8000
	puls	y
	ENDC

	clra
	sta	>D.DskTmr	; Zero motor timer
	
	IFNE	dalpha	; Turn off all drives
	lbsr	AlphaDskCtl

	lda	#NMICA2Dis	; Set PIA2 CA2 as output & disable NMI
	sta	PIA2CRA

	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    
	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
	 
;
; Prepare to do disk read/write.
;
	 
PrepDiskRW    

	clr	DskError,u
	
	lda   	#$FF		; Make DP=$FF, to make i/o faster
        tfr   	a,dp
	
;
; Do not attempt to talk to ACIA if this machine does not have one !
;
	
	IFEQ	Upgraded32
        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
	ENDC
		 
L00DE   orcc  	#$50		; Mask inturrupts
        lda   	<DPPia0CRB	; Save PIA0 IRQ Status
        sta   	>SavePIA0CRB,u	
        lda   	#$34		; Disable it.
        sta   	<DPPia0CRB	

	IFEQ	Upgraded32
	lda   	<DPACIACmd	; Disable ACIA Inturrupt
        anda  	#$FE
        sta   	<DPACIACmd	
	ENDC
	
        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
	ora	>Density,u	; mask in density 
	
        ORB   	>SideSel,U 	; Set up Side		 
         
	IFNE	dalpha	; Turn on drives & NMI
	lbsr	AlphaDskCtl
        stb   	<DPCmdReg	; issue command to controler
	lda	#NMICA2En	; Enable NMI
	sta	<DPPIA2CRA
	ELSE
        stb   	<DPCmdReg	; issue command to controler 
        sta   	<DPDskCtl
	ENDC
		 		 
		 
	rts  
		 
;
; Restore saved iO states of peripherals.
;

RestoreSavedIO
	IFNE	dalpha	
	lda	#NMICA2Dis	; Disable NMI (Alpha)
	sta	<DPPIA2CRA
	ENDC

	lda   	>DrivSel,u	; Deselect drives, but leave motor on
        ora   	#MotorOn
	
	IFNE	dalpha	; Turn off drives & NMI
	lbsr	AlphaDskCtl
	ELSE
        sta   	<DPDskCtl
	ENDC
         
	lda   	>SavePIA0CRB,u	; Recover PIA0 state	
        sta   	<DPPia0CRB	
	
        lda   	<DPPIACRB	; Disable Drive FIRQ source.
        anda  	#$FC
        sta   	<DPPIACRB	

	IFEQ	Upgraded32
        lda   	>SaveACIACmd,u	; Recover ACIA state
        sta   	<DPAciaCmd	
	ENDC
	
        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
	

;
; NMI Handler code.
;

NMIService
	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 drive
        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
	LBSR	SetDensity	; Set density
        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	LBSR	SetDensity	; make sure density set correctly even for LSN0 !

	clr   	>SideSel,U	; make sure sidesel is always 0 if lsn0
	PSHS  	B 
SeekT9  LDB   	PD.Typ,Y  	; Dragon and Coco disks
        BITB  	#TYP.CCF       	; count sectors from 1 no
        BEQ   	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
	bsr	GetStepInB	; get step rate
        ORB   	#SeekCmnd 	; add seek command
        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

;
; Get step rate in bottom 2 bits of B
;
GetStepInB
	ldb	PD.Stp,Y	; Set Step Rate according to Parameter block
	andb	#%00000011	; mask in only step rate bits
	eorb	#%00000011	; flip bits to make correct encoding
	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	dalpha
	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	dalpha
	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
	bsr	SetDensity	; Set density
	
;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    
	lbsr	GetStepInB	; Get step rate for this drive
	orb   	#StpICmnd	; Step away from track 0 5 times
        pshs  	a
        lbsr  	FDCCommand
        puls  	a
        deca  
	bne   	ResetTrack0Loop

        lbsr	GetStepInB	; Get step rate for this drive
        orb   	#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	dalpha
	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
;
; Entry :
; 	A =	Track no

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 


;
; Set density acording to disk type.
;
; Entry A = 	Track no
;

SetDensity
	PSHS  	A,B 
	ldb	PD.TYP,Y	; Dragon/CoCo disk ?
	bitb	#TYP.CCF
	bne	SetDouble	; Always double density
	
        LDB   	PD.DNS,Y 	; Get density flags from Path descriptor
        
	bitb	#DNS.MFM	; Disk is MFM ?
	beq	SetSingle	; no : set single density
	
	cmpa	#0		; track 0 ?
	bne	SetDouble	; not track 0, exit
	
	tst	SideSel,u	; is this side 0 ?
	bne	SetDouble	; no : use double density
	
	bitb	#DNS.MFM0	; track 0 mfm ?
	bne	SetDouble
	
SetSingle
	lda	#SDensEn	; flag single density
	sta	Density,u	
	bra	ExitDensity
	
SetDouble
	clr	Density,u	; flag double density
	
ExitDensity	
	puls	a,b,pc

	IFNE	dalpha

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

ADrvTab	FCB		Drive0A,Drive1A,Drive2A,Drive3A

AlphaDskCtl	
	PSHS	x,A,B,CC
	
	PSHS	A	
	anda	#DDosDriveMask	; mask out dragondos format drive number
	leax	ADrvTab,pcr	; point at table
	lda	a,x		; get bitmap
	ldb	,s
	andb	#AlphaCtrlMask	; mask out drive number bits
	stb	,s
	ora	,s		; recombine drive no & ctrl bits
;	sta	,s

	bita	#MotorOn	; test motor on ?
	bne	MotorRunning

	clra			; No, turn off other bits.
MotorRunning
	anda	#Mask58		; Mask out 5/8 bit to force the use of 5.25" clock
	sta	,s	

        orcc  	#$50		; disable inturrupts
		
	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 etc
	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
	

        emod
eom     equ   *
        end