;
;PROGRAM TO READ OR WRITE A 512 X 16
;DEBLOCKED DISK OR READ OR WRITE A 5 1/4 INCH
;WINCHESTER DISK. THIS PROGRAM IS JUST LIKE SYSGEN.
;
;TRACK 0 = SINGLE DENSITY 128 BYTES X 26 SECTORS.
;TRACK 1 = DOUBLE DENSITY 512 BYTES X 16 SECTORS.
;
;ALL OF TRACK 1 IS READ OR WRITTEN WITH THIS PROGRAM.
;IF RUNNING DOUBLE SIDED, TRACK 1 NOW BECOMES
;TRACK 0 ON SIDE 1. IT IS DOUBLE DENSITY,
;WITH 16 SECTORS OF 512 BYTES.
;PROGRAM USES FUNCTION 10 FOR ALL CONSOLE INPUT
;
;LAST MODIFICATION 12-04-81
;G.W.MULCHIN, TARBELL ELECTRONICS
;WINCHESTER DISK ROUTINES ADDED 2-14-83
;MACABILITY ADDED 2-14-83
;BY EDWIN E. SMITH - TARBELL ELECTRONICS
;
;ASSEMBLE USING ZASM.COM OR MAC (Z-80 CODE)
;
;SYSTEM EQUATES
;
FALSE	EQU	0		;DEFINE VALUE OF FALSE.
TRUE	EQU	NOT FALSE	;DEFINE VALUE OF TRUE.
;
MAC	EQU	FALSE		;TRUE IF USING MACRO ASSEMBLER
DMACNTL	EQU	TRUE		;TRUE IF USING DMA CONTROL
DISK	EQU	0F8H		;DISK PORT BASE ADDRESS.
STPRAT	EQU	1		;0=3MS,1=6MS,2=10MS,3=15MS
ADR0	EQU	0E0H		;DMA ADDRESS PORT.
WCT0	EQU	0E1H		;DMA WORD COUNT PORT.
CMND	EQU	0E8H		;DMA COMMAND PORT.
DCOM	EQU	DISK		;COMMAND PORT.
DSTAT	EQU	DISK		;STATUS PORT.
SECT	EQU	DISK+2		;SECTOR PORT.
WAIT	EQU	DISK+4		;WAIT PORT.
DCONT	EQU	DISK+4		;CONTROL PORT.
DMACHK	EQU	DISK+5		;DMA CHECK PORT.
SYSTEM	EQU	0900H		;START OF CPM IMAGE
WINSYS	EQU	0780H		;OFFSET CCP TO SECTOR BOUNDARY
SDSPT	EQU	26		;26 SECTORS / TRACK.
DDSPT	EQU	16		;16 SECTORS / TRACK.
NSECTS	EQU	SDSPT + DDSPT	;TOTAL SYSTEM SECTORS.
RTCNT	EQU	10		;NUMBER OF RETRYS.
DMAW	EQU	80H		;DMA WRITE COMMAND
DMAR	EQU	40H		;DMA READ COMMAND
B128	EQU	128		;128 BYTE COUNT
B512	EQU	512		;512 BYTE COUNT
CR	EQU	0DH
LF	EQU	0AH
BEL	EQU	7		;BELL CODE
IDBUF	EQU	97EH		;ID BYTE LOCATION IN MEMORY
BOOT	EQU	0
BDOS	EQU	5
PRINTB	EQU	9		;PRINT BUFFER FUNCTION
RCB	EQU	10		;READ CONSOLE BUFFER <FUNC 10>
;
;****************************************************************
;*								*
;*DRIVE SPECIFICATION TABLE FOR THE HD DMA HARD DISK CONTROLLER *
;*								*
;****************************************************************
;
PRECOMP	EQU	0		;CYLINDER TO START WRITE PRECOMENSATION
LOWCURR	EQU	0		;LOW CURRENT NOT USED
STEPDLY	EQU	0		;STEP DELAY (0-12.7 MILLISECONDS)
STEPRCL	EQU	30		;RECALIBRATE STEP DELAY
HEADDLY	EQU	0		;SETTLE DELAY (0-25.5 MILLISECONDS)
SECTSIZ	EQU	3		;CODE FOR 512 BYTE SECTORS
HRDSPT	EQU	17		;PHYSICAL SECTORS/TRACK

