	TITLE	'North Star Disk Copy Program V4.2 of 09/17/81'
;	PAGE	44
;
; These routines copyright (c) 1980, 1981, 1982 by Steve Bogolub,
; 2338 S. Scoville Ave., Berwyn, IL 60402. This software may be
; used freely for non-commercial purposes only, and may not be sold.
;
;************************************************************
;
;  The purpose of this program is to allow the operator
; to copy, format, or validate North Star double density
; disks. Copying can be done between any two North Star
; drives, and can include all tracks, or just the system
; (0 and 1) tracks or data tracks (2 - LTRK) for special
; system handling, where LTRK is the last data track,
; which is 34 for Shugart SA400 and 39 for most others.
; The variable LASTTRK at 103H contains the genned-in
; value of LTRK, and can be patched to change the default
; disk size within reasonable bounds. If the number is
; too big, the generated DPB for the ID block will become
; incorrect. The patching feature is intended primarily
; for switching between 35 and 40 track versions, but
; can be used in a pinch for formatting larger sizes
; for experimental purposes.
;
; The allowed functions are:
;
;  1)	Copy all tracks from source disk to destination
;	disk. The density of sector zero on track zero
;	of the source determines the density of the
;	destination.
;
;  2)	Copy only the system tracks from source disk
;	to destination disk. If source disk is double
;	density, tracks 0 and 1 only are copied. If
;	source disk is single density, tracks 0, 1,
;	and 2 only are copied.
;
;  3)	Copy only the data tracks from source disk to
;	destination disk. If source disk is double
;	density, tracks 2-LTRK inclusive are copied.
;	If source disk is single density, tracks 3-LTRK
;	inclusive are copied.
;
;  4)	Validate the source disk. This is done by
;	reading all tracks.
;
;  5)	Format the destination disk in double
;	density. This is done by writing data
;	blocks with each byte containing the
;	hex value "E5" to all sectors of all
;	tracks. To be Lifeboat-compatible, we
;	write a double-density single-sided
;	disk ID block into track zero, sector
;	zero.
;
;  6)	Format the destination disk in single
;	density. Similar to 5) above.
;
;  7)	Format the destination disk for double
;	sided, 80 trks/side, double density.
;
;  In addition, the user is given the options of
; warm booting CP/M, or cold booting by jumping
; to the standard North Star ROM boot address. For
; functions 1 - 3, the user is asked for the source
; and destination drive letters. For function 4,
; the user is asked for just the destination drive
; letter, since the source is unused. For function
; 5, the user is asked for just the source letter,
; since the destination is unused.
;
;  To drive the disks at top speed, this program
; contains a complete set of North Star disk
; driver routines. CP/M's BIOS routines are used
; to write messages to the console and get user
; replies. All the area up to the BIOS warm
; boot page is assumed available for buffer
; space, so take note if a non-contiguous BIOS
; is in use.
;
;  This program may be restarted at 100H.
;
;  This program is built on the wreckage of the
; CP/M User's group COPY program, after most of
; it was discarded.
;
;************************************************************

	ORG	100H
;
;EQUATES
;
WBOOT	EQU	0	;CP/M WARM BOOT ADDRESS
CBOOT	EQU	0E800H	;NORTH STAR ROM BOOT ADDRESS
NDRIVE	EQU	4	;NUMBER OF NORTH STAR DRIVES ON SYSTEM
LASTSEC	EQU	9
LTRK	EQU	35-1	;NORMAL DRIVE IS 35 TRACK
LASTT80	EQU	159	;80 TRACK DOUBLE SIDE DRIVE
BDOS	EQU	5
IDOFF	EQU	050H	;Offset into BUF0 where Lifeboat
			; ID should be placed
CR	EQU	0DH
LF	EQU	0AH
;
;************************************************************
;
;  Start the program here.
;
START:	JMP	BEGIN
;
LASTTRK: DB	LTRK		;Contains last trk # (34 for
				; a 35-track drive)
;
;  Do one-time initialization involving LASTTRK, BIOS
; vectors, and NSTRK.
;
BEGIN:	LXI	SP,STKTOP	;Set stack ptr
	LHLD	WBOOT+1		;GET BIOS VECTORS
	LXI	D,BVECT		; INTO OUR PROGRAM
	MVI	B,BVECTL
;
MVECT:	MOV	A,M
	STAX	D
	INX	H
	INX	D
	DCR	B
	JNZ	MVECT
	LXI	H,0FFFFH	;INIT TRK TABLE
	SHLD	NSTRK
	SHLD	NSTRK+2
	MOV	A,L
	STA	CURDSK		;NO CURRENT DISK YET
	LDA	IDSSPT		;Calc single density
	CALL	CALCSZ		; disk size in groups
	SHLD	IDSSIZ		;Fix ID table
	LDA	IDDSPT		;Repeat for double
	CALL	CALCSZ		; density
	SHLD	IDDSIZ
	LDA	LASTTRK		;Get ASCII # trks
	INR	A
	MVI	B,'0'-1		; by repeat subtracts
ATEN:	INR	B
	SUI	10
	JNC	ATEN		;Get 10's in B
	ADI	10+'0'		; and 1's in A
	STA	SIGNTK+1	;Fix SIGNON msg
	MOV	A,B
	STA	SIGNTK
	LXI	H,SIGNON
	CALL	MSGOT	;Tell who we are
;
LIST:	LXI	SP,STKTOP	;Reload stack ptr
			; in case here after error
	CALL	MOFF	;Turn off North Star motors
			; to allow disk switching
	LXI	H,FLIST	;Print out the menu
	CALL	MSGOT
	CALL	CNSIN	;GET REPLY
	LXI	H,CRLF	;RET CARRIAGE AFTER
	CALL	MSGOT
	STA	FUNC	;Save for later
;
;  Dispatch to routine based on choice, unless it was bad.
;
	CPI	'1'
	JZ	ALLSET	;COPY ALL
	CPI	'2'
	JZ	SYSSET	;COPY SYSTEM
	CPI	'3'
	JZ	DATSET	;COPY DATA
	CPI	'4'
	JZ	VALSET	;VALIDATE
	CPI	'5'
	JZ	FORSTD	;FORMAT DOUBLE DENSITY
	CPI	'6'
	JZ	FORSTS	;FORMAT SINGLE DENSITY
	CPI	'7'
	JZ	FORS80	;FORMAT 80-TRKS, DOUBLE SIDED
	CPI	'8'
	JZ	EXIT	;WARM BOOT
	CPI	3	;ALLOW CTRL-C FOR WARM BOOT TOO
	JZ	EXIT
	CPI	'9'
	JZ	CEXIT	;COLD BOOT
	LXI	H,SELER
	CALL	MSGOT	;BAD NUMBER GIVEN
	JMP	LIST	;GO ASK AGAIN
;************************************************************
;
SIGNON:	DB	CR,LF,'North Star Copy V4.2 of 09/17/81 -- '
SIGNTK:	DB	((LTRK+1)/10)+'0',((LTRK+1) MOD 10)+'0'
	DB	' track version$'
