	NAM	3712UTIL
	OPT	M
	OPT	NOG
*
* PERTEC FD3712 DISK UTILITY FOR ALTAIR 680 CONNECTED TO A
*    A FD3712 THROUGH A UIO INTERFACE BOARD. RUNS AT $5000.
*
DSPBYTE	EQU	$FF6D		;DISPLAY BYTE IN HEX IN MONITOR
INTRPT	EQU	$FFEE		;MONITOR RE-ENTRY POINT
ECHO	EQU	$00F3		;MONITOR ECHO FLAG
*
* 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
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 (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

CR	EQU	$0D
LF	EQU	$0A

	ORG	$5000
	JMP	COLD		;COLD START
	JMP	MENU		;WARM START
*
* INITIALIZE UIO
*
COLD	SEI			;ENSURE INTERRUPTS ARE DISABLED
	LDS	#STACK

	LDAA	#$11		;INIT ACIA FOR 8N2, INTERRUPTS OFF
	STAA	SIOCSR

	LDAA	#DDRENA		;A=ACCESS DATA DIRECTION REGISTER
	STAA	P1ACSR
	CLR	P1ADR		;0=ALL BITS ARE INPUTS
	LDAB	#HSOFF		;HANDSHAKE OFF FOR NOW
	STAB	P1ACSR

	STAB	P2ACSR		;ENABLE ACCESS TO THE DATA REGISTER
	STAB	P1BCSR		;COMMAND PORT	 
	STAB	P2BCSR		;DATA OUT PORT
	CLR	P2ADR		;DATA REG TO 0 BEFORE ENABLING AS OUTPUT
	CLR	P1BDR		;COMMAND PORT
	CLR	P2BDR		;DATA OUT PORT

	STAA	P2ACSR		;ACCESS DATA DIRECTION REGISTERS
	STAA	P1BCSR		;COMMAND PORT
	STAA	P2BCSR		;DATA OUT PORT
	LDAA	#$FF		;$FF=ALL BITS ARE OUTPUTS
	STAA	P2ADR		;NOT USED
	STAA	P1BDR		;COMMAND PORT
	STAA	P2BDR		;DATA OUT PORT

	STAB	P2ACSR		;THIS PORT NOT USED (HANDSHAKE OFF)
	STAB	P1BCSR		;COMMAND PORT (HANDSHAKE OFF)

	LDAB	#HSAUTO		;HANDSHAKE ALWAYS ON FOR DATA PORT
	STAB	P2BCSR

	LDAA	P1ADR		;READ ALL DATA REGISTERS TO CLEAR
	LDAA	P1BDR		;  ANY SPURIOUS INTERRUPS
	LDAA	P2ADR
	LDAA	P2BDR

	LDAA	#CRESET		;RESET THE 3712 CONTROLLER
	JSR	DOCMD
*
* MENU LOOP
*
MENU	LDAA	#CCLRERR	;CLEAR ANY ERROR
	JSR	DOCMD

	LDX	#MMENU		;DISPLAY MENU, GET RESPONSE
	JSR	DSPMSG
	LDAA	SECNUM		;DISPLAY CURRENT SECTOR
	JSR	DSPBYTE
	LDX	#MMENU2
	JSR	DSPMSG
	JSR	INCHAR

	PSHA
	JSR	CRLF
	PULA

	CMPA	#'X		;EXIT?
	BEQ	EXIT

	CLRB			;SELECT DRIVE 0
	CMPA	#'A
	BEQ	SELDRV

	INCB			;SELECT DRIVE 1
	CMPA	#'B
	BEQ	SELDRV

	CMPA	#'H		;HOME
	BEQ	RESTORE

	CMPA	#'R		;READ NEXT SECTOR
	BEQ	READ

	CMPA	#'W		;WRITE SECTOR IN SECNUM
	BEQ	WRITE

	SUBA	#'1		;1-9?
	CMPA	#9
	BCC	MENU		;INVALID
	ADDA	#1		;ELSE, TRACK 1-9
*
* SEEK TO TRACK IN A, RESET SECNUM
*
	TAB
	LDAA	#CSETTRK	;SET DESIRED TRACK
	JSR	DOCMD
	LDAA	#CSEEK		;DO THE SEEK
	JSR	DOCMD
	CLR	SECNUM
	BRA	MENU
*
* EXIT - EXIT TO MONITOR
*
EXIT	LDS	#ECHO		;DUPLICATE PROM MONITOR INITIALIZATION
	CLRB			;   OTHER THAN RESETTING THE ACIA
	PSHB
	PSHB
	JMP	INTRPT		;RE-ENTER MONITOR
*
* SELDRV - SELECT DRIVE IN B
*
SELDRV	RORB			;PUT IN UPPER TWO BITS
	RORB
	RORB
	ANDB	#$C0
	STAB	DRVNUM

	ORAB	#1		;FORM DRIVE WITH VALID SECTOR
	LDAA	#CDRVSEC
	JSR	DOCMD
	BRA	MENU
*
* RESTORE - RESTORE TO TRACK 0, RESTART SECNUM
*
RESTORE	LDAA	#CRESTOR
	JSR	DOCMD
	CLR	SECNUM
	BRA	MENU
*
* READ - READ SECTOR SECNUM INTO RAM AT $1000
*
READ	BSR	READSEC
	BRA	MENU
*
* WRITE - WRITE SECTOR SECNUM FROM RAM AT $1000
*
WRITE	BSR	WRTSEC
	BRA	MENU
*
* READSEC - READ SECTOR SECNUM
*
READSEC	INC	SECNUM		;INCREMENT AND WRAP SECTOR NUMBER
	LDAB	SECNUM
	CMPB	#27
	BNE	RSSECOK
	CLR	SECNUM
	BRA	READSEC

RSSECOK	ORAB	DRVNUM		;FORM DRIVE:SECTOR
	LDAA	#CDRVSEC
	JSR	DOCMD

	LDAA	#CREAD		;ISSUE READ COMMAND
	JSR	DOCMD
	BNE	RSEXIT		;ERROR

	LDX	#$1000		;LOAD INTO RAM AT $1000
	LDAA	#CRDBUF		;SET CONTROLLER TO READ BUFFER MODE
	STAA	CMDOUT
	LDAA	#HSAUTO
	STAA	P1ACSR
	LDAB	#128		;B=NUM BYTES TO TRANSFER

RSLOOP	LDAA	DATAIN		;GET NEXT BYTE FROM FIFO
	COMA			;DATA IN IS INVERTED
	STAA	0,X		;STORE IN SECTOR BUFFER
	INX
	DECB
	BNE	RSLOOP

	LDAA	#HSOFF		;DISABLE AUTO-HANDSHAKE
	STAA	P1ACSR
	CLR	CMDOUT		;RESTORE NORMAL STATUS MODE

	LDAA	$1000
	JSR	DSPBYTE
	LDAA	#$20
	JSR	OUTCHAR
	LDAA	$1001
	JSR	DSPBYTE
	LDAA	#CR
	JSR	OUTCHAR
	LDAA	#LF
	JSR	OUTCHAR

RSEXIT	RTS
*
* WRTSEC - WRITE SECTOR SECNUM FROM RAM AT $1000
*
WRTSEC	LDAB	SECNUM
	ORAB	DRVNUM		;FORM DRIVE:SECTOR
	LDAA	#CDRVSEC
	JSR	DOCMD

	LDX	#$1000		;WRITE FROM RAM AT $1000
	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
	BSR	DOCMD
	RTS
*
* DOCMD - WRITE A TO THE FDC COMMAND REGISTER AND B TO THE
*   FDC DATA REGISTER. WAIT FOR THE COMMAND TO COMPLETE AND
*   RETURN THE STATUS REGISTER IN A. THE STATUS IS PRE-TESTED
*   WITH ALL ERROR BITS (OTHER THAN WRITE PROTECT).
*
DOCMD	STAA	CMDOUT		;GIVE COMMAND BYTE TO CONTROLLER
	STAB	DATAOUT		;WRITE DATA, TOGGLE CMD HANDSHAKE

WTCMD	LDAB	#3		;VERIFY NOT BUSY 3 TIMES IN A ROW
WTCMD2	LDAA	DATAIN		;WAIT FOR NOT BUSY
	ANDA	#SBUSY
	BEQ	WTCMD		;RESTART IF BUSY
	DECB			;VERIFY NOT BUSY 3 TIMES IN A ROW
	BNE	WTCMD2

	LDAA	DATAIN		;DISPLAY STATUS BYTE
	COMA			;DATA IN IS INVERTED
	JSR	DSPBYTE
	BSR	CRLF

	LDAA	DATAIN		;RETURN STATUS
	COMA
	BITA	#SERROR
	RTS
*
* INCHAR - RETURN CHARACTER FROM CONSOLE IN A
*
INCHAR	LDAA	SIOCSR		;WAIT FOR CHARACTER
	LSRA
	BCC	INCHAR

	LDAA	SIODR		;RETURN CHARACTER
	ANDA	#$7F
	RTS
*
* DSPMSG - DISPLAY NULL TERMINATED STRING POINTED TO BY X
*
DSPMSG	LDAA	0,X		;GET NEXT CHARACTER
	BEQ	DMEXIT		;ZERO = END OF STRING
	BSR	OUTCHAR		;ELSE, DISPLAY CHARACTER
	INX
	BRA	DSPMSG
*
* OUTCHAR - OUTPUT CHARACTER FROM A TO CONSOLE
*
OUTCHAR	PSHA			;PRESERVE CHARACTER TO SEND

OCWAIT	LDAA	SIOCSR		;WAIT FOR OK TO XMIT
	BITA	#TDRE
	BEQ	OCWAIT

	PULA			;OUTPUT CHARACTER
	STAA	SIODR
DMEXIT	RTS
*
* CRLF - DISPLAY CR, LF
*
CRLF	LDAA	#CR
	BSR	OUTCHAR
	LDAA	#LF
	BRA	OUTCHAR
*
* MESSAGES
*
MMENU	FCB	CR,LF,LF
	FCC	'=== FD3712 UTILITY ==='
	FCB	CR,LF,LF
	FCC	'A) SELECT DRIVE 0'
	FCB	CR,LF
	FCC	'B) SELECT DRIVE 1'
	FCB	CR,LF
	FCC	'H) HOME (RESTORE TO TRACK 0)'
	FCB	CR,LF
	FCC	'1-9) SEEK TRACK 1-9' 
	FCB	CR,LF
	FCC	'R) READ NEXT SECTOR TO $1000'
	FCB	CR,LF
	FCC	'W) WRITE CURRENT SECTOR FROM $1000'
	FCB	CR,LF
	FCC	'X) EXIT TO MONITOR'
	FCB	CR,LF,LF
	FCC	'(CURRENT SECTOR IS '
	FCB	0

MMENU2	FCC	')'
	FCB	CR,LF,LF
	FCC	'INPUT SELECTION: '
	FCB	0

DRVNUM	FCB	0
SECNUM	FCB	0

	RMB	32
STACK	EQU	*

	END
