;==============================================================
; MICMOD MICROPOLIS MODULE FOR THE POLEX MONITOR,
; FOR THE POLYMORPHIC SYSTEMS POLY-88
;
; INSTALLS IN THE 3RD CPU BOARD ROM SOCKET, TO ADD SUPPORT FOR
; THE MICROPOLIS FLOPPY SUBSYSTEM
;
; THE POLY-88 MONITOR IS STILL ACTIVE, PERFORMING THE
; INTERRUPT-DRIVEN INPUT ROUTINES, THE VIDEO OUTPUT, AS WELL
; AS THE CTRL-Z-ACTIVATED FRONT PANEL MODE.
;
; MICROPOLIS MODULE COMMANDS (ALL VALUES ARE IN HEX):
;
;
;   BO                  BOOT FROM MICROPOLIS DISK
;
;   DM <MODEL>          SET MICROPOLIS DISK MODEL:
;                         1= MOD  I: 35 TRACKS, 1-SIDED
;                         2= MOD II: 77 TRACKS, 1-SIDED
;                         3= (MOD II: 77 TRACKS, 1-SIDED)
;                         4= MOD IV: 77 TRACKS, 2-SIDED
;                         5= MOD  V: 80 TRACKS, 1-SIDED
;                         6= MOD VI: 80 TRACKS, 2 SIDED
;
;   FC <TRK1> <TRK2>    CP/M-FORMAT TRACKS FROM TRK1 THROUGH
;                         TRK2. DEFAULT IS ENTIRE DISK
;
;   FM <TRK1> <TRK2>    MICROPOLIS-FORMAT TRACKS FROM TRK1
;                         THROUGH TRK2. DEFAULT IS ENTIRE DISK
;
;   LH <HEAD>           LOAD HEAD AND KEEP IT LOADED (FOR
;                         ALIGNMENT.) TYPE ANY KEY TO ABORT
;
;   RE <TRACK>          RESTORE, THEN OPTIONALLY SEEK TRACK
;
;   RC <SECTOR> <ADDR>  READ CP/M (256-BYTE) SECTOR INTO MEMORY
;                         AT ADDR
;
;   RM <SECTOR> <ADDR>  READ FULL (266-BYTE) SECTOR INTO
;                         MEMORY AT ADDR
;
;   SK <TRACK>          SEEK TRACK. WILL RESTORE FIRST IF THE
;                         TRACK IS UNKNOWN.
;
;   SU <UNIT>           SELECT UNIT FOR ALL OPERATIONS
;
;   WC <SECTOR> <ADDR>  WRITE CP/M (256-BYTE) SECTOR FROM
;                         MEMORY AT ADDR
;
;   WM <SECTOR> <ADDR>  WRITE FULL (266-BYTE) SECTOR FROM
;                         MEMORY AT ADDR
;
;       FOR RM AND WM ON 2-SIDED DRIVES, THE LSB OF SS (0-F)
;       SPECIFIES THE SECTOR, AND THE THE MSB (0 OR 1)
;       SPECIFIES THE SIDE.
;
;   FORMATTED TO ASSEMBLE WITH DIGITAL RESEARCH'S ASM.
;==============================================================
; NOTES
;
; SOFTWARE MUST READ OR WRITE 270 BYTES FOR EACH SECTOR:
;  BYTE   FUNCTION
;   0   SYNC BYTE = 0FFH
;   1   HEADER: TRACK NUMBER
;   2   HEADER: SECTOR NUMBER
; 3-268 DATA (266 BYTES)
;  269  CHECKSUM
;
; MICROPOLIS DISK SECTOR HEADERS ARE 2 BYTES IN LENGTH. THE 1ST
; BYTE IS THE TRACK NUMBER. THE SECOND BYTE IS THE SECTOR
; NUMBER, BETWEEN 00 AND 0FH.  MICROPOLIS APPARENTLY DOES NOT
; ENCODE THE SIDE INTO THE SECTOR HEADERS OF 2-SIDED DISKS.
;
; CHECKSUMS ARE COMPUTED WITH THE CARRY BIT - I.E. THE ADC
; INSTRUCTION IS USED, RATHER THAN THE ADD INSTRUCTION. THE
; CHECKSUM IS THE SUM (WITH CARRY) OF BYTES 1 THROUGH 268.
;
; NOTE THAT CP/M ON MICROPOLIS DISKS READS AND WRITES ONLY 256
; DATA BYTES PER SECTOR, PADDING THE BEGINNING OF SECTORS WITH
; 10 BYTES OF 00.
;==============================================================
; REVISION HISTORY
; VERS. 1.00 BY M. EBERHARD 23 NOV 2013
;   CREATED
;
; VERS. 1.01 BY M. EBERHARD 3 DEC 2013
;   ADD RC & WC COMMANDS, TO READ/WRITE CP/M (256-BYTE) SECTORS
;
; VERS. 2.00 BY M. EBERHARD 6 DEC 2013
;   RETURN RAM ADDRESS IN HL INSTEAD OF DE, GET COMMAND ADDRESS
;   IN HL INSTEAD OF DE.
;   SUPPORT FORMATTING SELECTED TRACKS
;   ADD FC COMMAND TO FORMAT FOR CP/M
;
; POSSIBLE FUTURE ENHANCEMENTS:
;   SELECTED SIDE(S) FOR FORMAT COMMAND
;   GET TRACK ID CMD
;   MEASURE SPINDLE SPEED & TEST FOR 16-SECTOR DISK
;   DECODE STATUS AND/OR STAT COMMAND
;==============================================================

;*******
; ASCII
;*******
CR	EQU	0DH

;****************
; PROGRAM EQUATES
;****************
CPUKHZ	EQU	1850	;CPU CLOCK IN KHZ
DEFMOD	EQU	1	;DEFAULT DISK DRIVE MODEL
MFDATA	EQU	00H	;DATA FOR MICROPOLIS-FORMAT SECTORS
			;AND ALSO 1ST 10 BYTES OF CP/M SECS
CFDATA	EQU	0E5H	;DATA FOR CP/M-FORMAT SECTORS

;*********************************************
; MEMORY LOCATION BEFORE & AFTER CODE IS MOVED
;*********************************************
ROMLOC	EQU	0800H	;MICMOD ROM ADDRESS
RAMLOC	EQU	0F000H	;LOCATION AFTER MOVED
PBASE	EQU	0F400H	;POLEX RAM BASE

;***********************************
; POLEX ENTRY POINTS
;***********************************
PXINIT	EQU	PBASE		;RESTART POLEX
PXCSTA	EQU	PBASE+03H	;CONSOLE STATUS
PXCIN	EQU	PBASE+06H	;CONSOLE INTO A
PXCOUT	EQU	PBASE+09H	;CONSOLE OUT C. A=C ON RET
PXSSTA	EQU	PBASE+0CH	;SERIAL PORT STATUS
PXSIN	EQU	PBASE+0FH	;SERIAL PORT IN
PXSOUT	EQU	PBASE+12H	;SERIAL PORT OUT
PXSOST	EQU	PBASE+15H	;SERIAL PORT OUTPUT STATUS
PXBAUD	EQU	PBASE+18H	;SET SERIAL PORT BAUD RATE
PXIHEX	EQU	PBASE+1BH	;SCAN PAST BLANKS, GET HEX VAL
				;HL=VALUE
				;DE ADVANCED PAST CHR
				;Z SET, CARRY CLEAR IF VALUE
				;CARRY SET IF NO VALUE FOUND
