;******************************************************************************
;*                                                                            *
;* Programm fuer AT Format                                                    *
;* ATDRV.SYS 0..3                                                             *
;* Programmname       : ATDRV.ASM                                             *
;* Ersteller          : Martin Ernst                                          *
;* Erstellungsdatum   : 27.05.86                                              *
;* letzte Aenderung am: 19.01.87                                              *
;* letzter Test am    : 19.01.87                                              *
;* Versionsnummer     : 1.0                                                   *
;* Revisionsnummer    : 5.0                                                   *
;*									      *
;******************************************************************************
	
		page	72,132
		title	PC DOS   AT Format Treiber

false		equ	0
true		equ	not false

		
STEP		EQU	0		;Steprate (0 bis 2)

NUMDRV		EQU	1


monitoreprom		segment at 0f000H
			org	0e000h
monitor			label 	far
monitoreprom		ends

;-----------------------------------------------------------------------------
 
CODE		SEGMENT para public
ASSUME		CS:CODE,DS:CODE,ES:CODE,SS:CODE

;=============================================================================

DSKDEV: DW	-1,-1
	DW	00100100000000000b
	DW	STRATEGY
	DW	DSKINT
DRVMAX	DB	1
	DB	7 DUP(?)

;-----------------------------------------------------------------------------

DSKTBL: DW	DSKINIT
	DW	MEDIAC
	DW	GETBPB
	DW	ioctlin		;CMDERR
	DW	DSKRED
	DW	BUSEXIT
	DW	EXIT
	DW	EXIT
	DW	DSKWRT
	DW	DSKWRV
	DW	EXIT
	DW	EXIT
	DW	ioctlout	;EXIT
	dw	exit	;open
	dw	exit	;close
	dw	exit	;removable media

;-----------------------------------------------------------------------------

IODAT	STRUC
CMDLEN	DB	?	;Laenge der Tabelle
UNIT	DB	?	;Einheiten Code
CMD	DB	?	;Befehles Code
STATUS	DW	?	;Status der Operation
	DB	8 DUP (?)
MEDIA	DB	?	;Media Descriptor Byte
TRANS	DD	?	;Transfer Adresse
COUNT	DW	?	;Anzahl der Bloecke oder Character die uebertragen 
			;werden sollen
START	DW	?	;erster Block, der trasnferiert werden soll
IODAT	ENDS


;-----------------------------------------------------------------------------

BPBS	STRUC
	DB	13 DUP (?)
BPB1	DB	?
BPB2	DW	?
	DW	?
BPB3	DW	?
	DW	?
BPBS	ENDS

;-----------------------------------------------------------------------------

DBP	STRUC
SECSIZE DW	?		;Sectorgroesse (512 Bytes)
ALLOC	DB	?		;Anzahl Sectoren pro Zuordnung
RESSEC	DW	?		;wieviele reservierte Sectoren
FATS	DB	?		;Anzahl der FATs
MAXDIR	DW	?		;Anzahl Directory Eintraege
SECTORS	DW	?		;Gesamtzahl der Sectoren
MEDIAID DB	?		;Media Descriptor Byte
FATSEC	DW	?		;Anzahl Sectoren pro FAT
SECTRK	DW	?		;Anzahl Sectoren pro Track (nicht Cylinder) 
koepfe	dw	?
DBP	ENDS


DDRIVE1 DBP	<512,1,1,2,14*16,15*2*80,0F9H,7,15,2>
DDRIVE2 DBP	<512,2,1,2,14*16,17*2*80,0FdH,7,17,2>
DDRIVE3 DBP	<1024,2,1,2,14*16,9*2*80,0FeH,7,9,2>

INITTAB1:	dw	ddrive1.secsize
ptrsav          dd      0
;============================================================================

stratp	proc	far
; Registerbelegung beim Aufruf:
; BX = Offset der Anforderungszeile
; ES = Segment der Anfoderungszeile

STRATEGY:
	MOV	WORD PTR CS:[PTRSAV],BX
	MOV	WORD PTR CS:[PTRSAV+2],ES
	RET
