;This is a disk I/O driver for CPM+ and a Versafloppy II it is a self conta-
;ined unit and could be easly modified to other disk formats. 
;
;	John Monahan	(201) 783-1548		(Feb 1983)
;
;	FLOPPY DISK MODULES FOR CPM3 WITH VERSAFLOPPY II
;
    ; DEFINE LOGICAL VALUES:
TRUE		EQU	-1
FALSE		EQU	NOT TRUE

; DETERMINE IF BANK SELECTING:
BANKED		EQU	TRUE		;FULL BLOWN VERSION
;
;------	FLOPPY DISK PARAMETERS ---------------------------------
;
X	EQU	60H		;BASE ADDRESS OF PORTS FOR 1791
RSET	EQU	X+0		;CONTROLLER RESET ADDRESS
SELECT	EQU	X+3		;DRIVE SELECT PORT
STATUS	EQU	X+4		;STATUS PORT
TRACK	EQU	X+5		;TRACK PORT
SECTOR	EQU	X+6		;SECTOR PORT
DATA	EQU	X+7		;DATA PORT
CMD	EQU	X+4		;COMMAND PORT
RDACMD	EQU	0C0H		;READ ADDRESS CODE
RDCMD	EQU	088H		;READ SECTOR CODE
WRCMD	EQU	0A8H		;WRITE SECTOR CODE
WRTCMD	EQU	0F4H		;WRITE TRACK CODE
RSCMD	EQU	009H		;RESTORE COMMAND
SKNCMD	EQU	019H		;SEEK NO VERIFY
SKCMD	EQU	1DH		;SEEK WITH VERIFY
STDSDT	EQU	26		;STANDARD 8" 26 SECTORS/TRACK
STDDDT	EQU	50		;STANDARD DD 8" 50 SECTORS/TRACK
NBYTES	EQU	128		;BYTES/SECTOR 
NTRKS	EQU	77		;TRACKS/DISK
;
;
;   ASCII CHARACTERS
;
CR:	EQU	0DH	     ;CARRIAGE RETURN
LF:	EQU	0AH	     ;LINE FEED
BELL:	EQU	7	     ;DING
;

	; DEFINE PUBLIC LABELS:
	PUBLIC	DPH1,DPH2		;DISK PARAMETER HEADERS

	; DEFINE EXTERNAL LABELS:
	EXTRN	@ADRV,@RDRV
	EXTRN	@DMA,@TRK,@SECT
	EXTRN	@DBNK			;BANK FOR DMA OPERATION
	EXTRN	@ERMDE			;BDOS ERROR MODE
	EXTRN	?WBOOT			;WARM BOOT VECTOR
	EXTRN	?PMSG			;PRINT MESSAGE @<HL> UP TO 00, SAVES
					; [BC] AND [DE]
	EXTRN	?PDERR			;PRINT BIOS DISK ERROR HEADER
	EXTRN	?CONIN,?CONO		;CONSOLE IN AND OUT
	EXTRN	?CONST			;CONSOLE STATUS
	EXTRN	?BNKSL			;SELECT PROCESSOR MEMORY BANK
	EXTRN	@CBNK

	EXTRN	?SMSG			;TALK STRING (Note this is a routine
					;I have to send speech to my speech
					;synthesis board you can remove 
					;references to it)


	; INCLUDE CP/M 3.0 DISK DEFINITION MACROS:
	MACLIB	CPM3

	; INCLUDE Z-80 MACRO LIBRARY:
	MACLIB	Z80

	IF	BANKED
	DSEG			;PUT IN OP SYS BANK IF BANKING
	ELSE
	CSEG			;ELSE KEEP IN COMMON MEMORY
	ENDIF

	; EXTENDED DISK PARAMETER HEADER FOR DRIVE 0:
	DW	FWRITEA		;FLOPPY SECTOR WRITE
	DW	FREADA		;FLOPPY SECTOR READ
	DW	FLOGA		;FLOPPY LOGIN PROCEDURE
	DW	FINIT		;FLOPPY DRIVE INITIALIZATION ROUTINE
	DB	0		;RELATIVE DRIVE 0 ON THIS CONTROLLER
	DB	-1		;MEDIA TYPE:

DPH1:	DPH	SKEW6,SDFLOPPY,,

	; EXTENDED DISK PARAMETER HEADER FOR DRIVE 1:
	DW	FWRITEB		;FLOPPY SECTOR WRITE
	DW	FREADB		;FLOPPY SECTOR READ
	DW	FLOGB		;FLOPPY LOGIN PROCEDURE
	DW	FINIT		;FLOPPY DRIVE INITIALIZATION ROUTINE
	DB	1		;RELATIVE DRIVE 1 ON THIS CONTROLLER
	DB	-1		;MEDIA TYPE:

