Adok's Way to Assembler
Folge 2
Begleit-Dateien:
Nun sind wir endlich bei unserem ersten Beispielprogramm angelangt!
MODEL TINY ;Für COM-Files
CODE SEGMENT ;Beginn Code-Seg
ASSUME CS:CODE;Zuweisung an CS
ORG 100h ;Startadresse COM
start: ;Startlabel
MOV AX,0902h ;Funkt. 9h, ASCII-Code 2h
MOV BX,15 ;Farbcode Weiß
MOV CX,1 ;Anzahl der Zeichen = 1
INT 10h ;Zeichen auf Screen bringen
MOV AX,4C00h ;Funkt. 4Ch
INT 21h ;DOS-Exit
CODE ENDS ;Ende Code-Seg
END start ;Ende des Proggys
Um aus diesem Listing eine ausführbare COM-Datei zu erzeugen, tippt es mit dem
MS-DOS-Editor ab, speichert es in eurem TASM-Verzeichnis ab und gebt unter DOS
folgendes ein:
TASM /T/L progname => erzeugt die Datei progname.OBJ
TLINK progname /t => linkt die OBJ-Datei zu COM
'progname' steht hierbei für den Namen des Programms ohne Dateierweiterung.
Habt ihr das Listing beispielsweise als KURS0001.ASM gespeichert, müßt ihr
statt progname KURS0001 schreiben.
+++ Interrupt 10h Funktion 9 +++
Wenn ihr anschließend das COM-File startet, erscheint ein schöner Smiley auf
eurem Bildschirm! Wie funktioniert das? Ganz einfach: Das Proggy benutzt den
Funktion 9 des Interrupts 10h, welche ein einzelnes Zeichen auf dem Bildschirm
ausgibt, ohne die Cursorposition zu ändern. Das heißt, wenn danach noch einmal
dieser Interrupt aufgerufen wird, überschreibt das neue das alte Zeichen. So
funktioniert's: AH=9, AL=ASCII-Code des Zeichens (im Beispiel 2), BH=Nummer
der Bildschirmseite, auf die geschrieben werden soll (i.B. 0), BL=Farbattribut
des Zeichens (i.B. 15), CX=wie oft das Zeichen ausgegeben werden soll (i.B.
1). Doch bevor wir auf den "Sinn" des Programms näher eingehen, schauen wir
uns an Hand dieses Beispiels den Aufbau einer COM-Datei an.
+++ Aufbau einer COM-Datei +++
Zeile 1: MODEL TINY Gibt das Speichermodell an. Für COMs kommt nur TINY
in Frage. Für EXEs gibt's auch:
SMALL: Codeseg und Dataseg zusammen kleiner 64 KB
MEDIUM: Code < 64 KB, Summe aller Datasegs > 64 KB
COMPACT: ein Codeseg, mehrere Datasegs
LARGE: Summe der Segmente > 64 KB
HUGE: alles ist erlaubt!
Summe bedeutet, daß das Proggy mehrere Codesegmente,
Datasegmente,... haben kann, aber ein einzelnes Seg
nicht größer als 64 KB sein darf.
Zeile 2: CODE SEGMENT: Das ist der Anfang des Codesegs, in dem (normaler-
weise) die Befehle eines Programms stehen!
Zeile 3: ASSUME CS:CODE Dadurch wird dem CS-Register befohlen, auf das Code-
segment zu zeigen.
Zeile 4: ORG 100h Besonders wichtig bei COM-Dateien! Diese beginnen
immer bei der Adresse CS:100h. (Erinnert ihr euch?
Das ist Segment:Offset-Schreibweise!) Der Grund dafür
ist, daß DOS beim Starten eines Programms ein soge-
nanntes PSP einfügt, das von CS:0 bis CS:FFh reicht.
Zeile 5: start: Ein Label! Genauso wie in Basic bestehen sie aus
einem Namen und einem Doppelpunkt. Jede COM-Datei
benötigt ein solches Label am Anfang des Programms.
Bei EXEs ist es nicht notwendig. Man kann natürlich
auch innerhalb des Programms Labels verwenden, die
sich mit den verschiedenen Jump-Befehlen anspringen
lassen.
Zeile 12: CODE ENDS Ende des Codesegs.
Zeile 13: END start Ende des Programms.
+++ Der MOV-Befehl und seine Varianten +++
So, das ist es! Auf den ersten Blick scheint es sehr viel zu sein, ist es aber
nicht. Nun zum eigentlichen Programm... Die 6. Zeile lautet MOV AX,0902h. Hier
haben wir einen neuen ASM-Befehl. MOV entspricht der Zuweisung in
Hochsprachen, man kann damit einer Speicheradresse oder Register einen Wert
zuweisen. Syntax: MOV Ziel,Quelle. Die Quelle ist der Wert, der dem Ziel
zugewiesen werden soll. Dabei gibt es mehrere Varianten:
MOV AX,0902h Eine Konstante wird einem allgemeinen Register
zugewiesen.
MOV AX,BX Der Inhalt eines allgemeinen Registers wird einem
anderen allgemeinen Reg zugewiesen.
MOV AX,[0A000h] Einem allgemeinen Reg wird der Inhalt der Speicherstelle
DS:0A000h zugewiesen. Was in eckigen Klammern steht, ist
also der Offset.
MOV AX,ES:[0A000h] Mit Segment-Override: Statt DS:0A000h wird AX der Inhalt
von ES:0A000h zugewiesen.
MOV AX,[BX] Besonders tricky: indirekte Adressierung. AX wird der
Inhalt der Speicherstelle zugewiesen, deren Segment DS
und deren Offset der Inhalt von BX sind. Ist BX also
beispielsweise 1000h, wird AX der Inhalt von DS:1000h
zugewiesen. Doch Vorsicht: Die indirekte Adressierung
funktioniert nur mit einigen Registern wie BX und BP!
Man kann also nicht MOV AX,[DX] schreiben. Dafür geht
indirekte Adressierung auch mit Segment Override.
MOV WORD PTR 1000h,AX Der Inhalt von AX wird auf die Speicherstelle DS:1000h
geschreiben.
MOV AX,0 HOT TIP! Diese Variante ist zwar nicht verboten, aber es
gibt eine viel schnellere Möglichkeit, ein allgemeines
Register auf 0 zu setzen: XOR AX,AX! Hiermit wird eine
XOR-Bitverknüpfung zwischen AX und sich selbst durchge-
führt. Folge: Der Inhalt von AX wird gelöscht.
Folgende Varianten funktionieren jedoch NICHT:
MOV ES,0A000h Einem Segmentregister kann keine Konstante oder der
Inhalt einer Speicherstelle zugewiesen werden! Hier muß
man den Umweg mit einem allgemeinen Register machen,
also: MOV CX,0A000h und dann MOV ES,CX.
MOV DX,[AX] Indirekte Adressierung funktioniert nur mit Registern
wie BX, BP, DI und SI.
Noch etwas zu WORD PTR: Wenn man einer Speicherstelle/Variablen etwas zuweist
oder ihren Inhalt einem Register zuweist, ist es manchmal unklar, ob ein Byte
oder ein Word (2 Bytes) gelesen/geschrieben werden soll. Aus diesem Grund kann
man vor einer Speicherstelle BYTE PTR bzw. WORD PTR schreiben. Beispiel:
MOV WORD PTR 1000h,AX schreibt 2 Bytes an die Adresse 1000h. Dadurch wird auch
der Inhalt der Adresse 1001h verändert!
MOV BYTE PTR 1000h,AL schreibt 1 Byte an die Adresse 1000h. Hier bleibt die
Adresse 1001h unverändert.
+++ Zum Beispielprogramm +++
ENDLICH! Das hat gedauert. Aber Theorie muß sein, besonders in Assembler. Nun
können wir uns das Proggy unter die Lupe nehmen.
Zeile 6: MOV AX,0902h Damit wird AX der Wert 0902h zugewiesen. Hm, was soll
das bedeuten? Genau nachdenken, bevor ihr weiterlest!
Ein Tip: Um ein Zeichen mittels INT 10h Funktion
(Ufo :-) ) 9 auszugeben, müssen in AH die Funktions-
nummer (also 9) und in AL der ASCII-Code stehen. AH
und AL sind... Na, ein Licht aufgegangen? Richtig, AH
ist das Hi- und AL das Lo-Byte von AX! Wie wir in
Folge 1 festgestellt haben, ist ein Word vier Hex-
Stellen groß. Die oberen zwei sind das Hi-, die
unteren das Lo-Byte. Genau das nützen wir hier aus:
Wenn wir AX die vierstellige Hex-Zahl 0902h zuweisen,
erhält automatisch AH den Wert 9 und AL 2. Das spart
einige Nanosekunden Rechenzeit! (Wir ASM-Coder sind
alle so optimierungswütig... :-) )
Zeile 7: MOV BX,15 Weist BX den Wert 15 zu. Damit wenden wir den Trick
noch einmal an: BL erhält den Farbcode von Weiß, und
BH (die Bildschirmseite) wird auf 0 gesetzt.
Zeile 8: MOV CX,1 Weist CX den Wert 1 zu. Also wird das Zeichen nur
einmal ausgegeben.
Zeile 9: INT 10h Ruft den Interrupt 10h auf, der den Smiley ausgibt!
+++ Interrupt 21h Funktion 4Ch +++
Gaaaanz wichtig sind Zeilen 10 und 11! Diese rufen den Interrupt 21h Ufo 4Ch
auf. Dieser beendet das Programm und gibt den Wert, der sich in AL befindet,
als Errorlevel zurück. Der Errorlevel kann dann beispielsweise von BAT-Dateien
mit IF ERRORLEVEL GOTO abgefragt werden. Wenn ihr diesen Interrupt am Ende des
Programms vergeßt, wird das Programm nicht beendet, und der Computer führt die
Befehle aus, die sich im Speicher NACH dem Programm befinden - das kann leicht
zu einem Systemabsturz führen!
Das war... Moment, die Strichpunkte, die Semikolons. Das sind nichts anderes
als Kommentare. Und somit ist Schluß für heute. Bis zur dritten Folge viel
Spaß wünscht euch (wieder einmal) euer Adok!