Ein MS-DOS Programm ohne Compiler entwickeln???

1. Die Programmiersprache: Assembler

Wer glaubt, die Programmiersprache Assembler gehöre nur in die Welt der Microcontroller und der antiken Homecomputer, irrt sich gewaltig!
Genau wie alle anderen Prozessoren lässt sich auch ein Pentium IV in Assembler programmieren. Und das sogar mit recht einfachen Hilfsmitteln. Ob es Sinn macht, einen Pentium mit Assembler-Code zu füttern kommt ganz darauf an, was man damit anstellen möchte. Insgesamt ist diese Art von Bitschubserei natürlich ziemlich umständlich.
Ich möchte Sie dennoch zu einem recht einfachen, aber sehr interessanten Versuch mitnehmen, nach welchem Sie sich sicher dem Herzen Ihres PCs etwas verbundener fühlen werden.

2. Das Werkzeug: Debug

Das MS-DOS Kommando debug erscheint heute wie der Schrei eines prähistorischen Dinosauriers. Probieren Sie's trotzdem mal aus und tippen Sie debug in der Eingabeaufforderung ein. Sie werden merken, dass nicht viel passiert. Es erscheint lediglich ein - als Eingabeprompt. Ein ? zeigt eine Liste aller debug-Befehle und mit q kommen Sie wieder raus, falls Sie schon jetzt keine Lust mehr haben. (Ein Hinweis an die Mausschubser: Jedes Kommando muss mit einem Druck auf die Return-Taste abgeschickt werden ...

3. Ein "Hello World" Programm in drei Schritten und mit Erklärung

1.Schritt
Wir rufen mit a den Zeilen-Assembler auf und sehen, dass der Assembler eine Eingabe an der Adresse 0100 erwartet.
Dort tippen wir einfach mal die folgenden Assembler-Anweisungen ein. Jede Zeile muss mit der Return-Taste bestätigt werden, worauf der Assembler uns zur nächsten verfügbaren Speicheradresse führt.


Die Anweisung jmp 0200 in der ersten Zeile sagt dem Rechner, dass unser eigentliches Programm erst an der Adresse 0200 anfängt.
Denn Platz davor benötigen wir als Speicher für unseren "Hello World"-Text. Die auszugebenden Zeichen werden mit der folgenden Anweisung db direkt im ASCII Code hexadezimal in den Speicher geschrieben. 0a und 0d sind die Zeichen für den Zeilenvorschub und den Wagenrücklauf (damit die Ausgabe am Anfang einer neuen Zeile erfolgt). Das "$" Zeichen schliesst die Zeichenkette ab. Daran erkennt der Rechner, dass der auszugebende Text zu Ende ist, denn ansonsten würden auch noch die Inhalte der folgenden Speicherzellen als Buchstaben interpretiert werden.

2.Schritt
Nun geben wir ab der Adresse 0200 unser Hauptprogramm ein. Sie können nun solange NOP (No Operation) in die Zeilen eingeben, bis diese Stelle erreicht ist, oder aber (viel schneller) dort hin springen.
Dazu geben Sie eine Leerzeile ein und dann nochmal a, allerdings mit der Adresse wo's hingehen soll.
Bei 0200 angekommen geben Sie dann das im nächsten Fenster ersichtliche Hauptprogramm ein.


Kernstück des Programms sind die beiden Aufrufe von Interrupt 21, im Code als int 21 zu finden. Diese lösen den "DOS-Interrupt" aus, der dann anhängig von den in den Registern gespeicherten Werten bestimmte Kunststücke (Ausgaben, Eingaben, Umschaltung des Grafikmodus, usw.) veranstaltet.

In unserem Fall möchten wir eine Ausgabe veranlassen. Dazu müssen wir vor Auslösen des Interrupts Wert 9 in das höherwertige Byte des AX-Registers schreiben. Genau das wird durch den Befehl mov ah,9 erledigt.