DPH2:	DPH	SKEW6,SDFLOPPY,,

;	DRIVE TRANSLATION TABLES AND DISK PARAMETER BLOCKS:		     
	; MAKE SURE DPB'S ARE IN COMMON MEMORY:
	CSEG

	; 128 BYTE SECTORS, DSDD DISK PARAMETER BLOCK:
DDFLOPPY:	DPB	128,50,77,2048,64,2

	; 128 BYTE SECTORS, SSSD DISK PARAMETER BLOCK (IBM 3740):
SDFLOPPY:	DPB	128,26,77,1024,64,2


	IF	BANKED
	DSEG			;CAN SET BACK TO BANKED SEGMENT IF BANKING
	ENDIF

SKEW6:	SKEW	26,6,1
;
SKEW1:	SKEW	50,1,1
;
;
	;;;;; DCBINIT:
	; INITIALIZATION FOR EACH DRIVE PERFORMED HERE:
FINIT:
	RET			;WE DON'T DO ANY PARTICULAR DRIVE INITS


	;;;;; DCBLOGIN
	; ROUTINE LOGS IN SELECTED DRIVE -- IF HI BIT OF TYPE FLAG IN XDPH
	;  IS SET THEN DRIVE RECALIBRATION IS PERFORMED, DISK FORMAT IS
	;  INSPECTED TO DETERMINE DENSITY AND DPB TYPE TO USE:
FLOGA:	MVI	C,0
	CALL	GETTYPE		;FIND OUT IF SD OR DD DISK
	STA	UNITA		;STORE DENSITY FLAG
	ANI	01000000B
	JNZ	DDFMT
	LXI	H,TALKSD
	CALL	?SMSG
	LXI	H,SKEW6		;DROP IN THE SD SKEW TABLE ADDRESS
	SHLD	DPH1
	LXI	H,SDFLOPPY	;AND THE SD DPB ADDRESS
	SHLD	DPH1+12
	RET
DDFMT:	LXI	H,TALKDD
	CALL	?SMSG
	LXI	H,SKEW1		;NEED TO PLACE ADDRESS BACK BECAUSE MAY HAVE
	SHLD	DPH1		;A SD DISK THERE PREVIOUSLY
	LXI	H,DDFLOPPY
	SHLD	DPH1+12
	RET
;
FLOGB:	MVI	C,1
	CALL	GETTYPE		;FIND OUT IF SD OR DD DISK
	ORI	01H
	STA	UNITB		;STORE DENSITY FLAG
	ANI	01000000B
	JNZ	DDFMTB
	LXI	H,TALKSD
	CALL	?SMSG
	LXI	H,SKEW6		;DROP IN THE SD SKEW TABLE ADDRESS
	SHLD	DPH2
	LXI	H,SDFLOPPY	;AND THE SD DPB ADDRESS
	SHLD	DPH2+12
	RET
DDFMTB:	LXI	H,TALKDD
	CALL	?SMSG
	LXI	H,SKEW1		;NEED TO PLACE ADDRESS BACK BECAUSE MAY HAVE
	SHLD	DPH2		;A SD DISK THERE PREVIOUSLY
	LXI	H,DDFLOPPY
	SHLD	DPH2+12
	RET
;
; THIS ROUTINE SETS UP THE FLOPPY DISK UNIT BYTE
; THE REQUIRED DRIVE IS IN [A]
;
GETTYPE:MOV	A,C		;FIND OUT TYPE OF DRIVE
	ANI	0FH
	ORI	40H		;COME UP DEFALT IN 8" DD
	STA	UNIT
	MVI	B,5		;MAX NO OF RETRYS
	CALL	USL1
	LDA	UNIT
	ANI	01000000B	;GET DENSITY FLAG
	RET
;
USL1:	PUSH	B
	PUSH	H
	DB	0EDH,73H
	DW	SPSV		;-----<LD  (SPSV),SP
	POP	H
	CALL	DRVSET		;SELECT DRIVE IN HARDWARE
	CALL	IDRD		;TRY READING TRACK ID
	POP	B
	RZ			;IF CORRECT DENSITY WILL BE Z
	DCR	B		;DECREASE 5.......0 IF Z THEN ERROR
	JZ	SPECIAL
	CALL	CHGTYP
	JMP	USL1		
;
SPECIAL:LXI	H,DENSERR
	CALL	?PMSG
	JMP	0F000H		;ABORT TO MONITOR
;
;
CHGTYP:	LDA	UNIT
	ADI	01000000B	;TOGGLE DENSITY BIT
	ANI	01111111B	;CLEAR BIT 7
	STA	UNIT
	RET
;
;	READ A SECTOR
FREADA:	LDA	UNITA
	JMP	FREAD