PXOHEX	EQU	PBASE+1EH	;PRINT A AS HEX ON THE CONSOLE
PXWARM	EQU	PBASE+21H	;WARM-START RE-ENTRY
PXCIN2	EQU	PBASE+24H	;TEST KBD STATUS, RZ IF NO CHR
				;IF CHR, ABORT IF CABKEY
				;ELSE RET WITH CHR IN A, NZ
PXGLIN	EQU	PBASE+27H	;GET A LINE OF USER INPUT

;*************************************************
; MICROPOLIS DISK CONTROLLER MEMORY-MAPPED PORTS
; NOTE: THE "STANDARD" ADDRESS FOR THE MICROPOLIS
; CONTROLLER IS F800, WHICH CONFLICTS WITH THE
; STANDARD POLY-88 VIDEO MEMORY ADDRESS. STDMEM
; SETS THE VIDEO MEMORY TO ITS POLY-88 STANDARD.
;*************************************************
MBASE	EQU	0FC00H	;NON-STANDARD CONTROLLER ADDR
MBOOT	EQU	MBASE		;BOOT CODE ENTRY
MCMD	EQU	MBASE+200H	;COMMAND WRITE-REGISTER
				;..ALSO AT MBASE+201H
MDHSEL	  EQU	  20H	;SELECT HEAD/UNIT
			;BITS 1:0 SELECT THE UNIT
			;BIT 4 SELECTS THE HEAD
MINTC	  EQU	  40H	;INTERRUPT CONTROL
			;BIT 0=1 TO ENABLE, =0 DISABLE
MSTEP	  EQU	  60H	;STEP TRACK (30 mS PER STEP) 
			;BIT 0=0 STEPS OUT, 1 STEPS IN
MWREN	  EQU	  80H	;WRITE ENABLE
MRESET	  EQU	  0A0H	;RESET CONTROLLER

MSECTR	EQU	MBASE+200H	;SECTOR READ-REGISTER
MSMASK	  EQU	  0FH	;SECTOR BITS MASK
MJW10	  EQU	  10H	;JUMPER W10 (RESERVED)
MJW9	  EQU	  20H	;JUMPER W9 (CPU SPEED)
			;0=4 MHZ, 1=2 MHZ
MSINT	  EQU	  40H	;SECTOR INTERRUPT
MSFLAG	  EQU	  80H	;SECTOR VALID FLAG

MSTAT	EQU	MBASE+201H	;STATUS READ-REGISTER
MUMASK	  EQU	  07H	;UNIT & SELECTED MASK
MSLTD	  EQU	  04H	;UNIT IS SELECTED
MTK0	  EQU	  08H   ;TRACK 0 DETECTED
MWPT	  EQU	  10H	;WRITE PROTECT DETECTED
MRDY	  EQU	  20H	;DRIVE READY
MPINTE	  EQU	  40H	;S-100 PINTE STATUS
MTF	  EQU	  80H	;TRANSFER FLAG

MDATA	EQU	MBASE+202H	;READ/WRITE DATA REGISTER

MBPS	EQU	266	;DATA BYTES PER SECTOR - DOES NOT
			;..INCLUDE HEADER OR CHECKSUM

MAXUNT	EQU	3	;MAX UNIT NUMBER
MAXSEC	EQU	15	;MAX SECTOR NUMBER
SYNCHR	EQU	0FFH	;SECTOR SYNCH CHARACTER

MAXMOD	EQU	6	;MAX SUPPORTED DRIVE MODEL NUMBER

;==============================================================
; START OF ROM CODE
; GETS CALLED BY POLEX DURING INITIALIZATION
; INSTALLS MODULE CODE IN MEMORY AT F000
; ON EXIT:
;   HL=ADDRESS OF COMMAND PROCESSOR IN RAM
;==============================================================
	ORG	ROMLOC

;=========================
; MICMOD ROM ENTRY POINT =
;=========================

;MOVE CODE INTO RAM, BACKWARDS, SO HL IS RIGHT AT THE END

MDINIT:	LXI	B,RAMEND-RAMSRC		;BYTE COUNT
	LXI	D,RAMEND		;END OF RAM SOURCE
	LXI	H,RAMEND-RAMSRC+RAMLOC	;END OF DEST

MOVER:	DCX	D
	DCX	H

	LDAX	D
	MOV	M,A

	DCX	B		;BUMP COUNT
	MOV	A,B
	ORA	C		;DONE?
	JNZ	MOVER

;PRINT BANNER AND RETURN TO POLEX

	JMP	BANNER+ROF	;MUST BE IN RAM CODE

;==============================================================
; RAM CODE, GETS MOVED INTO RAM AT RAMLOC (F000)
;==============================================================
RAMSRC:
ROF	EQU	RAMLOC-RAMSRC	;RAM OFFSET

;=========================
; MICMOD RAM ENTRY POINT =
;=========================

;**********************************************
; MICMOD COMMAND PROCESSOR
;
; SCAN FOR MODULE COMMANDS, AND EXECUTE THEM
;
; ON ENTRY:
;   DE POINTS TO POTENTIAL COMMAND
; ON EXIT:
;   Z CLEAR MEANS SUCCESFUL COMMAND EXECUTION
;   Z SET MEANS NO COMMAND FOUND
;   SOME ERRORS EXIT DIRECTLY TO PXWARM
;**********************************************
MDCMD:	LDAX	D		;1ST CHR
	MOV	B,A		;1ST CHR INTO B
	INX	D
	LDAX	D		;2ND CHR
	MOV	C,A		;2ND CHR INTO C
	DCX	D		;FIX DE IN CASE NO MATCH

	LXI	H,COMTAB+ROF-1	;POINT TO COMMAND TABLE

;SEARCH THROUGH TABLE AT HL FOR A 2-CHR MATCH OF BC

NCOM:	INX	H		;GO TO NEXT ENTRY
	MOV	A,M		;1ST TABLE CHR
	ORA	A		;END OF TABLE?
	RZ			;Y: NOT A MODULE CMD

	INX	H
	CMP	B		;MATCH?
	MOV	A,M		;2ND TABLE CHR
	INX	H
	JNZ	NCOM+ROF	;N: KEEP LOOKING
	SUB	C		;MATCH?
	JNZ	NCOM+ROF	;N: KEEP LOOKING

;GOT A MATCH. COMPUTE ROUTINE ADDRESS AND PUT IT ON STACK
;A=0
;(HL)= COMMAND ADDRESS OFFSET
;DE=ADDRESS OF START OF 2-LETTER COMMAND

	MOV	L,M		;GET ADDRESS OFFSET
	MOV	H,A		;H=A=0

	LXI	B,CMDBAS+ROF	;BASE OF COMMAND ROUTINES
	DAD	B		;HL=ADDRESS OF ROUTINE

	PUSH	H		;GO ADDRESS ON STACK

	INX	D		;SKIP PAST 2-LETTER COMMAND
	INX	D

;GET FOLLOWING PARAMETER (IF ANY) AND PUT IT IN HL AND A.
;SET Z FLAG IF NO PARAMETER PRESENT

	CALL	PXIHEX		;GET 1ST PARAM, CY SET IF NONE
	MOV	A,L		;POTENTIAL LOW BYTE IN A
	RET			;GO TO COMMAND ROUTINE:
				;CY SET IF NO PARAMETERS
				;HL=1ST PARAMETER
				;A=L
				;DE POINTS TO 1ST CHR
				;..AFTER 1ST PARAM

