* NEWDISK
*
* DISK FORMATTING PROGRAM FOR FLEX 2.0 and 3.0
*
* MODIFIED FOR THE FD3712 ON AN ALTAIR 680 COMPUTER BY
* MIKE DOUGLAS, NOVEMBER 2022
*

* TEMPORARY STORAGE

TRK16	EQU	$0020		TRACK IN LSB OF WORD
SECTOR	EQU	$0020
TRACK	EQU	$0021
BADCNT	EQU	$0022
DRN	EQU	$0023		DRIVE NUMBER
INDEX1	EQU	$0026
INDEX2	EQU	$0028
INDEX3	EQU	$002A
SECCNT	EQU	$002C
FSTAVL	EQU	$002E
LSTAVL	EQU	$0030
RETRIES	EQU	$0032		RETRY COUNTER

* GENERAL EQUATES USED IN NEWDISK

FIRST	EQU	$0101		FIRST USED SECTOR
FCT	EQU	30		FCB CURRENT TRACK
FCS	EQU	31		FCB CURRENT SECTOR
FSB	EQU	64		FCB SECTOR BUFFER
IRS	EQU	16		INFO RECORD START
AVLP	EQU	FSB+IRS+13
DIRSEC	EQU	5		FIRST DIR. SECTOR
RDSS	EQU	9		READ SS FMS CODE
WTSS	EQU	10		WRITE SS FMS CODE

NUMTRK	EQU	77		NUMBER OF TRACKS
NUMSEC	EQU	13		NUMBER OF SECTORS
SECLEN	EQU	256		LENGTH OF SECTOR

* NOTE: EXPRESSION MATH IS L TO R, NO HEIRARCHY, NO PARENS

TOTSEC	EQU	NUMTRK-1*NUMSEC		TOTAL DATA SECTORS
LASTSEC	EQU	NUMTRK-1*256+NUMSEC	LAST TRACK, SECTOR

* WORK SPACE DEFINITIONS

WORK	EQU	$0100		WORK SPACE
FMSFCB	EQU	$0100		ALSO USED AS AN FCB
LINKTRK	EQU	$0100		FORWARD LINK
LINKSEC	EQU	$0101

* FLEX LOCATIONS

SSTACK	EQU	$A07F
SMONTH	EQU	$AC0E		DOS DATE
SDAYMN	EQU	$AC0F
SYEARN	EQU	$AC10

* FLEX ROUTINES EQUATES

WARMST	EQU	$AD03
GETCHR	EQU	$AD15
PUTCHR	EQU	$AD18
INBUFF	EQU	$AD1B
PSTRNG	EQU	$AD1E
PRCRLF	EQU	$AD24
GETFIL	EQU	$AD2D
OUTDEC	EQU	$AD39
OUTHEX	EQU	$AD3C
GETHEX	EQU	$AD42
INDECM	EQU	$AD48
FMSCAL	EQU	$B406

* DISK DRIVER ROUTINES

DREAD	EQU	$BE80		READ SECTOR
DWRITE	EQU	$BE83		WRITE SECTOR
DRSTOR	EQU	$BE89		SELECT DRIVE, RESTORE TO TRACK 0

	ORG	$A100
*************************************************
* MAIN PROGRAM STARTS HERE
*************************************************

STARTR	BRA	FORM1
VN	FCB	2		VERSION NUMBER

FKFCB	FDB	0,0
VOLNAM	FDB	0,0,0,0,0
	FCB	0
VOLNUM	FDB	0

FORM1	LDS	#SSTACK
	JSR	GETHEX		GET DRIVE NUMBER
	BCC	FORM12		BR IF GOOD NUMBER
	LDX	#0		DEFAULT TO DRIVE 0

