;**************************************************************
; CCS 2422 Z-80-COMPATIBLE CP/M SYSTEM DISK GENERATOR         *
;                                                             *
;REVISION HISTORY                                             *
; 3.07  13 November 2021  M. Eberhard                         *
;   Fix old bug reporting write error. Fix new bug from 3.06. *
;   Add delay for trim-erase after track write.               *
; 3.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.)       *
; 3.05  22 March 2021  M. Eberhard                            *
;   Return to original disk when returning to CP/M            *
; 3.04  20 February 2018  M. Eberhard                         *
;   Always format the system tracks (eliminate the choice)    *
;   Allow the user to specify a CPM.cOM image file on the     *
;   command line, and use that image if specified. Track 0 is *
;   always SD. Further system tracks are DD unless the system *
;   was read from a disk where all system tracks were SD -    *
;   then we write all tracks SD too. Better precision on how  *
;   many bytes to verify for verify-only operation.           *
; 3.03  31 August 2017 M. Eberhard                            *
;   Deselect the drive to unlock the drive door               *
; 3.02  22 May 2017  M. Eberhard                              *
;   Fix stupid bug in 3.01                                    *
; 3.01  14 May 2017  M. Eberhard                              *
;   Add option to not format system tracks                    *
; 3.00  29 April 2017  M. Eberhard                            *
;   Format system tracks before writing                       *
; 2.02  27 April 2017  M. Eberhard                            *
;   Add assembly option for 2065 compatibility                *
; 2.01  7 April 2017   M. Eberhard                            *
;   Add option to boot new image with ^C. Add warning about   *
;   CCS 2065 DRAM board incompatibility.                      *
; 2.00  3 March 2017   M. Eberhard                            *
;   Rewrite to transfer sector at a time, and speed up for 2  *
;   MHz operation (using INIR and OTIR instructions). replace *
;   BDOS calls with BIOS calls so that buffers can overwrite  *
;   CCP and BDOS.                                             *
;**************************************************************
; This program always formats the system tracks before writing
; the system to disk. Track 0 is formatted as single-density,
; 128 bytes per sector. Track 1 (on 3.5", HD, and 8" disks) or
; tracks 1 & 2 (on low data rate minidisks) are formatted as
; double-density, 256 bytes per sector, even if the disk is a
; single-density disk, or if the rest of the disk is formatted
; with a different sector size. 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 CCS disks
; Also note that most single-density. disks and disk drives can
; actually handle double-density data near the outside diameter
; of the disk (though not PerSci 270/277 drives...)
;
; 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 MSYSGEN 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.)
;
; MSYSGEN 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. MSYSGEN will abort cleanly with
; an error message if there is insufficent system RAM.
;
; Written to assemble with Digital Research's ASM.COM.
;**************************************************************
VERSION	equ	3	;Major revision number
VERSMIN	equ	07	;Minor revision number

FALSE	equ	0
TRUE	equ	not FALSE

;-----------------
;MSYSGEN 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	6	;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	;256 BPS for DD system tracks
BPSCSD	equ	0	;Code for 128 BPS for SD system tracks
BPSCDD	equ	1	;Code for 256 BPS for DD system tracks

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)
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+80	;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

LDIR	equ	0B0EDh
CPIR	equ	0B1EDh
CPD	equ	0A9EDh
BIT4A	equ	067CBh		;BIT 4,a
BIT5A	equ	06FCBh		;BIT 5,a
BIT6A	equ	077CBh		;BIT 6,a
BIT0B	equ	040CBh		;BIT 0,b
BIT1B	equ	048CBh		;BIT 1,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
BIT6D	equ	072CBh		;BIT 6,d
BIT4M	equ	066CBh		;BIT 4,(hl)
BIT5M	equ	06ECBh		;BIT 5,(hl)
BIT6M	equ	076CBh		;BIT 6,(hl)
SET4M	equ	0E6CBh		;SET 4,(hl)
SET6M	equ	0F6CBh		;SET 6,(hl)
RES7E	equ	0BBCBh		;RES 7,e
RES5M	equ	0AECBh		;RES 5,(hl)
RES6M	equ	0B6CBh		;RES 6,(hl)	

