;******************************************************************************
;*                                                                            *
;* SECDRIVE - Treiber fr einen 2. HD-Kontroller im PC                        *
;*                                                                            *
;* Historie:  12. 8.1993  Grundsteinlegung, Definitionen                      *
;*            16. 8.1993  Dispatcher                                          *
;*                        Basislesefunktionen                                 *
;*            17. 8.1993  Partitionsinformationen zusammenkratzen             *
;*            18. 8.1993  Laufwerksparameter initialisieren                   *
;*            19. 8.1993  Zylinder/Sektorregister setzen                      *
;*                        Partitiossektorbaum durchgehen                      *
;*            24. 8.1993  BPB aufbauen                                        *
;*            25. 8.1993  Parameterbersetzung				      *
;*                        Einlesen                                            *
;*                        Sektoren schreiben                                  *
;*            26. 8.1993  Fehlerbehandlung                                    *
;*                        Verify                                              *
;*                        1. Restore-Versuch mit Seek                         *
;*             7. 9.1993  Versuch Version 1.39 mit Proc's                     *
;*            28. 9.1993  etwas gekrzt                                       *
;*            27.12.1994  leichte Korrekturen im Restore                      *
;*            28.12.1994  Trennung Low-Level-Routinen begonnen                *
;*            19. 1.1995  Fehlermeldungen im Klartext                         *
;*                                                                            *
;******************************************************************************

;******************************************************************************
;* globale Definitionen                                                       *
;******************************************************************************

; A C H T U N G : Mono.SYS muá fr Debugging geladen sein !!!!

debug           equ     0
debug2          equ     0

                include bitfuncs.inc

                cpu     80186           ; WD 1003 fordert min. 80286

Diag_NoError    equ     01h             ; Selbstdiagnosecodes: kein Fehler
Diag_ContError  equ     02h             ; Controller-Fehler
Diag_SBufError  equ     03h             ; Sektorpuffer defekt
Diag_ECCError   equ     04h             ; Fehlerkorrektor defekt
Diag_ProcError  equ     05h             ; Steuerprozessor defekt
Diag_Timeout    equ     06h             ; Controller antwortet nicht

ParTab		struct
BFlag		 db	?		; Partitionseintrag: Partition aktiv ?
FHead		 db	?		; Startkopf
FSecCyl		 dw	?		; Startzylinder/sektor
Type		 db	?		; Partitionstyp
LHead		 db	?		; Endkopf
LSecCyl  	 dw     ?		; Endzylinder/sektor
LinSec   	 dd     ?		; Anzahl Sektoren
NSecs    	 dd     ?		; linearer Startsektor
		endstruct

DErr_WrProtect  equ     00h             ; Treiberfehlercodes: Schreibschutz
DErr_InvUnit    equ     01h             ; unbekannte Ger„tenummer
DErr_NotReady   equ     02h             ; Laufwerk nicht bereit
DErr_Unknown    equ     03h             ; Unbekannes Treiberkommando
DErr_CRCError   equ     04h             ; Prfsummenfehler
DErr_InvBlock   equ     05h             ; ungltiger Request-Header
DErr_TrkNotFnd  equ     06h             ; Spur nicht gefunden
DErr_InvMedia   equ     07h             ; Unbekanntes Tr„gerformat
DErr_SecNotFnd  equ     08h             ; Sektor nicht gefunden
DErr_PaperEnd   equ     09h             ; Papierende im Drucker
DErr_WrError    equ     0ah             ; allg. Schreibfehler
DErr_RdError    equ     0bh             ; allg. Schreibfehler
DErr_GenFail    equ     0ch             ; allg. Fehler
DErr_InvChange  equ     0fh             ; unerlaubter Diskettenwechsel

DErr_UserTerm   equ     0ffh            ; Fehlercode Abbruch durch Benutzer

SecSize         equ     512             ; Sektorgr”áe in Bytes
MaxPDrives      equ     2               ; Maximalzahl physikalischer Laufwerke
MaxDrives       equ     10              ; Maximalzahl verwaltbarer Laufwerke
MaxParts	equ	4		; Maximalzahl Partitionen in einem Sektor
MaxRetry        equ     2               ; max. 2 Lese/Schreibversuche
StackSize       equ     512             ; etwas mehr wg. Rekursion

DrPar_Offset    equ     1aeh            ; Offset Parametertabelle im Part.-Sektor
ParTab_Offset	equ	1beh		; Offset Partitionstabelle "   "     "
ParSecID_Offset	equ	1feh		; Offset Partitionssektorflag (55aa)
BPBOfs          equ     11              ; Offset BPB im Bootsektor

INT_DOS         equ     21h             ; DOS-Funktionsaufruf
INT_Mono        equ     60h             ; Haken zum VT100-Treiber
INT_Clock       equ     1ah             ; BIOS-Uhreneinstieg
INT_Keyboard    equ     16h             ; BIOS-Tastatureinstieg

DOS_WrString    equ     9               ; DOS-Funktionen
DOS_WrChar      equ     6
DOS_RdString    equ     10
DOS_RdChar      equ     8

HD_ID           equ     0f8h            ; Media-ID fr Festplatten

CR              equ     13
LF              equ     10
BEL             equ     7
ESC             equ     27

;******************************************************************************
; Makros                                                                      *
;******************************************************************************

;jmp             macro adr
;                !jmp long adr
;                endm

beep            macro
                push    ax
                mov     ax,0e07h
                int     10h
                pop     ax
                endm

PrMsg           macro   Adr             ; Meldung ausgeben
                push    dx              ; Register retten
                lea     dx,[Adr]
                mov     ah,DOS_WrString
                int     INT_DOS
                pop     dx              ; Register zurck
                endm

PrChar          macro   Zeichen         ; Zeichen ausgeben
                push    dx              ; Register retten
                push    ax
                mov     dl,Zeichen
                mov     ah,DOS_WrChar
                int     INT_DOS
                pop     ax
                pop     dx              ; Register zurck
                endm

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

btst            macro   op,bit          ; ein einzelnes Bit testen
		test	op,(1<<bit)
		endm

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

ljnz            macro   adr             ; lange Sprnge
                jz      Next
                jmp     adr
Next:
                endm

ljne            macro   adr
                je      Next
                jmp     adr
Next:
                endm

ljc             macro   adr
                jnc     Next
                jmp     adr
Next:
                endm

lje             macro   adr
                jne     Next
                jmp     adr
Next:
                endm

ljz             macro   adr
                jnz     Next
                jmp     adr
Next:
                endm

ljb             macro   adr
                jnb     Next
                jmp     adr