;DEFINE CONTROLLER COMMANDS
DMAREAD	EQU	0		;READ SECTOR
DMAWRIT	EQU	1		;WRITE SECTOR
DMARHED	EQU	2		;FIND A SECTOR
DMAWHED	EQU	3		;WRITE HEADERS (FORMAT A TRACK)
DMALCON	EQU	4		;LOAD DISK PARAMETERS
DMASSTA	EQU	5		;SENSE DISK DRIVE STATUS
DMANOOP	EQU	6		;NULL CONTROLLER OPERATION

RESET	EQU	54H		;RESET CONTROLLER
ATTN	EQU	55H		;SEND A CONTROLLER ATTENTION

CHAN	EQU	50H		;DEFAULT CHANNEL ADDRESS
STEPOUT	EQU	10H		;STEP DIRECTION OUT
STEPIN	EQU	0		;STEP DIRECTION IN
BAND1	EQU	40H		;NO PRECOMP, HIGH CURRENT
BAND2	EQU	0C0H		;PRECOMP, HIGH CURRENT
BAND3	EQU	80H		;PRECOMP, LOW CURRENT
TRACK0	EQU	1		;TRACK ZERO STATUS
WFLT	EQU	2		;WRITE FAULT FROM DRIVE
DREADY	EQU	4		;DRIVE READY
SEKCMP	EQU	8		;SEEK COMPLETE
;
;
	IF	MAC
	MACLIB	Z80		;USING MACRO ASSEMBLER
	ENDIF
;
	ORG	100H		;RUN AREA
;
BEGIN:	JMP	INIT		;JUMP AROUND STORAGE
;
MSG1:	DB	CR,LF,'Tarbell Winchester sysgen program vers. 2.0 of 2-14-83'
	DB	CR,LF,'Floppies are A,B,C OR D  -  Winchesters are 0,1,2 OR 3'
MSG1A:	DB	CR,LF,'Source Drive or <cr to skip> $'
MSG3:	DB	CR,LF,'Source Drive> = $'
MSG2:	DB	CR,LF,'Destination Drive or <cr to reboot> $'
MSG4:	DB	CR,LF,'Destination Drive = $'
READY:	DB	' , Type Return When Ready$'
WRITER:	DB	CR,LF,BEL,'DISK WRITE ERROR$'
RRERR:	DB	CR,LF,BEL,'DISK READ ERROR$'
ERROR:	DB	CR,LF,BEL,'INVALID DRIVE SELECT$'
MSG5:	DB	CR,LF,'Reading a double sided disk (Y OR N) $'
MSG6:	DB	CR,LF,'Writing a double sided disk (Y OR N) $'
MSG7:	DB	'Winchester Drive $'
NMERR:	DB	CR,LF,BEL,'MIS-MATCH ON DRIVE ID BYTE.$'
KEYBUF:	DB	3,0,0,0,0
;
;SUBROUTINES
;
;INPUT ROUTINE - GET A CHARACTER FROM KEYBOARD
;
;ENTER:	NONE
;EXIT:	REG A = CHAR FROM KEYBOARD
;	STORED AT SELD
;REGISTERS USED: D,E H,L C
;
INPUT:	MVI	A,3		;RESET BUFFER LENGTH
	LXI	H,0
	SHLD	KEYBUF+1	;CLEAR BUFFER
	STA	KEYBUF
	LXI	D,KEYBUF	;POINT TO BUFFER
	MVI	C,RCB		;READ BUFFER FUNCTION
	CALL	BDOS		;GET CHARACTER
	LDA	KEYBUF+2	;GET THE FIRST CHAR ONLY
	CPI	'A'		;CHECK FOR LOWER CASE
	RC
	CPI	'Z'+1
	RNC
	ANI	5FH		;MAKE INTO UPPER CASE
	RET
;
;PRINT BUFFER ROUTINE
;
;ENTER:	D,E -> MESSAGE TO PRINT
;EXIT:	NONE
;REGS:	D,E C
;
PRINT:	MVI	C,PRINTB	;PRINT BUFFER FUNCTION
	JMP	BDOS		;USE BDOS TO PRINT
;
;SELECT DRIVE ROUTINE
;
DSELDSK:ADD	A		;SHIFT
	ADD	A		; LEFT
	ADD	A		;  FOUR
	ADD	A		;   TIMES
	OUT	DCONT		;SEND THE SELECT CODE
	STA	CLATCH		;SAVE IT
	RET
