Systemnahe
Programmierung

E-Books


MS-DOS Kurs


4. Direkter Zugriff auf Dos

Schauen wir uns als nächstes an, wie die Schnittstellen des Betriebssystems direkt angesprochen werden - was für einige Probleme auch in Hochsprachen unvermeidbar ist. Während andere Betriebssysteme über eine streng definierte und hochgelegene Systemschnittstelle verfügen, ist bei DOS das Kraxeln in den Niederungen der Interrupts und Bytes gang und gäbe und leider auch für bestimmte Anwendungen unumgänglich.
Mit der internen Dreiteilung des Betriebssystems MS-DOS wurden Sie bereits bei der Behandlung der Systemdateien bekannt gemacht. Hardwarenächster Teil ist das BIOS (Basic Input Output System). Es steht zum Teil im ROM, zum anderen in der Datei IO.SYS (IBMBIO.SYS bei IBM). Hier werden elementare Funktionen zur Ansteuerung der einzelnen Komponenten (Tastatur, Bildschirm, Schnittstellen) zur Verfügung gestellt. Verschiedene Hardwarekomponenten benötigen dementsprechend auch verschiedene BIOS-Routinen. Darauf setzt das BDOS (Basic Disk Operation System) oder kurz DOS auf, dessen Name bereits darauf hindeutet, daß besondere Operationen für Datenträger in kompfortabler Form (Arbeit auf Dateiebene, im Gegensatz zu Lese- und Schreiboperationen auf Sektorebene) angeboten werden: Aber auch andere Funktionen stehen hier auf einem höheren Level, beispielsweise die Ausgabe von Zeichenketten auf den Bildschirm, was im BIOS zeichenweise und ohne automatisches Weiterstellen des Curors erfolgen muß. Das BDOS befindet sich in der Datei MSDOS.SYS (bzw. IBMDOS.SYS bei IBM) und wird beim Booten des Systems installiert.
Die eigentliche Schnittstelle zu den Anwendungsprogrammen (bzw. den Compilern mit denen sie erzeugt werden) sollte das BDOS sein. Da der Zwang zu sauberer Programmierung bei einem Einbenutzersystem wie MS-DOS aber ohne Belang zu sein scheint (?), halten sich heutzutage nur die wenigsten Programme daran und greifen einfach auf das BIOS zu oder sprechen direkt die Hardware an. Jedes Programm, das etwas auf sich hält, schreibt in den Bildschirmspeicher, statt Systemfunktionen zu nutzen. Dem Programmierer sollte man deswegen nicht unbedingt Vorwürfe machen, nimmt er doch sogar Mehrarbeit in Kauf - die Anwendungen fordern einfach ein vernünftiges Zeitverhalten.
An einem kleinen Beispiel können Sie das ganz einfach selbst überprüfen. Turbo-Pascal bietet seine Standard-Bildschirm-Ausgabe sowohl über das BDOS als auch über das BIOS an. Entsprechend gibt es zwei Write-Funktionen, eine in der Unit SYSTEM und eine in der Unit CRT. Ist die Unit Crt eingebunden (durch USES CRT), so wird über das BIOS auf den Bildschirm geschrieben, andernfalls über das BDOS. Um sicher zu gehen, die richtige Funktion aufzurufen, kann der Unitname explizit angegeben werden, also SYSTEM.Write oder CRT.Write. Als kleinen Test können Sie ja mal 10 000 Zeichen in einer Schleife in beiden Varianten einzeln auf den Bildschirm ausgeben. Sie werden sehen, daß die gestellte Aufgabe vom BIOS deutlich schneller (in Turbo-Pascal um etwa ein Drittel) bewältigt wird, da der Overhead im BDOS (insbesondere das Prüfen von Steuerzeichen wie <CTRL><S> oder <CTRL><C>) nicht zum Zuge kommt. Noch deutlich schneller wäre das direkte Schreiben in den Bildschrimspeicher, allerdings hätte man sich um einige Aufgaben selbst zu kümmern, etwa das Scrollen beim Erreichen der letzten Cursor-Position.
Stellt sich die Frage warum dann nicht immer die schnellen Funktionen verwendet werden. Weil sie leider auch Nachteile haben; am deutlichsten werden diese beim Schreiben in den Bildschirmspeicher. Dieser beginnt bei den Grafikstandards CGA und VGA auf Segment B800H und bei Hercules bei B000H, so daß also die Hardware vorher ermittelt werden muß. Auch dafür gibt es eine Möglichkeit, die in den meisten Fällen erfolgreich ist (Test der Eintragung auf Adresse 40:49H). Die nächste Frage wäre, ob auch alle Grafikstandards (besonders zukünftige) bedacht wurden und ob nicht ein bestimmter Hersteller doch ein wenig davon abweicht. Was in solchen Fällen passiert, haben Sie bestimmt schon einmal erlebt. Während sich derlei Probleme im Textmodus noch meistern lassen, ist spätestens im Grafikmodus Schluß. Der Weg über das BIOS ist in den meisten Fällen unkritisch und deshalb wird er auch von den meisten "besseren" Programmen beschritten (außer für die Dateiarbeit). Es gibt aber auch Gründe, auf das BDOS aufzusetzen. Bestes Beispiel ist die Ausgabe von Escape-Sequenzen an den Treiber ANSI.SYS. Nur eine Ausgabe über BDOS führt zu ihm und bewirkt die bekannten Steuerungen; das BIOS würde die entsprechenden Sonderzeichen einfach auf den Bildschirm schreiben. Sie können das mit Write-Funktionen von Turbo-Pascal leicht nachprüfen.
Kern der im DOS-Interrupt 21H zusammengefaßten Funktionen ist die Dateiarbeit, so daß man das Gros der Funktionen niemals direkt aufrufen wird. Wie wäre es aber zum Beispiel mit Ermittlung der DOS-Version ?
Um eine DOS-Funktion auf der Ebene des Betriebssystems direkt aufzurufen, muß man sich in die Niederungen des Prozessors hinabbegeben, wenngleich dazu heute kein Assembler mehr notwendig ist. In Turbo-Pascal gibt es die Funktion MsDos, die eine Datenstruktur vom vordefinierten Typ Registers erwartet, in welche die Werte eingetragen werden, die beim DOS-Aufruf in den Prozessorregistern stehen müssen. Nach der Rückkehr ist die Struktur dann mit den Ergebnisparametern gefüllt. Die Funktion MsDos lädt nur die Register und ruft dann den Interrupt 21H auf, unter dem fast alle DOS-Funktionen zu finden sind. Die Unterfunktion zur Ermittlung der DOS-Version trägt die Nummer 30H; sie wird jeweils im Register AH erwartet. Das Laden von AX mit 3000H bewirkt, daß der zweite Teil des Registers gleich mit Nullen gefüllt wird (siehe folgende Tabelle). Bei der Rückkehr steht in AL (der "unteren" Hälfte von AX) die Hauptversion (vor dem Punkt) und in AH (dem "oberen" Teil von AX) die Unterversion (siehe auch Tafel 1).

