From 333b605b2afd472b823aeda0adf0e8b1ea9843c0 Mon Sep 17 00:00:00 2001 From: fishsoupisgood Date: Mon, 27 May 2019 02:41:51 +0100 Subject: initial commit from asl-1.41r8.tar.gz --- tests/t_secdrive/asflags | 0 tests/t_secdrive/lowlevel.inc | 1 + tests/t_secdrive/secparam.inc | 1619 +++++++++++++++++++++++++++++++++++++++ tests/t_secdrive/t_secdrive.asm | 1476 +++++++++++++++++++++++++++++++++++ tests/t_secdrive/t_secdrive.doc | 9 + tests/t_secdrive/t_secdrive.ori | Bin 0 -> 11271 bytes tests/t_secdrive/wd1002xt.inc | 773 +++++++++++++++++++ tests/t_secdrive/wd1003at.inc | 952 +++++++++++++++++++++++ 8 files changed, 4830 insertions(+) create mode 100644 tests/t_secdrive/asflags create mode 120000 tests/t_secdrive/lowlevel.inc create mode 100644 tests/t_secdrive/secparam.inc create mode 100644 tests/t_secdrive/t_secdrive.asm create mode 100644 tests/t_secdrive/t_secdrive.doc create mode 100755 tests/t_secdrive/t_secdrive.ori create mode 100644 tests/t_secdrive/wd1002xt.inc create mode 100644 tests/t_secdrive/wd1003at.inc (limited to 'tests/t_secdrive') diff --git a/tests/t_secdrive/asflags b/tests/t_secdrive/asflags new file mode 100644 index 0000000..e69de29 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 ",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 ",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<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 Binary files /dev/null and b/tests/t_secdrive/t_secdrive.ori 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 ",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 -- cgit v1.2.3