FORM12	STX	INDEX3
	LDAA	INDEX3+1
	CMPA	#3		DRIVE 0-3?
	BHI	FORM23		INVALID

	LDX	#FMSFCB
	STAA	$03,X
	STAA	DRN		DRIVE NUMBER
	LDX	#SURES		ARE YOU SURE
	JSR	PSTRNG
	JSR	GETCHR
	CMPA	#'Y
	BNE	FORM23

	LDX	#SCRDS		SCRATCH DISK
	JSR	PSTRNG
	LDX	#FMSFCB+2
	CLR	0,X
	CLRB
	JSR	OUTDEC		PRINT DRIVE NUMBER
	LDAA	#'?
	JSR	PUTCHR
	LDAA	#' 		SPACE
	JSR	PUTCHR
	JSR	GETCHR
	CMPA	#'Y
	BEQ	FORM25

FORM23	JMP	EXIT

FORM25	LDX	#NMSTR		VOLUME NAME
	JSR	PSTRNG
	JSR	INBUFF		GET LINE
	LDX	#FKFCB		FAKE FCB
	JSR	GETFIL

FORM27	LDX	#NUMSTR		VOLUME NUMBER
	JSR	PSTRNG
	JSR	INBUFF
	JSR	INDECM
	BCS	FORM27		GET GOOD VOLUME NUMBER

	STX	VOLNUM
	JSR	PRCRLF

	LDAA	#cRESET		MAKE SURE THE CONTROLLER IS READY
	JSR	DOCMD
	LDX	#FMSFCB		SELECT DISK, RESTORE TO TRACK 0
	JSR	DRSTOR
	BNE	EXIT

************************************************
* ACTUAL FORMAT ROUTINE
*   THE FD3712 CANNOT FORMAT A DISK, SO THIS SECTION
*   JUST WRITES THE ZEROED SECTOR DATA WITH THE LINK
*   TO THE NEXT SECTOR FOR EVERY SECTOR ON THE DISK.
*
	LDX	#WORK		ZERO THE SECTOR BUFFER
	LDAB	#SECLEN
CLRLOOP	CLR	0,X
	INX
	DECB
	BNE	CLRLOOP

* LOOP THROUGH ALL 77 TRACKS AND WRITE SECTORS WITH FORWARD LINKS
*    TO THE NEXT SECTOR IN THE CHAIN

FORMAT	LDX	#MFORMAT	"FORMATTING"
	JSR	PSTRNG
	CLR	TRACK		START ON TRACK ZERO
	
TRKLOOP	LDAB	#2		WRITE EVEN SECTORS FIRST
	
SECLOOP	STAB	SECTOR		SAVE SECTOR NUMBER WE'RE ON
	LDAA	TRACK		A=TRACK FOR FORWARD LINK
	INCB			B=SECTOR FOR FORWARD LINK
	CMPB	#NUMSEC+1	PAST LAST SECTOR ON TRACK?
	BLO	SAVLINK		NO

	INCA			LINK TO SECTOR 1 ON NEXT TRACK
	LDAB	#1
	CMPA	#NUMTRK		REACH END OF DISK?
	BNE	SAVLINK		NOT YET
	CLRA			ELSE ZERO THE LAST FORWARD LINK
	CLRB

SAVLINK	STAA	LINKTRK		SAVE THE FORWARD LINK
	STAB	LINKSEC

	LDX	#WORK		X->SECTOR BUFFER TO WRITE
	LDAA	TRACK		A=TRACK TO WRITE
	LDAB	SECTOR		B=SECTOR TO WRITE
	JSR	DWRITE
	
	LDAB	SECTOR		WRITE EVERY OTHER SECTOR
	ADDB	#2
	CMPB	#NUMSEC+1	PAST SECTOR LAST SECTOR?
	BLO	SECLOOP		NO

	BNE	WNXTTRK		FINISHED ODD SECTORS, TRACK DONE
	LDAB	#1		ELSE, DO ODD SECTORS NEXT
	BRA	SECLOOP

* TRACK WRITTEN, MOVE TO NEXT TRACK