;
;HOME SELECTED DRIVE
;
DHOME:	MVI	A,STPRAT	;RESTORE AT STEP RATE
	OUT	DCOM
	IN	WAIT		;WAIT FOR INTRQ.
	RET
;
;MAIN LINE STARTS HERE
;
INIT:	LXI	SP,STACK	;SET LOCAL STACK
	LXI	D,MSG1		;SHOW OPENING MESSAGE
GDRV:	CALL	PRINT
	CALL	INPUT		;GET SOURCE DRIVE
	ORA	A		;= CARRIAGE RETURN?
	JRZ	DESTDRV		;YES, MEMORY ALREADY SET
	STA	READY		;SHOW DRIVE SELECTED
	CPI	'4'		;TEST FOR WINCHESTER
	JRNC	NOTWIN
	SUI	'0'		;MAKE HEX
	JRC	NOTLEG		;IF NOT LEGAL DRIVE
	STA	MWDRIVE		;SAVE WINCHESTER DRIVE #
	LXI	D,MSG3		;POINT TO MESSAGE
	CALL	PRINT
	LXI	D,MSG7		;PRINT 'WINCHESTER DRIVE'
	CALL	PRINT
	LXI	D,READY		;READY MESSAGE
	CALL	PRINT
WOK1:	CALL	INPUT		;GET A CR
	ORA	A		;MUST BE ZERO
	JRNZ	WOK1		;LOOP TILL CR
	MVI	A,DMAREAD	;SETUP READ COMMAND
	STA	RWOP
	CALL	MWRDWR		;GO READ 34 SECTORS
	JMP	DESTDRV		;GO DO DESTINATION
NOTWIN:	CPI	'A'		;TEST FOR LEGAL FLOPPY
	JRC	NOTLEG		;JUMP IF NOT
	ANI	7		;STRIP ASCII
	DCR	A		;MAKE CPM DRIVE NUMBER
	CPI	4		;CHECK DRIVE LIMITS
	STA	SELD		;MODIFIED FOR CPM SELECT
	JRC	OK
NOTLEG:	LXI	D,ERROR		;POINT TO ERROR MESSAGE
	CALL	PRINT
	LXI	D,MSG1A
	JMPR	GDRV		;TRY AGAIN
;
OK:	LXI	D,MSG5		;READING DOUBLE SIDED?
	CALL	PRINT
	CALL	INPUT		;GET RESPONSE
	CPI	'N'		;NO?
	MVI	A,0		;0 = DOING SINGLE SIDED DISK
	JRZ	OK2		;YES, JUST DOING SINGLE SIDED
	MVI	A,1		;DOING DOUBLE SIDED, SET A FLAG
OK2:	STA	SIDED		;SAVE THE FLAG
	LXI	D,MSG3
	CALL	PRINT
	LXI	D,READY		;READY MESSAGE
	CALL	PRINT
OK3:	CALL	INPUT		;GET A CR
	ORA	A		;MUST BE ZERO
	JRNZ	OK3		;LOOP TILL CR
	LDA	SELD		;GET DRIVE TO READ
	CALL	DSELDSK
	CALL	DHOME		;HOME THE DRIVE
	CALL	READDSK		;READ THE SELECTED DRIVE
;
DESTDRV:LXI	D,MSG2		;GET DESTINATION DRIVE TO DO
DDRV:	CALL	PRINT
	CALL	INPUT		;GET SOURCE DRIVE
	ORA	A		;= 0?
	JZ	BOOT		;ALL DONE
	STA	READY		;SHOW DRIVE SELECTED
	CPI	'4'		;TEST FOR WINCHESTER
	JRNC	NTWIN1
	SUI	'0'		;MAKE HEX
	JRC	NTLEG1		;IF NOT LEGAL DRIVE
	STA	MWDRIVE		;SAVE WINCHESTER DRIVE #
	LXI	D,MSG4		;POINT TO MESSAGE
	CALL	PRINT
	LXI	D,MSG7		;PRINT 'WINCHESTER DRIVE'
	CALL	PRINT
	LXI	D,READY		;READY MESSAGE
	CALL	PRINT
DWOK1:	CALL	INPUT		;GET A CR
	ORA	A		;MUST BE ZERO
	JRNZ	DWOK1		;LOOP TILL CR
	MVI	A,DMAWRIT	;SETUP WRITE COMMAND
	STA	RWOP
	CALL	MWRDWR		;GO WRITE 34 SECTORS
	JMP	DESTDRV