FREADB:	LDA	UNITB
FREAD:	STA	UNIT
	LXI	B,302H
READ1:	PUSH	B
	CALL	RDSC
	POP	B
	RZ
	CALL	FRETRY
	JMP	READ1		
;
;	WRITE A SECTOR
FWRITEA:LDA	UNITA
	JMP	FWRITE
FWRITEB:LDA	UNITB
FWRITE:	STA	UNIT
	LXI	B,302H
WRITE1:	PUSH	B
	CALL	WRSC	
	POP	B
	RZ
	CALL	FRETRY
	JMP	WRITE1		  
;
FRETRY:	DCR	B		  
	JNZ	RETRY2
	MVI	B,3
	DCR	C
	JNZ	RETRY1
	POP	PSW
	XRA	A
	INR	A
RETRY2:	RET
;
RETRY1:	PUSH	B
	PUSH	H
	DB	0EDH,73H
	DW	SPSV		;------<<<<<<LD	(SPSV),SP
	POP	H
	MVI	A,RSCMD
	CALL	SEEK4
	XRA	A
	POP	B
	RET
;
;	SELECT DRIVE IN HARDWARE
;
DRVSET: LXI	D,UNIT
	LDAX	D
	ANI	0E0H
	MOV	C,A		;STORE DRIVE TYPE IN [C]
	LDAX	D
	ANI	03
	MOV	B,A		;STORE DRIVE # IN [B]
	MVI	A,1
	JZ	DRVSEL		
CKDRV1:	RLC
	DCR	B		  
	JNZ	CKDRV1	
DRVSEL:	ORA	C		;COMBINE TYPE & DRIVE#
	ANI	7FH
	MOV	B,A		;[B] CONTAINS INFO FOR HARDWARE
	MVI	A,STDSDT	;SETUP FOR SD
	STA	COUNT		;STORE AS 26 SECTORS/TRACK
	MVI	A,40H		;WAS IT DD
DRV1:	CMP	C
	JNZ	CKDRV		
	MVI	A,STDDDT	;SETUP FOR DD 
	STA	COUNT		;SET TO 50 SECTORS/TRACK
CKDRV:	MOV	A,B		;GET HARDWARE SELECT DATA
	CMA			;HARDWARE IS INVERTED
	OUT	SELECT
	LDAX	D
	STA	UNITCK
	CALL	DELAY
RDYCK:	IN	STATUS
	ANI	80H
	JNZ	END2
	RET
;
;	READ PRESENT DISK ADDRESS
IDRD:	CALL	WAIT
	LXI	H,IDSV
	LXI	B,600H+DATA	;READ 6 BYTES
	MVI	A,0F8H
	STA	ERMASK
	CALL	SWEB
	MVI	A,RDACMD	;DO THE ID READ
	CALL	RDSCID
	LDA	IDSV
	CPI	NTRKS		;IS IT REASONABLE
	JNC	SEEK0
	OUT	TRACK	
	XRA	A
	RET
;
RDSCID:	STA	CMDSV
	DI
	OUT	CMD
	NOP
	INIR
	DI
	JMP	ENDX
;
DELAY:	MVI	A,80		;DELAY ~32 MS (DOES NOT SEEM TO BE CRITICAL)
DELAY1:	MVI	B,0
M0:	DCR	B		
	JNZ	M0
	DCR	A
	JNZ	DELAY1		
	RET
;
;	READ SECTOR COMMAND
RDSC:	CALL	DRINIT
	MVI	A,RDCMD
RDSCO:	STA	CMDSV
	DI

	IF	BANKED
	JMP	ADJBNKS
	
	CSEG

ADJBNKS:LDA	@CBNK
	PUSH	PSW
	LDA	@DBNK
	CALL	?BNKSL
	MVI	A,RDCMD
	OUT	CMD
	NOP
	INIR
	POP	PSW
	CALL	?BNKSL	
	DI
	JMP	ENDX	

	DSEG

	ELSE

	OUT	CMD
	NOP
	INIR
	DI
	JMP	ENDX	

	ENDIF
;
;	WRITE SECTOR COMMAND
WRSC:	CALL	DRINIT
	MVI	A,WRCMD
	STA	CMDSV
	DI	

	IF	BANKED
	JMP	ADJBBB
	
	CSEG

ADJBBB:	LDA	@CBNK
	PUSH	PSW
	LDA	@DBNK
	CALL	?BNKSL
	MVI	A,WRCMD
	OUT	CMD
	NOP
	OUTIR
	POP	PSW
	CALL	?BNKSL	
	DI
	JMP	ENDX	

	DSEG

	ELSE

	OUT	CMD
	NOP
	OUTIR
	DI			;FOLLOW INTO ENDX

	ENDIF