;**************************************************
;INIT BANNER ROUTINE - MUST BE IN RAM TO USE CLPRNT
;**************************************************
BANNER:	CALL	CILPRT+ROF
	DB	'Micropolis Module 2.0','0'+80H
	RET

;==========================
; BASE ADDRESS FOR COMMANDS
;==========================
CMDBAS	EQU	$

;***COMMAND ROUTINE*********************
; BO (BOOT FROM MICROPOLIS DRIVE)
;***************************************
GOMICR:	JMP	MBOOT

;***COMMAND ROUTINE*********************
; DM (SET MICROPOLIS MODEL)
; ON ENTRY:
;   A=VALUE (1-6)
;***************************************
SETMOD:	DCR	A
	CPI	MAXMOD		;BOUNDS CHECK
	JNC	ERROR+ROF
				;Z IS CLEARED

	MOV	E,A		;COMPUTE TABLE ENTRY
	MVI	D,0
	LXI	H,MODTAB+ROF

	DAD	D
	DAD	D		;2 BYTES/ENTRY

	MOV	A,M		;GET MAXTRK
	INX	H
	MOV	H,M		;GET SIDED-NESS
	MOV	L,A
	SHLD	MAXTRK+ROF	;SAVE BOTH		

	RET			;RET WITH Z CLEAR

;***COMMAND ROUTINE*********************
; SU (SET UNIT)
; ALSO SET TRACK TO 0FFH, MEANING WE
; DON'T KNOW WHERE THE HEAD IS.
;***************************************
STUNIT:	CPI	MAXUNT+1	;BOUNDS CHECK
	JNC	ERROR+ROF	;Z IS CLEARED
	STA	UNIT+ROF

	MVI	A,0FFH		;WE DON'T KNOW WHERE
	STA	TRACK+ROF	;..WE ARE ANY MORE

	RET			;RET WITH Z CLEAR

;***COMMAND ROUTINE**********************************
; LH (LOAD HEAD AND KEEP IT LOADED)
; THE MICROPOLIS CONTROLLER HAS A TIMER THAT UNLOADS
; THE HEAD(S) AFTER ABOUT 4 SECONDS OF INACTIVITY.
; THIS TIMER IS RESET WITH EACH READ OF THE STATUS
; REGISTER. THIS ROUTINE REDS THE STATUS REGISTER
; OVER AND OVER UNTIL TOLD TO STOP BY THE USER.
; ON ENTRY:
;   A=HEAD
;****************************************************
DHLOAD:	CPI	2		;HEAD MUST BE 0 OR 1
	JNC	ERROR+ROF

	RLC
	RLC
	RLC
	RLC			;0 OR 10H
	MOV	B,A		;HEAD IN B FOR SELECT
	CALL	SELECT+ROF

	CALL	CILPRT+ROF
	DB	'Q to qui','t'+80H

DHLUP:	MOV	A,M		;..TO KEEP HEAD LOADED

	CALL	PXCSTA		;ANYTHING FROM KBD?
	JZ	DHLUP+ROF	;N: KEEP HEAD LOADED

	JMP	PXCIN		;GET CHR, RET W/ Z CLEAR	

;***COMMAND ROUTINE*********************
; SK (SEEK TRACK)
;   WILL RESTORE IF LOST
; ON ENTRY:
;   L=TARGET TRACK
;***************************************
DSEEK:	LDA	TRACK+ROF	;DO WE KNOW WHERE WE ARE?
	MOV	D,A		;D=CURRENT TRACK
	INR	A		;FF MEANS NO
	JZ	DRESTR+ROF	;N: RESTORE-SEEK INSTEAD

	CALL	SKSTUP+ROF	;CHECK TRACK #, SELECT
				;C=MSTEP+1 (STEP INWARD)
				;E=TARGET TRACK
				;HL=MCMD+1

	MOV	A,E		;HL=TRACK IN OR OUT?

	SUB	D		;DESIRED TK - CURRENT TK
	JZ	SKDONE+ROF	;ALREADY THERE: RET W/ Z

	JNC	DSK1+ROF	;C MEANS DESIRED<CURRENT
				;NC MEANS CURRENT<DESIRED
	DCR	C		;TOWARD SMALLER TRACK #
	CMA			;MAKE IT A POSITIVE NUMBER
	INR	A

DSK1:	MOV	B,A		;NUMBER OF STEPS	

GOSTEP:	CALL	STEP+ROF	;B=0 ON EXIT

	MOV	A,E		;REMEMBER TRACK
	STA	TRACK+ROF

SKDONE:	INR	A		;CLEAR Z FOR RET
	RET

;***COMMAND ROUTINE*********************
; RE (RESTORE TO TRACK)
; IGNORES ALL BUT THE LAST 2 HEX DIGITS
; ON ENTRY:
;   L=TARGET TRACK
; ON EXIT:
;   HL=MCMD+1
;   D PRESERVED
;***************************************
DRESTR:	CALL	SKSTUP+ROF	;CHECK TRACK #, SELECT
				;C=MSTEP+1
				;E=TARGET TRACK
				;HL=MCMD+1

; STEP AWAY FROM THE TRACK ZERO SENSOR

	MVI	B,2		;2 STEPS SHOULD DO IT
	CALL	STEP+ROF	;IN CASE BEYOND TK 0

; STEP OUTWARD TO TRACK 0

	DCR	C		;C=MSTEP: STEP OUT
	LDA	MAXTRK+ROF
	ADI	6		;A FEW EXTRA
	MOV	B,A
	CALL	STEP+ROF
	JZ	RSTERR+ROF	;Z:CAN'T FIND TRACK 0

;STEP INWARD TO THE TARGET TRACK

	MOV	B,E		;B=TARGET TRACK
	INR	C		;C=MSTEP+1: STEP
	JMP	GOSTEP+ROF	;.. INWARD B STEPS

;***COMMAND ROUTINE*******************
; RC (READ CP/M-STYLE 256-BYTE SECTOR)
;  THE FIRST 10 BYTES OF SECTOR DATA
;  ARE IGNORED, EXCEPT THAT THEY ARE
;  INCLUDED IN THE CHECKSUM.
; ON ENTRY:
;   A<3:0>=SECTOR
;   A<4>  = HEAD
;*************************************
DRD256:	CALL	RDSTUP+ROF	;READ SETUP
				;  HL=DATA REG
				;  DE=DEST
				;  A=CHECKSUM SO FAR
				;  CARRY VALID FROM CKSUM
				;  B=MBPS-256=10
				;  HEADER ALREADY READ

;READ AND CHUCK THE FIRST 10 DATA BYTES
;  HL=DATA REG
;  DE=DEST
;  A=CHECKSUM SO FAR
;  B=MBPS-256
; ONE BYTE TYME = 32 uS = 58 CYCLES

DRLP0:	ADC	M		;(4)JUST DO CHECKSUM W/CARRY
	DCR	B		;(4)NEXT
	JNZ	DRLP0+ROF	;(10)42 CYCLES READ-READ

;GO READ AND STORE THE REMAINING 256 BYTES

	JMP	RD256+ROF
		
