	page
;	Read a CP/M 128 byte sector.
;
;	EXIT	A = 0, successful read operation.
;		A = 1, unsucessful read operation.
;		Z bit = 1, successful read operation.
;		Z bit = 0, unsuccessful read operation.

READ:	CALL	CHKBKD		;Check for blocked drive
	MVI	A,F.RDAT	;Read from single density floppy
	JC	FINAL		;If non-blocked transfer

	XRA	A		;Set flag to force a read
	STA	UNACNT		;Clear sector counter
	CALL	FILL		;Fill buffer with data
	POP	H
	POP	D
	MVI	C,128
	CALL	MOVDTA		;Move 128 bytes
	LDA	ERFLAG
	ORA	A
	RZ			;If no error
	XRA	A
	STA	HSTACT		;Clear host active
	ORI	001h		;Set error flag
	RET
	space	4,20
;	Write the selected 128 byte CP/M sector.
;
;	ENTRY	C = 0, write to a previously allocated block.
;		C = 1, write to the directory.
;		C = 2, write to the first sector of unallocated
;		data block.
;
;	EXIT	A = 0, write was successful.
;		A = 1, write was unsucessful.
;		Z bit = 1, write was successful.
;		Z bit = 0, write was unsucessful.

WRITE:	CALL	CHKBKD		;Check for blocked drive
	MVI	A,F.WRT		;Write to single density floppy
	JC	FINAL		;If non-blocked transfer

	MOV	A,C		;Write type in c
	STA	WRTYPE
	CPI	WRUAL
	JNZ	WRIT2		;If write to allocated
WRIT1:	STA	UNACNT
	LHLD	SEKTRK
	SHLD	UNATRK		;UNATRK = SEKTRK
	LDA	LOGSEC
	INR	A
	JMP	WRIT3

WRIT2:	LDA	UNACNT
	ORA	A
	JZ	WRIT4		;If no unallocated records
	DCR	A
	STA	UNACNT
	LDA	SEKTYP
	RAR
	MOV	L,A
	MVI	H,0
	LXI	D,LSITT-1
	DAD	D
	LDA	UNASEC		;Increment logical sector
	INR	A
	CMP	M		;Last sector in track table
	JNZ	WRIT3		;If not end of track
	LHLD	UNATRK
	INX	H
	SHLD	UNATRK
	XRA	A

WRIT3:	STA	UNASEC
	MVI	A,0FFh

WRIT4:	CALL	FILL
	POP	D
	POP	H
	MVI	C,128
	CALL	MOVDTA		;Move 128 bytes
	MVI	A,1
	STA	HSTWRT		;HSTWRT = 1
	LDA	ERFLAG
	ORA	A
	RNZ			;If any errors occurred

	LDA	WRTYPE		;write type
	CPI	WRDIR		;to directory?
	CZ	FLUSH		;Force write of directory
	LDA	ERFLAG
	ORA	A
	RET

LSITT:	DB	2*26		;Double 256 byte
	DB	4*15		;Double 512 byte
	DB	 8*8		;Double 1024 byte
	page
;	TREAD - Determine floppy disk type.
;
;	ENTRY	C = Selected drive.
;
;	Exit	Zbit set = no error
;		A = disk type (0-3)

TREAD:	MOV	A,C
	ADI	'A'
	STA	NRDYM2		;Set drive into message
	CALL	SPECIFY		;Set disk parameters
	LXI	B,240		;Delay for selecting sides
	CALL	DELAY
	LDA	SEKDSK		;Move drive to command buffer
	STA	ACTDSK		;Set into ACTDSK
	LDK	HL,DSTS
	LDK	B,DSTSL
	CALL	EXECP		;Perform command
	LDK	B,1
	CALL	GCMPS		;Get the one status byte
	ANI	020h		;Mask ready bit
	JNZ	TRD1		;If drive is ready
	LDK	HL,NRDYM1
	CALL	PRINT
	ORI	0FFh		;Clear zero flag
	RET