INI	equ	0A2EDh
OUTI	equ	0A3EDh
INIR	equ	0B2EDh
OTIR	equ	0B3EDh
LDED	equ	05BEDh		;ld de,(nn)
SBCD	equ	043EDh		;ld (nn),bc
SDED	equ	053EDh		;LD (nn),de
RLCB	equ	000CBh		;RLC b
RLCC	equ	001CBh		;RLC c

OUTCH	equ	061EDh		;OUT c,h
OUTCL	equ	069EDh		;OUT c,l

;---------------------------------------
;CCS 2422 floppy disk controller equates
;---------------------------------------
BCTRL	equ	4		;Control port 2
BSTAT	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 inT

;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  Minidisk  8" disk
; RS03MS    6 mS     3 MS
; RS06MS   12 mS     6 MS
; RS10MS   20 mS    10 MS
; RS15MS   30 mS    15 MS

STEP5	equ	RS10MS		;SA400L Minidisk rate=20 mS
STEP8	equ	RS10MS		;SA800 8" 1-sided rate=8 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

;Control register 1 (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" disk
DCMDMO	equ	00100000b	;Minidisk motor on
DCDDEN	equ	01000000b	;double density enable
DCAUTOW	equ	10000000b	;autowait

;Control register 2 (BCTRL) output bits

BCEJECT	equ	00000100b	;remote eject control (PerSci)
BCFSEEK	equ	00010000b	;fast seek: voicecoil drives
BCSIDE0	equ	01000000b	;0=side 1, 1=side 0
BCROMEN	equ	10000000b	;enable onboard ROM

;Composite BCTRL values used to select disk side
; Note: ROM is actually disabled via the page register
; Note: fast seek disabled VIA hardware jumper

SIDE0	equ	BCROMEN+BCFSEEK+BCSIDE0
SIDE1	equ	BCROMEN+BCFSEEK

;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

DFINTRQ	equ	00000001b	;WD1793 INTRQ signal, active high
DFDS1	equ	00000010b	;DS1 in control reg 1
DFDS2	equ	00000100b	;DS2 in control reg 1
DFDS3	equ	00001000b	;DS3 in control reg 1
DFDS4	equ	00010000b	;DS4 in control reg 1
DFSHLD	equ	00100000b	;WD1793 HLD signal, active high
DFAUTOB	equ	01000000b	;autoboot jumper, 0=autoboot
DFSDRQ	equ	10000000b	;WD1793 DRQ signal, 1=ready

;Status reg 2 (BSTAT) input bits

BTRK0	equ	00000001b	;when on track 0: 1=8" disk
BMINI	equ	00000010b	;-DCTRL reg bit: 1=Minidisk
BWPRT	equ	00000100b	;0=disk is write-protected
BSIDE0	equ	00001000b	;SIDE0 bit in control register 2
BINDEX	equ	00010000b	;index signal, active low
BDDEN	equ	00100000b	;DDEN signal in control register 1
BSIDED	equ	01000000b	;0=selected disk is 2-sided

;*****************
;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 drive (A-','A'+ NDRIVES -1
	db	', Return to use RAM image, Q to quit):',' '+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 drive (A-','A'+ NDRIVES -1
	db	', ^C to boot, Q to quit):',' '+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	'Writing syste','m'+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 eirther 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. First error 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
	inr	a		;a=1: step in before restore
	call	RESTOR		;restore to track 0
	rc			;restore error?

;The head is on track 0 and CURTRK=0
;Side 0 is selected on BCTRL
;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:

	xra	a		;BPS code for SD tracks
;---------------------------------------------
;  a=BPS code (e.g. 0 for 128 or 1 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 code
;----------------------------------------------
;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=0 for track 0, 1 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 = CURBPS
;--------------------------------------------
	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 128, 1 means 256
	xri	1
	rrc			;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 of the 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
;----------------------------------------------
	pop	h		;toss format table sector pointer
	lxi	h,0		;for upcoming dad SP

	mvi	c,DDATA		;for OUTC instructions

	di
	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 22 cycles
;(worst case) between writes. Thus, this program can
;reliably format DD tracks on an 8" disk at 2 MHz.
;----------------------------------------------------------
	jmp	WTLENT	;(10)Enter loop at the right spot

WTLUP:	dw	OUTCH	;(12) write byte 4

WTLENT:	pop	h	;(10) get bytes 1 & 2
	
	dw	OUTCL	;(12) write byte 1
	dcr	e	;(4)

	dw	OUTCH	;(12) write byte 2
	pop	h	;(10)get bytes 3 & 4

	dw	OUTCL	;(12) write byte 3
	jnz	WTLUP	;(10)	

;bump the high byte counter and test for underflow.

	dw	OUTCH	;(12) write byte 4
	pop	h	;(10) get bytes 1 & 2

	dw	OUTCL	;(12) write byte 1
	dcr	d	;(4) bump high byte of quad-byte count

	dw	OUTCH	;(12) write byte 2
	pop	h	;(10)get bytes 3 & 4

	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
;interrupt 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:	dw	OUTCL	;(12)
	in	DFLAG	;(11) Read status

	dw	OUTCL	;(12)
	rar		;(4) Test interrupt bit

	dw	OUTCL	;(12)
	jnc	POSTLP	;(10) Continue sending until int
;----------------------------------
;Restore the stack 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

	lda	SAVDCL
	ori	DCDDEN		;double density now
	sta	SAVDCL

	ani	DCMAXI

	lxi	h,T8DD		;8" double-density table
	db	JRNZ,DDFL1-$-1
	lxi	h,T5DD		;minidisk DD table
DDFL1:

	mvi	a,BPSCDD	;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 each track.
;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:
;  BCTRL 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

				;a=0: no need to step in first
	call	RESTOR		;select disk, restore to track 0
				;..and select side 0
	rc			;restore error?

	mvi	e,BPSSD/4	;BPS/4 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/4
;  hl=beginning of this track in RAM
;  CURSPT=SPT
;  Drive is selected and restored
;----------------------------------------------
TRKLUP:	mvi	c,1		;initial sector number
	lda	CURSPT
	mov	b,a		;SPT = sector loop count

;b=sector loop counter
;c=next expected sector
;d=read or write command
;e=BPS/4
;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	;(7)reset R/W retry counter
	sta	RETRYS		;(7)..for the sector read/write

SRETRY:	push	b		;(11)sector & loop count
	push	h		;(11)Buffer address for retry	

	mov	b,e		;(4)BPS/4
	mov	a,c		;(4)sector number
	out	DSCTR		;(11)WD1793 sector port

	call	RWPSEC		;(17)read or write physical sector
				;returns hl=next buffer address	
	db	JRNZ,RWSERR-$-1	;(7)retry on error

;Good sector read/write. Next physical sector

;Wrap to beginning of track when necessary. This will occur
;exactly once per track (regardless of which sector is first)
;and will cause the track's last DMA address to replace the
;track beginning DMA address on the stack.

	pop	b		;(10)chuck old buffer address
	pop	b		;(10)sector & loop count

	inr	c		;(4)

	db	DJNZ,(SECLUP-$-1)XOR 0FF00h ;(12) More sectors?

;-----------------------------------------------------
;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
;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/4	;BPS/4

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 the 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/4	;single-density BPS/4

	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	CILPRT
	db	'Seek erro','r'+80h

	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

	dw	BIT6A		;write protect error?
	db	JRNZ,WPERR-$-1	;y: tell user, abort

	push	h
	lxi	h,RETRYS	;bump & test sector retry counter
	dcr	m
	pop	h
	db	JRNZ,(SRETRY-$-1)XOR 0FF00h ;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 code:',' '+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

;***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
;  b=BPS/4
;  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)Data port
	mov	a,d		;(4)
	out	DCMMD		;(11)disk command port

	cpi	CCWRSEC		;(7)read or write?
	db	JRNZ,RDPSEC-$-1	;(12)

;Fall into WRPSEC

;***Subroutine***********************************
;Write one physical sector
;Fast as possible: 26 cycles (13 uS) out-to-out.
;Too slow for an 8"DD disk with a 2 MHz CPU.
;(WD2793 requires 11.5 uS worst case)
;On Entry:
;  write sector command has been written do DCMMD
;  b=byte count/4 (1024 BPS encoded as 0)
;  c=DDATA
;  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:	dw	OUTI	;(16)
	inr	b	;(4) undo 'dcr b' in OUTI

	dw	OUTI	;(16)
	inr	b	;(4) undo 'dcr b' in OUTI

	dw	OUTI	;(16)
	inr	b	;(4) undo 'dcr b' in OUTI

	dw	OUTI	;(16)
	jnz	WRPSEC	;(10) z set/cleared by OUTI

;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: 26 cycles (13 uS) in-to-in.
;Just fast enough for an 8"DD disk with a 2 MHz CPU.
;On Entry:
;  read or read address command has been issued to WD1793
;  b=byte count/4 (1024 BPS encoded as 0)
;  c=DDATA
;  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:	dw	INI	;(16)
	inr	b	;(4) undo 'dcr b' in INI

	dw	INI	;(16)
	inr	b	;(4) undo 'dcr b' in INI

	dw	INI	;(16)
	inr	b	;(4) undo 'dcr b' in INI

	dw	INI	;(16)
	jnz	RDPSEC	;(10) z set/cleared by INI

;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
;abort if 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 DFINTRQ 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

	db	JR,EORW2-$-1		;drive not ready

EORW1:	in	DSTAT			;disk status
	ana	c			;error mask
	rp

EORW2:	jmp	DNRERR			;drive not ready?

;==============================================================
;Head Movement Routines: Seeks and Restores
;==============================================================
;***Subroutine***********************************************
;Restore selected disk. Determine the drive type, and
;set up appropriately. WE recognize 3 drive types:
; 1) Low data rate 5.25" drives (40- or 80-track)
; 2) high data rate 360 RPM drives (8" and HD 5.25")
; 3) high data rate 300 RPM drives (3.25")
;On Entry:
;   a=1 if we should step in before restoring
;   SAVDCL set up with select bits, motor on, high data rate??????
;On Exit:
;  Drive is selected
;  CURTRK=0
;  Side 0 selected on BCTRL
;  CURSPT and SYSTKS set up
;  DCTRL set up for single-density
;  DCTRL and SAVDCL set up
;  carry set if drive not ready or track 0 not found
;    (error message printed) 
;Trashes bc
;************************************************************
RESTOR:	mov	b,a		;step-in requirement
	lda	SAVDCL		;Select bits, motor on
	ani	(DCDDEN+DCMAXI) xor 0FFh
				;single-density track 0
	out	DCTRL		;select drive (no autowait)
	sta	SAVDCL

	in	DSTAT		;disk status
	ora	a		;test busy bit
	jm	DNRERR		;error message, ret with c set

;Select side 0

	mvi	a,SIDE0		;set side 0
	out	BCTRL
;----------------------------------------
;if b=1 then step in a few tracks first
;(Some drives can over-step past track 0)
;----------------------------------------
	db	DJNZ,RSTOR2-$-1 ;need to step in?

	mvi	b,RESTEP
RSTOR1:	mvi	c,CCSTEPI
	call	T1DCMD
	db	DJNZ,(RSTOR1-$-1)xor 0FF00h

	ani	STRACK0		;still on track 0?
	db	JRNZ,T0ERR-$-1	;y: error
RSTOR2:
;------------------------------------------
;actually restore now, and test for track 0
;------------------------------------------
	mvi	c,CCRSTOR	;Restore (no verify)
	call	T1DCMD		;...Type I command

	ani	STRACK0		;on track 0?
	db	JRZ,T0ERR-$-1	;n: error
;------------------------------------------------------
;Test for low data rate drive (on the 34-pin interface)
;while on track 0. Set up controller and our variables
;for the kind of drive we find.
;------------------------------------------------------
	push	h

	in	BSTAT		;read BSTAT while on track 0
	ani	BTRK0		;0 if 34-pin I/F on track 0
	lxi	h,SYSTK5*256+BSPT5
	mov	a,l		;FMTSPT=CURSPT
	db	JRZ,RSTOR4-$-1

	lxi	h,SAVDCL
	dw	SET4M		;DCMAXI: high data rate
	mov	a,m		
	out	DCTRL		;Update controller
;------------------------------------------------------
;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+0h	;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	BSTAT		;(11)
	xra	c		;(4)maybe invert

	ani	BINDEX		;(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,RSTOR3-$-1	;2MHz or 4 MHz CPU?
	rlc			;times 2 if 2 MHz
RSTOR3:	cpi	34h		;carry set if 360 RPM

	mov	a,l		;8": FMTSPT=CURSPT
	db	JRC,RSTOR4-$-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)
RSTOR4:	shld	CURSPT		;also saves SYSTKS
	sta	FMTSPT		;format SPT may be different
	xra	a
	sta	CURTRK		;on track 0

	pop	h
	ret

;---Fail----------------
;Disk speed is way off
;On Exit:
;  Error message printed
;  Carry set
;-----------------------
INDTO:	pop	h
	call	CILPRT	;returns with carry set
	db	'RPM erro','r'+80h

	ret

;---Fail----------------
;Track 0 not as expected
;On Exit:
;  Error message printed
;  Carry set
;-----------------------
T0ERR:	call	CILPRT	;returns with carry set
	db	'Track 0 fai','l'+80h

	ret

;***Subroutine*********************************************
;Issue Type I command to the WD1793 and wait for completion
;On Entry:
;  c=DCMMD value (disk command)
;On Exit:
;  a=disk status
;  Z set if a=0 (no errors)
;  'Drive not ready' printed if timeout or not ready
;Trashes c
;*********************************************************
T1DCMD:	in	DSTAT		;disk status, clear INTRQ
	ral			;Drive not ready?
	db	JRC,DNRERR-$-1	;y: error abort

	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)