STRATP	ENDP

;=============================================================================

diskint proc	far
DSKINT: PUSH	SI
	MOV	SI,OFFSET DSKTBL


ENTRY:	PUSH	AX
	PUSH	CX
	PUSH	DX
	PUSH	DI
	PUSH	BP
	PUSH	DS
	PUSH	ES
	PUSH	BX		;erst alle Register retten
	LDS	BX,CS:[PTRSAV]	;in BX das Offset der Anforderungskopfzeile
	MOV	AL,[BX.UNIT]	;AL welche Einheit
	MOV	AH,[BX.MEDIA]	;Media Descriptor Byte
	MOV	CX,[BX.COUNT]	;Anzahl Bytes oder Bloecke
	MOV	DX,[BX.START]	;Startadresse des ersten Bytes oder Blocks
	XCHG	DI,AX		;AX sichern das nun die Adresse der Befehls -
	MOV	AL,[BX.CMD]	;routine berechnet wird. Dazu muss der
	XOR	AH,AH		;Befehlscode zweimal auf die Startadresse der
	ADD	SI,AX		;Tabelle - Wert in SI - addiert werden
	ADD	SI,AX
	CMP	AL,18		;Funktionsnummer groesser als 11 ?
	JA	CMDERR		;ja dann Fehler
	XCHG	AX,DI		;altes AX zurueck
	LES	DI,[BX.TRANS]	;DI enthaelt nun die Transferadresse Offset
				;ES das Segment der Transferadresse
	PUSH	CS		;Datensegment gleich dem Codesegment
	POP	DS
	JMP	WORD PTR [SI]	;und zur Befehlsroutine des betreffenden Treib.

;-----------------------------------------------------------------------------

BUSEXIT:MOV	AH,00000011B	;wenn die entsprechende Device busy ist 
	JMP	SHORT EXIT1	;dann hierher und weiter zum gemeinsamen Ausgang

;Fehlercodes :
;               0 = schreibgeschuetzt
;		1 = unbekannte Einheit
;		2 = Laufwerk nicht bereit
;		3 = unbekannter Befehl in der Anfoderungskopfzeile
;		4 = CRC Fehler
;		5 = falsche Laufwerk Anforderungszeilen Laenge
;		6 = Seek Fehler
;		7 = unbekanntes MEDIA DESCRIPTOR BYTE
;		8 = Sector nicht gefunden
;		9 = Drucker hat kein Papier mehr
;	       10 = Fehler beim Schreiben
;	       11 = Fehler beim Lesen
;              12 = allgemeiner Fehler

CMDERR: MOV	AL,3		;unbekannter Befehl springt hierher

ERREXIT:MOV	AH,10000001B	;Bits fuer Fertig und Fehler setzen
	STC
	JMP	SHORT EXIT1	;und weg

EXIT:	MOV	AH,00000001B
EXIT1:	LDS	BX,CS:[PTRSAV]
	MOV	[BX.STATUS],AX	;in der Anfoderungskopfzeile den Status melden
	POP	BX
	POP	ES
	POP	DS
	POP	BP
	POP	DI
	POP	DX
	POP	CX		;alle Register zurueck vom BIOSStack
	POP	AX
	POP	SI
	RET			;zurueck zum DOS
diskint	endp	

;=============================================================================
localer	proc 	near
dskinit:
	mov	es,dx
	mov	bx,cx
;	add	bx,10
	push	bx
	push	es
	mov	ax,0f000h
	mov	es,ax
	mov	bx,0fffeh
	mov	al,es:[bx]
	test	al,80h
	jnz	istibmpc
istct86:mov	al,22h
	call	romcall
	test	al,20h
	jnz	mitifckarte
	mov	ibmifcflg,1
	jmp	short weiterinit
istibmpc:
	mov	ibmifcflg,0
	jmp	short weiterinit
mitifckarte:
	mov	ibmifcflg,2
