aboutsummaryrefslogtreecommitdiffstats
path: root/tests/t_secdrive
diff options
context:
space:
mode:
Diffstat (limited to 'tests/t_secdrive')
-rw-r--r--tests/t_secdrive/asflags0
l---------tests/t_secdrive/lowlevel.inc1
-rw-r--r--tests/t_secdrive/secparam.inc1619
-rw-r--r--tests/t_secdrive/t_secdrive.asm1476
-rw-r--r--tests/t_secdrive/t_secdrive.doc9
-rwxr-xr-xtests/t_secdrive/t_secdrive.oribin0 -> 11271 bytes
-rw-r--r--tests/t_secdrive/wd1002xt.inc773
-rw-r--r--tests/t_secdrive/wd1003at.inc952
8 files changed, 4830 insertions, 0 deletions
diff --git a/tests/t_secdrive/asflags b/tests/t_secdrive/asflags
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/t_secdrive/asflags
diff --git a/tests/t_secdrive/lowlevel.inc b/tests/t_secdrive/lowlevel.inc
new file mode 120000
index 0000000..968acf7
--- /dev/null
+++ b/tests/t_secdrive/lowlevel.inc
@@ -0,0 +1 @@
+wd1003at.inc \ No newline at end of file
diff --git a/tests/t_secdrive/secparam.inc b/tests/t_secdrive/secparam.inc
new file mode 100644
index 0000000..8bf8381
--- /dev/null
+++ b/tests/t_secdrive/secparam.inc
@@ -0,0 +1,1619 @@
+;*****************************************************************************
+;* Sekund„rlaufwerkstreiber, Modul SecParam *
+;* liefert Laufwerksparameter, falls fehlend im Bootsektor *
+;* stellt das ganze Formatiermen zur Verfgung *
+;*****************************************************************************
+
+ section SecParam
+
+;*****************************************************************************
+;* gemeinsam benutzte Meldungen *
+;*****************************************************************************
+
+TrackMsg db "Spur (0..Spurzahl-1) : $"
+HeadMsg db "Kopf (0..Kopfzahl-1) : $"
+ConfirmMsg db "Sind Sie sicher ?$"
+InterleaveMsg db "Interleave (1..Sektorzahl-1) : $"
+
+;*****************************************************************************
+;* Routine BreakOnESC *
+;* schaut nach, ob ESC gedrckt wurde *
+;* Ausgabe: C=1, falls ja *
+;*****************************************************************************
+
+ GlobProc BreakOnESC
+
+ push ax ; Register retten
+ mov ah,1 ; Tastaturpuffer antesten
+ int INT_Keyboard
+ clc ; Annahme nicht gedrckt
+ jz Ende
+ mov ah,0 ; Zeichen da: dies abholen
+ int INT_Keyboard
+ cmp al,ESC ; ist es ESC ?
+ clc ; Annahme nein
+ jne Ende
+ stc ; jawoll!
+Ende: pop ax ; Register zurck
+ ret
+
+ endp
+
+;*****************************************************************************
+;* Routine YesNo *
+;* fragt nach Ja oder Nein *
+;* Ausgabe: C=0, falls ja *
+;*****************************************************************************
+
+ proc YesNo
+
+ push ax ; Register retten
+
+QueryLoop: mov ah,DOS_RdChar ; ein Zeichen lesen
+ int INT_DOS
+
+ cmp al,'a' ; Kleinbuchstaben ?
+ jb Upper
+ cmp al,'z'
+ ja Upper
+ add al,'A'
+ sub al,'a'
+Upper:
+ cmp al,'J' ; akzeptierte Zeichen fr Ja
+ clc
+ je YNDone
+ cmp al,'Y'
+ clc
+ je YNDone
+ cmp al,'N' ; akzeptierte Zeichen fr Nein
+ stc
+ je YNDone
+
+ PrChar BEL ; alles andere anmeckern
+ jmp QueryLoop
+
+YNDone: PrChar al ; Zeichen als Echo ausgeben
+ PrChar CR ; Zeilenvorschub
+ PrChar LF
+ pop ax ; Register zurck
+
+ ret
+
+ endp
+
+;*****************************************************************************
+;* Routine ReadNumber *
+;* liest einen Wert von 0..n in AX ein *
+;* Eingabe: AX = Maximalwert *
+;* DI = Zeiger auf Meldung *
+;* Ausgabe: AX = eingelesene Zahl *
+;*****************************************************************************
+
+ proc ReadNumber
+
+ push bx ; Register retten
+ push cx
+ push dx
+ push si
+
+ mov si,ax ; Maximalwert retten
+InLoop: mov dx,di ; Meldung ausgeben
+ mov ah,DOS_WrString
+ int INT_DOS
+ lea dx,[KeyBuffer] ; Zahl als String einlesen
+ mov ah,DOS_RdString
+ int INT_DOS
+ PrChar CR
+ PrChar LF
+ mov ax,0 ; jetzt Zeichen verarbeiten
+ mov cl,[KeyBuffer+1]
+ mov ch,0
+ lea bx,[KeyBuffer+2]
+ jcxz InvFormat ; Nullschleife abfangen
+ConvLoop: mov dx,10 ; bisheriges Ergebnis hochmultiplizieren
+ mul dx
+ mov dl,[bx] ; ein Zeichen holen
+ inc bx
+ sub dl,'0' ; ASCII-Offset abziehen
+ jc InvFormat ; bei Formatfehler abbrechen
+ cmp dl,9 ; nur 0..9 erlaubt
+ ja InvFormat
+ add al,dl ; dazuaddieren
+ adc ah,0
+ loop ConvLoop
+ jmp ChkRange ; fertig: weiter zur Bereichsprfung
+InvFormat: PrMsg InvMsg ; wenn fehlerhaft, meckern
+ jmp InLoop ; und nochmal versuchen
+ChkRange: cmp ax,si ; auáerhalb Bereich ?
+ jbe OK
+ PrMsg OverMsg ; ja: meckern...
+ jmp InLoop ; ...und auf ein neues
+
+OK: pop si ; Register zurck
+ pop dx
+ pop cx
+ pop bx
+
+ ret
+
+KeyBufferLen equ 30 ; 30 Zeichen sollten fr Zahlen reichen...
+KeyBuffer db KeyBufferLen ; Maimall„nge fr DOS
+ db 0 ; effektive L„nge Eingabe
+ db KeyBufferLen dup (0) ; Platz fr Eingabe
+InvMsg db "Ungltiges Zahlenformat",CR,LF,'$'
+OverMsg db "Bereichsberschreitung",CR,LF,'$'
+
+ endp
+
+;******************************************************************************
+;* eine Anzahl Leerzeichen ausgeben *
+;* In : CX = Anzahl *
+;******************************************************************************
+
+ globproc WriteSpc
+
+ push dx ; Register retten
+
+ jcxz NULL ; Nullschleife abfangen
+Loop: mov ah,DOS_WrChar
+ mov dl,' '
+ int INT_DOS
+ loop Loop
+
+NULL: pop dx ; Register zurck
+ ret
+
+ endp
+
+;******************************************************************************
+;* vorzeichenlose Zahl dezimal ausgeben *
+;* In : AX = Zahl *
+;* CL = min. Stellenzahl *
+;******************************************************************************
+
+ globproc WriteDec
+
+ push di ; Register retten
+ push cx
+ push dx
+
+ mov ch,0 ; CH z„hlt effektive Zeichenzahl
+InLoop: sub dx,dx ; Stellendivision
+ mov di,10 ; gewnschtes Zahlensystem
+ div di
+ mov di,ax ; war es vorher 0 ?
+ or di,dx ; (wenn Quotient & Rest 0)
+ jnz NZero ; nein-->normal ausgeben
+ or ch,ch ; noch erste Stelle ?
+ jz NZero ; dann auf jeden Fall 0 ausgeben
+ mov dl,0f0h ; ansonsten Leerzeichen fr leading 0
+NZero: push dx ; Zeichen speichern...
+ inc ch ; ...und mitz„hlen
+ cmp ch,cl ; Mindestzahl ausgegeben ?
+ jb InLoop ; nein-->weiter
+ or ax,ax ; ansonsten: 0 ?
+ jnz InLoop ; nein, weitermachen
+
+ shr cx,8 ; effektive Zahl nach CX
+OLoop: pop dx ; ein Zeichen runterholen
+ add dl,'0' ; in ASCII konvertieren
+ mov ah,DOS_WrChar ; ber DOS ausgeben
+ int INT_DOS
+ loop OLoop
+
+ pop dx
+ pop cx
+ pop di ; Register zurck
+ ret
+
+ endp
+
+;******************************************************************************
+;* vorzeichenlose Zahl hexadezimal ausgeben *
+;* In : AX = Zahl *
+;* CL = min. Stellenzahl *
+;******************************************************************************
+
+ globproc WriteHex
+
+ push di ; Register retten
+ push cx
+ push dx
+
+ mov ch,0 ; CH z„hlt effektive Zeichenzahl
+InLoop: sub dx,dx ; Stellendivision
+ mov di,16 ; gewnschtes Zahlensystem
+ div di
+ mov di,ax ; war es vorher 0 ?
+ or di,dx ; (wenn Quotient & Rest 0)
+ jnz NZero ; nein-->normal ausgeben
+ or ch,ch ; noch erste Stelle ?
+ jz NZero ; dann auf jeden Fall 0 ausgeben
+ mov dl,0f0h ; ansonsten Leerzeichen fr leading 0
+NZero: push dx ; Zeichen speichern...
+ inc ch ; ...und mitz„hlen
+ cmp ch,cl ; Mindestzahl ausgegeben ?
+ jb InLoop ; nein-->weiter
+ or ax,ax ; ansonsten: 0 ?
+ jnz InLoop ; nein, weitermachen
+
+ shr cx,8 ; effektive Zahl nach CX
+OLoop: pop dx ; ein Zeichen runterholen
+ add dl,'0' ; in ASCII konvertieren
+ cmp dl,'9'
+ jbe NoHex
+ add dl,7
+NoHex: mov ah,DOS_WrChar ; ber DOS ausgeben
+ int INT_DOS
+ loop OLoop
+
+ pop dx
+ pop cx
+ pop di ; Register zurck
+ ret
+
+ endp
+
+;*****************************************************************************
+;* Routine GeometryDefined - stellt fest, ob Geometrie fr ei Laufwerk defi- *
+;* niert ist *
+;* Eingabe: AL = Laufwerksnummer *
+;* Ausgabe: C = 0, falls OK *
+;*****************************************************************************
+
+ proc GeometryDefined
+
+ push di ; Register retten
+ call GetPTabAdr ; Tabellenadresse bilden
+ cmp word ptr[di+DrPar_Cyls],0 ; Zylinderzahl 0 ?
+ stc
+ je Fin
+ cmp byte ptr[di+DrPar_Heads],0 ; Kopfzahl 0 ?
+ stc
+ je Fin
+ cmp byte ptr[di+DrPar_NSecs],0 ; Sektorzahl 0 ?
+ stc
+ je Fin
+ clc ; alles OK
+Fin: pop di ; Register zurck
+ ret
+
+ endp
+
+;*****************************************************************************
+;* Routine QueryRedefine - fragt nach, ob Geometrie neu definert werden soll *
+;* Eingabe: AL = Laufwerk *
+;* Ausgabe: C = 0, falls ja *
+;*****************************************************************************
+
+ proc QueryRedefine
+
+ add al,'1' ; Laufwerksnummer in ASCII umrechnen
+ mov [UndefMsg2],al ; in Meldung einschreiben
+ PrMsg UndefMsg
+ PrMsg DoQueryMsg ; nachfragen, ob Definition erwnscht
+ call YesNo
+ ret
+
+UndefMsg db "Geometrie fr Laufwerk "
+UndefMsg2 db " undefiniert.",CR,LF,"$"
+DoQueryMsg db "Geometrie neu definieren ? $"
+
+ endp
+
+;*****************************************************************************
+;* Routine ReadGeomety - liest Laufwerksgeometrie vom Benutzer ein *
+;* Eingabe: AL = phys. Laufwerksnummer *
+;*****************************************************************************
+
+ proc ReadGeometry
+
+ push ax ; Register retten
+ push si
+ push di
+
+ call GetPTabAdr ; Zeiger auf Parametertabelle holen
+ mov si,di
+
+ lea di,[CylInpMsg] ; Zylinderzahl erfragen
+ mov ax,1024
+ call ReadNumber
+ mov [si+DrPar_Cyls],ax
+
+ lea di,[HeadInpMsg] ; Kopfzahl erfragen
+ mov ax,16
+ call ReadNumber
+ mov [si+DrPar_Heads],al
+
+ lea di,[RWCInpMsg] ; RWC-Zylinder erfragen
+ mov ax,65535
+ call ReadNumber
+ mov [si+DrPar_RedWr],ax
+
+ lea di,[PreInpMsg] ; Pr„kompensations-Zylinder erfragen
+ mov ax,65535
+ call ReadNumber
+ mov [si+DrPar_PrComp],ax
+
+ lea di,[ECCInpMsg] ; ECC-L„nge erfragen
+ mov ax,11
+ call ReadNumber
+ mov [si+DrPar_ECCLen],ax
+
+ mov al,[si+DrPar_Heads] ; Steuerbyte Bit 3=1, falls Platte
+ dec al
+ and al,8 ; mehr als 8 K”pfe hat
+ mov [si+DrPar_CByte],al
+
+ mov al,0 ; Timeouts unbenutzt
+ mov [si+DrPar_TOut],al
+ mov [si+DrPar_FTOut],al
+ mov [si+DrPar_CTOut],al
+
+ mov ax,[si+DrPar_Cyls] ; Parkzylinder=Zylinderzahl+1
+ inc ax
+ mov [si+DrPar_LZone],ax
+
+ lea di,[SecInpMsg] ; Sektorzahl erfragen
+ mov ax,255
+ call ReadNumber
+ mov [si+DrPar_NSecs],al
+
+ pop di ; Register zurck
+ pop si
+ pop ax
+ ret
+
+CylInpMsg db "Anzahl Zylinder (max. 1024) : $"
+HeadInpMsg db "Anzahl K”pfe (max. 16) : $"
+RWCInpMsg db "Startzylinder fr reduzierten Schreibstrom (max. 65535) : $"
+PreInpMsg db "Startzylinder fr Pr„kompensation (max. 65535) : $"
+ECCInpMsg db "max. ECC-Fehlerburstl„nge (5 oder 11) : $"
+SecInpMsg db "Sektorzahl (max. 255) : $"
+
+ endp
+
+;*****************************************************************************
+;* Routine WriteGeoToDisk - schreibt Laufwerksgeometrie auf Platte *
+;* Eingabe: AL = phys. Laufwerksnummer *
+;* Ausgabe: C+AX = Fehlerstatus *
+;*****************************************************************************
+
+ proc WriteGeoToDisk
+
+ push bx ; Register retten
+ push cx
+ push dx
+ push si
+ push di
+ push es
+
+ mov dx,ds ; alles im folgenden im Datensegment
+ mov es,dx
+
+ mov dl,al ; Laufwerksnummer retten
+ mov ah,0 ; Kopf 0,
+ sub bx,bx ; Spur 0,
+ mov cx,0101h ; Sektor 1 lesen
+ lea di,[SectorBuffer]
+ call ReadSectors
+ jc GeoError ; Abbruch bei Fehler
+
+ mov al,dl ; Geometrietabelle adressieren
+ call GetPTabAdr
+ mov si,di ; selbige in MBR einschreiben
+ lea di,[SectorBuffer+DrPar_Offset]
+ mov cx,DrPar_Len/2
+ cld
+ rep movsw
+
+ mov al,dl ; jetzt den ganzen Kram zurckschreiben
+ mov ah,0
+ sub bx,bx
+ mov cx,0101h
+ lea si,[SectorBuffer]
+ call WriteSectors
+ jc GeoError
+
+Fin: pop es ; Register zurck
+ pop di
+ pop si
+ pop dx
+ pop cx
+ pop bx
+ ret
+
+GeoError: mov ah,bl ; Fehlerausgabe
+ call WrErrorCode
+ jmp Fin
+
+ endp
+
+;*****************************************************************************
+;* Routine QueryParams *
+;* Eingabe: AL = Laufwerksnummer *
+;* AH = 1, falls Rckschreiben erlaubt *
+;* Ausgabe: AL = 0: Laufwerk vergessen *
+;* AL = 1: Mastersektor neu lesen *
+;* AL = 2: Tabelle nur transient eingetragen, kein Neulesen *
+;*****************************************************************************
+
+ globproc QueryParams
+
+ push bx ; Register retten
+ push cx
+ push dx
+ push si
+ push di
+ push es
+
+ mov bx,ax ; Laufwerksnummer retten
+ call QueryRedefine ; nachfragen, ob Neudefinition erwnscht
+ mov al,0 ; Abbruch bei Nein
+ ljc Terminate
+
+ mov al,bl ; Geometrie einlesen
+ call ReadGeometry
+
+ shr bh,1 ; Rckschreiben erlaubt ?
+ cmc ; C=1-->verboten
+ jc NoWriteBack
+
+ PrMsg WriteBackMsg ; nachfragen, ob Rckschreiben erwnscht
+ call YesNo
+NoWriteBack: mov al,2 ; Neulesen bei Nein verhindern
+ jc Terminate
+
+ mov al,bl
+ call WriteGeoToDisk
+ jc WrError
+
+ mov al,1 ; Ergebnis: MBR-Lesen wiederholen
+ jmp Terminate
+WrError: mov al,0 ; Schreibfehler: Laufwerk ignorieren
+
+Terminate: pop es ; Register zurck
+ pop di
+ pop si
+ pop dx
+ pop cx
+ pop bx
+
+ ret
+
+WriteBackMsg db "Parametersatz zurckschreiben ? $"
+
+ endp
+
+;****************************************************************************
+;* Laufwerksnummer einlesen *
+;* Ausgabe: AL = Laufwerksnummer *
+;****************************************************************************
+
+ proc InputDrive
+
+ push dx ; Register retten
+
+RdLoop: PrMsg PromptMsg ; Anfrage ausgeben
+ mov ah,DOS_RdChar ; ein Zeichen holen
+ int INT_DOS
+ mov dl,al ; Zeichen retten
+ PrChar dl ; Laufwerk als Echo zurckgeben
+ PrChar CR
+ PrChar LF
+ sub dl,'1' ; nur 1 oder 2 erlaubt
+ jc RdLoop
+ cmp dl,MaxPDrives
+ jae RdLoop
+ mov al,dl ; Laufwerk in AL zurckgeben
+
+ pop dx ; Register zurck
+ ret
+
+PromptMsg db "Laufwerksnummer (1 oder 2) : $"
+
+ endp
+
+;****************************************************************************
+;* Men fr Plattenfunktionen *
+;****************************************************************************
+
+ proc SelectDisk
+
+ push ax ; Register sichern
+ push bx
+ push cx
+ push dx
+ push si
+ push di
+ push es
+
+ mov dh,ah ; Rckschreibeflag sichern
+ call InputDrive
+ mov dl,al ; Laufwerksnummer sichern
+
+ mov al,dl ; Geometrie noch undefiniert ?
+ call GeometryDefined
+ jnc IsOK ; Nein, alles in Butter
+
+ mov al,dl ; nicht definiert: versuchen,
+ mov ah,0 ; Geometrie vom MBR zu lesen
+ mov bx,ds
+ mov es,bx
+ sub bx,bx
+ mov cx,0101h
+ lea di,[SectorBuffer]
+ call ReadSectors
+ jnc CopyTable ; kein Fehler->Tabelle auslesen
+ mov dh,0 ; wenn Fehler, nie zurckschreiben
+ jmp ReadManual ; und manuell probieren
+CopyTable: lea si,[SectorBuffer+DrPar_Offset]
+ mov al,dl
+ call GetPTabAdr
+ mov cx,DrPar_Len/2
+ cld
+ rep movsw
+ mov al,dl ; Geometrie jetzt da ?
+ call GeometryDefined
+ jnc IsOK ; falls ja, Ende
+
+ReadManual: mov al,dl ; fragen, ob Redefinition erwnscht
+ call QueryRedefine
+ jc NotOK ; falls nein, Abbruch
+ mov al,dl ; ansonsten einlesen
+ call ReadGeometry
+ shr dh,1 ; zurckschreiben ?
+ jnc IsOK
+ mov al,dl ; ja...
+ call WriteGeoToDisk ; Fehler ignorieren
+
+IsOK: mov [MomDrive],dl ; Laufwerk akzeptiert
+
+NotOK: pop es ; Register zurck
+ pop di
+ pop si
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+
+ ret
+
+ endp
+
+;----------------------------------------------------------------------------
+
+ proc ChangeGeometry
+
+ cmp [MomDrive],-1 ; Laufwerk berhaupt schon definiert ?
+ jne DriveDefined
+ call InputDrive ; nein: lesen & einschreiben
+ mov [MomDrive],al
+DriveDefined: mov al,[MomDrive] ; neue Geometrie einlesen
+ call ReadGeometry
+ mov al,[MomDrive]
+ call WriteGeoToDisk ; die auch gleich zu schreiben versuchen
+
+ ret
+
+ endp
+
+;----------------------------------------------------------------------------
+
+ proc VerifyDisk
+
+ pusha ; restlos alles...
+
+ mov al,[MomDrive] ; erstmal sicherstellen, daá der
+ call SetDriveParams ; Kontroller die Geometrie kennt
+
+ mov al,[MomDrive] ; die Geometrie brauchen wir auch
+ call GetPTabAdr
+
+ PrMsg ESCMsg
+ sub bp,bp ; Fehlerz„hler
+ mov si,bp ; Zylinderz„hler
+ mov dx,bp ; Folgefehlerz„hler
+CylLoop: mov dl,0 ; Kopfz„hler
+HeadLoop: PrMsg CylMsg ; zu testende Spur ausgeben
+ mov ax,si
+ mov cl,4
+ call WriteDec
+ PrMsg HeadMsg
+ mov al,dl
+ mov ah,0
+ mov cl,2
+ call WriteDec
+ PrChar CR
+
+ mov al,[MomDrive] ; eine Spur testen
+ mov ah,dl
+ mov bx,si
+ mov cl,[di+DrPar_NSecs]
+ mov ch,1
+ call VeriSectors
+
+ jnc NoError ; evtl. Fehlerbehandlung
+ push ax
+ PrChar LF
+ pop ax
+ mov ah,[MomDrive]
+ call WrErrorCode
+ inc bp ; Fehlerz„hler rauf
+ inc dh
+ test dh,7 ; alle 8 Fehler in Reihe nachfragen
+ jnz NextTrack
+ PrMsg GoOnMsg
+ call YesNo
+ jc Terminate
+ jmp NextTrack
+NoError: mov dh,0 ; eine Spur gut->Folgenz„hler l”schen
+NextTrack: call BreakONESC ; Abbruch ?
+ jc Terminate
+ inc dl ; n„chster Kopf
+ cmp dl,[di+DrPar_Heads]
+ jb HeadLoop
+ inc si ; n„chster Zylinder
+ cmp si,[di+DrPar_Cyls]
+ ljb CylLoop
+
+Terminate: mov ax,bp ; Fehlerzahl ausgeben
+ mov cl,5
+ call WriteDec
+ PrMsg ErrorMsg
+
+ popa ; Register zurck
+
+ ret
+
+EscMsg db "Verifizieren..",CR,LF,"Abbruch mit <ESC>",CR,LF,'$'
+CylMsg db "Zylinder $"
+HeadMsg db ", Kopf $"
+GoOnMsg db "Test fortsetzen? $"
+ErrorMsg: db " Fehler gefunden ",CR,LF,'$'
+
+ endp
+
+;----------------------------------------------------------------------------
+
+ proc FormatDisk
+
+ push ax ; Register retten
+ push bx
+ push cx
+ push di
+ push es
+
+ mov al,[MomDrive] ; erstmal sicherstellen, daá der
+ call SetDriveParams ; Kontroller die Geometrie kennt
+
+ mov al,[MomDrive]
+ call GetPTabAdr
+InterLoop: mov al,[di+DrPar_NSecs] ; Maximum=Sektorzahl-1
+ dec al
+ mov ah,0
+ lea di,[InterleaveMsg] ; Interleave erfragen
+ call ReadNumber
+ or ax,ax ; Null wollen wir nicht
+ jz InterLoop
+ mov bl,al ; Interleave retten
+ PrMsg ConfirmMsg ; sicherheitshalber nachfragen
+ call YesNo
+ jc Fin
+ PrMsg NewLine
+ PrMsg FormatMsg
+ mov ah,bl ; Interleave zurck
+ mov al,[MomDrive]
+ call FormatUnit
+ jc FormatError ; Fehler beim Formatieren ?
+
+NoFormatError: PrMsg WriteMsg
+ lea di,[SectorBuffer] ; MBR erzeugen
+ cld
+ mov cx,SecSize/2-1 ; letztes Wort anders!
+ mov ax,ds
+ mov es,ax
+ sub ax,ax ; prinzipiell erstmal alles Nullen
+ rep stosw
+ mov word ptr[di],0aa55h ; Gltigkeitsflag am Ende
+ mov al,[MomDrive] ; Geometrietabelle eintragen
+ call GetPTabAdr
+ mov si,di
+ lea di,[SectorBuffer+DrPar_Offset]
+ mov cx,DrPar_Len/2
+ rep movsw
+ mov al,[MomDrive] ; Sektor schreiben
+ mov ah,0 ; MBR auf Kopf 0,
+ mov bx,0 ; Spur 0,
+ mov cx,0101h ; Sektor 1
+ lea si,[SectorBuffer]
+ call WriteSectors
+ jc FormatError ; Fehler beim Schreiben ?
+
+Fin: pop es ; Register zurck
+ pop di
+ pop cx
+ pop bx
+ pop ax
+ ret
+
+FormatError: cmp al,DErr_UserTerm ; Abbruch durch Benutzer ?
+ je Fin ; dann nicht meckern
+ push ax ; Fehlercode retten
+ pushf
+ PrMsg NewLine
+ popf
+ pop ax
+ mov ah,[MomDrive] ; Fehlermeldung ausgeben
+ call WrErrorCode
+ jmp Fin
+
+FormatMsg db "Formatieren...",CR,LF,'$'
+WriteMsg db "MBR schreiben...",CR,LF,'$'
+
+ endp
+
+;----------------------------------------------------------------------------
+
+ proc BadTrack
+
+ push bx
+ push si
+ push di
+
+ mov al,[MomDrive] ; Zeiger auf Geometrietabelle
+ call GetPTabAdr ; holen
+ mov si,di
+
+ lea di,[TrackMsg] ; Spurnummer abfragen
+ mov ax,[si+DrPar_Cyls]
+ dec ax
+ call ReadNumber
+ mov bx,ax
+ lea di,[HeadMsg] ; Kopfnummer abfragen
+ mov ax,[si+DrPar_Heads]
+ dec ax
+ call ReadNumber
+ mov ah,al
+ push ax ; sicherheitshalber noch best„tigen
+ PrMsg ConfirmMsg
+ call YesNo
+ pop ax
+ jc NoError
+ mov al,[MomDrive] ; Spur markieren
+ call MarkBad
+ jnc NoError
+ push ax ; Fehlercode retten
+ pushf
+ PrMsg NewLine
+ popf
+ pop ax
+ mov ah,[MomDrive]
+ call WrErrorCode
+NoError:
+ pop di
+ pop si
+ pop bx
+
+ ret
+
+ endp
+
+;----------------------------------------------------------------------------
+
+ proc FormTrack
+
+ push bx
+ push si
+ push di
+
+ mov al,[MomDrive] ; Zeiger auf Geometrietabelle
+ call GetPTabAdr ; holen
+ mov si,di
+
+ lea di,[TrackMsg] ; Spurnummer abfragen
+ mov ax,[si+DrPar_Cyls]
+ dec ax
+ call ReadNumber
+ mov bx,ax
+ lea di,[HeadMsg] ; Kopfnummer abfragen
+ mov ax,[si+DrPar_Heads]
+ dec ax
+ call ReadNumber
+ mov ah,al
+ push ax ; Kopf retten
+InterLoop: mov al,[si+DrPar_NSecs] ; Interleave-Maximum=Sektorzahl-1
+ dec al
+ mov ah,0
+ lea di,[InterleaveMsg] ; Interleave erfragen
+ call ReadNumber
+ or ax,ax ; Null wollen wir nicht
+ jz InterLoop
+ mov cl,al ; Interleave passend ablegen
+ PrMsg ConfirmMsg ; nochmal nachfragen
+ call YesNo
+ pop ax
+ jc NoError
+ mov al,[MomDrive] ; Kopf zurck
+ call FormatTrack
+ jnc NoError
+ push ax ; Fehlercode retten
+ pushf
+ PrMsg NewLine
+ popf
+ pop ax
+ mov ah,[MomDrive]
+ call WrErrorCode
+NoError:
+ pop di
+ pop si
+ pop bx
+
+ ret
+
+ endp
+
+;----------------------------------------------------------------------------
+
+; packt eine Sektorkoordinate ins BIOS-Format
+; -->Zylinder=BX, Kopf=AH, Sektor=AL
+; <--Zylinder/Sektor=BX, Kopf=AH
+
+ proc PackCoordinate
+
+ shl bh,6
+ or bh,al
+ xchg bl,bh
+ ret
+
+ endp
+
+ proc UnpackCoordinate
+
+ xchg bh,bl ; Zylinderbytes in richtige Reihenfolge
+ mov al,bh ; Sektor ausmaskieren
+ and al,00111111b
+ shr bh,6 ; Zylinder korrigieren
+
+ ret
+
+ endp
+
+; berechnet aus einer Sektorkoordinate die lineare Sektornummer
+; -->Zylinder/Sektor=BX, Kopf=AH, Geometriezeiger=SI
+; <--Sektornummer=DX/AX
+
+ proc LinearizeCoordinate
+
+ push bx
+ push cx
+
+ mov cx,ax ; Kopf retten
+ mov al,bh ; Zylinder rekonstruieren
+ mov ah,bl
+ shr ah,6
+ mov dl,[si+DrPar_Heads]
+ mov dh,0
+ mul dx ; = Anzahl Spuren bis Zylinderbeginn
+ add al,ch ; Startkopf dazuaddieren
+ adc ah,0 ; bisher hoffentlich nur 16 Bit...
+ mov dl,[si+DrPar_NSecs]
+ mov dh,0
+ mul dx ; = Anzahl Spuren bis Spurbeginn
+ and bl,3fh ; letztendlich Sektor-1 dazu
+ dec bl
+ add al,bl
+ adc ah,0
+ adc dx,0
+
+ pop cx
+ pop bx
+
+ ret
+
+ endp
+
+ proc MakePart
+
+ push bx
+ push cx
+ push dx
+ push si
+ push di
+ push es
+
+ PrMsg ConfirmMsg ; sind wir sicher ?
+ call YesNo
+ ljc End
+
+ mov al,[MomDrive] ; Laufwerk rekalibrieren
+ call Recalibrate
+ ljc PartError
+
+ mov al,[MomDrive] ; alten MBR auslesen
+ mov ah,0
+ mov bx,0
+ mov cx,0101h
+ mov si,ds
+ mov es,si
+ lea di,[SectorBuffer]
+ call ReadSectors
+ ljc PartError
+
+ mov al,[MomDrive] ; Plattengeometrie holen
+ call GetPTabAdr
+ mov si,di
+
+ lea di,[SectorBuffer+ParTab_Offset] ; an erste Tabelle schreiben
+ mov byte ptr [di+ParTab_BFlag],80h ; Partition aktiv
+ cmp byte ptr[si+DrPar_Heads],1 ; nur ein Kopf ?
+ ja MoreHeads
+ mov bx,1 ; ja: Start auf Zyl. 1, Kopf 0
+ mov ah,0
+ jmp WriteStart
+MoreHeads: sub bx,bx ; nein: Start auf Zyl. 0, Kopf 1
+ mov ah,1
+WriteStart: mov al,01h ; Startsektor immer 1
+ call PackCoordinate
+ mov [di+ParTab_FHead],ah
+ mov [di+ParTab_FSecCyl],bx
+ call LinearizeCoordinate ; linearen Start schreiben
+ mov [di+ParTab_LinSec],ax
+ mov [di+ParTab_LinSec+2],dx
+ push dx
+ push ax
+ mov bx,[si+DrPar_Cyls] ; Ende: Zylinder n-2, Kopf n-1, Sektor n
+ sub bx,2
+ mov ah,[si+DrPar_Heads]
+ dec ah
+ mov al,[si+DrPar_NSecs]
+ call PackCoordinate
+ mov [di+ParTab_LHead],ah
+ mov [di+ParTab_LSecCyl],bx
+ call LinearizeCoordinate ; Sektorzahl berechnen
+ pop bx ; dazu Start abziehen
+ sub ax,bx
+ pop bx
+ sbb dx,bx
+ add ax,1 ; !!! L„nge=Stop-Start+1
+ adc dx,0
+ mov [di+ParTab_NSecs],ax
+ mov [di+ParTab_NSecs+2],dx
+ or dx,dx ; falls >64K Sektoren,
+ jz NoBigDOS ; eine BigDOS-Partition
+ mov bl,6
+ jmp TypeFound
+NoBigDOS: cmp ax,32679 ; ab 32680 Sektoren 16-Bit-FAT
+ jb NoFAT16
+ mov bl,04
+ jmp TypeFound
+NoFAT16: mov bl,1 ; kleine 12-Bit-Partition
+TypeFound: mov [di+ParTab_Type],bl
+ add di,ParTab_Len ; die anderen 3 Partitionen l”schen
+ mov cx,3*(ParTab_Len/2)
+ sub ax,ax
+ cld
+ rep stosw
+
+ mov al,[MomDrive] ; neuen MBR schreiben
+ mov ah,0
+ mov bx,0
+ mov cx,0101h
+ lea si,[SectorBuffer]
+ call WriteSectors
+ ljc PartError
+
+End: pop es
+ pop di
+ pop si
+ pop dx
+ pop cx
+ pop bx
+
+ ret
+
+PartError: push ax ; Fehlercode retten
+ pushf
+ PrMsg NewLine
+ popf
+ pop ax
+ mov ah,[MomDrive] ; Fehlermeldung ausgeben
+ call WrErrorCode
+ jmp End
+
+ConfirmMsg: db "ACHTUNG! Alle bisherigen Partitionen auf der",CR,LF
+ db "Festplatte werden gel”scht! Fortfahren? $"
+
+ endp
+
+;----------------------------------------------------------------------------
+
+ proc FormatPart
+
+ pusha
+
+ mov ax,ds ; wir arbeiten nur im Datensegment
+ mov es,ax
+
+ PrMsg ConfirmMsg ; vorher nachfragen
+ call YesNo
+ ljc Ende
+
+ mov al,[MomDrive] ; Laufwerk rekalibrieren
+ call Recalibrate
+ ljc LFormError
+
+; Schritt 1: MBR lesen
+
+ mov al,[MomDrive] ; MBR auslesen, um Partitions-
+ mov ah,0 ; daten zu bekommen
+ mov bx,0
+ mov cx,0101h
+ lea di,[SectorBuffer]
+ call ReadSectors
+ ljc LFormError
+
+; Schritt 2: Partitionsdaten in BPB kopieren
+
+ lea di,[SectorBuffer+ParTab_Offset] ; auf Partitionsdaten
+ mov al,[di+ParTab_Type] ; muá prim„re Partition sein
+ cmp al,1 ; DOS 2.x FAT12?
+ je ParTypeOK
+ cmp al,4 ; DOS 3.x FAT16?
+ je ParTypeOK
+ cmp al,6 ; DOS 4.x BIGDOS?
+ je ParTypeOK
+ PrMsg InvParTypeMsg ; nichts dergleichen: Abbruch
+ jmp Ende
+ParTypeOK: mov word ptr[BPB_SysID],'AF' ; FAT-Kennung in BPB eintragen
+ mov word ptr[BPB_SysID+2],'1T' ; FAT12/FAT16
+ mov word ptr[BPB_SysID+5],' '
+ mov ah,'2' ; Annahme FAT12
+ cmp al,1
+ je ParIsFAT12 ; wenn Typ=1, OK
+ mov ah,'6' ; ansonsten FAT16
+ParIsFAT12: mov byte ptr[BPB_SysID+4],ah
+ mov ax,[di+ParTab_NSecs] ; Sektorzahl in BPB schreiben
+ mov dx,[di+ParTab_NSecs+2]
+ mov word ptr[BPB_NumSecs32],ax
+ mov word ptr[BPB_NumSecs32+2],dx
+ or dx,dx ; falls < 64K Sektoren,
+ jz ParIsNotBig ; GrӇe auch unten eintragen,
+ sub ax,ax ; ansonsten 0
+ParIsNotBig: mov [BPB_NumSecs16],ax
+ mov ax,word ptr[di+ParTab_LinSec] ; Startsektor umkopieren
+ mov dx,word ptr[di+ParTab_LinSec+2]
+ mov word ptr[BPB_LinStart],ax
+ mov word ptr[BPB_LinStart+2],dx
+
+; Schritt 3: Partitionsdaten in Partitionstabelle kopieren, damit wir die
+; linearen Schreib/Lesefunktionen nutzen k”nnen
+
+ mov [DrCnt],1 ; nur ein Laufwerk belegt
+ mov ah,[di+ParTab_FHead] ; Startkoordinate ablegen
+ mov bx,[di+ParTab_FSecCyl]
+ call UnpackCoordinate
+ mov [DrTab+DrTab_StartHead],ah
+ mov word ptr [DrTab+DrTab_StartCyl],bx
+ mov [DrTab+DrTab_StartSec],al
+ mov ax,[di+ParTab_LinSec]
+ mov word ptr [DrTab+DrTab_LinStart],ax
+ mov ax,[di+ParTab_LinSec+2]
+ mov word ptr [DrTab+DrTab_LinStart+2],ax
+ mov ax,[di+ParTab_NSecs]
+ mov word ptr [DrTab+DrTab_SecCnt],ax
+ mov ax,[di+ParTab_NSecs+2]
+ mov word ptr [DrTab+DrTab_SecCnt+2],ax
+ mov al,[MomDrive] ; Laufwerk einschreiben
+ mov [DrTab+DrTab_Drive],al
+ mov byte ptr[DrTab+DrTab_BPB],0 ; kein BPB
+
+; Schritt 4: konstante Felder in BPB eintragen
+
+ mov [BPB_SecSize],SecSize ; SektorgrӇe konstant 512 Byte
+ mov [BPB_ResvdSecs],1 ; nur Bootsektor reserviert
+ mov [BPB_NumFATs],2 ; 2 FATs ist DOS-Standard
+ mov [BPB_MediaID],0f8h ; Media-Byte fr Platten konstant
+ mov al,[MomDrive]
+ call GetPTabAdr
+ mov ah,0
+ mov al,[di+DrPar_NSecs]; Plattenzylinder und -k”pfe
+ mov [BPB_SecsPerTrk],ax
+ mov al,[di+DrPar_Heads]
+ mov [BPB_Heads],ax
+ mov al,[MomDrive] ; Plattennummer+80h
+ add al,80h
+ mov [BPB_PhysNo],ax
+ mov [BPB_ExtID],29h ; Erkennung, daá erw. BPB gltig
+ mov ah,0 ; Seriennummer=Uhrzeit
+ int INT_Clock
+ mov word ptr[BPB_SerialNo],cx
+ mov word ptr[BPB_SerialNo+2],dx
+ lea di,[BPB_Name] ; Name ist leer
+ mov cx,11
+ mov al,' '
+ cld
+ rep stosb
+
+; Schritt 5: einige Sachen vom Anwender erfragen
+
+DirEntLoop: mov ax,1024 ; mehr ist wohl kaum sinnvoll
+ lea di,[DirEntriesMsg]
+ call ReadNumber
+ cmp ax,SecSize/32 ; weniger als ein Sektor ergibt
+ jb DirEntLoop ; keinen Sinn
+ mov [BPB_DirLen],ax ; Anzahl in BPB eintragen
+ mov dx,0 ; Directory-Sektorzahl berechnen
+ mov bx,SecSize/32
+ div bx
+ or dx,dx ; ggfs. aufrunden
+ jz DirLenEven
+ inc ax
+DirLenEven: mov [DirLen],ax
+
+; Schritt 6: Clusterl„nge berechnen
+
+ mov ax,word ptr[BPB_NumSecs32] ; # Sektoren in Datenfeld
+ mov dx,word ptr[BPB_NumSecs32+2] ; und FATs berechnen
+ sub ax,[BPB_ResvdSecs]
+ sbb dx,0
+ sub ax,[DirLen]
+ sbb dx,0
+ mov bl,1 ; Annahme: m”glichst wenig Sektoren pro Cluster
+ClusterLoop: or dx,dx ; wenn noch mehr als 64K Cluster,
+ jnz ClusterNxtLoop ; auf jeden Fall zu groá
+ cmp ax,4080 ; bei weniger als 4K Clustern
+ jb ClusterEndLoop ; auf jeden Fall OK
+ cmp [BPB_SysID+4],'2' ; sonst bei FAT12
+ je ClusterNxtLoop ; auf jeden Fall zu viel
+ cmp ax,65510 ; bei FAT16 Vergleich auf 64K
+ jb ClusterEndLoop
+ClusterNxtLoop: shl bl,1 ; zu viel: Cluster verdoppeln
+ js ClusterEndLoop ; bei 128 Sektoren/Cluster abbrechen
+ shr dx,1 ; Clusterzahl halbiert sich
+ rcr ax,1
+ jmp ClusterLoop
+ClusterEndLoop: mov [BPB_SecsPerClust],bl ; Ergebnis einschreiben
+ add ax,2 ; Dummy-Eintr„ge in FAT
+ adc dx,0
+ mov bx,341 ; Anzahl FAT-Sektoren berechnen
+ cmp [BPB_SysID+4],'2'
+ jz Cluster12
+ mov bx,256
+Cluster12: div bx
+ or dx,dx ; Sektorzahl aufrunden
+ jz FATLenEven
+ inc ax
+FATLenEven: mov [BPB_SecsPerFAT],ax ; Anzahl FAT-Sektoren einschreiben
+
+; Schritt 7: Bootsektor aufbauen
+
+ PrMsg WrBootSectMsg
+ lea di,[SectorBuffer]
+ cld
+ mov al,0ebh ; Dummy-Sprung einschreiben
+ stosb
+ mov al,0feh
+ stosb
+ mov al,90h
+ stosb
+ mov ax,'ES' ; OEM-ID einschreiben
+ stosw
+ mov ax,'DC'
+ stosw
+ mov ax,'IR'
+ stosw
+ mov ax,'EV'
+ stosw
+ lea si,[BPBBuffer] ; BPB einschreiben
+ mov cx,BPB_Length
+ rep movsb
+ mov cx,SectorBuffer+SecSize ; Rest vom Bootsektor nullen
+ sub cx,di
+ mov al,0
+ rep stosb
+ mov ax,0 ; Bootsektor ist log. Sektor 0
+ mov dx,ax
+ mov bl,0 ; Partition 0
+ call TranslateParams
+ mov cl,1 ; nur ein Sektor
+ lea si,[SectorBuffer]
+ call WriteSectors
+ ljc LFormError
+
+; Schritt 8: Directory & FATs ausnullen
+
+ lea di,[SectorBuffer] ; Sektorpuffer wird benutzt
+ mov cx,SecSize/2
+ cld
+ sub ax,ax
+ rep stosw
+ PrMsg WrFATMsg
+ mov ax,[BPB_ResvdSecs] ; Startsektor FATs holen
+ sub dx,dx
+ lea si,[SectorBuffer]
+ mov cx,[BPB_SecsPerFAT]
+ add cx,cx ; beide FATs nullen
+FATZeroLoop: push ax ; Sektornummer und -zahl retten
+ push cx
+ push dx
+ mov bl,0
+ call TranslateParams
+ mov cl,1
+ call WriteSectors
+ pop dx
+ pop cx
+ pop ax
+ ljc LFormError
+ add ax,1 ; n„chster Sektor
+ adc dx,0
+ loop FATZeroLoop
+ push ax ; !!! PrMsg zerst”rt AX-Register
+ PrMsg WrDirMsg
+ pop ax
+ mov cx,[DirLen] ; dito fr Directory
+DirZeroLoop: push ax
+ push cx
+ push dx
+ mov bl,0
+ call TranslateParams
+ mov cl,1
+ call WriteSectors
+ pop dx
+ pop cx
+ pop ax
+ ljc LFormError
+ add ax,1 ; n„chster Sektor
+ adc dx,0
+ loop DirZeroLoop
+
+; Schritt 9: Sektoren testen und FAT initialisieren
+
+ mov ax,[BPB_ResvdSecs] ; Datensektorbeginn berechnen
+ sub dx,dx
+ mov [FAT1Pos],ax ; Beginn 1. FAT hinter Bootsektor
+ mov [FAT1Pos+2],dx
+ add ax,[BPB_SecsPerFAT] ; Beginn 2. FAT hinter 1. FAT
+ adc dx,0
+ mov [FAT2Pos],ax
+ mov [FAT2Pos+2],dx
+ add ax,[BPB_SecsPerFAT]
+ adc dx,0
+ add ax,[DirLen] ; Datenbeginn hinter Directory
+ adc dx,0
+ mov [DataPos],ax ; diesen Startsektor retten
+ mov [DataPos+2],dx
+
+ mov ax,word ptr[BPB_NumSecs32] ; Anzahl Cluster berechnen
+ mov dx,word ptr[BPB_NumSecs32+2]
+ sub ax,[DataPos]
+ sbb dx,[DataPos+2]
+ mov bl,[BPB_SecsPerClust] ; / SecsPerCluster
+ mov bh,0
+ div bx
+ mov [ClusterCnt],ax
+
+ call ClearFATBuffer ; erste Elemente in FAT schreiben
+ mov ah,0ffh
+ mov al,[BPB_MediaID]
+ call WriteFAT
+ mov al,0ffh
+ call WriteFAT
+ PrMsg ESCMsg
+ mov ax,[DataPos] ; Schleifenvorbereitung
+ mov dx,[DataPos+2]
+ mov cx,[ClusterCnt]
+VerifyLoop: push ax ; Z„hler retten
+ mov bp,cx
+ push dx
+ mov bl,0 ; immer Laufwerk 0
+ call TranslateParams ; Cluster testlesen
+ mov cl,[BPB_SecsPerClust]
+ test bp,15 ; nur alle 16 Cluster schreiben
+ jnz NoWriteVeri
+ push ax ; Clusternummer ausgeben
+ push cx
+ PrMsg ClusterMsg
+ mov ax,[ClusterCnt]
+ sub ax,bp
+ add ax,2 ; erster Datencluster hat Nummer 2
+ mov cl,5
+ call WriteDec
+ PrChar CR
+ pop cx
+ pop ax
+NoWriteVeri: call VeriSectors
+ mov ax,0 ; Annahme OK (SUB wrde C l”schen...)
+ jnc Verify_OK
+ mov ax,0fff7h
+Verify_OK: call WriteFAT
+ pop dx ; Z„hler zurck
+ mov cx,bp
+ pop ax
+ add al,[BPB_SecsPerClust]
+ adc ah,0
+ adc dx,0
+ call BreakOnESC ; Abbruch durch Benutzer ?
+ jc Ende
+ loop VerifyLoop
+ cmp [FATBufferFill],0 ; Puffer rausschreiben
+ je NoFATFlush
+ call FlushFATBuffer
+NoFATFlush:
+
+Ende: PrMsg NewLine
+ mov [DrCnt],0 ; sonst kommt jemand ins Schleudern...
+ popa
+ ret
+
+LFormError: push ax ; Fehlercode retten
+ pushf
+ PrMsg NewLine
+ popf
+ pop ax
+ mov ah,[MomDrive] ; Fehlermeldung ausgeben
+ call WrErrorCode
+ jmp Ende
+
+WriteFAT: push bx ; einen FAT-Eintrag schreiben
+ mov bx,ax
+ call WriteFATNibble ; Bit 0..3 schreiben
+ mov al,bl ; Bit 4..7 schreiben
+ shr al,4
+ call WriteFATNIbble
+ mov al,bh ; Bit 8..11 schreiben
+ call WriteFATNibble
+ cmp [BPB_SysID+4],'2' ; evtl. Bit 12..15 schreiben
+ je WriteFAT12
+ mov al,bh
+ shr al,4
+ call WriteFATNibble
+WriteFAT12: pop bx
+ ret
+
+WriteFATNibble: push bx
+ and al,15 ; Bit 0..3 ausmaskieren
+ jz SkipWriteNibble ; Nullen brauchen wir nicht schreiben
+ mov [MustFlushFAT],1 ; vermerken, daá Puffer nicht genullt ist
+ mov bx,[FATBufferFill] ; Bit 1.. enthalten Adresse
+ shr bx,1
+ jnc WriteFATLower ; oben oder unten schreiben
+ shl al,4
+WriteFATLower: or FATBuffer[bx],al
+SkipWriteNibble:inc [FATBufferFill]
+ cmp [FATBufferFill],SecSize*2 ; Sektor voll ?
+ jne WriteFAT_NFlush
+ call FlushFATBuffer
+WriteFAT_NFlush:pop bx
+ ret
+
+FlushFATBuffer: push bx
+ push cx
+ push dx
+ push si
+ cmp [MustFlushFAT],0 ; nix zu tun ?
+ je SkipFlushDisk
+ mov ax,[FAT1Pos] ; erste FAT schreiben
+ mov dx,[FAT1Pos+2]
+ mov bl,0
+ call TranslateParams
+ mov cl,1
+ lea si,[FATBuffer]
+ call WriteSectors
+ mov ax,[FAT2Pos] ; zweite FAT schreiben
+ mov dx,[FAT2Pos+2]
+ mov bl,0
+ call TranslateParams
+ mov cl,1
+ lea si,[FATBuffer]
+ call WriteSectors
+SkipFlushDisk: call ClearFATBuffer ; Zeiger wieder auf 0
+ add [FAT1Pos],1 ; FAT-Sektornummern weiterz„hlen
+ adc [FAT1Pos+2],0
+ add [FAT2Pos],1
+ adc [FAT2Pos+2],0
+ pop si
+ pop dx
+ pop cx
+ pop bx
+ ret
+
+
+ClearFATBuffer: push cx
+ push di
+ cld
+ lea di,[FATBuffer]
+ mov cx,SecSize/2
+ sub ax,ax
+ rep stosw
+ pop di
+ pop cx
+ mov [FATBufferFill],ax
+ mov [MustFlushFAT],al
+ ret
+
+ConfirmMsg db "ACHTUNG! Alle Daten gehen verloren! Fortfahren? $"
+InvParTypeMsg db CR,LF,"Partition 1 hat ungltigen Typ oder ist undefiniert!",CR,LF,'$'
+DirEntriesMsg db "Anzahl Eintr„ge im Wurzelverzeichnis (16..1024) : $"
+WrBootSectMsg db "Schreibe Boot-Sektor...",CR,LF,'$'
+WrFATMsg db "Initialisiere FATs...",CR,LF,'$'
+WrDirMsg db "Initialisiere Wurzelverzeichnis...",CR,LF,'$'
+ESCMsg db "Abbruch mit <ESC>",CR,LF,'$'
+ClusterMsg db "Teste Cluster $"
+
+DirLen dw ? ; # Directory-Sektoren
+ClusterCnt dw ?
+FAT1Pos dw 2 dup (?) ; speichern Sektorz„hler w„hrend Test
+FAT2Pos dw 2 dup (?)
+DataPos dw 2 dup (?)
+
+BPBBuffer: ; Zwischenspeicher fr BPB
+BPB_SecSize dw ? ; SektorgrӇe in Bytes
+BPB_SecsPerClust db ? ; Sektoren pro Cluster
+BPB_ResvdSecs dw ? ; reservierte Sektoren (Bootsektor)
+BPB_NumFATs db ? ; Anzahl FATs
+BPB_DirLen dw ? ; Anzahl Eintr„ge im Directory
+BPB_NumSecs16 dw ? ; Anzahl Sektoren, falls <32 MByte
+BPB_MediaID db ? ; Media-Erkennungsbyte
+BPB_SecsPerFAT dw ? ; Sektoren pro FAT
+BPB_SecsPerTrk dw ? ; Sektoren pro Spur
+BPB_Heads dw ? ; Anzahl K”pfe
+BPB_LinStart dd ? ; linearer Startsektor auf Laufwerk
+BPB_NumSecs32 dd ? ; Anzahl Sektoren, falls >=32 MByte
+BPB_PhysNo dw ? ; physikalische Laufwerks-Nummer
+BPB_ExtID db ? ; Erkennung, daá es ein erweiterter Boot-Record ist
+BPB_SerialNo dd ? ; Seriennummer
+BPB_Name db 11 dup (?) ; Name
+BPB_SysID db 7 dup (?) ; Systemerkennung
+BPB_Length equ $-BPBBuffer ; L„nge BPB
+
+FATBuffer db SecSize dup (?) ; Puffer, um FATs aufzubauen
+MustFlushFAT db ? ; Flag, ob FAT-Puffer geschrieben werden muá
+FATBufferFill dw ? ; Anzahl Nibbles in FAT-Puffer
+
+ endp
+
+;----------------------------------------------------------------------------
+
+ globproc DiskMenu
+
+ push ax ; Register retten
+ push cx
+ push di
+
+ mov [spsave],sp ; Stack umschalten
+ mov [sssave],ss
+ mov ax,cs
+ mov ss,ax
+ lea sp,[Stack]
+
+MenuLoop: mov al,[MomDrive] ; Festplatten-Nr. in Kopfmeldung einschreiben
+ add al,'1'
+ cmp al,'0'
+ jnz DrivePresent ; falls Laufwerk noch undefiniert, - ausgeben
+ mov al,'-'
+DrivePresent: mov [MenuMsg_Drv],al
+ PrMsg MenuMsg
+ mov al,[MomDrive]
+ cmp al,-1 ; falls <>-1, Geometrie ausgeben
+ je NoDrivePresent
+ call GetPTabAdr ; dazu Tabelle holen
+ mov ax,[di+DrPar_Cyls]
+ mov cl,5
+ call WriteDec
+ PrMsg CylMsg
+ mov al,[di+DrPar_Heads]
+ mov ah,0
+ mov cl,3
+ call WriteDec
+ PrMsg HeadMsg
+ mov al,[di+DrPar_NSecs]
+ mov ah,0
+ mov cl,3
+ call WriteDec
+ PrMsg SecMsg
+NoDrivePresent:
+ PrMsg MenuList
+ mov ah,DOS_RdChar
+ int INT_DOS
+ push ax
+ PrChar al
+ PrMsg TwoLines
+ pop ax
+
+ cmp al,'0' ; 0 = Men verlassen
+ lje MenuEnd
+
+ cmp al,'1' ; 1 = Platte wechseln
+ jne NoSelectDisk
+ mov ah,1 ; Rckschreiben erlaubt
+ call SelectDisk
+ jmp MenuLoop
+NoSelectDisk:
+ cmp al,'2' ; 2 = Geometrie wechseln
+ jne NoChangeGeometry
+ call ChangeGeometry
+ jmp MenuLoop
+NoChangeGeometry:
+ cmp [MomDrive],-1 ; fr alles weitere muá Platte gesetzt sein
+ jne DiskIsSelected
+ push ax
+ shl ax,8 ; Annahme: Geometrie nicht zurckschreiben
+ cmp ah,'3'
+ je NoWriteBack
+ inc al ; fr alles auáer low-level schon
+NoWriteBack: call SelectDisk ; implizit Laufwerk erfragen
+ PrMsg NewLine
+ pop ax
+ cmp [MomDrive],-1
+ lje MenuLoop ; wenn immer noch nicht gesetzt, Abbruch
+DiskIsSelected:
+ cmp al,'3' ; 3 = Platte low-leveln
+ jne NoFormatDisk
+ call FormatDisk
+ jmp MenuLoop
+NoFormatDisk:
+ cmp al,'4' ; 4 = Spur formatieren
+ jne NoFormTrack
+ call FormTrack
+ jmp MenuLoop
+NoFormTrack:
+ cmp al,'5' ; 5 = Platte prflesen
+ jne NoBadTrack
+ call BadTrack
+ jmp MenuLoop
+NoBadTrack:
+ cmp al,'6' ; 6 = Platte verifizieren
+ jne NoVerifyDisk
+ call VerifyDisk
+ jmp MenuLoop
+NoVerifyDisk:
+ cmp al,'7' ; 7 = Partition anlegen
+ jne NoMakePart
+ call MakePart
+ jmp MenuLoop
+NoMakePart:
+ cmp al,'8' ; 8 = Partition formatieren
+ jne NoFormatPart
+ call FormatPart
+ jmp MenuLoop
+NoFormatPart: PrChar BEL ; alle anderen Angaben anmeckern
+ jmp MenuLoop
+MenuEnd: mov ss,[sssave] ; Stack zurck
+ mov sp,[spsave]
+
+ pop di
+ pop cx
+ pop ax ; Register zurck
+
+ ret
+
+MenuMsg db CR,LF,"SECDRIVE Men Platte:"
+MenuMsg_Drv db '-',CR,LF,'$'
+CylMsg db " Zylinder,$"
+HeadMsg db " K”pfe,$"
+SecMsg db " Sektoren",CR,LF,'$'
+MenuList db CR,LF
+ db "1 = Platte wechseln",CR,LF
+ db "2 = Geometrie neu definieren",CR,LF
+ db "3 = Platte formatieren",CR,LF
+ db "4 = Spur formatieren",CR,LF
+ db "5 = Defekte Spuren markieren",CR,LF
+ db "6 = Platte verifizieren",CR,LF
+ db "7 = Partition erstellen",CR,LF
+ db "8 = Partition log. formatieren",CR,LF
+ db "------------------------",CR,LF
+ db "0 = Men verlassen",CR,LF,'$'
+spsave dw ?
+sssave dw ?
+
+ db 1024 dup (?) ; Programmstack
+Stack:
+
+ endp
+
+MomDrive db -1
+TwoLines: db CR,LF
+NewLine: db CR,LF,'$'
+
+ endsection
diff --git a/tests/t_secdrive/t_secdrive.asm b/tests/t_secdrive/t_secdrive.asm
new file mode 100644
index 0000000..4fc82c9
--- /dev/null
+++ b/tests/t_secdrive/t_secdrive.asm
@@ -0,0 +1,1476 @@
+;******************************************************************************
+;* *
+;* 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
diff --git a/tests/t_secdrive/t_secdrive.doc b/tests/t_secdrive/t_secdrive.doc
new file mode 100644
index 0000000..0aece08
--- /dev/null
+++ b/tests/t_secdrive/t_secdrive.doc
@@ -0,0 +1,9 @@
++----------------------- Test Application SECDRIVE --------------------------+
+| |
+| YUCK! 8086/MSDOS-Code ;-) |
+| This is a MSDOS driver for a secondary MFM/RLL/ESDI/IDE controller I |
+| wrote a few years ago - just to get another 40MB of storage (which was |
+| a lot at that time...) |
+| This app also demonstrates the usage of the newly introduced structures. |
+| |
++----------------------------------------------------------------------------+
diff --git a/tests/t_secdrive/t_secdrive.ori b/tests/t_secdrive/t_secdrive.ori
new file mode 100755
index 0000000..0538002
--- /dev/null
+++ b/tests/t_secdrive/t_secdrive.ori
Binary files differ
diff --git a/tests/t_secdrive/wd1002xt.inc b/tests/t_secdrive/wd1002xt.inc
new file mode 100644
index 0000000..7dd1268
--- /dev/null
+++ b/tests/t_secdrive/wd1002xt.inc
@@ -0,0 +1,773 @@
+;******************************************************************************
+;* *
+;* Includedatei fr SECMAIN.ASM *
+;* liefert low-level-Routinen fr SecMain *
+;* Version hier fr WD1002XT-kompatible Kontroller: *
+;* MFM, RLL (?) *
+;* *
+;* Historie: 28.12.1994 *
+;* 26. 3.1994 Formatierroutinen *
+;* 8. 4.1994 defekte Spuren markieren *
+;* *
+;******************************************************************************
+
+ section wd1002xt
+
+;------------------------------------------------------------------------------
+; Portadressen
+
+Port_Base equ 320h ; prim„re Basisadresse
+Port_Data equ Port_Base+0 ; Datenregister (R+W)
+Port_Status equ Port_Base+1 ; Statusregister (R)
+Port_Reset equ Port_Base+1 ; Reset ausl”sen (W)
+Port_Config equ Port_Base+2 ; Jumper auslesen (R)
+Port_Select equ Port_Base+2 ; Kontroller selektieren (W)
+Port_IRQDRQ equ Port_Base+3 ; IRQ/DRQ-Leitungen freigeben (W)
+
+;------------------------------------------------------------------------------
+; Kommandocodes
+
+Cmd_Diagnose equ 0e4h ; Kommando: Kontroller-Selbsttest
+Cmd_GetStatus equ 003h ; Status letzter Operation lesen
+Cmd_TestReady equ 000h ; Test, ob Laufwerk bereit
+Cmd_Restore equ 001h ; Laufwerk rekalibrieren
+Cmd_SetParams equ 00ch ; Laufwerksparameter setzen
+Cmd_Seek equ 00bh ; Spur anfahren
+Cmd_Read equ 008h ; Sektoren lesen
+Cmd_Write equ 00ah ; Sektoren schreiben
+Cmd_Verify equ 005h ; Sektoren auf Lesbarkeit prfen
+Cmd_WriteBuffer equ 00fh ; Sektorpuffer beschreiben
+Cmd_FormatDisk equ 004h ; Laufwerk formatieren
+Cmd_FormatTrack equ 006h ; Spur formatieren
+Cmd_FormatBad equ 007h ; Spur als defekt markieren
+
+;------------------------------------------------------------------------------
+; I/O-Bremse
+
+IODelay macro
+ jmp $+2
+ endm
+
+;------------------------------------------------------------------------------
+; Puffer
+
+CmdBufSize equ 6 ; enth„lt Kommandoblock fr WD1002
+CmdBuf db CmdBufSize dup (0)
+
+StatBufSize equ 4 ; enth„lt Statusinfo vom WD1002
+StatBuf db StatBufSize dup (0)
+
+GeomBufSize equ 8 ; enth„lt Parametertabelle fr Laufwerk
+GeomBuf db GeomBufSize dup (0)
+
+;******************************************************************************
+;* Kommandopuffer initialisieren *
+;******************************************************************************
+
+ proc InitCmdBuf
+
+ push ax ; Register retten
+
+ sub ax,ax ; mit Nullen initialisieren
+ mov word ptr[CmdBuf],ax
+ mov word ptr[CmdBuf+2],ax
+ mov ah,45h ; Retry on, 70us Steprate
+ mov word ptr[CmdBuf+4],ax
+
+ pop ax ; Register zurck
+
+ ret
+
+ endp
+
+;******************************************************************************
+;* einen Datenblock an den Kontroller schicken *
+;* In : ES:SI = Datenblock *
+;* CX = Anzahl Bytes *
+;* Out : C=1 bei Protokollfehler *
+;******************************************************************************
+
+ proc SendBlock
+
+ push ax ; Register retten
+ push cx
+ push dx
+ push si
+
+ mov dx,Port_Status
+ jcxz ZeroLoop ; Nullschleife abfangen
+ cld ; !!!
+OutLoop: in al,dx ; Status lesen
+ btst al,0 ; warten, bis REQ-Bit auf 1
+ jz OutLoop
+ btst al,1 ; IO-Bit muá 0 sein
+ stc
+ jnz ErrEnd
+ mov dl,Lo(Port_Data); ein Byte auf Datenport ausgeben
+ seges
+ outsb
+ mov dl,Lo(Port_Status) ; zurck fr n„chsten Durchlauf
+ loop OutLoop
+ZeroLoop: clc ; Ende ohne Fehler
+ErrEnd:
+ pop si ; Register zurck
+ pop dx
+ pop cx
+ pop ax
+
+ ret
+
+ endp
+
+;******************************************************************************
+;* einen Datenblock vom Kontroller lesen *
+;* In : ES:DI = Datenblock *
+;* CX = Anzahl Bytes *
+;* Out : C=1 bei Protokollfehler *
+;******************************************************************************
+
+ proc RecvBlock
+
+ push ax ; Register retten
+ push cx
+ push dx
+ push di
+
+ mov dx,Port_Status
+ jcxz ZeroLoop ; Nullschleife abfangen
+ cld ; !!!
+InLoop: in al,dx ; Status lesen
+ btst al,0 ; warten, bis REQ-Bit auf 1
+ jz InLoop
+ btst al,1 ; IO-Bit muá 1 sein
+ stc
+ jz ErrEnd
+ mov dl,Lo(Port_Data); ein Byte von Datenport einlesen
+ insb
+ mov dl,Lo(Port_Status) ; zurck fr n„chsten Durchlauf
+ loop InLoop
+ZeroLoop: clc ; Ende ohne Fehler
+ErrEnd:
+ pop di ; Register zurck
+ pop dx
+ pop cx
+ pop ax
+
+ ret
+
+ endp
+
+;******************************************************************************
+;* Status bilden *
+;* Out : C+AX = Status *
+;******************************************************************************
+
+ proc BuildStatus
+
+ push dx ; Register retten
+
+ mov dx,Port_Status ; auf Datum warten
+Loop: in al,dx
+ btst al,0 ; bis REQ=1
+ jz Loop
+ btst al,1 ; und IO=1
+ jz Loop
+ mov dl,Lo(Port_Data); CCB auslesen
+ in al,dx
+ mov ah,al ; retten fr Fehlerabfrage
+ and al,2 ; Bit 1 ausmaskieren
+ clc
+ ljz End ; wenn = 0, kein Fehler und AL=0
+
+ push cx ; zus„tzliche Register retten
+ push si
+ push di
+ push es
+
+ call InitCmdBuf ; ansonsten Kommando absetzen, um
+ mov [CmdBuf],Cmd_GetStatus ; Status zu lesen
+ and ah,20h ; Status fr korr. Laufwerk abfragen
+ mov [CmdBuf+1],ah
+ mov dx,Port_Status
+WaitNBusy: in al,dx
+ btst al,3
+ jnz WaitNBusy
+ mov ax,ds ; NICHT ExecCmd benutzen, da sonst
+ mov es,ax ; Rekursion !
+ lea si,[CmdBuf]
+ mov cx,CmdBufSize
+ mov dl,Lo(Port_Select)
+ out dx,al
+ call SendBlock
+ lea di,[StatBuf] ; 4 Statusbytes auslesen
+ mov cx,StatBufSize
+ call RecvBlock
+ mov dl,Lo(Port_Status); CCB nicht vergessen!!
+Loop2: in al,dx
+ btst al,0 ; bis REQ=1
+ jz Loop2
+ btst al,1 ; und IO=1
+ jz Loop2
+ mov dl,Lo(Port_Data)
+ in al,dx
+ mov al,[StatBuf] ; Fehlercode = 1.Byte,
+ and al,7fh ; Bit 0..6
+ stc ; Carry signalisiert Fehler
+ pop es ; zus„tzliche Register zurck
+ pop di
+ pop si
+ pop cx
+
+End: mov ah,0 ; MSB ohne Bedeutung
+ pop dx ; Register zurck
+ ret
+
+ endp
+
+;******************************************************************************
+;* XT- in AT-Fehlerkode umsetzen *
+;* Eingabe: AL = XT-Fehlerkode *
+;* Ausgabe: C+AX = AT-Fehlerstatus *
+;******************************************************************************
+
+ proc TranslateStatus
+
+ push bx
+ push si
+
+ mov bl,al ; alten Status sichern
+ mov bh,-1
+ lea si,[TransTable]
+ cld
+TransLoop: lodsw ; einen Eintrag laden
+ cmp al,bh ; Tabellenende?
+ je TransEnd
+ cmp al,bl ; Treffer?
+ jne TransLoop ; nein, weitersuchen
+ mov al,ah ; bersetzten Code laden
+ cmp al,0 ; Code fr kein Fehler?
+ clc
+ je Ende ; ja, C=0
+ jmp TransErr ; ansonsten C=1
+TransEnd: mov al,04h ; Aborted Command annehmen
+TransErr: stc ; Fehlerflag setzen
+
+Ende: pop si ; Register zurck
+ pop bx
+
+ ret
+
+TransTable: db 00h,00h ; kein Fehler
+ db 02h,02h ; kein Seek Complete-Signal
+ db 03h,04h ; Write Fault
+ db 04h,04h ; Laufwerk nicht bereit
+ db 06h,02h ; Spur 0 nicht gefunden
+ db 08h,02h ; Laufwerk positioniert noch
+ db 11h,40h ; unkorrigierbarer Datenfehler
+ db 12h,01h ; Adreámarke nicht gefunden
+ db 15h,10h ; Positionierfehler
+ db 18h,00h ; korrigierbarer Fehler (ignoriert)
+ db 19h,80h ; Spur als defekt markiert
+ db -1,-1 ; Tabellenende
+
+ endp
+
+;******************************************************************************
+;* ein Kommando ausfhren *
+;* In : AL = Kommando *
+;******************************************************************************
+
+ proc ExecCmd
+
+ push cx ; Register retten
+ push ax
+ push dx
+ push si
+ push es
+
+ mov [CmdBuf],al ; Kommandokode in Datenblock einschreiben
+ mov dx,Port_Status ; warten, bis Kontroller frei
+WaitNBusy: in al,dx
+ btst al,3
+ jnz WaitNBusy
+ mov dx,Port_Select ; Kontroller selektieren
+ out dx,al
+ mov ax,ds ; Adresse Kommandoblock
+ mov es,ax
+ lea si,[CmdBuf]
+ mov cx,CmdBufSize ; L„nge Kommandoblock
+ call SendBlock ; Kommandoblock abschicken
+
+ pop es ; Register zurck
+ pop si
+ pop dx
+ pop ax
+ pop cx
+
+ ret
+
+ endp
+
+;******************************************************************************
+;* Laufwerk und Sonderwerte in Kommandoblock einprogrammieren *
+;* In : AL = Laufwerk *
+;* AH = Kopf *
+;******************************************************************************
+
+ proc SetDriveEnv
+
+ push ax ; Register retten
+
+
+ shl al,5 ; Laufwerksbit an Stelle 5
+ or al,ah
+ mov [CmdBuf+1],al ; als 2. Byte im Kommandopuffer schreiben
+
+ pop ax ; Register zurck
+
+ ret
+
+ endp
+
+;******************************************************************************
+;* Zylinder- und Sektorparameter an Kontroller ausgeben *
+;* In : BX = Startzylinder *
+;* CL = Sektorzahl/Interleave *
+;* CH = Startsektor *
+;******************************************************************************
+
+ proc SetTransParams
+
+ push ax ; Register retten
+
+ mov [CmdBuf+3],bl ; LSB Startzylinder
+ mov al,bh ; MSB Startzylinder
+ shl al,6 ; in Bit 6..7 schieben
+ add al,ch ; Sektor dazu
+ dec al ; !!! Sektoren ab 0
+ mov [CmdBuf+2],al
+ mov [CmdBuf+4],cl ; Sektorzahl
+
+ pop ax ; Register zurck
+ ret
+
+ endp
+
+;******************************************************************************
+;* Begráungsmeldung ausgeben: *
+;******************************************************************************
+
+ globproc LowLevelIdent
+
+ push ax ; Register retten
+
+ PrMsg IdentMsg
+
+ pop ax
+
+ ret
+
+IdentMsg db "Low-Level-Routinen fr WD1002S-WX2 und kompatible Controller",CR,LF,'$'
+
+ endp
+
+;******************************************************************************
+;* Controller-Diagnose: *
+;* Out : AL = Diagnosecode *
+;******************************************************************************
+
+ globproc ContDiag
+
+ push cx ; Register retten
+ push bx
+ push dx
+
+ sub cx,cx
+ mov dx,Port_Status ; auf Status
+BWait: in al,dx
+ btst al,3 ; auf NOT BUSY warten
+ loopnz BWait ; oder bis 64K Durchl„ufe durch
+ or cx,cx ; Timeout ?
+ jne NTOut
+ mov al,Diag_Timeout ; ja: Fehlercode setzen...
+ jmp End ; ...und Feierabend
+
+NTOut: call InitCmdBuf ; Kommando Selbsttest ausfhren
+ mov al,Cmd_Diagnose
+ call ExecCmd
+ call BuildStatus ; Status holen
+
+ cmp al,5 ; WD1002 definiert nur Code 0..5
+ jb DoTrans
+ mov al,7 ; "undefinierter Code"
+ jmp End
+DoTrans: lea bx,[TransTbl] ; ansonsten umsetzen
+ xlat
+
+End: pop dx ; Register zurck
+ pop bx
+ pop cx
+ ret
+
+TransTbl: db Diag_NoError ; Code 0: kein Fehler
+ db Diag_ContError ; Code 1: WD1010 fehlerhaft
+ db Diag_ECCError ; Code 2: WD11C00 fehlerhaft
+ db Diag_SBufError ; Code 3: Sektorpuffer defekt
+ db Diag_ProcError ; Code 4: WD1015 RAM defekt
+ db Diag_ProcError ; Code 5: WD1015 ROM defekt
+
+
+ endp
+
+;******************************************************************************
+;* Laufwerk rekalibrieren, gleichzeitig Test, ob vorhanden *
+;* In : AL = Laufwerk *
+;* Out : C + AX = Status *
+;******************************************************************************
+
+
+ globproc Recalibrate
+
+ push ax ; Register retten
+ push cx
+
+ call InitCmdBuf ; testen, ob Laufwerk bereit
+ mov ah,0 ; Kopf dafr unwichtig
+ call SetDriveEnv
+ mov dl,al ; Laufwerksnummer retten, gleichzeitig
+ mov dh,0 ; Kopf auf 0 setzen
+ mov al,Cmd_TestReady
+ call ExecCmd
+ call BuildStatus
+ jc TotEnde ; C=1 --> Ende mit Fehler
+
+ call InitCmdBuf ; sanfte Tour: Spur 0 mit Seek anfahren
+ mov ax,dx
+ call SetDriveEnv
+ mov al,0 ; Zylinder lo=0
+ mov [CmdBuf+3],al
+ inc al ; Zylinder Hi=0, Startsektor=1
+ mov [CmdBuf+2],al
+ mov al,Cmd_Seek
+ call ExecCmd
+ call BuildStatus
+ jnc TotEnde ; kein Fehler, alles in Butter
+
+ call InitCmdBuf ; ansonsten echtes Restore versuchen
+ mov ax,dx
+ call SetDriveEnv
+ mov al,Cmd_Restore
+ call ExecCmd
+ call BuildStatus
+ call TranslateStatus
+
+TotEnde: pop dx ; Register zurck
+ pop ax
+
+ ret
+
+ endp
+
+;******************************************************************************
+;* Dem Kontroller die Laufwerksgeometrie mitteilen *
+;* In : AL = Laufwerk *
+;* Out : C = 1-->Fehler *
+;******************************************************************************
+
+ globproc SetDriveParams
+
+ push cx ; Register retten
+ push si
+ push es
+
+ call GetPTabAdr ; Adresse Parametertabelle holen
+ call InitCmdBuf ; Kommando anstoáen
+ call SetDriveEnv
+ mov al,Cmd_SetParams
+ call ExecCmd
+
+
+ mov ax,[di+DrPar_Cyls] ; Parametertabelle aufbauen
+ xchg ah,al
+ mov word ptr [GeomBuf],ax
+ mov al,[di+DrPar_Heads]
+ mov byte ptr[GeomBuf+2],al
+ mov ax,[di+DrPar_RedWr]
+ xchg ah,al
+ mov word ptr[GeomBuf+3],ax
+ mov ax,[di+DrPar_PrComp]
+ xchg ah,al
+ mov word ptr[GeomBuf+5],ax
+ mov al,[di+DrPar_ECCLen]
+ mov byte ptr[GeomBuf+7],al
+ lea si,[GeomBuf] ; Block abschicken
+ mov cx,GeomBufSize
+ mov ax,ds
+ mov es,ax
+ call SendBlock
+ call BuildStatus
+ call TranslateStatus
+
+ pop es ; Register zurck
+ pop si
+ pop cx
+
+ ret
+
+ endp
+
+;******************************************************************************
+;* Sektor(en) lesen *
+;* In : AL = Laufwerk *
+;* AH = Startkopf *
+;* BX = Startzylinder *
+;* CL = Sektorzahl *
+;* CH = Startsektor *
+;* ES:DI = Zeiger auf Datenpuffer *
+;* Out : C+AX = Fehlerstatus *
+;******************************************************************************
+
+ globproc ReadSectors
+
+ push bx ; Register retten
+ push cx
+ push dx
+ push di
+ push es
+
+ call InitCmdBuf ; Puffer initialisieren
+ call SetDriveEnv
+ call SetTransParams
+ mov al,Cmd_Read ; Lesen triggern
+ PrChar '1'
+ call ExecCmd
+ PrChar '2'
+
+SecLoop: mov dx,Port_Status ; warten, bis Request-Bit gesetzt
+RLoop: in al,dx
+ btst al,0
+ jz RLoop
+ btst al,2 ; Daten oder Status ?
+ jnz ErrEnd ; wenn jetzt Status, ist etwas schief gelaufen
+ push cx ; ansonsten Sektor auslesen
+ mov cx,SecSize
+ PrChar '3'
+ call RecvBlock
+ PrChar '4'
+ pop cx
+ dec cl
+ add di,SecSize
+ jnz RLoop ; und n„chsten Sektor verarbeiten
+
+ErrEnd: PrChar '5'
+ call BuildStatus
+ PrChar '6'
+ call TranslateStatus
+
+ pop es ; Register zurck
+ pop di
+ pop dx
+ pop cx
+ pop bx
+
+ ret
+
+ endp
+
+;******************************************************************************
+;* Sektor(en) verifizieren *
+;* In : AL = Laufwerk *
+;* AH = Startkopf *
+;* BX = Startzylinder *
+;* CL = Sektorzahl *
+;* CH = Startsektor *
+;* Out : C+AX = Fehlerstatus *
+;******************************************************************************
+
+ globproc VeriSectors
+
+ push bx ; Register retten
+ push cx
+ push dx
+
+ call InitCmdBuf ; Puffer initialisieren
+ call SetDriveEnv
+ call SetTransParams
+ mov al,Cmd_Verify ; Verifikation triggern
+ call ExecCmd
+
+ call BuildStatus
+ call TranslateStatus
+
+ pop dx ; Register zurck
+ pop cx
+ pop bx
+
+ ret
+
+ endp
+
+;******************************************************************************
+;* Sektor(en) schreiben *
+;* In : AL = Laufwerk *
+;* AH = Startkopf *
+;* BX = Startzylinder *
+;* CL = Sektorzahl *
+;* CH = Startsektor *
+;* ES:SI = Zeiger auf Datenpuffer *
+;* Out : C+AX = Fehlerstatus *
+;******************************************************************************
+
+ globproc WriteSectors
+
+ push bx ; Register retten
+ push cx
+ push dx
+ push si
+ push es
+
+
+ call InitCmdBuf ; Puffer initialisieren
+ call SetDriveEnv
+ call SetTransParams
+ mov al,Cmd_Write ; Lesen triggern
+ call ExecCmd
+
+SecLoop: mov dx,Port_Status ; warten, bis Request-Bit gesetzt
+WLoop: in al,dx
+ btst al,0
+ jz WLoop
+ btst al,2 ; Daten oder Status ?
+ jnz ErrEnd ; wenn jetzt Status, ist etwas schief gelaufen
+ push cx ; ansonsten Sektor auslesen
+ mov cx,SecSize
+ call SendBlock
+ pop cx
+ dec cl
+ add si,SecSize
+ jnz WLoop ; und n„chsten Sektor verarbeiten
+
+ErrEnd: call BuildStatus
+ call TranslateStatus
+
+ pop es ; Register zurck
+ pop si
+ pop dx
+ pop cx
+ pop bx
+
+ ret
+
+ endp
+
+;******************************************************************************
+;* Laufwerk formatieren *
+;* In : AL = Laufwerk *
+;* AH = Interleave *
+;* Out : C+AX = Fehlerstatus *
+;******************************************************************************
+
+ globproc FormatUnit
+
+ push bx ; Register retten
+ push cx
+ push dx
+ push si
+ push di
+ push es
+
+ mov bx,ax ; Interleave & Laufwerk retten
+
+ mov ax,ds ; vorher noch den Sektorpuffer im
+ mov es,ax ; Controller ausnullen
+ lea di,[SectorBuffer]
+ mov cx,SecSize/2
+ sub ax,ax
+ rep stosw
+ call InitCmdBuf
+ mov al,Cmd_WriteBuffer
+ call ExecCmd
+ lea si,[SectorBuffer]
+ mov cx,SecSize
+ call SendBlock
+ call BuildStatus
+ jc End ; unwahrscheinlich, aber...
+
+ call InitCmdBuf ; Puffer initialisieren
+ mov al,bl ; Laufwerk wieder zurck
+ mov ah,0 ; Startkopf ist 0
+ call SetDriveEnv
+ mov [CmdBuf+4],bh ; Interleave einschreiben
+ mov al,Cmd_FormatDisk ; Formatieren triggern
+ call ExecCmd
+
+ErrEnd: call BuildStatus
+End: call TranslateStatus
+
+ pop es ; Register zurck
+ pop di
+ pop si
+ pop dx
+ pop cx
+ pop bx
+
+ ret
+
+ endp
+
+;******************************************************************************
+;* Spur formatieren *
+;* In : AL = Laufwerk *
+;* AH = Kopf *
+;* BX = Zylinder *
+;* CL = Interleave *
+;* Out : C+AX = Fehlerstatus *
+;******************************************************************************
+
+ globproc FormatTrack
+
+ push bx
+ push cx
+
+ call InitCmdBuf ; Parameter einschreiben
+ call SetDriveEnv
+ mov ch,1 ; Sektorinformation muá nur gltig sein
+ call SetTransParams
+ mov al,Cmd_FormatTrack
+ call ExecCmd
+ call BuildStatus
+
+ pop cx
+ pop bx
+ ret
+
+ endp
+
+;******************************************************************************
+;* Spur als defekt markieren *
+;* In : AL = Laufwerk *
+;* AH = Kopf *
+;* BX = Zylinder *
+;* Out : C+AX = Fehlerstatus *
+;******************************************************************************
+
+ globproc MarkBad
+
+ push bx
+ push cx
+
+ call InitCmdBuf ; Parameter einschreiben
+ call SetDriveEnv
+ mov cx,0103h ; Sektorinformation muá nur gltig sein
+ call SetTransParams
+ mov al,Cmd_FormatBad
+ call ExecCmd
+ call BuildStatus
+
+ pop cx
+ pop bx
+ ret
+
+ endp
+
+ endsection
diff --git a/tests/t_secdrive/wd1003at.inc b/tests/t_secdrive/wd1003at.inc
new file mode 100644
index 0000000..7714eec
--- /dev/null
+++ b/tests/t_secdrive/wd1003at.inc
@@ -0,0 +1,952 @@
+;******************************************************************************
+;* *
+;* Includedatei fr SECMAIN.ASM *
+;* liefert low-level-Routinen fr SecMain *
+;* Version hier fr WD1003-kompatible Kontroller: *
+;* MFM, RLL, ESDI, AT-Bus *
+;* *
+;* Historie: 28.12.1994 herberkopiert aus Hauptmodul *
+;* 30.12.1994 LowLevelIdent *
+;* 19. 1.1995 Workaround fr LCS6220 *
+;******************************************************************************
+
+ section wd1003at
+
+Base1 equ 170h ; Basisadresse Task-File
+Base2 equ 370h ; Basisadresse Floppy-Teil
+Task_Data equ Base1+0 ; Datentransferregister (R/W)
+Task_Error equ Base1+1 ; genauerer Fehlercode (R)
+Task_PreComp equ Base1+1 ; erster Zylinder Pr„komp. (/4, nur W)
+Task_SecCnt equ Base1+2 ; Zahl zu transferierender Sektoren (R/W)
+Task_SecNum equ Base1+3 ; Startsektor (R/W)
+Task_CylLo equ Base1+4 ; Startzylinder Bit 0..7 (R/W)
+Task_CylHi equ Base1+5 ; Startzylinder Bit 8..n (R/W)
+Task_DrHead equ Base1+6 ; Laufwerk/Startkopf (R/W)
+Task_Status equ Base1+7 ; Status Laufwerk & Controller (R)
+Task_Command equ Base1+7 ; Kommando Controller (W)
+Task_FDiskReg equ Base2+6 ; Bit 3=1: >8 K”pfe
+
+Cmd_Restore equ 10h ; Kommando: Rekalibrieren
+Cmd_Seek equ 70h ; Kommando: Zylinder anfahren
+Cmd_Read equ 20h ; Kommando: Sektoren lesen
+Cmd_Write equ 30h ; Kommando: Sektoren schreiben
+Cmd_Format equ 50h ; Kommando: Spur formatieren
+Cmd_Verify equ 40h ; Kommando: Sektoren auf Lesbarkeit prfen
+Cmd_Diagnose equ 90h ; Kommando: Selbsttest
+Cmd_SetParams equ 91h ; Kommando: Laufwerksparameter setzen
+
+ proc WriteParams
+
+ mov [axsave],ax
+ mov [cxsave],cx
+ PrChar ' '
+ mov ax,bx
+ mov cl,5
+ call WriteDec
+ PrChar ' '
+ mov al,byte ptr[axsave+1]
+ mov ah,0
+ mov cl,2
+ call WriteDec
+ PrChar ' '
+ mov al,byte ptr[cxsave+1]
+ mov ah,0
+ mov cl,2
+ call WriteDec
+ PrChar ' '
+ mov al,byte ptr[cxsave]
+ mov ah,0
+ mov cl,2
+ call WriteDec
+ PrChar ' '
+ mov ax,es
+ mov cl,4
+ call WriteHex
+ PrChar ':'
+ mov ax,bp
+ mov cl,4
+ call WriteHex
+ mov ax,[axsave]
+ mov cx,[cxsave]
+ ret
+
+cxsave dw ?
+axsave dw ?
+
+ endp
+
+;******************************************************************************
+;* Workaround fr LCS6220: Wird direkt nach dem Einschalten ein Seek ausge- *
+;* fhrt, gibt der Kontroller f„lschlicherweise Daten aus und blockiert alle *
+;* weiteren Kommandos. Diese Routine r„umt einfach den Puffer leer... *
+;******************************************************************************
+
+ proc ClearBuffer
+
+ push dx ; Register retten
+ push ax
+
+RdLoop: mov dx,Task_Status ; Bit 3 noch gesetzt ?
+ in al,dx
+ btst al,3
+ jz RdLoopEnd ; nein --> fertig
+ mov dx,Task_Data
+ in ax,dx
+ jmp RdLoop
+RdLoopEnd:
+ pop ax ; Register zurck
+ pop dx
+
+ ret
+
+ endp
+
+;******************************************************************************
+;* Interleave-Tabelle berechnen *
+;* In : AL = Sektorzahl *
+;* AH = Interleave *
+;* DH = Bad-Flag *
+;******************************************************************************
+
+ proc SetInterleaveBuffer
+
+ pusha ; Register retten
+ push es
+
+ push ax ; Sektorpuffer initialisieren
+ mov ax,ds
+ mov es,ax
+ sub ax,ax
+ lea di,[SectorBuffer]
+ mov cx,SecSize/2
+ cld
+ rep stosw
+ pop ax
+
+ sub di,di ; DI=Adresse in Puffer=(phys. Sektor-1)*2
+ mov dl,dh ; DL = Bad-Flag
+ mov dh,1 ; DH=log. Sektornummer
+ mov cl,al ; CX=Schleifenz„hler
+ mov ch,0
+ mov bl,al ; Sektorzahl*2 nach BX
+ mov bh,0
+ add bx,bx
+ mov si,ax ; Interleave*2 nach SI
+ shr si,8
+ add si,si
+InterLoop: cmp byte ptr SectorBuffer[di],0 ; Eintrag frei ?
+ je Inter_FreeFound ; ja, beenden
+ add di,2 ; nein, linear weitersuchen
+ cmp di,bx
+ jb InterLoop
+ mov di,0 ; Wrap-Around bercksichtigen
+ jmp InterLoop
+Inter_FreeFound:mov word ptr SectorBuffer[di],dx ; Sektor einschreiben
+ add di,si ; Interleave-Sprung dazu
+ cmp di,bx ; Modulo Sektorzahl
+ jb Inter_NoWrap
+ sub di,bx
+Inter_NoWrap: inc dh ; n„chster log. Sektor
+ loop InterLoop
+
+ pop es ; Register zurck
+ popa
+
+ ret
+
+ endp
+
+;******************************************************************************
+;* Laufwerk und Sonderwerte einprogrammieren *
+;* In : AL = Laufwerk *
+;* AH = Kopf *
+;******************************************************************************
+
+ proc SetDriveEnv
+
+ push di ; Register retten
+ push dx
+ mov dx,ax ; Laufwerk/Kopf retten
+
+ call GetPTabAdr ; Tabellenadresse holen
+
+ mov al,dl ; Laufwerk und Kopf zusammenbauen
+ shl al,4
+ or al,dh
+ or al,0a0h
+ mov dx,Task_DrHead
+ out dx,al
+ mov ax,[di+DrPar_PrComp] ; Startzylinder Pr„kompensation
+ shr ax,2
+ mov dl,Lo(Task_PreComp)
+ out dx,al
+ mov al,[di+DrPar_CByte] ; Wert fr Fixed Disk Register
+ mov dx,Task_FDiskReg
+ out dx,al
+ call WaitBusy
+
+ clc ; Ende ohne Fehler
+ pop dx
+ pop di
+ ret
+
+ endp
+
+;******************************************************************************
+;* Zylinder- und Sektorparameter an Kontroller ausgeben *
+;* In : BX = Startzylinder *
+;* CL = Sektorzahl *
+;* CH = Startsektor *
+;******************************************************************************
+
+ proc SetTransParams
+
+ push dx ; Register retten
+
+ mov dx,Task_CylLo ; Startzylinder programmieren
+ mov al,bl
+ out dx,al
+ mov dx,Task_CylHi ;***
+ mov al,bh
+ out dx,al
+ mov dx,Task_SecNum ; Startsektor... ;***
+ mov al,ch
+ out dx,al
+ mov dx,Task_SecCnt ; ...und Sektorzahl ;***
+ mov al,cl
+ out dx,al
+
+ pop dx ; Register zurck
+ ret
+
+ endp
+
+;******************************************************************************
+;* warten, bis Controller bereit oder Fehler *
+;* Out : AL = letzter Status *
+;******************************************************************************
+
+ proc WaitBusy
+
+ push dx ; Register retten
+ mov dx,Task_Status ; auf Statusregister
+Loop: in al,dx ; Status lesen
+ btst al,7 ; Bit 7 noch gesetzt ?
+ jnz Loop ; ja--> weiter pollen
+ pop dx ; Register zurck
+ ret
+
+ endp
+
+;******************************************************************************
+;* warten, bis Laufwerk bereit *
+;* Out : AL = letzter Status *
+;******************************************************************************
+
+ proc WaitDrive
+
+ push dx ; Register retten
+ mov dx,Task_Status ; auf Statusregister
+Loop: in al,dx ; Status lesen
+ btst al,7 ; Bit 7 = 0 ? ( Controller Busy )
+ jnz Loop
+ btst al,6 ; Bit 6 = 1 ? ( Drive not Ready )
+ jz Loop
+ btst al,4 ; Bit 4 = 1 ? ( Seek not complete )
+ jz Loop
+ pop dx
+ ret
+
+ endp
+
+;******************************************************************************
+;* warten, bis Datentransfer erforderlich *
+;* Out : AL = letzter Status *
+;* C = 1, falls Fehler *
+;******************************************************************************
+
+ proc WaitData
+
+ push dx ; Register retten
+ mov dx,Task_Status ; auf Statusregister
+Loop: in al,dx ; Status lesen
+ btst al,7 ; Bit 7 = 0 ?
+ jnz Loop
+ btst al,3 ; Bit 3 = 1 ?
+ jz Loop
+ pop dx ; Register zurck
+ ret
+
+ endp
+
+;******************************************************************************
+;* Status bilden *
+;* Out : C+AX = Status *
+;******************************************************************************
+
+ proc BuildError
+
+ push dx ; Register retten
+
+ mov dx,Task_Status ; Statusregister lesen
+ in al,dx
+ mov ah,al
+ btst ah,0 ; Fehlerflag gesetzt ?
+ clc
+ jz End ; kein Fehler
+
+ mov dx,Task_Error ; ja: Error-Register lesen ;***
+ in al,dx
+ stc
+
+End: pop dx ; Register zurck
+ ret
+
+ endp
+
+;******************************************************************************
+;* Begráungsmeldung ausgeben: *
+;******************************************************************************
+
+ globproc LowLevelIdent
+
+ push ax ; Register retten
+
+ PrMsg IdentMsg
+
+ pop ax
+
+ ret
+
+IdentMsg db "Low-Level-Routinen fr WD1003-WAH und kompatible Controller",CR,LF,'$'
+
+ endp
+
+;******************************************************************************
+;* Controller-Diagnose: *
+;* Out : AL = Diagnosecode *
+;******************************************************************************
+
+ globproc ContDiag
+
+ push cx ; Register retten
+ push dx
+
+ mov dx,Task_Status ; das erste Mal mit Timeout warten
+ sub cx,cx
+BWait: in al,dx
+ btst al,7 ; auf NOT BUSY warten
+ loopnz BWait ; oder bis 64K Durchl„ufe durch
+ or cx,cx ; Timeout ?
+ jne NTOut
+ mov al,Diag_Timeout ; ja: Fehlercode setzen...
+ jmp End ; ...und Feierabend
+
+NTOut: mov al,CMD_Diagnose ; Selbsttest starten
+ mov dl,Lo(Task_Command)
+ out dx,al
+ call WaitBusy ; auf Fertigstellung warten
+ mov dl,Lo(Task_Error) ; Ergebnis laden
+ in al,dx
+
+End: pop dx ; Register zurck
+ pop cx
+ ret
+
+ endp
+
+;******************************************************************************
+;* Dem Kontroller die Laufwerksgeometrie mitteilen *
+;* In : AL = Laufwerk *
+;* Out : C = 1-->Fehler *
+;******************************************************************************
+
+ globproc SetDriveParams
+
+ push di ; Register retten
+ push dx
+ mov dl,al ; Laufwerk retten
+
+ call GetPTabAdr ; Adresse Parametertabelle holen
+
+ call WaitBusy ; Kontroller muá frei sein
+
+ mov al,dl ; Kopfzahl/Laufwerk vorbesetzen
+ shl al,4
+ mov ah,[di+DrPar_Heads]
+ dec ah ; Maximalnummer anstelle Gesamtzahl
+ or al,ah
+ or al,0a0h
+ mov dx,Task_DrHead
+ out dx,al
+ mov dl,Lo(Task_SecCnt) ; Sektorzahl setzen
+ mov al,[di+DrPar_NSecs]
+ out dx,al
+
+ mov dl,Lo(Task_Command) ; Parameter bertragen
+ mov al,Cmd_SetParams
+ out dx,al
+
+ call WaitBusy ; auf Fertigstellung warten
+
+ clc ; Ende ohne Fehler
+ pop dx
+ pop di
+ ret
+
+ endp
+
+;******************************************************************************
+;* Laufwerk rekalibrieren, gleichzeitig Test, ob vorhanden *
+;* In : AL = Laufwerk *
+;* Out : C + AX = Status *
+;******************************************************************************
+
+ globproc Recalibrate
+
+ push cx ; Register retten
+ push dx
+
+ mov cx,ax ; Laufwerk retten
+ call WaitBusy ; warten, bis Controller frei
+
+ mov dx,Task_DrHead ; Laufwerk eintragen
+ mov al,cl
+ shl al,4
+ add al,0a0h
+ out dx,al
+
+ mov dl,Lo(Task_Status) ; Laufwerk muss jetzt bereit sein,
+ in al,dx ; da sich einige Kontroller sonst im
+ and al,50h ; folgenden aufh„ngen, falls
+ cmp al,50h ; keine Platte angeschlossen ist.
+ stc ; falls nicht bereit, Fehler simulieren
+ mov al,4 ; "Aborted Command"
+ jne TotEnde
+ mov al,0
+ mov dl,Lo(Task_CylLo) ; erstmal auf die sanfte Tour:
+ out dx,al ; Spur 0 anfahren
+ mov dl,Lo(Task_CylHi)
+ out dx,al
+ mov dl,Lo(Task_Command)
+ mov al,Cmd_Seek
+ out dx,al
+ call WaitBusy
+ call BuildError
+ jnc Ende ; wenn OK: fertig
+
+ call ClearBuffer ; falls sich der Longshine verheddert...
+ mov dl,Lo(Task_Command) ; 2. Anlauf: echtes Restore
+ mov al,Cmd_Restore
+ out dx,al
+
+ call WaitBusy ; auf Controller warten
+
+Ende: call BuildError ; Status einlesen
+TotEnde:
+ pop dx ; Register zurck
+ pop cx
+ ret
+
+ endp
+
+;******************************************************************************
+;* Sektor(en) lesen *
+;* In : AL = Laufwerk *
+;* AH = Startkopf *
+;* BX = Startzylinder *
+;* CL = Sektorzahl *
+;* CH = Startsektor *
+;* ES:DI = Zeiger auf Datenpuffer *
+;* Out : C+AX = Fehlerstatus *
+;******************************************************************************
+
+ globproc ReadSectors
+
+ push si ; Register sichern
+ push dx
+ push bp
+
+ if debug
+ PrChar 'R'
+ mov bp,di
+ call WriteParams
+ endif
+
+ sub bp,bp ; Fehlerz„hler auf 0
+
+Retry: push ax ; Parameter sichern
+ push bx
+ push cx
+ push di
+
+ mov si,ax ; Laufwerk/Kopf retten
+ call WaitBusy ; warten, bis Ruhe im Wald
+
+ mov ax,si
+ call SetDriveEnv ; Laufwerk jetzt schon setzen, damit
+ ; korr. Ready-Signal abgefragt wird
+ call WaitDrive ; bis Laufwerk bereit
+
+ call SetTransParams ; restliche Parameter ausgeben
+
+ mov ch,0 ; Sektorzahl nach SI
+ mov si,cx
+ mov dx,Task_Command ; Kommando triggern
+ mov al,Cmd_Read
+ out dx,al
+
+ mov dx,Task_Data ; Vorbereitung fr INSW
+ cld
+Loop: call WaitBusy ; auf gelesenen Sektor warten
+ btst al,0 ; Fehler ?
+ jnz Again ; -->neu aufsetzen
+ call WaitData
+ btst al,0
+ jnz Again
+ call WaitDrive
+ btst al,0
+ jnz Again
+ mov cx,SecSize/2 ; Daten transferieren
+ rep insw ; bagger, schaufel
+ dec si ; n„chster Sektor
+ jnz Loop
+
+End: pop di ; Parameter nicht mehr gebraucht
+ pop cx
+ pop bx
+ pop ax
+Term: if debug
+ PrChar CR
+ PrChar LF
+ endif
+ call BuildError
+ pop bp
+ pop dx
+ pop si
+
+ ret
+
+Again: inc bp ; Fehlerz„hler rauf
+ cmp bp,MaxRetry ; zu oft aufgetreten ?
+ jae End
+
+ pop di ; nein: Parameter vom Stack
+ pop cx
+ pop bx
+ pop ax
+ mov si,ax ; Laufwerk retten
+ call Recalibrate ; auf Spur 0 zurck
+ jc Term ; bei erneutem Fehler Abbruch
+ mov ax,si
+ call SetDriveParams ; Parameter neu initialisieren
+ mov ax,si
+ jmp Retry ; neuer Versuch
+
+
+ endp
+
+;******************************************************************************
+;* Sektor(en) verifizieren *
+;* In : AL = Laufwerk *
+;* AH = Startkopf *
+;* BX = Startzylinder *
+;* CL = Sektorzahl *
+;* CH = Startsektor *
+;* Out : C+AX = Fehlerstatus *
+;******************************************************************************
+
+ globproc VeriSectors
+
+ push si ; Register sichern
+ push dx
+ push bp
+
+ if debug
+ PrChar 'V'
+ mov bp,0
+ call WriteParams
+ endif
+
+ sub bp,bp ; Fehlerz„hler auf 0
+
+Retry: push ax ; Parameter sichern
+ push bx
+ push cx
+
+ mov si,ax ; Laufwerk/Kopf retten
+ call WaitBusy ; warten, bis Ruhe im Wald
+
+ mov ax,si
+ call SetDriveEnv ; Laufwerk jetzt schon setzen, damit
+ ; korr. Ready-Signal abgefragt wird
+ call WaitDrive ; bis Laufwerk bereit
+
+ call SetTransParams ; restliche Parameter ausgeben
+
+ mov dx,Task_Command ; Kommando triggern
+ mov al,Cmd_Verify
+ out dx,al
+
+ call WaitBusy ; auf Fertigstellung warten
+ mov cx,16 ; einige Kontroller brauchen
+DelStat: loop DelStat ; etwas fr das Fehlerflag
+ mov dx,Task_Status
+ in al,dx
+ btst al,0 ; Fehler ?
+ jnz Again ; -->neu aufsetzen
+ call WaitDrive
+ btst al,0
+ jnz Again
+
+Ende: pop cx ; Parameter nicht mehr gebraucht
+ pop bx
+ pop ax
+Term: if debug
+ PrChar CR
+ PrChar LF
+ endif
+ call BuildError
+ pop bp
+ pop dx
+ pop si
+
+ ret
+
+Again: inc bp ; Fehlerz„hler rauf
+ cmp bp,MaxRetry ; zu oft aufgetreten ?
+ jae Ende
+
+ pop cx ; nein: Parameter vom Stack
+ pop bx
+ pop ax
+ mov si,ax ; Laufwerk retten
+ call Recalibrate ; auf Spur 0 zurck
+ jc Term ; bei erneutem Fehler Abbruch
+ mov ax,si
+ call SetDriveParams ; Parameter neu initialisieren
+ mov ax,si
+ jmp Retry ; neuer Versuch
+ mov ax,si
+ endp
+
+;******************************************************************************
+;* Sektor(en) schreiben *
+;* In : AL = Laufwerk *
+;* AH = Startkopf *
+;* BX = Startzylinder *
+;* CL = Sektorzahl *
+;* CH = Startsektor *
+;* ES:SI = Zeiger auf Datenpuffer *
+;* Out : C+AX = Fehlerstatus *
+;******************************************************************************
+
+ globproc WriteSectors
+
+ push di ; Register sichern
+ push dx
+ push bp
+
+ if debug
+ PrChar 'W'
+ mov bp,si
+ call WriteParams
+ endif
+
+ xor bp,bp ; Fehlerz„hler auf 0
+
+Retry: push ax ; Parameter sichern
+ push bx
+ push cx
+ push si
+
+ mov di,ax ; Laufwerk/Kopf retten
+ call WaitBusy ; warten, bis Ruhe im Wald
+
+ mov ax,di
+ call SetDriveEnv ; Laufwerk jetzt schon setzen, damit
+ ; korr. Ready-Signal abgefragt wird
+ call WaitDrive ; bis Laufwerk bereit
+
+ call SetTransParams ; restliche Parameter ausgeben
+
+ mov ch,0 ; Sektorzahl nach DI
+ mov di,cx
+ mov dx,Task_Command ; Kommando triggern
+ mov al,Cmd_Write
+ out dx,al
+
+ mov dx,Task_Data ; Vorbereitung fr OUTSW
+ cld
+Loop: call WaitBusy ; auf Datenbereitschaft warten
+ btst al,0 ; Fehler ?
+ jnz Again ; ja-->neu aufsetzen
+ call WaitData
+ btst al,0
+ jnz Again
+ call WaitDrive
+ btst al,0
+ jnz Again
+ mov cx,SecSize/2 ; Daten transferieren
+ seges
+ rep outsw ; bagger, schaufel
+ call WaitBusy ; warten, bis Transfer fertig
+ btst al,0
+ jnz Again
+ dec di ; n„chster Sektor
+ jnz Loop
+
+End: pop si ; Parameter nicht mehr gebraucht
+ pop cx
+ pop bx
+ pop ax
+Term: if debug
+ PrChar CR
+ PrChar LF
+ endif
+ call BuildError
+ pop bp
+ pop dx
+ pop di
+
+ ret
+
+Again: inc bp ; Fehlerz„hler rauf
+ cmp bp,MaxRetry ; zu oft aufgetreten ?
+ jae End
+
+ pop si ; nein: Parameter vom Stack
+ pop cx
+ pop bx
+ pop ax
+ mov di,ax ; Laufwerk retten
+ call Recalibrate ; auf Spur 0 zurck
+ jc Term ; bei erneutem Fehler Abbruch
+ mov ax,di
+ call SetDriveParams ; Parameter neu initialisieren
+ mov ax,di
+ jmp Retry ; neuer Versuch
+
+ endp
+
+;******************************************************************************
+;* Laufwerk formatieren *
+;* In : AL = Laufwerk *
+;* AH = Interleave *
+;* Out : C+AX = Fehlerstatus *
+;******************************************************************************
+
+ globproc FormatUnit
+
+ push bx
+ push cx
+ push dx
+ push si
+ push di
+ push bp
+
+ mov bx,ax ; Interleave retten
+ PrMsg ESCMsg
+ mov ax,bx
+ call GetPTabAdr ; Parametertabelle->DI
+ mov ax,bx
+ mov dh,0 ; gute Spuren schreiben
+ mov al,[di+DrPar_NSecs]
+ call SetInterleaveBuffer ; Tabelle berechnen
+ mov ax,bx
+ call Recalibrate ; Kontroller reinitialisieren
+ jc Fin
+ mov ax,bx
+ mov bp,[di+DrPar_Cyls] ; Zylinderz„hler in BP (abw„rts)
+ dec bp
+ mov dl,al ; Laufwerk in DL
+ cld
+CylLoop: mov dh,0 ; Kopf in dh
+HeadLoop: call WaitBusy ; warten, bis WD1003 frei
+ call WriteCoords ; Bildschirmausgabe
+ mov ax,dx ; Laufwerk+Kopf progr.
+ call SetDriveEnv
+ mov bx,bp ; Zylinder+Sektor progr.
+ mov cl,[di+DrPar_NSecs]
+ mov ch,1
+ call SetTransParams
+ mov bx,dx
+ mov dx,Task_Command
+ mov al,Cmd_Format
+ out dx,al
+ call WaitData ; Sektortabelle schicken
+ mov cx,SecSize/2
+ mov dx,Task_Data
+ lea si,[SectorBuffer]
+ rep outsw
+ call WaitBusy ; warten, bis Kontroller fertig
+ shr al,1 ; Fehlerbit in Carry laden
+ jnc GoOn
+ PrMsg ErrorMsg ; falls Fehler, Meldung ausgeben
+ mov dx,bx
+ call WriteCoords
+ PrChar LF
+GoOn: mov dx,bx ; Laufwerk und Kopf zurck
+ call BreakOnESC ; will der Benutzer abbrechen ?
+ jc UserTerm ; ja, Abbruch
+ inc dh ; n„chster Kopf
+ cmp dh,[di+DrPar_Heads]
+ jb HeadLoop
+ dec bp ; n„chster Zylinder
+ jns CylLoop
+TermLoop: mov al,dl ; damit die Seek-Rate wieder stimmt
+ call Recalibrate
+
+Fin: push ax ; Fehlerstatus halten
+ pushf
+ PrChar LF
+ popf ; Fehlerstatus zurck
+ pop ax
+ pop bp
+ pop di
+ pop si
+ pop dx
+ pop cx
+ pop bx
+ ret
+
+UserTerm: mov al,dl ; Abbruch durch Benutzer: noch schnell
+ call Recalibrate ; rekalibrieren
+ jc Fin ; Fehler dabei ?
+ stc ; Ansonsten Sonderfehlercode
+ mov al,DErr_UserTerm
+ jmp Fin
+
+WriteCoords: push ax ; Kopf/Zylinder ausgeben
+ push cx
+
+ PrMsg CylMsg
+ mov ax,bp
+ mov cl,6
+ call WriteDec
+ PrMsg HeadMsg
+ mov al,dh
+ mov ah,0
+ mov cl,3
+ call WriteDec
+ PrChar CR
+
+ pop cx
+ pop ax
+ ret
+
+ESCMsg: db "Abbruch mit <ESC>",CR,LF,'$'
+CylMsg: db "Zylinder $"
+HeadMsg: db ", Kopf $"
+ErrorMsg: db "Formatierfehler auf $"
+
+ endp
+
+;******************************************************************************
+;* Spur formatieren *
+;* In : AL = Laufwerk *
+;* AH = Kopf *
+;* BX = Zylinder *
+;* CL = Interleave *
+;* Out : C+AX = Fehlerstatus *
+;******************************************************************************
+
+ globproc FormatTrack
+
+ push bx ; Register retten
+ push cx
+ push dx
+ push si
+ push di
+ push bp
+
+ mov bp,ax ; Laufwerk & Kopf retten
+ call Recalibrate ; Laufwerk sicherheitshalber rekalibrieren
+ mov ax,bp
+ call GetPTabAdr ; Sektortabelle aufbauen
+ mov dh,0 ; fehlerhafte Sektoren schreiben
+ mov ah,cl ; Interleave vorgeben
+ mov al,[di+DrPar_NSecs]
+ call SetInterleaveBuffer
+ mov ax,bp ; Laufwerk und Kopf zurck
+ call SetDriveEnv ; in Kontroller einprogrammieren
+ mov cl,[di+DrPar_NSecs] ; Sektor& Zylinder einschreiben
+ mov ch,1
+ call SetTransParams
+ mov dx,Task_Command ; Kommando schicken
+ mov al,Cmd_Format
+ out dx,al
+ call WaitData ; Sektortabelle schicken
+ mov cx,SecSize/2
+ mov dx,Task_Data
+ lea si,[SectorBuffer]
+ rep outsw
+ call WaitBusy ; warten, bis Kontroller fertig
+ jc Fin ; Abbruch bei Fehler
+ mov ax,bp ; Laufwerk nochmal rekalibrieren
+ call Recalibrate ; damit Steprate stimmt
+
+Fin: pop bp
+ pop di
+ pop si
+ pop dx
+ pop cx
+ pop bx
+ ret
+
+ endp
+
+;******************************************************************************
+;* Spur als defekt markieren *
+;* In : AL = Laufwerk *
+;* AH = Kopf *
+;* BX = Zylinder *
+;* Out : C+AX = Fehlerstatus *
+;******************************************************************************
+
+ globproc MarkBad
+
+ push bx ; Register retten
+ push cx
+ push dx
+ push si
+ push di
+ push bp
+
+ mov bp,ax ; Laufwerk & Kopf retten
+ call Recalibrate ; Laufwerk sicherheitshalber rekalibrieren
+ mov ax,bp
+ call GetPTabAdr ;Sektortabelle aufbauen
+ mov dh,80h ; fehlerhafte Sektoren schreiben
+ mov ah,3 ; Interleave ist ziemlich egal...
+ mov al,[di+DrPar_NSecs]
+ call SetInterleaveBuffer
+ mov ax,bp ; Laufwerk und Kopf zurck
+ call SetDriveEnv ; in Kontroller einprogrammieren
+ mov cl,[di+DrPar_NSecs] ; Sektor& Zylinder einschreiben
+ mov ch,1
+ call SetTransParams
+ mov dx,Task_Command ; Kommando schicken
+ mov al,Cmd_Format
+ out dx,al
+ call WaitData ; Sektortabelle schicken
+ mov cx,SecSize/2
+ mov dx,Task_Data
+ lea si,[SectorBuffer]
+ rep outsw
+ call WaitBusy ; warten, bis Kontroller fertig
+ jc Fin ; Abbruch bei Fehler
+ mov ax,bp ; Laufwerk nochmal rekalibrieren
+ call Recalibrate ; damit Steprate stimmt
+
+Fin: pop bp
+ pop di
+ pop si
+ pop dx
+ pop cx
+ pop bx
+ ret
+
+ endp
+
+ endsection