Nun muss man wissen, dass der Interrupt 21, wenn er mit 9 in AH aufgerufen wird genau die Zeichenkette ausgibt, welche er an der Adresse findet, die im DX-Register steht. Also müssen wir bevor wir den Interrupt auslösen die Speicheradresse an der unser Text beginnt ins DX Register schreiben.
Das erledigt der Befehl mov dx,0103, da unser Text an der Adresse 0103 (das ist die nächste Adresse nach dem JMP Befehl) beginnt. Die Endadresse oder Länge des Texts müssen wir nicht angeben, denn die wird automatisch durch das "$"-Zeichen erkannt.

Ist doch gar nicht so kompliziert. Doch wozu wird nun der Interrupt 21 nochmals bei der Adresse 0209 aufgerufen, und zwar mit dem Wert 0 in AH?
Ist doch logisch ... wir müssen dem Rechner sagen, wann unser Programm beendet wird. Beim zweiten Aufruf von Interrupt 21 wird also das Programm verlassen. (Ok, ok, als C++ oder Java Programmierer macht man sich darüber keine Gedanken. Dort wo die main() fertig ist, da ist halt Schluss ...)

3.Schritt
Wir starten unser Programm. Geben Sie einfach g für "Go" ein.


Unglaublich, aber wahr: Das Programm funktioniert.

Interessant ist es jetzt auch, sich den Assembler-Code ausgeben zu lassen.
Probieren sie mal u 0200. Sie werden zwischen den Adressen 0200 und 0209 Ihren Programmcode wiedererkennen. Den Text sich als Assembler Code ausgeben zu lassen wäre nicht besonders sinnvoll. Probieren Sie stattdessen mal d 0100 und bewundern Sie das Ganze hexadezimal und in Klartext.

4.Schritt
"Das kann ich aber in der Interpretersprache X (z.B. BASIC) auch!", werden Sie jetzt sagen.
Mag schon sein ... Doch jetzt wird's richtig cool! Wir speichern nämlich unser Programm jetzt direkt als ausführbare Datei ab, und das geht sonst ohne Compiler überhaupt nicht!

Allerdings müssen wir debug zuerst sagen, wie unser Programm heissen soll und über welchen Adressraum sich unser Programm erstreckt.


Mit n legen wir den Dateinamen fest.
Die Eingabe von h 020b 0100 ist nicht unbedingt erforderlich, wenn man gut im hexadezimalen Kopfrechnen ist. Dieser Befehl berechnet uns nämlich die Differenz zwischen 0100 (Startadresse) und 020b (Endadresse), also die Programmlänge. Das Ergebnis ist die zweite hexadezimale Zahl, die als Antwort ausgegeben wird, 010B.
Genau diesen Wert müssen wir nun ins Register CX schreiben. Das können wir direkt mit dem debug Befehl rcx.
Wenn wir anschliessend w eingeben, wird die Datei erzeugt und das Programm gespeichert.

Nach Beendigung von debug mit q finden Sie die entsprechende Datei in dem Verzeichnis, aus dem Sie debug aufgerufen haben. Da die Datei ausführbar ist, kann sie direkt im MS-DOS Fenster aufgerufen werden.

4. Weitere Informationen

Ich hoffe, Sie haben den Ausflug ins "Eingemachte" genossen und Appetit auf mehr Informationen über das Thema bekommen. Sie werden sich vielleicht fragen woher man weiss, welche Funktionen Interrupt 21 noch ausführen kann, wie man die Register belegen muss, wie man Eingaben von der Tastatur einlesen kann, usw.
Am Besten geben suchen Sie hierzu im Internet entsprechende Referenzlisten, z.B. durch Eingabe von +"int 21" +list als Searchstring in GOOGLE eingeben.

Trotz aller Euphorie jedoch noch eine Warnung zum Schluss:
Da mit debug direkt Speicherinhalte verändert und sogar Sektoren der Festplatte direkt angesprochen werden könnsn, sollten Sie nur diejenigen Funktionen und Interrupts verwenden, deren Folgen Sie kennen! Für jegliche Schäden übernimmt der Verfasser dieser Seite natürlich keinerlei Haftung!