TITLE	   'Memory disk implementation for Quasi-disk with CPM80+ '

	; define logical values:
true		equ	-1
false		equ	not true

	; determine if bank selecting:
banked		equ	true		;full blown version

	; define public labels:
	public	dph4			;disk parameter header (drive E:)

	; define external labels:
	extrn	@dma,@trk,@sect		;transfer parameters
	extrn	@dbnk			;bank for dma operation
	extrn	@ermde			;BDOS error mode
	extrn	?pmsg			;print message @<hl> up to 00, saves
					; [bc] and [de]
	extrn	?pderr			;print BIOS disk error header
	extrn	?bnksl			;select banked segment
	EXTRN	?SMSG			;ROUTINE TO SPEAK A STRING

	; include CP/M 3.0 disk definition macros:
	maclib	cpm3

	; include z-80 macro library:
	maclib	z80


	; misc. equates:
qbase	equ	80h
qpages	equ	16
cpmbs	equ	2048
iobyte	equ	0EFH		;my front panell switch
qdata	equ	qbase+0
qstat	equ	qbase+1
qcom	equ	qbase+1
qpfack	equ	qbase+2
qaddl	equ	qbase+4
qaddm	equ	qbase+5
qaddh	equ	qbase+6

	if	banked
	dseg			;put in op sys bank if banking
	else
	cseg			;else keep in common memory
	endif

	; Extended disk parameter header for drive 11:
	dw	MDwrt		;memory disk write routine
	dw	MDrd		;memory disk read routine
	dw	MDlogin		;memory disk login routine
	dw	MDinit		;memory disk initialization routine
	db	0		;relative drive 0 on this controller
	db	0		;type byte not used
dph4:	dph	0,MD$dpb,,


;*	Drive translation tables and disk parameter blocks:		     

	; make sure DPB's are in common memory:
	cseg

	; Memory disk disk parameter block set up as follows --
	;	128 byte sectors
	;	255 sectors / track
	;	2K allocation block size
	;	allocation for up to 32 tracks (1 MByte ramdisk)
	;	128 directory entries (none checked)
MD$dpb:	;	dpb	128,255,16,2048,128,0,8000
	;       Note I enter by hand because I want to remove one block
	;	at the top of ram for disk formatted flag

      	DW	00ffh		; 128 BYTE RECORDS PER TRACK
      	DB	04h,0fh		; BLOCK SHIFT AND MASK
       	DB	00h		; EXTENT MASK
      	DW	01fah		; MAXIMUM BLOCK NUMBER - 1 for format flag
      	DW	007fh		; MAXIMUM DIRECTORY ENTRY NUMBER
      	DB	0c0h,00h	; ALLOC VECTOR FOR DIRECTORY
      	DW	8000h		; CHECKSUM SIZE (FIXED DISK)
      	DW	0		; OFFSET FOR SYSTEM TRACKS
      	DB	0,0		; PHYSICAL SECTOR SIZE SHIFT
                


	if	banked
	dseg			;can set back to banked segment if banking
	endif


;**************	Code begins here: *****************************************

	;;;;; MDinit
	; memory disk initialization:
MDinit:	ret			;no memory disk inits done here!!


	;;;;; MDlogin
	; memory disk login procedure:
MDlogin:
	OUT 	QPFACK			;Reset power fail
	MVI 	A,6 
	OUT 	QCOM		        ;Reset auto-incr/decr
	in	iobyte			;my front pannel switch
	bit	7,a
	jz	allok
	bit	6,a
	jz	cleanup			;force a disk cleanup. else check if
	mvi	a,qpages-1		;disk is formatted. Get top 64k page
	out	qaddh
	mvi	a,0ffh
	out	qaddm
	dcr	a
	out	qaddl			;board addressed to xfffeh
	in	qdata			;flush buffer
	in	qdata			;get flag 
	cpi	0aah			;is it 0aah
	jnz	cleanup			;if not must be new disk
allok:	LXI	H,TALK
	CALL	?SMSG
	ret

CLEANUP:MVI 	A,55H 		;is disk alive at all
	OUT 	QDATA
	IN	QDATA
	IN 	QDATA
	CPI 	55H 
	JNZ 	QERR
	MVI 	A,0AAH 
	OUT 	QDATA
	IN 	QDATA 
	IN 	QDATA
	CPI 	0AAH 
	JNZ 	QERR

	LXI	H,TALK$FORM		;speak formating directory
	CALL	?SMSG
					; Format Qdisk directory
	MVI 	A,4 
	OUT 	QCOM
	XRA 	A 
	OUT 	QADDH
	OUT 	QADDM 
	OUT 	QADDL
	MVI 	B,32			;# directory sectors
QF2	MVI 	A,0E5H 
	MVI 	L,128