NTWIN1:	CPI	'A'		;TEST FOR LEGAL FLOPPY
	JRC	NTLEG1		;JUMP IF NOT
	ANI	7		;STRIP ASCII
	DCR	A		;MAKE CPM DRIVE NUMBER
	CPI	4		;CHECK DRIVE LIMITS
	STA	SELD		;MODIFIED FOR CPM SELECT
	JRC	DOK
NTLEG1:	LXI	D,ERROR		;POINT TO ERROR MESSAGE
	CALL	PRINT
	LXI	D,MSG1A
	JMPR	DDRV		;TRY AGAIN
;
DOK:	LXI	D,MSG6		;WRITING DOUBLE SIDED DRIVE
	CALL	PRINT
	CALL	INPUT		;GET RESPONSE
	CPI	'N'		;IS IT NO?
	MVI	A,0		;SET FLAG FOR NO IN CASE
	MVI	B,0DEH		;TALKING TO SINGLE SIDED?
	JRZ	DOK2
	MVI	A,1		;YES, SET = DOUB SIDED WANTED
	MVI	B,0DCH		;MUST BE USING DOUBLE SIDED
DOK2:	STA	SIDED		;SAVE THE FLAG CODE
	LDA	IDBUF		;GET THE ID BYTE IN MEMORY
	CMP	B		;ARE THEY THE SAME?
	JRNZ	NOMTCH		;JUMP IF THEY DONT MATCH
	LXI	D,MSG4		;POINT TO MESSAGE
	CALL	PRINT
	LXI	D,READY		;READY MESSAGE
	CALL	PRINT
DOK1:	CALL	INPUT		;GET A CR
	ORA	A		;MUST BE ZERO
	JRNZ	DOK1		;LOOP TILL CR
	LDA	SELD		;GET THE SELECTED DRIVE
	CALL	DSELDSK		;SELECT IT
	CALL	DHOME		;HOME THE DRIVE
	CALL	DSKWRT		;WRITE THE OPERATING SYSTEM
	JMP	DESTDRV
;
NOMTCH:	LXI	D,NMERR		;POINT TO MISMATCH ERROR
	CALL	PRINT		;PRINT IT
	JMP	DESTDRV		;RETRY
;
READDSK	EQU	$
;
	IF	DMACNTL
	LXI	H,DMAR SHL 8 OR B128
	SHLD	WRITOP
	ENDIF
;
	MVI	A,SDSPT+1	;NUMBER OF SINGLE DENSITY SECTS
	STA	RSECTSZ+1
	MVI	E,RTCNT		;GET RETRY COUNT.
RBLOOP:	LXI	H,SYSTEM	;CP/M STARTS HERE.
	SHLD	SYSVAL		;SAVE IT
	MVI	D,NSECTS	;NUMBER OF SECTORS TO WRITE.
RNTRK:	MVI	C,1		;SECTOR NUMBER.
	MVI	B,080H		;FOR HEAD LOAD.
RNSEC:	CALL	RRED		;READ FIRST SECTOR.
	DCR	D		;IF DONE,
	RZ
	INR	C		;INCREMENT SECTOR COUNT.
	MOV	A,C		;DONE WITH
RSECTSZ:CPI	SDSPT+1		;THIS TRACK?
	JRC	RNSEC		;IF NOT, READ NEXT SECTOR.
	MVI	A,DDSPT+1	;NUMBER OF DOUB DEN SECTORS
	STA	RSECTSZ+1	;MODIFY THE CODE.
;
	IF	DMACNTL
	LXI	H,DMAR SHL 8 OR B512
	SHLD	WRITOP		;DO 512 BYTE WRITE
	ENDIF
;
	CALL	SETDEN		;SET UP FOR DOUBLE DENSITY
	JMPR	RNTRK
;
SETDEN:	LDA	CLATCH		;GET CURRENT CODE
	ORI	8		;SET DOUBLE DENSITY CODE
	STA	CLATCH		;SAVE IT
	OUT	WAIT		;SET LATCH FOR D.DENSITY
	LDA	SIDED		;DOING DOUB SIDED?
	ORA	A		;= TO 0?
	JRNZ	RSIDED		;NOT =, DOING DOUBLE SIDED
	MVI	A,5BH		;STEP COMMAND.
	OUT	DCOM		;ISSUE IT.
	IN	WAIT		;WAIT UNTIL DONE.
	RET