;***COMMAND ROUTINE*************
; RM (READ FULL 266-BYTE SECTOR)
; ON ENTRY:
;   A<3:0>=SECTOR
;   A<4>  = HEAD
;*******************************
DRD266:	CALL	RDSTUP+ROF	;READ SETUP
				;  HL=DATA REG
				;  DE=DEST
				;  A=CHECKSUM SO FAR
				;  CARRY VALID FROM CKSUM
				;  B=MBPS-256=10
				;  HEADER ALREADY READ

;READ THE FIRST 10 DATA BYTES
;  HL=DATA REG
;  DE=DEST
;  A=CHECKSUM SO FAR
;  B=MBPS-256
; ONE BYTE TYME = 32 uS = 58 CYCLES

DRLP1:	MOV	C,A		;(5)SAVE CHECKSUM SO FAR
	MOV	A,M		;(7)GET DATA BYTE
	STAX	D		;(7)SAVE IT
	ADC	C		;(4)DO CHECKSUM W/CARRY
	INX	D		;(5)NEXT
	DCR	B		;(4)NEXT
	JNZ	DRLP1+ROF	;(10)42 CYCLES READ-READ		

;READ THE LAST 256 DATA BYTES
;  HL=DATA REG
;  DE=DEST
;  A=CHECKSUM SO FAR
;  B=0
; ONE BYTE TYME = 32 uS = 58 CYCLES

RD256:	MOV	C,A		;(5)SAVE CHECKSUM SO FAR
	MOV	A,M		;(7)GET DATA BYTE
	STAX	D		;(7)SAVE IT
	INX	D		;(5)NEXT
	ADC	C		;(4)DO CHECKSUM W/CARRY
	DCR	B		;(4)NEXT
	JNZ	RD256+ROF	;(10)42 CYCLES READ-READ
		
;READ AND COMPARE THE CHECKSUM TO A

	CMP	M		;GET & COMPARE CHECKSUM
	EI			;INTS OKAY FINALLY
	CNZ	CSMERR+ROF	;BAD CHECKSUM:ERROR

;GO SEE IF THE SECTOR AND TRACK MATCH WHAT WE EXPECTED

	JMP	CHKHDR+ROF	;CHECK & PRINT HEADER INFO
				;AND RETURN TO POLEX

;***COMMAND ROUTINE**********************
; WC (WRITE CP/M-STYLE 256-BYTE SECTOR)
;   WRITES 0'S TO THE 1ST 10 SECTOR BYTES
; ON ENTRY:
;   DRIVE IS SELECTED (OR WE WILL ERROR)
;   A<3:0>=SECTOR
;   A<4>  = HEAD
;****************************************
DWR256:	CALL	WRSTUP+ROF	;A=CHECKSUM SO FAR
				;CARRY VALID FROM CKSUM
				;B=MBPS-256=10
				;DE=SOURCE ADDRESS
				;HL=DATA REGISTER ADDRESS
				;HEADER ALREADY WRITTEN

;WRITE ZEROS FOR THE 1ST 10 BYTES OF SECTOR DATA	
;  HL=DATA REG
;  DE=SOURCE
;  A=CHECKSUM SO FAR
; ONE BYTE TYME = 32 uS = 58 CYCLES

DWLP0:	MVI	M,0		;(11)
	DCR	B		;(5)
	JNZ	DWLP0+ROF	;(10)

	JMP	WR256+ROF	;(10)

;***COMMAND ROUTINE**********************
; WM (WRITE FULL 266-BYTE SECTOR)
; ON ENTRY:
;   DRIVE IS SELECTED (OR WE WILL ERROR)
;   A<3:0>=SECTOR
;   A<4>  = HEAD
;****************************************
DWR266:	CALL	WRSTUP+ROF	;A=CHECKSUM SO FAR
				;CARRY VALID FROM CKSUM
				;B=MBPS-256=10
				;DE=SOURCE ADDRESS
				;HL=DATA REGISTER ADDRESS
				;HEADER ALREADY WRITTEN

;WRITE THE 1ST 10 BYTES OF SECTOR DATA	
;  HL=DATA REG
;  DE=SOURCE
;  A=CHECKSUM SO FAR
; ONE BYTE TYME = 32 uS = 58 CYCLES

DWLP1:	MOV	C,A		;(4)
	LDAX	D		;(7)
	INX	D		;(5)
	MOV	M,A		;(7)
	ADC	C		;(4)CHECKSUM

	DCR	B		;(5)
	JNZ	DWLP1+ROF	;(10)

;WRITE THE LAST 256 BYTES OF SECTOR DATA	
;  HL=DATA REG
;  DE=SOURCE
;  A=CHECKSUM SO FAR
; ONE BYTE TYME = 32 uS = 58 CYCLES

WR256:	MOV	C,A		;(4)
	LDAX	D		;(7)
	MOV	M,A		;(7)
	INX	D		;(5)
	ADC	C		;(4)CHECKSUM

	DCR	B		;(5)
	JNZ	WR256+ROF	;(10)

;WRITE THE CHECKSUM

	MOV	M,A

;WAIT FOR TRANSFER FLAG TO CLEAR, SO THAT THE CONTROLLER
;MAY FINISH WRITING THE POSTAMBLE (NOT NECESSARY HERE,
;BECAUSE WE WRITE ONE SECTOR AT A TIME, MANUALLY.)

;	XRA	A

;WNTF:	ORA	M		;MSB IS TR FLAG
;	JM	WNTF+ROF

;	EI			;INTS FIXED AT MAIN

	ORA	H		;CLEAR Z
	RET

;***COMMAND ROUTINE************************************
; FC (FORMAT CP/M DISK)
;  WRITES CORRECT SECTOR HEADERS AND CHECKSUMS,
;  FILLS FIRST 10 BYTES OF DATA FIELDS WITH MFDATA (00),
;  AND THE REMAINING 256 BYTES WITH CFDATA (E5H).
; ON ENTRY:
;   CARRY SET IF NO PARAMETERS
;   L=1ST TRACK TO FORMAT
;  (HL)=LAST TRACK TO FORMAT
;******************************************************
CFORMT:	MVI	A,CFDATA	;SECTOR DATA
	DB	01H		;'LXI B' OPCODE SKIPS 2
				;..BYTES TO GO TO FORMAT

;***COMMAND ROUTINE*****************************
; FM (FORMAT MICROPOLIS DISK)
;  WRITES CORRECT SECTOR HEADERS AND CHECKSUMS,
;  FILLS ALL DATA FIELDS WITH MFDATA (00)
; ON ENTRY:
;   CARRY SET IF NO PARAMETERS
;   L=1ST TRACK TO FORMAT
;  (HL)=LAST TRACK TO FORMAT
;**********************************************
MFORMT:	MVI	A,MFDATA	;SECTOR DATA,
				;LEAVE CARRY ALONE

; FALL INTO FORMAT

;*********************************************
; GENERAL FORMAT ROUTINE
; ON ENTRY:
;   CARRY SET IF NO PARAMETERS
;   L=1ST TRACK TO FORMAT
;  (HL)=LAST TRACK TO FORMAT
;   A=DATA FOR LAST 256 BYTES OF SECTORS
;   THE FIRST 10 BYTES ALWAYS GET MFDATA (00).
;*********************************************
FORMAT:	STA	FORDAT+ROF	;GET SECTOR DATA

	MVI	B,0		;DEFAULT INITIAL TRACK
	LDA	MAXTRK+ROF	;DEFAULT FINAL TRACK
	INR	A		;..+1
	MOV	C,A
	JC	FORM1+ROF	;DEFAULTS IF NO PARAMS
	
	MOV	B,L		;GOT START TRACK
				;DRESTR WILL VALIDATE B

	CALL	PXIHEX		;GET END TRACK
	MOV	A,L
	CMP	C		;VALID FINAL TRACK?
	JNC	ERROR+ROF

	INR	L		;LAST TRACK+1
	MOV	C,L