WNXTTRK	LDAA	#'.		;DISPLAY PACIFIER
	JSR	PUTCHR

	INC	TRACK		;MOVE TO NEXT TRACK
	LDAA	TRACK
	CMPA	#NUMTRK
	BNE	TRKLOOP		;DO ANOTHER TRACK
	
	BRA	VERIFY		;DISK IS WRITTEN, GO VERIFY IT

* EXIT ROUTINES

EXIT	LDX	#ABORTS
EXIT2	JSR	PSTRNG
	JMP	WARMST

*************************************************
* DISK VERIFY - READ ALL SECTORS FOR ERRORS

VERIFY	LDX	#MVERIFY	"VERIFYING"
	JSR	PSTRNG
	CLR	TRACK		START ON TRACK ZERO
	
VFYTRK	LDAB	#2		READ EVEN SECTORS FIRST
	
VFYSEC	STAB	SECTOR		SAVE SECTOR WE'RE ON
	LDAA	#3		INIT RETRY COUNTER
	STAA	RETRIES

VFYRTRY	LDX	#WORK		X->WHERE TO PUT DATA
	LDAA	TRACK
	JSR	DREAD
	BEQ	VFYGOOD

	LDAB	SECTOR		SETUP TO RETRY
	DEC	RETRIES		MORE RETRIES?
	BNE	VFYRTRY		YES, TRY AGAIN
	BRA	BADSEC		ELSE, FAIL

VFYGOOD	LDAB	SECTOR		READ EVERY OTHER SECTOR
	ADDB	#2
	CMPB	#NUMSEC+1	PAST LAST SECTOR?
	BLO	VFYSEC		NO

	BNE	VNXTTRK		FINISHED ODD SECTORS, TRACK DONE
	LDAB	#1		ELSE, DO ODD SECTORS NEXT
	BRA	VFYSEC
	
* TRACK VERIFIED, MOVE TO NEXT TRACK

VNXTTRK	LDAA	#'.		DISPLAY PACIFIER
	JSR	PUTCHR

	INC	TRACK		MOVE TO NEXT TRACK
	LDAA	TRACK
	CMPA	#NUMTRK
	BNE	VFYTRK		DO ANOTHER TRACK
	
	BRA	SETUP		DISK VERIFIED, SETUP SYSTEM INFO

* BADSEC - VERIFY FAILED, REPORT AND EXIT

BADSEC	LDX	#MBADSEC	"VERIFY FAILED"
	JSR	PSTRNG

	LDX	#TRK16		FORM 16 BIT TRACK NUMBER
	CLR	0,X		MSB IS ZERO
	CLRB			NO LEADING SPACES
	JSR	OUTDEC		DISPLAY TRACK NUMBER
	JMP	FATAL

* READSS - READ SINGLE SECTOR

READSS	LDX	#FMSFCB		POINT TO FCB
	LDAA	#RDSS		SET UP COMMAND
	STAA	0,X
	JMP	FMSCAL		GO DO IT
	
* WRITSS - WRITE SINGLE SECTOR

WRITSS	LDX	#FMSFCB		POINT TO FCB
	LDAA	#WTSS
	STAA	0,X
	JSR	FMSCAL		GO DO IT
	BEQ	READSS		ERRORS?
	RTS			ERROR RETURN

* SETUP SYSTEM INFORMATION RECORD

