	TITLE 'FDC 0765 DOUBLE density diskette handler'

; Testversion Laufwerke G und H als 1.2
;             Laufwerk  I als 1.6

;    CP/M-80 Version 3     --  Modular BIOS

; MC80 Version 1.0
; 04.02.90

;	Disk I/O Module based diskette systems

	;	Initial version
	;	DOUBLE density floppy only.

;	CSEG	;FUER TEST
	dseg

    ; Disk drive dispatching tables for linked BIOS

	public	fdsd0765,fdsd1765,fdsd2765
	public	motoff,motcot

	extrn	flosta

    ; Variables containing parameters passed by BDOS

	extrn	@adrv,@rdrv
	extrn	@dma,@trk,@sect
	extrn	@dbnk,@cbnk
	EXTRN	@CNT
;	extrn	@comon

    ; System Control Block variables

	extrn	@ermde		; BDOS error mode

    ; Utility routines in standard BIOS

;	extrn	?wboot	; warm boot vector
	extrn	?pmsg	; print message @<HL> up to 00, saves <BC> & <DE>
;	extrn	?conin,?cono	; con in and out
;	extrn	?const		; get console status
	extrn	?xmove,?move

    ; Port Address Equates

	maclib ports

    ; CP/M 3 Disk definition macros

	maclib cpm3

    ; Z80 macro library instruction definitions

	maclib z80

    ; common control characters

cr	equ 13
lf	equ 10
bell	equ 7

;**************************************************************
; Vereinbarungen
;**************************************************************

p$765$cmd equ 0f4h
p$765$dat equ 0f5h
p$fdc$ctr equ 0f2h

    ; Extended Disk Parameter Headers (XPDHs)

	dw	fd$write
	dw	fd$read
	dw	fd$login
	dw	fd$init0
	db	0,0		; relative drive zero
fdsd0765:
	dph     0,dpbsd

	dw	fd$write
	dw	fd$read
	dw	fd$login
	dw	fd$init1
	db	1,0		; relative drive one
fdsd1765:
	dph	0,dpbsd

	dw	fd$write
	dw	fd$read
	dw	fd$login
	dw	fd$init2
	db	2,0		; relative drive one
fdsd2765:
	dph	0,dpbsd1


	cseg	; DPB must be resident

; FUER 40 SPUR-LW:
DPBSD	DPB	1024,5,40,1024,64,2
; FUER 80 SPUR-LW:
DPBSD1	DPB	1024,10,80,2048,128,2


	dseg	; rest is banked
;	cseg

    ; Disk I/O routines for standardized BIOS interface

; Initialization entry point.

;		called for first time initialization.

fd$init2:
fd$init1:
fd$init0:
	lda @adrv
	cpi 0
	cz ini765
	ret


fd$login:
		; This entry is called when a logical drive is about to
		; be logged into for the purpose of density determination.

		; It may adjust the parameters contained in the disk
		; parameter header pointed at by <DE>

	ret	; we have nothing to do in
		;	simple single density only environment.


; disk READ and WRITE entry points.

		; these entries are called with the following arguments:

			; relative drive number in @rdrv (8 bits)
			; absolute drive number in @adrv (8 bits)
			; disk transfer address in @dma (16 bits)
			; disk transfer bank	in @dbnk (8 bits)
			; disk track address	in @trk (16 bits)
			; disk sector address	in @sect (16 bits)
			; pointer to XDPH in <DE>

		; they transfer the appropriate data, perform retries
		; if necessary, then return an error code in <A>

fd$read:		; EINEN SEKTOR LESEN
	lxi	h,read$msg
	mvi	a,'R'
	CALL	CALC
	call	readit	; SEKTOR LESEN
	push	psw
	lda	@dbnk
	ora	a
	jrz	fd$read1
	mov	b,a
	mvi	c,0
	call	?xmove
fd$read1:
	lxi	d,buffer
	lhld	@dma
	lxi	b,1024
	call	?move
	pop	psw
	JR	RETAUS

fd$write:		; SCHREIBEN EINES SEKTORS
	lxi	h,write$msg
	mvi	a,'W'
	CALL	CALC
	lda	@dbnk
	ora	a
	jrz	fd$write1
	mov	c,a
	mvi	b,0
	call	?xmove
fd$write1:
	lxi	h,buffer
	lded	@dma
	lxi	b,1024
	call	?move
	call	writeit		; Sektor schreiben
;
RETAUS:

	ORA	A
	rz		; kein Fehler