TRD1:	LDA	TEMPBF		;Get status byte
	ANI	008h		;Mask TS bit
	RRC
	RRC
	RRC
	STA	SEKTYP		;Save sided flag
	LDK	HL,RECAL	;Do a test seek
	LDK	B,LRECAL
	CALL	MOVETO		;Process command
	RNZ			;If error

	LDK	A,2		;Seek to track two
	CALL	DOSEEK		;Do seek
	RNZ			;If error

	LDK	A,F.DRID
	STA	DRID
TRD2:	LDK	HL,DRID
	LDK	B,DRIDL
	LDK	C,7
	CALL	EXECX		;Process command
	JZ	TRD3		;If read valid
	LDA	DRID
	XRI	040h		;Compliment MFM bit
	STA	DRID
	ANI	040h
	JNZ	TRD2		;If MFM not tried
	ORI	0FFh
	RET

TRD3:	LDA	TEMPBF+6	;Get number of bytes
	ADD	A
	MOV	B,A
	LDA	SEKTYP
	ORA	B		;Combine N with sided flag
	CMP	A		;Set zero flag
	RET

DSTS:	DB	F.DSTS,0
DSTSL:	=	*-DSTS

RECAL:	DB	F.RECA,0
LRECAL:	=	*-RECAL

DRID:	DB	F.DRID,0
DRIDL:	=	*-DRID

NRDYM1:	DB	CR,LF,'Drive '
NRDYM2:	DB	'x'
	DB	' not ready.',0
	page
;	FILL - fill host buffer with approprite host sector.
;
;	ENTRY	A = 0, Read required if not in buffer.
;		0therwise read not required.
;
;	EXIT	On exit the stack will contain the following
;		values:
;	      POP     x         ;x = host record address.
;	      POP     y		;y = caller's buffer address.

FILL:	STA	RDFLAG		;Save read flag
	LDA	SEKTYP		;Get disk type
FILL1:	RRC			;divide by 2
	ANI	3h
	MOV	B,A		;B = log base 2 (sector size) - 7
	LXI	D,HSTBUF	;initial offset
	LXI	H,128		;128 byte records
	LDA	SEKSEC		;Get logical sector
FILL2:	XCHG
	RRC
	JNC	FILL3		;If low bit not set
	DAD	D		;Add bias to offset
FILL3:	XCHG
	DAD	H
	ANI	07Fh		;Mask sector
	DCR	B
	JNZ	FILL2		;If not all bits checked
	STA	SEKSEC
	LHLD	DMAADR
	XTHL			;Set return parameters
	PUSH	D
	PUSH	H		;Set return address

	LXI	H,HSTACT	;host active flag
	MOV	A,M
	MVI	M,1		;always becomes 1
	ORA	A
	JZ	FILL6		;If host buffer inactive
	LXI	H,HSTSEC
	LXI	D,SEKSEC
	MVI	C,SEKTYP-SEKSEC+1
FILL4:	LDAX	D
	CMP	M
	JNZ	FILL5		;If mis-match
	INX	H
	INX	D
	DCR	C
	JNZ	FILL4		;If all bytes not checked
	RET

FILL5:	CALL	FLUSH		;Flush host buffer

FILL6:	LHLD	SEKDSK		;Move disk and type
	SHLD	HSTDSK
	SHLD	ACTDSK
	LHLD	SEKTRK
	SHLD	HSTTRK
	SHLD	ACTTRK
	LDA	SEKSEC
	STA	HSTSEC
	STA	ACTSEC
	LDA	RDFLAG
	ORA	A
	RNZ			;If no read required

	MVI	A,F.RDAT+040h	;Read double density
	JMP	BLKXFR
	space	4,10
;	FLUSH - Write out active host buffer onto disk.

FLUSH:
	LXI	H,HSTWRT
	MOV	A,M
	ORA	A
	RZ			;If host buffer already on disk
	MVI	M,0
	LHLD	HSTDSK		;Move disk and type
	SHLD	ACTDSK
	LHLD	HSTTRK
	SHLD	ACTTRK
	LDA	HSTSEC
	STA	ACTSEC
	MVI	A,F.WRT+040h	;Write double density