SETUP	LDX	#TOTSEC		TOTAL SECTORS
	STX	SECCNT		SAVE IT
	LDX	#LASTSEC
	STX	LSTAVL
	LDX	#FIRST		SET FIRST AVAIL
	STX	FSTAVL
	
	LDX	#FMSFCB		X->FCB
	LDAA	DRN		SET DRIVE NUMBER
	STAA	3,X
	CLR	FCT,X		SET TO TRACK 0
	LDAA	#3		SECTOR 3
	STAA	FCS,X
	JSR	READSS		READ IN SIR SECTION
	BNE	FATAL		ERROR?

	LDX	#FMSFCB		FIX POINTER
	CLR	FSB,X		CLEAR FOWARD LINK
	CLR	FSB+1,X

	LDAA	FSTAVL		ADDR OF 1ST FREE SECTOR
	LDAB	FSTAVL+1
	STAA	AVLP,X		SET IN SIR
	STAB	AVLP+1,X

	LDAA	LSTAVL		ADDR OF LAST FREE SECTOR
	LDAB	LSTAVL+1
	STAA	AVLP+2,X	PUT IN SIR
	STAB	AVLP+3,X

	LDAA	SECCNT		GET TOTAL SECTORS
	LDAB	SECCNT+1
	STAA	AVLP+4,X
	STAB	AVLP+5,X

	LDAA	#NUMSEC		MAX SECTOR NUMBER
	STAA	AVLP+10,X
	LDAA	#NUMTRK-1	MAX TRACK NUMBER
	STAA	AVLP+9,X

	LDAA	SMONTH		DATE
	LDAB	SDAYMN
	STAA	AVLP+6,X
	STAB	AVLP+7,X
	LDAA	SYEARN
	STAA	AVLP+8,X

	LDAB	#13		COPY IN VOLUME NAME & NUMBER
	LDX	#VOLNAM	
	STX	INDEX2
	LDX	#FMSFCB

DOTR33	STX	INDEX1
	LDX	INDEX2
	LDAA	0,X
	INX
	STX	INDEX2
	LDX	INDEX1
	STAA	FSB+IRS,X
	INX
	DECB
	BNE	DOTR33

	JSR	WRITSS		WRITE SIR BACK OUT
	BEQ	DIRINT
	
FATAL	LDX	#FATERS		REPORT FATAL ERROR
	JMP	EXIT2

* INITIALIZE DIRECTORY

DIRINT	LDX	#FMSFCB
	LDAA	#NUMSEC-1	DON'T USE LAST SECTOR ON TRACK 0
	STAA	FCS,X
	JSR	READSS		READ IN SECTOR
	BNE	FATAL		ERRORS?

	LDX	#FMSFCB		RESTORE POINTER
	CLR	FSB,X		CLEAR LINK
	CLR	FSB+1,X
	JSR	WRITSS		WRITE SECTOR BACK
	BNE	FATAL		ERRORS?

* SAVE BOOT ON PHYSICAL 128 BYTE SECTORS 1 AND 2. PHYSICAL SECTOR 1 IS
*    THE 2ND 128 BYTES OF LOGICAL SECTOR 13 (128 BYTE SEC 26 AND 1).
*    PHYSICAL SECTOR 2 IS THE 1ST 128 BYTES OF LOGICAL SECTOR 1.

DOBOOT	LDX	#BOOTIMG-128	POINT TO LOADER OFFSET BY -128
	CLRA			TRACK #0
	LDAB	#13		WRITES PHYSICAL 128 BYTE SECS 26 & 1
	JSR	DWRITE		WRITE THE SECTOR
	BNE	FATAL		ERRORS?

	LDX	#BOOTIMG+128	2ND 128 BYTES
	CLRA			TRACK #0
	LDAB	#1		WRITES PHYSICAL 128 BYTE SECS 2 & 4
	JSR	DWRITE		WRITE THE SECTOR
	BNE	FATAL		ERRORS?

* REPORT TOTAL SECTORS AND EXIT

	LDX	#FMSFCB		SETUP AN FCB
	LDAA	#16		OPEN SIR FUNCTION
	STAA	0,X
	JSR	FMSCAL		OPEN SIR
	BNE	FATAL

	LDAA	#7		GET INFO RECORD FUNCTION
	STAA	0,X
	JSR	FMSCAL		GET 1ST INFO RECORD
	BNE	FATAL

	LDX	#CMPLTE		FORMATTING COMPLETE
	JSR	PSTRNG		REPORT IT
	LDX	#FMSFCB+21	TOTAL IS IN INFO RECORD
	CLRB
	JSR	OUTDEC
	JMP	WARMST		ALL FINISHED!

* STRINGS

MFORMAT	FCC	"FORMATTING"
	FCB	$04