weiterinit:
	mov	si,offset startmes
	call	ausgabe
	pop	es
	pop	bx
	call	scan_for_para
	mov	al,es:[bx]
	or	al,al
	jnz	node
	jmp	default_init
node:	cmp	al,'0'
	jl	errordata
	cmp	al,'3'
	jg	errordata
	sub	al,'0'
store:	mov	drivesel,al
xxxx:   mov     si,offset crlf
	call	ausgabe
	mov	dx,cs
	lea	ax,endetreiber
	mov	cl,4
	sar	ax,cl
	add	dx,ax
	push	ds
	lds	bx,[ptrsav]
	mov	[bx.bpb2],0
	mov	[bx.bpb2+2],dx
	pop	ds
	mov	ax,1
	MOV	SI,OFFSET INITTAB1 ; und Standardanpassung zurueckgeben
	JMP	GET_BP5

errordata:
	mov	si,offset fehlertxt
	call	ausgabe
default_init:
	mov	drivesel,1
	mov	si,offset deftxt
	call	ausgabe
	jmp	short xxxx

scan_for_para:
	push	ds
	push	es
	pop	ds
	mov	si,bx
s1:	lodsb
	cmp	al,' '
	jz	s1
s2:	lodsb
	cmp	al,' '
	jnz	s2
s3:	lodsb
	cmp	al,' '
	jz	s3
	dec	si
	mov	bx,si
	pop	ds
	ret

;-----------------------------------------------------------------------------
ioctlin:	
	int	3
	jmp	cmderr
ioctlout:
	int	3
	jmp	exit


MEDIAC: mov	di,1		;1=not changed   0=dont' know   -1=changed 

	mov	al,ibmifcflg
	or	al,al
	jz	media1
	cmp	al,2
	jz	media1

	mov	al,24h
	call	romcall	
	or	al,al
	jnz	MEDIA1
	JMP	SHORT MEDIAEXIT


MEDIA1: xor	di,di		;sonst eine Null, da nicht bekannt

MEDIAEXIT:
	LDS	BX,[PTRSAV]	;in der Anfoderungszeile den Status sichern
	MOV	WORD PTR [BX].TRANS,DI
	mov	al,0
	JMP	EXIT

;-----------------------------------------------------------------------------

GETBPB: 
	MOV	AH,AL	;bei PC DOS wird es schon ueber Pointer in DI geliefert
	MOV	AL,ES:[DI] ;MEDIA Byte holen Unit Code jetzt in AH

	push	ax
	push	bx
	xchg	ah,al

	call	getpara

	mov	si,bx
	pop	bx
	pop	ax


GET_BP5:LDS	BX,[PTRSAV]		;und beide Werte in der 
	MOV	[BX.BPB1],AL		;Anfoderungszeile sichern
	MOV	[BX.BPB3],SI
	MOV	[BX.BPB3+2],CS
	xor	al,al			;kein Fehler
	JMP	EXIT			;weg


DSKRED:
ASSUME	DS:CODE
	CALL	SETUP		;Track und Sector berechnen aus BPB
	JC	DISK$IO		;bei Fehler ->
	CALL	DISKRD		;versuchen zu lesen
	JMP	SHORT DISK$IO

DSKWRV:
DSKWRT:
	CALL	SETUP		;wie beim Lesen nur eben Schreiben
	JC	DISK$IO
	CALL	DISKWRT
DISK$IO:JNC	DSKOK		;gab es beim Lesen oder Schreiben einen Fehler
	CALL	DERROR		;wenn ja dann DERROR aufrufen (wandelt 
	JMP	ERREXIT		;Floppy Code in IBM Treiber Code)
DSKOK:	JMP	EXIT		;sonst mit o.k. weg

;-----------------------------------------------------------------------------