Next:
                endm

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

proc            macro   name
                section name
                public  name:parent
name            label   $
                endm

globproc        macro   name
                section name
                public  name
name            label   $
                endm

endp            macro
                endsection
                endm

;------------------------------------------------------------------------------
; Fehlermakro

JmpOnError      macro   Drv,Adr
                jnc     GoOn            ; kein Fehler, weitermachen
                mov     ah,Drv
                call    WrErrorCode     ; Fehler-->ausgeben...
                jmp     Adr             ; ...Abbruch
GoOn:
                endm

;******************************************************************************
;* Treiberkopf                                                                *
;******************************************************************************

 
              assume  cs:code,ds:nothing,ss:nothing,es:nothing

                org     0

DriverHead:     dd      -1              ; Zeiger auf Nachfolger
                dw      0000100000000010b ; Attribut
;                       ^   ^         ^
;                       ³   ³         ÀÄ kann 32-Bit-Setornummern verarbeiten
;                       ³   ÀÄÄÄÄÄÄÄÄÄÄÄ kennt Close/Open/Removeable
;                       ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Blocktreiber
                dw      StrategyProc    ; Zeiger auf Strategieroutine
                dw      InterruptProc   ; Zeiger auf eigentlichen Treibercode
NrOfVols:       db      0               ; Zahl log. Laufwerke
                db      "SECDRIV"       ; bei Blocktreibern unbenutzt

;******************************************************************************
;* residente Daten                                                            *
;******************************************************************************

Rh_Ptr          dd      ?               ; Speicher fr Request-Header

JmpTable        dw      Init            ; Sprungtabelle: Initialisierung
                dw      MediaCheck      ; Medium gewechselt ?
                dw      BuildBPB        ; Parameterblock laden
                dw      IOCTLRead       ; Steuerdaten vom Treiber
                dw      Read            ; Daten lesen
                dw      ND_Read         ; Lesen, ohne Pufferstatus zu „ndern
                dw      InputStatus     ; Daten im Eingabepuffer ?
                dw      InputFlush      ; Eingabepuffer l”schen
                dw      Write           ; Daten schreiben
                dw      Write_Verify    ; Daten mit Prflesen schreiben
                dw      OutputStat      ; Ausgabepuffer leer ?
                dw      OutputFlush     ; Ausgabepuffer l”schen
                dw      IOCTLWrite      ; Steuerdaten zum Treiber
                dw      DeviceOpen      ; DOS hat eine Datei darauf ge”ffnet
                dw      DeviceClose     ; DOS hat eine Datei darauf geschlossen
                dw      Removeable      ; Ist Datentr„ger wechselbar ?
                dw      OutputTillBusy  ; Ausgabe, bis Puffer voll
                dw      GenIOCTL        ; genormtes IOCTL
                dw      GetLogical      ; Laufwerkszuordnung lesen
                dw      SetLogical      ; Laufwerkszuordnung setzen
                dw      IOCTLQuery      ; Abfrage, ob GenIOCTL untersttzt

SectorBuffer:   db      SecSize dup (?) ; Sektorpuffer fr Treiber selber

                db      StackSize dup (?) ; Treiberstack
DriverStack:

BPBSize         equ     36
DrTab		struct			; Laufwerkstabelle:
StartHead	 db     ?		;  Startkopf
StartCyl	 dw	?		;  Startzylinder
StartSec	 db	?		;  Startsektor
LinStart	 dd	?		;  lin. Startsektor
SecCnt		 dd	?		;  Gesamtsektorzahl
Drive		 db	?		;  Laufwerk
BPB		 db	BPBSize dup (?)	;  BPB
		endstruct

DrTab           db      DrTab_Len*MaxDrives dup (?)
DrTab_BPBs      dd      2*MaxDrives dup (?)
DrCnt           db      0               ; Anzahl gefundener Laufwerke
DrOfs           db      0               ; erster freier Laufwerksbuchstabe

DrPar		struct			; Plattenparametersatz: 
Cyls		 dw	?		;  Zylinderzahl
Heads		 db	?		;  Kopfzahl
RedWr		 dw	?		;  Startzylinder reduzierter Schreibstrom
PrComp		 dw	?		;  Startzylinder Pr„kompensation
ECCLen		 db	?		;  max. korrigierbarer Fehlerburst (Bits)
CByte		 db	?		;  Wert frs Plattensteuerregister
TOut		 db	?		;  genereller Timeout
FTOut		 db	?		;  Timeout Formatierung
CTOut		 db	?		;  Timeout fr Prfung
LZone		 dw	?		;  Landezylinder
NSecs		 db	?		;  Sektorzahl
Dummy		 db	?		;  unbenutzt
		endstruct

DrPars          db      DrPar_Len*MaxPDrives dup (0)

;******************************************************************************
;* Strategieroutine                                                           *
;******************************************************************************

StrategyProc:   mov     word ptr [Rh_Ptr],bx ; Zeiger speichern
                mov     word ptr [Rh_Ptr+2],es
                retf

;******************************************************************************
;* Treiberdispatcher                                                          *
;******************************************************************************

Rh		struct
Size		 db	?		; gemeinsame Headerteile: L„nge Block
Unit		 db	?		; angesprochenes Laufwerk
Func		 db	?		; Treibersubfunktion
Status		 dw	?		; Ergebnis
Resvd		 db	8 dup (?)	; unbenutzt
		endstruct

InterruptProc:  pusha                   ; alle Register retten

                cli                     ; Stack umschalten
                mov     si,ss           ; alten zwischenspeichern
                mov     di,sp
                mov     ax,cs           ; neuen setzen
                mov     ss,ax
                lea     sp,[DriverStack]
                push    di              ; alten auf neuem (!) speichern
                push    si
                sti

                mov     ax,cs           ; DS auf Treibersegment
                mov     ds,ax
                assume  ds:code

                les     bx,[Rh_Ptr]     ; Zeiger laden
                mov     word ptr es:[bx+Rh_Status],0 ; Status l”schen

                mov     al,es:[bx+Rh_Func] ; Subfunktion ausrechnen
                if      debug
                 call   PrByte
                endif
                mov     ah,0
                add     ax,ax
                mov     si,ax
                jmp     JmpTable[si]

                jmp     Init

;******************************************************************************
;* gemeinsames Ende                                                           *
;******************************************************************************

StateError:     btst    al,1            ; Bit 1: Spur 0 nicht gefunden
                jz      StateError_N1
                mov     al,DErr_TrkNotFnd
                jmp     StateError_End
