;**************************************************************
; Cromemco 16FDC/64FDC CP/M SYSTEM DISK GENERATOR             *
;                                                             *
;REVISION HISTORY                                             *
; 1.07  13 November 2021  M. Eberhard                         *
;   Fix bug introduced in 1.06. Add trim-erase delay after    *
;   writing each track                                        *
; 1.06  11 November 2021  M. Eberhard                         *
;   Format all 31 sectors on 3.5" disks, even though we only  *
;   use 26 sectors/track. (Otherwise track-buffered CP/M      *
;   fails when trying to read tracks during warm boot.)       *
;  1.05  28 May 2021  M. Eberhard                             *
;   Acknowledge support for 64FDC too                         *
; 1.04  9 May 2021  M. Eberhard                               *
;   Rename to be CroGEN. Fix size problem.                    *
; 1.03  8 May 2021  M. Eberhard                               *
;   Validate track number on initial step-in for DRIVID       *
; 1.02  8 May 2021  M. Eberhard                               *
;   More intelligent initial guess as to drive type in DRIVID *
; 1.01  7 May 2021  M. Eberhard                               *
;   Try mini before maxi in RESTOR. Interleave 2:1 on disk    *
;   writes and reads, for disk drives that can't do back-to-  *
;   back sector writes (such as many 5.25" drives)            *
; 1.00  25 April 2021   M. Eberhard                           *
;   Ported from Eberhard's CCS MSYSGEN Version 3.05           *
;**************************************************************
; In the following discussion, "minidisk" means any disk that
; is written with the half-speed data rate (125 bits/sec
; single-density and 250 bits/sec double density), by setting
; the DCMAXI bit in th DCTRL register to 0. This includes
; 40-track 1-sided and 2-sided 5.25" disks, as well as 80-
; track "quad-density" 5.25" disks. All others (3.5" disks,
; "high density" 5.25" disks, and all 8" disks) are "maxi
; disks.
;
; This program always formats the system tracks before writing
; the system to disk. Track 0 is always formatted as single-
; density,128 bytes per sector. Track 1 (on maxi disks) or
; tracks 1 & 2 (on minidisks) are formatted as double-density,
; 256 bytes per sector, even if the disk is otherwise a single-
; density disk, or if the rest of the disk is formatted with a
; different sector size.
;
; 300 RPM disks (3.5" disks and minidisks) are all formatted
; with 15 sectors per track. 360 RPM disks (8" disks and high-
; density 5.25" disks) are formatted with 26 sectors per track.
;
; The double-density system tracks make single-density disks
; bootable with a larger BIOS. Note that the system tracks are
; never read by any other system that was reading or writing
; one of these Cromemco disks. Also note that most single-
; density disks and disk drives can actually handle double-
; density data near the outside diameter of the disk. (PerSci
; 270 and 277 disk drive cannot, however.)
;
; You can specify the source file 3 ways:
; 1. On the command line, specify a CPM.COM files, as created
;    by Digital Research's MOVCPM (and perhaps patched using
;    DDT to install a custom BIOS and boot loader) and then
;    saved to a file with SAVE.
; 2. Already loaded in RAM when CroGEN is executed. The image
;    must be at 900h, where MOVCPM would leave it.
; 3. From another disk, specified here.
;
; You can either write the image to a disk, or you can verify
; the system on a disk. (You will be asked whether you want
; to write or verify the system.)
;
; However, if the system was read from a disk where all system
; tracks were single-density, then the target disks will be
; formatted and written with all tracks single-density too.
; (So that this program can copy the system from "standard"
; CP/M disks too.)
;
; CroGEN can overwrite the CCP and BDOS. In the worst case,
; it needs 25K of free RAM below CP/M's BIOS (through address
; 62FFh). Realistically, this means the system must have at a
; total of least 32K of RAM. CroGEN will abort cleanly with
; an error message if there is insufficent system RAM.
;
; Written to assemble with Digital Research's ASM.COM. Z-80
; instructions are done the hard way, with DS and DW pseudo-
; operations.
;**************************************************************
VERSION	equ	1	;Major revision number
VERSMIN	equ	07	;Minor revision number

FALSE	equ	0
TRUE	equ	not FALSE
;-----------------
;CroGEN Constants
;-----------------
NDRIVES	equ	4	;Number of supported disk drives
DDTBUF	equ	900h	;RAM buffer (where MOVCPM puts CP/M)

MAXTRYS	equ	8	;Retries any error
RESTEP	equ	3	;Number of step-ins before restoring drive
MAXLIN	equ	16	;max input line (should only be 1 chr...)

;Constants for system tracks