FLIST:	DB	CR,LF
	DB	CR,LF,'Function List:'
	DB	CR,LF
	DB	CR,LF,'  1. Copy all tracks, using density'
	DB	' of source disk'
	DB	CR,LF,'  2. Copy system tracks only,'
	DB	' using density of source disk'
	DB	CR,LF,'  3. Copy data tracks only, using'
	DB	' density of source disk'
	DB	CR,LF,'  4. Validate'
	DB	CR,LF,'  5. Format DOUBLE density'
	DB	CR,LF,'  6. Format SINGLE density'
	DB	CR,LF,'  7. Format 80 trk ds/dd'
	DB	CR,LF,'  8. Warm boot'
	DB	CR,LF,'  9. Cold boot'
	DB	CR,LF
	DB	CR,LF,'Enter function number: $'
SELER:	DB	CR,LF,'Not a valid number',CR,LF,'$'
;
SRCMSG:	DB	'Enter source$'
DSTMSG:	DB	'Enter destination$'
SDMSG:	DB	' drive (A-'
	DB	NDRIVE-1+'A',') or <ctrl-C> to reselect: $'
;
MESGA:	DB	CR,LF,'Compare error on track $'
MESGB:	DB	' (hex) sector $'
MESGC:	DB	' -- permanent $'
MESGD:	DB	CR,LF,'Source read error exit $'
MESGE:	DB	CR,LF,'Destination write error exit $'
MESGF:	DB	CR,LF,'Destination read error exit $'
S1:	DB	CR,LF,'Ready to copy $'
S2:	DB	' from disk '
SSRC:	DB	0,': to disk '
SDST:	DB	0,':.$'
SSYS:	DB	'system tracks only$'
SDAT:	DB	'data tracks only$'
SALL:	DB	'all tracks$'
DFOR:	DB	CR,LF,'Ready to format and validate disk '
DFORD:	DB	0,': in DOUBLE density,',CR,LF
	DB	'overwriting all previous data there.$'
SFOR:	DB	CR,LF,'Ready to format and validate disk '
SFORD:	DB	0,': in SINGLE density,',CR,LF
	DB	'overwriting all previous data there.$'
FOR80:	DB	CR,LF,'Ready to format and validate '
	DB	'80 trk double sided disk '
FOR80D:	DB	0,': in',CR,LF,'DOUBLE density, '
	DB	'overwriting all previous data there.$'
SVAL:	DB	CR,LF,'Ready to validate disk '
SVALS:	DB	0,':.$'
GOMSG:	DB	CR,LF,CR,LF
	DB	'If that is what you want, switch disks if'
	DB	CR,LF
	DB	'appropriate and type <CR> when ready: $'
INSERT:	DB	CR,LF,'Insert system disk in drive A'
	DB	' and type <CR> to $'
BOOT:	DB	' boot: $'
WMSG:	DB	'warm$'
CMSG:	DB	'cold$'
ABMSG:	DB	' ^C',CR,LF,'** Console abort **$'
DONMSG:	DB	CR,LF,'Function complete.$'
CRLF:	DB	CR,LF,'$'
CTCMSG:	DB	'^C$'
;
;  This is the Lifeboat-compatible label we write to a
; double density disk when we format it. Note that the
; contents of the second part of the label are actually
; a CP/M disk parameter block. We assume this must match
; the disk we are formatting, so this DPB is for a
; double-density single-sided disk. The name seems to
; be ignored, so we use NSCOPY (Lifeboat uses FORMAT).
; The rest of the data between the name and the DPB
; has no meaning to us, so we are just blindly using
; the stuff Lifeboat puts there, which doesn't seem
; to hurt.
;
IDD:	DB	'NSCOPY  '	;Format program name
	DB	0,0,0,0,90H,0,0,0,90H	; ???
IDDSPT:	DW	40	;DPB - Sectors per track
	DB	3	;Block shift factor
	DB	07H	;Block mask
	DB	0	;Exm mask
IDDSIZ:	DW	(((LTRK-1)*40)/8)-1 ;Disk size in groups - 1
	DW	63	;Directory max entries
	DB	11000000B ;Alloc 0
	DB	0	;Alloc 1
	DW	16	;Check size
	DW	2	;Track offset to data
;
IDLEN	EQU	$-IDD	;# bytes in label
;
;  SINGLE DENSITY LIFEBOAT LABEL (SEE IDD ABOVE)
;
IDS:	DB	'NSCOPY  '	;Format program name
	DB	0,0,0,0,10H,0,0,0,10H	; ???
IDSSPT:	DW	20	;DPB - Sectors per track
	DB	3	;Block shift factor
	DB	07H	;Block mask
	DB	0	;Null mask
IDSSIZ:	DW	(((LTRK-1)*20)/8)-1 ;Disk size in groups - 1
	DW	63	;Directory max entries
	DB	11000000B ;Alloc 0
	DB	0	;Alloc 1
	DW	16	;Check size
	DW	3	;Track offset to data
;
;  DUMMY 80 TRACKS PER SIDE, DOUBLE SIDE, DOUBLE
; DENSITY LABEL. WE PUT THIS ON OUR DRIVE FOR OUR
; OWN INFORMATION, SINCE LIFEBOAT DOES NOT SUPPORT
; ANY SUCH FORMAT.
;
ID80:	DB	'NSCOPY  '	;Format program name
	DB	0,0,0,0,0F0H,0,0,0,0F0H	; ???
	DW	40	;DPB - Sectors per track
	DB	4	;Block shift factor
	DB	0FH	;Block mask
	DB	0	;Exm mask
	DW	395-1	;Disk size in groups - 1
	DW	127	;Directory max entries
	DB	11000000B ;Alloc 0
	DB	0	;Alloc 1
	DW	32	;Check size
	DW	2	;Track offset to data
;
;  CALCSZ calculates a group size for a disk whose
; number of 128-byte sectors is supplied in acc on
; entry. The two-byte result is returned in HL.
; Note that only the formula (LASTTRK-1)*SECTORS/8-1
; is used, so the disk shouldn't be too big if this
; is to be valid.
;
CALCSZ:	MOV	E,A	;Get # sectors/trk
	MVI	D,0	; into DE
	LDA	LASTTRK	;Get # trks
	DCR	A	; - 1 into acc
	LXI	H,0	;Init total # sectors
;
CSZ5:	DAD	D	;Count up sectors
	DCR	A
	JNZ	CSZ5
	MVI	B,3	;Divide result by 8
;
CSZ10:	MOV	A,H	;Shift right
	ORA	A
	RAR
	MOV	H,A
	MOV	A,L
	RAR
	MOV	L,A
	DCR	B	; 3 times for divide
	JNZ	CSZ10
	DCX	H	; - 1 to complete
	RET		;Ret answer in HL
;
;  ASKSRC IS USED TO GET THE SOURCE DRIVE NAME FROM
; THE USER. SRCDSK IS SET ON RETURN WITH A VALUE FROM
; 0 TO NDRIVE-1, AND ACC CONTAINS THE ASCII DRIVE
; LETTER. IF USER TYPED CTRL-C, WE EXIT BACK TO LIST.
;
ASKSRC:	LXI	H,SRCMSG	;GET SOURCE
	CALL	ASKC
	JC	ASKSRC	;GO BACK IF BAD LETTER
	CPI	NDRIVE
	JNC	ASKSRC
	STA	SRCDSK	; ELSE SET DRIVE NUMBER
	ADI	'A'	;RESTORE LETTER FOR CALLER
	STA	SSRC	;SET FOR COPY FUNCTIONS
	RET