StateError_N1:  btst    al,2            ; Bit 2: abgebrochenes Kommando
                jz      StateError_N2
                btst    ah,5            ; Bit S5: Schreibfehler
                jz      StateError_N21
                mov     al,DErr_WrError
                jmp     StateError_End
StateError_N21: btst    ah,4            ; Bit S4: Positionierung nicht vollst„ndig
                jnz     StateError_N22
                mov     al,DErr_TrkNotFnd
                jmp     StateError_End
StateError_N22: btst    ah,6            ; Bit S6: Laufwerk nicht bereit
                jnz     StateError_N23
                mov     al,DErr_NotReady
                jmp     StateError_End
StateError_N23: mov     al,DErr_GenFail ; Notnagel 1
                jmp     StateError_End
StateError_N2:  test    al,11h          ; Bit 0/4: Sektor nicht gefunden
                jz      StateError_N3
                mov     al,DErr_SecNotFnd
                jmp     StateError_End
StateError_N3:  btst    al,6            ; Bit 6: Prfsummenfehler
                jz      StateError_N4
                mov     al,DErr_CRCError
                jmp     StateError_End
StateError_N4:  mov     al,DErr_GenFail ; Notnagel 2
StateError_End: les     bx,[Rh_Ptr]     ; Code einspeichern
                mov     es:[bx+Rh_Status],al
                jmp     Error

Unknown:        les     bx,[Rh_Ptr]
                mov     byte ptr es:[bx+Rh_Status],DErr_Unknown ; unbek. Funktion

Error:          or      byte ptr es:[bx+Rh_Status+1],80h ; Fehler-Flag setzen
                jmp     Done

Busy:           les     bx,[Rh_Ptr]
                or      byte ptr es:[bx+Rh_Status+1],2  ; Busy-Flag setzen

Done:           les     bx,[Rh_Ptr]
                or      byte ptr es:[bx+Rh_Status+1],1  ; Done-Flag setzen

                if      debug
                 call   NxtLine
                endif

                cli                     ; Stack zurckschalten
                pop     si
                pop     di              ; alten in SI:DI laden
                mov     sp,di           ; einschreiben
                mov     ss,si
                sti

                popa                    ; Register zurck
                retf

;******************************************************************************
;* Debugginghilfe                                                             *
;******************************************************************************

                if      debug||debug2

HexTab          db      "0123456789ABCDEF"

PrByte:         push    es              ; Register retten
                push    di
                push    bx
                push    ax

                lea     bx,[HexTab]


                db      0d4h,10h        ; AAM 16
                push    ax
                mov     al,ah
                xlat
                mov     ah,0eh
                int     10h
                pop     ax
                xlat
                mov     ah,0eh
                int     10h

                pop     ax              ; Register zurck
                pop     bx
                pop     di
                pop     es
                ret

PrWord:         xchg    ah,al           ; Hi-Byte
                call    PrByte
                xchg    ah,al
                call    PrByte
                ret

PrChar:         push    ax
                mov     ah,0eh
                int     10h
                pop     ax
                ret

NxtLine:        push    ax              ; Register retten
                push    bx
                push    dx

                mov     ax,0e0dh
                int     10h
                mov     ax,0e0ah
                int     10h

                pop     dx              ; Register zurck
                pop     bx
                pop     ax

                endif

;******************************************************************************
;* residente Subfunktionen                                                    *
;******************************************************************************

;******************************************************************************
;* eine logische Laufwerksnummer berprfen                                   *
;*              In  :   AL = Laufwerk                                         *
;*              Out :   C  = 1, falls Fehler                                  *
;******************************************************************************

                proc    ChkDrive

                cmp     al,[DrCnt]      ; C=1, falls < (d.h. OK)
                cmc                     ; Deshalb noch einmal drehen
                jnc     OK              ; C=0, alles in Butter
                les     bx,[Rh_Ptr]     ; ansonsten Fehlerstatus setzen
                mov     byte ptr es:[bx+Rh_Status],DErr_InvUnit
OK:             ret

                endp

;******************************************************************************
;* Adresse der phys. Laufwerkstabelle errechnen                               *
;*              In  :   AL = Laufwerk                                         *
;*              Out :   DI = Adresse                                          *
;******************************************************************************

                proc    GetPTabAdr

                mov     ah,DrPar_Len	; relative Adresse berechnen
                mul     ah
                lea     di,[DrPars]     ; Offset dazu
                add     di,ax
                ret

                endp

;******************************************************************************
;* Adresse eines Partitionsdeskriptors errechnen                              *
;*              In  :   AL = log. Laufwerk                                    *
;*              Out :   DI = Adresse                                          *
;******************************************************************************

                proc    GetTabAdr

                mov     ah,DrTab_Len    ; relative Adresse berechnen
                mul     ah
                lea     di,[DrTab]      ; Offset dazu
                add     di,ax
                ret

                endp

;******************************************************************************
;* logische Parameter in physikalische bersetzen                             *
;*              In  :   BL = log. Laufwerk                                    *
;*                      DX:AX = relative Sektornummer			      *
;*              Out :   AL = phys. Laufwerk                                   *
;*                      AH = Kopf                                             *
;*                      BX = Zylinder                                         *
;*                      CH = Sektor                                           * 
;******************************************************************************

                proc    TranslateParams

                push    di              ; Register retten

		xchg	bx,ax		; Adresse Parametertabelle holen
		call	GetTabAdr	

		add	bx,[di+DrTab_LinStart]   ; in absolute Sektornummer
		adc	dx,[di+DrTab_LinStart+2] ; umrechnen
		mov	al,[di+DrTab_Drive]      ; phys. Laufwerksnummer holen
		push	ax		; bis zum Ende retten
		
		call	GetPTabAdr	; von dieser phys. Platte die Tabelle holen
		mov	ax,bx		; Sektor# wieder nach DX:AX
		mov	bl,[di+DrPar_NSecs] ; Sektorzahl auf 16 Bit
		mov	bh,0		; aufblasen
		div	bx
		mov	ch,dl		; Modulo-Rest ist Sektornummer auf
		inc	ch		; Spur: Vorsicht, Numerierung ab 1 !!!
		sub	dx,dx		; wieder auf 32 Bit erweitern
		mov	bl,[di+DrPar_Heads] ; Kopfnummer herausfummeln
		div	bx
		mov	bx,ax		; Quotient ist Zylinder
		pop	ax		; Laufwerk zurck
		mov	ah,dl		; Rest ist Kopf

		pop	di		; Register zurck
		ret

                endp

;******************************************************************************
;* Einbindung Low-Level-Routinen                                              *
;******************************************************************************

