\documentclass[11pt,a4paper]{article} \usepackage[german]{babel} \usepackage[utf8]{inputenc} % can use Umlauts now ü instead of "u \begin{document} \title{Bootmanager und FAT--Reparatur: Zehnter Fort(h)schritt ({\ttfamily putdiskimage})} \shorttitle{Bootmanager und FAT--Reparatur} \author{Fred Behringer} \maketitle \begin{multicols}{2} Der vorliegende Teil 10 meiner Artikelserie kann als Fortsetzung von Teil 8 betrachtet werden. In Teil 8 wurde gezeigt, wie man bei einem PC (bei mir konkret mit AMD--K6--2 als CPU) eine 3.5--HD--Diskette als Hex--Abbild ,an einem Stück` im RAM unterbringen kann (\texttt{getdiskimage}). Hier nun folgt ein Programm zum Zurückschreiben auf die (oder auf eine andere) Diskette (\texttt{putdiskimage}). Das Ganze spielt sich im Real--Mode ab, als Zusammenspiel von BIOS, Assembler und Forth. Es wird kein Int15h, kein DPMI, kein RAM--Management und insbesondere kein Abstecher in den Protected--Mode benötigt. Zentrales Mittel ist der (erweiterte) BIOS--Interrupt 13h (hier in der Schreibversion als 13h/3). Es wird auch kein 32--Bit--Forth--Assembler gebraucht. Der im 16--Bit--Turbo--Forth enthaltene 16--Bit--Assembler genügt --- wenn er mit einigen wenigen Ergänzungen versehen wird. Wichtig für die Analyse ist der mit viel Überlegung entwickelte 32--Bit--Dump--Apparat (\texttt{dump-32}). Selbstverständlich ist vorgesehen, das Disk\-image im RAM als Arbeitsfeld für Disk--Modifikationen, wie z.B. die Aufgaben aus Teil 4, zu verwenden. Die Automatisierung der dazu nötigen Parameter--Eingaben (Spielwiese für High--Level--Forth) soll für später zurückgestellt bleiben. Ich habe einige Dinge aus Teil 8 hier noch einmal aufgeführt. Für das Zurückschreiben des Disketten--Images auf Diskette kommt man damit mit der Eingabe \verb|forth include bootma10| [ret] durch, wobei forth.com das 16--Bit--Turbo--Forth--System ist und \texttt{bootma10.fth} das Programm aus dem Listing des vorliegenden Artikels. Will man ein vollständiges Programm (zum Lesen, Verarbeiten und Schreiben) haben, dann gehe man so vor, wie im Abschnitt ,Schnelleinstieg` beschrieben. Bei den hier entwickelten Mitteln zeigt sich die Stärke von Forth, wo man ganz schnell Dinge gestalten kann, die (im vorliegenden Beispielsfall) von Diskcopy (aus dem altehrwürdigen DOS) oder Rawwrite (z.B. \cite{JN00}) nicht immer erledigt werden können. \section{Voraussetzungen} Es mögen die in Teil 8 gemachten Voraussetzungen gelten. Aber auch nur diese. Weitere werden hier nicht benötigt. Am leichtesten tut man sich, wenn man das Listing aus Teil 8 mit dem Listing des vorliegenden Teils 10 zusammenführt und als einheitliches Ganzes betrachtet. Redundante Definitionen können dabei der Einfachheit halber beibehaltewn werden. Sie stören nicht. Ich gehe davon aus, dass ab der in der \texttt{2Variablen imgbuf} aufbewahrten RAM--Adresse der von (\texttt{getdiskimage}) sektorweise hexadezimal abgelegte Inhalt einer 3.5--HD--Diskette liegt, die den in Teil 8 \cite{FB1a} gemachten Voraussetzungen genügt. Natürlich --- und das ist im vorliegenden Teil volle Absicht --- kann das ein ,intelligent` abgeänderter Disketten--Image--Inhalt sein! An sich ist es völlig egal, wo das Disketten--Image für das hier zu entwickelnde putdiskimage herkommt. Es muss halt so aufgebaut sein, dass es beim Zurückschreiben eine sinnvolle Diskette ergibt. Die Übertragung des Disketten--Images aus dem RAM in die ,reale` Diskette ist der eigentliche Gegenstand des diesmaligen Teils 10 meiner Artikelserie. imgbuf soll als Punktzahl (32 Bit) eingegeben worden sein! Der Wert \texttt{2000000.} (Punktzahl!) wäre ein guter Wert für Eigenexperimente. Turbo--Forth unterscheidet nicht zwischen Groß-- und Kleinschreibung. Ich bevorzuge im Vorliegenden die Kleinschreibung. Sämtlichen Programmteilen liegt die Zahlenbasis hex zugrunde. \section{Schnelleinstieg} Es sei \verb| forth.com| das 16--Bit--Turbo--Forth--System.\\ Es sei \verb|bootma-8.fth| das Forth--Programm aus Teil 8 der Artikelserie.\\ Es sei \verb|bootma10.fth| das Forth--Programm des vorliegenden Teils 10. Man gebe ein: \verb|forth include bootma-8 include bootma10| [ret] . Damit steht dann alles zur Verfügung, um \begin{enumerate} \item[(1)] von einer 3.5--HD--Diskette in Laufwerk a: ein Image ins RAM zu schreiben, \item[(2)] das Image zu analysieren und gegebenenfalls zu verändern, \item[(3)] das Image dann auf (dieselbe oder eine andere) Diskette zu schreiben. \end{enumerate} Man erreicht das (beispielsweise) durch Eingabe von: \begin{enumerate} \item[(1)] \verb|2000000. getdiskimage|\\ Wert (Punktzahl!) weitgehend beliebig wählbar, \item[(2)] \verb|2000000. 100. dump-32|\\ Anfangs--Screen (auch \texttt{100.} ist Punktzahl!). \item[(3)] \verb|2000000. putdiskimage|\\ Zurück. Eventuelle Fehlermeldungen beachten. \end{enumerate} \subsection{Achtung} Das Forthwort \texttt{dump-32} bezieht sich (zumindest in der Voreinstellung) auf $\mathrm{fs=0}$. Wenn man beim Experimentieren beispielsweise per \begin{verbatim} 4711 2000000. cc!-32 2000000. cc@-32 \end{verbatim} etwas Falsches herausbekommt, hatte sich das Segment fs ursprünglich wahrscheinlich auf einen anderen Wert als 0 eingestellt. (Bei mir stellt sich fs gleich nach Beginn auf $\mathrm{fs=f000}$ ein.) Abhilfe schafft die Eingabe \texttt{0 fs!} vor der eben vorgeschlagenen Überprüfung mit 4711. \subsection{Schnelle Plausibilitätsüberprüfung} Um sicherzugehen, dass bei den Eingaben 1 bis 3 ,alles mit rechten Dingen zugegangen ist,` gebe man ein: \verb|100000. 100. dump-32| Bei mir begann dann die erste Zeile des Dumpings rechts mit der Eingangs--Kennung \dots{}VDISK3.3\dots{} für die RAM--Disk. Erklärung: Die zweifach includete Programmeingabe des vorausgegangenen Abschnitts stellt das System auf hex ein (und dabei soll es bleiben!). Die RAM--Adresse \texttt{100000.} (der Punkt gehört zur Zahl) enthält das erste Byte des ,Extended Memorys.` Dorthin legt das DOS--System bei mir normalerweise eine RAM--Disk (von 30\,MB Länge). Eine Eintragung in der config.sys sorgt dafür. Wenn ich das Disketten--Image nach \texttt{2000000.} lege, wird die RAM--Disk, wie man sich leicht ausrechnet, von dem Disketten--Image, welches aus getdiskimage hervorging, nicht angetastet. Ich kann und konnte also während meiner Experimente die RAM--Disk voll ausnutzen und alle benötigten Dateien in der RAM--Disk vorhalten. \subsection{Die Punkte 1 bis 3 auch auseinanderziehbar} Man gebe ein: \verb|forth include bootma-8| [ret] . Dann: \begin{enumerate} \item[(1)] \verb|2000000. getdiskimage| (ohne die Nummerierung und mit Abschluss [ret].) Dann kann man sich das Disketten--Image ansehen, es überprüfen und es gegebenfalls auch abändern per: \item[(2)] \verb|2000000. 100. dump-32| und Weiterschalten über die Plustaste, wobei die Änderungen per \texttt{c!-32} etc. vorgenommen werden können. Dann kann man aus dem ganzen Forth--System auch ruhig wieder herausgehen: \item[(2a)] \verb|bye| Das Disketten--Image bleibt (zumindest bei dem gewählten höheren Adresswert) erhalten, solange man das DOS--System nicht per Affengriff (oder sogar per Netzschalter) ausschaltet. Dann kann man eingeben: \verb|forth include bootma10| [ret] . Nun ist alles fürs Schreiben auf Diskette vorbereitet. Man lege, so man will, eine neue Diskette ins Laufwerk und leite das Schreiben ein durch: \item[(3)] \verb|2000000. putdiskimage| \end{enumerate} \section{Falsifizierung} Nicht jedem genügt die eben angeführte Plausibilitätsbetrachtung. Gerade bei solchen Übernahmen von ,Fremdprogrammen` wie dem im vorliegenden Artikel kann sich leicht ein Abschreibefehler einschleichen. Eine genauere Überprüfung wäre besser. Natürlich kann man mit noch so genauen Methoden nicht nachprüfen, ob das vorgeschlagene Verfahren immer zum Ziel führt. Man kann aber das Vertrauen in das generelle Funktionieren des (abgetippten oder sonstwie beschafften) Programms erhöhen, beispielsweise dadurch, dass man die ,geklonte` Diskette wieder einliest, diesmal aber beispielsweise an die Adresse 3000000. (andere Werte tun es natürlich auch): \begin{enumerate} \item[(1a)] \verb|3000000. getdiskimage| \end{enumerate} Und dann kann man die Bytes ab \texttt{2000000.} mit denen ab \texttt{3000000.} vergleichen. Im Listing habe ich dafür das Forth--Wort \texttt{falsify} vorgesehen. Das Wort \texttt{falsify} ist nur einer von vielen denkbaren Falsifikatoren, aber sicher ein plausibler: Das Image der Ursprungsdiskette wird Byte für Byte mit dem Image der geklonten Diskette verglichen. Bei den Bytes aus dem ersten Image, die sich vom korrespondierenden Byte aus dem zweiten Image unterscheiden, wird ein Aufaddierer (im Listing heißt er \texttt{falsi}) inkrementiert. Liefert \texttt{falsi} nach Beenden des Vorgangs den Wert 0, dann ist das Vertrauen in das richtige Funktionieren (des Klon--Programms) groß. Ganz sicher kann man natürlich nicht sein, denn es könnten ja beispielsweise sowohl im Lese-- wie auch im Schreib--Programm die CHS--Reihenfolgen zu CSH vertauscht worden sein (siehe Bemerkungen in Teil 8), was auf den Wert von falsi keinen Einfluss hätte. Sicher ist nur, dass im Gegenbeispielsfall (wenn falsi einen Wert ungleich null liefert) das Klonen nicht erfolgreich war. Außerdem könnte sich ja auch in den Falsifikator \texttt{falsify} ein Fehler (beim Abtippen oder schon beim Entwerfen) eingeschlichen haben. Und das würde das Thema Falsifizierung weiterverkomplizieren: Kann ein Falsifikator (als Aussage gefasst) in die zu falsifizierende Aussage einbezogen werden, ohne dass das Ganze, ähnlich der Menge aller Mengen, die sich selbst nicht enthalten, zu einem Widerspruch in sich führt. Liegt ein Fehler vor, dann stellt der von \texttt{falsi} gelieferte Wert ein Maß dafür dar, wie ,stark` die geklonte Diskette von der Ursprungsdiskette abweicht. (Diesen Gedanken könnte man natürlich ausbauen: ,Forth und die Philosophie.`) Über ,Falsifizierung` (im Popperschen Sinne) kann man sich per ,Google --- Wikipedia --- Falsifizierung` Informationen verschaffen. \subsection{Falsifikator für was?} Ich versuche es einmal mit folgender Formulierung: Das Forth--Wort \texttt{falsify} (siehe Listing) ist ein Falsifikator für die Vermutung "`Die durch ad1 und ad2 charakterisierten Disketten--Images sind zwei Images ein und derselben Diskette."' \begin{enumerate} \item Sind die Strings ad1 und ad2 byteweise gleich, dann kann man den Satz vom unzureichenden Grund aus der Entscheidungstheorie anwenden und sagen, es liegt kein Grund vor, an der eben aufgestellten Vermutung zu zweifeln. Sicher sein kann man natürlich nicht: Eine beliebige gleichlaufende Umordnung beider Strings würde ja zum gleichen Ergebnis führen, und darunter wären auf jeden Fall auch solche, die gar keine Images von Disketten sein können. Beispiel: ,Null--Images.` \item Sicher ist aber eines (Falsifizierungsfall): Ist auch nur ein einziges Paar von Bytes aus den beiden Disk--Images im RAM voneinander verschieden, dann ist unsere Vermutung zu Fall gebracht. Dann kann es sich nicht um zwei Images ein und derselben Diskette handeln. \item Das Ganze ist natürlich stark abhängig von einer sauberen Definition dessen, was wir unter einem Disketten--Image verstehen wollen. \end{enumerate} \subsection{Kaputte Diskette wegwerfen?} Angenommen, das eine Image stammt von einer als einwandfrei erkannten ,Original--Diskette.` Weiterhin angenommen, wir haben damit per putdiskimage eine Kopie--Diskette erstellt. Und schließlich angenommen, wir haben von dieser Kopie--Diskette aus ,Sicherheitsgründen` ebenfalls ein Image (,zweites Image`) per getdiskimage angefertigt. Der Bytepaar--Vergleich läuft in meinem Programm (kann natürlich auch anders eingerichtet wrden) von höheren zu niedrigeren Adressen. Es bot sich an, die Adresse des zuletzt ermittelten Unstimmigkeits--Paars (falls es überhaupt ein solches gibt) in der Bildschirm--Statistik festzuhalten. Das geschieht denn auch (siehe Listing). Damit haben wir ein schnelles Mittel an der Hand, herauszubekommen, ab welchem Byte auf die Image--Werte kein Verlass mehr ist. \subsection{Einbettung des Disk--Images in eine Datei im RAM} Inzwischen habe ich mich über Teil 8 hinaus mit dem ,Handling` des Images der Diskette (im RAM, zur Aufbewahrung oder/und zur Weitergabe) beschäftigt und in Teil 9 der Artikelserie \cite{FB1b} Vorschläge unterbreitet. \section{Programm--Aufbau} Dem in Teil 8 Gesagten lag der Leseteil des Interrupts 13h, also dessen Funktion 2, zugrunde. Hier in Teil 10 ist es der Schreibteil, also die Funktion 3 von Int 13h. Entsprechend stark ausgeprägt ist die Analogie des vorliegenden Programms putdiskimage zu getdiskimage aus Teil 8. \subsection{Hauptlast im vorliegenden Artikel} trägt das Zubringer--Wort (putdiskimage), die Klammern gehören zum Forth--Wort, das vollständig in Forth--Assembler geschrieben ist. Das High--Level--Wort putdiskimage ruft (putdiskimage) auf und nutzt die im Forth--System schon vorhandene Organisationsgewalt aus. Es läuft so ziemlich alles im vorliegenden Teil 10 analog zu dem in Teil 8 Gesagten. Zur Erinnerung darf ich ein paar Erklärungen in Form von Beispielen (mit konkreten Eingabewerten) aufziehen. \subsection{\ttfamily 2000000. getdiskimage} legt ein Disketten--Abbild nach Adresse \texttt{2000000.} --- der Punkt gehört zum Forth--Wort. Das zugrunde gelegte Turbo--Forth ist ein 16--Bit--System. Eine 3.5--HD--Diskette fasst 1.44 Megabyte. Mit (einfachgenauen) 16--Bit--Zahlen für die Adressen komme ich also (auch schon wegen des begrenzten PC--RAM--Bereichs) nicht durch. Punktzahlen, wie die eben genannte, sind doppeltgenau (32 Bit breit). Über mögliche RAM--Organisationen (wie beim Aufbau von Dateisystemen) habe ich hier (immer noch) nicht weiter nachgedacht. Ich bewege mich mit Turbo--Forth in einem DOS--System und agiere nach dem Motto: Mein RAM gehört mir und ich kann damit machen, was ich will. (Übrigens befolgt ja DOS ein solches Motto mit .com--Dateien, die bei Aufruf (zunächst einmal) den gesamten PC--RAM--Speicher zur Verfügung gestellt bekommen, auch.) \texttt{2000000.} ist ein leicht merkbarer ,runder` Wert. Ich habe mit Erfolg auch mit anderen Werten experimentiert. (Der Eingabe--Parameter ist eine Punktzahl, doppeltgenau, also 32 Bit breit. Den Punkt nicht vergessen!) \subsection{High--Low--Vertauschung in Forth} Man beachte die bei doppeltgenauen Zahlen (Punktzahlen) üblicherweise vorzufindende Vertauschung von höherwertigem und niederwertigem Anteil! Formt man aus einer Turbo--Forth--Punktzahl (z.B. aus dem Inhalt von imgbuf) dann eine 32--Bit--Assembler--(Ganz--)Zahl (wie beispielsweise zum Einlagern in das CPU--Register eax), dann muss aus der Punktzahl erst eine Little--Endian--Zahl gemacht werden! \subsection{Nachträgliche Veränderungen am Disk--Image} lassen sich mit den über \texttt{dump-32} gewonnenen Adress--Informationen per \texttt{!-32}, \texttt{@-32} etc. gezielt vornehmen. Wegen näherer Erklärungen vergleiche man das in Teil 8 Gesagte. Will man solche Veränderungen per \texttt{dump-32} überprüfen, dann achte man darauf, vorher (oder zumindest zwischendurch) $\mathrm{fs = 0}$ zu setzen (zu erreichen über 0 fs!), da \texttt{dump-32} (allerdings nur im Default--Fall) mit einem nullgesetzten Segment fs arbeitet. (Im Real--Mode berücksichtigt das System die Segment--Register fs und gs nur 16 Bit breit.) \subsection{Ich verwende eine 16/32--Bit--Mischung} Will man beispielsweise den Disk--Spuren--Puffer trackbuf komfortabel über \texttt{dump-32} auslesen (statt einfach über das in Turbo--Forth eingebaute dump), dann lese man sich vorher den Abschnitt mit der gleichen Überschrift in Teil 8 und die daran anknüpfenden Abschnitte aufmerksam durch! \subsection{Liefert {\ttfamily putdiskimage} des vorliegenden Teils 10 einen Schreibfehler,} dann wird der Schreibversuch für die gerade anstehende Disketten--Spur wiederholt. Nach drei hintereinander aufgetretenen Schreibfehlern wird das Programm abgebrochen. Am Bildschirm wird beim Aussprung dann die erreichte Seite, die erreichte Spur, die Adresse des zuletzt übertragenen Bytes und die Angabe, ob ein Fehler vorliegt oder nicht, angezeigt. \section{Abschließendes Experiment mit Nulldiskette} Ich habe eine voll beschriebene DOS--Diskette ins Laufwerk a: gelegt und mein Gesamtsystem aufgerufen: \begin{enumerate} \item[(1)] \verb|forth include bootma-8 include bootma10| [ret] \item[(2)] \verb|2000000. getdiskimage | [ret] $\longrightarrow$ Kein Lesefehler \item[(3)] \verb|2000000. 3000000. falsify| [ret] $\longrightarrow$ \texttt{151b8e} unstimmige Bytepaare \item[(4)] \verb|3000000. putdiskimage | [ret] $\longrightarrow$ Kein Schreibfehler \item[(5)] \verb|2000000. getdiskimage | [ret] $\longrightarrow$ Kein Lesefehler \item[(6)] \verb|2000000. 3000000, falsify| [ret] $\longrightarrow$ Kein unstimmiges Bytepaar \end{enumerate} \subsection{Interpretation} Alle Bytes, insbesondere auch die ab \texttt{2000000.} bis \texttt{2161f00.} und die ab \texttt{3000000.} bis \texttt{3161f00.} waren anfangs null. In Punkt 2 wurde ein Disk--Image nach \texttt{2000000.} gelegt. Das Falsifizieren in Punkt 3 lieferte nicht die Höchstzahl an unstimmigen Bytepaaren (\texttt{161f00}): Die Diskette enthielt ja auch Nullbytes --- und die wurden mit 0 verglichen, was zu stimmigen Bytepaaren führte. In Punkt 4 wurde eine ,Nulldiskette` hergestellt (alle Bytes, aber auch wirklich alle, gleich 0)! Davon wurde in Punkt 5 ein Disk--Image angefertigt und nach \texttt{2000000.} gelegt. Und das Falsifizieren in Punkt 6 ließ erkennen, dass die Diskette jetzt tatsächlich zur Nulldiskette geworden war. \subsection{Diskette schreddern} Der ursprüngliche Disketten--Inhalt, oh Schreck, war natürlich weg ;--) War aber nicht weiter schlimm, da das ja sowieso eine Experimentier--Diskette war. Ich hätte in Punkt 5 und 6 mit z.B. \texttt{4000000. statt mit 2000000.} arbeiten können --- und das ursprüngliche Image aus \texttt{2000000.} anschließend zurückschreiben. Jedenfalls habe ich auf diese Weise gezeigt, wie man eine Diskette inhaltlich ganz schnell und ganz leicht schreddern kann. Forth macht's (auch mir als Amateur) möglich! Dazu brauche ich keine Spezialprogramme aus dem Internet. \subsection{Turbo--Forth, und kein ZF?} Auch im vorliegenden Artikel gilt das in Teil 8 im Abschnitt mit der gleichen Überschrift Gesagte. Das Thema ,Anpassung an ZF` verschiebe ich auf später. \begin{thebibliography}{FB99} \bibitem[FB98]{FB98} Behringer, Fred: Real--Mode--32--Bit--Erweiterung für Turbo--Forth. Vierte Dimension 2/1998. \bibitem[FB08]{FB08} Behringer, Fred: Erster Teil meiner VD--Artikel--Serie. Vierte Dimension 3/2008. \bibitem[FB09]{FB09} Behringer, Fred: Vierter Teil meiner VD--Artikel--Serie. Vierte Dimension 2/2009. \bibitem[FB1a]{FB1a} Behringer, Fred: Achter Teil meiner VD--Artikel--Serie. Vierte Dimension 1/2011. \bibitem[FB1b]{FB1b} Behringer, Fred: Neunter Teil meiner VD--Artikel--Serie. Vierte Dimension 2/2011. \bibitem[JN00]{JN00} Newbigin, John: \url{http://www.chrysocome.net/rawwrite} \dots{} (2000). \end{thebibliography} \end{multicols} \section{Listing} \begin{quote} \begin{small} \listinginput[1]{1}{2011-03/bootma10.fth} \end{small} \end{quote} \end{document}