MVERIFY	FCC	"VERIFYING"
	FCB	$04
SURES	FCC	"ARE YOU SURE? "
	FCB	$04	
SCRDS	FCC	"SCRATCH DISK IN DRIVE "
	FCB	$04
FATERS	FCC	"FATAL ERROR --- "
ABORTS	FCC	"FORMATTING ABORTED"
	FCB	$04
MBADSEC	FCC	"VERIFY FAILED ON TRACK "
	FCB	$04
CMPLTE	FCC	"FORMATTING COMPLETE"
	FCB	$0D,$0A
	FCC	"TOTAL SECTORS = "
	FCB	$04
NMSTR	FCC	"VOLUME NAME? "
	FCB	$04
NUMSTR	FCC	"VOLUME NUMBER? "
	FCB	$04

**************************************************************************
*
* FLEX cold boot loader
*   The following code is written to track 0, sectors 1 and 2 by the
*   NEWDISK routine. It is loaded into RAM at $2400 (BOOTLOC) by the
*   boot ROM to then load FLEX. As part of this NEWDISK source code,
*   it is not at $2400 in this file, so a few equates are used to
*   allow assembly here but to run at $2400.
*
**************************************************************************
BOOTLOC	equ	$2400		;runtime address of the loader
BOOTIMG	equ	*		;location of the loader in NEWDISK

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

cREAD	equ	$03		;read sector
cSEEK	equ	$09		;seek to track
cCLRERR	equ	$0B		;clear error flags
cSETTRK	equ	$11		;set track to seek
cDRVSEC	equ	$21		;set unit and sector
cRDBUF	equ	$40		;read buffer
cRESET	equ	$81		;reset controler

* 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
CMDOUT	equ	UIOBASE+$0B	;FDC command out (IC-C port B)
DATAIN	equ	UIOBASE+$09	;FDC data/status in (IC-C port A)
DATAOUT	equ	UIOBASE+$0F	;FDC data out (IC-B port B)

* Start of code

boot	lds	#BOOTLOC-1	;init stack
	bra	loadFlx		;jump to load entry point

bootDat	fcb	0,0,0,0,0,0	;see data section at end
	fcb	0,0,0,0,0  

*------------------------------------------------------------------
* loadFlx - Load FLEX entry point and main loop
*------------------------------------------------------------------
loadFlx	bsr	read1st		;fetch the 1st sector of FLEX.SYS

* recLoop - Record loop for reading an FMS binary file

recLoop	bsr	getByte		;get next byte
	cmpa	#$02		;data record header?
	beq	hdrRec		;yes

	cmpa	#$16		;transfer address header?
	bne	recLoop		;neither, get nextbyte

	bsr	getByte		;get transfer address
	staa	pFlex		;MSB
	bsr	getByte		;LSB
	staa	pFlex+1

	ldx	pFlex		;enter FLEX
	jmp	0,x					

* hdrRec - Record header found. Get load address and length
*   and begin transfer of data.

hdrRec	bsr	getByte		;A=MSB of load address
	psha
	bsr	getByte		;A=LSB of load address
	pulb			;B=MSB of load address
	staa	pDest+1		;save load address
	stab	pDest

	bsr	getByte		;A=byte count
	tab			;B=byte count
	beq	recLoop		;byte count was zero

* movLoop - Move number of bytes specified in B

movLoop pshb
	bsr	getByte		;get next data byte
	pulb
	ldx	pDest		;store in secBuf
	staa	0,x

	inx			;increment pointer
	stx	pDest
	decb			;decrement counter
	bne	movLoop 	;loop for byte count in B

	bra	recLoop		;get next record

*------------------------------------------------------------------
* getByte - Get next byte from sector buffer. Retrieve next
*    sector as needed.
*------------------------------------------------------------------
getByte	ldx	pSource		;X=pointer to sector data
	cpx	#secBuf+SECLEN	;at end of sector?
	beq	nextSec		;yes, go get another sector

	ldaa	0,x		;get next byte, update ptr
	inx
	stx	pSource
	rts