;
;  ASKDST IS USED TO GET THE DESTINATION DRIVE
; NAME. DSTDSK IS SET ON RETURN WITH A VALUE FROM
; 0 TO NDRIVE-1, AND ACC CONTAINS THE ASCII DRIVE
; LETTER. IF USER TYPED CTRL-C, WE EXIT BACK TO LIST.
;
ASKDST:	LXI	H,DSTMSG	;GET DESTINATION
	CALL	ASKC
	JC	ASKDST	;GO BACK IF BAD LETTER
	CPI	NDRIVE
	JNC	ASKDST
	STA	DSTDSK	; ELSE SET DRIVE NUMBER
	ADI	'A'	;RESTORE LETTER FOR CALLER
	STA	SDST	;SET FOR COPY FUNCTIONS
	RET
;
ASKC:	CALL	MSGOT
	LXI	H,SDMSG
	CALL	MSGASK
	SUI	'A'
	RET
;
;  MAIN COPY ROUTINE HERE. PERFORM FUNCTION BETWEEN
; TRACKS SPECIFIED BY THE TWO BYTES AT TRKSRT.
;
COPY:	MVI	A,1	;ASSUME ONE TRK AT A TIME
	STA	NTR	; FOR FORMAT OR VALIDATE CASES
	LDA	FUNC	;CHECK FUNCTION
	CPI	'5'	;FORMAT?
	JNC	FRSTRK	;DENSITY ALL SET IF SO
	CALL	SETDD	;ASSUME DOUBLE D IF NOT
	XRA	A	;SET UP SOURCE READ IF SO
	STA	HSTSEC	; ON SECTOR ZERO
	STA	HSTTRK	; OF TRACK ZERO
	LDA	SRCDSK
	STA	HSTDSK
	LXI	H,MESGD	;IN CASE OF ERROR
	SHLD	IOEMSG
	LXI	H,BUF1	;PT TO SCRATCH BUFFER
	SHLD	HSTADR
	CALL	SETUP	;SELECT DISK AND SEEK
	CALL	READ	;READ THE SECTOR
	LDA	ERFLAG	;CHECK FOR ERRORS
	ORA	A
	CNZ	FAILR	;ERROR IF CAN'T READ SECTOR
	LDA	NSDENS	;CHECK DENSITY
	ORA	A
	JZ	SETNTR	;ALL SET IF DOUBLE D
	CALL	SETSD	;SET SINGLE DENS VARIABLES
	LDA	FUNC	;IF SINGLE, MAY NEED ADJUSTMENTS
	CPI	'1'	;COPY ALL TRACKS?
	JZ	SETNTR	;ALL SET IF SO
	CPI	'4'	;VALIDATE?
	JZ	FRSTRK	;ALSO ALL SET
	CPI	'2'	;COPY SYSTEM TRACKS ONLY?
	LXI	H,3*256+0 ;ASSUME SO
	JZ	SDSYS	;GO STUFF IF SO
	LDA	LASTTRK	;Data copy if not
	INR	A
	MOV	H,A
	MVI	L,0
SDSYS:	SHLD	TRKSRT	;NEW START OR END
;
SETNTR:	LDA	FUNC	;CHECK FOR VALIDATE
	CPI	'4'	; IN CASE DOUBLE DENSITY
	JZ	FRSTRK	;ONE TRACK AT A TIME IF SO
	LHLD	TRKSIZ	;GET TRACK SIZE
	LDA	WBOOT+2	;GET BIOS BASE PAGE
	DCR	A	;ADJUST FOR COMPARE
	XCHG		;TRK SIZE TO DE
	LXI	H,BUF0	;BUF STARTS HERE
	MVI	B,0	;INIT TRACK CTR
;
SNTRL:	DAD	D	;ADD ANOTHER BUFFER
	JC	SNTRD	;OUT IF NO MORE MEM
	INR	B	;ASSUME ROOM
	CMP	H	;UP TO BIOS YET?
	JNC	SNTRL	;GO BACK IF NOT
	DCR	B	;ONE LESS TRACK IF SO
SNTRD:	MOV	A,B
	STA	NTR	;CAN READ THIS MANY
;
FRSTRK:	LDA	TRKSRT	;SET 1ST TRK TO DO
	STA	HSTTRK
	ADI	'0'	;GET ASCII EQUIV
	STA	CTRK	; FOR STATUS REPORTING
;
RDLOOP:	LDA	HSTTRK	;REMEMBER START TRACK FOR
	STA	STRK	; THIS BUFFER, FOR WRITING
	LXI	H,BUF0	;INIT INPUT BUFFER ADDRESS
	SHLD	BUF0A
	LDA	NTR	;CAN FIT THIS MANY TRACKS
;
RDNT:	STA	NTRC	;UPDATE # TRKS ROOM LEFT
	CALL	BCNSCK	;CHECK FOR CONSOLE ABORT
	ORA	A
	CNZ	ABORT	;IF CTRL-C, NO RETURN
	LDA	CTRK	;SHOW NEXT TRACK TO DO
	PUSH	PSW	;SAVE FOR BOP
	CALL	CNSOT	;OUTPUT DIGIT
	POP	PSW	;GET BACK AGAIN
	INR	A	;NOW BOP BY ONE
	CPI	'9'+1	;GOING OVER NOW?
	JC	CTRKNZ	;PASS IF NOT
	MVI	A,' '	;SPACE OVER EACH TEN
	CALL	CNSOT
	MVI	A,'0'	;REWIND DIGIT
CTRKNZ:	STA	CTRK	;SAVE NEXT TRK #
	LDA	FUNC	;SEE IF FORMATTING ONLY
	CPI	'5'
	JNC	WONLY	;WRITE ONLY IF SO
	LDA	SRCDSK	;IF NOT,
	STA	HSTDSK	; SELECT SOURCE DRIVE
	CALL	READT	;READ ENTIRE TRACK
	LDA	FUNC	;CHECK FUNCTION
	CPI	'4'	;VALIDATE?
	JZ	SKPW	;SKIP WRITE IF SO
	LDA	HSTTRK	;BOP TRK #
	INR	A
	STA	HSTTRK
	LXI	H,TRKSRT+1
	CMP	M	;READ LAST TRACK?
	JZ	WRLOOP	;GO WRITE IF SO
	LHLD	BUF0A	;ADVANCE BUF PTR IF NOT
	XCHG
	LHLD	TRKSIZ	; BY ONE TRACK
	DAD	D
	SHLD	BUF0A
	LDA	NTRC	;COUNT OFF LAST TRACK
	DCR	A
	JNZ	RDNT	;IF MORE ROOM, GO READ
;
WRLOOP:	LDA	STRK	;REWIND CURR TRK #
	STA	HSTTRK	; FOR WRITE
	LXI	H,BUF0	;DITTO BUF PTR
	SHLD	BUF0A
;
;  HERE IF FORMATTING ONLY
;
WONLY:	LDA	DSTDSK	;SELECT DESTINATION DRIVE
	STA	HSTDSK
	LDA	NTR	;WRITE THIS MANY TRKS
;
WRNT:	STA	NTRC	;THIS MANY LEFT TO WRITE
	XRA	A	;REWIND COMPARE ERR CNT
	STA	CMPERR
RETRYW:
	CALL	BCNSCK	;CHECK FOR CONSOLE ABORT
	ORA	A
	CNZ	ABORT	;IF CTRL-C, NO RETURN
	CALL	WRITET	;WRITE TRACK
	CALL	COMPT	;REREAD AND COMPARE
	JNZ	RETRYW	;RETRY IF ERR
