*--------------------------------------------------------------
*
* FLEX 3.0 disk driver for an Altair 680 connected to a
*    Pertec FD3712 through a UIO interface board. This code
*    code assumes the PIAs on the UIO board and the FD3712
*    controller have already been configured for operation by
*    the disk boot PROM.
*
*    Ver      Date		     Description
*    ---   ----------   ---------------------------------------
*    1.0   11/13/2022	Mike Douglas, Original
*
*--------------------------------------------------------------

PROMLOC	equ	$FC00		;boot and I/O PROM
READSEC	equ	PROMLOC+9	;read sector routine
DOCMD	equ	PROMLOC+12	;execute 3712 command
	
* FD3712 controller commands

cSTATUS	equ	$00		;read status
cREAD	equ	$03		;read sector
cWRITE	equ	$05		;write sector
cRDCRC	equ	$07		;validate sector CRC
cSEEK	equ	$09		;seek to track
cCLRERR	equ	$0B		;clear error flags
cRESTOR	equ	$0D		;seek to track 0
cSETTRK	equ	$11		;set track to seek
cDRVSEC	equ	$21		;set unit and sector
cWRTBUF	equ	$31		;write buffer
cRDBUF	equ	$41		;read buffer
cRESET	equ	$81		;reset the controller

* FD3712 controller status bits

sBUSY	equ	$01		;1=busy
sCRCERR	equ	$08		;1=CRC error
sWRTPRT	equ	$10		;1=write protected
sNOTRDY	equ	$20		;1=drive not ready
sERROR	equ	$29		;any error bit except write protect

* UIO interface board
*   PIA #1 (IC-C) port A is status/data in
*   PIA #1 (IC-C) port B is command out
*   PIA #2 (IC-B) port A is not used
*   PIA #2 (IC-B) port B is data out
*
*   Command bit 0 (the execute bit) comes from the OR of
*   the data-out and data-in handshake lines from the PIAs.
*   CA2 from PIA #1 is the data-in handshake and CB2 from
*   PIA #2 is the data-out handshake.

UIOBASE	equ	$F000	 	;base address of UIO board
P1ACSR	equ	UIOBASE+$08	;control/status for FDC data in
DATAIN	equ	UIOBASE+$09	;FDC data/status in (IC-C port A)
CMDOUT	equ	UIOBASE+$0B	;FDC command out (IC-C port B)
DATAOUT	equ	UIOBASE+$0F	;FDC data out (IC-B port B)

NUMDRV	equ	4		;max number of drives
NUMTRK	equ	77		;number of tracks on disk
SIOCSR	equ	$F000		;console serial port control register

*--------------------------------------------------------------
* Disk driver jump table
*--------------------------------------------------------------
	org	$BE80
	jmp	read		;read sector
	jmp	write		;write sector
	jmp	verify		;verify sector
	jmp	restore		;restore to track 0
	jmp	drvSel		;select drive
	jmp	drvSel		;check for drive ready
	jmp	drvSel		;quick drive ready check
	jmp	exit		;cold start init (does nothing)
	jmp	wInit		;warm start init
	jmp	exit		;seek to track (does nothing)

* Local driver data

curTrk	fcb	$FF		;current track, else $FF if not known
curDrv	fcb	0		;drive num in select position (bits 7,6)
saveX	fdb	0		;save are for X register

pSecs	equ	*		;logical 256 byte sec is 2x128 byte
pSec1	fcb	0		;physical sector for bytes 1-128
pSec2	fcb	0		;physical sector for bytes 129-256

*--------------------------------------------------------------
* wInit - Warm start initialization. The 680 monitor enables
*    console port interrupts and it shouldn't. Make sure
*    the serial port interrupts are off. Next, make sure the
*    disk controller is reset and ready to accept commands.
*--------------------------------------------------------------
wInit	ldaa	#$11		;8N2, interrupts off
	staa	SIOCSR
	ldaa	#cRESET		;reset the 3712 controller
	jmp	DOCMD		;issue reset and return

*--------------------------------------------------------------
* read - Read a Sector
*
* Entry - (X) = Sector Buffer Address
*         (A) = Track Number
*         (B) = Sector Number
*
* The sector referenced by the track and sector number
* is read into the sector buffer. Exits with zero status
* true for success, else, non-zero status and 1771 status
* byte returned in B.
*--------------------------------------------------------------
read	bsr	seek		;seek and give sector num
	bne	seekErr
			
	jsr	READSEC		;read 1st physical sector (1-128)
	bne	readErr		;error

	ldab	pSec2		;B=second sector to read
	bsr	setSec
	jsr	READSEC		;read 2nd physical sector (129-256)
	bne	readErr		;error
exit	rts			;exit with zero status (success)

*--------------------------------------------------------------
* seek - Seek to track specified in A and give the 1771 the
*    sector number specified in B.
*--------------------------------------------------------------
seek	stx	saveX		;convert logical to physical sectors
	ldx	#secTbl		;X->sector translation table
	decb			;zero index the sector number
	aslb			;two bytes per entry
	stab	*+4		;modify offset in ldx instruction
	ldx	0,x		;X=the two physical sectors
	stx	pSecs		;set pSec1 and pSec2
	ldx	saveX		;restore x
	
	psha			;preserve track number
	ldab	pSec1		;B=1st sector to read
	bsr	setSec		;set sector to read/write

	pulb			;B=requested track
	cmpb	curTrk		;same track we're on?
	beq	skExit		;yes, we're done

	cmpb	#NUMTRK		;valid track?
	bhs	skBad		;no
	stab	curTrk		;else save new current track
	beq	seek0		;use restore for track 0, not seek
	
	ldaa	#cSETTRK	;set desired track number
	jsr	DOCMD
	ldaa	#cSEEK		;seek to track
	jmp	DOCMD		;execute and return to caller

