*----------------------------------------------------------------
*
* FLEX 1.0 (MiniFLEX) disk driver for an Altair 680 connected
*    to a Pertec FD3712 through a UIO interface board. This
*    code assumes the PIAs on the UIO board and the FD3712
*    controller have already been configured for operation by
*    the disk boot PROM.
*
*    This file also contains patches to the console drivers
*    in FLEX in order to work wih the Altair 680 instead of
*    the SWTPC console I/O.
*
*    Ver      Date		     Description
*    ---   ----------   -----------------------------------------
*    1.0   05/21/2022	Mike Douglas, Original
*
*    1.1   06/04/2022   Mike Douglas, Use read sector and doCmd
*			from PROM to save space in this code
*			and allow room for FLEX system stack
*			at $7FFF. Fix write error return values.
*
*----------------------------------------------------------------

PROMLOC	equ	$FC00		;boot and I/O PROM
INCH	equ	PROMLOC+3	;input character routine
OUTCH	equ	PROMLOC+6	;output character routine
RDSEC	equ	PROMLOC+9	;read sector routine
DOCMD	equ	PROMLOC+12	;execute 3712 command

ACIA	equ	$F000		;address of console ACIA
STACK	equ	$7FFF		;address for FLEX system stack
	
* 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)

HSOFF	equ	$3C		;auto handshake off
HSAUTO	equ	$2C		;auto handshake on
NUMTRK	equ	77		;number of tracks on disk

*--------------------------------------------------------------
* Disk driver jump table
*--------------------------------------------------------------
	org	$7F00
	jmp	readSec		;read sector
	jmp	wrtSec		;write sector
	jmp	vfySec		;verify sector
	jmp	restore		;restore to track 0
	jmp	drvSel		;select drive

* Local driver data. FLEX cold start clears 8 byte starting here.

curTrk	rmb	1		;current track, else $FF if not known
curDrv	rmb	1		;drive num in select position (bits 7,6)
	rmb	6		;cleared by FLEX startup

*--------------------------------------------------------------
* readSec - Read 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.
*--------------------------------------------------------------
readSec	bsr	seek		;seek and give sector num
	bne	seekErr
	
	jsr	RDSEC		;read sector (in boot PROM)
	bne	readErr		;error
	
return	rts			;exit with zero status (success)

*--------------------------------------------------------------
* seek - Seek to track specified in A and give the 1771 the
*    sector number specified in B.
*--------------------------------------------------------------
seek	psha			;preserve track number
	orab	curDrv		;form B=drive and sector
	ldaa	#cDRVSEC	;select drive and set sector
	jsr	DOCMD

	pulb			;B=requested track
	cmpb	curTrk		;same track we're on?
	beq	return		;yes, do nothing

	cmpb	#NUMTRK		;valid track?
	bhs	seekErr		;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

*--------------------------------------------------------------
* Return 1771 error code in B for a 3712 error
*--------------------------------------------------------------
seekErr	ldab	#$10		;return 1771 seek error
	bra	errExit
	
readErr	ldab	#$08		;return 1771 CRC 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

*--------------------------------------------------------------
* wrtSec - Write 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.
*--------------------------------------------------------------
wrtSec	bsr	seek		;seek and give sector number
	bne	seekErr

	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
	jsr	DOCMD
	bne	wrtErr		;error
	rts			;else, success

*--------------------------------------------------------------
* vfySec - 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.
*--------------------------------------------------------------
vfySec	ldaa	#cRDCRC		;read sector to check CRC
	jsr	DOCMD
	bne	readErr		;verify failed
	rts			;else, exit

*--------------------------------------------------------------
* restore - Restore to track zero
* Entry - (X) = FCB Address
* Exit - CC, NE, B=$0B if write protected
*	 CS, NE, B=$0F if no drive
*	 CC, EQ for success
* Restore to track zero for the drive in the FCB
*--------------------------------------------------------------
restore	bsr	drvSel		;select the drive
	ldaa	#cRESTOR	;issue restore to track 0 command
	jsr	DOCMD
	bne	notRdy		;drive not ready
	
	clr	curTrk		;drive is on track 0
	clrb			;return code is in B
	bita	#sWRTPRT	;write protect?
	beq	resExit		;no
	ldab	#$0B		;else $0B=write protected
resExit	rts

notRdy	ldaa	#cCLRERR	;clear error in the controller
	jsr	DOCMD
	ldab	#$0F		;code for drive not ready
	sec
	rts
	
*--------------------------------------------------------------
* drvSel - Select Drive
* Entry - (X) = FCB Address
* The drive specified in the FCB is selected
*--------------------------------------------------------------
drvSel	ldab	3,x		;A=drive number from FCB
	rorb			;put drive in upper two bits
	rorb
	rorb
	andb	#$C0		;keep drive bits only
	cmpb	curDrv		;different drive?
	bne	dsNew		;yes
	rts

dsNew	stab	curDrv		;curDrv=drive num in select position	
	orab	#1		;include a valid sector number
	ldaa	#cDRVSEC	;select drive/and set sector
	jsr	DOCMD

	ldaa	#$FF		;track number isn't known
	staa	curTrk
	clrb			;status = good
	rts

*--------------------------------------------------------------
* Patches to FLEX to work with the Altair 680
*--------------------------------------------------------------

* Patches to MiniFLEX jump table

	org	$7109		;MiniFLEX INCH and OUTCH
	jmp	INCH		;use INCH in boot PROM
	jmp	OUTCH		;use OUTCH in boot PROM

	org	$7136		;MiniFLEX OUTCH2
	jmp	OUTCH		;use OUTCH in boot PROM

* Patch to the routine that checks for TTYESC while not processing an
*   input request. This is done by directly accessing the status register
*   of the ACIA. This patch puts in our ACIA address.

	org	$72E3		;input status check
	fdb	ACIA

	org	$72E9		;input byte
	fdb	ACIA+1

* Patch to force the check for an ACIA to always be true

	org	$760E		;ACIA presence test
	fcb	$20		;force true (BRA instead of BEQ)

* Patch to put stack in space at the end of this driver

	org	$7145
	lds	#STACK

	org	$715D
	lds	#STACK

* Patch track mask in read sector from $3F to $7F

	org	$7A2F
	fcb	$7F

* Patch track and sector compares in write sector

	org	$7A75
	fcb	76		;max track is 76

	org	$7A79
	fcb	26		;max sector is 26

* Patch to exit to the 680 monitor instead of SWTBUG

	org	$70F5
	fdb	$FFD8		;680 monitor entry point

	end	$7100		;FLEX cold start entry
