\ **************************************************** \ * * \ * BOOTMAST.FTH * \ * * \ * Zutaten fuer FAT-Reparatur und Bootmaster unter * \ * Turbo-FORTH-83. Auch fuer ZF geeignet. * \ * * \ * Fred Behringer - Forth-Gesellschaft - 8.8.2008 * \ * * \ **************************************************** \ ==================================================== \ Bei Arbeiten mit ZF: \ zf fload bootmast.fth - .fth nicht vergessen! \ attributs off wegnehmen! attributs in ZF unbekannt. \ Ansonsten scheint auch unter ZF alles zu gehen. \ ==================================================== attributs off \ Fuer den Fall, dass kein ANSI.SYS in der CONFIG.SYS ist. \ Bei Arbeiten mit ZF wegnehmen ! hex 210 allot here \ Platz fuer mind. 1 Sektor = 512d Bytes here 0f and - \ sectbuf an Paragraphenanfang 200 - \ Anfang des Sektorpuffers constant sectbuf \ Liefert Adresse des Sektorpuffers \ Sektor lesen: cx = Spur/Sektor-Kombination \ cx = Bits F-0 = FEDCBA98 76543210 : Spur = 76FEDCBA98 : Sektor = 543210 \ = ch cl : 76(cl) &ch : von cl code (getsect) ( seite spur/sektor -- ) ds push \ ds --> es es pop 80 # dl mov \ dl = erste Festplatte cx pop \ Kombination aus Spur (track #) und Sektor ax pop al dh mov \ dh = Seitennummer (head #) sectbuf # bx mov \ bx auf den Anfang des Puffers setzen. 201 # ax mov \ Einen physikalischen Sektor lesen 13 int \ HD-Interrupt aufrufen next end-code \ Sektor schreiben: cx = Spur/Sektor-Kombination (wie unter "Sektor lesen"): \ Spur = ch mit vorangesetzten Bits 7-6 von cl, Sektor = Bits 5-0 von cl code (putsect) ( seite spur/sektor -- ) ds push \ ds --> es es pop 80 # dl mov \ dl = erste Festplatte cx pop \ Kombination aus Spur (track#) und Sektor ax pop al dh mov \ dh = Seitennummer (head #) sectbuf # bx mov \ bx auf den Anfang des Puffers setzen. 301 # ax mov \ Einen physikalischen Sektor schreiben. 13 int \ HD-Interrupt aufrufen next end-code \ 10 Bit Spur und 6 Bit Sektor --> 16 Bit Spur/Sektor \ In dieser Codierung steht es im Master-Boot-Record \ und so wird es in Int 13h, 2/3 in cx verlangt. code sp,sc>spsc ( sp sc -- spsc ) ax pop \ Sektoreingabe 3f # ax and \ Sektor = 6 niederwertige Bits von al bx pop \ Spureingabe 6 # cl mov \ Spur = Bit 6,7 von al vorn an bl bh cl shl \ um 6 Bit nach links bh al or \ Spurbits 6,7 nach Sektorbyte bl ah mov \ Beides in ax sammeln ax push \ und gemeinsam zum Stack next end-code \ Umkehrung von sp,sc>spsc. Weitere Erklaerungen dort. code spsc>sp,sc ( spsc -- sp sc ) ax pop \ Eingabe (und Aufbewahrung in ax) ax bx mov \ der Spur/Sektor-Kombination 6 # cl mov bl cl shr \ um 6 Bit nach rechts bl dh mov \ Spurbits 6,7 nach bits 0,1 von dh ah dl mov \ Spur dx zu 10 Bits ergaenzen dx push \ Spur auf Stack 3f # ax and \ Sektor = 6 niederwertige Bits von al ax push \ Sektor auf Stack next end-code \ MBR lesen und nach sectbuf speichern \ Die Partitionstabelle beginnt bei Adresse 1be. \ Der MBR endet mit den Bytes 55 aa. : getmbr ( -- ) 0 1 (getsect) ; \ Inhalt von sectbuf (gaanz vorsichtig!) in den MBR der Festplatte schreiben. : putmbr ( -- ) 0 1 (putsect) ; \ Sektor-Puffer am Bildschirm anzeigen : showsectbuf ( -- ) sectbuf 200 dump ; \ Nur 100 Bytes anzeigen : showsectbuf100 ( -- ) sectbuf 100 dump ; \ Sektoradresse s-ad (Spur/Sektor-Kombination wie bei (getsect)) des \ n-ten logischen Laufwerks (der erweiterten Partition) auf den Stack \ holen und auch den zugehoerigen Sektor nach sectbuf speichern. Die \ Partitionstabelle beginnt bei Parttab-Offset 1be; ansonsten hat der \ Parttab-Sektor bis auf die beiden Bytes 55 aa am Ende nur Nullen. \ Zur (besser aufbereiteten) Anzeige der Partitionstabelle des logischen \ Laufwerks n kann man (nach entsprechender Um-Interpretation) auch \ (showparttab) verwenden. Man verwechsle die Partitionstabellen der logischen \ Laufwerke der erweiterten Partition nicht mit derem jeweiligen Bootsektor. \ Die Partitionstabellen der logischen Laufwerke entsprechen dem entsprechenden \ (und am selben Platz liegenden) Teil des MBRs (der gesamten Festplatte). : getpart ( n -- s-ad ) \ n = 1 -> 1. logisches Laufwerk, usw. getmbr sectbuf 1b2 + \ Ausgangsposition im Puffer 4 0 \ 4 relevante Zeilen im MBR do 10 + dup c@ 5 = \ Schon erweiterte Partition? if 2 - @ leave then \ Ja, dann s-ad holen und raus. loop \ ( n s-ad(1) ) begin \ k = 0 ... 0 over \ ( n-k s-ad(k+1) 0 s-ad(k+1) ) (getsect) \ ( n-k s-ad(k+1) ) swap 1 - >r \ ( s-ad(k+1) ) sectbuf 1d0 + @ \ ( s-ad(k+1) s-ad(k+2)? ) dup 0= \ ( s-ad(k+1) s-ad(k+2) fl ) if drop 1 \ ( s-ad(k+1) 1 ) else nip 0 \ ( s-ad(k+2) 0 ) then \ ( s-ad(k+?) 0/1 ) r@ \ ( s-ad(k+?) 0/1 n-k-1 ) -rot r> \ ( n-k-1 s-ad(k+?) 0/1 n-k-1 ) 0= \ ( s-ad(k+?) 0/1 fl ) or \ ( s-ad(k+?) fl ) until \ parttab(n) jetzt in sectbuf drop sectbuf 1c0 + @ \ ( fl s-ad(n) ) swap if cr ." Letztes Ext-Laufwerk schon erreicht!" then ; \ Partitionstabelle eines logischen Laufwerks (der erweiterten Partition) aus \ dem Puffer sectbuf holen und an "richtiger Stelle" (nach s-ad) auf die Platte \ zurueckschreiben. s-ad ist derjenige Wert auf dem Stack, der nach Aufruf von \ getpart dort abgelegt wurde. s-ad enthaelt Spur und Sektor in der Codierung \ des Interrupts 13h. Die 0 in putpart entspricht der Seitennummer 0. \ Das Paar n getpart und putpart dient also der Reparatur einer verunglueckten \ Partitionstabelle eines logischen Laufwerks der erweiterten Partition - soweit \ eine solche ueberhaupt vorhanden ist. : putpart ( s-ad -- ) 0 swap (putsect) ; \ Aeusserste Vorsicht ! \ Bootsektor des logischen Laufwerks n (1 = erstes Laufwerk der \ erweiterten Partition usw) holen und in den Sektorpuffer schreiben \ Der Bootsektor des logischen Laufwerks n der erweiterten Partition hat nichts \ mit der Partitionstabelle des logischen Laufwerks zu tun! Voellig falsch waere \ es, sich nach n getboot irgendwelche brauchbaren Daten per (showparttab) \ anzeigen lassen zu wollen (siehe dort). : getboot ( n -- ) getpart 1 swap (getsect) ; \ Partition verstecken. Zunaechst nur in sectbuf (Sektorpuffer). MBR muss \ schon per getmbr in den Puffer geschrieben worden sein. Zum \ Wirksamwerdenlassen dann mit putmbr abschliessen! Vorsicht bei der erweiterten \ Partition! Es ist die Frage, ob ein Verstecken der erweiterten Partition \ sinnvoll ist. Das Verstecken von Partitionen mit zweistelligen \ Dateisystemkennungen wird hier nicht erlaubt. Davon betroffen sind \ insbesondere 82 (Linux swap) und 83 (Linux native). Windows ME auf FAT32-Basis \ hat die Kennung 0C und wird von hidepart voll einbezogen. Windows XP hat (bei \ der ueblichen NTFS-Basis) die Kennung 07 und wird von hidepart ebenfalls voll \ einbezogen. \ Achtung: Es wird bei hidepart, hideall und hideall-ext davon ausgegangen, dass \ Linux, falls vorhanden, in der erweiterten Partition liegt, dass Linux also in \ der Partitionstabelle des MBRs nicht in Erscheinung tritt. Andernfalls wuerde \ der hier verwendete Mechanismus des Versteckens oder Sichtbarmachens nicht \ greifen. Diese Dinge muessen unbedingt noch genauer untersucht werden. \ Alle Operationen spielen sich "nur" im Sektorpuffer sectbuf ab. Sie muessen \ dann noch per putmbr auf die Festplatte geschrieben und durch Neubooten \ des Computers wirksam gemacht werden. : hidepart ( n -- ) \ n wird vorsichtshalber auf [1..4] begrenzt. 1 - 3 and 10 * 1c2 + \ n-1 mal Zeilenverschiebung (10) plus Offset sectbuf + dup dup \ im Puffer (dreimal). c@ f0 and 0= \ Keine Nicht-DOS-Kennung (wie etwa 83 bei Linux) if c@ 0f and 10 or \ Unteres Nibble uebernehmen, 1 in oberes Nibble, swap c! \ Ergebnis nach sectbuf (Sektorpuffer) schreiben; else 2drop \ sonst Kennungsadresse aus dem Puffer wegnehmen. then ; \ Partition sichtbar machen. Zunaechst nur in sectbuf (Sektorpuffer). MBR muss \ schon per getmbr in den Puffer geschrieben sein. Zum Wirksamwerdenlassen \ mit putmbr abschliessen! Vorsicht bei der erweiterten Partition! Weiter wie \ bei hidepart. : unhidepart ( n -- ) \ n wird vorsichtshalber auf [1..4] begrenzt. 1 - 3 and 10 * 1c2 + \ n-1 mal Zeilenverschiebung (10h) plus Offset sectbuf + dup dup \ im Puffer (dreimal). c@ f0 and 10 = \ Keine Nicht-DOS-Kennung (wie etwa 83 bei Linux): if c@ 0f and \ Unteres Nibble uebernehmen, 0 in oberes Nibble, swap c! \ Ergebnis nach sectbuf (Sektorpuffer) schreiben; else 2drop \ sonst Kennungsadresse aus dem Puffer wegnehmen. then ; \ Alle Partitionen, auch die erweiterte, verstecken. Alle Vorsichtsmassnahmen \ von hidepart werden uebernommen. Die Bearbeitung findet nur im Sektorpuffer \ sectbuf statt. Der MBR muss vorher per getmbr dorthin gelegt worden sein. Um \ die Aenderungen auf die Festplatte zu bringen, muss dann noch putmbr \ eingesetzt werden. : hideall ( -- ) 4 0 \ 4 zu bearbeitende Zeilen im MBR do i 1 + hidepart loop ; \ Alle Partitionen, mit Ausnahme der erweiterten, verstecken. Ansonsten alles \ wie bei hideall. : hideall-ext ( -- ) 4 0 \ 4 zu bearbeitende Zeilen im MBR do i 3 and 10 * 1c2 + sectbuf + c@ 0f and \ Erweiterte Partition? 05 <> if i 1 + hidepart \ Nein, dann verstecken else i 1 + unhidepart \ Ja, dann 05 in sectbuf schreiben then loop ; \ Partition bootbar machen. Hat natuerlich fuer die erweiterte Partition \ (normalerweise) keinen Sinn. Zunaechst nur in sectbuf (Sektorpuffer). MBR \ muss schon per getmbr in den Puffer geschrieben worden sein. Zum \ Wirksamwerdenlassen mit putmbr abschliessen! : activatepart ( lw n -- ) \ lw = HD-Laufwerk (1...). Keine Begrenzung! swap 7f + swap \ Jetzt lw = 80... . 1 - 3 and 10 * 1be + \ n = Partition. n-1 begrenzt auf [0...3]. sectbuf + c! ; \ In Partitionstabelle (nach sectbuf) schreiben. \ Partition nicht-bootbar machen. Mit deactivateall (siehe gleich) kann man \ die gesamte Festplatte (welche?) ausschalten. Auch bei Linux? : deactivatepart ( n -- ) \ Byte bei Offset 1be in Partitionstabelle auf 0 -7f swap \ setzen: Also keine Laufwerkangabe noetig !?! activatepart swap ; \ Alle Partitionen auf nicht-bootbar (ID = 00) setzen. Nur im Sektorpuffer \ sectbuf. Der MBR muss vorher in den sectbuf geholt werden. Wenn alle weiteren \ Massnahmen erledigt sind, per putmbr auf der Platte wirksam werden lassen! : deactivateall ( -- ) 4 0 \ 4 zu bearbeitende Zeilen im MBR do i 1 + deactivatepart loop ; \ Die im MBR enthaltene Partitionstabelle mit Erlaeuterungen aus dem im \ Forth-Puffer gespeicherten MBR herausholen und am Bildschirm anzeigen. \ Achtung: (showparttab) kuemmert sich nicht darum, ob im Puffer wirklich ein \ Abbild des momentanen (oder wenigstens eines brauchbaren) MBRs liegt. Vor \ Aufruf von (showparttab) muss man den MBR erst per getmbr von der Festplatte \ in den Puffer holen. Das Forth-Wort showparttab (siehe weiter unten im \ vorliegenden Listing) erledigt beides. \ Eine .com-Datei von etwa demselben Funktionsumfang (mit Erlaeuterungen in \ englischer Sprache) wurde mir am 20.12.2003 von Rolf Schoene \ (Forth-Gesellschaft und damals Institut fuer Angewandte Mathematik der \ TU-Muenchen) uebermittelt. Das Vorliegende praesentiert also das Ganze in \ Forth. Schon allein fuer die ueberaus kompakte Moeglichkeit der Darstellung \ in Forth hat sich der mit diesem Artikel verbundene Aufwand (fuer mich) \ gelohnt. : (showparttab) ( -- ) ." MBR-Partitionstabelle (Kopf 0, Spur 0, Sektor 1, Offset 01BE): " cr cr 05 1 do i 30 + emit ." :" space 10 0 do sectbuf 1be + i + 10 j 1 - * + c@ 0 <# # # #> type space loop cr loop 4 spaces 10 0 do b3 emit 2 spaces loop cr 4 spaces 0c 0 do b3 emit 2 spaces loop c0 emit 03 0 do c4 emit c4 emit c1 emit loop 03 0 do c4 emit loop ." Part.-Laenge (in Sektoren)" cr 4 spaces 08 0 do b3 emit 2 spaces loop c0 emit 03 0 do c4 emit c4 emit c1 emit loop 08 0 do c4 emit loop ." Anzahl vorausgegangener Sektoren" cr 4 spaces 07 0 do b3 emit 2 spaces loop c0 emit 15 0 do c4 emit loop ." Nr des letzten Zylinders (0..7)" cr 4 spaces 06 0 do b3 emit 2 spaces loop c0 emit 09 0 do c4 emit loop ." Nr des letzten Sektors (0..5), Zylinder (6..7)" cr 4 spaces 05 0 do b3 emit 2 spaces loop c0 emit 25 0 do c4 emit loop ." Nr des letzten Kopfes" cr 4 spaces 04 0 do b3 emit 2 spaces loop c0 emit ." 01:FAT12, 04:FAT16<32MB, 05:erw.Part., 06:FAT16>32MB, 07:NTFS" cr 4 spaces 03 0 do b3 emit 2 spaces loop c0 emit 22 0 do c4 emit loop ." Nr des ersten Zylinders (0..7)" cr 4 spaces 02 0 do b3 emit 2 spaces loop c0 emit 16 0 do c4 emit loop ." Nr des ersten Sektors (0..5), Zylinder (6..7)" cr 4 spaces 01 0 do b3 emit 2 spaces loop c0 emit 2b 0 do c4 emit loop ." Nr des ersten Kopfes (0..5)" cr 4 spaces c0 emit 08 0 do c4 emit loop ." 80:aktive Primaerpartition (Bootpartition - nur eine!)," ." 00:inaktiv" cr cr ." Achtung: little endian!" cr ." Die vier Bytes " 04 0 do sectbuf 1ca + i + c@ 0 <# # # #> type space loop ." in Zeile 1 stellen die Hexzahl " 04 0 do sectbuf 1ca + 3 + i - c@ 0 <# # # #> type loop ." dar, usw." cr ; : showparttab ( -- ) \ Kommentare siehe (showparttab) getmbr cr cr (showparttab) ; \ Mit dem Booteinrichtungsprogramm n (bootpart) wird die Partitionstabelle im \ Sektorpuffer sectbuf auf das Booten der Partition n vorbereitet. n wird durch \ "wrapping" auf die Werte 1 bis 4 beschraenkt und stellt die Zeile in der \ Partitionstabelle dar, deren Entsprechung gebootet werden soll. Wird \ irrtuemlich ein Booten von der erweiterten Partition (so man eine eingerichtet \ hat) verlangt, so bricht (bootpart) mit einer Fehlermeldung ab und das System \ wartet auf eine neue Eingabe. Bevor (bootpart) vernuenftig arbeiten kann, muss \ der MBR per getmbr in den Sektorpuffer sectbuf geholt worden sein. Damit die \ Neueinstellungen auf die Festplatte geschrieben werden, muss abschliessend \ putmbr eingesetzt werden. \ Vorsicht mit putmbr, wenn man sich nicht ganz sicher ist, ob man das Resultat \ von getmbr fuer den Fall aller Faelle irgendwo abgespeichert hat! : (bootpart) ( n -- ) \ n = Zeile in der Partitionstabelle dup 1 - 3 and 10 * 1c2 + sectbuf + c@ 0f and 05 = if cr ." Erweiterte Partition" drop exit then hideall-ext \ Alle Partitionen, ausser erweiterter, verstecken. deactivateall \ Alle Laufwerke inaktiv setzen 1 over activatepart \ Laufwerk n in Boot-HD aktiv setzen unhidepart \ Laufwerk n in Boot-HD sichtbar machen (showparttab) ; \ Partitionstabelle anzeigen \ Die vorausgegangenen Vorbereitungsschritte werden ueber bootpart zu einem \ einzigen Schritt zusammengefasst. Die neue Partitionstabelle steht dann im \ MBR der Festplatte. Zum endgueltigen Booten muss der Computer dann neu \ gestartet werden. : bootpart ( n -- ) \ Laufwerk n in Boot-HD endgueltig booten getmbr \ MBR in den Puffer sectbuf holen (bootpart) \ Partitionstabelle vorbereiten putmbr ; \ Puffer sectbuf auf Boot-HD zurueckspeichern \ Glossar \ sectbuf ( -- ad) Konstante, Adresse des Sektorpuffers \ (getsect) ( seite spur/sektor -- ) Sektor von HD nach sectbuf holen \ (putsect) ( seite spur/sektor -- ) Sektor von sectbuf nach HD schreiben \ sp,sc>spsc ( sp sc -- spsc ) Spur und Sektor zu 2 Bytes zusammenfassen \ spsc>sp,sc ( spsc -- sp sc ) Umkehrung von sp,sc>spsc \ getmbr ( -- ) 0 1 (getsect) , MBR von HD nach sectbuf holen \ putmbr ( -- ) 0 1 (putsect) , sectbuf als MBR auf HD schreiben \ showsectbuf ( -- ) sectbuf (200 Bytes) anzeigen - egal, was drin \ showsectbuf100 ( -- ) sectbuf (100 Bytes) anzeigen - egal, was drin \ getpart ( n -- s-ad ) Partitionstabelle n von HD nach sectbuf holen \ putpart ( s-ad -- ) Ergebnis von getpart von sectbuf nach HD schreiben \ getboot ( n -- ) Bootsektor des Laufwerks n von HD nach sectbuf holen \ hidepart ( n -- ) Partition n mit Hi-Nibble 1 versehen - nur im sectbuf \ hideall ( -- ) Alle Partitionen im sectbuf mit Hi-Nibble 1 versehen \ hideall-ext ( -- ) Wie hideall, aber erweiterte Partition mit 05 versehen \ unhidepart ( n -- ) Partition n in sectbuf wieder sichtbar machen \ activatepart ( lw n -- ) HD-ID von Part. n von Platte lw auf 80+lw setzen \ deactivatepart ( n -- ) HD-ID von Partition n auf 00 setzen \ deactivateall ( -- ) HD-ID aller Partitionen auf 00 setzen \ (showparttab) ( -- ) MBR-Partitionstabelle aus sectbuf am Monitor anzeigen \ showparttab ( -- ) Wie (showparttab), aber erst MBR von HD nach sectbuf. \ (bootpart) ( n -- ) sectbuf zum Booten von Partition n vorbereiten \ bootpart ( n -- ) MBR auf der HD zum Booten von Partition n vorbereiten \ Ende des Listings