;
;  Check to see if we just wrote track zero. If so,
; make sure the Lifeboat label area is set to 0E5H's,
; just in case we are formatting. If we are in fact
; copying, it doesn't matter, since we're done with
; this track anyway.
;
	LDA	HSTTRK	;See if just wrote track zero
	ORA	A
	JNZ	SKPW	;Pass if not
	LXI	H,BUF0+IDOFF	;If so, pt to label area
	MVI	B,IDLEN	;This many bytes to fill
;
FFIX:	MVI	M,0E5H	;Stuff an E5
	INX	H
	DCR	B	;Count it off
	JNZ	FFIX	;Do the whole label
;
;  HERE TO SKIP TRACK WRITE ON VALIDATE ONLY
;
SKPW:	LDA	HSTTRK
	INR	A
	STA	HSTTRK
	LXI	H,TRKSRT+1 ;POINT TO LAST TRACK+1 STORAGE
	CMP	M	;ARE WE DONE?
	RZ		;RETURN TO CALLER IF SO
	LHLD	BUF0A	;ADVANCE BUF PTR IF NOT
	XCHG
	LHLD	TRKSIZ	; BY ONE TRACK
	DAD	D
	SHLD	BUF0A
	LDA	NTRC	;COUNT OFF LAST TRK
	DCR	A
	JNZ	WRNT	;GO BACK IF MORE TO WRITE
	JMP	RDLOOP	;IF NOT, GO READ NEXT BUFFER
;
;
COMPT:	LXI	H,MESGF	;Load address of msg for
	SHLD	IOEMSG	; destination read error
	LXI	H,BUF1
	CALL	RT2	;REREAD INTO BUF1
	LHLD	TRKSIZ	;GET TRACK SIZE IN BYTES
	MOV	B,H	; INTO BC
	MOV	C,L
	LHLD	BUF0A
	LXI	D,BUF1
CMPLP:	LDAX	D
	CMP	M
	JNZ	CERR
	INX	H
	INX	D
	DCX	B
	MOV	A,C	;End of buffer yet?
	ORA	B
	JNZ	CMPLP	;Keep checking if not
	RET
CERR:	PUSH	H
	LXI	H,MESGA
	CALL	MSGOT	;REPORT COMPARE ERROR
	LDA	HSTTRK
	CALL	HXBOT
	LXI	H,MESGB
	CALL	MSGOT
	POP	H
	LDA	BUF0A	;CALC OFFSET INTO BUFFER
	SUB	L
	LDA	BUF0A+1	;ONLY NEED KEEP HIGH
	SBB	H
	MOV	H,A	;SAVE FOR NOW
	LDA	NSCNT	;GET DENSITY FLAG
	ORA	A	;SET ZERO COND CODE IF DOUBLE DENS
	MOV	A,H
	JNZ	CERRSD	;NUMBER RIGHT IF SINGLE DENS
	RAR		;A DOUBLE DENS SECTOR IS TWO PAGES
CERRSD:	CALL	HXBOT	;PRINT SECTOR
	LDA	CMPERR
	INR	A
	STA	CMPERR	;INCREMENT ERROR COUNT
	CPI	10
	RNZ
	LXI	H,MESGC	;REPORT GIVING UP
	CALL	MSGOT
	XRA	A
	RET
;
;
READT:	LXI	H,MESGD	;Load address of msg for
	SHLD	IOEMSG	; source read error
	LHLD	BUF0A
;
;  ENTER HERE WITH IOEMSG AND HL PRELOADED FOR
; COMPARE TRACK READ
;
RT2:	SHLD	HSTADR
	CALL	SETUP		;SELECT DRIVE AND
				; SEEK TO TRACK
	LDA	ERFLAG		;CHECK FOR ERRORS
	ORA	A
	CNZ	FAILR	;Index pulse not found is fatal
	MVI	C,0
RT3:	PUSH	B
	MOV	A,C
	STA	HSTSEC
	CALL	READ
	LDA	NSDENS	;CHECK FOR DENSITY MISMATCH
	MOV	C,A
	LDA	NSCNT
	CMP	C
	JZ	RT4	;GO ON IF OK
	MVI	A,5	;SET ERROR IF NOT
	STA	ERFLAG
	LXI	H,SDEMSG
	CALL	DSKERR	;RAT ON BAD SECTOR
RT4:	LDA	ERFLAG
	ORA	A	;Any errors?
	CNZ	FAILR
	LHLD	HSTADR	;Advance read address
	XCHG
	LHLD	BLKSIZ
	DAD	D
	SHLD	HSTADR
	POP	B
	MVI	A,LASTSEC
	CMP	C
	RZ
	INR	C
	JMP	RT3
;
;
WRITET:	LHLD	BUF0A
	SHLD	HSTADR
	CALL	SETUP		;SELECT DRIVE AND
				; SEEK TO TRACK
	LDA	ERFLAG		;CHECK FOR ERRORS
	ORA	A
	CNZ	FAILW	;Index pulse not found is fatal
	MVI	C,0
WT3:	PUSH	B
	MOV	A,C
	STA	HSTSEC
	CALL	WRITE
	LDA	ERFLAG
	ORA	A	;Any errors?
	CNZ	FAILW
	LHLD	HSTADR	;Advance write address
	XCHG
	LHLD	BLKSIZ
	DAD	D
	SHLD	HSTADR
	POP	B
	MVI	A,LASTSEC
	CMP	C
	RZ
	INR	C
	JMP	WT3
;************************************************************
SYSSET:
	CALL	ASKSRC
	CALL	ASKDST
	CALL	PRNTS1
	LXI	H,SSYS
	CALL	MSGOT
	CALL	PRNTS2
	MVI	L,0		;FIRST TRACK TO TRANSFER
	MVI	H,2		;LAST TRACK PLUS ONE
	JMP	PUTPAR
;************************************************************
DATSET:
	CALL	ASKSRC
	CALL	ASKDST
	CALL	PRNTS1
	LXI	H,SDAT
	CALL	MSGOT
	CALL	PRNTS2
	MVI	L,2		;FIRST TRACK TO TRANSFER
	LDA	LASTTRK		;Last track plus one
	INR	A
	MOV	H,A
	JMP	PUTPAR
;************************************************************
ALLSET:
	CALL	ASKSRC
	CALL	ASKDST
	CALL	PRNTS1
	LXI	H,SALL
	CALL	MSGOT
	CALL	PRNTS2
	JMP	DOALL		;DO ALL TRACKS
;************************************************************
;
;  Format disk. Fill BUF0 with 0E5H's, except for a Lifeboat-
; compatible label, so that Lifeboat CP/M can read and write
; our disks. After track zero is formatted, COPY will make
; the entire buffer 0E5H again by overwriting the label.
;
FORSTD:	CALL	ASKDST
	STA	DFORD
	LXI	H,DFOR
	CALL	MSGOT
	LDA	LASTTRK		;Set for all tracks
	INR	A
	MOV	H,A
	MVI	L,0
	SHLD	TRKSRT
	CALL	SETDD		;SET UP DOUBLE DENS VARS
	LXI	D,IDD		;PT TO DOUBLE DENS ID
;
FLBOTH:				;MERGE HERE FROM FORSTS
	MOV	B,H		;MOVE # BYTES/TRK TO BC
	MOV	C,L
	LXI	H,BUF0		;Fill track buffer with E5's
