	NAM	FD3712
*
* DISK I/O ROUTINES FOR THE ALTAIR 680 AND PERTEC FD3712 DISK 
*    DRIVE TO PROVIDE "WORKSPACE" I/O FOR ALTAIR 680 BASIC AND
*    THE ALTAIR 680 ASSEMBLER/EDITOR PACKAGE.
*
*    THIS CODE ASSUMES THE FLEX BOOT PROM AT $FC00 IS PRESENT
*    AS IT USES CONSOLE I/O AND SOME DISK I/O COMMANDS FROM
*    THAT PROM TO SAVE SPACE IN THIS PROM.
*
*    VER      DATE		     DESCRIPTION
*    ---   ----------   ---------------------------------------
*    1.0   06/01/2022	MIKE DOUGLAS, ORIGINAL
*
	OPT	M		;PUT IN MEMORY
	OPT	NOG		;DON'T EXPAND STRINGS
*
* LOCATION EQUATES FOR THIS PROM
*
PROMLOC	EQU	$FD00		;BASE ADDRESS OF THIS PROM
RAMLOC	EQU	$7FE0		;SHARED PARAMETERS START HERE
*
* PROM MONITOR EQUATES
*
ECHO	EQU	$00F3		;ECHO FLAG
INTRPT	EQU	$FFEE		;MONITOR RE-ENTRY POINT
*
* BOOT PROM EQUATES
*
BOOTLOC	EQU	$FC00		;BOOT AND I/O PROM
INCH	EQU	BOOTLOC+3	;INPUT CHARACTER ROUTINE
OUTCH	EQU	BOOTLOC+6	;OUTPUT CHARACTER ROUTINE
RDSEC	EQU	BOOTLOC+9	;READ SECTOR ROUTINE
DOCMD	EQU	BOOTLOC+12	;EXECUTE 3712 COMMAND
INITPIA	EQU	BOOTLOC+15	;INITIALIZE PIAS ON UIO BOARD
*
* 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
SWRTPRT	EQU	$10		;1=WRITE PROTECTED
SCRCERR	EQU	$08		;1=CRC ERROR
SNOTRDY	EQU	$20		;1=DRIVE NOT READY
SERROR	EQU	$29		;ANY ERROR BIT EXCEPT WRITE PROTECT
*
* DRIVE EQUATES
*
NUMTRK	EQU	77		;NUMBER OF TRACKS ON THE DISK
NUMSEC	EQU	26		;SECTORS PER TRACK
SECLEN	EQU	128
WSTRKS	EQU	7		;NUMBER OF TRACKS IN A WORKSPACE
NUMWS	EQU	11		;NUMBER OF WORKSPACES ON A DISK
*
* UIO INTERFACE BOARD
*   PIA #1 (IC-C) PORT A IS STATUS/DATA IN (INVERTED)
*   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
P1ADR	EQU	UIOBASE+$09	;DATA/DIRECTION P1A
P1BCSR	EQU	UIOBASE+$0A	;CONTROL/STATUS FOR FDC CMD OUT
P1BDR	EQU	UIOBASE+$0B	;DATA/DIRECTION P1B
P2ACSR	EQU	UIOBASE+$0C	;NOT CONNECTED/USED
P2ADR	EQU	UIOBASE+$0D	;DATA/DIRECTION P1A
P2BCSR	EQU	UIOBASE+$0E	;CONTROL/STATUS FOR FDC DATA OUT
P2BDR	EQU	UIOBASE+$0F	;DATA/DIRECTION P1A

CMDOUT	EQU	P1BDR		;FDC COMMAND OUT (IC-C PORT B)
DATAIN	EQU	P1ADR		;FDC DATA/STATUS IN (IC-C PORT A)
DATAOUT	EQU	P2BDR		;FDC DATA OUT (IC-B PORT B)

HSOFF	EQU	$3C		;HANDSHAKE NOT ASSERTED
HSON	EQU	$34		;HANDSHAKE MANUAL ASSERTED
HSAUTO	EQU	$2C		;AUTO HANDSHAKE ON
DDRENA	EQU	$38		;ACCESS DATA DIRECTION REGISTER
*
* ACIA EQUATES
*
SIOCSR	EQU	$F000		;CONTROL/STATUS REGISTER
SIODR	EQU	$F001		;DATA REGISTER
RDRF	EQU	$01		;RECEIVE DATA AVAILABLE
TDRE	EQU	$02		;READY TO TRANSMIT
*
*ASCII EQUATES
*
CR	EQU	$0D
LF	EQU	$0A
*
* THE LOCATION OF DRVNUM, TRKNUM, AND WRTFLAG MUST NOT BE CHANGED.
*    THEY ARE EXTERNALLY VISIBLE PARAMETERS SET BY CALLERS TO
*    READTRK AND WRTTRK.
*
	ORG 	RAMLOC