; Fehlerauswertung Floppy-Treiber
	lda @ermde ! cpi 0ffh ! jz hard$error
	; Had permanent error, print message like:

	; BIOS Err on d: T-nn, S-nn, <operation> <type>, Retry ?

	lxi h,flerr$msg ! call ?pmsg		; print message header
	lhld operation$name ! call ?pmsg	; last function

	; then, messages for indicated error

	lda	disk$status+1	; get status byte 1 from last error
	lxi	h,error$table	; point at table of message addresses
	call	errmodes
	lda	disk$status+2	; get status byte 2 from last error
	call	errmodes

;	lxi h,error$msg 
; call ?pmsg	; print "<BEL>, Retry (Y/N) ? "
;	call u$conin$echo	; get operator response
;	cpi 'Y' 
; jz more$retries	; Yes, then retry 10 more times

hard$error:
	MVI	A,1	; Return hard error to BDOS
	RET

errmodes:
	mvi	b,8
errmode:
	mov e,m ! inx h ! mov d,m ! inx h ; get next message address
	add a ! push psw	; shift left and push residual bits with status
	xchg ! cc ?pmsg ! xchg	; print message, saving table pointer
	pop psw
	djnz	errmode		; continue for all bit
	ret

; Berechnen physischen Sektor
;
CALC:	sta	anzrw	; Speichern R / W fuer Anzeige
	shld	operation$name
	XRÁ	A
	MOÖ	B,Á	ť HDSEL AUĆ 0
	STÁ	head
	LDA	@SECT	; 0..9 IST DER BEREICH
	INR	A	; 1..10 STARTSEKTOR DES GEBIETES (1K)
	STA	sector
	SUI	6	; PRUEFEN OB VORDERSEITE
	JC	CAL1
	INR	A	; RUECKSEITE SEKTOREN AB 1 (1K)
	STA	sector
	mvi	b,4	; HDSEL in unit auf 1
	MVI	A,1	; SIDE SELECT AUF 1
	STA	head
CAL1:	LDA	@RDRV	; LAUFWERK
	ora	b	; + HDSEL
	STA	unit
	LDA	@TRK	; SPUR
	STA	track
	lxi	h,buffer
	SHLD	zdma
	lded	@rdrv
	lxi	h,initab
	mvi	d,0
	dad	d
	mov	a,m
	ora	a
	jrnz	anz
	dcr	m
	call	ini765
;
; Anzeige Read / Write
;
anz:	LDA	@adrv	; log. Laufwerk
	ADI	41H
	STA	ANZDRV
	sta	drive$msg
	LDA	track	; phys. Spur
	CALL	HEXASC
	SHLD	ANZTRK
	shld	track$msg
	LDA	@SECT	; log. Sektor
	INR	A
	CALL	HEXASC
	SHLD	ANZSEK
	shld	sector$msg
	LXI	H,ANZEIG
	call	flosta
	ret
;
; UNTERPROGRAMM FUR UMRECHNUNG HEX IN ASCII
;
HEXASC:
	MVI	L,'0'
HEXAS1:	sui	10
	JRC	HEXAS2
	INR	L
	JR	HEXAS1
HEXAS2:	ADI	'0'+10
	MOV	H,A
	RET

;
; ANZEIGEPUFFER LESE- / SCHREIBVORGANG
;
ANZEIG:
ANZDRV:	DB	' :'
ANZTRK:	DB	'  /'
ANZSEK:	DB	'   '
ANZRW:	DB	' '

	; error message components
flerr$msg	db	cr,lf,bell,'BIOS Error on '
drive$msg	db	' : T-'
track$msg	db	'  , S-'
sector$msg	db	'  ',0

operation$name:
	dw	read$msg

read$msg:
	db	', Read',cr,lf,0
write$msg:
	db	', Write',cr,lf,0
;error$msg:
;	db	' Retry (Y/n) ? ',0

	; table of pointer to error message strings
	; first entry is for bit 7 of Biosdskt-Routine

error$table:
	dw	b17$msg
	dw	b16$msg
	dw	b15$msg
	dw	b14$msg
	dw	b13$msg
	dw	b12$msg
	dw	b11$msg
	dw	b10$msg

	dw	b27$msg
	dw	b26$msg
	dw	b25$msg
	dw	b24$msg
	dw	b23$msg
	dw	b22$msg
	dw	b21$msg
	dw	b20$msg

b17$msgş	dâ	§ End of cylinder,',0
b16$msgş	db	0
b15$msg:	db	' CRC-Error,',0
b14$msg:	db	' Over run,',0
b13$msgş	db	0
b12$msg:	db	' Sector not found,',0
b11$msg:	dâ	§ Disk write protect,',0
b10$msg:	db	' Missing address mark,',0