;
RSIDED:	LDA	CLATCH		;GET CURRENT DRIVE CODE
	ORI	40H		;SWITCH SIDE
	OUT	WAIT		;SWITCH SIDE NOW
	RET
;
RRED	EQU	$
;
	IF	DMACNTL
	CALL	DMARW		;USE COMMON ROUTINE
	ENDIF
;
	IF	NOT DMACNTL	;IF NOT USING DMA CONTROL.
DATA	EQU	DISK+3		;DATA PORT.
;
	LHLD	SYSVAL
	MVI	A,0D0H		;FORCE INTRP COMMAND
	OUT	DCOM
	MOV	A,C		;GET SECTOR TO DO
	OUT	SECT
	CALL	HDLD		;CHECK FOR HEAD LOADED
	ORA	B		;MERGE IN DISK COMMAND
	OUT	DCOM		;SEND DISK COMMAND
RLOOP:	IN	WAIT		;WAIT FOR DRQ.
	ORA	A		;SET FLAGS.
	JP	RCHK		;JUMP IF DONE.
	IN	DATA
	MOV	M,A		;PUT IN MEMORY.
	INX	H		;INCREMENT POINTER.
	JMPR	RLOOP		;LOOP UNTIL DONE.
RCHK	EQU	$
	ENDIF

	SHLD	SYSVAL		;SAVE NEXT ADDRESS
	IN	DSTAT		;READ STATUS.
	ANI	09DH		;LOOK AT ERROR BITS.
	RZ			;OK IF ZERO.
	DCR	E		;DECREMENT RETRY COUNT.
	JRNZ	RBLOOP		;TRY AGAIN IF NOT ZERO.
	LXI	D,RRERR		;RETRY EXAUSTED
	CALL	PRINT		;PRINT THE ERROR MESSAGE
	JMP	BOOT		; AND RETURN TO CPM.
;
DSKWRT	EQU	$
;
	IF	DMACNTL
	LXI	H,DMAW SHL 8 OR B128
	SHLD	WRITOP
	ENDIF
;
	MVI	A,SDSPT+1	;NUMBER OF SINGLE DENSITY SECTS
	STA	SECTSZ+1
	MVI	E,RTCNT		;GET RETRY COUNT.
BLOOP:	LXI	H,SYSTEM	;CP/M STARTS HERE.
	SHLD	SYSVAL		;SAVE IT
	MVI	D,NSECTS	;NUMBER OF SECTORS TO WRITE.
WNTRK:	MVI	C,1		;SECTOR NUMBER.
	MVI	B,0A0H		;FOR HEAD LOAD.
WNSEC:	CALL	WRIT		;WRITE FIRST SECTOR.
	DCR	D		;IF DONE,
	RZ
	INR	C		;INCREMENT SECTOR COUNT.
	MOV	A,C		;DONE WITH
SECTSZ:	CPI	SDSPT+1		;THIS TRACK?
	JRC	WNSEC		;IF NOT,WRITE NEXT SECTOR.
	MVI	A,DDSPT+1	;NUMBER OF DOUB DEN SECTORS
	STA	SECTSZ+1	;MODIFY THE CODE.
;
	IF	DMACNTL
	LXI	H,DMAW SHL 8 OR B512
	SHLD	WRITOP		;DO 512 BYTE WRITE
	ENDIF
;
	CALL	SETDEN
	JMPR	WNTRK		;WRITE NEXT TRACK.
;
WRIT	EQU	$
;
	IF	DMACNTL
	CALL	DMARW		;USE COMMON ROUTINE
	ENDIF
;
	IF	NOT DMACNTL	;IF NOT USING DMA CONTROL.
	LHLD	SYSVAL
	MVI	A,0D0H		;FORCE INTRP COMMAND
	OUT	DCOM
	MOV	A,C		;GET SECTOR TO DO
	OUT	SECT
	CALL	HDLD		;CHECK FOR HEAD LOADED
	ORA	B		;MERGE IN DISK COMMAND
	OUT	DCOM		;SEND DISK COMMAND
WLOOP:	IN	WAIT		;WAIT FOR DRQ.
	ORA	A		;SET FLAGS.
	JP	WCHK		;JUMP IF DONE.
	MOV	A,M		;PUT IN MEMORY.
	OUT	DATA
	INX	H		;INCREMENT POINTER.
	JMPR	WLOOP		;LOOP UNTIL DONE.