nextSec	ldaa	secBuf		;A=next track num
	ldab	secBuf+1	;B=next sector num
	bsr	readSec		;get next sector
	bra	getByte		;and continue
	
*------------------------------------------------------------------
* Sector read routines
*------------------------------------------------------------------

* diskErr - Clear the error and start over
 
diskErr	ldaa	#cCLRERR	;clear error in the controller
	jsr	DOCMD
	ldaa	#$FF		;force re-seek
	staa	curTrk
	bra	boot		;start over

* read1st - Read first sector of the FLEX system file

read1st	ldaa	dosTrk		;read 1st sector of DOS.SYS
	ldab	dosSec		;fall into bRdSec

* readSec - Read a 256 byte sector with track in A, sector in B

readSec	bsr	seek		;seek to track in A
	bne	diskErr
			
	ldx	#secBuf		;X->sector buffer
	jsr	READSEC		;read 1st physical sector (1-128)
	bne	diskErr 	;error

	ldab	pSec2		;B=second sector to read
	ldaa	#cDRVSEC	;select drive 0 and set sector
	jsr	DOCMD
	jsr	READSEC		;read 2nd physical sector (129-256)
	bne	diskErr 	;error
		
	ldx	#secBuf+4	;data starts at offset 4
	stx	pSource		;pSource->data in secBuf
return	rts

*------------------------------------------------------------------
* seek - Seek to track in A, set sector to hunt from B
*------------------------------------------------------------------
seek	ldx	#secTbl		;X->sector translation table
	decb			;zero index the sector number
	aslb			;two bytes per entry
	stab	TBLOFF		;modify offset in ldx instruction

TBLOFF	equ	*-boot+BOOTLOC+1	;location of offset in ldx 0,x

	ldx	0,x		;X=the two physical sectors
	stx	pSecs		;set pSec1 and pSec2

	psha			;save track
	ldab	pSec1		;B=1st sector to read
	ldaa	#cDRVSEC	;select drive 0 and set sector
	jsr	DOCMD
	pulb			;B=track
	
	cmpb	curTrk		;on correct track already?
	beq	return		;yes

	cmpb	#NUMTRK		;valid track?
	bhs	diskErr		;no

	stab	curTrk		;save new current track
	ldaa	#cSETTRK	;set track in controller
	jsr	DOCMD
	ldaa	#cSEEK		;seek to track
	jmp	DOCMD		;issue command and exit

*--------------------------------------------------------------
* 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 	equ	*-boot+BOOTLOC
	fcb	2,4		;logical sector 1
	fcb	3,5		;logical sector 2
	fcb	6,8
	fcb	7,9
	fcb	10,12
	fcb	11,13
	fcb	14,16
	fcb	15,17
	fcb	18,20
	fcb	19,21
	fcb	22,24
	fcb	23,25		;logical sector 12
	fcb	26,1		;logical sector 13

*------------------------------------------------------------------
* secBuf - The sector buffer immediately follows the boot code.
*   The label is used as an immediate argument for LDX so it needs
*   to be the actual address relative to BOOTLOC ($2400)
*------------------------------------------------------------------
secBuf 	equ	*-boot+BOOTLOC


*------------------------------------------------------------------
* References to boot data items must be at the actual location
*   relative to BOOTLOC ($2400). These data items are present
*   near the start of the boot code (see bData above).
*------------------------------------------------------------------
	org	bootDat-boot+BOOTLOC
dosTrk	rmb	1		;DOS.SYS track and sector
dosSec	rmb	1		;  (filled in by LINK)

curTrk	rmb	1		;current track drive is on
pFlex	rmb	2		;FLEX entry address
pDest	rmb	2		;pointer to destination in memory
pSource	rmb	2		;pointer to data in secBuf

pSecs	equ	*		;physical sector numbers
pSec1	rmb	1		;1st 128 bytes of 256 byte sector
pSec2	rmb	1		;2nd 128 bytes

	END	STARTR