FL:	MVI	M,0E5H		;Set a byte to E5
	DCX	B		; and count it off
	INX	H		;Bop buffer ptr
	MOV	A,B
	ORA	C
	JNZ	FL		;Go back if more to set
	LXI	H,BUF0+IDOFF	;Now time to put in label
	MVI	B,IDLEN		;Move in this many bytes
;
FL5:	LDAX	D		;Get label byte
	MOV	M,A		; and store it
	INX	H		; then bop ptrs
	INX	D
	DCR	B		;Count off a byte
	JNZ	FL5		;Go back if more
	JMP	AGIN		;DO ALL TRACKS
;
;  SINGLE DENSITY FORMAT DISK
;
FORSTS:	CALL	ASKDST
	STA	SFORD
	LXI	H,SFOR
	CALL	MSGOT
	LDA	LASTTRK		;Set for all tracks
	INR	A
	MOV	H,A
	MVI	L,0
	SHLD	TRKSRT
	CALL	SETSD		;SET UP SINGLE DENS VARS
	LXI	D,IDS		;PT TO SINGLE DENS ID
	JMP	FLBOTH		;GO MERGE WITH FORSTD
;
;  80 TRK DRIVE FORMAT
;
FORS80:	CALL	ASKDST
	STA	FOR80D
	LXI	H,FOR80
	CALL	MSGOT
	LXI	H,(LASTT80+1) SHL 8 ;SET FOR ALL TRACKS
	SHLD	TRKSRT
	CALL	SETDD		;SET UP DOUBLE DENS VARS
	LXI	D,ID80		;80 TRK ID
	JMP	FLBOTH		;GO MERGE WITH FORSTD
;
;  DOUBLE DENSITY VARIABLES SETUP, HL = BYTES/TRK ON RETURN
;
SETDD:	LXI	H,512
	SHLD	BLKSIZ		;# BYTES/SECTOR
	MVI	A,00H
	STA	NSCNT		;DENSITY FLAG = DOUBLE
	LXI	H,512*(LASTSEC+1) ;# BYTES/TRACK
	SHLD	TRKSIZ		;SET TRACK SIZE
	RET
;
;  SINGLE DENSITY VARIABLES SETUP, HL = BYTES/TRK ON RETURN
;
SETSD:	LXI	H,256
	SHLD	BLKSIZ
	MVI	A,80H
	STA	NSCNT
	LXI	H,256*(LASTSEC+1)
	SHLD	TRKSIZ
	RET
;************************************************************
VALSET:
	CALL	ASKSRC
	STA	SVALS
	LXI	H,SVAL
	CALL	MSGOT
;
DOALL:	MVI	L,0		;1st trk to write
	LDA	LASTTRK		;Last trk + 1
	INR	A
	MOV	H,A
;
PUTPAR:	SHLD	TRKSRT		;PUT PARAMETERS IN
				; TRKSRT AND TRKSRT+1
;
AGIN:	LXI	H,GOMSG		;Wait until user ready
	CALL	MSGASK		;Print message and get reply
	CPI	CR
	JNZ	AGIN
;************************************************************
	CALL	COPY		;MAIN ROUTINE
	LXI	H,DONMSG	;SAY WE'RE DONE
	CALL	MSGOT
	JMP	LIST		;DONE. GO ASK FOR MORE TO DO.
;
;
PRNTS1:	LXI	H,S1
	JMP	MSGOT
;
PRNTS2:	LXI	H,S2
	JMP	MSGOT
;************************************************************
FAILR:	LHLD	IOEMSG	;Get addr of appro error msg
	JMP	DIE
FAILW:	LXI	H,MESGE
DIE:	CALL	MSGOT
	JMP	LIST		;GO TRY SOMETHING ELSE
;
;  HERE ON CONSOLE ABORT
;
ABORT:	CALL	BCNSIN	;GET CONSOLE CHAR
	CPI	3	;CTRL-C?
	RNZ		;IGNORE IF NOT
	LXI	H,ABMSG
	CALL	MSGOT	;REPORT STOPPING
	JMP	LIST	;GO TRY SOMETHING ELSE
;************************************************************
EXIT:	LXI	H,INSERT
	CALL	MSGOT
	LXI	H,WMSG
	CALL	MSGOT
	LXI	H,BOOT
	CALL	MSGASK		;WAIT BEFORE REBOOTING
	CPI	CR
	JNZ	EXIT
	JMP	WBOOT		;GO WARM BOOT CP/M
;
CEXIT:	LXI	H,INSERT
	CALL	MSGOT
	LXI	H,CMSG
	CALL	MSGOT
	LXI	H,BOOT
	CALL	MSGASK
	CPI	CR
	JNZ	CEXIT
	JMP	CBOOT		;GO COLD BOOT NORTH STAR
;
;
;
;
;  NORTH STAR MEMORY MAPPED I/O ADDRESSES
;
WDATA	EQU	0E900H		;WRITE DATA. DATA IS
				; LOW 8 ADRESS BITS.
CORDER	EQU	0EA00H		;CONTROLLER ORDER
CCMND	EQU	0EB00H		;CONTROLLER COMMAND
;
;  MOFF SHUTS OFF THE NORTH STAR DRIVE MOTORS
; AND RESETS THE CONTROLLER.
;
MOFF:	LDA	CCMND+17H	;RESET CONTROLLER
	RET
;
;  WRITEHST PERFORMS THE PHYSICAL WRITE TO THE
; NORTH STAR DISK. ON ENTRY, SECTOR IS IN HSTSEC. ON
; EXIT, ERROR FLAG IS IN ERFLAG (ZERO IF NONE).
;
;  ***  INTERRUPTS MUST BE DISABLED HERE  **
;
WRITE:	DI			;WRITE NORTH STAR
	XRA	A
	STA	ERFLAG		;NO ERROR YET
	LDA	CCMND+20H	;GET B-STATUS
	ANI	02H		;IS DISK WRITE-PROTECTED?
	MVI	A,6		;ASSUME SO, ERROR CODE 6
	STA	ERFLAG
	JNZ	WPERR		;WRITE ALWAYS FAILS IF SO
	CALL	SEKSEC		;WAIT FOR OUR SECTOR
	LHLD	HSTADR		;GET ADDRESS TO WRITE FROM
	MVI	B,31		;ASSUME DOUBLE DENSITY WRITE
	MVI	E,2
	LDA	NSCNT		; THEN CHECK
	MOV	C,A		;PUT DATA COUNT IN C
	ORA	A
	JZ	WRIT5		;GO ON IF DOUBLE D
	MVI	B,15		; ELSE CORRECT
	DCR	E
WRIT5:	LDA	CCMND+16H	;INITIATE SECTOR WRITE
;
WRIT10:	LDA	CCMND+15H	;KEEP MOTORS RUNNING, GET A-STAT
	ANI	08H		;MUST LOOP UNTIL 96 USEC WINDOW
	JNZ	WRIT10		; PAST, SO LOOP WHILE WI TRUE
;
;  NOW WRITE THE 15 OR 31 BYTES OF LEADING ZEROES ON THE SECTOR
;
WRIT15:	LDA	WDATA+00H	;WRITE A BYTE OF ZEROES
	NOP			;KILL TIME
	MVI	D,WDATA SHR 8	;KILL TIME AND LOAD D FOR LATER
	DCR	B		;COUNT OFF A ZERO BYTE
	JNZ	WRIT15		;DO THEM ALL