FORM1:	MOV	D,C		;D GETS FINAL TRACK

;B=START TRACK, D=FINAL TRACK+1

	CALL	CILPRT+ROF	;ARE YOU SURE?
	DB	'Format (Y/N)','?'+80H
	CALL	PXCIN
	CPI	'Y'
	RNZ			;NOT Y: DONE, Z CLEARED

	CALL	ILPRNT+ROF
	DB	'Y',CR		;FAKE ECHO
	DB	'Formattin','g'+80h

; RESTORE TO TRACK B, SELECT SIDE 0

	MOV	L,B		;RESTORE TO TRACK B
	CALL	DRESTR+ROF	;SELECT & RESTORE
				;HL=MCMD+1

;ABORT IF DISK IS WRITE-PROTECTED

	MOV	A,M		;READ STATUS REG
	ANI	MWPT
	JNZ	WPTERR+ROF

;LOOP TO FORMAT ALL REQUESTED TRACKS. IF THE REQUESTED
;LAST TRACK IS LESS THAN THE REQUESTED FIRST TRACK, OR IF
;NO LAST TRACK WAS SPECIFIED, THEN JUST FORMAT THE FIRST
;REQUESTED TRACK.
; A=0
; D=FINAL TRACK
; HEAD IS ALREADY ON REQUESTED FIRST TRACK

	MOV	B,A		;0 STEPS 1ST TIME

FTLOOP:	MVI	C,MSTEP+1	;STEP INWARD B STEPS
	CALL	STEP+ROF

	CALL	ILPRNT+ROF	;TRACK PACIFIER
	DB	'.'+80H

	CALL	PXCIN2		;USER ABORT?

	XRA	A		;SIDE 0 1ST

;LOOP TO FORMAT SIDES, IF THE DRIVE IS 2-SIDED

FHLOOP:	MOV	E,A		;E REMEMBERS SIDE
	MOV	B,A		;SELECT SIDE
	CALL	SELECT+ROF	;SETS HL=MCMD+1 TOO

;LOOP TO FORMAT ALL 16 SECTORS. START WITH WHATEVER SECTOR WE
;FINDFIRST, AND PROCEED TO DO ALL SECTORS SEQUENTIALLY FROM
;THERE.GET THE SECTOR NUMBER FOR THE SECTOR HEADER FROM THE
;CONTROLLER'S SECTOR REGISTER.

	PUSH	D		;SAVE D=LAST TRACK
				; & E=SIDE

	LDA	FORDAT+ROF	;DATA FOR 256 SECTOR BYTES
	MOV	E,A

	LXI	B,0		;1ST SECTOR TIMEOUT
	MVI	D,MAXSEC+1	;D COUNTS SECTORS
	DI			;INTS OFF WHILE WE DO TRACK

FSLOOP:	DCR	L		;HL=MCMD=MSECTR

;HUNT FOR THE START OF ANY SECTOR

FHUNT:	DCX	B		;(7)TIMEOUT?
	MOV	A,B		;(5)
	ORA	C		;(4)
	JZ	STOERR+ROF	;(10)Y:ERROR

	XRA	A		;(4)
	ORA	M		;(7)TEST MSB=MSFLAG
	JP	FHUNT+ROF	;(10)WAIT FOR MSFLAG
				;(47 CYCLES PER PASS)

	MOV	C,A		;(4)SAVE SECTOR FOR HEADER
	INR	L		;(4)HL=MCMD+1

	MVI	B,SYNCHR	;(7)

	MVI	M,MWREN		;(11)INITIATE WRITE
				;(87 CYCLES MAX TO HERE)

;WAIT FOR THE TF FLAG TO BECOME TRUE.
;MUST BEGIN WRITING WITHIN 20 uS OF TF BECOMING TRUE
;THIS PRECLUDES THE POSSIBILITY OF A TIMEOUT HERE

	XRA	A		;(4)

FWTF:	ORA	M		;(7)MSB IS TR FLAG
	JP	FWTF+ROF	;(10) 17 CYCLES/PASS
				;25 CYCLES MAX FROM TF

;WRITE THE SYNCH CHR AND 2-BYTE HEADER
;HEADER 1ST BYTE IS TRACK NUMBER FROM (TRACK)
;HEADER 2ND BYTE IS THE SECTOR NUMBER FROM C
;BEGIN CHECKSUM COMPUTATION TOO

	INR	L		;(4)HL=MCMD+2=DATA REG

	MOV	M,B		;(10)WRITE SYNCH CHR

	LDA	TRACK+ROF	;WRITE HEADR 1ST BYTE
	MOV	M,A		;..WHICH IS THE TRACK #
	MOV	B,A		;START OF CHECKSUM

	MOV	A,C		;GET SECTOR REG DATA
	ANI	MSMASK		;STRIP OFF THE SECTOR

	MOV	M,A		;2ND HEADER IS SECTOR #
	ADD	B		;FIRST CKSUM ADD W/O CARRY

;WRITE THE FIRST 10 BYTES OF SECTOR DATA AS MFDATA
;(USUALLY 0). NOTE THAT CP/M IGNORES THE 1ST 10 BYTES
;OF EACH SECTOR.

;  HL=DATA REG
;  A=CHECKSUM SO FAR

	MVI	B,MBPS-256

FBLP1:	MVI	M,MFDATA	;WRITE A BYTE

 IF MFDATA			;MUST CKSUM OF MFDATA<>0
	ACI	MFDATA		;CHECKSUM
 ENDIF
	DCR	B
	JNZ	FBLP1+ROF

;WRITE THE REMAINING 256 BYTES OF SECTOR DATA PER E	
;  HL=DATA REG
;  A=CHECKSUM SO FAR
;  E=SECTOR FORMAT DATA
;  B=0
; ONE BYTE TYME = 32 uS = 58 CYCLES

FBLP2:	MOV	M,E		;WRITE A BYTE
	ADC	E		;COMPUTE CHECKSUM
	DCR	B
	JNZ	FBLP2+ROF

; WRITE THE SECTOR CHECKSUM

	MVI	C,255		;B=0. SHORT TIMEOUT FOR
				;...THE NEXT SECTOR
	MOV	M,A

;NEXT SECTOR, QUICKLY, SO AS NOT TO MISS IT

	DCR	L		;HL=MCMD+1
	DCR	D		;D COUNTS SECTORS
	JNZ	FSLOOP+ROF

;WAIT FOR TRANSFER FLAG TO CLEAR BEFORE SWITCHING HEADS
;OR MOVING ACTUATOR, SO THAT THE CONTROLLER MAY FINISH
;WRITING THE POSTAMBLE

FWNTF:	XRA	A
	ORA	M		;MSB IS TR FLAG
	JM	FWNTF+ROF