; definiert werden mssen:

; LowLevelIdent:  Meldung ber untersttzte Hardware ausgeben
; ContDiag:       Kontroller-Selbsttest durchfhren
;                 Ergebniskode in AL
; Recalibrate:    Laufwerk [AL] auf Zylinder 0 fahren
;                 Fehlerflag in C, Fehlerkode in AX
; SetDriveParams: dem Kontroller die Geometrie fr Laufwerk [AL] einbleuen
;                 Fehlerflag in C
; ReadSectors:    von Laufwerk [AL] ab Zylinder [BX], Kopf [AH], Sektor [CH]
;                 [CL] Sektoren in Puffer ab ES:DI lesen
;                 Fehlerflag in C, Fehlerkode in AX
; WriteSectors:   auf Laufwerk [AL] ab Zylinder [BX], Kopf [AH], Sektor [CH]
;                 [CL] Sektoren von Puffer ab ES:SI schreiben
;                 Fehlerflag in C, Fehlerkode in AX
; VeriSectors:    auf Laufwerk [AL] ab Zylinder [BX], Kopf [AH], Sektor [CH]
;                 [CL] Sektoren verifizieren
;                 Fehlerflag in C, Fehlerkode in AX
; FormatUnit:     Laufwerk [AL] mit Interleave [AH] formatieren, Fehlerkode
;                 in AX
; FormatTrack:    Zylinder [BX], Kopf [AH] auf Laufwerk [AL] mit Interleave
;                 [CL] formatieren, Fehlerkode in AX
; MarkBad:        Zylinder [BX], Kopf [AH] auf Laufwerk [AL] als defekt
;                 markieren, Fehlerkode in AX

                include "lowlevel.inc"

;******************************************************************************
;* Bootsektor eines log. Laufwerkes lesen                                     *
;*              In  :   AL = Laufwerksnummer                                  *
;*              Out :   C+AX = Fehlerstatus                                   *
;******************************************************************************

                proc    ReadBootSec

                push    es              ; Register retten
                push    bx
                push    cx
                push    di

                call    GetTabAdr       ; Eintrag in Laufwerkstabelle ermitteln
                mov     al,[di+DrTab_Drive] ; davon ersten Sektor lesen
                mov     ah,[di+DrTab_StartHead]
                mov     bx,[di+DrTab_StartCyl]
                mov     cl,1
                mov     ch,[di+DrTab_StartSec]
                mov     di,cs
                mov     es,di
                lea     di,[SectorBuffer]
                call    ReadSectors

                pop     di              ; Register zurck
                pop     cx
                pop     bx
                pop     es
                ret

                endp

;******************************************************************************
;* Funktion 1: Test, ob Medium gewechselt                                     *
;******************************************************************************

                proc    MediaCheck

Rh_MediaID      equ     Rh_Resvd+8      ; erwartetes Media-ID
Rh_Return       equ     Rh_MediaID+1    ; Ergebnis-Flag
Rh_VolName      equ     Rh_Return+1     ; Adresse alter Laufwerksname

                cmp     byte ptr es:[bx+Rh_MediaID],HD_ID ; gltige ID ?
                je      OK
                mov     byte ptr es:[bx+Rh_Status],DErr_InvMedia ; nein...
                jmp     Error
OK:             mov     byte ptr es:[bx+Rh_Return],1 ; nie gewechselt
                jmp     Done

                endp

;******************************************************************************
;* Funktion 2: BPB aufbauen                                                   *
;******************************************************************************

                proc    BuildBPB

Rh2		struct
		 db	Rh_Len dup (?)
MediaID          db	?		; erwartetes Media-ID
FATSector	 dd	?		; Pufferadresse 1. FAT-Sektor
BPBAddress	 dd	?		; Adresse neuer BPB
		endstruct

                mov     al,es:[bx+Rh_Unit]
		call    ChkDrive        ; Laufwerksnummer gltig ?
                ljc     Error           ; nein-->Fehler & Ende

                call    ReadBootSec     ; Bootsektor lesen
                ljc     StateError      ; bei Fehlern Ende

                les     bx,[Rh_Ptr]        ; Zeiger neu laden
		mov	al,es:[bx+Rh_Unit] ; Tabellenadresse aufbauen
		call	GetTabAdr
		lea	di,[di+DrTab_BPB]  ; DI auf BPB-Speicher
                mov     es:[bx+Rh2_BPBAddress],di ; BPB-Zeiger abspeichern
                mov     es:[bx+Rh2_BPBAddress+2],cs
		
                mov     si,cs              ; BPB umkopieren
                mov     es,si
		lea	si,[SectorBuffer+BPBOfs]
		cld
		mov	cx,BPBSize
		rep	movsb

                jmp     Done

                endp

;******************************************************************************

IOCTLRead:      jmp     Unknown

;******************************************************************************
;* Funktion 4: Sektoren lesen                                                 *
;******************************************************************************

Rh4		struct
		 db	Rh_len dup (?)
MediaID		 db	?		; Media-ID Laufwerk
BufOfs		 dw	?		; Adresse Datenpuffer
BufSeg		 dw	?
NSecs		 dw	?		; Anzahl zu lesender Bl”cke
FirstSec	 dw	?		; Startsektor bzw. $FFFF fr 32-Bit-Nummern
VolID		 dd	?		; Adresse Laufwerksname
LFirstSec	 dw	?		; lange Startsektornummer
HFirstSec	 dw	?
		endstruct

Read:           mov	al,es:[bx+Rh_Unit] ; Laufwerksnummer prfen
		call	ChkDrive
		ljc	Error

                mov     ch,al                   ; Laufwerksnummer retten
                mov     ax,es:[bx+Rh4_FirstSec] ; Sektor holen (BIGDOS beachten)
                sub     dx,dx
                cmp     ax,-1
                jne     Read_SmallSec
                mov     ax,es:[bx+Rh4_LFirstSec]
                mov     dx,es:[bx+Rh4_HFirstSec]