WCHK	EQU	$
	ENDIF

	SHLD	SYSVAL		;SAVE NEXT ADDRESS
	IN	DSTAT		;READ STATUS.
	ANI	0FDH		;LOOK AT ERROR BITS.
	RZ			;OK IF ZERO.
	DCR	E		;DECREMENT RETRY COUNT.
	JRNZ	BLOOP		;TRY AGAIN IF NOT ZERO.
	LXI	D,WRITER
	CALL	PRINT
	JMP	BOOT
;
	IF	DMACNTL		;IF USING DMA CONTROL.
DMARW:	XRA	A		;CLEAR CHAN 0
	OUT	CMND
	MVI	A,0D0H		;FORCE INTRP COMMAND
	OUT	DCOM		;CLEAR FLOPPY CHIP
	PUSH	D		;SAVE D,E
	LDED	WRITOP		;GET WRITE OPERATION
	LHLD	SYSVAL		;GET MEMORY POINTER
	DCX	D		;COUNT = COUNT-1
	MOV	A,E		;COUNT BYTE
	OUT	WCT0
	MOV	A,D		;WRITE COMMAND
	OUT	WCT0
	INX	D
	MOV	A,L		;GET LOW ADDRESS BYTE
	OUT	ADR0
	MOV	A,H		;HIGH ADDRESS BYTE
	OUT	ADR0
	MVI	A,41H		;CHAN 0 REQUEST
	OUT	CMND
	MOV	A,C		;SECTOR IN A.
	OUT	SECT		;SET SECTOR REGISTER.
	CALL	HDLD
	ORA	B		;GET HEAD LOAD BIT.
	OUT	DCOM		;ISSUE COMMAND.
RLOPP:	IN	DMACHK		;CHECK DMA STATUS
	RLC			; BIT 7
	JRC	RLOPP		;LOOP IF CARRY
	XRA	A		;CLEAR ACCUM
	OUT	CMND		;RESET DMA CHIP
	MOV	A,D
	ANI	3FH		;STRIP COMMAND BYTE
	MOV	D,A
	DAD	D		;H,L = (H,L + D,E)
	POP	D
	RET
	ENDIF
;
;CHECK HEAD LOAD BIT
;
HDLD:	IN	DSTAT		;GET DISK STATUS
	ANI	20H		;MASK HEAD LOAD BIT
	MVI	A,4		;GET A 4 FOR LOAD HEAD
	RZ			;RELOAD THE HEAD
	MVI	A,0		;ELSE, HEAD IS LOAD ALREADY
	RET
;
	IF	DMACNTL
WRITOP:	DS	2		;DMA WRITE OPERATION STORAGE
	ENDIF
;
SYSVAL:	DS	2		;SYSTEM POINTER STORAGE
CLATCH:	DS	1		;CURRENT DRIVE SELECT CODE
SIDED:	DS	1		;DOUB SIDED FLAG BYTE
SELD:	DS	1		;CURRENT DRIVE TO SELECT
	DS	30		;STACK AREA
STACK:	DS	1
;
;
;****************************************************************
;*								*
;* THE FOLLOWING ARE THE LOWEST LEVEL DRIVERS FOR THE MORROW	*
;* DESIGNS HARD DISK DMÁ CONTROLLER.				*
;*								*
;****************************************************************
MWRDWR:	CALL	MWHOME		;HOME THE SELECTED DRIVE
	XRA	A		;CLEAR A REG
	STA	MWHEAD		;SET HEAD 0
	STA	MWSECTR		;SET SECTOR 0
	STA	DMADMA+2	;CLEAR EXTENDED ADDRESS
	LXI	H,WINSYS	;GET ADDRESS
	SHLD	DMADMA		;SETUP CONTROLLER
;DO 17 SECTORS FROM HEAD 0
MWRWLP:	LDA	RWOP		;GET OPERATION
	CALL	MWPREP		;DO A SECTOR
	CALL	INRAD		;INCREMENT THE ADDRESS
	LDA	MWSECTR		;GET THE SECTOR #
	INR	A		;BUMP TO NEXT
	STA	MWSECTR
	CPI	17		;CHECK FOR OVER FLOW
	JRNZ	MWRWLP		;DO ANOTHER
;DO 8 SECTORS FROM HEAD 1
	XRA	A
	STA	MWSECTR		;SECTOR 0
	INR	A
	STA	MWHEAD		;HEAD 1