T1DC2:	pop	h
	db	JRNC,DNRERR-$-1	;timeout? drive not ready

;Get controller status for return, and test for not ready

	in	DSTAT		;disk status
	ani	SNOTRDY+STRACK0+SBUSY+SSEEKER+SCRCERR

	rp			;if ready, ret with status

;Fall into DNRERR

;---Fail----------------
;Drive not ready
;On Exit:
;  Error message printed
;  Carry set
;-----------------------
DNRERR:	call	CILPRT		;returns with carry set
	db	'Drive not read','y'+80h

	ret

;***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

	xra	a		;a=0: no need to step in first
	call	RESTOR		;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/4	;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
	
;***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,34,'='	;Repeat this 34 times
	db CR,LF,'   CP/M System Transfer Utility'
	db CR,LF,' For the CCS 2422 Disk Controller'
	db CR,LF,' Version ',VERSION+'0'
	db '.',VERSMIN/10+'0',(VERSMIN MOD 10)+'0'
	db '  By Martin Eberhard'
	db CR,LF,' (8" disks require a 4 MHz Z-80.)'
	db CR,LF
	db 0,34,'='	;Repeat this 34 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
	db	JRZ,DSKERR-$-1

	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	

;-------------------------
;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

;---------------------------
;Uninitialized RAM variables
;---------------------------
FMTSPT:	ds	1	;format sectors/track

;The following 6 must be in order
CURSPT:	ds	1	;sectors/track
SYSTKS:	ds	1	;number of system tracks
CURTRK:	ds	1	;Current track
CURSID:	ds	1	;Current side (always 0)
CURSEC:	ds	1	;Current sector
CURBPS:	ds	1	;Current BPS ({0:3}={128:1024})

RETRYS:	ds	1	;Retry counter
SPSAVE:	ds	2	;stack pointer save

;=========================================
;Check to see if we overflowed into DDTBUF
;=========================================
 if (DDTBUF-$)/8000h
 ERROR: Code is too large
 endif
;-----------------------------------------------------
;Buffer for CP/M read image, located where MSYSGEN
;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