;
;  FOLLOW WITH ONE OR TWO SYNCH BYTES
;
WRIT17:	LDA	WDATA+0FBH	;WRITE A SYNCH BYTE
	DCR	E
	JNZ	WRIT17		;GO BACK IF DOUBLE D
;
;  NOW WRITE OUT 256 OR 512 BYTES. DO THIS TWO AT A TIME SO
; WE CAN USE SINGLE PRECISION COUNTER. B IS ZERO FROM ABOVE
; TO INIT THE CRC BYTE.
;
WRIT20:	MOV	A,M		;GET NEXT BYTE TO OUTPUT
	MOV	E,A		; IN OUTPUT REG
	XRA	B		;ADD INTO CHECKSUM
	RLC
	MOV	B,A		;LEAVE CHECKSUM IN B
	LDAX	D		;WRITE DATA BYTE TO DISK
	INX	H		;BOP BUF PTR
	MOV	A,M		;REPEAT FOR NEXT BYTE
	MOV	E,A
	XRA	B
	RLC
	MOV	B,A
	LDAX	D
	INX	H
	DCR	C		;COUNT OFF LAST PAIR OF BYTES
	JNZ	WRIT20		;GO BACK IF MORE TO WRITE
	MOV	E,B		;IF NOT, TIME TO WRITE CHECKSUM
	INX	B		;KILL TIME
	LDAX	D		; THEN WRITE THE BYTE
	XRA	A		;DONE, INDICATE NO ERROR
	STA	ERFLAG
;
WHSTRT:	RET			; AND RETURN TO CALLER
;
WPERR:	LXI	H,WPEMSG	;WRITE PROTECT ERROR
	CALL	DSKERR		;TELL OPERATOR
	JMP	WHSTRT		; THEN LEAVE
;
;  READHST PERFORMS THE PHYSICAL READ FROM THE
; NORTH STAR DISK. ON ENTRY, SECTOR IS IN HSTSEC. ON
; EXIT, ERROR FLAG IS IN ERFLAG (ZERO IF NONE).
;
;  ***  INTERRUPTS MUST BE DISABLED HERE  ***
;
READ:	DI			;READ NORTH STAR
	MVI	A,10		;RETRY COUNT ON ERROR
	STA	RTCNT
;
READRT:	CALL	SEKSEC		;WAIT FOR OUR SECTOR
	MVI	B,08CH		;COUNT FOR SYNCH CHAR LOOP
	LXI	D,CCMND+40H	;SET UP READ DATA REGS
;
;  WAIT FOR RE SO WE CAN DO SECTOR READ, AND TEST
; THE DOUBLE DENSITY BIT.
;
WAITRE:	LDA	CCMND+10H	;GET A-STATUS
	ANI	04H		;CHECK RE
	JZ	WAITRE		;LOOP UNTIL RE TRUE
	XTHL			;KILL TIME TO GET INTO ZEROES
	XTHL
	XTHL
	XTHL
	XTHL
	XTHL
	XTHL
	XTHL
	LDA	CCMND+10H	;GET A-STATUS AGAIN
	ANI	20H		;ARE WE READING DOUBLE DENS?
	RAL			;GET ZERO IN NSDENS IF SO
	RAL			; ELSE GET 80H
	XRI	80H
	STA	NSDENS
;
;  NOW WAIT FOR SYNCH CHAR DETECTED. ERROR IF WE HAVE TO
; WAIT TOO LONG.
;
READ5:	LDA	CCMND+10H	;WAIT FOR SYNCH CHAR DETECTED
	RRC			;CHECK BIT
	JC	READ15		;OUT IF GOT IT
	DCR	B		;COUNT DOWN IF NOT
	JNZ	READ5		;GO BACK IF STILL OK
	MVI	A,1		;IF WAITED TOO LONG, SYNCH ERROR
	LXI	H,SYEMSG	;SYNCH ERROR MESSAGE
;
RERR:	STA	ERFLAG		;STORE ERROR CODE
	LDA	RTCNT		;COUNT OFF A RETRY
	DCR	A
	STA	RTCNT
	JNZ	READRT		;IF COUNT LEFT, GO RETRY
;
;  READ ERROR RETRIES FAILED. FLAG ERROR.
;
FRERR:	CALL	DSKERR		;TELL OPERATOR OF ERROR THEN OUT
;
RHSTRT:	RET
;
;  READ THE DATA INTO HSTBUF
;
READ15:	LHLD	HSTADR		;READ INTO HERE
	MVI	B,0		;INIT CHECKSUM BYTE
	LDA	NSDENS		;LOAD COUNT
	MOV	C,A		; INTO C-REG
;
READ20:	LDAX	D		;READ NEXT DATA BYTE
	MOV	M,A		; AND PUT IT IN BUFFER
	XRA	B		;ADD TO CHECKSUM
	RLC
	MOV	B,A
	INX	H		;BOP BUFFER PTR
	NOP			;KILL TIME
	LDAX	D		;GET NEXT BYTE
	MOV	M,A
	XRA	B
	RLC
	MOV	B,A
	INX	H
	DCR	C		;COUNT OFF LAST PAIR
	JNZ	READ20		;GO BACK IF MORE TO DO
	LDAX	D		; ELSE READ CRC BYTE
	XRA	B		; AND CHECK IT AGAINST OURS
	STA	ERFLAG		;IF OK, ZERO EFLAG
	JZ	RHSTRT		; AND RETURN TO CALLER
	MVI	A,2		; ELSE FLAG CHECKSUM ERROR
	LXI	H,RERMSG	;MESSAGE IF NEEDED
	JMP	RERR		;GO MAYBE RETRY
;
;  SETUP SELECTS THE UNIT CORRESPONDING TO HSTDSK,
; SEEKS TO THE TRACK IN HSTTRK. WRITE PRECOMPENSATION
; IS SET IF REQUIRED AS WELL. ON ERROR, ERFLAG CONTAINS
; A NON-ZERO VALUE ON RETURN.
;
SETUP:	MVI	B,00H		;ASSUME TOP SIDE
	LDA	HSTTRK		;CHECK TARGET TRK
	STA	TRUTRK		;ASSUME GOING HERE
	SUI	80		;GOING TO SIDE TWO?
	JC	SIDE1		;PASS IF NOT
	MOV	B,A		;CALC # ON OPPOSITE SIDE
	MVI	A,80-1		; TO MOVE BACK TOWARD
	SUB	B		; TRK ZERO
	MVI	B,40H		;SIDE TWO SELECT
	STA	TRUTRK		;GOING HERE
SIDE1:
	LDA	NSCNT		;GET DOUBLE D BIT
	XRI	80H
	MOV	C,A		; INTO C-REG
	LDA	HSTDSK		;WANT THIS DISK
	INR	A		;REMAP TO CONTROLLER MASK
	CPI	03H		;IF UNIT 1 OR 2,
	JC	SET0		; GOT CORRECT MASK
	RAL			;IF 3 OR 4, MUST MAP
	ANI	0CH		; TO 4 OR 8 RESPECTIVELY
SET0:	ORA	C		;OR IN DENSITY BIT
	ORA	B		;OR IN SIDE MASK
	MOV	C,A		;SAVE IN REG C
	CALL	WAIT1S		;WAIT A SECTOR TIME, GET B-STAT
	ANI	10H		;ARE MOTORS ALREADY ON?
	LDA	CCMND+15H	;GIVE THEM EXTRA KICK ANYWAY
	JNZ	SET5		;BRANCH IF SO
	MVI	D,17H		;WAIT FOR MOTORS TO
	CALL	SCWAIT		; COME UP TO SPEED IF NOT
	JMP	SET10		; THEN GO SELECT DRIVE