Read_SmallSec:  mov     cl,es:[bx+Rh4_NSecs]    ; Sektorzahl laden (muá <=128 sein)
                les     di,es:[bx+Rh4_BufOfs]   ; Zieladresse laden
                mov     bl,ch                   ; Laufwerksnummer nach BL

                if      debug
                 push   ax
                 push   cx
                 mov    cx,ax
                 mov    al,' '
                 call   PrChar
                 mov    al,bl           ; Laufwerksnummer
                 call   PrByte
                 mov    al,' '
                 call   PrChar
                 mov    ax,dx           ; Startsektor
                 call   PrWord
                 mov    ax,cx
                 call   PrWord
                 mov    al,' '
                 call   PrChar
                 pop    cx
                 mov    al,cl           ; Sektorzahl
                 call   PrByte
                 mov    al,' '
                 call   PrChar
                 mov    ax,es           ; Startadresse
                 call   PrWord
                 mov    al,':'
                 call   PrChar
                 mov    ax,di
                 call   PrWord
                 pop    ax
                endif

                call    TranslateParams         ; umrechnen lassen
                call    ReadSectors             ; der eigentliche Lesevorgang

                ljc     StateError              ; bei Fehlern...
                jmp     Done                    ; ansonsten o.k.

;******************************************************************************

ND_Read:        jmp     Unknown

InputStatus:    jmp     Unknown

InputFlush:     jmp     Unknown

;******************************************************************************
;* Funktion 8: Sektoren schreiben                                             *
;******************************************************************************

Rh8		struct
		 db	Rh_len dup (?)
MediaID		 db	?		; Media-ID Laufwerk
BufOfs		 dw	?		; Adresse Datenpuffer
BufSeg		 dw	?
NSecs		 dw	?		; Anzahl zu lesender Bl”cke
FirstSec	 dw	?		; Startsektor bzw. $FFFF fr 32-Bit-Nummern
VolID		 dd	?		; Adresse Laufwerksname
LFirstSec	 dw	?		; lange Startsektornummer
HFirstSec	 dw	?
		endstruct

DoWrite:        if      debug2
                 mov    al,es:[bx+Rh_Unit]
                 call   PrByte
                 mov    al,' '
                 call   PrChar
                 mov    ax,es:[bx+Rh8_FirstSec]
                 call   PrWord
                 mov    al,' '
                 mov    ax,es:[bx+Rh8_HFirstSec]
                 call   PrWord
                 mov    ax,es:[bx+Rh8_LFirstSec]
                 call   PrWord
                 call   NxtLine
                endif

                mov     al,es:[bx+Rh_Unit]
                mov     ch,al                   ; Laufwerksnummer retten
                mov     ax,es:[bx+Rh8_FirstSec] ; Sektor holen (BIGDOS beachten)
                sub     dx,dx
                cmp     ax,-1
                jne     DWrite_SmallSec
                mov     ax,es:[bx+Rh8_LFirstSec]
                mov     dx,es:[bx+Rh8_HFirstSec]
DWrite_SmallSec:mov     cl,es:[bx+Rh8_NSecs]    ; Sektorzahl laden (muá <=128 sein)
                les     si,es:[bx+Rh8_BufOfs]   ; Zieladresse laden
                mov     bl,ch                   ; Laufwerksnummer nach BL

                if      debug
                 push   ax
                 push   cx
                 mov    cx,ax
                 mov    al,' '
                 call   PrChar
                 mov    al,bl           ; Laufwerksnummer
                 call   PrByte
                 mov    al,' '
                 call   PrChar
                 mov    ax,dx           ; Startsektor
                 call   PrWord
                 mov    ax,cx
                 call   PrWord
                 mov    al,' '
                 call   PrChar
                 pop    cx
                 mov    al,cl           ; Sektorzahl
                 call   PrByte
                 mov    al,' '
                 call   PrChar
                 mov    ax,es           ; Startadresse
                 call   PrWord
                 mov    al,':'
                 call   PrChar
                 mov    ax,si
                 call   PrWord
                 pop    ax
                endif

                call    TranslateParams         ; umrechnen lassen
                call    WriteSectors            ; der eigentliche Lesevorgang

                ret

Write:          mov     al,es:[bx+Rh_Unit] ; Laufwerksnummer prfen
		call	ChkDrive
		ljc	Error

                call    DoWrite

                ljc     StateError              ; bei Fehlern...
                jmp     Done                    ; ansonsten o.k.


;******************************************************************************
;* Funktion 9: Sektoren schreiben mit šberprfung                             *
;******************************************************************************

Rh9		struct
		 db	Rh_len dup (?)
MediaID		 db     ?		; Media-ID Laufwerk
BufOfs		 dw     ?		; Adresse Datenpuffer
BufSeg		 dw     ?
NSecs		 dw     ?		; Anzahl zu lesender Bl”cke
FirstSec	 dw     ?		; Startsektor bzw. $FFFF fr 32-Bit-Nummern
VolID		 dd     ?		; Adresse Laufwerksname
LFirstSec	 dw     ?		; lange Startsektornummer
HFirstSec	 dw     ?
		endstruct

Write_Verify:   mov     al,es:[bx+Rh_Unit] ; Laufwerksnummer prfen
		call	ChkDrive
		ljc	Error

                call    DoWrite         ; schreiben

                ljc     StateError      ; bei Fehlern vorher abbrechen
                
                les     bx,[Rh_Ptr]     ; Parameter nochmal fr Verify laden
                mov     al,es:[bx+Rh_Unit]
                mov     ch,al
                mov     ax,es:[bx+Rh9_FirstSec]
                sub     dx,dx
                cmp     ax,-1
                jne     VWrite_SmallSec
                mov     ax,es:[bx+Rh9_LFirstSec]
                mov     dx,es:[bx+Rh9_HFirstSec]
VWrite_SmallSec:mov     cl,es:[bx+Rh9_NSecs]
                mov     bl,ch

                call    TranslateParams ; nochmal umrechen...
                call    VeriSectors     ; und prflesen

                jmp     Done            ; alles gut gegangen

;******************************************************************************

OutputStat:     jmp     Unknown

OutputFlush:    jmp     Unknown

IOCTLWrite:     jmp     Unknown

;******************************************************************************
;* kein Device wechselbar, ™ffnen/Schleáen interessiert nicht                 *
;******************************************************************************

DeviceOpen:     jmp     Done

DeviceClose:    jmp     Done

Removeable:     jmp     Done

;******************************************************************************

OutputTillBusy: jmp     Unknown

GenIOCTL:       jmp     Unknown

GetLogical:     jmp     Unknown

SetLogical:     jmp     Unknown

IOCTLQuery:     jmp     Unknown

;******************************************************************************
;* Funktion 0: Initialisierung                                                *
;******************************************************************************

                include "secparam.inc"

Rh0		struct
		 db	Rh_len dup (?)
Units		 db     ?		; Zahl bedienter Laufwerke
EndOfs		 dw     ?		; Endadresse Offset
EndSeg		 dw     ?		; Endadresse Segment
ParamOfs	 dw     ?		; Parameter Offsetadresse
ParamSeg	 dw     ?		; Parameter Segmentadresse
FirstDrive	 db     ?		; erstes freies Laufwerk
MsgFlag		 db     ?		; Flag, ob DOS Fehler ausgeben darf
		endstruct

