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!