;
SET5:	LDA	CURDSK		;IS THIS THE CURRENT DISK?
	CMP	C		;COMPARE DISK AND SIDE
	JZ	SET20		;GO SEEK TO TRACK IF SO
;
;  MUST SELECT NEW DISK OR SIDE
;
SET10:	MOV	A,C
	STA	CURDSK		;NEW CURRENT DISK
	MVI	B,CORDER SHR 8	;SET UP FOR SELECT
	LDAX	B		; AND DO IT
	MVI	D,2		;WAIT TWO SECTOR TIMES
	CALL	SCWAIT		; BEFORE LOOKING FOR INDEX
	MVI	B,12		;DON'T LOOK TOO LONG
;
SET15:	CALL	WAIT1S		;WAIT ONE SECTOR TIME
	LDA	CCMND+10H	;GET A-STATUS
	ANI	40H		;INDEX HOLE SEEN?
	JNZ	SET20		;CAN GO SEEK NOW IF SO
	DCR	B		;COUNT DOWN IF NOT
	JNZ	SET15		; AND MAYBE GO LOOK AGAIN
;
;  INDEX HOLE NOT FOUND. THAT'S A FATAL ERROR.
;
	MVI	A,4		;NO INDEX PULSE
	STA	ERFLAG
	LXI	H,NIPMSG
	JMP	DSKERR		;GO TELL OPERATOR ON WAY OUT
;
;  SEEK TO TRACK SPECIFIED BY TRUTRK NOW, AND SET
; WRITE PRECOMPENSATION IF APPROPRIATE.
;
SET20:	CALL	SEEK		;SEEK TO TRUTRK
	LDA	TRUTRK		;WHERE ARE WE?
	CPI	14H+1		;ARE WE BEYOND TRACK 14H?
	JC	SET25		;GO ON IF NOT
	LDA	NSCNT		;WRITING DOUBLE DENSITY?
	ORA	A
	JNZ	SET25		;NO PRECOMP IF NOT
	LDA	CURDSK		;IF SO, GET CURRENT MASK
	ORI	20H		; AND SET PRECOMP BIT
	MVI	H,CORDER SHR 8
	MOV	L,A
	MOV	A,M		;SET BIT IN CONTROLLER
SET25:	XRA	A		;NO ERRORS
	STA	ERFLAG
	RET			;SETUP IS DONE
;
;  HERE TO POSITION TO HSTSEC WITHIN THE CURRENT TRACK.
;
SEKSEC:	LDA	HSTSEC		;GET TARGET SECTOR
	MOV	C,A		;IN C
	STA	ERSEC		;SET UP FOR POSSIBLE ERROR
;
SET30:	CALL	WAIT1S		;WAIT FOR NEXT SECTOR
	LDA	CCMND+35H	;KICK MOTORS AND GET SECNUM
	ANI	0FH		;STRIP NON-SECTOR BITS
	CMP	C		;IS THIS THE TARGET SECTOR?
	JNZ	SET30		;WAIT UNTIL IT IS
	RET			;POSITIONED TO SECTOR
;
;  SEEK TO TRACK SPECIFIED BY TRUTRK. CURRENT OFFSET INTO
; NSTRK TABLE IS GIVEN BY HSTDSK.
;
SEEK:	LDA	TRUTRK		;GET TARGET TRACK
	MOV	D,A		; INTO D
	LDA	HSTDSK		;GET NSTRK INDEX
	MOV	C,A
	MVI	B,0
	LXI	H,NSTRK		;PT AT TABLE
	DAD	B		; THEN AT CORRECT ENTRY
	MOV	A,M		;GET CURRENT TRACK
	INR	A		;IS THIS DISK NEW?
	JNZ	SEEK5		;PASS IF NOT
	PUSH	D		;MUST HOME HEAD IF SO
	STA	TRUTRK		; TO KNOW WHERE WE ARE
	MVI	A,90		;MAKE SURE STEP TO ZERO
	MOV	M,A
	PUSH	H
	CALL	SEEK		;GO TO TRACK ZERO
	POP	H
	POP	D
	MOV	A,D
	STA	TRUTRK		;RESTORE TRUTRK
;
SEEK5:	MOV	A,D
	SUB	M		;SEE HOW FAR AWAY WE ARE
				; FROM TARGET TRACK
	MOV	M,D		; BUT ALWAYS SET NEW TRACK
	RZ			;IF THERE, DONE
	LXI	H,CORDER+30H	;ASSUME STEPPING IN
	MOV	C,A		;SAVE STEP COUNT
	JP	STEPIN		;BRANCH IF RIGHT
	CMA			;IF WRONG, NEGATE COUNT
	INR	A
	MOV	C,A		; THEN SAVE THAT
	LDA	CCMND+20H	;GET B-STATUS
	ANI	01H		;ARE WE ON TRACK ZERO?
	RNZ			;MUST BE DONE IF SO
	MVI	L,10H		;IF NOT, STEPPING OUT
;
STEPIN:	LDA	CURDSK		;GET CURRENT UNIT MASK
	ORA	L		;FORM FINAL CORDER VALUE
	MOV	L,A
	MOV	D,M		;SET THE STEP FLIP-FLOP
	ORI	10H
	MOV	L,A
	MOV	D,M		;SET IT AGAIN FOR SOME REASON
	XRI	10H
	MOV	L,A
	MOV	D,M		;NOW RESET THE STEP FLIP-FLOP
;
;  WAIT WHILE HEAD STARTS MOVING
;
	MVI	A,14H
STEPW1:	MVI	D,38H
STEPW2:	DCR	D		;KILL TIME
	JNZ	STEPW2
	DCR	A
	JNZ	STEPW1
;
;  THE STEP RATE DETERMINES HOW LONG TO WAIT. ONE SECTOR
; TIME IS 20 MSEC. A SLOW SHUGART SA400 MAY REQUIRE 40
; MSEC, OR TWO SECTOR TIMES. MOST TYPICAL SA400'S WILL
; WORK WITH 20 MSEC, SO THAT'S WHAT WE'LL USE HERE. WE
; WILL CALL SCWAIT INSTEAD OF WAIT1S IN CASE WE NEED TO
; PATCH THIS VALUE.
;
	MVI	D,1		;NOW WAIT 20 MSEC
;	MVI	D,2		;NOW WAIT 40 MSEC
	CALL	SCWAIT
	LDA	CCMND+25H	;GET B-STATUS AND KICK MOTORS
	ANI	01H		;ARE WE AT TRACK ZERO?
	JNZ	WAIT1S		;DONE STEPPING IF SO
	DCR	C		; ELSE COUNT OFF LAST TRACK
	JNZ	STEPIN		;GO BACK IF MORE TO DO
				; ELSE WAIT ONE MORE SECTOR TIME
				; TO MAKE SURE SAFE FOR I/O