Init:           PrMsg   HelloMsg        ; Meldung ausgeben
                call    LowLevelIdent   ; Startmeldung des Low-Level-Treibers

                mov     byte ptr es:[bx+Rh0_Units],0 ; noch keine Laufwerke

                mov     al,es:[bx+Rh0_FirstDrive] ; Startlaufwerk retten
                mov     [DrOfs],al

                mov     ax,cs           ; ES auf gem. Segment
                mov     es,ax

; Schritt 1: Controller prfen

                PrMsg   DiagMsg0
                call    ContDiag        ; Diagnose ausfhren
                sub     al,Diag_NoError
                cmp     al,6            ; auáerhalb ?
                jae     Diag_Over
                add     al,al           ; Meldung ausrechnen
                mov     ah,0
                mov     si,ax
                mov     dx,DiagMsgTable[si]
                mov     ah,9
                int     INT_DOS
                or      si,si           ; fehlerfrei ?
                ljnz    Init_Err        ; Nein, Fehler
                jmp     Init_ChkDrives  ; Ja, weiter zum Laufwerkstest

Diag_Over:      push    ax
                PrMsg   UndefDiagMsg    ; undefinierter Fehlercode
                pop     ax
                add     al,Diag_NoError ; Meldung rckkorrigieren
                db      0d4h,10h        ; AAM 16
                add     ax,'00'
                push    ax
                mov     al,ah
                mov     ah,14
                int     10h
                pop     ax
                mov     ah,14
                int     10h
                PrChar  CR
                PrChar  LF
                jmp     Init_Err


; Schritt 2: Laufwerke testen

; Menaufruf?

Init_ChkDrives: mov     ax,40h          ; CTRL gedrckt ?
                mov     es,ax
                btst    byte ptr es:[17h],2
                jz      Init_Menu
                call    DiskMenu

; Schritt 2a: Laufwerk rekalibrieren

Init_Menu:      mov     al,[MomDrive]
                call    Recalibrate
                ljc     Init_NextDrive  ; Fehler: Laufwerk berspringen

; Schritt 2b: Masterpartitionssektor lesen

ReadMaster:     mov     al,[MomDrive]
		mov	ah,0		; Kopf... 
		sub	bx,bx		; ...Zylinder...
		mov	cx,0101h	; ...ein Sektor ab Sektor 1
                mov     di,ds
                mov     es,di
                lea     di,[SectorBuffer] ; in den Sektorpuffer
		call	ReadSectors
                JmpOnError [MomDrive],Init_NextDrive ; Fehler ?

; Schritt 2c: Laufwerksparameter initialisieren

                lea     si,[SectorBuffer+DrPar_Offset] ; Quelladresse im Sektor
                mov     al,[MomDrive]   ; Zieladresse ausrechnen
                call    GetPTabAdr
                mov     cx,DrPar_Len
                cld
                rep     movsb

                sub     di,DrPar_Len    ; Laufwerk nicht initialisiert ?
                cmp     word ptr[di+DrPar_Cyls],0
                je      DoQuery
                cmp     byte ptr[di+DrPar_Heads],0
                je      DoQuery
                cmp     byte ptr[di+DrPar_NSecs],0
                jne     NoQuery
DoQuery:        mov     al,[MomDrive]   ; wenn ja, dann nachfragen
                mov     ah,1            ; Rckschreiben hier erlaubt
                call    QueryParams
                or      al,al           ; =0-->Laufwerk ignorieren
                jz      Init_NextDrive
                dec     al              ; =1-->nochmal lesen
                jz      ReadMaster      ; ansonsten weitermachen

NoQuery:        mov     al,[MomDrive]   ; Laufwerksparameter ausgeben...
                call    PrintPDrive
                mov     al,[MomDrive]   ; ...und dem Controller einbleuen
                call    SetDriveParams
                JmpOnError [MomDrive],Init_NextDrive
                mov     al,[MomDrive]
                call    Recalibrate
                JmpOnError [MomDrive],Init_NextDrive

; Schritt 2d: durch die Partitionssektoren hangeln

		mov	al,[MomDrive]	; Laufwerk : momentanes
		cbw			; Kopf : 0
		push	ax
		sub	ax,ax
		push	ax		; Zylinder : 0
		inc	ax		; Sektor : 1
		push	ax
		dec	ax
		push	ax		; lin. Sektornummer 0
		push	ax
		call	ScanParts

Init_NextDrive: inc     [MomDrive]      ; Z„hler weitersetzen
		cmp	[MomDrive],MaxPDrives
                ljb     Init_ChkDrives

                cmp     [DrCnt],0       ; keine Partitionen gefunden ?
                jne     Init_PDrives
                PrMsg   ErrMsgNoDrives  ; ja: meckern
                jmp     Init_Err

Init_PDrives:   PrMsg   LDriveMsg
                mov     [MomDrive],0    ; Parameter der Partitionen ausgeben
                lea     bp,[DrTab_BPBs] ; und BPB-Tabelle aufbauen
                cld

Init_PLDrives:  mov     al,[MomDrive]
                call    PrintLDrive

                mov     al,[MomDrive]   ; Bootsdektor lesen
                call    ReadBootSec
                lea     si,[SectorBuffer+BPBOfs] ; BPB rauskopieren
                mov     al,[MomDrive]
                call    GetTabAdr
                lea     di,[di+DrTab_BPB]
                mov     ax,cs
                mov     es,ax
                mov     ds:[bp],di      ; Adresse nebenbei ablegen
                add     bp,2
                mov     cx,BPBSize
                rep     movsb

                inc     [MomDrive]
                mov     al,[MomDrive]
                cmp     al,[DrCnt]
                jb      Init_PLDrives

                PrChar  LF              ; sieht besser aus...

                les     bx,[Rh_Ptr]     ; Zeiger auf BPB-Zeiger einschreiben
                lea     ax,[DrTab_BPBs]
                mov     es:[bx+Rh0_ParamOfs],ax
                mov     es:[bx+Rh0_ParamSeg],cs
                jmp     Init_OK         ; Initialisierung erfolgeich zu Ende

Init_Err:       PrMsg   WKeyMsg
                xor     ah,ah           ; damit Meldung lesbar bleibt
                int     16h
                sub     ax,ax           ; Treiber aus Speicher entfernen
                jmp     Init_End