SETUP:	mov	si,0

	call	getpara

	XCHG	BX,DI		;nach DI mit dem Pointer
	MOV	AL,BYTE PTR [DI].koepfe
	MOV	AH,BYTE PTR [DI].SECTRK
	IMUL	AH		;Anzahl der Sectoren pro Track
	MOV	[MAXSEC],AL	;fuer spaeter (Ueberlauf ins naechste Track)
	push	si
	MOV	SI,CX		;CX = Anzahl zu lesende Sectoren
	ADD	SI,DX		;DX = Startsector
	CMP	SI,WORD PTR [DI].SECTORS ; groesser als die gesamte Diskette
	JBE	INRANGE		;Sectoren hat ? nein ->
	pop	si
	MOV	AL,8		;Fehler 8 = Sector nicht gefunden
	STC
	RET



INRANGE:MOV	[SECCNT],CX	;Anzahl zu lesende Sectoren sichern
	pop	si
	add	dx,si
	MOV	SI,BX		;BX ist Offset Transferadresse
	AND	BX,0FH		;das Offset soll so klein wie moeglich sein,
	MOV	[DMAADR],BX	;darum wird esntsprechend das Segment in ES
	MOV	AX,DX		;erhoeht
	XOR	DX,DX
	MOV	BL,[MAXSEC]	;der logische sector wird durch die Maximal -
	xor	bh,bh		;zahl Sectoren pro Track geteilt . Ergibt den
	DIV	BX		;relativen Sector innerhalb des Tracks
	INC	DL		;dient zum addieren von 1 (modulo funktion !!)
	MOV	[CURSEC],DL
	MOV	[CURTRK],AX	;und als Quotient den TrackWert
	MOV	CL,4
	CLC
	SHR	SI,CL		;hier jetzt die Segment Vergroesserung
	MOV	CX,ES
	ADD	CX,SI

	mov	[dmasegment],cx	;Segmentadresse sichern fuer spaeter

driveeq:mov	cx,[CURTRK]	;sicher ist sicher
	CLC			;kein Fehler
DIRF:	RET


getpara:
	mov	bx,offset ddrive1
	CMP	AH,0F9H
	JZ	SFOU
	MOV	BX,OFFSET ddrive2
	cmp	ah,0fdh
	jz	sfou
	mov	bx,offset ddrive3
	cmp	ah,0feh
	jz	sfou
	mov	bx,offset ddrive1
SFOU:	ret

;-----------------------------------------------------------------------------

DISKRD:
RDLP:	CALL	PRESET		;Werte fuer Track, Sector, Kopf, Anzahl Sec
	cmp	ibmifcflg,2
	jnz	ueberintles
	mov	al,16h
	call	uebercallf
	jmp	short rdlp2
ueberintles:
	mov	ah,2		;eintragen und dann INT 13H aufrufen
	int	13h
rdlp2:	JC	ERRORR		;in AH steht Errorcode

	DEC	[SECCNT]	;naechsten Sector
	JNZ	RDLP
	CLC			;kein Fehler
	RET
ERRORR: STC			;bei Fehler hierher
	RET

DISKWRT:
WRLOOP: CALL	PRESET		;wie RDLP nur eben Schreiben
	cmp	ibmifcflg,2
	jnz	ueberintsch
	mov	al,18h
	call	uebercallf
	jmp	short wrlp2	
ueberintsch:
	mov	ah,3
	int	13h

wrlp2:	JC	ERRORR
	
	DEC	[SECCNT]
	JNZ	WRLOOP
	CLC
	RET

uebercallf:
	push	ax
	push	cx
	push	dx
	push	bx
	mov	cx,es
	mov	al,14h	;setze Segment
	call	romcall
	pop	cx
	mov	al,0ch	;setze Offset
	call	romcall
	pop	cx
	push	cx
	mov	al,1
	shl	al,cl
	mov	ah,[secpertrak]
	mov	cx,ax

;fuer AT Format ist das 8 Zoll Double Density	
	or	cl,10h	;setzt 8 Zoll
	
	mov	al,10h	;setze Laufwerk
	call	romcall
	pop	dx
	mov	cl,dh
	mov	ch,0
	mov	al,[secpertrak]
	mov	ah,0
	mul	cl
	pop	cx
	push	cx
	mov	ch,0
	add	cx,ax
	mov	al,12h	;setze Sector
	call	romcall
	pop	cx
	mov	cl,ch
	mov	ch,0
	mov	al,0eh	;setze Spur
	call	romcall
	pop	ax	;lesen oder schreiben
	call	romcall
	mov	ah,al
	or	al,al
	jz	noerr
	stc
	ret