b27$msgş	dâ	0
b26$msgş	db	0
b25$msg:	db	' CRC-Error in data field,',0
b24$msg:	db	' Track error,',0
b23$msgş	db	0
b22$msg:	db	' Scan not satisfied,',0
b21$msg:	dâ	§ Bad Track,',0
b20$msg:	db	' Missing address mark in data field,',0

initab:	db	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
buffer:	ds	1024

;**************************************************************
; Floppy-Disk Interface using upD 765/P8282 Controller
;**************************************************************

ini765:	mvi	a,4		; reset TC
	out	p$fdc$ctr
	sta	r$fdc$ctr
ini765z:
	mvi b,0
ini765w:
	djnz ini765w		; delay
	in p$765$cmd ! cpi 80h	; request for master ?
	jrz cspecify		; no --> skip
	in p$765$dat		; else read data
	jr ini765z		; and try again
cspecify:
	call	specify

	call	recal

	; recalibrate drive to track zero
recal:
	lxi b,0207h		; Recal-cmd. =07 - 2 Byte
	call moto

	; sense drive interrupt status
sense:
	lxi b,0108h		; comd. =08 - 1 Byte
	call cmdfd
	call next		; fetch result
	mov b,a			; save it
	cpi 80h			; invalid cmd. ?
	cnz next		; no --> next result
	bit 5,b			; seek finnished ?
	jrz sense		; no --> loop back
	ret			; yes --> all done

specify:
	lxi h,spec5-1		; specify parameter
	lda @rdrv
	cpi 2
	jrc specify1
	lxi h,spec52-1
specify1:
	lxi b,0303h		; specify command parameter
	call cmdfd1		; send command
	ret

;**************************************************************
; specify parameter for TEAC FD-55 B
;**************************************************************

; vorerst fuer 1.2-Laufwerke

spec5:
; Simon
	db	9fh
	db	03h

; Schiller
;	db	9fh
;	db	15h

; Kramer
;	db	1fh		; xxxx=SRT xxxx=HUT
;	db	33h		; xxxxxxx=HLT x=ND

spec52:
; Simon
	db	0e1h		; Wert von Loeber , Simons: 0efh
	db	03h

; cpm3org
;	db	1101$1111B
;	db	0001$0011B	; specify param. 5 1/4 TEAC B
				; HLT= 36 ms , HUT= 480 ms
				; SRT=  6 ms , NON-DMA MODE

;**************************************************************
; command table for upD 765
;**************************************************************

cmdtab:	db	046h		; MFM read command
unit:	db	0		; Head 0 / unit A
track:	db	0		; Track 0
head:	db	0		; Head 0
sector:	db	1		; Sector 1
N:	db	3		; N=3 1024 Bytes
EOT:	db	5		; EOT , 5 sectors per track
GPL:	db	25h		; GPL gap length
DTL:	db	-1		; DTL data terminal length

disk$status:
resltb:	ds	7		; result table for FDC
zdma:	ds	2		; current DMA-Address
r$fdc$ctr:
	db	0

	cseg		; wird aus INT abgefragt
motcot:
	db	0		; Time-counter for motor on

	dseg

;**************************************************************
; sense drive status
;**************************************************************

sdrs:	lxi b,0204h		; SDRS-cmd. = 04 - 2 Bytes
	call cmdfd
	call next ! ret

;**************************************************************
; seek to track in A, drive in unit
;**************************************************************

seek:	
;	sta track
	lxi b,030fh		; SEEK-cmd. = 0F - 2 Bytes
	call moto
	jmp sense
;	call sense
;seekby:	in p$765$cmd
;	ani 0fh
;	jrnz seekby
;	ret

;	mov	b,a
;	lda	track
;	cmp	b
;	rz
;	call	recal		; solange suchen bis Spur gefunden
;	jr	seek

;**************************************************************
; motor off and select disk off
; calld by time-routine after 5 second
;**************************************************************

	cseg
motoff:
	mvi a,4 ! sta r$fdc$ctr
	out p$fdc$ctr
	ret

	dseg

;**************************************************************
; motor on and select disk drive on
; wait until disk ready, then transmit cmd. in BC to FDC
;**************************************************************

moto:
	push b			; save cmd.
	call specify
moto1:	lda @rdrv
	ori 0f4h
	sta r$fdc$ctr
	out p$fdc$ctr
	mvi a,5			; 5 seconds for motor on
	sta motcot
	call sdrs ! bit 5,a	; Drive ready ?
	jrz moto1		; no --> Wait
	pop b

;**************************************************************
; Command to upD 765
; Transmitt command in BC to Floppy Disk Controller
; B=Number of Bytes to transmitt, C=command
;**************************************************************