Init_OK:        mov     al,[DrCnt]      ; Laufwerkszahl holen
                les     bx,[Rh_Ptr]
                mov     es:[bx+Rh0_Units],al ; im Request Header eintragen
                mov     [NrOfVols],al   ; im Treiberkopf eintragen
                lea     ax,[Init]       ; residenten Teil installieren

Init_End:       les     bx,[Rh_Ptr]
                mov     es:[bx+Rh0_EndOfs],ax ; Endadresse setzen
                mov     es:[bx+Rh0_EndSeg],cs

                jmp     Done

;******************************************************************************
;* transiente Unterroutinen                                                   *
;******************************************************************************

;******************************************************************************
;* Partitionsbaum durchgehen                                                  *
;*              In  :   dw Kopf/Laufwerk                                      *
;*                      dw Zylinder                                           *
;*			dw Sektor					      *
;*			dd lineare Nummer des Sektors			      *
;******************************************************************************

ScParts_DrHd	equ	12		; Parameteradressen auf Stack
ScParts_Cyl	equ	10
ScParts_Sec	equ	8
ScParts_LinSec	equ	4
ScParts_ParTab  equ     0-(MaxParts*ParTab_Len)   ; Kopie Partitionstabelle
ScParts_LocSize equ     0-ScParts_ParTab          ; belegter Stack

ScanParts:      enter	ScParts_LocSize,0

; Partitionssektor lesen

		mov	ax,[bp+ScParts_DrHd]
		mov	bx,[bp+ScParts_Cyl]
		mov	ch,[bp+ScParts_Sec]
		mov	cl,1
		mov	di,cs
		mov	es,di
		lea	di,[SectorBuffer]
                call    ReadSectors
                JmpOnError [MomDrive],ScanParts_End

; Partitionssektorkennung o.k. ?

		cmp	word ptr SectorBuffer[ParSecID_Offset],0aa55h
                ljne    ScanParts_End

; Partitionstabelle auslesen

		lea	si,[SectorBuffer+ParTab_Offset] ; Quelladresse
		mov	di,ss		; Zieladresse auf Stack
		mov	es,di
                lea     di,[bp+ScParts_ParTab]
		mov	cx,MaxParts*ParTab_Len  ; L„nge
		cld
		rep	movsb

; Partitionstabelle durchgehen

                mov     si,ScParts_ParTab       ; vorne anfangen
		mov	cx,MaxParts		; alle durchgehen
ScanParts_Scan:	push	cx

		mov	al,[bp+si+ParTab_Type]	; Typ der Partition lesen
                lea     bx,[AccPartTypes-1]     ; auf Suchtabelle
ScanParts_LAcc: inc     bx                      ; einen Eintrag weiter
                cmp     byte ptr [bx],0         ; Tabellenende ?
		je	ScanParts_Next		; ja-->war nix
		cmp	al,[bx]			; gefunden ?
		jne	ScanParts_LAcc		; 

		mov	bx,[bp+si+ParTab_LinSec] ; linearen Startsektor ausrechnen
		mov	cx,[bp+si+ParTab_LinSec+2]
                add     bx,[bp+ScParts_LinSec]  ; in CX:BX
                adc     cx,[bp+ScParts_LinSec+2]

		cmp	al,5			; extended partition ?
		jne	ScanParts_Enter

		push	si			; ja: Zeiger fr Rekursion retten
		mov	al,[bp+ScParts_DrHd]	; Laufwerk & Kopf zusammenbauen
		mov	ah,[bp+si+ParTab_FHead]
		push	ax
		mov	ax,[bp+si+ParTab_FSecCyl] ; Zylinder ausfiltern
                xchg    ah,al
		shr	ah,6
		push	ax
                mov     al,[bp+si+ParTab_FSecCyl] ; Sektor ausfiltern
		and	ax,63
		push	ax
		push	cx
		push	bx
		call	ScanParts
		pop	si			; Zeiger zurck
		jmp	ScanParts_Next

ScanParts_Enter:mov	al,[DrCnt]		; Partition in Tabelle eintragen
		call	GetTabAdr		; dazu Adresse neuen Eintrags holen
		cld
		mov	ax,cs			; Ziel im Segment
		mov	es,ax
		mov	al,[bp+si+ParTab_FHead] ; Kopf kopieren
		stosb	
		mov	ax,[bp+si+ParTab_FSecCyl] ; Zylinder kopieren
                xchg    ah,al
		shr	ah,6
		stosw
                mov     al,[bp+si+ParTab_FSecCyl] ; Sektor kopieren
		and	al,63
		stosb
		mov	ax,bx			; linearen Startsektor kopieren
		stosw
		mov	ax,cx
		stosw
		mov	ax,[bp+si+ParTab_NSecs]	; Sektorzahl kopieren
		stosw
		mov	ax,[bp+si+ParTab_NSecs+2]
		stosw
		mov	al,[bp+ScParts_DrHd]	; Laufwerksnummer kopieren
		stosb
		inc	[DrCnt]			; ein log. Laufwerk mehr

ScanParts_Next:	add	si,ParTab_Len		; auf n„chste Partition
		pop	cx
                dec     cx
                ljnz    ScanParts_Scan

ScanParts_End:  leave
		ret	10

;******************************************************************************
;* Daten eines physikalischen Laufwerks ausgeben                              *
;*              In  :   AL = Laufwerk                                         *
;******************************************************************************

                proc    PrintPDrive

                push    cx              ; Register retten
                push    dx
                push    di

                cbw                     ; AH l”schen
                push    ax              ; Laufwerk ausgeben
                PrMsg   PDriveMsg1
                pop     ax
                push    ax
                inc     ax
                mov     cl,1
                call    WriteDec
                PrMsg   PDriveMsg2

                pop     ax              ; Adresse Laufwerkstabelle berechnen
                call    GetPTabAdr

                mov     ax,[di+DrPar_Cyls] ; Zylinder ausgeben
                mov     cl,5
                call    WriteDec
                PrMsg   PDriveMsg3

                mov     al,[di+DrPar_Heads] ; K”pfe ausgeben
                mov     ah,0
                mov     cl,3
                call    WriteDec
                PrMsg   PDriveMsg4

                mov     al,[di+DrPar_NSecs] ; Sektoren ausgeben
                mov     ah,0
                mov     cl,4
                call    WriteDec
                PrMsg   PDriveMsg5

                mov     al,[di+DrPar_Heads] ; Gesamtsektorzahl berechnen
                mul     byte ptr [di+DrPar_NSecs]
                mul     word ptr [di+DrPar_Cyls]
		call	WriteMBytes
                PrMsg   PDriveMsg6

                pop     di              ; Register zurck
                pop     dx
                pop     cx
                ret

                endp