seek0	ldaa	#cRESTOR	;issue restore to track 0 command
	jmp	DOCMD		;execute and return to caller
	
skBad	ldab	#$10		;exit non-zero
skExit	rts

* setSec - Give the controller the sector in B

setSec	orab	curDrv		;form B=drive and sector
	ldaa	#cDRVSEC	;select drive and set sector
	jmp	DOCMD

*--------------------------------------------------------------
* Return 1771 error code in B for a 3712 error
*--------------------------------------------------------------
readErr	ldab	#$08		;return 1771 CRC error
	bra	errExit

seekErr	ldab	#$10		;return 1771 seek error
	bra	errExit
	
wrtErr	ldab	#$40		;assume 1771 write protect error
	bita	#sWRTPRT	;is it write protect?
	bne 	errExit		;yes
	ldab	#$10		;else return sector not found
	
errExit	ldaa	#cCLRERR	;clear error in the controller
	jsr	DOCMD
	tstb			;force non-zero status
	rts

*--------------------------------------------------------------
* write - Write a Sector
*
* Entry - (X) = Sector Buffer Address
*         (A) = Track Number
*         (B) = Sector Number
* The content of the Sector Buffer is written to the
* sector specified by the track and sector number. Exits
* with zero status true if success, else, non-zero status
* and 1771 status byte returned in B.
*--------------------------------------------------------------
write	bsr	seek		;seek and give sector number
	bne	seekErr

	bsr	wrtSec		;write 1st of two physical sectors
	bne	wrtErr		;error

	ldab	pSec2		;B=second sector to write
	bsr	setSec
	bsr	wrtSec		;write 2nd of two physical sectors
	bne	wrtErr		;error
	rts			;exit with zero status (success)

* wrtSec - Move 128 bytes to the 3712 buffer and issue write
	
wrtSec	ldaa	#cWRTBUF	;set controller to write buffer mode
	staa	CMDOUT
	ldab	#128		;B=num bytes to transfer
	
wsLoop	ldaa	0,x		;(5) A=next byte from sector buffer
	staa	DATAOUT		;(5) give it to the controller
	inx			;(4)
	decb			;(2)
	bne	wsLoop		;(4)

	ldaa	#cWRITE		;issue write command and exit
	jmp	DOCMD

*--------------------------------------------------------------
* verify - Verify Sector
*    Read the sector just written to verify the CRC. Exits
*    with zero status true if success, else, non-zero status
*    and 1771 status byte returned in B.
*--------------------------------------------------------------
verify	ldab	pSec1		;verify 1st 128 byte
	bsr	setSec
	ldaa	#cRDCRC		;read sector to check CRC
	jsr	DOCMD
	bne	readErr		;verify failed

	ldab	pSec2		;verify 2nd 128 bytes
	bsr	setSec
	ldaa	#cRDCRC		;read sector to check CRC
	jsr	DOCMD
	bne	readErr		;verify failed
	rts			;else, exit with success

*--------------------------------------------------------------
* restore - Restore to track zero
* Entry - (X) = FCB Address
* Exit - Non-zero for error, zero for success
*--------------------------------------------------------------
restore	bsr	drvSel		;select the drive
	bne	resExit		;error

	clr	curTrk		;drive is on track 0
	ldaa	#cRESTOR	;restore to track 0 command
	jsr	DOCMD		;issue command and exit
	bne	seekErr

resExit	rts

*--------------------------------------------------------------
* drvSel - Select Drive
* Entry - (X) = FCB Address
* The drive specified in the FCB is selected. Exit with
*    zero true, carry false for success, else zero false
*    carry set for err. Exit with $0F in B if drive number
*    is invalid.
*--------------------------------------------------------------
drvSel	ldab	#$0F		;invalid drive code
	ldaa	3,x		;valid drive number?
	cmpa	#NUMDRV-1
	bhi	dsErr		;no
	
	rora			;put drive in upper two bits
	rora
	rora
	anda	#$C0		;keep drive bits only
	cmpa	curDrv		;different drive?
	beq	chkRdy		;no, go check ready status

	staa	curDrv		;different drive selected
	oraa	#1		;include a valid sector number
	tab			;DOCMD parameter in B
	ldaa	#cDRVSEC	;select drive/and set sector
	jsr	DOCMD
	ldaa	#$FF		;track number isn't known
	staa	curTrk
	
chkRdy	ldab	DATAIN		;get drive status
	comb			;data in is inverted, sets carry
	bitb	#sNOTRDY	;test ready bit
	bne	dsErr		;exit with error
	clc			;else zero and carry clear
	rts

dsErr	sec
	rts

*--------------------------------------------------------------
* secTbl - Two 128 byte physical sectors make up a 256 byte
*    logical sector. Consecutive sectors cannot be read without
*    an extra disk revolutuon, so the two physical sectors have
*    a sector between them. The sector 26-1 pair is technically
*    consecutive, but there is enough space after sector 26
*    that sector 1 can be read without a revolution slip.
*--------------------------------------------------------------
secTbl	fcb	2,4		;logical sector 1
	fcb	3,5		;logical sector 2
	fcb	6,8		;3
	fcb	7,9		;4
	fcb	10,12		;5
	fcb	11,13		;6
	fcb	14,16		;7
	fcb	15,17		;8
	fcb	18,20		;9
	fcb	19,21		;10
	fcb	22,24		;11
	fcb	23,25		;12
	fcb	26,1		;13

	end