DRVNUM	RMB	1		;REQUESTED DRIVE IN UPPER 2 BITS 
TRKNUM	RMB	1		;REQUESTED TRACK NUMBER
WRTFLAG	RMB	1		;NON-ZERO IF WRITING

CURDRV	RMB	1		;CURRENT DRIVE NUMBER
CURTRK	RMB	1		;CURRENT TRACK NUMBER
SECNUM	RMB	1		;SECTOR NUMBER WE'RE ON
TRKCNT	RMB	1		;TRACK COUNTER
COMPMSG	RMB	2		;I/O COMPLETE MESSAGE POINTER
	RMB	16		;STACK SPACE
STACK	EQU	*
*
* JUMP TABLE
*
	ORG	PROMLOC
	JMP	LOADWS		;$XX00 LOAD WORK SPACE
	JMP	SAVEWS		;$XX03 SAVE WORK SPACE
	JMP	XFERTRK		;$XX06 TRANSFER A TRACK
	JMP	READSEC		;$XX09 READ A SECTOR
	JMP	WRTSEC		;$XX0C WRITE A SECTOR	
	JMP	DSPMSG		;$XX0F DISPLAY NULL TERMINATED MESSAGE
*
* LOAD AND SAVE WORKSPACE ENTRY POINTS
*
LOADWS	CLR	WRTFLAG		;NOT WRITING
	LDX	#MLOADED	;COMPLETION MESSAGE
	JMP	INITXFR

SAVEWS	LDAA	#$FF		;SET WRITE FLAG TRUE
	STAA	WRTFLAG
	LDX	#MSAVED		;COMPLETION MESSAGE

INITXFR	STX	COMPMSG		;SAVE THE MESSAGE TO SHOW WHEN DONE
	LDS	#STACK		;SWITCH TO OUR STACK
	SEI			;ENSURE INTERRUPTS ARE DISABLED
	LDAA	#$11		;INIT ACIA FOR 8N2, INTERRUPTS OFF
	STAA	SIOCSR

	JSR	INITPIA		;INIT PIAS FOR DISK I/O
	LDAA	#$FF		;FORCE NEW DRIVE SELECT
	STAA	CURDRV

	JSR	GETWS		;GET THE WORKSPACE NUMBER
	BCC	ERROR
*
* XFERWS - TRANSFER THE WORKSPACE (LOAD OR SAVE)
*
XFERWS	CLR	DRVNUM		;ALWAYS USE DRIVE 0
	LDX	#0		;START AT ADDRESS ZERO
	LDAA	#WSTRKS		;INIT TRACK COUNTER
	STAA	TRKCNT

TRKLOOP	BSR	XFERTRK		;TRANSFER A TRACK
	BNE	ERROR		;FAILED
	INC	TRKNUM
	DEC	TRKCNT
	BNE	TRKLOOP		;REPEAT

	LDX	COMPMSG		;X->MESSAGE TO DISPLAY
	BRA	MSGEXIT		;DISPLAY AND EXIT TO MONITOR
*
* XFERTRK - READ OR WRITE AND ENTIRE TRACK TO/FROM BUFFER POINTED
*   TO BY X. PRIOR TO THE TRANSFER, DRVNUM SHOULD BE SET (DRIVE
*   IN UPPER TWO BITS, REST ZERO), TRKNUM (0-76), AND WRTFLAG
*   (0=READ, NOT 0=WRITE). RETURN ZERO TRUE IF SUCCESS, ZERO FALSE
*   FOR ERROR.
*
XFERTRK	LDAB	#1		;START WITH ODD SECTORS
XTLOOP	STAB	SECNUM		;SAVE SECTOR NUMBER

	LDAA	WRTFLAG		;WRITING?
	BNE	WRITE		;YES
	JSR	READSEC		;ELSE, READ
	BRA	XTSTAT
WRITE	JSR	WRTSEC
XTSTAT	BNE	XTERR		;BRANCH IF ERROR

	LDAB	SECNUM		;INCREMENT BY 2 SECTORS EACH TIME
	ADDB	#2
	CMPB	#NUMSEC+1	;REACHED END OF TRACK?
	BCS	XTLOOP		;NOT YET

	BNE	XTEXIT		;EVEN SECTORS DONE, TRACK COMPLETE
	LDAB	#2		;DO EVEN SECTORS
	BRA	XTLOOP

XTEXIT	CLRA			;FORCE ZERO STATUS (SUCCESS)
XTERR	RTS
*
* GETWS - PROMPT FOR AND INPUT A WORKSPACE NUMBER (0-A) TO LOAD/SAVE.
*    RETURNS WITH CARRY SET IF VALID, CARRY CLEAR IF INVALID.
*
GETWS	LDX	#MLOADWS	;PROMPT FOR WORKSPACE, ASSUME LOAD
	LDAA	WRTFLAG		;WRITING?
	BEQ	GETWS1		;NO, READING
	LDX	#MSAVEWS	;ELSE, LOAD SAVE MESSAGE