;******************************************************************************
;* Daten eines logischen Laufwerks ausgeben                                   *
;*              In  :   AL = Laufwerk                                         *
;******************************************************************************

                proc    PrintLDrive

                push    cx              ; Register retten
                push    dx
                push    di

                mov     dx,ax           ; Laufwerk retten
                push    dx
                mov     cx,3            ; ausgeben
                call    WriteSpc
                add     dl,[DrOfs]
                add     dl,'A'
                mov     ah,DOS_WrChar
                int     INT_DOS
                PrChar  ':'

                pop     ax              ; Tabelle holen
                call    GetTabAdr

                mov     al,[di+DrTab_Drive] ; Laufwerk ausgeben...
                inc     al
                cbw
                mov     cl,9
                call    WriteDec

                mov     ax,[di+DrTab_StartCyl] ; ...Zylinder...
                mov     cl,10
                call    WriteDec

                mov     al,[di+DrTab_StartHead] ; ...Kopf...
                cbw
                mov     cl,7
                call    WriteDec

                mov     al,[di+DrTab_StartSec] ; ...Sektor...
                cbw
                mov     cl,8
                call    WriteDec

                mov     cx,2
                call    WriteSpc
                mov     ax,[di+DrTab_SecCnt] ; ...Gr”áe
                mov     dx,[di+DrTab_SecCnt+2]
                call    WriteMBytes

                PrMsg   PDriveMsg6      ; Meldung wiederverwertet...

                pop     di              ; Register zurck
                pop     dx
                pop     cx
                ret

                endp

;******************************************************************************
;* Fehlercode eines Laufwerks ausgeben                                        *
;*              In :    AL = Fehlercode                                       *
;*                      AH = Laufwerksnummer (0,1...)                         *
;******************************************************************************

                proc    WrErrorCode

                push    bx              ; Register retten
                push    cx
                push    dx

                add     ah,'1'          ; LW-Nummer in ASCII umrechnen...
                mov     [DrvErrorMsg2],ah ; ...und einschreiben
                mov     ch,al           ; Kode sichern
                PrMsg   DrvErrorMsg
                mov     cl,7            ; bei Bit 0 anfangen
ErrLoop:        rol     ch,1            ; fagl. Bit in Carry
                jnc     NoErrBit
                mov     bl,cl           ; Bit gefunden: Index ausrechnen
                mov     bh,0
                add     bx,bx
                mov     dx,[bx+Pointers]
                mov     ah,DOS_WrString
                int     INT_DOS
NoErrBit:       dec     cl              ; n„chstes Bit
                jnz     ErrLoop

                pop     dx              ; Register zurck
                pop     cx
                pop     bx

                ret

DrvErrorMsg:    db      "Fehler auf Festplatte "
DrvErrorMsg2:   db      "0:",CR,LF,'$'

Pointers        dw      Msg0,Msg1,Msg2,Msg3,Msg4,Msg5,Msg6,Msg7
Msg0            db      "  Adreámarke nicht gefunden",CR,LF,'$'
Msg1            db      "  Spur 0 nicht gefunden",CR,LF,'$'
Msg2            db      "  Kommandoabbruch",CR,LF,'$'
Msg3            db      "$"
Msg4            db      "  Sektor nicht gefunden",CR,LF,'$'
Msg5            db      "$"
Msg6            db      "  Datenfehler",CR,LF,'$'
Msg7            db      "  Sektor als defekt markiert",CR,LF,'$'

                endp

;******************************************************************************
;* Sektorenzahl als MBytes ausgeben                                           *
;*              In:     DX:AX = Sektorzahl                                    *
;******************************************************************************

                proc    WriteMBytes

SecsPerMByte    equ     (2^20)/SecSize

                push    cx              ; Register retten
		push	dx

                add     ax,SecsPerMByte/20 ; wg. Rundung
                adc     dx,0

                mov     cx,SecsPerMByte ; durch 2048 teilen = MByte
                div     cx
                push    dx              ; Nachkommastellen retten
                mov     cl,6
                call    WriteDec

                PrChar  '.'             ; Nachkommastelle
                pop     ax              ; holen
                cwd
                mov     cx,SecsPerMByte/10 ; Sektoren pro 100 KByte
		div	cx
		mov	cl,1		; ausgeben
		call	WriteDec

		pop	dx		; Register zurck
		pop	cx
		ret

                endp

;******************************************************************************
;* transiente Daten                                                           *
;******************************************************************************

HelloMsg:       db      CR,LF,"Sekund„rlaufwerkstreiber V0.4",CR,LF,'$'

ErrMsgNoDrives: db      CR,LF,"Fehler: keine Partitionen gefunden",CR,LF,'$'

DiagMsg0:       db      CR,LF,"Controller-Selbsttest: ",'$'
DiagMsg1:       db      "OK",CR,LF,'$'
DiagMsg2:       db      "Controller fehlerhaft",CR,LF,'$'
DiagMsg3:       db      "Sektorpuffer defekt",CR,LF,'$'
DiagMsg4:       db      "Fehlerkorrektur defekt",CR,LF,'$'
DiagMsg5:       db      "Steuerprozessor defekt",CR,LF,'$'
DiagMsg6:       db      "Timeout",CR,LF,'$'
DiagMsgTable    dw      DiagMsg1,DiagMsg2,DiagMsg3,DiagMsg4,DiagMsg5,DiagMsg6
UndefDiagMsg    db      "Unbekannter Fehlercode #$"
WKeyMsg:        db      "Weiter mit beliebiger Taste...",CR,LF,'$'

PDriveMsg1:     db      "Festplatte $"
PDriveMsg2:     db      " :$"
PDriveMsg3:     db      " Zylinder,$"
PDriveMsg4:     db      " K”pfe,$"
PDriveMsg5:     db      " Sektoren,$"
PDriveMsg6:     db      " MByte",CR,LF,'$'

LDriveMsg:      db      CR,LF,"vorhandene Partitionen:",CR,LF
                db      "Laufwerk  Platte  Zylinder  Kopf"
                db      "  Sektor      Kapazit„t",CR,LF,'$'

AccPartTypes	db	1		; akzeptierte Partitionstypen: DOS 2.x FAT12
                db      4               ; DOS 3.x FAT16
		db	5		; DOS 3.3 extended
                db      6               ; DOS 3.31 >32 MByte
		db	0		; Tabellenende

MomDrive	db	0		; momentan gescanntes Laufwerk

                end