noerr:	clc
	ret



PRESET: MOV	BX,[DMAADR]	;fuer den INT 13H muessen die Register belegt

	mov	ES,[dmasegment] ;Segment laden
	mov	al,[cursec]		
	cmp	al,[maxsec]
	jbe	gotsec1		;zu lesender Sector schon im naechsten Track ?
	inc	[curtrk]	;ja , dann eben Track erhoehen
	mov	al,1		;relativer Sector wird eins
	mov	[cursec],al
	mov	cl,al
	xor	dh,dh		;Kopf ist dann logischerweise 0
	jmp	short wegvon
gotsec1:push	bx		;kein Ueberlauf
	mov	bx,word ptr [di].sectrk ; dann den Kopf berechnen, indem man
	mov	[secpertrak],bl
	xor	dh,dh		;solange SECTRK abzieht, bis es richtig ist
loopinc:cmp	al,bl
	jbe	nohead
	inc	dh		;Kopf immer um eins erhoehen (damit koennen
	sub	al,bl		;wir auch Harddisks bedienen)
	jmp	short loopinc
nohead:	mov	cl,al		;richtiger Sector und richtiger Kopf
	pop	bx		;DMA Adresse wieder vom Stack

wegvon:	mov	ax,word ptr [di].secsize
	add	[dmaadr],ax
	inc	[cursec]
	mov	ax,[curtrk]
	mov	ch,al
	and	ah,03h
	ror	ah,1
	ror	ah,1
	or	cl,ah	;oberes Byte der Tracknummer
	mov	al,1
	mov	dl,[drivesel]
	ret
 
DERROR: LDS	BX,CS:[PTRSAV]
	MOV	[BX.COUNT],0
	PUSH	CS
	POP	DS
	TEST	AL,80H
 	JZ	DE1
 	MOV	AL,2
 	JMP	SHORT DEE
DE1:	TEST	AL,40H
 	JZ	DE2
 	MOV	AL,6
 	JMP	SHORT DEE
DE2:	TEST	AL,3
 	JZ	DE3
 	xor	al,al
 	JMP	SHORT DEE
DE3:	TEST	AL,10H
 	JZ	DE4
 	MOV	AL,4
 	JMP	SHORT DEE
DE4:	test	al,2
	jz	de5
	mov	al,8
	jmp	short dee
de5:	MOV	AL,12
DEE:	RET

drivesel	db	0
CURSEC          DB      0
CURTRK		DW	0
DMAADR		DW	0
dmasegment	dw	0
MAXSEC		DB	0
SECCNT		DW	0
DRVNUM		DB	1+STEP*40H
		DB	2+STEP*40H
		DB	4+STEP*40H
secpertrak	db	0
ibmifcflg	db	0
crlf		db	0dh,0ah,0
deftxt		db	"Default Werte :",0dh,0ah
		db	"80 Track Drive 1",0dh,0ah,0
fehlertxt	db	"Fehler in Parameterfeld",0dh,0ah,0


startmes	db	0dh,0ah,"AT Disk Treiber (ct86)    V 1.5 (19.01.87)"
		db	0dh,0ah,0


ROMCALL:PUSH	BX
	PUSH	DI
	CALL	monitor
	POP	DI
	POP	BX
	RET

ausgabe:mov	al,[si]
	or	al,al
	jz	ausgend
	mov	ah,0eh
	push	si
	int	10h
	pop	si
	inc	si
	jmp	short ausgabe
ausgend:ret
endetreiber	db	?	

localer	endp
CODE	ENDS

	if1
	%out	End of Pass 1
	endif

	if2
	%out	End of Pass 2
	endif

	END