GETWS1	JSR	DSPMSG
	JSR	INCH		;GET RESPONSE
	JSR	OUTCH		;ECHO
	SUBA	#'0		;SUBTRACT ASCII OFFSET
	CMPA	#10		;0-9?
	BCS	HAVEWS		;YES
	SUBA	#7		;ELSE 'A'-'F' TO A-F

HAVEWS	CMPA	#NUMWS		;VALID?
	BCC	GWEXIT		;NO

	TAB			;MULTIPLY BY 7 TRACKS PER WORKSPACE
	ASLA			;*2
	ASLA			;*4
	ASLA			;*8
	SBA			;*7
	STAA	TRKNUM
	SEC			;CARRY SET = VALID WORKSPACE
GWEXIT	RTS
*
* ERROR, MSGEXIT - DISPLAY ERROR, EXIT TO MONITOR
*
ERROR	LDX	#MERROR		;"ERROR" 

MSGEXIT	JSR	DSPMSG		;DISPLAY ERROR MESSAGE->X

	LDS	#ECHO		;DUPLICATE PROM MONITOR INITIALIZATION
	CLRB			;   OTHER THAN RESETTING THE ACIA
	PSHB
	PSHB
	JMP	INTRPT		;RE-ENTER MONITOR

*
* READSEC - READ SECTOR SPECIFIED IN B INTO BUFFER POINTED
*   TO BY X. DRVNUM AND TRKNUM SPECIFY THE DRIVE AND TRACK
*
READSEC	BSR	SEEK		;SEEK AND GIVE SECTOR NUMBER
	BNE	DISKERR	
	JSR	RDSEC		;USE READ SECTOR IN BOOT PROM
	BNE	DISKERR		;ERROR
RETURN	RTS
*
* SEEK - SEEK TO TRKNUM ON DRIVE DRVNUM AND GIVE THE FD3712
*    THE SECTOR NUMBER SPECIFIED IN B.
*
SEEK	LDAA	DRVNUM		;SEE IF NEW DRIVE SELECTED
	CMPA	CURDRV
	BEQ	SAMEDRV		;NO

	STAA	CURDRV		;SET NEW DRIVE NUMBER
	LDAA	#$FF		;FORCE A SEEK
	STAA	CURTRK

SAMEDRV	ORAB	CURDRV		;FORM DRIVE:SECTOR
	LDAA	#CDRVSEC	;CMD=SELECT DRIVE AND SET SECTOR
	JSR	DOCMD

	LDAB	TRKNUM		;B=REQUESTED TRACK
	CMPB	CURTRK		;SAME TRACK WE'RE ON?
	BEQ	RETURN		;YES, DO NOTHING

	CMPB	#NUMTRK		;VALID TRACK?
	BCC	DISKERR		;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
*
* DISKERR - RESET ERROR IN THE CONTROLLER AND RETURN ERROR CODE IN B 
*
DISKERR	TAB			;SAVE ERROR CODE IN B
	LDAA	#CCLRERR	;CLEAR ERROR IN THE CONTROLLER
	JSR	DOCMD
	TSTB			;FORCE NON-ZERO STATUS
	RTS
*
* WRTSEC - WRITE SECTOR SPECIFIED IN B INTO BUFFER POINTED
*   TO BY X. DRVNUM AND TRKNUM SPECIFY THE DRIVE AND TRACK
*
WRTSEC	BSR	SEEK		;SEEK AND GIVE SECTOR NUMBER
	BNE	DISKERR

	LDAA	#CWRTBUF	;SET CONTROLLER TO WRITE BUFFER MODE
	STAA	CMDOUT
	LDAB	#SECLEN		;B=NUM BYTES TO TRANSFER

WSLOOP	LDAA	0,X		;A=NEXT BYTE FROM SECTOR BUFFER
	STAA	DATAOUT		;GIVE IT TO THE CONTROLLER
	INX
	DECB
	BNE	WSLOOP

	LDAA	#CWRITE		;ISSUE WRITE COMMAND
	JSR	DOCMD
	BNE	DISKERR		;ERROR
	RTS			;ELSE, SUCCESS
*
* DSPMSG - DISPLAY NULL TERMINATED STRING POINTED TO BY X. PREDEDE
*    MESSAGES WITH A CR/LF.
*
DSPMSG	LDAA	#CR		;PRECEDE WITH CR/LF
	JSR	OUTCH
	LDAA	#LF
	JSR	OUTCH

DMLOOP	LDAA	0,X		;GET NEXT CHARACTER
	BEQ	DMEXIT		;ZERO = END OF STRING
	JSR	OUTCH
	INX
	BRA	DMLOOP

DMEXIT	RTS
*
* MESSAGES
*
MLOADWS	FCC	'LOAD (0-A): '
	FCB	0

MSAVEWS	FCC	'SAVE (0-A): '
	FCB	0

MERROR	FCC	'ERROR'
	FCB	0

MLOADED	FCC	'LOADED'
	FCB	0

MSAVED	FCC	'SAVED'
	FCB	0

	END