;	JMP	BLKXFR
	space	4,10
;	BLKXFR -- blocked mode transfer.
;
;	ENTRY	A = command.

BLKXFR:	MOV	C,A
	LXI	H,HSTBUF	;Set buffer address
	SHLD	BUFADR
	LDK	A,BXADR
	STA	BUFADE
	MOV	A,C
;	JMP	FINAL
	space	4,10
;	F I N A L   --  Preform final transfer processing.
;
;	ENTRY	A = Command.

FINAL:	CALL	PRCDCH		;Process command, drive, cylinder
	LDK	HL,CIOPB+0	;Set buffer address
	STO	C,[hl]		;Set command
	INC	HL
	STO	B,[hl]		;Set drive
	INC	HL
	STO	E,[hl]		;Set cylinder
	INC	HL
	STO	D,[hl]		;Set head
	INC	HL
	MOV	E,A		;Save N field
	LDA	ACTSEC		;Get sector
	MOV	C,A
	INR	A
	STO	A,[hl]		;Set beginning sector
	INC	HL
	MOV	A,E		;Get type
	CPI	MEMTYP/2+1
	JZ	MEMFNL
	STO	A,[hl]		;Set N field
	INC	HL
	ADD	A,A		;N*2
	ADI	low CMDTYP
	MOV	E,A
	MVI	A,0
	ACI	high CMDTYP
	MOV	D,A
	LDA	NUMSEC		;Compute ending sector number
	ADD	A,C
	STO	A,[hl]		;Set EOT
	INC	HL
	LD	A,[de]
	STO	A,[hl]		;Set GPL field
	INC	DE
	INC	HL
	LD	A,[de]
	STO	A,[hl]		;Set DTL

	MVI	A,MRTRY		;Set retry count
FNL1:	STA	RTRY		;Clear retry count
	LDA	CIOPB+2		;Get cylinder number
	CALL	DOSEEK		;Seek to proper track
	JNZ	FNL3		;If seek error

	LDK	HL,BUFADE
	LDK	B,3
FNL2:	LD	A,[hl]		;get ext adr
	OUT	DMA
	DEC	HL		;data is backward in memory
	DEC	B
	JNZ	FNL2		;If not all 3 bytes

	LDK	HL,CIOPB
	LDK	B,CIOPL		;Set command buffer length
	LDK	C,7
	CALL	EXEC		;perform operation
	CMP	40h
	JNZ	FNL3		;If error
	LDA	TEMPBF+1
	SUI	80h
	STA	ERFLAG
	RZ			;If no errors

FNL3:	LDA	RTRY		;Get retry counter
	DCR	A
	JNZ	FNL1		;If not permanent error
	ORI	01h
	STA	ERFLAG		;Set error flag
	RET

MEMFNL:	LHLD	CIOPB+2
	MOV	A,H
	RRC
	ANI	80H
	MOV	H,A
	MOV	A,L
	RRC
	ANI	7FH
	ORA	H
	MOV	H,A
	MOV	A,L
	RRC
	ANI	80H
	MOV	L,A
	XCHG
	LHLD	BUFADR
	MVI	B,128/2
	LDA	CIOPB
	CPI	F.RDAT
	LDA	CIOPB+4
	JZ	MEMOUT
	CALL	INTO
	XRA	A
	RET
MEMOUT:	CALL	OUTOF
	XRA	A
	RET

RTRY:	DB	0
MRTRY:	EQU	10		;Maximum retry count

;	Command buffer disk type dependent values.

CMDTYP:	;	GPL  DTL
	DB	007h,128	;Single density
	DB	00Eh,255	;Double density 256 bytes
	DB	01Bh,255	;Double density 512 bytes
	DB	035h,255	;Double density 1024 bytes
	space	4,10
;	PRCDCH -- Process Command, Drive, Cylinder, and Head.
;
;	ENTRY	A = command.
;
;	EXIT	A = N field (0..4).
;		B = drive.
;		C = command.
;		D = head.
;		E = cylinder.