QF3	OUT 	QDATA 
	DCR 	L 
	JNZ 	QF3			;128 bytes of E5
	MVI 	A,80H 
	OUT 	QDATA			;and 80 for Checksum
	DCR 	B 
	JNZ 	QF2
	RET

	; here if we couldn't send command to ramdisk -- must be no ramdisk:
	; no memory disk -- we must return a 0000h to the BDOS so that it
	; knows this is an invalid drive select:
QERR:	LXI	H,mdoffline
	CALL 	?PMSG
	pop	d		;[de] has calling routine's return address
	lxi	h,0		;this will be invalid drive flag to BDOS
	xthl			;put on the stack in place of the XDPH ptr.
	xchg
	pchl			;back to caller...


	;;;;; MDrd
	; memory disk read sector entry point:
MDrd:	CALL	QDSETUP
	LDA	@DBNK
	JMP	MDrd1

	cseg

MDrd1:	CALL	?BNKSL
	IN	QDATA		;flush buffer
QRLOOP:	IN	QDATA
	MOV	M,A
	ADD	C
	MOV	C,A
	INX	H 
	DJNZ	QRLOOP
	XRA	A
	CALL	?BNKSL
	JMP	MDrd2

	dseg

MDrd2:	in	qstat
	ani	00001000b
	jz	power$error
	IN	QDATA
	XRA	C		;will be 0 if checksum was ok
	rz
	jmp	check$error


	;;;;; MDwrt
	; memory disk write sector entry point:
MDwrt:	CALL	QDSETUP
	LDA	@DBNK
	JMP	MDwrt1

	cseg

MDwrt1:	CALL	?BNKSL
QWLOOP:	MOV	A,M
	OUT	QDATA
	ADD	C
	MOV	C,A
	INX	H
	DJNZ	QWLOOP
	MOV	A,C
	OUT	QDATA			;store checksum byte
	XRA	A
	CALL	?BNKSL
	JMP	MDwrt2

	dseg

MDwrt2:	in	qstat
	bit	3,a
	jz	power$error
	bit	2,a
	jz	wr$error
	XRA	A
	RET
;
; CALCULATE LINEAR RAM ADDRESS FROM TRACK AND SECTOR
; --------------------------------------------------
; Maximum is 255 tracks and 255 sectors per track
; Sector size is fixed at 128 bytes

QDSETUP: 			; Multiply track number by sectors per track
	LHLD 	MD$dpb 
	XCHG				;DE = sectors per track
	MVI 	H,0 
	MVI 	L,0			;Must be MVI for absolute
	LDA 	@TRK
	ORA 	A 
	JZ 	QDS2			;Br if track 0
MUxL	DAD 	D 
	DCR 	A
	JNZ 	MUxL

	; HL has the result, now add the sector number

QDS2	XCHG 
	LHLD 	@sect
	MVI 	H,0			;In case caller had junk in B
	DAD 	D

	; HL has logical sector number

	; Now calculate the real address and output it to the
	; QDISK address registers. adrs = adrs + (adrs * 128) 
	; Every sector adds 81H  to the address (80H bytes
	; + 1 byte for checksum byte)

	ORA 	A			;Reset carry
	MOV 	A,H 
	RAR 
	MOV 	D,A			;DE = HL shr 1 
	MOV 	A,L 
	RAR 
	MOV 	E,A
	RAR				;A(7) has carry
	ANI 	80H 
	ADD 	L 
	OUT 	QADDL			;A = L + carry
	MOV 	A,H 	
	ADC 	E 
	OUT 	QADDM			;A = H + E + carry
	MVI 	A,0 
	ADC 	D 
	OUT 	QADDH			;A = D + carry

	; Initalize for read or write

	MVI 	A,4 
	OUT 	QCOM			;auto-increment
	LHLD	@dma
	MVI 	B,128
	MVI	C,0
	RET

power$error:			; here to process disk read-only error:
	lxi	h,md$wp
erx1:	call	errpnt		;display message if so
	mvi	a,1		;return error to bios
	ret

check$error:			; here for checksum error
	lxi	h,md$cksum
	jmp	erx1

wr$error:			; here for write protected disk
	lxi	h,read$only
	call	errpnt
	mvi	a,2		;tell BDOS read only disk
	ret	

	;;;;; Errpnt
	; perhaps print error message if disk error:
errpnt:	lda	@ermde		;get bdos error mode
	inr	a		;bdos returning errors ?
	rz			;yes -- don't print error message
	push	h		;save [hl]
	call	?pderr		;no -- print error message header
	pop	h
	call	?pmsg		;then the error message
	ret

	; memory disk error messages:
md$offline:	db	' Memory disk appears to be OFFLINE',0
md$wp:		db	' Memory disk power fail',0
md$cksum:	db	' Memory disk checksum error',0
read$only:	db	' Memory disk is set to read only',0
TALK$FORM	db	'FORM ATING '
TALK:		DB	'MEMRY DISK ON DRIVE EE ',0
	end