;NEXT SIDE

	EI			;INTS ON FOR A MOMENT
	POP	D		;RECOVER D=LAST TRACK
				;& E = SIDE

	LDA	SIDED2+ROF
	CMP	E		;AT MAX SIDE?
	JNZ	FHLOOP+ROF

;NEXT TRACK

	INR	B		;B=1: STEP ONCE

	LDA	TRACK+ROF	;BUMP TRACK
	INR	A
	STA	TRACK+ROF

	CMP	D		;D=LAST TRACK + 1
	JC	FTLOOP+ROF	;TOLERATES END < START

; DONE: RESTORE TO TRACK 0	

	MVI	L,0		;SPECIFY TRACK 0
	JMP	DRESTR+ROF

;***SUBROUTINE*********************
; SET UP TO READ A SECTOR
; ON EXIT:
;   A = CHECKSUM SO FAR
;  CARRY VALID FROM CKSUM
;   B = MBPS-256
;  DE = DESTINATION ADDRESS
;  HL = DATA REGISTER ADDRESS
;  THE HEADER HAS ALREADY BEEN READ
;**********************************
RDSTUP:	CALL	RWSTUP+ROF	;GET PARAMS, HUNT FOR SECT
				;A=0
				;B=SYNCHR
				;C=SECTOR #
				;DE=DEST ADDRESS
				;HL=MSTAT
				;HEAD ON SECTOR FOR 46 uS
				;INTERRUPTS DISABLED

; WAIT FOR THE TF FLAG TO BECOME TRUE
; MUST BEGIN READING WITHIN 20 uS OF TF BECOMING TRUE
; THIS PRECLUDES THE POSSIBILITY OF A TIMEOUT HERE

DRWTF:	ORA	M		;(7)MSB IS TR FLAG
	JP	DRWTF+ROF	;(10) 17 CYCLES/PASS
				;27 CYCLES MAX TO HERE