cmdfd:	lxi h,cmdtab
cmdfd1:
;	call delay
	in p$765$cmd
	ani 0c0h ! cpi 80h
	jrnz cmdfd1		; Request for Master ?
	mov a,c ! out p$765$dat	; send command
	inx h ! mov c,m
	djnz cmdfd1		; next command
	ret

;**************************************************************
; Verzoegerung fuer Statusflag 765
;**************************************************************

;delay:	push b ! mvi b,0fh
;delay1:	djnz delay1
;	pop b ! ret

;**************************************************************
; next result
; read next result byte from FDC
;**************************************************************

next:
;	call	delay
	in p$765$cmd
	ani 0c0h ! cpi 0c0h
	jrnz next		; Request for Master ?
	in p$765$dat
	ret

;**************************************************************
; Fetch result from FDC
; check for R/W Errors, if any Z=0
;**************************************************************

result:	mvi b,6			; 7 results
	call next		; fetch it
	lxi h,resltb
	mov m,a			; store it
	ani 0c0h		;any Error in Status 0
	mov c,a
reslop:	call next
	inx h ! mov m,a
	djnz reslop
	mov a,c			; Error report Status 0
	ora a ! ret		; Return with Error in A

;**************************************************************
; Simple READ and WRITE Entry Points
; R/W in non DMA Mode a 1024 Byte Sector
; R/W one sector in <SECTOR>
; R/W track in <TRACK>, Seek-command given
; R/W Drive number in <UNIT>
; Destination / Source in <ZDMA>
;**************************************************************

;**************************************************************
; Write selected sector to FDD
;**************************************************************

writeit:
	db 11h			; LXI D,...
	OUTI			; Write data
	mvi a,45h		; 765 MFM-Write-command in A
	mvi	b,10		; Schreiben mit 10 Versuchen
				; wegen Over Run
	jr rwit

;**************************************************************
; read selected sector from FDD
;**************************************************************

readit:	db 11h			; LXI D,...
	INI			; Read data
	mvi a,46H		; 765 MFM-Read-command in A
	mvi	b,1		; Lesen mit einem Versuch wegen
				; dem PLL-Test
rwit:	sta cmdtab		; command code for Read/Write
	sded inimfm1		; set INI / OUTI op-codes
	sded inimfm2
	sded inimfm3
	sded inimfm4
	call seek
rwloop:
;	mvi b,1		; vorerst nur 1 Versuch retry-counter
rwop:	push b
	mvi b,9			; 9 commands
	lda cmdtab
	mov c,a			; command in C
	di
	call moto		; Transfer command
	lhld zdma		; DMA-adress in <HL>
	mvi c,p$765$dat
	call rw765m		; Transfer Sector
	lda r$fdc$ctr
	res 2,a
	out p$fdc$ctr		; Terminate Count
	setb 2,a
	nop ! nop ! nop		; delay
	out p$fdc$ctr		; reset TC
	EI			; Enable INT.
	call result		; Enter result phase
	pop b ! rz		; no Errors --> RET
	djnz rwop		; Try it again if Errors
	mvi a,1 ! ret		; unrecoverable Error

;**************************************************************
; Physical READ/WRITE Entrys non-DMA mode
;**************************************************************

;**************************************************************
; HL=Destination, C=Port  only sectors with 1024 Bytes
;**************************************************************

rw765m:				; read or write 1024 bytes
	mvi b,0			; 256 bytes
rwmfm1:	in p$765$cmd
	rlc			; Bit 7 in Carry
	jrnc rwmfm1		; Request for Master ?
	rlc ! rlc
	rnc			; Execution mode ?
inimfm1:
	INI			; INI if Read data, OUTI if Write data
	jnz rwmfm1		; repeat til done
rwmfm2:	in p$765$cmd		; next 256 bytes, Register B is Zero
	rlc			; Bit 7 in Carry
	jrnc rwmfm2		; Request for Master ?
	rlc ! rlc
	rnc			; Execution mode ?
inimfm2:
	INI			; INI if Read data, OUTI if Write data
	jnz rwmfm2		; repeat til done
rwmfm3:	in p$765$cmd		; next 256 bytes, Register B is Zero
	rlc			; Bit 7 in Carry
	jrnc rwmfm3		; Request for Master ?
	rlc ! rlc
	rnc			; Execution mode ?
inimfm3:
	INI			; INI if Read data, OUTI if Write data
	jnz rwmfm3		; repeat til done
rwmfm4:	in p$765$cmd		; next 256 bytes, Register B is Zero
	rlc			; Bit 7 in Carry
	jrnc rwmfm4		; Request for Master ?
	rlc ! rlc
	rnc			; Execution mode ?
inimfm4:
	INI			; INI if Read data, OUTI if Write data
	jnz rwmfm4		; repeat til done
	ret

	end
