\ ***************************************************** \ * * \ * BOOTMA-5.FTH * \ * * \ * Zutaten fuer FAT-Reparatur und Bootmanager unter * \ * Turbo-FORTH-83 und ZF * \ * * \ * Fred Behringer - Forth-Gesellschaft - 01.10.2009 * \ * * \ ***************************************************** \ Fuer Turbo-Forth: \ FORTH INCLUDE BOOTMA-1.FTH INCLUDE ... Hierbei ist die erste Include-Datei \ die aus Heft 3/2008, die zweite die aus Heft 4/2008 usw. Hier - bis zum \ allerletzten INCLUDE (man darf natuerlich auch alles kleinschreiben) - \ koennen alle Eingaben 'geschachtelt' werden, ohne per [ret] 'absetzen' zu \ muessen. \ Fuer ZF: \ ZF [ret] [ret] \ FLOAD BOOTMA-1.FTH [ret] FLOAD BOOTMA-2.FTH [ret] FLOAD usw. \ In BOOTMA-1.FTH muss vorher attributs off per '\' auskommentiert werden (in \ ZF gibt es kein attributs). Die Schachtelung bei FLOAD geht in ZF nicht. Die \ Endung .FTH nicht vergessen! \ Nicht jedes DOS-System kennt den Escape-Sequenzen-Treiber ANSI.SYS. DOS 6.2 \ hat ihn. In Turbo-Forth sorgt ANSI.SYS fuer ein 'fettes' OK-Prompt. Hat man \ kein ANSI.SYS, dann kann man auch auf ANSI.COM (siehe Teil 3 aus dem VD-Heft \ 1/2009) auszuweichen: ANSI.COM von Michael J. Mefford aus dem PC Magazine des \ Jahres 1988 laesst sich im Internet (z.B. per Google) leicht beschaffen und \ funktioniert prima. \ Fuer ZF sind die folgenden Worte leider nicht verwendbar (es gibt dort kein \ $execute): sect>bak bak>sect mbr>bak bak>mbr mbr>bak-hd# bak>mbr-hd#. Zum \ Ausprobieren habe ich sie zeitweilig herausgenommen: Geht prima! Ueberprueft \ habe ich nur stichprobenartig (Aufgabe fuer den geneigten Leser). Der gesamte \ vorliegende Teil 5 kommt andererseits mit den Teilen 1 bis 3 als Basis aus. \ Glossar \ ------- \ #... = Anzahl..., #sect = LBA-Sektoranzahl \ ...# = ...Nummer, sect# = LBA-Sektornummer, hd# = Festplatten-Nr. (80,81,...) \ ( d#lba -- ) = Beispiel fuer einen doppeltgenauen Wert im Stack-Kommentar \ tmp1 (--ad) Variable fuer kurzzeitige Speicherung von (Stack-)Parametern \ tmp2 (--ad) dito \ Zur Abgrenzung zu aehnlichen Forth-Worten aus den bisherigen Teilen der Serie \ enden die Namen aller Worte, die eine Auswahl der Festplatte zulassen, ab dem \ vorliegenden Artikel in -hd# . Herkoemmliche Festplatten enthalten bewegliche \ Teile. Elektronik, die auf bewegliche Teile warten muss, ist fehleranfaellig. \ Also habe ich bei allen Worten, deren Namen auf -hd# enden, Fehlerabfragen \ ( -- fl ) eingebaut. Bei Intel-maschinennahen Worten heisst fl=0, dass kein \ Fehler vorliegt, bei High-Level-Forth-Worten (Vergleiche, Abfragen) kann fl=0 \ 'falsch' ('nicht zutreffend') bedeuten. Besonders geachtet habe ich darauf, \ auch dann noch ein sauberes fl<>0 zu bekommen, wenn bei interaktiven Eingaben \ das ( hd# -- ) vergessen wird und der Stack dabei leer ist. \ sect>kb (dsects -- dkb) Sektoranzahl nach Kilobyte. dsects doppeltgenau, \ kb>sect (dkb -- dsects) Kilobyte nach Sektoranzahl. dkb auch. \ (getmbr-hd#) (hd# -- fl) Maschinenprogrammkern fuer getmbr-hd# \ getmbr-hd# (hd# -- fl) Wie getmbr, aber Festplatte hd# beliebig. fl=0: OK \ (putmbr-hd#) (hd# -- fl) Maschinenprogrammkern fuer putmbr-hd# \ putmbr-hd# (hd# -- fl) Wie putmbr, aber Festplatte hd# beliebig. fl=0: OK \ mbr>bak-hd# (hd# -- fl) Wie mbr>bak, aber Festplatte hd# waehlbar. fl=0: OK \ bak>mbr-hd# (hd# -- fl) Wie bak>mbr, aber Festplatte hd# waehlbar. fl=0: OK \ testpart# (part# -- part#/1) part#=1 und raus, wenn nicht part# aus [1..4]. \ getLBAsect# (part# -- dlba#) LBA-Anfangs-Sektor der Part. part# aus sectbuf \ getLBA#sect (part# -- d#lba) Anzahl LBA-Sektoren der Part. part# aus sectbuf \ putLBAsect# (dlba# part# --) LBA-Anfangs-Sektor der Part. part# nach sectbuf \ putLBA#sect (d#lba part# --) Anzahl LBA-Sektoren der Part. part# nach sectbuf \ gettyp (part# -- c) Holt den Dateisystem-Typus aus part# in sectbuf \ puttyp (c part# --) Legt den Dateisystem-Typus nach part# in sectbuf \ putfat16 (part# --) 06 in das untere Nibble an Offset 4 von part# in sectbuf \ putfat32 (part# --) 0c in das untere Nibble an Offset 4 von part# in sectbuf \ putntfs (part# --) 07 in das untere Nibble an Offset 4 von part# in sectbuf \ putlin83 (part# --) Legt 83 in das Byte mit Offset 4 von part# in sectbuf \ putlin82 (part# --) Legt 82 in das Byte mit Offset 4 von part# in sectbuf \ put55-aa (--) Legt die Magic Number ans Ende von sectbuf \ testfat16 (part# -- fl) 06 im unt. Nibble an part#/Offs.4 in sectbuf? fl=0: No \ testfat32 (part# -- fl) 0c im unt. Nibble an part#/Offs.4 in sectbuf? fl=0: No \ testntfs (part# -- fl) 07 im unt. Nibble an part#/Offs.4 in sectbuf? fl=0: No \ testlin83 (part# -- fl) 83 an Byte mit Offset 4 v. part# in sectbuf? fl=0: No \ testlin82 (part# -- fl) 82 an Byte mit Offset 4 v. part# in sectbuf? fl=0: No \ test55-aa (-- fl) 55 aa am Ende von sectbuf (Offset 1fe/1ff)? fl=0: No \ drvparam (--ad) Konst., Adresse des Parameterpuffers (1 Sektor = 512d Bytes) \ showdrvparam (--) Zeigt den gesamten Sektor drvparam am Bildschirm an \ showdrvparam100 (--) Zeigt die ersten 100h Bytes daraus am Bildschirm an \ (getdrvparam-hd#) (hd# -- fl) Maschinenprogrammkern fuer getdrvparam-hd# \ getdrvparam-hd# (hd# -- fl) Parameter per Int13h/48h nach drvparam, fl=0: OK \ q@ (ad -- n1 n2 n3 n4) Vierfachgenauen Wert als little-endian aus ad im RAM \ q! (n1 n2 n3 n4 ad --) Vierfachgenauen Wert als little-endian nach ad im RAM \ q. (n1 n2 n3 n4 --) Vierfachgenauen Wert als little-endian anzeigen \ get#sects-hd# (hd# -- q) Anzahl der Sektoren der Platte hd# aus drvparam \ getbyte/sect-hd# (hd# -- n) Bytes pro Sektor der Platte hd# aus drvparam \ getsect/head-hd# (hd# -- d) Sektoren pro Kopf der Platte hd# aus drvparam \ gethead/cyli-hd# (hd# -- d) Koepfe pro Zylinder der Platte hd# aus drvparam \ get#cylis-hd# (hd# -- d) Anzahl der Zylinder der Platte hd# aus drvparam \ (getLBAsect-hd#) (hd# dlba# -- fl) Maschinenprogrammkern zu getLBAsect-hd# \ getLBAsect-hd# (hd# dlba# -- fl) LBA-Sektor von hd# nach sectbuf, fl=0: OK \ (putLBAsect-hd#) (hd# dlba# -- fl) Maschinenprogrammkern zu putLBAsect-hd# \ putLBAsect-hd# (hd# dlba# -- fl) LBA-Sektor von sectbuf nach hd#, fl=0: OK \ getbyte/sect-fat16 (-- n) Anzahl Bytes pro Sektor, normalerweise 512d, \ putbyte/sect-fat16 (n --) dito schreiben nach sectbuf \ getsect/head-fat16 (-- n) Anzahl Sektoren pro Kopf, normalerweise 63d \ putsect/head-fat16 (n --) dito schreiben nach sectbuf \ getsect/clust-fat16 (-- c) Anzahl Sektoren pro Cluster aus sectbuf holen \ putsect/clust-fat16 (c --) dito schreiben nach sectbuf \ getsect/fat-fat16 (-- n) Anzahl Sektoren pro FAT aus sectbuf holen \ putsect/fat-fat16 (n --) dito schreiben nach sectbuf \ get#head-fat16 (-- n) Anzahl Koepfe aus sectbuf holen \ put#head-fat16 (n --) dito schreiben nach sectbuf \ get#fat-fat16 (-- c) Anzahl FATs aus sectbuf holen - immer c=2 \ put#fat-fat16 (c --) dito schreiben nach sectbuf \ get#root-fat16 (-- n) Anzahl Datei-/Verzeichnis-Eintraege im Rootverz. \ put#root-fat16 (n --) dito schreiben nach sectbuf \ get#hidsect-fat16 (-- d) Anzahl Sektoren vor dem Bootsektor \ put#hidsect-fat16 (d --) dito schreiben nach sectbuf \ get#resvsect-fat16 (-- n) Anzahl reservierter Sektoren vor der ersten FAT \ put#resvsect-fat16 (n --) dito schreiben nach sectbuf \ getdrv#-fat16 (-- c) Festplatte (80) oder Diskette (00)? \ putdrv#-fat16 (c --) dito schreiben nach sectbuf \ getmedid-fat16 (-- c) Media-Descriptor. f8 = Festplatte, f0 = 3,5"-Disk \ putmedid-fat16 (c --) dito schreiben nach sectbuf \ get#small-fat16 (-- n) Anzahl Sektoren, die in 32 Bit Platz finden \ put#small-fat16 (n --) dito schreiben nach sectbuf \ get#large-fat16 (-- d) Anzahl Sektoren, die in 32 Bit keinen Platz finden \ put#large-fat16 (d --) dito schreiben nach sectbuf \ getbootprim-hd# (hd# part# -- fl) Bootsektor bei primaerer part#; fl=0: OK \ getEBR-hd# (hd# drv# -- fl) EBR des logischen Laufwerks drv#; fl=0: OK \ getbootext-hd# (hd# drv# -- fl) Bootsektor d. log. Laufwerks drv#; fl=0: OK hex variable tmp1 \ zum (kurzzeitigen) Aufbewahren eines Parameters variable tmp2 \ dito variable hd# \ Nummer der zuletzt eingegebenen Festplatte variable part# \ Nummer der zuletzt eingegebenen Partition variable drv# \ Nummer des zuletzt eingegebenen logischen Laufwerks 2variable EBR \ LBA-Nummer des zuletzt in sectbuf bearbeiteten EBRs 2variable EBR1 \ LBA-Nummer des Anfangs der erweiterten Partition 2variable boot \ LBA-Nummer des zuletzt in sectbuf bearbeiteten Bootsektors 2variable LBA# \ LBA-Nummer des zuletzt in sectbuf bearbeiteten Sektors \ Zunaechst zwei Umwandlungsworte in allereinfachster Ausfuehrung, d.h. fuer \ byte/sect = 200h . Ein- und Ausgabe auf dem Stack doppeltgenau: : sect>kb ( dsects -- dkb ) d2/ ; \ Sektoranzahl nach Kilobyte umwandeln : kb>sect ( dkb -- dsects ) d2* ; \ Kilobyte nach Sektoranzahl umwandeln \ Das folgende Wort getmbr-hd# ist eine Erweiterung des Wortes getmbr aus \ Teil 1 (VD-Heft 3/2008). getmbr bezog sich auf die Bootplatte, getmbr-hd# \ laesst die freie Wahl der Festplatte (oder ZIP-Diskette oder USB-Stick) zu. \ Der MBR liegt immer am Festplatten-Anfang. Es kann dafuer also bei der \ CHS-Adressierung, mit den Werten C,H,S = 0,0,1 bleiben. code (getmbr-hd#) ( hd# -- fl ) \ fl=0, wenn alles OK; sonst fl<>0 ds push es pop dx pop 1 # cx mov dh dh xor sectbuf # bx mov 201 # ax mov 13 int ah al mov ax push next end-code : getmbr-hd# ( hd# -- fl ) \ fl=0, wenn alles OK; sonst fl<>0. depth 0 = \ Stack leer, d.h., if -1 exit then \ Eingabe von hd# vergessen? dup hd# ! \ Eingabe hd# auch in die Variable hd# (getmbr-hd#) ; \ Zum Interrupt 13h \ Das folgende Wort putmbr-hd# ist genau das schreibende Gegenstueck zum \ 'lesenden' Wort getmbr-hd# . Vorsicht: Erst ueberlegen, dann anwenden! code (putmbr-hd#) ( hd# -- fl ) \ fl=0, wenn alles OK; sonst fl<>0 ds push es pop dx pop 1 # cx mov dh dh xor sectbuf # bx mov 301 # ax mov 13 int ah al mov ax push next end-code : putmbr-hd# ( hd# -- fl ) \ fl=0, wenn alles OK; sonst fl<>0. depth 0 = \ Stack leer, d.h., if -1 exit then \ Eingabe von hd# vergessen? dup hd# ! \ Eingabe hd# auch in die Variable hd# (putmbr-hd#) ; \ Zum Interrupt 13h \ Bei Veraenderungen am MBR ist man gut beraten, vorher die Worte mbr>bak \ und bak>mbr aus Teil 1 anzuwenden, um den MBR notfalls aus der Datei \ mbr.bak wieder hereinholen zu koennen. Es folgen Versionen fuer frei \ waehlbare Festplatten hd#: : mbr>bak-hd# ( hd# -- fl ) \ fl=0, wenn alles OK; sonst fl<>0 getmbr-hd# " sectbuf dup 200 + save mbr.bak " $execute ; : bak>mbr-hd# ( hd# -- fl ) \ fl=0, wenn alles OK; sonst fl<>0 " open mbr.bak >r dsegment sectbuf 200 r> (get) close " $execute nip abort" Fehler!" putmbr-hd# ; \ Das gleich folgende Wort getLBAsect# geht vom Inhalt des Sektorpuffers \ sectbuf aus. (Welche Festplatte, ist dabei unwichtig.) Es wird angenommen, \ dass in sectbuf ein gueltiger MBR liegt (keine Pruefung!). Es wird der \ LBA-Anfangssektor der betrachteten Partition part# aus dem MBR in sectbuf \ ausgelesen und auf den Datenstack gelegt. part# ist einfachgenau, dlba# \ doppeltgenau. Das Wort kann auch zum Auslesen der Adresse eines EBRs (in \ Sektoren relativ zum Anfang der erweiterten Partition) verwendet werden \ (siehe getEBR-hd#). \ Vorsicht: 12345678. xxxx 2! erscheint im RAM, also auch in sectbuf, an der \ Adresse xxxx als 34-12-78-56 (kann mit dump ueberprueft werden). Also kein \ reines 'Little Endian' (siehe auch Bemerkungen im Textteil des Artikels)! \ Im folgenden Fehlerauffangwort testpart# wird ein unzulaessiges part# \ (vorsichtshalber) durch part# = 1 ersetzt. : testpart# ( part# -- part#/1 ) \ Wenn 0 < part# < 5, dann wird part# dup 1 5 within not \ durchgereicht; wenn nicht, dann wird if cr ." Partition?" drop 1 \ die Meldung 'Partition?' ausgegeben und then ; \ mit part#=1 gearbeitet. \ Jetzt also das angekuendigte Forth-Wort getLBAsect# zum Einlesen des \ relativen LBA-Anfangssektors der betrachteten Partition: : getLBAsect# ( part# -- dlba# ) \ Anfangssektor der Partition part#. testpart# \ Wenn nicht part# aus [1..4], dann 10 * sectbuf 1b6 + + 2@ swap \ schaltet testpart# auf part#=1 um. 2dup lba# 2! ; \ dlba# nach lba# zur Reserve \ Das folgende Forth-Wort getLBA#sect wirkt aehnlich wie das eben besprochene \ getLBAsect#. Diesmal wird aber die Laenge (in Sektoren) der betreffenden \ Partition eingeholt und auf den Datenstack und nach lba# gelegt. : getLBA#sect ( part# -- d#lba ) \ Sektoranzahl in der Partition part# testpart# \ Wenn nicht part# aus [1..4], dann 10 * sectbuf 1ba + + 2@ swap \ schaltet testpart# auf part#=1 um. 2dup lba# 2! ; \ d#lba nach lba# zur Reserve \ Bei der Bildschirmanzeige der Stackwerte reicht hier i.A. das Wort d. \ nicht. Es ist ud. zu verwenden. Der einfache Punkt waere ganz falsch. \ Erhaelt man bei getLBAsect# den Wert 00000000, dann hat man es mit einem der \ folgenden drei Faelle zu tun: \ (1) Regelfall und betreffende Partition part# nicht vorhanden, \ (2) 'Logisches' Laufwerk der erweiterten Partition und part# = 3 oder 4, \ (3) Fehler in der Partitionstabelle MBR oder EBR. \ Entsprechendes gilt fuer getLBA#sect . \ Die folgenden beiden Forth-Worte sind die Gegenstuecke zu getLBAsect# und \ getLBA#sect . Ausgehend von der Partitionsnummer part# auf dem Datenstack \ werden diese Werte an die dafuer vorgesehene Stelle der Partitionstabelle \ gelegt. \ Die Operationen wirken zunaechst nur auf den Sektorpuffer sectbuf. Gefahr \ bestuende erst dann, wenn man (zum Abschluss) eines der 'direkten' put-Worte, \ wie putmbr oder putmbr-hd#, ins Spiel bringt. : putLBAsect# ( dlba# part# -- ) \ Anfangssektor der Partition part#. testpart# \ Umschalten auf part#=1, wenn nicht 10 * sectbuf 1b6 + + rot rot \ part# aus [1..4]. 2dup lba# 2! rot rot swap 2! ; \ dlba# in 2Variable lba# in Reserve : putLBA#sect ( d#lba part# -- ) \ Sektoranzahl in der Partition part#. testpart# \ Umschalten auf part#=1, wenn nicht 10 * sectbuf 1ba + + rot rot \ part# aus [1..4]. 2dup lba# 2! rot rot swap 2! ; \ d#lba in 2Variable lba# in Reserve \ Die folgenden Forth-Worte erwarten den MBR im Puffer sectbuf. Die Nummer hd# \ der Platte wird dann nicht mehr benoetigt. Es muss also vorher getmbr-hd# \ und hinterher putmbr-hd# in Aktion treten. Wichtig ist die einzugebende \ Partitions-Nummer part#. Ich habe ueberall, wo sinnvoll, das Forth-Wort \ testpart# eingebaut. Damit wird ueberprueft, ob part# wirklich zulaessig ist. \ Wenn nicht, erscheint die Warnung 'Partition?' und es wird part#=1 gesetzt. \ Von MS-DOS aus hat man den Zugriff nur auf Partitionen des Typs 01, 04, 05 \ und 06 (oder 16 usw., falls 'versteckt'). Es bedeutet dabei: \ 01 = FAT12, Primaerpart. oder log. Laufwerk mit weniger als 32680 Sektoren, \ 04 = FAT16, dito mit 32680 bis 65535 Sektoren (16-33 MB), \ 05 = erweiterte Partition, \ 06 = FAT16, mehr als 65535 Sektoren (33 MB bis 4 GB), \ 07 = NTFS, primaer oder erweitert, \ 0C = FAT32, mit erweitertem Interrupt 13h, \ 0E = FAT16, BIGDOS mit erweitertem Interrupt 13h, \ 0F = erweiterte Partition mit erweitertem Interrupt 13h. : gettyp ( part# -- n ) \ Holt den Dateisystem-Typus aus part# in sectbuf testpart# dup part# ! \ part# aus [1..4] ? Aufbewahren in Variable part# 10 * sectbuf 1b2 + + c@ ; \ Bei 16 ('versteckt' 06) wird 1 mitgefuehrt usw \ Bei dem folgenden Forth-Wort puttyp (das lediglich auf den Puffer sectbuf \ wirkt, nicht auf die Festplatte) kann man ohne Weiteres auch andere Typ-Werte \ als die oben angegebenen 'DOS-Werte' verwenden. Das wird beispielsweise fuer \ Linux interessant: Linux native = 83, Linux swap = 82. : puttyp ( n part# -- ) \ Legt den Dateisystem-Typus nach part# in sectbuf testpart# dup part# ! \ part# aus [1..4] ? Aufbewahren in Variable part# 10 * sectbuf 1b2 + + c! ; \ Bei 16 muss die 1 mit angegeben werden usw \ Und nun ein paar spezielle Faelle zum Einbringen oder Aendern von Typnummern \ in (vorerst nur) sectbuf. Eine eventuelle 1 fuer 'versteckt' bleibt erhalten. : putfat16 ( part# -- ) \ Schreibt 6 in das untere Halbbyte an Offset 4 testpart# dup part# ! \ part# aus [1..4] ? >r r@ gettyp f0 and 06 or r> puttyp ; : putfat32 ( part# -- ) \ Schreibt c in das untere Halbbyte an Offset 4 testpart# dup part# ! \ part# aus [1..4] ? >r r@ gettyp f0 and 0c or r> puttyp ; : putntfs ( part# -- ) \ Schreibt 7 in das untere Halbbyte an Offset 4 testpart# dup part# ! \ part# aus [1..4] ? >r r@ gettyp f0 and 07 or r> puttyp ; \ Die beiden folgenden Forth-Worte beziehen sich auf Linux. 'Versteckt' gilt \ hier nicht. : putlin83 ( part# -- ) \ Schreibt 83 in das Byte mit Offset 4 in part# testpart# dup part# ! \ part# aus [1..4] ? 83 swap puttyp ; : putlin82 ( part# -- ) \ Schreibt 82 in das Byte mit Offset 4 in part# testpart# dup part# ! \ part# aus [1..4] ? 82 swap puttyp ; \ Bootsektoren, MBR und EBRs (extended boot records) werden mit 55 aa (der \ 'Magic Number') in Offset 1fe und 1ff abgeschlossen. : put55-aa ( -- ) \ Magic Number nach sectbuf aa55 sectbuf 1fe + ! ; : testfat16 ( part# -- fl ) \ Prueft (fl=0, wenn nicht), ob im unteren testpart# dup part# ! \ Halbbyte von part#/Offset 4 der Wert 6 liegt. sectbuf 1b2 + c@ 0f and 06 = ; : testfat32 ( part# -- fl ) \ Prueft (fl=0, wenn nicht), ob im unteren testpart# dup part# ! \ Halbbyte von part#/Offset 4 der Wert c liegt. sectbuf 1b2 + c@ 0f and 0c = ; : testntfs ( part# -- fl ) \ Prueft (fl=0, wenn nicht), ob im unteren testpart# dup part# ! \ Halbbyte von part#/Offset 4 der Wert 7 liegt. sectbuf 1b2 + c@ 0f and 07 = ; : testlin83 ( part# -- fl ) \ Prueft (fl=0, wenn nicht), ob im Byte von testpart# dup part# ! \ part#/Offset 4 der Wert 83 liegt. sectbuf 1b2 + c@ 83 = ; : testlin82 ( part# -- fl ) \ Prueft (fl=0, wenn nicht), ob im Byte von testpart# dup part# ! \ part#/Offset 4 der Wert 82 liegt. sectbuf 1b2 + c@ 82 = ; : test55-aa ( -- fl ) \ Prueft (fl=0, wenn nicht), ob ab Offset 1fe sectbuf 1fe + @ aa55 = ; \ von sectbuf die beiden Bytes 55 aa liegen. \ Das Forth-Wort getdrvparam-hd# weiter unten legt die ermittelten Parameter im \ gleich besprochenen Puffer drvparam ab. Nach erfolgter Operation (bis zum \ naechsten Aufruf von getdrvparam-hd#) koennen sie von dort aus auch beliebig \ oft wieder abgeholt werden. Dem Puffer drvparam habe ich mit Absicht die \ Laenge eines 'ueblichen' Sektors (512d Bytes) gegeben, damit ich drvparam \ notfalls (neben sectbuf) auch als zusaetzlichen Sektorpuffer einsetzen kann. \ Die Konstruktion der Forth-Konstanten drvparam gleicht hier der von sectbuf. 210 allot here \ Platz fuer mind. 1 Sektor = 512d Bytes here 0f and - \ drvparam an Paragraphenanfang 200 - \ Anfang des Sektorpuffers constant drvparam \ Liefert Adresse des Parameterpuffers \ In Analogie zu showsectbuf aus Teil 1 noch schnell das Wort showdrvparam : : showdrvparam ( -- ) drvparam 200 dump ; : showdrvparam100 ( -- ) drvparam 100 dump ; \ Das jetzt folgende Forth-Wort getdrvparam-hd# und die damit verbundenen Worte \ beziehen ihre Werte ueber den Interrupt 13h/48h und koennen zur Ueberpruefung \ der entsprechenden Eintragungen in den Datentabellen der Bootsektoren \ verwendet werden (vergleiche weiter unten). \ Die Anwendung von getdrvparam-hd# ist nicht nur auf Bootplatten beschraenkt. \ Ich habe das mit einem von der Platte hd#=80 gebooteten Windows-ME-System und \ einem ueber hex 81 getdrvparam-hd# abgefragten festplatten-formatierten \ USB-Stick ueberprueft. (Win-ME gehoert noch zu denjenigen Windows-Systemen, \ die sich, anders als XP, bei direktem Festplatten-Zugriff nicht beschweren.) code (getdrvparam-hd#) ( hd# -- fl ) \ fl=0, wenn alles OK dx pop \ Festplattennummer hd# (80...ff) nach dl dh dh xor \ Vorsichtshalber dh=0 si push \ si (=ip) sicherstellen drvparam # si mov \ ds:si mit ds:drvparam laden 4800 # ax mov \ Funktion 'get drive parameters' (LBA) 13 int \ Interrupt aufrufen si pop \ si (=ip) wiederherstellen ah al mov \ Jetzt ax = 0 bei Erfolg ax push \ ax <> 0 => Fehler next end-code : getdrvparam-hd# ( hd# -- fl ) \ fl=0, wenn alles OK; sonst fl<>0. depth 0 = if -1 then \ Eingabe von hd# vergessen und Stack leer? dup hd# ! \ Eingabe hd# auch in die Variable hd# (getdrvparam-hd#) ; \ Zum Interrupt 13h/48h \ Aus Offset 10h der Tabelle drvparam erhaelt man die Gesamtzahl der Sektoren \ der Festplatte hd#. Dafuer sind 8 Bytes vorgesehen. Das entspricht einem \ vierfachgenauen Wert (64 Bit). Wegen Fragen bei der Darstellung von \ vierfachgenauen Werten (Little-Endian?) vergleiche den Textteil des \ vorliegenden Artikels. \ Das folgende Forth-Wort q@ holt vom RAM-Speicher ab Adresse ad den dort in \ Little-Endian-Darstellung als Hex-Byte-Folge zu interpretierenden 64-Bit-Wert \ auf den Datenstack, und zwar so, dass er im Stack-RAM-Bereich (ich gehe von \ einem nach kleineren Adressen hin wachsenden Stack aus) wieder als \ Little-Endian-Bytefolge auftritt. \ Mit sp@ wollte ich hier und anderswo in diesem Artikel nicht arbeiten, da \ mir dieses Wort als zu systemabhaengig und daher inkompatibilitaetstraechtig \ erschien (in den ANS-Forth-Empfehlungen wird es nicht erwaehnt). : q@ ( ad -- n1 n2 n3 n4 ) 8 + tmp1 ! 4 0 do tmp1 @ 2 - dup tmp1 ! @ loop ; \ Das jetzt folgende Wort q! ist die Umkehrung zu q@. Die Eintragungen auf dem \ Datenstack seien so wie bei q@ zu interpretieren. Fuehrende Nullen brauchen \ in keinem dieser Viererpakete eingegeben zu werden. In der (ueber q! \ erzeugten) RAM-Darstellung und dann auch (bei Anwendung von q.) auf dem \ Bildschirm erscheint aber jede einzelne Null, auch eventuell vorhandene \ fuehrende Nullen der Gesamtanzeige. Der Einfachheit halber seien nur Zahlen \ ohne Vorzeichen zugelassen. Und die Viererpakete erhalten nur dann ihren \ eigentlichen Sinn, wenn sie als Hexzahlen auftreten. : q! ( n1 n2 n3 n4 ad -- ) 2 - tmp1 ! 4 0 do tmp1 @ 2 + dup tmp1 ! ! loop ; \ Das folgende Forth-Wort q. liefert eine schreibgerechte Anzeige von \ vierfachgenauen Werten auf dem Bildschirm nach der besprochenen Methode. : q. ( n1 n2 n3 n4 -- ) base @ tmp1 ! hex swap 2swap swap <# 8 0 do # loop #> type <# 8 0 do # loop #> type space tmp1 @ base ! ; \ Und nun wieder zu den Festplatten-Parametern. d ist auf dem Stack wie ueblich \ als d = n1n2 = n2 n1 zu lesen, q aber als q = n1n2n3n4 = n1 n2 n3 n4 . Im \ Fehlerfall ist q = ffff ffff ffff ffff und es wird 'HD-Fehler' angezeigt. : get#sects-hd# ( hd# -- q ) \ Anzahl der Sektoren der Platte hd# getdrvparam-hd# if \ HD-Fehler? Ja, dann Ausstieg cr ." HD-Fehler!" -1. 2dup exit \ mit q = ffff ffff ffff ffff then drvparam 10 + q@ ; \ Eine Ueberpruefung meiner Festplatte per 80 get#sects q. ergab den Hexwert \ 2629ffb als Gesamtzahl aller Sektoren. Sektorgroesse ist 512 Bytes dezimal. \ Auf das Kilobyte gehen also 2 Sektoren. Das liefert 1314ffd Kilobyte als \ Hexwert und somit 20008957 als dezimale Anzahl aller Kilobytes. Das sind rund \ 20 Gigabyte, was genau der Kapazitaet der von mir fuer diese Experimente \ eingesetzten Festplatte entspricht. \ Und nun der Rest der vom Interrupt 13h/48h gelieferten HD-Parameter: : getbyte/sect-hd# ( hd# -- n ) \ Zahl der Bytes pro Sektor auf hd# getdrvparam-hd# abort" Fehler!" drvparam 18 + @ ; : getsect/head-hd# ( hd# -- d ) \ Zahl der Sektoren pro Kopf auf hd# getdrvparam-hd# abort" Fehler!" drvparam 0c + 2@ swap ; : gethead/cyli-hd# ( hd# -- d ) \ Zahl der Koepfe pro Zylinder auf hd# getdrvparam-hd# abort" Fehler!" drvparam 08 + 2@ swap ; : get#cylis-hd# ( hd# -- d ) \ Gesamtzahl der Zylinder auf hd# getdrvparam-hd# abort" Fehler!" drvparam 04 + 2@ swap ; \ Wie schon erwaehnt, werdem vom System in den Datentabellen der Bootsektoren \ ebenfalls noch einige Parameter der Festplatte festgehalten. Nur an wenige \ davon kommt man auch mit dem Interrupt 13h/48h heran. Jetzt noch schnell \ ein paar Worte ueber eben solche Parameter aus den Bootsektoren. \ Gerade bei den Festplatten-Parametern waere es interessant, nicht nur die \ bootende Festplatte, sondern auch eine eventuell eingesetzte zweite \ Festplatte (z.B. einen im System eingebundenen USB-Stick oder ein als \ Festplatte formatiertes Zip-Laufwerk) zu beruecksichtigen. Dazu brauche ich \ ein lesendes und ein schreibendes Forth-Wort fuer beliebige LBA-Sektoren, im \ Gegensatz zu Teil 3 diesmal aber bei vorgebbarer Festplatten-Nummer hd#. Ich \ hole das jetzt nach. Die \-Kommentare laufen wie in Teil 3 (VD-Heft 1/2009) \ bei getLBAsect und putLBAsect und koennen hier weggelassen werden. \ Das folgende Forth-Wort getLBAsect-hd# holt von der Festplatte hd# (80,81,.., \ einfachgenau) den Sektor mit der Nummer dlba# (0,1,2,.., doppeltgenau, d.h. \ Punktzahl!) und legt ihn in den Puffer sectbuf. hd# wird (bis auf das \ naechste Forth-Wort mit dem Eingabe-Parameter hd#) in der gleichnamigen \ Variablen hd# aufbewahrt. dlba# wird (bis zur naechsten dlba#-Verarbeitung) \ in der 2Variablen lba# gespeichert. \ Achtung: Es werden auch in diesem Artikel jeweils nur immer einzelne Sektoren \ beruecksichtigt. Mehrere Sektoren in einem einzigen Durchgang kann man sich \ leicht dadurch beschaffen, dass man in der Befehlsfolge 1 # dap 02 + #) mov \ im folgenden Wort (getLBAsect-hd#) den Wert 1 entsprechend erhoeht und \ dafuer sorgt, dass der Puffer sectbuf entsprechend groesser angesetzt wird. code (getLBAsect-hd#) ( hd# dlba# -- fl ) \ fl=0, wenn alles OK 18 # dap #) mov 1 # dap 02 + #) mov sectbuf # dap 04 + #) mov csegment # dap 06 + #) mov ax pop ax dap 0a + #) mov ax lba# #) mov ax pop ax dap 08 + #) mov ax lba# 02 + #) mov 0 # dap 0c + #) mov 0 # dap 0e + #) mov dx pop dx hd# #) mov dh dh xor si push dap # si mov 4200 # ax mov 13 int si pop ah al mov ax push next end-code : getLBAsect-hd# ( hd# dlba# -- fl ) \ fl=0, wenn alles OK depth 3 < \ hd# oder/und dlba# vergessen? if \ Ja, dann Stack saeubern und begin depth 0 > if drop 0 else -1 then until cr ." Fehler!" -1 exit \ Ausstieg unter fl=ffff then (getLBAsect-hd#) ; \ getLBAsect aus Teil 3 bezieht sich auf die Bootplatte, getLBAsect-hd# hier \ auf die beliebig vorgebbare Festplatte mit der Nummer hd#. dap (disk address \ packet) ist der Parameter-Uebergabe-Puffer aus Teil 3 (VD-Heft 1/2009). \ Achtung: dlba# muss doppeltgenau (als 'Punktzahl') eingegeben werden! \ In Offset 'dap 04 +' von dap wird die 'Lang-'Adresse des Uebertragungspuffers \ in der Form segment:sectbuf (bei mir beispielsweise 2860:6750) festgehalten, \ und zwar in der Little-Endian-Form 50 67 60 28 . Steht hier der Wert \ ffff:ffff, dann soll laut 'Browns Liste' ab Offset 'dap 10 +' die lineare \ 64-Bit-Adresse des Uebertragungspuffers abgelegt werden. \ Und hier als Schreib-Pendant zu (getLBAsect-hd#) das Wort (putLBAsect-hd#). \ (Vorsicht vor unueberlegtem Tastendruck!) Der einzige Unterschied zu \ (getLBAsect-hd#) liegt in 4300 statt 4200. Vielleicht koennte (sollte) man \ putLBAsect-hd# und (putLBAsect-hd#) durch 'einfaches' Patchen aus \ getLBAsect-hd# und (getLBAsect-hd#) heraus erzeugen? code (putLBAsect-hd#) ( hd# dlba# -- fl ) \ fl=0, wenn alles OK 18 # dap #) mov 1 # dap 02 + #) mov sectbuf # dap 04 + #) mov csegment # dap 06 + #) mov ax pop ax dap 0a + #) mov ax lba# #) mov ax pop ax dap 08 + #) mov ax lba# 2 + #) mov 0 # dap 0c + #) mov 0 # dap 0e + #) mov dx pop dx hd# #) mov dh dh xor si push dap # si mov 4300 # ax mov 13 int si pop ah al mov ax push next end-code : putLBAsect-hd# ( hd# dlba# -- fl ) \ fl=0, wenn alles OK depth 3 < \ hd# oder/und dlba# vergessen? if \ Ja, dann Stack saeubern und begin depth 0 > if drop 0 else -1 then until cr ." Fehler!" -1 exit \ Ausstieg unter fl=ffff then (putLBAsect-hd#) ; \ Die jetzt folgenden Forth-Worte bearbeiten Parameter aus den Bootsektoren. \ Ich gehe davon aus, dass im Sektorpuffer sectbuf ein (primaerer oder \ erweiterter) Bootsektor liegt. Festplatten- oder die Partitions-Nummern \ werden also nicht benoetigt: Die Namen der betreffenden Forth-Worte haben \ keine Endung -hd#. Das Einholen von Bootsektoren in den Puffer sectbuf (unter \ Einbeziehung von hd# und part#) wird am Schluss des Artikels besprochen. \ Jeder Bootsektor enthaelt einen BIOS-Parameter-Block, den BPB. Ich bin hier \ nur an FAT16 interessiert. FAT32 kommt spaeter dran. Die Sektoren werden als \ LBA-Sektoren (Zaehlung 0,1,2,...) aufgefasst. : getbyte/sect-fat16 ( -- n ) \ Anzahl Bytes pro Sektor, normalerweise 512d, sectbuf 0b + @ ; \ aber auch 1024, 2048 oder 4096 sind zulaessig. : putbyte/sect-fat16 ( n -- ) \ Anzahl Bytes pro Sektor, ... dito sectbuf 0b + ! ; : getsect/head-fat16 ( -- n ) \ Anzahl Sektoren pro Kopf, normalerweise 63d sectbuf 18 + @ ; : putsect/head-fat16 ( n -- ) \ Anzahl Sektoren pro Kopf, ... dito sectbuf 18 + ! ; : getsect/clust-fat16 ( -- c ) \ Anzahl Sektoren pro Cluster. Nur bis zu 65536 sectbuf 0d + c@ ; \ Cluster sind moeglich. Gueltige Werte sind \ 1,2,4,8,16,32,64,128 (alles dezimal). : putsect/clust-fat16 ( c -- ) \ Anzahl Sektoren pro Cluster, ... dito sectbuf 0d + c! ; : getsect/fat-fat16 ( -- n ) \ Anzahl Sektoren pro FAT aus sectbuf holen sectbuf 16 + @ ; : putsect/fat-fat16 ( n -- ) \ Anzahl Sektoren pro FAT in sectbuf legen sectbuf 16 + ! ; : get#head-fat16 ( -- n ) \ Anzahl Koepfe aus sectbuf holen sectbuf 1a + @ ; : put#head-fat16 ( n -- ) \ Anzahl Koepfe in sectbuf legen sectbuf 1a + ! ; : get#fat-fat16 ( -- c ) \ Anzahl FATs aus sectbuf holen sectbuf 10 + c@ ; \ In FAT16 immer c=2 : put#fat-fat16 ( c -- ) \ Anzahl FATs in sectbuf legen sectbuf 10 + c! ; \ In FAT16 immer c=2 : get#root-fat16 ( -- n ) \ Anzahl der Datei- oder Verzeichnis-Eintraege, sectbuf 11 + @ ; \ die im Root-Verzeichnis gespeichert werden \ koennen (je 32 Bit), normalerweise bis zu \ 512d. Lange Dateinamen (ab Windows 95) \ benoetigen mehrere 32-Bit-Eintraege. : put#root-fat16 ( n -- ) \ ... dito sectbuf 11 + ! ; : get#hidsect-fat16 ( -- d ) \ Anzahl Sektoren vor dem Bootsektor. Daraus sectbuf 1c + 2@ swap ; \ abs. Offset zum Root-Verzeichnis ermittelbar. : put#hidsect-fat16 ( d -- ) \ ... dito swap sectbuf 1c + 2! ; : get#resvsect-fat16 ( -- n ) \ Reservierte Sektoren: Zahl der Sektoren, mit sectbuf 0e + @ ; \ Bootsektor, die der ersten FAT vorausgehen. : put#resvsect-fat16 ( n -- ) \ Reservierte Sektoren: ... dito sectbuf 0e + ! ; : getdrv#-fat16 ( -- c ) \ c aus sectbuf holen. Zur Unterscheidung von sectbuf 24 + c@ ; \ Festplatte (80) oder Diskette (00). Nur fuer \ Bootlaufwerke von Bedeutung. : putdrv#-fat16 ( c -- ) \ c nach sectbuf legen. ... dito sectbuf 24 + c! ; : getmedid-fat16 ( -- c ) \ Media-Descriptor. f8 = Festplatte, sectbuf 15 + c@ ; \ f0 = High-Density-3,5"-Diskette, : putmedid-fat16 ( c -- ) \ ... dito: c nach sectbuf legen. sectbuf 15 + c! ; : get#small-fat16 ( -- n ) \ Anzahl Sektoren der Partition, die in 32 Bit sectbuf 13 + @ ; \ Platz finden (n<65536). Partitionen mit mehr \ als 65536 Sektoren haben hier den Wert 0 und \ get#large-fat16 wird wirksam. : put#small-fat16 ( n -- ) \ ... dito: n nach sectbuf legen. sectbuf 13 + ! ; : get#large-fat16 ( -- d ) \ Anzahl Sektoren der Partition, die in 32 Bit sectbuf 20 + 2@ swap ; \ keinen Platz finden (n>65536). Partitionen \ mit weniger als 65536 Sektoren haben hier den \ Wert 0 und get#small-fat16 wird wirksam. : put#large-fat16 ( d -- ) \ ... dito: d nach sectbuf legen. swap sectbuf 20 + 2! ; \ Einige dieser Parameter aus dem Bootsektor koennen (siehe oben) auch ueber \ den Interrupt 13h/48h ermittelt werden. Dieser erscheint mir fuer einen \ Reparaturfall als vertrauenswuerdiger, da er nicht so leicht von \ zerschossenen Tabellen auf der Festplatte beeinflusst werden kann. Oder \ irre ich mich? Ueber Hinweise wuerde ich mich freuen. \ Das folgende Forth-Wort getbootprim-hd# laedt den Bootsektor der primaeren \ Partition part# der Platte hd# in den Puffer sectbuf. Die genaue Funktion, \ auch die eingebaute Fehler-Erkennung und -Vermeidung geht aus den \ Kommentaren hervor. Die Sektor-Adresse des Bootsektors wird in der \ 2Variablen boot aufbewahrt, die Plattennummer hd# in der Variablen hd#. : getbootprim-hd# ( hd# part# -- fl ) \ fl=0, wenn alles OK depth 0 = \ hd# und part# vergessen? if -1 exit then \ Ja, dann mit fl=ffff raus. part# ! \ part# auch in die Variable part# depth 0 = \ hd# oder part# vergessen? if -1 exit then \ Ja, dann mit fl=ffff raus. hd# ! \ Eingabe hd# auch in die Variable hd# part# @ testpart# part# ! \ 0 < part# < 5 ? Nein, dann part#=1 hd# @ getmbr-hd# \ Versuch, MBR zu holen if cr ." HD-Fehler!" -1 exit then \ HD-Fehler? Ja, dann Ausstieg unter \ fl=ffff. Andernfalls liegt jetzt der \ MBR im Sektorpuffer sectbuf sectbuf 1ae + part# @ \ Ausgangsposition im Puffer sectbuf 10 * + 4 + c@ dup \ Offset 04 in MBR-Partitionszeile 0 = swap 0f and 5 = or \ Typ 05 oder 00 ? if cr ." Keine primaere Partition!" \ Ja, dann Fehler -1 exit \ und Ausstieg unter fl=ffff then hd# @ part# @ getLBAsect# \ Ansonsten jetzt Adr(Sektor-Nr.) 2dup boot 2! \ in der 2Variablen boot aufbewahren getLBAsect-hd# \ und dann den Bootsektor der dup if cr ." HD-Fehler!" then ; \ Partition part# nach sectbuf holen \ Und jetzt noch die Bootsektoren der erweiterten Partition (vergleiche das \ im Textteil ueber die Festplatten-Organisation Gesagte. Das Thema wurde \ schon bei getboot aus dem VD-Heft 3/2008 behandelt. Hier fuehre ich frei \ waehlbare Platten (nicht nur hd#=80), Fehler-Flags im Zusammenhang mit \ beweglichen Teilen und LBA-Sektoren (statt CHS) ein. \ Wichtig beim gleich folgenden Forth-Wort getEBR-hd#, das den EBR (extended \ boot record) des logischen Laufwerks drv# der erweiterten Partition von hd# \ in den Puffer sectbuf legt, sind vier Werte in den ersten beiden Zeilen der \ 'Partitionstabelle' (Offset 1be bis einschliesslich 1dd). Diese Tabelle \ des EBRs stimmt mit der Aufteilung der Tabelle des MBRs ueberein. Die Zeilen \ 3 und 4 bleiben aber beim EBR auf 0 gesetzt. Auch die anderen Bytes, bis auf \ die 'Magic Number' 55 aa bei Offset 1fe und 1ff, enthalten lauter Nullen. \ Offset 1c6: Abstand in Sektoren vom EBR (Laufwerksanfang) zum Bootsektor, \ Offset 1ca: Laenge in Sektoren des logischen Laufwerks, \ Offset 1d6: Abstand in Sektoren vom Anfang der erweiterten Partition zum \ Anfang des naechsten logischen Laufwerks (zu dessem EBR), \ Offset 1da: Laenge in Sektoren der gesamten erweiterten Partition. \ Ist die ganze Zeile des EBRs, in welcher die Eintragungen bei Offset 1d6 und \ 1da zu finden sein koennen, mit 0 belegt, dann war das betreffende logische \ Laufwerk das letzte in der erweiterten Partition. \ EBRs kann es nur in der erweiterten Partition geben. Die Bezeichnung des \ folgenden Wortes getEBR-hd# kann also entsprechend kurz gehalten werden. Hat \ man den EBR des gewuenschten logischen Laufwerks, dann kann man aus Offset \ 1c6 und der LBA-Nummer des EBRs die LBA-Nummer des zugehoerigen Bootsektors \ gewinnen (siehe gleich). Primaere Partitionen beginnen bei ihrem Bootsektor: \ Man kann sagen, ihre EBRs sind in den vier Zeilen der Partitionstabelle des \ MBRs enthalten. \ Beim folgenden getEBR-hd# wird zunaechst versucht, den MBR der Platte hd# \ nach sectbuf zu speichern (bei Fehler Ausstieg mit fl=ffff und der Meldung \ 'HD-Fehler!'). Gelingt es, dann wird die Partitionsnummer der erweiterten \ Partition ermittelt (bei Fehler Ausstieg mit fl=ffff und einer Meldung). Dann \ geht es an die Ermittlung der LBA-Adresse des EBRs des Laufwerks drv#. \ Dazu muss die verkettete Liste der EBRs bis zum logischen Laufwerk drv# \ durchlaufen werden und dann ist im EBR zu drv# noch die EBR-Adresse von drv# \ (in Sektoren ab dem Anfang der erweiterten Partition) zu ermitteln. Sodann \ wird der EBR zu drv# in den Sektorpuffer sectbuf geholt. \ Wurde bei der Stack-Eingabe fuer drv# (irrtuemlich) eine Zahl gewaehlt, die \ groesser ist als die vorhandene Zahl von logischen Laufwerken, dann behaelt \ sectbuf den EBR des letzten gerade noch vorhandenen logischen Laufwerks, es \ wird die Fehlermeldung 'Letztes logisches Laufwerk = drv#' (mit der dann \ geltenden konkreten Zahl fuer drv#) ausgegeben und getEBR-hd# wird mit \ fl=ffff verlassen. Klappt alles, dann bleibt fl=0 auf dem Stack. \ In jedem Fall wird die letzte noch brauchbare EBR-Adresse (als Sektor-Nummer) \ in der 2Variablen EBR festgehalten, von wo aus sie beispielsweise per hd# \ drv# getEBR-hd# (siehe folgendes Forth-Wort) in Verbindung mit einem \ anschliessenden 1 getlbasect# bei der Ermittlung des zugehoerigen Bootsektors \ helfen kann. \ Hier und anderswo haette man -rot erwartet. In ANS-Forth gibt es kein -rot. \ Ich schreibe dafuer rot rot und bin ANS-kompatibel. : getEBR-hd# ( hd# drv# -- fl ) \ fl=0, wenn alles OK depth 0 = \ hd# und drv# vergessen? if -1 exit then \ Ja, dann mit fl=ffff raus. drv# ! \ drv# auch in die Variable drv# depth 0 = \ hd# oder drv# vergessen? if -1 exit then \ Ja, dann mit fl=ffff raus. hd# ! \ Eingabe hd# auch in die Variable hd# hd# @ getmbr-hd# \ Versuch, MBR zu holen if cr ." HD-Fehler!" -1 exit then \ HD-Fehler? Ja, dann Ausstieg unter \ fl=ffff. Andernfalls liegt jetzt der \ MBR im Sektorpuffer sectbuf 0 tmp1 ! \ tmp1 ist jetzt Partitionszaehler begin sectbuf 1ae + tmp1 @ 1 + tmp1 ! tmp1 @ 4 > \ Schon mehr als 4 MBR-Zeilen? if \ Ja, dann Fehlermeldung, Zaehlwert cr ." Erweiterte Partition nicht vorhanden!" drop -1 exit \ entfernen und Ausstieg mit fl=ffff; then \ ansonsten tmp1 @ 10 * + 4 + c@ 0f and 05 = \ Offset 04 in MBR-Partitionszeile until \ = 05, dann raus: tmp1 = # erw. Part. tmp1 @ getLBAsect# \ Partitionsanfang der erw. Partition EBR1 2! \ LBA-Nummer in die 2Variable EBR1 hd# @ EBR1 2@ getLBAsect-hd# \ Den ersten EBR nach sectbuf holen if cr ." HD-Fehler!" -1 exit \ HD-Fehler? Dann Meldung und then \ Ausstieg mit fl=ffff 1 tmp1 ! \ Nun ist tmp1 Zaehler fuer die EBRs begin \ Verkettete EBR-Liste hd# @ EBR1 2@ tmp1 @ \ Wenn tmp1 bereits > 1, dann naechste 1 <> if 2 getLBAsect# d+ then \ EBR-Adresse (LBA) in die 2Variable 2dup EBR 2! getLBAsect-hd# \ EBR legen; LBA-Sektor nach sectbuf if cr ." HD-Fehler!" -1 exit \ HD-Fehler? Dann Meldung und then \ Ausstieg mit fl=ffff. Sonst: tmp1 @ drv# @ = \ Wenn tmp1 die eingegebene Nummer if \ des Laufwerks drv# erreicht hat, -1 \ dann raus (-1 fuer until) mit fl=0. else \ Sonst: 2 getLBAsect# or 0 = \ Naechster EBR-Offset = 0 ? Ja, dann if cr ." Letztes logisches Laufwerk = " tmp1 @ . ." oder EBR-Fehler (?)" -1 exit \ Fehlermeldung und raus mit fl=ffff. then \ Nein, dann tmp1 @ 1 + tmp1 ! 0 \ EBR-Zaehler um 1 weiterschalten und then \ weiter in Schleife mit 0 fuer until. until 0 ; \ Das folgende Forth-Wort getbootext-hd# legt den Bootsektor des logischen \ Laufwerks drv# der Festplatte hd# in den Sektorpuffer sectbuf und die \ LBA-Nummer dieses Bootsektors in die 2Variable boot. Dazu wird erst ueber \ getEBR-hd# der EBR geholt, dann aus dem EBR im Puffer sectbuf unter Offset \ 1c6 der Abstand (in Sektoren) des gesuchten Bootsektors vom EBR und \ schliesslich aus der 2Variablen EBR die LBA-Nummer des EBRs, die zum Abstand \ addiert wird. Das Wort ist gegen vergessenes hd# oder/und drv# abgesichert. : getbootext-hd# ( hd# drv# -- fl ) \ fl=0, wenn alles OK 2dup drv# ! hd# ! \ drv# und hd# aufbewahren und getEBR-hd# \ EBR von Laufwerk drv# holen 1 getLBAsect# EBR 2@ d+ boot 2! \ LBA-Nummer des Bootsektors nach boot if -1 exit then \ EBR-Fehler? Dann raus mit fl=ffff. hd# @ boot 2@ getLBAsect-hd# \ Sonst Bootsektor nach sectbuf if cr ." HD-Fehler! " -1 exit then \ HD-Fehler? Dann Meldung und Ausstieg 0 ; \ mit fl=ffff; sonst: fl=0