;READ SYNC BYTE, READ & SAVE 2-BYTE HEADER

	INR	L		;(4)POINT HL TO DATA REG

	MOV	A,M		;(7)GET/CHUCK SYNC BYTE

	MVI	B,MBPS-256	;BYTE  FOR DATA LOOP
				;(CAN'T DO BACK-TO-BACK READS)

	MOV	A,M		;(7)GET/SAVE 1ST HEADER
	STA	HDRTRK+ROF	;(13)..TRACK NUMBER
	MOV	C,A		;(4)INITIATE CHECKSUM

	MOV	A,M		;(7)GET/SAVE 2ND HEADER
	STA	HDRSEC+ROF	;(13)..SECTOR NUMBER
	ADD	C		;(4)COMPUTE CHECKSUM
				;CARRY IS PART OF IT
	RET

;***SUBROUTINE************************
; SET UP TO WRITE A SECTOR
; ON EXIT:
;   A = CHECKSUM SO FAR
;  CARRY VALID FROM CKSUM
;   B = MBPS-256
;  DE = SOURCEN ADDRESS
;  HL = DATA REGISTER ADDRESS
;  THE HEADER HAS ALREADY BEEN WRITTEN
;*************************************
WRSTUP:	PUSH	PSW		;SAVE SECTOR/HEAD

	LDA	TRACK+ROF	;DO WE KNOW WHERE WE ARE?
	INR	A		;FFH MEANS NO
	JZ	LSTERR+ROF	;NO: WE ARE LOST

;ABORT IF DRIVE IS WRITE-PROTECTED

	LDA	MSTAT		;STATUS REG
	ANI	MWPT		;WRITE PROTECT FLAG
	JNZ	WPTERR+ROF

	POP	PSW		;RECOVER A=SECTOR/HEAD

	CALL	RWSTUP+ROF	;GET PARAMS, HUNT FOR SECT
				;A=0
				;B=SYNCHR
				;SECTOR=SECTOR #
				;DE=DEST ADDRESS
				;HL=MSTAT
				;HEAD ON SECTOR FOR 46 uS
				;CURTRK = CURRENT TRACK
				;INTERRUPTS DISABLED

; INITIATE WRITE, AND WAIT FOR THE TF FLAG TO BECOME TRUE
; MUST BEGIN WRITING WITHIN 20 uS OF TF BECOMING TRUE
; THIS PRECLUDES THE POSSIBILITY OF A TIMEOUT HERE

	MVI	M,MWREN		;(11)INITIATE WRITE

DWWTF:	ORA	M		;(7)MSB IS TR FLAG
	JP	DWWTF+ROF	;(10) 17 CYCLES/PASS
				;27 CYCLES MAX TO HERE

;WRITE THE SYNCH CHR AND 2-BYTE HEADER
;HEADER 1ST BYTE IS TRACK NUMBER FROM (TRACK)
;HEADER 2ND BYTE IS THE SECTOR NUMBER
;BEGIN CHECKSUM COMPUTATION TOO

	INR	L		;(4)HL=DATA REG

	MOV	M,B		;(7)WRITE SYNCH CHR

	LDA	TRACK+ROF	;(13)WRITE HEADR 1ST BYTE
	MOV	M,A		;(7)..IS THE TRACK #
	MOV	C,A		;(4)BEGIN CHECKSUM

	MVI	B,MBPS-256	;(7)BYTE CNT FOR DATA LOOP

	LDA	SECTOR+ROF	;(13)REQUESTED SECTOR
	MOV	M,A		;(7)2ND HEADER IS THE SECTOR
	ADD	C		;(4)FIRST CKSUM ADD W/O CARRY
				;CARRY IS PART OF IT
	RET

;***SUBROUTINE*********************************
; SET UP UNIT FOR READ/WRITE
;   IGNORES JUMPER W9 & W10 BITS IN MSECTR
; ON ENTRY:
;   A<3:0> = DESIRED SECTOR
;   A<4>   = HEAD
;   DE POINTS TO NEXT PARAM
;   UNIT HAS SELECTED UNIT
; ON EXIT:
;   DRIVE IS READY FOR READ/WRITE FOR 46 uS MIN
;   A=0
;   B=SYNCHR
;   DE=SOURCE/DEST ADDRESS
;   HL=MSTAT
;   SECTOR=SECTOR NUMBER
;   INTERRUPTS ARE DISABLED
;***********************************************
RWSTUP:	MOV	C,A		;SAVE SECTOR #

;GET SOURCE/DEST ADDRESS INTO HL

	CALL	PXIHEX		;GET ADDRESS
	JC	ERROR+ROF	;MUST SPECIFY ADDRESS
	PUSH	H		;SOURCE/DEST ADDRESS

; VALIDATE SIDE AND SECTOR, SET B=SIDE BIT, C=SECTOR

	LDA	SIDED2+ROF	;GET SIDED MASK
	MOV	B,A
	ADI	MAXSEC
	CMP	C		;SECTOR TOO BIG?
	JC	ERROR+ROF

	MOV	A,C		;SECTOR & SIDE
	ANA	B		;STRIP JUST THE SIDE
	MOV	B,A		;..INTO B FOR SELECT


	MOV	A,C
	ANI	0FH		;STRIP JUST THE SECTOR
	STA	SECTOR+ROF	;SAVE FOR VALIDATION

	ORI	MSFLAG		;SECTOR MUST BE VALID
	MOV	C,A		;INTO C FOR HUNT LOOP

;SELECT THE SPECIFIED UNIT AND SIDE

	CALL	SELECT+ROF	;SELECT DRIVE & HEAD
				;HL=MCMD+1=MSTAT

;STALL FOR >250 mS TO LET SECTOR LOGIC SETTLE
;NOTE: INTERRUPTS ARE BLOCKED DURING STALL

	LXI	D,(CPUKHZ/24)*250
	CALL	STALLD+ROF	;..SECTOR CNTR SETTLE
				;EXITS WITH DE=0

	MVI	B,SYNCHR	;B=SYNCHR FOR EXIT

;HUNT FOR SPECIFIED SECTOR. 200 mS/REV
;TIMEOUT IF NOT FOUND. DE=0 FOR ~1.5 uS TIMEOUT

	DCR	L		;POINT HL TO MSECTR

	DI			;INTS OFF WHILE WE HUNT

HUNT:	DCX	D		;(7)TIMEOUT?
	MOV	A,D		;(5)
	ORA	E		;(4)
	JZ	STOERR+ROF	;(10)Y:ERROR

	MVI	A,MSMASK+MSFLAG	;(7)CORRECT SECTOR, READY
	ANA	M		;(7)READ SECTOR REG
	SUB	C		;(4)MATCH DESIRED SECTOR?
				;   A=0 IF SO
	JNZ	HUNT+ROF	;(10)N: KEEP LOOKING
				;55 CYCLES PER PASS
				;WORST CASE 75 CYCLES TO HERE

	INR	L		;(4)POINT HL TO STATUS REG

	POP	D		;(10)SOURCE/DEST ADDR

	RET			;(10)100 CYCLES MAX FROM
				;.. ON-SECTOR = 54 uS

;***SUBROUTINE***************************
;STALL FOR 30 uS
;NOTE: INTERRUPTS ARE OFF DURING STALL
;
; ON RETURN:
;  A,DE=0
; INTS ARE ENABLED
;*****************************************
STAL30:	LXI	D,(30*CPUKHZ)/24 ;MAKES IT 30 mS

; FALL INTO STALLD

;***SUBROUTINE****************************
;STALL FOR DE*24 CYCLES
;NOTE: INTERRUPTS ARE OFF DURING STALL
;
; ON RETURN:
;  A,DE=0
; INTS ARE ENABLED
;*****************************************
STALLD:	DI
STALUP:	DCX	D		;(5)
	MOV	A,D		;(5)
	ORA	E		;(4)
	JNZ	STALUP+ROF	;(10)
				;24 CYCLES/PASS
	EI
	RET

;***SUBROUTINE********************************
;STEP POSITIONER
; ON ENTRY:
;   B = NUMBER OF STEPS
;   C = MSTEP TO STEP OUTWARD
;     = MSTEP+1 TO STEP INWARD
;   HL= MCMD+1
; ON EXIT:
;   Z SET AND A=0 IF B COUNTED ALL THE WAY DOWN
;   Z CLEARED IF EXIT BECAUSE OF TRACK 0
; TRASHES A,B
;*********************************************
STEP:	INR	B

STLUP:	DCR	B		;ON DESIRED TRACK?
	RZ			;Y: DONE
	MOV	M,C		;STEP INWARD/OUTWARD
	PUSH	D
	CALL	STAL30+ROF	;STALL FOR STEP
	POP	D
	MOV	A,M		;DISK STAT REG
	ANI	MTK0		;TRACK 0?
	RNZ			;DONE IF ON TRACK 0
	JMP	STLUP+ROF

;***SUBROUTINE********************************
; SET UP FOR SEEK
; ON ENTRY:
;   L=TARGET TRACK
; ON EXIT:
;   C=MSTEP+1
;   E=TARGET TRACK
;   HL=MCMD+1
;   (HL)=DRIVE STATUS
; TRASHES A,B
; ABORT IF DRIVE NOT READY OR BAD TRACK NUMBER
;*********************************************
SKSTUP:	LDA	MAXTRK+ROF	;GET MAX TRACK #
	CMP	L		;REQUESTED TRACK
	JC	ERROR+ROF	;BOUNDS CHECK

	MOV	E,L		;E=TARGET TRACK

	LXI	B,MSTEP+1	;HEAD 0, C FOR RET

; FALL INTO SELECT

;***SUBROUTINE*******************
; SELECT UNIT AND HEAD
; ON ENTRY:
;   B=0 FOR SIDE 0, 10 FOR SIDE 1
; ON EXIT:
;   Z SET
;   HL=MCMD+1
;   (HL)=DRIVE STATUS
; ABORT IF DRIVE NOT READY
; TRASHES A,B
;******************************** 
SELECT:	LXI	H,MCMD+1
	MOV	A,M		;WAKE UP CONTROLLER

	MVI	M,MINTC		;DISABLE DISK INTS

	LDA	UNIT+ROF	;SELECT UNIT

	ORA	B		;COMBINE SIDE

	MOV	B,A		;B HAS HEAD & UNIT

	ORI	MDHSEL		;SELECT COMMAND
	MOV	M,A		;TO CONTROLLER

	PUSH	D
	CALL	STAL30+ROF
	POP	D

	MVI	A,MRDY		;ALSO TEST DRIVE READY
	XRA	M		;TEST SELECTED UNIT

	ANI	MUMASK+MRDY	;DID UNIT GET SELECTED?
	CMP	B		;THE RIGHT ONE?

	RZ

;FALL INTO DRIVE NOT READY ERROR

;***ERROR EXIT**********
; DRIVE NOT READY 
; ON ENTRY:
;   HL=MSTAT
;***********************
DNRERR:	CALL	CILPRT+ROF	;CR FIRST
	DB	'Drive not read','y'+80H
	JMP	DSKERR+ROF	;WILL PRINT STATUS REG

;***ERROR EXIT*******************
; SECTOR NOT FOUND (HUNT TIMEOUT)
; ON ENTRY:
;   INTERRUPTS ARE DISABLED
;   HL=MSECTR
;********************************
STOERR:	EI
	CALL	CILPRT+ROF	;CR FIRST
	DB	'Sector not found'
	DB	CR,'Sector reg=',' '+80H
	JMP	DSKERR+ROF	;WILL PRINT SECTOR REG

;***ERROR EXIT**********
; WRITE-PROTECTED
; ON ENTRY:
;   HL=MSTAT
;***********************
WPTERR:	CALL	CILPRT+ROF	;CR FIRST
	DB	'Write protec','t'+80H
	JMP	DSKERR+ROF	;WILL PRINT STATUS REG

;***ERROR EXIT**********
; RESTORE ERROR - 
; TRACK 0 NOT FOUND
; ON ENTRY:
;   HL=MSTAT
;***********************
RSTERR:	MVI	A,0FFH		;DON'T KNOW WHAT TRACK
	STA	TRACK+ROF
	CALL	CILPRT+ROF	;CR FIRST
	DB	'Track 0 not foun','d'+80H

;FALL INTO DSKERR

;***ERROR EXIT**********************
; DISK ERROR
; PRINT DISK STATUS & QUIT
; ON ENTRY:
;   HL=ADDRESS OF STATUS REG TO READ
;***********************************
DSKERR:	CALL	CILPRT+ROF
	DB	'Status reg=',' '+80H

DSKER2:	MOV	A,M
	CALL	PXOHEX		;PRINT VALUE
	JMP	PXWARM		;BACK TO MAIN
				;WHERE STACK GETS FIXED

;***ERROR EXIT**********
; DON'T KNOW WHAT TRACK
;***********************
LSTERR:	CALL	CILPRT+ROF
	DB	'Must RE firs','t'+80h
	JMP	PXWARM		;BACK TO MAIN

;***ERROR EXIT**********
; GENERIC ERROR HANDLER
; PRINT ERROR MESSAGE,
; RETURN WITH Z CLEARED
;***********************
ERROR:	CALL	CILPRT+ROF	;CR FIRST
	DB	'?'+80H		;CLEARS Z FOR RET

	JMP	PXWARM		;BACK TO MAIN
				;WHERE STACK GETS FIXED

;***ERROR SUBROUTINE****
; READ CHECKSUM
;***********************
CSMERR:	CALL	CILPRT+ROF	;CR FIRST
	DB	'Checksum erro','r'+80H
	RET

;***ERROR SUBROUTINE***********
; SECTOR HEADER MISMATCH ERROR
;******************************
HMMERR:	CALL	CILPRT+ROF
	DB	'Header mismatc','h'+80h
	RET

;*****EXIT ROUTINE*********************************
;COMPARE TRACK AND SECTOR FROM THE SECTOR HEADER
;TO THE EXPECTED TRACK AND SECTOR, AND REPORT ERROR
;IF DIFFERENT. HOWEVER, IF WE DON'T KNOW WHAT TRACK
;WE ARE ON (TRACK=FF), THEN UPDATE TRACK WITH THE
;HEADER'S TRACK NUMBER. REPORT THE HEADER'S TRACK
;AND SECTOR.
; ON ENTRY:
;   HDRTRK = FOUND TRACK NUMBER
;   HDRSEC = FOUND SECTOR NUMBER
;   TRACK = EXPECTED SECTOR, 0FFH IF LOST
;   SECTOR = EXPECTED SECTOR
;   B=0
;**************************************************
CHKHDR:	LHLD	HDRTRK+ROF	;FOUND TRACK AND SECTOR

	LDA	TRACK+ROF	;EXPECTED TRACK
	CPI	0FFH		;DO WE KNOW WHAT TRACK?
	JNZ	CHKH1+ROF	;Y: JUST COMPARE

	MOV	A,L		;LOST: NOW WE ARE FOUND
	STA	TRACK+ROF	;REMEMBER CURRENT TRACK

CHKH1:	XRA	L		;CORRECT TRACK?
	MOV	C,A

	LDA	SECTOR+ROF	;EXPECTED SECTOR
	XRA	H		;CORRECT SECTOR?
	ORA	C
	CNZ	HMMERR+ROF	;EITHER ERROR

	CALL	CILPRT+ROF	;CR FIRST
	DB	'Header: track',' '+80H

	MOV	A,L		;FOUND TRACK
	CALL	PXOHEX

	CALL	ILPRNT+ROF
	DB	', sector',' '+80H

	MOV	A,H		;FOUND SECTOR
	CALL	PXOHEX

	INR	B		;CLEAR Z
	RET

;*****SUBROUTINE******************************
; PRINT CR THEN INLINE MESSAGE
; CALLS TO CILPRT ARE FOLLOWED BY THE STRING
; THE LAST STRING BYTE HAS ITS MSB SET
; A TRASHED, ALL OTHER REGISTERS PRESERVED
;*********************************************
CILPRT:	CALL	ILPRNT+ROF
	DB	CR+80H

; FALL INTO ILPRNT

;*****SUBROUTINE******************************
; PRINT INLINE MESSAGE
; CALLS TO ILPRNT ARE FOLLOWED BY THE STRING
; THE LAST STRING BYTE HAS ITS MSB SET
; Z CLEARED ON EXIT
; A TRASHED, ALL OTHER REGISTERS PRESERVED
;*********************************************
ILPRNT:	XTHL			;SAVE HL, GET MSG ADDR
	PUSH	B

IPLOOP:	MOV	A,M		;LOOP THROUGH MESSAGE
	ANI	7FH		;STRIP END-MARKER
	MOV	C,A		;PXCOUT WANTS CHR IN C
	CALL	PXCOUT
	CMP	M		;END?
	INX	H
	JZ	IPLOOP+ROF

	POP	B
	XTHL			;RESTORE HL
				;..GET RET ADDRESS
	RET

;********************************
; COMMAND TABLE
; NOTE THAT ASCII CHRS STORED IN
; VIDEO MEMORY HAVE THEIR MSB SET
;********************************
COMTAB:	DB	'B'+80H,'O'+80H	;BOOT FROM MICROPOLIS
	DB	GOMICR-CMDBAS
	DB	'R'+80H,'E'+80H	;RESTORE TO TRACK
	DB	DRESTR-CMDBAS
	DB	'S'+80H,'K'+80H	;SEEK
	DB	DSEEK-CMDBAS
	DB	'R'+80H,'M'+80H	;READ 266-BYTE SECTOR
	DB	DRD266-CMDBAS
	DB	'R'+80H,'C'+80H	;READ 256-BYTE SECTOR
	DB	DRD256-CMDBAS
	DB	'W'+80H,'M'+80H	;WRITE 266-BYTE SECTOR
	DB	DWR266-CMDBAS
	DB	'W'+80H,'C'+80H	;WRITE 256-BYTE SECTOR
	DB	DWR256-CMDBAS
	DB	'D'+80H,'M'+80H	;DISK MODE LEVEL
	DB	SETMOD-CMDBAS
	DB	'L'+80H,'H'+80H	;LOAD HEAD
	DB	DHLOAD-CMDBAS
	DB	'S'+80H,'U'+80H	;SET UNIT
	DB	STUNIT-CMDBAS
	DB	'F'+80H,'M'+80H	;FORMAT DISK
	DB	MFORMT-CMDBAS
	DB	'F'+80H,'C'+80H	;CP/M FORMAT DISK
	DB	CFORMT-CMDBAS

	DB	0		;END OF TABLE MARK

;******************************
; MICROPOLIS MODEL TABLE
; 2 BYTES/ENTRY
; 1ST BYTE IS MAX TRACK NUMBER
; 2ND BYTE IS SIDED
;******************************
MODTAB:	DB	34,0	;MOD 1
	DB	76,0	;MOD 2
	DB	76,10H	;MOD 3 (DOES NOT EXIST)
	DB	76,10H	;MOD 4
	DB	79,0	;MOD 5
	DB	79,10H	;MOD 6	

;*** END OF PROGRAM ***

;**********************
; INITIALIZED VARIABLES
;**********************
UNIT:	DB	0	;CURRENTLY SELECTED DRIVE (0-3)
TRACK:	DB	0FFH	;CURRENT TRACK (FF MEANS LOST)

;SIDED2 MUST FOLLOW MAXTRK

MAXTRK:	DB	34	;MAX TRACK NUMBER (INIT TO MOD 1)
SIDED2:	DB	0	;0 MEANS 1-SIDED (INIT TO MOD 1)

RAMEND:			;END OF COPIED CODE

;***************************
; UNITIALIZED RAM VARIABLES
;***************************
SECTOR:	DS	1	;REQUESTED SECTOR

FORDAT:	DS	1	;FORMAT DATA FOR 256 BYTES

; HDRSEC MUST FOLLOW HDRTRK

HDRTRK:	DS	1	;1ST SECTOR HEADER BYTE IS TRACK #
HDRSEC:	DS	1	;2ND SECTOR HEADER BYTE IS SECTOR #
	END