MWRLP1:	LDA	RWOP		;GET OPERATION
	CALL	MWPREP		;DO A SECTOR OF HEAD 1
	CALL	INRAD		;INCREMENT THE ADDRESS
	LDA	MWSECTR		;GET THE SECTOR #
	INR	A		;BUMP TO NEXT
	STA	MWSECTR
	CPI	8		;CHECK FOR OVER FLOW
	JRNZ	MWRLP1		;DO ANOTHER
	RET
INRAD:
	LDA	DMADMA+1	;GET HIGH BYTE OF DMA ADDRESS
	INR	A		;BUMP DMA ADDRESS BY 512 BYTES
	INR	A
	STA	DMADMA+1
	RET
;
MWSEL	MVI	A,DMANOOP
	JMP	MWPREP		;EXECUTE DISK COMMAND

MWSTAT	MVI	A,DMASSTA	;SENSE STATUS OPERATION CODE
	JMP	MWPREP		;EXECUTE DISK COMMAND

MWHOME	CALL	MWRESET		;RESET CONTROLLER
	LXI	H,DMARG1	;LOAD ARGUMENTS
	MVI	M,STEPRCL	;LOAD STEP DELAY (SLOW)
	INX	H
	MVI	M,HEADDLY	;HEAD SETTLE DELAY
	CALL	MWISSUE		;DO LOAD CONSTANTS AGAIN
	CALL	MWPTR		;GET POINTER TO CURRENT CYLINDER NUMBER
	MVI	M,0		;SETUP FAKE AT CYL 0
	INX	H
	MVI	M,0
	LXI	B,2		;SEEK IN 2 CYLINDERS
	CALL	MWSEEK
	CALL	MWPTR		;GET POINTER AGAIN
	MVI	M,0FFH		;FAKE AT CYL 255 FOR MAX HEAD TRAVEL
	INX	H
	MVI	M,0FFH
	LXI	B,0		;SEEK TO CYLINDER 0
	CALL	MWSEEK		;RECAL SLOWLY
	JMP	MWRESET		;BACK TO FAST STEPPING MODE
;
MWSEEK	CALL	MWPTR		;GET TRACK POINTER
	MOV	E,M		;GET OLD TRACK NUMBER
	INX	H
	MOV	D,M
	DCX	H
	MOV	M,C		;STORE NEW TRACK NUMBER
	INX	H
	MOV	M,B
	MOV	L,C		;BUILD CYLINDER WORD
	MOV	H,B
	SHLD	DMARG0		;SET COMMAND CHANNEL CYLINDER NUMBER
	MOV	A,D
	INR	A
	LXI	H,0FFFFH
	JNZ	MWSKP0
	MVI	C,STEPOUT
	JMP	MWSKIP

MWSKP0:
	MOV	H,B		;(HL) = NEW TRACK, (DE) = OLD TRACK
	MOV	L,C
	CALL	MWHLMDE
	MVI	C,STEPOUT
	MOV	A,H
	ANI	80H		;CHECK HIT BIT FOR NEGITIVE DIRECTION
	JNZ	MWSOUT		;STEP IN
	MVI	C,0
	JMP	MWSKIP
MWSOUT:	CALL	MWNEGHL
MWSKIP:	SHLD	DMASTEP
	LDA	MWDRIVE
	ORA	C
	STA	DMASL0

	MVI	A,DMANOOP	;NO-OPERATION COMMAND FOR THE CHANNEL
	CALL	MWPREP		;STEP TO PROPER TRACK
	LXI	H,0		;CLEAR STEP COUNTER
	SHLD	DMASTEP
	RET

MWRESET	OUT	RESET		;SEND RESET PULSE TO CONTROLLER
	LXI	H,DMACHAN	;ADDRESS OF COMMAND CHANNEL
	SHLD	CHAN		;DEFAULT CHANNEL ADDRESS
	XRA	A
	STA	CHAN+2		;CLEAR EXTENDED ADDRESS BYTE
	LXI	H,DMARG1	;LOAD ARGUMENTS
	MVI	M,STEPDLY	;LOAD STEP DELAY
	INX	H
	MVI	M,HEADDLY	;HEAD SETTLE DELAY
	INX	H
	MVI	M,SECTSIZ	;SECTOR SIZE CODE
	INX	H
	MVI	M,DMALCON	;LOAD CONSTANTS COMMAND
	JMP	MWISSUE		;DO LOAD CONSTANTS