;
;	END  OF COMMAND
ENDX:	CALL	WAIT
	IN	STATUS
	MOV	D,A
	LDA	ERMASK
	ANA	D
	RZ
END1:	MOV	A,D
END2:	STA	ERSTAT
	CALL	DELAY
	DB	0EDH,7BH
	DW	SPSV		;------< LXI	SP,(SPSV)
	XRA	A
	DCR	A		;RETURN NZ TO INDICATE AN ERROR
	STA	UNITCK
	RET
;
;	DRIVE INITIALIZATION
;
DRINIT:	POP	H
	DB	0EDH,73H
	DW	SPSV		;-------< LD	(SPSV),SP
	PUSH	H
	LDA	UNIT
	MOV	D,A
	LDA	UNITCK
	CMP	D
	JZ	DINIT1		
	CALL	DRVSET
	CALL	IDRD
DINIT1:	CALL	SEEK
	MVI	A,0FEH
	STA	ERMASK
;
TRINT:	LHLD	@DMA		;SETUP DMA ADDRESS AND BYTE COUNT
	LDA	@SECT
	OUT	SECTOR
	LXI	B,NBYTES*100H+DATA
;
SWEB:	IN	SELECT		;ENABLE WAIT STATES
	ANI	7FH
	OUT	SELECT
	RET
;
;	SEEK TRACK
;
SEEK:	CALL	RDYCK
	MVI	C,NTRKS		;MUST BE REASONABLE TRACK #
	LDA	@TRK		;ALWAYS < 0FFH TRACKS FOR FLOPPY
	CMP	C
	JC	SEEK1		
SEEK0:	MVI	A,0FH
	JMP	END2		
SEEK1:	MOV	C,A
	IN	TRACK
	CMP	C
	RZ			;IF SAME TRACK NO NEED TO SEEK
	MVI	A,SKCMD
SEEK4:	STA	CMDSV
	MVI	B,210
S0:	DCR	B		
	JNZ	S0
	CALL	WAIT
	LDA	@TRK
	OUT	DATA
	MVI	A,80H
	STA	ERMASK
	LDA	CMDSV
	OUT	CMD
	MVI	B,10
D0:	DCR	B		
	JNZ	D0	
	CALL	ENDX
	CALL	DELAY
	LDA	CMDSV
	CPI	RSCMD		;NO NEED TO CHECK RESTORE COMMAND
	RZ
	IN	STATUS
	ANI	10H
	JNZ	SEEK2		
	IN	TRACK
	CMP	C
	RZ
SEEK2:	MVI	A,20H
END2JP:	JMP	END2  
;
WAIT:	MVI	E,0
	PUSH	B
	MVI	C,2
WAIT2:	IN	STATUS
	ANI	1
	JZ	DWAIT			
	DCR	B
	JNZ	WAIT2			
	DCR	E
	JNZ	WAIT2			
	DCR	C
	JNZ	WAIT2			
	POP	B
	IN	SELECT			;IF BY THIS TIME NOT READY FORCE
	ORI	80H			;A HARDWARE RESET
	OUT	RSET
F0:	DCR	B			
	JNZ	F0			
	IN	RSET
	CALL	FRCINT
	MVI	A,RSCMD
	CALL	SEEK4
	MVI	A,0FEH
	JMP	END2JP	
;
;	DISABLE WAIT STATES
DWAIT:	POP	B			;TO BALANCE THE ABOVE PUSH IN WAIT
	IN	SELECT
	ORI	80H
	OUT	SELECT
	RET
;
;	FORCE CHIP INTERUPT
FRCINT:	MVI	A,0D0H
	OUT	CMD
	MVI	A,10
FRC1:	DCR	A
	JNZ	FRC1			
	IN	STATUS
	RET
;
;
TALKSD	DB	'FLOPPY IS SINGLE DENSITY',0
TALKDD	DB	'FLOPPY IS DOUBLE DENSITY',0
DENSERR	DB	CR,LF,'Could not determine disk density/format',0
;
;
UNIT	DS	1		;STORE FOR FLOPPY NEW UNIT BYTE
ERMASK	DS	1		;FLOPPY ERROR MASK
ERSTAT	DS	1		;FLOPPY ERROR FLAG STORE
CMDSV	DS	1		;COMMAND SAVE 
SPSV	DS	2		;SP SAVE 
COUNT	DS	1		;SECTORS/TRACK STORE
UNITCK	DS	1		;OLD FLOPPY UNIT BYTE
UNITA	DS	1		;STORE OF DENSITY DATA FOR DRIVE A
UNITB	DS	1		;STORE OF DENSITY DATA FOR DRIVE B
IDSV	DS	7		;6 BYTES (USED FOR TRACK ID COMMAND)
;
    