;High Data Rate Drives (3.5", HD, and 8" drives)
SYSTK8	equ	2	;Number of system tracks
BSPT8	equ	26	;reading/writing sectors/track
FSPT3	equ	31	;SPT for *formatting* 3.5" disks
			;(reading/writing 3.5" disks uses BSPT8)

;Low data rate drives (40-track minidisks and QD disks)
SYSTK5	equ	3	;Number of system tracks
BSPT5	equ	18	;Sectors per track

BPSSD	equ	128	;128 BPS for SD system tracks
BPSDD	equ	256	;(0 means 256) 256 BPS for DD system tracks

RABC	equ	6	;Byte count for read addr command

DEFSIZ	equ	BPSSD*BSPT8+(SYSTK8-1)*BPSDD*BSPT8
			;files size used when images is already
			;in RAM and it's a verify-only operation
;--------------------------------------------------------------
;Format Buffer Lengths
;Each entry is the maximum number of bytes in the format buffer
;for each disk size and density. The tail end of each track
;(many copies of the track postamble byte) is written  until
;the WD1793 indicates the end of the track (because the sector
;pulse arrives).
;
;These values must be divisible by 4, and the number of track
;postamble characters must be no lower than 4 and no higher
;than 8.
;--------------------------------------------------------------
BL8SD	EQU	4916		;8" SD 128 BPS
BL8DD	EQU	9768		;8" DD 256 BPS
BL5SD	EQU	3080		;5.25" SD 128 BPS
BL5DD	EQU	6048		;5.25" DD 256 BPS
BL3SD	equ	5814		;3.5" DD 256 BPS
BL3DD	equ	11628		;3.5" DD 256 BPS

;These get encoded for the fast track-write loop as follows:
; 1. Divide by 4, since the loop writes 4 bytes per pass
; 2. Subtract the high byte from the result, since the
;    high byte underflow causes an additional 4 bytes to be
;    written
; 3. if this subtraction causes the high byte to decrement
;    then add one back to the result. (This happens here
;    for BL5SD only.)
; Note: this will fail if adding 1 back causes the high byte
; to increment again...

EBL8SD	equ	BL8SD/4-(BL8SD/1024+1)
EBL8DD	equ	BL8DD/4-(BL8DD/1024+1)
EBL5SD	equ	BL5SD/4-(BL5SD/1024+1)+1
EBL5DD	equ	BL5DD/4-(BL5DD/1024+1)
EBL3DD	equ	BL3DD/4-(BL3DD/1024+1)
EBL3SD	equ	BL3SD/4-(BL3SD/1024+1)
EBL3DD	equ	BL3DD/4-(BL3DD/1024+1)

;--------------
; ASCII equates
;--------------
CTRLC	equ	3
BS	equ	8		;Backspace
CR	equ	0Dh
LF	equ	0Ah
DEL	equ	7Fh
;-------------------------
;Page 0 reserved locations
;-------------------------
WBOOT	equ	0		;CP/M warm boot entry
WBOOTA	equ	WBOOT+1		;address of BIOS
CDISK	equ	WBOOT+4		;current disk

FCB	equ	WBOOT+5CH	;CP/M file control blk
FCBDR	equ	FCB		;Drive Descriptor
FCBFN	equ	FCB+1		;File name (8 chrs)
FCBFT	equ	FCB+9		;File Type (3 chrs)
FCBNL	equ	11		;File name length
FCBEXT	equ	FCB+12		;File extent within FCB
FCBCLR	equ	24		;# of bytes to clear,
				;starting at FCBEXT
BDOS	equ	WBOOT+5		;BDOS Entry Point
COMBUF	equ	80h		;MAXLIN bytes for user-input
STACK	equ	COMBUF+80h	;Stack is in the COM buffer
TPA	equ	100h		;transient programming area
;---------------------------------------------------------
;BIOS entry points, relative to the base address in WBOOTA
;---------------------------------------------------------
CONST	equ	06h		;Get console status in a
				;(0=not ready, ffh=ready)
CONIN	equ	09h		;Get console chr into a
CONOUT	equ	0Ch		;Write c to console output
;------------------------------------------
;BDOS Function Codes, passed in register C
;------------------------------------------
;BRESET	equ	0	;System Reset
;BCONIN	equ	1	;Read Console Chr
;BCONOT	equ	2	;Type Chr on Console
;BRDRIN	equ	3	;Read Reader Chr
;BPUNOT	equ	4	;Write Punch Chr
;BPRINT	equ	9	;Print $-terminated String
;BRDCON	equ	10	;Get Line from Console
;BCONST	equ	11	;Console Status (<>0 IF CHR)
;BDRST	equ	13	;Reset Disk
BSDISK	equ	14	;select disk
BOPEN	equ	15	;Disk File Open
BCLOSE	equ	16	;Close disk file, FCB at de
BSERCH	equ	17	;Search dir for file, FCB at de
BDELET	equ	19	;delete file, FCB at (de)
BREAD	equ	20	;Read from Disk, 0=OK, <>0=EOF
BWRITE	equ	21	;Write next record, 0=OK, <>0=ERR
BMAKE	equ	22	;Make new file, 0FFH=BAD
BCDISK	equ	25	;get current disk
BSTDMA	equ	26	;Set disk buffer to (de)
;--------------------
;Other CP/M Constants
;--------------------
CPMBPS	equ	128	;bytes per logical sector
;-----------------------
;Z80 instruction opcodes
;-----------------------
JR	equ	18h
JRNZ	equ	20h
JRZ	equ	28h
JRNC	equ	30h
JRC	equ	38h
DJNZ	equ	10h

;2-byte instructions have bytes reversed for dw

BIT0A	equ	047CBh		;BIT 0,a
BIT4A	equ	067CBh		;BIT 4,a
BIT5A	equ	06FCBh		;BIT 5,a
BIT6A	equ	077CBh		;BIT 6,a
BIT7A	equ	07FCBh		;BIT 7,a
BIT0B	equ	040CBh		;BIT 0,b
BIT1B	equ	048CBh		;BIT 1,b
BIT5B	equ	068CBh		;BIT 5,b
BIT6B	equ	070CBh		;BIT 6,b
BIT7B	equ	078CBh		;BIT 7.b
BIT4C	equ	061CBh		;BIT 4,c
BIT5C	equ	069CBh		;BIT 5,c	
BIT6C	equ	071CBh		;BIT 6,c	
BIT0D	equ	042CBh		;BIT 0,d
BIT5D	equ	06ACBh		;BIT 5,d
BIT6D	equ	072CBh		;BIT 6,d
BIT2M	equ	056CBh		;BIT 2,(hl)
BIT3M	equ	05ECBh		;BIT 3,(hl)
BIT4M	equ	066CBh		;BIT 4,(hl)
BIT5M	equ	06ECBh		;BIT 5,(hl)
BIT6M	equ	076CBh		;BIT 6,(hl)
BIT7M	equ	07ECBh		;BIT 7,(hl)

SET3B	equ	0D8CBh		;SET 3,b
SET4M	equ	0E6CBh		;SET 4,(hl)
SET6M	equ	0F6CBh		;SET 6,(hl)
SET7M	equ	0FECBh		;SET 7,(hl)

RES4B	equ	0A0CBh		;RES 4,b
RES7E	equ	0BBCBh		;RES 7,e
RES4M	equ	0A6CBh		;RES 4,(HL)
RES5M	equ	0AECBh		;RES 5,(hl)
RES6M	equ	0B6CBh		;RES 6,(hl)
RES7M	equ	0BECBh		;RES 7,(hl)

LDIR	equ	0B0EDh
CPIR	equ	0B1EDh
ZCPI	equ	0A1EDh		;Z80 CPI
CPD	equ	0A9EDh

OUTCA	equ	079EDh		;OUT (c),a
OUTCB	equ	041EDh		;OUT (c),b
OUTCH	equ	061EDh		;OUT c,h
OUTCL	equ	069EDh		;OUT c,l

INI	equ	0A2EDh
OUTI	equ	0A3EDh
INIR	equ	0B2EDh
OTIR	equ	0B3EDh
LDED	equ	05BEDh		;LD de,(nn)
SBCD	equ	043EDh		;LD (nn),bc
RLCC	equ	001CBh		;RLC c
;------------------------------------------
;16FDC/64FDC floppy disk controller equates
;------------------------------------------
ADCMD	equ	4		;Control port 2
ASTAT	equ	4		;status port 2
DSTAT	equ	30h		;disk status port
DCMMD	equ	DSTAT		;disk command port
DTRCK	equ	DSTAT+1		;track port
DSCTR	equ	DSTAT+2		;sector port
DDATA	equ	DSTAT+3		;data port
DFLAG	equ	DSTAT+4		;disk flag port
DCTRL	equ	DSTAT+4		;disk control port

;WD1793 commands

RSTRCM	equ	00000000b	;(I) restore
SEEKCM	equ	00010000b	;(I) seek
STEPCM	equ	00100000b	;(I) step
STPICM	equ	01000000b	;(I) step-in
STPOCM	equ	01100000b	;(I) step-out
RSECCM	equ	10000000b	;(II) read sector
WSECCM	equ	10100000b	;(II) write sector
RADRCM	equ	11000000b	;(III) read address
RTRKCM	equ	11100000b	;(III) read track
WTRKCM	equ	11110000b	;(III) write track
FINTCM	equ	11010000b	;(IV) force interrupt

;WD1793 command options (1st letter matches WD1793 data sheet)

RS03MS	equ	00000000b	;(I) 3 mS step rate
RS06MS	equ	00000001b	;(I) 6 mS step rate
RS10MS	equ	00000010b	;(I) 10 mS step rate
RS15MS	equ	00000011b	;(I) 15 mS step rate
VERTRK	equ	00000100b	;(I) verify on destination track
HDLOAD	equ	00001000b	;(I) head load
UPDTTR	equ	00010000b	;(I) update track reg
F1SCMP	equ	00000010b	;(II)enable side compare
F2SCS1	equ	00001000b	;(II)side compare, side 1
MULTCM	equ	00010000b	;(II) read/write multiple
ED15MS	equ	00000100b	;(II & III) settling delay

;WD1793 step rates (note: Minidisk mode halves the clock rate):
;
;  value   5.25"      3.5" & 8"
; RS03MS    6 mS        3 MS
; RS06MS   12 mS        6 MS
; RS10MS   20 mS       10 MS
; RS15MS   30 mS       15 MS

STEP35	equ	RS03MS		;3.5" drive step rate
STEP5	equ	RS10MS		;SA400L Minidisk rate=20 mS
				;(SA450 is the same...)
STEP5D	equ	RS03MS		;2-sided minidisk rate=6 mS
STEP5Q	equ	RS03MS		;Quad-density 5.25" rate=6 mS
STEP5H	equ	RS03MS		;5.25" HD 5.25" rate=3 mS
STEP8	equ	RS10MS		;SA800 8" 1-sided rate=8 mS
STEP8D	equ	RS03MS		;SA851 8" 2-sided rate=3 mS

;Composite WD1793 commands used in this program

CCRSTOR	equ	RSTRCM+HDLOAD+STEP5	;restore command
CCSEEK	equ	SEEKCM+HDLOAD+STEP5	;seek command, no verify
CCSTEPI	equ	STPICM+HDLOAD+UPDTTR+STEP5	;step-in command
CCWTRAC	EQU	WTRKCM+ED15MS		;write track command
CCRDADD	equ	RADRCM+ED15MS		;read address command

					;These assume side 0:
CCRDSEC	equ	RSECCM+F1SCMP		;read sector command
CCWRSEC	equ	WSECCM+F1SCMP		;write sector command

;Disk Control Register (DCTRL) output bits

DCDS1	equ	00000001b	;drive select 1
DCDS2	equ	00000010b	;drive select 2
DCDS3	equ	00000100b	;drive select 3
DCDS4	equ	00001000b	;drive select 4
DCMAXI	equ	00010000b	;0=Minidisk, 1=8"/3.5" disk
DCMDMO	equ	00100000b	;Minidisk motor on
DCDDEN	equ	01000000b	;double density enable
DCAUTOW	equ	10000000b	;autowait

;Auxiliary Disk Command (ADCMD) output bits
;(All bits are active-low)

ADSIDE0	equ	00000010b	;0=side 1, 1=side 0
ADCTRL0	equ	00000100b	;-Control Out (test)
ADRSTOR	equ	00001000b	;-Restore (PerSci, 16FDC only)
ADFSEKN	equ	00010000b	;-Fast Seek (PerSci, 16FDC only)
ADDSO	equ	00100000b	;-Drive Select Override (test)
ADEJECT	equ	01000000b	;-Remote eject (PerSci, 16FDC only)

;Composite ADCMD values used to select disk side

SIDE0	equ	0FFh		 ;Needs to be <>0
SIDE1	equ	0FFh xor ADSIDE0 ;Needs to be <>0

;WD1793 status (DSTAT) after a command

SBUSY	equ	00000001b	;(All) busy
SCRCERR	equ	00001000b	;(All) CRC error
SWRPROT	equ	01000000b	;(All) write protect
SNOTRDY	equ	10000000b	;(All) drive not ready
SINDEX	equ	00000010b	;(I) index
STRACK0	equ	00000100b	;(I) track 0
SSEEKER	equ	00010000b	;(I) seek error
SHEADLD	equ	00100000b	;(I) head loaded
SWFAULT	EQU	00100000B	;(Write cmds) write fault
SDATRQ	equ	00000010b	;(II & III) data request state
SLOSTD	equ	00000100b	;(II & III) lost data error
SRNFER	equ	00010000b	;(II & III) record not found
SRCTYF	equ	00100000b	;(II & III) record type fault
SWFALT	equ	00100000b	;(II & III) Write fault

;Status register 1 (DFLAG) input bits

DEOJ	equ	00000001b	;WD1793 INTRQ signal, active high
DAWTO	equ	00000010b	;Autowait Timeout
DMOTO	equ	00000100b	;Motor Timeout
DMOTON	equ	00001000b	;Motor On
DINHIN	equ	00010000b	;-Inhibit Init
DFSHLD	equ	00100000b	;WD1793 HLD, active high
DFAUTOB	equ	01000000b	;autoboot jumper, 0=autoboot
DFSDRQ	equ	10000000b	;WD1793 DRQ signal, 1=ready

;Auxiliary Status Register (ASTAT) input bits

BSW8	equ	00000001b	;Switch 8
BSW7	equ	00000010b	;Switch 7
BSW6	equ	00000100b	;Switch 6
BSW5	equ	00001000b	;Switch 5
BSIP	equ	01000000b	;Seek in Progress (PerSci, 16FDC only)
BRTC	equ	10000000b	;512 m,Sec reat-time clock

;*****************
;Beginning of Code
;*****************
	org TPA

	lxi	sp,STACK	;local stack
;------------------------------------------------------
;Print sign-on banner, load OS file if one is specified
;on the command line. Return with b=1 if a source file
;was loaded via the command line, b=0 otherwise
;------------------------------------------------------
	call	INIT
	dcr	b		;1 means we loaded CP/M already
	db	JRZ,SREPRT-$-1
;--------------------------------------------------
;Get valid source drive from user. If the user just
;types CR, then skip source read, using RAM image
;--------------------------------------------------
GETSRC:	call	CILPRT
	db	CR,LF,'Source (A-','A'+ NDRIVES -1
	db	', Return uses RAM image, Q quits):',' '+80h

	call	GETDRV		;Sets up DCTRL
				;e=ASCII disk number
	db	JRC,(GETSRC-$-1)xor 0FF00h ;Bogus?

	db	JRZ,GETDST-$-1	;no input: use RAM image
;-----------------------
;Ask for the source disk
;-----------------------
	call	CILPRT
	db	'Put sourc','e'+80h

	call	WATDSK		;wait for user
	db	JRNZ,(GETSRC-$-1)xor 0FF00h ;Bogus?
;----------------------------------------------------
;Read source disk into RAM buffer, and report results
;----------------------------------------------------
	lxi	h,CPMBUF	;destination
	mvi	d,CCRDSEC	;Read command
	call	RWCPM		;go read entire file
				;returns hl=last address+1

	cc	RDERR		;read error? Try again.
	db	JRC,(GETSRC-$-1)xor 0FF00h

	lxi	d,-CPMBUF	;calculate bytes loaded
	dad	d
;-----------------------------------------------------------
;Report and remember the size of the source file, and report
;the number of retries required to read the source file
;(unless the source file was specified on the command line).
;hl=source file byte count (either the size of the file read
;by the command line or the size of the system tracks read
;by RWCPM above)
;-----------------------------------------------------------
SREPRT:	shld	VBCNT		;verify byte count

	call	CILPRT
	db	'Bytes read:',' '+80h
	call	PHLHEX		;byte count

	call	PRTRYS		;Print read retries, if any

;-----------------------------------------
;Get valid destination drive from the user
;-----------------------------------------
GETDST:	call	CILPRT
	db	'Destination (A-','A'+ NDRIVES -1
	db	', ^C boots, Q quits):',' '+80h

	call	GETDRV		;Sets up DCTRL
				;e=ASCII drive letter
	db	JRZ,(GETDST-$-1)xor 0FF00h ;CR is invalid
	db	JRC,(GETDST-$-1)xor 0FF00h ;Bogus?
;------------------------------------------
;Ask whether we should verify after writing
;e=ASCII destination disk from GETDRV
;------------------------------------------
GETVER:	call	CILPRT
	db	'Write or verify (W/V)?',' '+80h

	call	GETLIN
	sui	'V'
	cpi	'W'-'V'+1
	db	JRNC,(GETVER-$-1)xor 0FF00h ;Bogus?
	push	psw		;a=0 for verify, 1 for write

;--------------------------------
;Ask for the target disk
;e=ASCII disk number for WATDSK
;top-of-stack = 0 for verify-only
;--------------------------------
WATDD:	call	CILPRT
	db	'Put destinatio','n'+80h

	call	WATDSK		;wait for user
	db	JRNZ,(WATDD-$-1)xor 0FF00h ;Bogus?

	pop	psw		;a=0 for verify, 1 for write
	ora	a		;Verify only?
	db	JRZ,VERIFY-$-1
;------------------------
;Format the system tracks
;------------------------
	call	CILPRT
	db	'Formatting',' '+80h

	lda	SDFLAG		;Single density disk?
	ora	a
	db	JRZ,PSTKS-$-1

	call	ILPRNT
	db	'single-density',' '+80h

PSTKS:	call	ILPRNT	
	db	'system track','s'+80h

	call	FORMAT
	cc	FMTERR
	jc	GETDST
;----------------------------------
;Write CP/M from the buffer to disk
;----------------------------------
WRITE:	call	CILPRT
	db	'Writin','g'+80h

	lxi	h,CPMBUF	;destination
	mvi	d,CCWRSEC	;indicate write

	call	RWCPM		;go write entire file
				;returns hl=last address+1

	cc	WRERR		;write error?
	jc	GETDST

	lxi	b,-CPMBUF	;calculate bytes written
	dad	b		;hl=bytes written
	shld	VBCNT		;verify actual bytes written

	call	CILPRT
	db	'Bytes written:',' '+80h

	call	PHLHEX		;byte count
	call	PRTRYS		;print retries, if any
;-----------------------------------
;Read back what we wrote into the
;verify buffer, so we can verify it.
;-----------------------------------
VERIFY:	call	CILPRT
	db	'Verifyin','g'+80h

	lxi	h,VERBUF	;put image in verify buffer
	mvi	d,CCRDSEC	;indicate read
	call	RWCPM		;go read entire file again
				;returns hl=last address+1
	cc	RDERR
	db	JRC,VERDON-$-1
;-----------------------------------------------
;Compare read-back image the the write image
;Report either a complete match or the byte
;position within the image of the first mismatch
;-----------------------------------------------
	lhld	VBCNT		;Verify byte count
	xchg			;byte count to de
	lxi	b,CPMBUF	;compare source
	lxi	h,VERBUF	;compare destination

	call	CILPRT
	db	'Verify',' '+80h

VERLUP:	ldax	b
	cmp	m
	db	JRNZ,VERERR-$-1

	inx	b
	inx	h
	dcx	d
	mov	a,d
	ora	e
	db	JRNZ,(VERLUP-$-1)xor 0FF00h

	call	ILPRNT
	db	'O','K'+80h

VERDON:	call	PRTRYS		;Print retries, if any
	jmp	GETDST

;Verify mismatch. Report and end verify.
;  bc=RAM address of error

VERERR:	lxi	h,-CPMBUF	;compute byte location
	dad	b

	call	ILPRNT
	db	'fail at:',' '+80h

	call	PHLHEX

	db	JR,(VERDON-$-1)xor 0FF00h

;***Fail Subroutine******
;Report write error
;On Exit:
;  Error message printed
;  Carry set
;************************
WRERR:	call	CILPRT
	db	'Writ','e'+80h

	db	JR,FAIL-$-1

;***Fail Subroutine******
;Report format error
;On Exit:
;  Error message printed
;  Carry set
;************************
FMTERR:	call	CILPRT
	db	'Forma','t'+80h

	db	JR,FAIL-$-1

;***Fail Subroutine******
;Report verify read error
;On Exit:
;  Error message printed
;  Carry set
;************************
RDERR:	call	CILPRT
	db	'Rea','d'+80h

;Fall into FAIL

;***Fail Subroutine******
;Report failure
;On Exit:
;  Error message printed
;  Carry set
;************************
FAIL:	call	ILPRNT	;Returns with carry set
	db	' faile','d'+80h

	ret

;***Subroutine*******************************************
;Format the system tracks:
; If SDFLAG=0
;   Track 0: SD, 128 BPS
;   Track 1: DD, 256 BPS
;   Track 2: DD, 256 BPS (minidisks only)
; If SDFLAG<>0
;   all tracks SD, 128 BPS
;On Exit:
;  CURSPT = BSPT8 for high data rate drives,
;         = BSPT5 for low data rate drives
;********************************************************
FORMAT:	xra	a
	sta	CURSID		;always side 0
	call	DRIVID		;restore to track 0
	rc			;restore error?

;The head is on track 0 and CURTRK=0
;Side 0 is selected on ADCMD
;DCTRL and SAVDCL set up for low-or high-data rate drive
;FMTSPT set up for formatting
;-------------------------------------------
;Look up correct format table: low or high
;data rate, SD for track 0 or for an SD disk
;-------------------------------------------
SDFLUP:	lxi	h,T8SD
	lda	SAVDCL
	ani	DCMAXI
	db	JRNZ,SDFL1-$-1
	lxi	h,T5SD
SDFL1:

	mvi	a,BPSSD		;BPS for SD tracks
;---------------------------------------------
;  a=BPS (80h for 128 or 0 or 256 BPS)
;  hl=address of format table
;  SAVDCL=DCTRL value
;  CURTRK=current track
;  CURSID=0
;  head is on CURTRK
;Loop to format each system track
;---------------------------------------------
DDFLUP:	sta	CURBPS		;BPS value (80h or 0)

;Put encoded BPS in place for PATFIL
;0=128 BPS, 1=256BPS
	xri	80h
	rlc			;0 or 1
	sta	TRKBPS

;----------------------------------------------
;hl=beginning of actual pattern in format table
;Write the track header into the buffer
;----------------------------------------------
	lxi	d,FMTBUF	;de=format buffer
	call	PATFIL

	xra	a		;Start with sector 1
;--------------------------------------------------------------
;  a=sector number minus 1
;  de=beginning address for sector in format buffer
;  hl=address of sector description in the format table
;  SAVDCL has drive, disk size, density, and motor-on set up
;  DCTRL is set up with SAVDCL
;  FMTSPT=sectors per track
;        =FSPT3 if 3.5" drive (the only odd SPT value)
;  CURTRK=track number
;  CURSID=current side (always 0)
;  CURBPS=80h for track 0, 0 otherwise
;  
;For each sector, write into the buffer:
;  The sector preamble
;  The track number
;  The side number (always 0)
;  The sector number
;  The sector size (0 or 1)
;  128 or 256 bytes of the data fill byte
;  The sector postamble
;--------------------------------------------------------------
	push	h		;sector data within format table

DOSECT:	inr	a
	sta	CURSEC		;Current sector number

	pop	h		;Sector data within format table
	push	h
;-------------------------------------------------
;Write sector preamble up through the address mark
;-------------------------------------------------
	call	PATFIL
;--------------------------------------------
;  de=next address in format buffer
;Install sector identification in its header:
;  track = CURTRK
;  side = CURSID = 0 always
;  sector = CURSEC
;  size = TRKBPS
;--------------------------------------------
	push	h		;Format table pointer

	lxi	h,CURTRK	;source
	lxi	b,4		;4 variables in a row

	dw	LDIR		;copy 4 bytes

	pop	h		;Format table pointer
;-------------------------------------------
;Write the post-address mark sector preamble
;-------------------------------------------
	call	PATFIL
;----------------------------------------------
;Loop to fill the data field in the buffer with
;BPS copies of the byte value at hl
;----------------------------------------------
	lda	CURBPS		;0 means 256, 80h means 128
	mov	b,a
	mov	a,m		;Data field fill byte

DODATA:	stax	d
	inx	d
	db	DJNZ,(DODATA-$-1)xor 0FF00h

	inx	h		;done with fill byte
;------------------------------------------
;Write the sector postamble into the buffer
;------------------------------------------
	call	PATFIL
;------------------------------------------------------------
;Next sector, unless we just did the last sector in the track
;------------------------------------------------------------
	lda	FMTSPT		;Last sector?
	mov	b,a
	lda	CURSEC
	cmp	b
	db	JRC,(DOSECT-$-1)xor 0FF00h ;a=current sector
;----------------------------------------------
;Get the track-end values from the format table
;----------------------------------------------
	mov	c,m		;c=track postamble character

;Check for special case 3.5" drive, which uses the 8" drive
;tables, but uses the alternate encoded buffer length that's
;at the end of the table.
;  b=FMTSPT
	dw	BIT0B		;FSPT3 is the only odd number
	db	JRZ,NOT35-$-1
	inx	h		;3.5" so use alternate values
	inx	h
NOT35:

	inx	h
	mov	a,m		;get encoded length/4
	inx	h
	mov	h,m
	mov	l,a		;hl=encoded buffer length/4
;--------------------------------------------------------------
;All sectors on this track are in the buffer
;  c=track postamble character
;  de points to beginning of the track end gap in the buffer
;  hl=encoded buffer length/4
;  SAVDCL=MINI/MAXI + MOTOR ON + SD/DD + DRIVE SELECT
;  CURTRK=Current track
;
;Write 8 copies of the track postamble character into the
;format buffer. This may exceed the buffer end by as many as 4
;bytes, but guarantees that the buffer has at least 4 copies of
;the postamble character at its end. This does not represent
;the entire postamble - the rest will be written by a loop at
;the end ofthe track format that writes the postamble byte
;until an inde pulse is encountered.
;--------------------------------------------------------------
	xchg			;de=encoded quad-byte count
				;hl=end gap position in the buffer
	mvi	b,8
	
PSTLUP:	mov	m,c
	inx	h
	db	DJNZ,(PSTLUP-$-1)xor 0FF00h
;----------------------------------------------
;Buffer is complete: Set up to format the track
;  b=0
;----------------------------------------------
	pop	h		;toss format table sector pointer
	mov	h,b		;b=0
	mov	l,b		;now hl=0

	mvi	c,DDATA		;for OUTC instructions

	in	DDATA		;Flush WD1793 read buffer

	lda	SAVDCL		;enable auto-wait
	ori	DCAUTOW
	out	DCTRL
;------------------------------------------------------------
;Save the real stack pointer, and set up SP as a pointer into
;the format buffer, so we can pop the data from the buffer
;and send it to the hungry WD1793 as quickly as possible
;------------------------------------------------------------
	dad	SP
	shld	SPSAVE

	lxi	sp,FMTBUF
;----------------------------
;Initiate track write command
;----------------------------
	mvi	a,CCWTRAC	;track-write command
	out	DCMMD		;(11)
;----------------------------------------------------------
;  c=DDATA port address
;  de=cleverly-encoded quad-byte count
;  sp = address format buffer
;  SPSAVE=original sp
;  DCTRL set up with auto-wait
;Fast loop to write an entire track from the format buffer
;
;According Western Digital's June, 1980 FD179x Application
;Notes the 1793 requires 11.5 uS (worst case) service time
;for writes to a DD 8" disk. This loop requires 33 cycles
;(worst case) between writes. Thus, this program cannot
;reliably format DD tracks on an 8" disk at 2 MHz.
;----------------------------------------------------------
	jmp	WTLENT	;(10)Enter loop at the right spot

WTLUP:	in	DFLAG	;(11) autowait
	dw	OUTCH	;(12) write byte 4

WTLENT:	pop	h	;(10) get bytes 1 & 2
	
	in	DFLAG	;(11) autowait
	dw	OUTCL	;(12) write byte 1
	dcr	e	;(4)

	in	DFLAG	;(11) autowait
	dw	OUTCH	;(12) write byte 2
	pop	h	;(10)get bytes 3 & 4

	in	DFLAG	;(11) autowait
	dw	OUTCL	;(12) write byte 3
	jnz	WTLUP	;(10)	

;bump the high byte counter and test for underflow.

	in	DFLAG	;(11) autowait
	dw	OUTCH	;(12) write byte 4
	pop	h	;(10) get bytes 1 & 2

	in	DFLAG	;(11) autowait
	dw	OUTCL	;(12) write byte 1
	dcr	d	;(4) bump high byte of quad-byte count

	in	DFLAG	;(11) autowait
	dw	OUTCH	;(12) write byte 2
	pop	h	;(10)get bytes 3 & 4

	in	DFLAG	;(11) autowait
	dw	OUTCL	;(12) write byte 3
	jp	WTLUP	;(10) continue unless d underflowed	
;--------------------------------------------------------
;  l=postamble byte
;  c=DDATA
;Write track postamble byte until the WD1793 sets its
;intereupt request, indicating that a sector pulse was
;detected. Worst-case timing: 23 cycles out-to-out.
;(Fast enough for DD 8" disks at 2 MHz.)
;This may write a few extra postamble characters, which
;is okay.
;--------------------------------------------------------
POSTLP:	in	DFLAG	;(11) autowait
	dw	OUTCL	;(12)

	in	DFLAG	;(11) Read status, autowait
	dw	OUTCL	;(12)
	rar		;(4) Test interrupt bit

	in	DFLAG	;(11) autowait
	dw	OUTCL	;(12)
	jnc	POSTLP	;(10) Continue sending until int
;--------------------------------------
;Restore the stack and check for errors
;--------------------------------------
	lhld	SPSAVE
	sphl

	in	DSTAT		;Final status
	ani	SWRPROT+SWFAULT+SLOSTD

	jnz	ETERR		;fail: report error
;------------------------------------
;Next track: All tracks after track 0
;are DD unless SDFLAG is set
;------------------------------------
	lxi	h,SYSTKS	;number of system tracks
	mov	a,m
	inx	h		;point to CURTRK
	inr	m		;next track
	cmp	m		;done?
	rz

	mvi	c,CCSTEPI	;Step in one track
	call	T1DCMD

	lda	SDFLAG		;all single-density?
	ora	a
	jnz	SDFLUP		;y:set up again for SD

	lxi	h,T8DD		;double-density table
	lda	SAVDCL
	ori	DCDDEN		;double density now
	sta	SAVDCL
	ani	DCMAXI
	db	JRNZ,DDFL1-$-1
	lxi	h,T5DD		;minidisk DD table
DDFL1:

	mvi	a,BPSDD and 0FFh ;double-density tracks now

	jmp	DDFLUP		;do another track

;***Subroutine************************************************
;Fill the track buffer according to the plan at hl. The plan
;comprises pairs of bytes: the first byte of each pair is the
;number of times to write the second byte into the format
;buffer. Exit when the first byte is 0.
;On Entery:
;  de points to the target address in the format buffer
;  hl points to the plan to use
;On Exit:
;  de points to the next space in the format buffer
;  hl points past the last byte of the plan
;Trashes psw,b
;*************************************************************
PATFIL:	mov	a,m		;# of copies - 0 means done
	inx	h
	ora	a		;Done?
	rz

	mov	b,a		;b gets copy count
	mov	a,m		;a=fill byte
	inx	h

;Write b copies of a starting at de

FILLUP:	stax	d
	inx	d
	db	DJNZ,(FILLUP-$-1)xor 0FF00h

	db	JR,(PATFIL-$-1)xor 0FF00h

;***Subroutine*******************************************
;Read or write entire CP/M image
;On Entry:
;  hl = DMA address
;  d = WD1793 read or write command (CCRDSEC or CCWRSEC)
;  CURSPT = SPT for track read or write
;  SAVDCL = DCTRL value
;On Exit:
;  ADCMD is written for side 0
;  Carry set if error
;  hl=next address after the read or written image
;Trashes a,bc,de
;********************************************************
RWCPM:	xra	a
	sta	CRETRY		;clear cumulative retry counter
	sta	CRETRY+1	;..16-bit counter

	push	h
	call	DRIVID		;select disk, restore to track 0
	pop	h		;..and select side 0
	rc			;restore error?

	mvi	e,BPSSD		;BPS for track 0
;----------------------------------------------
;Read or write an entire track: Loop to read or
;write CURSPT sectors, starting with sector 1
;  d=read or write command
;  e=BPS = 128 or 0 (for 256)
;  hl=beginning of this track in RAM
;  CURSPT=SPT
;  Drive is selected and restored
;----------------------------------------------
TRKLUP:	mvi	c,1		;initial sector number

	lda	CURSPT
	inr	a		;round up

INTLUP:	ora	a		;clear carry
	rar			;SPT/2
	mov	b,a		;sector loop count

;b=sector loop counter
;c=next expected sector
;d=read or write command
;e=BPS
;hl=DMA address
;top-of-stack=beginning of this track in RAM
;Note: for back-to-back sector read/writes, the next DCMMD
;must not be later than about 800 uS max from end of the
;previous sector.

SECLUP:	mvi	a,MAXTRYS+1	;reset R/W retry counter
	sta	RETRYS		;..for the sector read/write

SRETRY:	push	b		;sector & loop count
	push	h		;Buffer address for retry	

	mov	b,e		;BPS
	mov	a,c		;sector number
	out	DSCTR		;WD1793 sector port

	call	RWPSEC		;read or write physical sector
				;returns hl=next buffer address	

	jnz	RWSERR		;retry on error

	pop	b		;chuck retry address
	pop	b		;sector & loop count

	mov	a,c		;just finish sector 1?
	dcr	a
	db	JRNZ,SLUP1-$-1
	shld	EVNSEC		;y: remember address for evens
SLUP1:
	shld	NXTSEC		;remember end too

;Good sector read/write. Next physical sector
;with 2:1 interleave

	inr	c		;Next Sector
	inr	c		;2:1 interleave

;skip a sector in the buffer too.

	mov	a,e		;256 BPS is encoded as 0
	ora	a
	mov	a,d		;temp
	mvi	d,0
	db	JRNZ,SLUP2-$-1
	inr	d		;de=BPS
SLUP2:	dad	d		;skip a sector in buffer
	mov	d,a		;recover d

	db	DJNZ,(SECLUP-$-1)XOR 0FF00h ;More sectors?

	mov	a,c		;done with even sectors?
	rrc			;carry clear if done

	lda	CURSPT
	db	JRNC,SLUP3-$-1	;y:done with track

	mvi	c,2		;n: do the evens now
	lhld	EVNSEC		;start for even sectors

	db	JR,(INTLUP-$-1)XOR 0FF00h	
SLUP3:
;-----------------------------------------------------
;Stall for 768 uS, more than enough for for trim-erase
;  b=0
;-----------------------------------------------------
TESTAL:	db	DJNZ,(TESTAL-$-1) xor 0FF00h	;(12)
;-----------------------------------------------
;Next track until we are done
;a=CURSPT
;hl=next address after the last written/read+BPS
;-----------------------------------------------

;Calculate next track's starting address
;if SPT is odd, then next address=hl
;if SPT is even then next address=NXTSEC

	rar
	db	JRC,ODDSPT-$-1	;odd SPT?
	lhld	NXTSEC
ODDSPT:

;hl=buffer address

	push	h
	lxi	h,CURTRK
	inr	m
	mov	a,m
	dcx	h		;point to SYSTKS
	cmp	m		;all done?

	pop	h		;hl=address for next track
	rz			;done? carry clear

;All further tracks are double-density
;256 BPS, unless SDFLAG is set

	push	h
	lxi	h,SDFLAG
	mov	a,m
	ora	a
	db	JRNZ,RWSD-$-1

	inx	h		;point to SAVDCL
	dw	SET6M		;set DCDDEN
	mov	a,m
	out	DCTRL

	mvi	e,BPSDD and 0FFh ;double density BPS

RWSD:	pop	h

;Step in one track, and have the WD1793 verify the correct track

	mvi	c,CCSTEPI+VERTRK ;Step in one track, verify
	call	T1DCMD		;trashes c
	db	JRZ,(TRKLUP-$-1)xor 0FF00h

;If it's a CRC error on a read, then try single density,
;so we can pull CP/M from a pure-single-density disk

	ani	SCRCERR
	db	JRNZ,SKERR-$-1

	mov	a,d		;read?
	cpi	CCRDSEC
	db	JRNZ,SKERR-$-1	;n: hard error

	push	h
	lxi	h,SDFLAG
	inr	m		;SDFLAG<>0

	inx	h		;point to SAVDCL
	dw	RES6M		;clear DCDDEN
	mov	a,m
	out	DCTRL

	pop	h

	mvi	e,BPSSD		;single-density BPS

	lda	CURTRK		;seek the track we are on
	out	DDATA		;...to try again to verify
	mvi	c,CCSEEK+VERTRK
	call	T1DCMD		;trashes c
	db	JRZ,(TRKLUP-$-1)xor 0FF00h

SKERR:	call	SEKERR		;prints 'Seek failed'
	db	JR,TKERR-$-1	;print track number
;----------------------------------------------------
;Retry the same sector if not too many retries aready
;On Entry:
;  a=error code from DSTAT
;----------------------------------------------------
RWSERR:	lhld	CRETRY		;bump cumulative retry counter
	inx	h
	shld	CRETRY

	pop	h		;retry RAM address
	pop	b		;sector & loop count

	dw	BIT7A		;carry=SNOTRDY
	db	JRNZ,DNRERR-$-1

	dw	BIT6A		;carry=Write protect
	db	JRNZ,WPERR-$-1

	push	h	
	lxi	h,RETRYS	;bump & test sector retry counter
	dcr	m
	pop	h
	jnz	SRETRY		;too many retries?

;---Fail--------------------------------
;Too many retries on a sector read/write
;On Entry:
;  a=error code from DSTAT
;  CURTRK=track
;  c=sector
;  one item still on stack
;On Exit:
;  Error message printed
;  Carry set
;---------------------------------------
	push	psw		;error code
	mov	d,c		;sector

	call	CILPRT
	db	'Sector:',' '+80h

	mov	a,d
	call	PAHEX
	pop	psw

ETERR:	push	psw		;error code
	call	CILPRT
	db	'Error:',' '+80h
	pop	psw
	call	PAHEX		;trashes bc

TKERR:	call	CILPRT
	db	'Track:',' '+80h

	lda	CURTRK
	call	PAHEX

	stc			;error flag
	ret
;---Fail----------------
;Write protected disk
;On Exit:
;  Error message printed
;  Carry set
;-----------------------
WPERR:	call	CILPRT
	db	'Write protecte','d'+80h

	stc			;error flag
	ret

;---Fail----------------
;Drive not ready
;On Exit:
;  Error message printed
;  Carry set
;-----------------------
DNRERR:	call	CILPRT		;returns with carry set
	db	'Drive not read','y'+80h

	stc
	ret

;***Subroutine************************
;Print cumulative retries, if non-zero
;On Entry:
;  CRETRY = cumulative retries
;Trashes psw,bc
;*************************************
PRTRYS:	push	h
	lhld	CRETRY		;any retries?
	mov	a,h
	ora	l
	db	JRZ,PCWRE-$-1	;n: done

	call	CILPRT
	db	'Retries:',' '+80h

	call	PHLHEX

PCWRE:	pop	h
	ret

;***Subroutine****************************************
;Read or write one physical sector
;On Entry:
;  b=BPS (max is 256, encoded as 0)
;  d = WD1793 command
;  hl=DMA address
;  SAVDCL=DCTRL value for current drive
;On Exit:
;  a=0 & Z set on success
;  a=error flags & Z clear on fail
;  hl=next address in RAM
;Trashes bc
;Note: at least 96 uS available from DCMMD to 1st data
;*****************************************************
RWPSEC:	lda	SAVDCL		;(13)select

	ori	DCAUTOW		;(7)enable auto-wait
	out	DCTRL		;(11)output DCTRL value

	mvi	c,DDATA		;(7)Port address

	mov	a,d		;(4)command
	out	DCMMD		;(11)disk command port

	cpi	CCWRSEC		;(7)read/TESTID or write?
	db	JRNZ,RDPSEC-$-1	;(12)

;Fall into WRPSEC

;***Subroutine***********************************
;Write one physical sector
;Fast as possible: 37 cycles out-to-out.
;Too slow for an 8"DD disk with a 2 MHz CPU.
;On Entry:
;  write sector command has been written do DCMMD
;  b=byte count (max is 256 BPS encoded as 0)
;  c=DDATA (port address)
;  hl=DMA address
;On Exit:
; Z set and a=0 if success
; Z clear and a=error flags if error detected
;  hl=next DMA address
;Trashes bc
;Stack depth: 0
;************************************************
WRPSEC:	in	DFLAG	;(11) autowait
	DW	OUTI	;(16)
	db	JRNZ,(WRPSEC-$-1) xor 0FF00h
			;(12) z set/cleared by dcr b
	
;Check for all the possible errors during a sector write

	mvi c,SNOTRDY+SRNFER+SCRCERR+SLOSTD+SWFALT+SWRPROT

	db	JR,EORW-$-1	;wait for completion,
				;get WD1793 status
				;return from there

;***Subroutine*****************************************
;Read one physical sector
;Fast as possible: 37 cycles in-to-in.
;Too slow for an 8"DD disk with a 2 MHz CPU.
;On Entry:
;  read sector or read address command has been
;     issued to the WD1793 (DCMMD)
;  b=byte count (max is 256 BPS encoded as 0)
;  b=RABC (=6) for read address command
;  c=DDATA (port address)
;  hl=DMA address
;On Exit:
; Z set and a=0 if success
; Z clear and a=error flags if error detected
;  hl=next DMA address
;Trashes bc
;Stack depth: 0
;*****************************************************
RDPSEC:	in	DFLAG	;(11) autowait
	dw	INI	;(16)
	db	JRNZ,(RDPSEC-$-1) xor 0FF00h
			;(12)

;Check for all the possible errors during a sector read
;or a read address

	mvi	c,SNOTRDY+SRNFER+SCRCERR+SLOSTD+SRCTYF

; Fall into EORW with b=0 and c=error mask

;***Subroutine***************************************
;Wait for read/write command to complete
;timeout after 35/4*256=2.1 mS causes drive not ready
;On Entry:
;  b = 0
;  c = error mask
;On Exit:
; Z set and a=0 if success
; Z clear and a=error flags if error detected
;trashes a,b
;****************************************************
EORW:	in	DFLAG			;(11)Wait for interupt bit
	rrc				;(4)1793 DEOJ flag set?
	db	JRC,EORW1-$-1	 	;(7)y: command complete

	db	DJNZ,(EORW-$-1)xor 0FF00h ;(13)n:keep waiting

;Controller failure: read/write/ID command did not complete

	mvi	a,SNOTRDY
	ora	a
	ret

EORW1:	in	DSTAT			;disk status, clear int
	ana	c			;error mask
	ret

;***Subroutine************************
;Test the ID from the current track
;On Entry:
; SAVDCL is set up for double-density
;On Exit:
; z set if success
; Track ID at IDSAVE
; hl=SAVDCL
;Trashes b
;DCMAXI in DCTRL may have been cleared
;*************************************
TESTID:	lxi	h,SAVDCL
	call	READID
	rz

;Double-density failed. Try single density

	dw	RES6M		;clear DCDDEN

;Fall into READID

;***Subroutine***********************
;Read the ID from the current track
;On Entry:
; SAVDCL=DCTRL value
;On Exit:
; z set if success
; Track ID at IDSAVE
;Trashes a, bc
;************************************
READID:	push	h
	push	d
	mvi	b,RABC		;set up to read 6 bytes
	lxi	h,IDSAVE	;put 'read addr' data here
	mvi	d,CCRDADD	;Read addr w/ head load delay
	call	RWPSEC		;On return, Z means good read
	pop	d
	pop	h

	ret
;==============================================================
;Head Movement Routines: Seeks and Restores
;==============================================================
;***Subroutine***********************************************
;Restore selected disk. Determine whether it was written at
;thhe high data rate (DCMAXI set) or the mini data rate, and
;set up accordingly. 
;On Entry:
;   SAVDCL set up  with select bits
;On Exit:
;  Drive is selected
;  CURTRK=0 and head is on track 0
;  Side 0 selected on ADCMD
;  CURSPT and SYSTKS set up for 8" or minidisk
;  SAVDCL set up for detected disk type, single-density
;  carry set if drive not ready or track 0 not found
;    (error message printed) 
;Trashes bc,hl
;************************************************************
DRIVID:	mvi	a,SIDE0		;set side 0
	out	ADCMD

	mov	b,a		;delay timer

	lxi	h,SAVDCL	;Select bits, motor on

	mov	a,m
	ori	DCDDEN+DCMAXI	;double density, MAXI
	mov	m,a
				;single-density, not MAXI
	out	DCTRL		;select drive (no autowait)

;Restore and then step to track (RESTEP)

	call	RESTOR		;start at track 0
	rc			;track 0 error?

	mvi	b,RESTEP	;number of tracks

DRVID1:	mvi	c,CCSTEPI	;loop to step in
	call	T1DCMD
	db	DJNZ,(DRVID1-$-1)xor 0FF00h

	ani	STRACK0		;still on track 0?
	db	JRNZ,T0ERR-$-1	;y: error

;If the drive is not ready, switch to mini mode.
;The 16FDS always asserts Ready id DCMAXI=0.
;hl=SAVDCL

	in	DSTAT		;disk status
	rlc			;SNOTRDY --> carry
	db	JRNC,DRVID3-$-1	;ready: probably maxi

DRVID2:	dw	RES4M		;clear DCMAXI in SAVDCL
DRVID3:

;Try reading an ID on track 3 at
;the current DCMAXI setting
	call	TESTID		;will try both SD and DD
				;returns hl=SAVDCL
	db	JRZ,DRVID4-$-1	;success?

	dw	SET6M		;set DCDDEN again

	dw	BIT4M		;was that DCMAXI?
	db	JRNZ,(DRVID2-$-1) xor 0FF00h
				;maxi failed. Try mini.

;ID fail. Print error and return

	call	CILPRT		;returns with carry set
	db	'Disk I','D'+80h
	db	JR,GOFAIL-$-1	;Prints 'failed'


;ID read success. Verify correct track 
;hl=SAVDCL

DRVID4:	lda	IDTRAC
	cpi	RESTEP
	db	JRNZ,SEKERR-$-1

;Restore to track 0, to route the index signal to
;the WD1793 status register, DSTAT
	call	RESTOR

;Set up for the correct drive type

	dw	RES6M		;DCDDEN off again
	dw	BIT4M		;DCMAXI?	
	lxi	h,SYSTK5*256+BSPT5
	mov	a,l		;N: FMTSPT=CURSPT
	db	JRZ,DRVID6-$-1	;N: low data rate drive

;------------------------------------------------------
;High data rate drive. Time the index pulse to
;determine whether or not this is a 300 RPM 3.5" drive.
;(Accuracy is not important.) Wait for a leading edge,
;then time to the next edge.
; The above Restore command routed the disk drive's
;   index signal to the WD1793's DSTAT register
;----------------------------------------------------
	lxi	b,3*256+0FFh	;b=loop counter
				;c=inverter

TINDX1:	mov	h,b		;hl is the timer

;Time the index. 55 cycles per pass:
;First find a rising edge, then time
;to the next falling edge
	
TINDX2:	inx	h		;(6)Bump 16-bit timer
	mov	a,h		;(4)
	ora	l		;(4)Timeout?
	db	JRZ,INDTO-$-1	;(7)y: error

	in	DSTAT		;(11)
	xra	c		;(4)maybe invert

	ani	SINDEX		;(7)active-low signal
	db	JRNZ,(TINDX2-$-1) XOR 0FF00h
				;(12)

	mov	a,c		;look for the other
	cma			;..index state next time
	mov	c,a	

	db	DJNZ,(TINDX1-$-1) XOR 0FF00h

	mov	a,h
;---------------------------------------------------
;Look at the high byte of the measured index rate to
;determine both the CPU speed and the disk's RPM. 
;  a is index time in units of 55*256 cycles
;Nominal de values:
;               2 MHz           4 MHz
; 360 RPM    6061 (a=17h)    12,121 (a=2Fh)
; 300 RPM    7272 (a=1Ch)    14,544 (a=38h)
;---------------------------------------------------
	cpi	14h		;Crazy slow?
	db	JRC,INDTO-$-1	;Y: error

	lxi	h,SYSTK8*256+BSPT8
				;CURSPT & SYSTKS are the
				;..same for 3.5" & 8"

	cpi	25h		;C set if 2MHz
	db	JRNC,DRVID5-$-1	;2MHz or 4 MHz CPU?
	rlc			;times 2 if 2 MHz
DRVID5:	cpi	33h		;carry set if 360 RPM

	mov	a,l		;8": FMTSPT=CURSPT
	db	JRC,DRVID6-$-1	;jump if 8"

	mvi	a,FSPT3		;3.5" FMTSPT is different

; a=FMTSPT (sectors per track while formatting)
; h=SYSTKS (number of system tracks)
; l=CURSPT (sectors per track while reading/writing)
DRVID6:	shld	CURSPT		;also saves SYSTKS
	sta	FMTSPT
	xra	a		;success
	ret

;***Subroutine********************
;Restore and test for track 0
;On exit:
;  CURTRK=0 if success
;  carry set means track 0 failure
;Trashes a,c
;*********************************
RESTOR:	mvi	c,CCRSTOR	;Restore (no verify)
	call	T1DCMD		;...Type I command

	cma			;so track 0 is 0
	ani	STRACK0		;on track 0? (clears carry)
	sta	CURTRK		;on track 0 if 0
	rz

;fall into T0ERR

;---Fail----------------
;Track 0 not as expected
;On Exit:
;  Error message printed
;  Carry set
;-----------------------
T0ERR:	call	CILPRT	;returns with carry set
	db	'Track ','0'+80h
GOFAIL:	jmp	FAIL		;PRints 'failed'

;---Fail----------------
;Disk speed is way off
;On Exit:
;  Error message printed
;  Carry set
;-----------------------
INDTO:	pop	h
	call	CILPRT	;returns with carry set
	db	'RP','M'+80h
	db	JR,(GOFAIL-$-1) xor 0FF00h

;---Fail-----------------------
;Track <RESTEP> not as expected
;On Exit:
;  Error message printed
;  Carry set
;------------------------------
SEKERR:	call	CILPRT	;returns with carry set
	db	'See','k'+80h
	db	JR,(GOFAIL-$-1) xor 0FF00h

;***Subroutine*********************************************
;Issue Type I command to the WD1793 and wait for completion
;(Does not check ready status)
;On Entry:
;  c=DCMMD value (disk command)
;On Exit:
;  a=disk status
;  Z set if a=0 (no errors)
;  'Drive not ready' if timeout
;Trashes c
;**********************************************************
T1DCMD:	in	DSTAT		;disk status, clear INTRQ
	mov	a,c		;issue command to WD1793
	out	DCMMD

;Wait for command to complete, with 2.5 second timeout

	push	h
	lxi	h,888		;(2.5 second timeout)

;Inner loop: 2.81 mS (a little random on the first pass)

T1DC1:	in	DFLAG		;(11)wait for completion
	rar			;(4)1793 INTRQ flag?
	db	JRC,T1DC2-$-1	;(12)y: done waiting, carry set

	dcr	c		;(4)
	db	JRNZ,(T1DC1-$-1)xor 0FF00h ;(13)

	dcx	h		;(6)
	mov	a,h		;(4)
	ora	l		;(4) clears carry too
	db	JRNZ,(T1DC1-$-1)xor 0FF00h ;(12)


;timeout: drivbe not ready

	pop	h
	ori	SNOTRDY		;clear z too
	ret

;Get controller status for return

T1DC2:	pop	h
	in	DSTAT		;disk status
	ani	STRACK0+SBUSY+SSEEKER+SCRCERR

	ret			;ret with status

;***Subroutine********************************
;Get and validate a drive letter from the user
;On Entry:
;  The request string has already been printed
;On Exit:
;  a = drive{0:3}
;  e = selected drive, in ASCII, 0 of none
;  Z set if the user just typed CR
;  Carry set and HUH printed if bogus input
;  otherwise, e=a=good drive number (0:3)
;  SAVDCL is set up for a minidisk
;Trashes a,bc,hl
;**********************************************
GETDRV:	xra	a		;Unlock the drive door:
	out	DCTRL		;motors off, lift heads

	call	GETLIN		;trashes bc
	mov	e,a		;ASCII drive for return
	rz			;blank line?

	jc	PRTHUH		;too many chrs?

	sui	'A'		;drive number
	cpi	NDRIVES		;valid?
	jnc	PRTHUH		;n: huh?

;Fall into SETDCL

;***Subroutine*********************************
;Compute and set up DCTRL value for this drive,
;assuming minidisk, motor on
;On Entry:
;  a=valid drive number (0:3)
;On Exit:
;  carry clear
;  SAVDCL is set up for a minidisk
;Trashes a,b
;**********************************************
SETDCL:	mov	b,a
	inr	b		;for count down to 0
	xra	a		;compute initial DCTL value
	stc

DCTLUP:	ral			;shift select bit into place
	db	DJNZ,(DCTLUP-$-1)xor 0FF00h

	ori	DCMDMO		;motor always on (clears carry)
	sta	SAVDCL		;minidisk disk DCTRL value

	ret

;***Exit*************************
;Cold Boot from A
;Read track 0, sector 1 into RAM
;at COMBUF, and execute it there.
;********************************
REBOOT:	lxi	sp,CPMBUF+10h	;Can't use stack in COMBUF

REBOT1:	call	CILPRT
	db	'Put boo','t'+80h
	call	WATDA		;wait for disk in A
	db	JRNZ,(REBOT1-$-1)xor 0FF00h

	xra	a		;select drive A
	call	SETDCL		;and set up DCTRL

	call	DRIVID		;set up for mini/maxi
	db	JRNZ,BOTERR-$-1	;Seek error?

	mvi	a,1		;Sector 1
	out	DSCTR		;WD1793 sector port

	mvi	b,MAXTRYS+1	;retry counter

RBRTRY:	lxi	h,COMBUF	;Put boot sector here
	push	h		;execution address too

	mvi	d,CCRDSEC	;read command


	push	b		;retry count
	mvi	b,BPSSD		;Single-density sector
	call	RWPSEC		;read sector
	pop	b

	rz			;success: execute loaded code

;Read fail. Try again if possible. (b=retry count)
;Give user feedback about retries.

	pop	h		;chuck address

	mvi	a,MAXTRYS+1
	cmp	b		;first retry?
	db	JRNZ,RBR1-$-1	;n: just print dot

	call	CILPRT
	db	'Retryin','g'+80h

RBR1:	call	ILPRNT
	db	'.'+80h

	db	DJNZ,(RBRTRY-$-1)xor 0FF00h

;Fatal error when trying to boot. Ask for another disk.

BOTERR:	call	CILPRT
	db	'Boo','t'+80h
	call	FAIL

	db	JR,(REBOT1-$-1)xor 0FF00h
	
;***Subroutine***********************************************
;Get input line from the user, terminated by CR
;  COMBUF is assumed to be in page 0
;On Exit:
;  Abort to CP/M if Q followed by CR
;  Cold-boot from drive A if ^C
;  a=character, converted to uppercase, with parity removed
;    unless it's just a CR
;  a=0 and Z set if just a CR typed
;  Carry set if more than one chr typed
;Trashes bc,hl
;************************************************************
GETLIN:	lxi	h,COMBUF

;Backspace if possible

GLDEL:	mov	a,l		;can we back up?
	cpi	COMBUF
	db	JRZ,GLOOP-$-1	;no: ignore

	call	ILPRNT
	db	BS,' ',BS+80h

	dcx	h		;back up
	
GLOOP:	mvi	a,MAXLIN+COMBUF
	cmp	l		;lots of chrs?
	rc			;y: bogus input

	mvi	a,CONIN		;Address offset
	call	GOBIOS		;get input chr
	ani	7Fh		;strip parity, ignore nulls
	db	JRZ,(GLOOP-$-1)xor 0FF00h

	cpi	CTRLC		;Immediate Abort?
	jz	REBOOT

	cpi	BS		;any backspaces?
	db	JRZ,(GLDEL-$-1)xor 0FF00h
	cpi	DEL		;of either sort...
	db	JRZ,(GLDEL-$-1)xor 0FF00h

	mov	m,a		;save new chr
	call	PRINTA		;echo chr, trash bc

	inx	h		;next slot in buffer

	cpi	CR		;end of input?
	db	JRNZ,(GLOOP-$-1)xor 0FF00h

;Look at result
	mov	a,l
	sui	COMBUF+1	;compute chr count
	rz			;just CR: ret with a=0, Z set

	mov	l,a		;chr count-1

	lda	COMBUF		;get first chr
	sui	'a'		;convert lowercase
	cpi	'z'+1
	db	JRNC,GL1-$-1
	sui	'a'-'A'		;..to uppercase
GL1:	adi	'a'

	dcr	l		;just 1 chr?
	stc
	rnz			;n: error exit

;test for Q abort

	cpi	'Q'
	stc			;clear carry
	cmc			;..without affecting Z
	rnz			;normal exit: chr in a

GABORT:

;Fall into DONE

;***Exit**********************
;Print message, return to CP/M
;*****************************
DONE:	lxi	sp,STACK	;in case of multiple Q's etc.

	call	CILPRT
	db	'Put CP/','M'+80h

	call	WATDA
	db	JRNZ,(DONE-$-1)xor 0FF00h

	jmp	WBOOT		;go reload FDOS

;***Subroutine******************************************
;Print drive letter and Get CR from user. If something
;else typed, then print 'HUH?', and return with Z clear.
;Entry at WATDA waits for disk A
;On Entry at WATDSK:
;  e = ASCII drive letter
;On Exit:
;  Z set means CR received
;  Z clear, carry set means other input
;  Rude abort if ^C or Q
;Trashes a,bc
;*******************************************************
WATDA:	mvi	e,'A'		;drive A

WATDSK:	call	ILPRNT
	db	' disk in',' '+80h
	mov	a,e
	call	PRINTA		;trashes bc

	call	ILPRNT
	db	' and press Return',':'+80h

	call	GETLIN		;trashes bc
	rz			;just CR typed

;Fall into PRTHUH and return with Z cleared

;***Subroutine***
;Print 'HUH?"
;On Exit:
;  Z cleared
;  carry set
;Trashes a
;****************
PRTHUH:	call	CILPRT	;returns with carry set
	db	'Huh','?'+80h

	ret
		
;***Subroutine****************************************
; Print CR, LF, then In-line Message
;  The call to CILPRT is followed by a message string.
;  The last message chr has its msb set.
; On Exit:
;   Carry set
; Trashes a
;*****************************************************
CILPRT:	call	ILPRNT
	db	CR,LF+80H

; fall into ILPRNT

;***Subroutine****************************************
; Print In-line Message
;  The call to ILPRNT is followed by a message string.
;  The last message chr has its msb set.
;  0 indicates a repeating chr. The next character is
;  the repeat count, and the next one os the chr to
; be repeated.
; On Exit:
;   Carry set
; Trashes a
;*****************************************************
ILPRNT:	xthl			;Save hl, get msg addr
	push	b

IPLOOP: mvi	b,1

IPRPT:	mov	a,m
	ani	7Fh		;strip end marker
	db	JRNZ,IPNRP-$-1

	inx	h		;get repeat count
	mov	b,m

	inx	h		;point to print chr
	mov	a,m

IPNRP:	call	PRINTA		;print byte
	db	DJNZ,(IPRPT-$-1)xor 0FF00h

	mov	a,m		;end?
	inx	h		;Next byte
	rlc			;msb set?
	db	JRNC,(IPLOOP-$-1)xor 0FF00h ;Do all bytes of msg

	pop	b
	xthl			;Restore hl,
				;..get return address
	ret			;with carry set

;***Subroutine***
;Print a as hex
;Trashes psw,bc
;****************
PAHEX:	push	psw		;high nibble first
	rrc
	rrc
	rrc
	rrc
	call	PNHEX

	pop	psw		;low nibble

;Fall into PNHEX

;***Subroutine******
;Print a<3:0> as hex
;Trashes psw,bc
;*******************
PNHEX:	ani	0Fh		;get nibble
	adi	'0'		;Make ASCII
	cpi	'9'+1		;A-F?
	db	JRC,PRINTA-$-1
	adi	'A'-'9'-1

;Fall into PRINTA

;***Subroutine*******************
; Print character in a on console
; trashes psw,bc
;********************************
PRINTA:	mov	c,a		;value to c for PRINTC
	db	JR,PRINTC-$-1

;***Subroutine***
;Print hl as hex
;Trashes psw,bc
;****************
PHLHEX:	mov	a,h
	call	PAHEX
	mov	a,l
	
;Fall into PAHEXH

;***Subroutine*******************
;Print a as hex, followed by 'h'
;Trashes psw,bc
;********************************
PAHEXH:	call	PAHEX
	mvi	c,'h'
	
; fall into PRINTC

;***Subroutine*******************
; Print character in c on console
; trashes psw,bc
;********************************
PRINTC:	mvi	a,CONOUT

; fall into GOBIOS

;***Subroutine**********************
; Go call a BIOS driver directly
; On Entry:
;   c=value for BIOS routine, if any
;   a = BIOS call address offset
; On Return:
;   psw as BIOS left it
;Trashes bc
;   (116 cycles + BIOS time)
;***********************************
GOBIOS:	push	h		;(11)
	push	d		;(11)

	call	DOBIOS		;(17+26+BIOS time)

	pop	d		;(10)
	pop	h		;(10)
	ret			;(10)done

;***Subroutine**********************
; Go call a BIOS driver directly
; On Entry:
;   c=value for BIOS routine, if any
;   a = BIOS call address offset
; On Return:
;   all regs as BIOS left them
;   (26 cycles + BIOS time)
;***********************************
DOBIOS:	lhld	WBOOTA		;(16)get BIOS base address
	mov	l,a		;(5)a has jump vector

	pchl			;(5) 'call' BIOS routine

;*******************
; DISK FORMAT TABLES
;*******************

;================================
; High Data Rate Single Density =
;================================
T8SD:
;---------TRACK HEADER----------
	db	40,0FFH		;40 BYTES OF 0FFH
	db	6,0		;6 BYTES OF 0
	db	1,0FCH		;1 BYTE OF FCH (INDEX MARK)
	db	26,0FFH		;26 BYTES OF FFH
	db	0		;END OF PATERN SEQUENCE
;-------SECTOR PREAMBLE---------
	db	6,0		;6 BYTES OF 0
	db	1,0FEH		;1 BYTE OF FEH (ADDRESS MARK)
	db	0		;END OF PATERN SEQUENCE
; TRACK NUMBER (0-77)
; SIDE NUMBER (0-1)
; SECTOR NUMBER (1-26)
; SECTOR SIZE (0-3)
	db	1,0F7H		;1 BYTE OF F7H
	db	11,0FFH		;11 BYTES OF FFH
	db	6,0		;6 BYTES OF 0
	db	1,0FBH		;1 BYTE FBH (DATA ADDRESS MARK)
	db	0		;END OF PATERN SEQUENCE
;---------SECTOR DATA-----------
;{128,256} BYTES OF FILL DATA
	db	0E5H		;FILL THE DATA FIELD WITH THIS
;-------SECTOR POSTAMBLE--------
	db	1,0F7H		;1 BYTE F7H (2 CRC'S WRITTEN)
	db	27,0FFH		;27 BYTES OF FFH
	db	0		;END OF PATERN SEQUENCE
;---TRACK POSTAMBLE CHARACTER---
	db	0FFH		;THIS BYTE UNTIL INDEX PULSE
;-----Encoded buffer length-----
	dw	EBL8SD		;Encoded Buffer length/4

;--Encoded buffer length for 3.5" drive--
	dw	EBL3SD		;Encoded Buffer length/4

;================================
; High Data Rate Double Density =
;================================
T8DD:
;---------TRACK HEADER----------
	db	80,4EH		;80 BYTES OF 4EH
	db	12,0		;12 BYTES OF 0 
	db	3,0F6H		;3 BYTES OF F6H
	db	1,0FCH		;1 BYTE OF FCH (INDEX MARK)
	db	50,4EH		;50 BYTES OF 4EH
	db	0		;END OF PATERN SEQUENCE
;--------SECTOR PREAMBLE--------
	db	12,0		;12 BYTES OF 0
	db	3,0F5H		;3 BYTES OF 5FH
	db	1,0FEH		;1 BYTE OF FEH (ADDRESS MARK)
	db	0		;END OF PATERN SEQUENCE
; TRACK NUMBER (0-77)
; SIDE NUMBER (0-1)
; SECTOR NUMBER (1-26)
; SECTOR SIZE (0-3)
	db	1,0F7H		;1 BYTE OF F7
	db	22,4EH		;22 BYTES OF 4EH
	db	12,0		;12 BYTES OF 0
	db	3,0F5H		;3 BYTES OF F5H
	db	1,0FBH		;1 BYTE FBH (DATA ADDRESS MARK)
	db	0		;END OF PATERN SEQUENCE
;--------SECTOR DATA------------
;{256,512,1024} BYTES OF FILL DATA
	db	0E5H		;FILL THE DATA FIELD WITH THIS
;------SECTOR POSTAMBLE---------
	db	1,0F7H		;1 BYTE F7 (2 CRC'S WRITTEN)
	db	54,04EH		;54 BYTES OF 4EH
	db	0		;END OF PATERN SEQUENCE
;---TRACK POSTAMBLE CHARACTER---
	db	4EH		;THIS BYTE UNTIL INDEX PULSE
;-----Encoded buffer length-----
	dw	EBL8DD		;Encoded Buffer length/4

;--Encoded buffer length for 3.5" drive--
	dw	EBL3DD		;Encoded Buffer length/4

;===============================
; Low Data Rate Single Density =
;===============================
T5SD:
;--------TRACK HEADER-----------
	db	16,0FFH		;16 BYTES OF FFH
	db	0		;END OF PATERN SEQUENCE
;------SECTOR PREAMBLE----------
	db	6,0		;6 BYTES OF 0
	db	1,0FEH		;1 BYTE FEH (ADDRESS MARK)
	db	0		;END OF PATERN SEQUENCE
; TRACK NUMBER (0-77)
; SIDE NUMBER (0-1)
; SECTOR NUMBER (1-26)
; SECTOR SIZE (0-3)
	db	1,0F7H		;1 BYTE OF F7
	db	11,0FFH		;11 BYTES OF FFH
	db	6,0		;6 BYTES OF 0
	db	1,0FBH		;1 BYTE FBH (DATA ADDRESS MARK)
	db	0		;END OF PATERN SEQUENCE
;--------SECTOR DATA------------
;{128,256} BYTES OF FILL DATA
	db	0E5H		;FILL THE DATA FIELD WITH THIS
;------SECTOR POSTAMBLE---------
	db	1,0F7H		;1 BYTE F7 (2 CRC'S WRITTEN)
	db	11,0FFH		;11 BYTES OF FFH
	db	0		;END OF PATERN SEQUENCE
;---TRACK POSTAMBLE CHARACTER---
	db	0FFH		;THIS CHR UNTIL INDEX PULSE
;-----Encoded buffer length-----
	dw	EBL5SD		;Encoded Buffer length/4

;===============================
; Low Data Rate Double Density =
;===============================
T5DD:
;--------TRACK HEADER-----------
	db	32,04EH		;32 BYTES OF 4EH
	db	0		;END OF PATERN SEQUENCE
;------SECTOR PREAMBLE----------
	db	8,0		;8 BYTES OF 0
	db	3,0F5H		;3 BYTES OF F5H
	db	1,0FEH		;1 BYTE FEH (ADDRESS MARK)
	db	0		;END OF PATERN SEQUENCE
; TRACK NUMBER (0-77)
; SIDE NUMBER (0-1)
; SECTOR NUMBER (1-26)
; SECTOR SIZE (0-3)
	db	1,0F7H		;1 BYTE OF F7H
	db	22,4EH		;22 BYTES OF 4EH
	db	12,0		;12 BYTES OF 0
	db	3,0F5H		;3 BYTES OF F5H
	db	1,0FBH		;1 OF FBH (DATA ADDRESS MARK)
	db	0		;END OF PATERN SEQUENCE
;---------SECTOR DATA-----------
;{256,512,1024} BYTES OF FILL DATA
	db	0E5H		;FILL DATA FIELD WITH THIS
;-------SECTOR POSTAMBLE--------
	db	1,0F7H		;1 BYTE F7 (2 CRC'S WRITTEN)
	db	22,4EH		;22 BYTES OF 4EH
	db	0		;END OF PATERN SEQUENCE
;---TRACK POSTAMBLE CHARACTER---
	db	4EH		;THIS BYTE UNTIL INDEX PULSE
;-----Encoded buffer length-----
	dw	EBL5DD		;Encoded Buffer length/4
	
;-------------------------
;Initialized RAM variables
;-------------------------
CRETRY:	dw	0	;Cumulative retry counter
VBCNT:	dw	DEFSIZ	;Number of bytes to verify
			;default used if RAM image is used
			;..and it's verify only

;The following 2 must be in order
SDFLAG:	db	0	;NZ means all tracks are SD
SAVDCL:	db	0	;DCTRL value for selected drive
;-------------------------------------------
;More RAM variables (could be ununitialized)
;-------------------------------------------
FMTSPT:	db	0	;Format sectors per track
;The following 6 must be in order
CURSPT:	db	0	;sectors/track
SYSTKS:	db	0	;number of system tracks
CURTRK:	db	0	;Current track
CURSID:	db	0	;Current side (always 0)
CURSEC:	db	0	;Current sector
TRKBPS:	db	0	;encoded BPS {(0,1)=(128,256)}

CURBPS:	db	0	;Current BPS ({80h,0}={128,256})

RETRYS:	db	0	;Retry counter
SPSAVE:	dw	0	;stack pointer save

;====================================================
;The following code gets overwritten by the CP/M file
;====================================================
;***Subroutine*******************************************
;Initialization
;On Exit:
;  b=1 if a source file was specified on the command line
;  b=0 if not
;  hl=byte count if command line source file specified
;********************************************************
INIT:

;Print sign-on banner

	call	CILPRT

	db 0,28,'='	;Repeat this 28 times
	db CR,LF,0,4,' ','Cromemco 16FDC/64FDC'
	db CR,LF,'CP/M System Transfer Utility'
 	db CR,LF,'Version ',VERSION+'0'
	db '.',VERSMIN/10+'0',(VERSMIN MOD 10)+'0'
	db '  By M. Eberhard'
	db CR,LF
	db 0,28,'='	;Repeat this 28 times
	db LF+80h

;Verify adequate RAM available

	lhld	WBOOTA		;BIOS address
	lxi	d,0-RAMEND	;end of RAM
	dad	d		;16-bit subtract
	db	JRNC,RAMERR-$-1	;carry set if OK
;----------------------------------------------
;Look for a file name on the command line.
;BDOS will put the file name in the FCB for us.
;----------------------------------------------
	lda	COMBUF		;anything on the command line?
	ora	a		;byte count of command line opts
	mov	b,a		;b=0 if no source file present
	rz			;no file: done with init

;-------------------------------------------
;Set up and load CP/M image from a CP/M file
;-------------------------------------------
;Clear the CP/M buffer, so anything at the end is pretty

	lxi	h,CPMBUF
	lxi	b,FMTBUF-CPMBUF

BCLR:	mvi	m,0E5h		;fill like a formatted sector
	inx	h
	dcx	b
	mov	a,b
	ora	c
	db	JRNZ,(BCLR-$-1)xor 0FF00h

;Clean up the FCB as required by CP/M

	lxi	h,FCBEXT
	mvi	b,FCBCLR

FCLR:	mov	m,h		;h=0
	inx	h
	db	DJNZ,(FCLR-$-1)xor 0FF00h

;set up and read the specified file. The first 800h bytes
;of the CPM.COM file (as created by MOVCPM.COM) are garbage,
;and will be ignored here

	lxi	d,FCB		;open the file
	mvi	c,BOPEN
	call	BDOS		;trashes all registers
	inr	a		;-1 means open failure
	jz	DSKERR

	mvi	b,800h/CPMBPS+1	;number of ignored sectors
	lxi	h,CPMBUF	;put image here

LOLOOP:	push	h		;hl=DMA address
	push	b		;b=ignore counter

	xchg			;de=dma address
	mvi	c,BSTDMA
	call	BDOS		;trashes all registers

	lxi	d,FCB
	mvi	c,BREAD		;read another sector
	call	BDOS		;a=0 if not eof
	pop	b
	pop	h		;hl=DMA address

	ora	a		;read success?
				;b=1 here, when sectors load
	db	JRNZ,LOLDON-$-1	;NZ means load is complete

	db	DJNZ,(LOLOOP-$-1)xor 0FF00h ;ignoring?

	inr	b		;done ignoring
	lxi	d,CPMBPS	;logical sector size
	dad	d
	db	JR,(LOLOOP-$-1)xor 0FF00h

;Done reading. Calculate the number of bytes read,
;and remember for verifying.

LOLDON:	lxi	d,-CPMBUF	;calculate bytes written
	dad	d		;hl=byte cpunt for exit

	ret			;done with INIT

;------------------------
;Error: Insufficient RAM.
;------------------------
RAMERR:	call	CILPRT
	db	LF,'Not enough RA','M'+80h
	db	JR,GOBOOT-$-1
;-------------------------------
;Error: couldn't open disk file.
;-------------------------------
DSKERR:	call	CILPRT
	db	LF,'File not foun','d'+80h
GOBOOT:	jmp	WBOOT	

;=========================================
;Check to see if we overflowed into DDTBUF
;=========================================
 if (DDTBUF-$)/8000h
 ERROR: Code is too large
 endif

;-----------------------------------------------
;Sector ID area, located in the COM buffer
;The WD1793 Read Address command reads 6 bytes
;from the header of the next sector encountered.
;IDREAD writes these 6 bytes here.
;-----------------------------------------------
	org	COMBUF+64
IDSAVE:
IDTRAC:	ds	1	;track number
IDSIDE:	ds	1	;side number
IDSECT:	ds	1	;sector address
IDSSIZ:	ds	1	;BPS(0:3)
IDCRC1:	ds	1	;CRC 1
IDCRC2:	ds	1	;CRC 2

;----------------------------------------------
;Other temp variables stashed in the COM buffer
;----------------------------------------------
EVNSEC:	ds	2	;start addr of even sectors
NXTSEC:	ds	2	;next sector in buffer

;-----------------------------------------------------
;Buffer for CP/M read image, located where CroGEN
;expects the CP/M image, and where MOVCPM puts it. The
;largest possible system would be on a minidisk where
;track 0 is formatted as SD, 128 BPS (as required),
;and the other 2 system tracks are DD, 512 BPS. (This
;program would never create such a disk, but it could
;have been created on a different system, and this
;program could be just verifying such disks.)
;-----------------------------------------------------
	org	DDTBUF

CPMBUF:	ds	BSPT5*BPSSD+(SYSTK5-1)*BSPT5*BPSDD
;------------------------------------------
;Format buffer (overlays the Verify buffer)
;------------------------------------------
FMTBUF:
;-------------
;Verify buffer
;-------------
VERBUF:	ds	BSPT5*BPSSD+(SYSTK5-1)*BSPT5*BPSDD

RAMEND	equ	$
	end