PRCDCH:	MOV	C,A		;Save Command
	LDA	ACTDSK
	MOV	B,A
	LHLD	ACTTRK		;Get track number
	LDA	ACTTYP		;Get type
	CPI	MEMTYP
	JZ	CDCHM
	XCHG
	MOV	H,A		;Save type
	ANI	1
	JZ	CDCH1		;If single sided
	MOV	A,E
	ANI	1
	MOV	D,A		;Set head
	RLC
	RLC
	ORA	B		;Combine head with drive
	MOV	B,A
	MOV	A,E		;Adjust track for cylinder
	RAR
	MOV	E,A

CDCH1:	MOV	A,H
	ANI	0FEh		;Remove sided bit
	RRC
	RET

CDCHM:	MOV	E,L
	MOV	D,H
	MVI	A,(MEMTYP)/2+1
	RET

	space	4,10
;	Seek to specified Track/Sector
;
;	Entry	A = Track

DOSEEK:
	STO	A,DSEKC+2
	LDK	HL,DSEKC
	LDK	B,DSEKL
;	JMP	MOVETO


;	Move head according to command.
;
;	ENTRY	HL = address of command buffer.
;		B = length of command buffer.
;
;	Exit	Z bit set if no error.

MOVETO:
	CALL	EXECP		;Perform seek
MVTO1:	IN	INTS
	OR	A
	JP	MVTO1		;if not complete
	LDK	A,F.RSTS
	OUT	FDCD		;request status
	LDK	B,2
	CALL	GCMPS		;Get status
	CMP	20h
	RZ   			;If seek complete
	LDA	TEMPBF		;Get true status byte
	ANI	3h		;Mask disk unit
	MOV	C,A
	LDA	ACTDSK
	CMP	C
	JNZ	MVTO1		;If not proper unit
	ORI	001h		;Clear zero flag
	RET
	space	4,10
;	SPECIFY - Specify disk drive characteristics.

SPECIFY:
	LDK	HL,SPEC+1
	LDK	B,LSPEC
	LDK	C,0
	LDA	STEPMS
	ORI	HUT
	STO	A,[hl]
	DEC	HL
	JMP	EXEC		;Specify disk command

DSEKC	DB	F.SEEK,0,0
DSEKL:	=	*-DSEKC

SPEC	DB	F.SPEC
	VFD	4\SRT,4\HUT
	VFD	7\HLT,1\ND
LSPEC	=	*-SPEC
	page
;	E X E C
;	Entry	HL = FWA of command buffer.
;		B  = # of bytes to output
;		C  = # of bytes for status
;
;	Exit	If C <> 0 then see GCMPS.

EXECP:	LDK	C,0		;Set no status byte

EXECX:	INX	H
	LDA	ACTDSK		;Set drive into command buffer
	MOV	M,A
	DCX	H

EXEC:
EXEC1:	IN	FDCS
	OR	A
	JP	EXEC1		;if no master ready bit
	LD	A,[hl]		;command byte
	OUT 	FDCD		;to controller
	INC	HL
	DCR	B
	JNZ	EXEC1		;if more bytes
	MOV	A,C		;# of status bytes+1
	OR	A
	RZ			;if no status bytes
	MOV	B,C		;# of status bytes

EXEC2:	IN	INTS
	OR	A
	JP	EXEC2		;If operation not complete
	space	4,10
;	Get completion status.
;
;	Entry	B= # of status bytes to read
;
;	Exit	TEMPBF = status bytes read in.
;		A = [TEMPBF] and 0F8h.
;		Flags set according to above value in A.

GCMPS:
	LDK	HL,TEMPBF	;Set status buffer address
GCMPS2:	IN	FDCS
	OR	A
	JP	GCMPS2		;if not ready
	IN	FDCD		;Get status byte
	STO	A,[hl]
	INC	HL
	DEC	B		;decrement counter
	JNZ	GCMPS2		;wait until all done
	LDA	TEMPBF		;Get first status byte
	ANI	0F8h
	RET