MWPREP	STA	DMAOP		;SAVE COMMAND CHANNEL OP CODE
	MVI	C,BAND1
	LHLD	DMARG0
	LXI	D,PRECOMP
	CALL	MWHLCDE
	JC	MWPRPS

	MVI	C,BAND2
	LXI	D,LOWCURR
	CALL	MWHLCDE
	JC	MWPRPS

	MVI	C,BAND3		;CYLINDER > LOW_CURRENT
MWPRPS	LDA	MWHEAD		;LOAD HEAD ADDRESS
	STA	DMARG2
	CMA			;NEGATIVE LOGIC FOR THE CONTROLLER
	ANI	7		;3 BITS OF HEAD SELECT
	RLC			;SHOVE OVER TO BITS 2 - 4
	RLC
	ORA	C		;ADD ON LOW CURRENT AND PRECOMP BITS
	MOV	C,A
	LDA	MWDRIVE		;LOAD DRIVE ADDRESS
	ORA	C		;SLAP IN DRIVE BITS
	STA	DMASL1		;SAVE IN COMMAND CHANNEL HEAD SELECT
	LDA	MWSECTR		;LOAD SECTOR ADDRESS
	STA	DMARG3

MWISSUE	LXI	H,DMASTAT	;STATUS BYTE
	MVI	M,0
	OUT	ATTN		;START CONTROLLER
	LXI	D,0		;TIME OUT COUNTER
	MOV	B,E		;CONTROLLER BUSY STATUS
MWILOOP	MOV	A,M		;GET STATUS
	ORA	A		;SET UP CPU FLAGS
	RM			;RETURN NO ERROR (CARRY RESET)
	STC
	RNZ			;RETURN ERROR STATUS
	PUSH	B
	MVI	B,4		;TOTAL 75 T-STATES
MWILOP	DJNZ	MWILOP		; 36+((B-1)*13)
	POP	B
	DCX	D		;BUMP TIMEOUT COUNTER
	MOV	A,D
	ORA	E
	JNZ	MWILOOP		;LOOP IF STILL BUSY
	STC			;SET ERROR FLAG
	RET

MWPTR	LDA	MWDRIVE		;GET CURRENTLY SELECT DRIVES TRACK ADDRESS
	RLC
	MOV	E,A
	MVI	D,0
	LXI	H,MWTAB
	DAD	D		;OFFSET INTO TRACK TABLE
	RET

MWNEGHL:
	MOV	A,H
	CMA
	MOV	H,A
	MOV	A,L
	CMA
	MOV	L,A
	INX	H
	RET

MWHLMDE:
	XCHG
	CALL	MWNEGHL
	XCHG
	DAD	D
	RET

MWHLCDE:
	MOV	A,H
	CMP	D
	RNZ
	MOV	A,L
	CMP	E
	RET

RWOP	DB	0		;READ OR WRITE OPERATION
MWTAB	DB	0FFH		;DRIVE 0
	DB	0FFH		;INITIALIZE TO (WAY OUT ON THE END OF THE DISK)
	DB	0FFH		;DRIVE 1
	DB	0FFH
	DB	0FFH		;DRIVE 2
	DB	0FFH
	DB	0FFH		;DRIVE 3
	DB	0FFH
	DB	0FFH

MWDRIVE	DB	0FFH		;CURRENTLY SELECTED DRIVE
MWHEAD	DB	0		;CURRENTLY SELECTED HEAD
MWSECTR	DB	0		;CURRENTLY SELECTED SECTOR

DMACHAN	EQU	$		;COMMAND CHANNEL AREA
DMASL0	DB	0		;DRIVE SELECT
DMASTEP	DW	0		;RELATIVE STEP COUNTER
DMASL1	DB	0		;HEAD SELECT
DMADMA	DW	0		;DMA ADDRESS
	DB	0		;EXTENDED ADDRESS
DMARG0	DB	0		;FIRST ARGUMENT
DMARG1	DB	0		;SECOND ARGUMENT
DMARG2	DB	0		;THIRD ARGUMENT
DMARG3	DB	0		;FOURTH ARGUMENT
DMAOP	DB	0		;OPERATION CODE
DMASTAT	DB	0		;CONTROLLER STATUS BYTE
DMALNK	DW	DMACHAN		;LINK ADDRESS TO NEXT COMMAND CHANNEL
	DB	0		;EXTENDED ADDRESS

	END	BEGIN