USES DOS;
VAR Regs: REGISTERS;

BEGIN
    Regs.AX:=$3000;
    MSDOS(Regs);
    Write('DOS ',Regs.AL,'.',Regs.AH);
END.

Tabelle 1
Tafel Allgemeine Register des Prozessors 80x86 und Nachfolger
AH AL AX
BH BL BX
CH CL CX
DH DL DX
Mit ganz wenigen Zeilen ist dieses nicht ganz einfache Problem also abgehandelt. Erwähnt werden sollte an dieser Stelle noch, daß ein Befehl wie Regs.AX=$3000; nicht dazu verwendet werden kann, den Inhalt der Prozessorregister zu ändern. Mit dem Befehl wird nur eine interne Datenstruktur gefüllt; das Laden der Register geschieht erst beim Aufruf von MsDos.
Wenn Sie sich die obige Tafel genau angeschaut haben, werden Sie bereits wissen, daß die Arbeit in Turbo-Pascal gar nicht nötig gewesen wäre, den es gibt ja die Funktion DOS-Version. Das prinzipielle Vorgehen ist aber für alle Unterfunktionen im Interrupt 21H das gleiche, und in Assembler würde Ihnen nichts weiter übrig bleiben, als die Befehlssequenz
MOV AX,300H
INT 21H ;Auswertung von AX
einzugeben, da keine höhergelegenen Funktionen zur Verfügung stehen.
Als zweites Beispiel wollen wir im nachfolgendem Bild die Ermittlung des freien Speicherplatzes auf der Platte - wie oben bereits gezeigt - diesmal "von Hand" ansehen. Der Ablauf ist dem des ersten Beispiels sehr ähnlich. Die Unterfunktionsnummer ist in diesem Falle 36H, in DX ist das Laufwerk einzutragen (0 = aktuelles, 1 = A:, 2 = B:, 3 = C:).
Rückgabewerte sind:
AX Sektoren/Cluster
BX freie Cluster
CX Byte/Sektor
DX Cluster/Laufwerk

USES DOS
VAR Regs: REGISTERS;
                  Free: REAL;

BEGIN
    WITH Regs DO BEGIN
       AX:=$3600;
       DX:=3;              {LW. C:}
       Intr($21,Regs);
       Free:=AX*BX;             {Frei Cluster * Sektoren}
       Free:=Free*CX;             {Mal Bytes/Cluster}
       Free:=ROUND(Free(1024);
       WRITE(Free:10:0,' KByte frei');
    END;
END.
Bild Ermittlung des freien Speicherplatzes auf der Platte
Mit einer Dokumentation der einzelnen DOS-Funktionen (die hier bei weitem den Rahmen sprengen würde), wäre es so möglich, auch andere DOS-Funktionen nach diesem Beispiel aufzurufen. In der Tafel 2 sind die restlichen Interrupts aufgelistet, die zum DOS gezählt werden; die meisten Dienste liegen aber im Interrupt 21H. (Tafel finden Sie im Anschluß)
Tafel 2 DOS-Interrupts
Interrupt Funktionen
20h Programm beenden
21h Sammelinterrupt (Zusammenfassung der meisten DOS-Funktionen)
22h Adresse der Steuerung nach Beendigung eine Programms
23h Adresse zur Behandlung von <CTRL>>BREAK> bzw. <STRG>>UNTBR>
24h Adresse zur Behandlung kritischer Fehler
25h Absolutes Disk-Schreiben
26h Absolutes Disk-Lesen
27 Programm beenden und resident belassen
28h DOS-Timeslice-Interrupt (wird aufgerufen, wenn DOS nicht beschäftigt ist)
2Ah MS-NET-Funktionen
2Eh Ausführen eines DOS-Kommandos (undokumentiert)
2Fh Multiplex-Interrupt (wird zur Koordination residenter Programme genutzt)

(c) Jürgen Richter