;
;  SECTOR WAIT. ON ENTRY, # SECTORS TO WAIT IS IN D.
; ON EXIT, D=0 AND ACC=A-STATUS. CALL WAIT1S TO WAIT
; ONE SECTOR TIME.
;
WAIT1S:	MVI	D,1		;WAIT ONE SECTOR TIME
;
SCWAIT:	LDA	CCMND+11H	;RESET SECTOR FLAG
SCW5:	LDA	CCMND+10H	;GET A-STATUS
	ORA	A		;CHECK SECTOR FLAG
	JP	SCW5		;WAIT FOR IT IF NOT UP
	LDA	CCMND+11H	; ELSE RESET SECTOR FLAG
	DCR	D		;COUNT DOWN WAIT COUNTER
	JNZ	SCW5		;GO BACK IF MORE TO DO
	RET			; ELSE RETURN A-STATUS IN ACC
;
;  DISK ERRORS REPORTED HERE
;
DSKERR:	PUSH	H		;SAVE BODY ADDRESS
	MVI	A,CR		;EJECT LINE
	CALL	CNSOT
	MVI	A,LF
	CALL	CNSOT
	POP	H
	CALL	MSGOT		;REPORT BODY
	LDA	HSTDSK		;THIS DISK
	ADI	'A'
	STA	ASCDSK
	LXI	H,ERRMSG
	CALL	MSGOT
	LDA	HSTTRK		;THIS TRACK
	CALL	HXBOT
	LXI	H,SECMSG
	CALL	MSGOT
	LDA	ERSEC		;THIS SECTOR
				;FALL INTO HXBOT TO FINISH
;
;  DISPLAY HEXIDECIMAL EQUIVALENT OF ACC CONTENTS
;
HXBOT:	PUSH	PSW		;SAVE CHAR
	RRC			;SWAP NIBBLES
	RRC
	RRC
	RRC
	CALL	HXNOT		;OUTPUT HIGH NIBBLE
	POP	PSW		; THEN LOW NIBBLE
HXNOT:	ANI	0FH		;STRIP HIGH NIBBLE
	CPI	10		;LESS THAN 10
	JC	HXNNM		;PASS IF SO
	ADI	'A'-'9'-1	;ADD LETTER OFFSET
HXNNM:	ADI	'0'		;MAKE ASCII
;
CNSOT:	PUSH	H		;Save all regs
	PUSH	D
	PUSH	B		; but PSW
	MOV	C,A
	CALL	BCNSOT		;OUTPUT CHAR TO CONSOLE
	POP	B		;Restore regs
	POP	D
	POP	H
	RET			;Return to caller
;
CNSIN:	PUSH	H		;SAVE ALL REGS
	PUSH	D
	PUSH	B
	CALL	BCNSIN		;READ CONSOLE CHAR
	MOV	C,A		;CHAR TO C FOR ECHO
	PUSH	PSW		;SAVE CHAR READ
	CPI	20H		;IS CHAR DISPLAYABLE?
	CNC	BCNSOT		;ECHO TO OUTPUT IF SO
	POP	PSW		;RESTORE CHAR READ
	POP	B		;RESTORE BC AND DE
	POP	D
	CPI	3		;CHECK FOR CTRL-C
	LXI	H,CTCMSG	;IN CASE GOT
	CZ	MSGOT		;ECHO IF GOT
	POP	H		;RESTORE HL NOW
	CPI	'A'+20H		;FOLD LOWER CASE TO UPPER
	RC
	CPI	'Z'+20H+1
	RNC
	SUI	20H
	RET
;
MSGASK:	CALL	MSGOT	;OUTPUT MSG @HL
	CALL	CNSIN	;GET OPERATOR REPLY
	CPI	3	;CTRL-C?
	JZ	LIST	;BACK TO MENU IF SO
	LXI	H,CRLF	;EJECT LINE IF NOT, THEN
			; RET CHAR IN ACC
;
MSGOT:	PUSH	PSW		;SAVE CALLER FLAGS
MSGL:	MOV	A,M		;LOAD CHAR
	CPI	'$'		;END OF STRING?
	JZ	MSGR		;OUT IF SO
	CALL	CNSOT		;IF NOT, OUTPUT CHAR
	INX	H		;ADVANCE PTR
	JMP	MSGL		;GO BACK FOR NEXT CHAR
;
MSGR:	POP	PSW		;RESTORE CALLER PSW
	RET
;
;  BIOS JUMP VECTORS STUFFED HERE AT PROG STARTUP
;
BVECT:	JMP	$-$	;WARM BOOT
BCNSCK:	JMP	$-$	;CONSOLE STATUS TO ACC
BCNSIN:	JMP	$-$	;CONSOLE INPUT TO ACC
BCNSOT:	JMP	$-$	;CONSOLE OUTPUT FROM C
;
BVECTL:	EQU	$-BVECT	;# BYTES OF VECTORS
;
;  DISK ERROR MESSAGES
;
WPEMSG:	DB	'Protect$'
SDEMSG:	DB	'Density mismatch$'
RERMSG:	DB	'CRC$'
SYEMSG:	DB	'No synch$'
NIPMSG:	DB	'No index$'
;
ERRMSG:	DB	' err on '
ASCDSK:	DB	' :, trk $'
SECMSG:	DB	', sec $'
;
;  NORTH STAR DRIVER CURRENT TRACK TABLE
;
NSTRK:	DS	4	;EACH ENTRY CONTAINS THE
			; LAST TRACK POSITION FOR
			; UNIT 1-4
			; 0FFH=NEVER ACCESSED
;
;  NORTH STAR DRIVER VARIABLES
;
HSTDSK:	DS	1	;HOST DISK NUMBER
ERFLAG:	DS	1	;ERROR REPORTING
CURDSK:	DS	1	;CURRENT ACTIVE DISK,
			; 0FFH=NONE YET
NSDENS:	DS	1	;DENSITY FLAG FOR LAST
			; BLOCK READ. DD=00H, SD=80H
NSCNT:	DS	1	;DENSITY FLAG AND COUNT FOR
			; NEXT BLOCK TO WRITE. DD=00H,
			; SD=80H
TRKSIZ:	DS	2	;# BYTES/TRACK
BLKSIZ:	DS	2	;# BYTES/SECTOR
HSTADR:	DS	2	;XFER MEM ADDRESS
HSTTRK:	DS	1	;HOST TRACK NUMBER
TRUTRK:	DS	1	;TRACK # AFTER SIDE ADJUST
HSTSEC:	DS	1	;HOST SECTOR NUMBER
RTCNT:	DS	1	;ERROR RETRY COUNTER
ERSEC:	DS	1	;SECTOR IN ERROR
;
;  MAIN COPY PROGRAM DATA AREA
;
TRKSRT:	DS	2	;STORAGE FOR FIRST AND
			; LAST+1 TRACK NUMBERS
BUF0A:	DS	2	;CURR READ TRK ADDR
NTR:	DS	1	;# TRKS THAT FIT IN BUF0
NTRC:	DS	1	;NTR COUNTER
STRK:	DS	1	;START TRK # IN BUF0
CTRK:	DS	1	;CURR TRK # FOR STATUS IN
			; ASCII, LOW DIGIT ONLY
CMPERR:	DS	1	;NUMBER OF COMPARE ERRORS
FUNC:	DS	1	;SELECTED FUNCTION NUMBER
SRCDSK:	DS	1	;SOURCE DRIVE NUMBER
DSTDSK:	DS	1	;DESTINATION DRIVE NUMBER
IOEMSG:	DS	2	;FAILR (read error) msg addr
STK:	DS	128
STKTOP:	DS	1
;
BUF1:	DS	512*(LASTSEC+1)	;COMPARE BUFFER
BUF0:			;BIG READ BUFFER (UP TO BIOS)
;
	END

