Die ersten Programmierschritte in Assembler
Jetzt wird es ernst :) Nein, keine Angst, Assembler ist gar nicht so schwer. Ich muss zugeben, dass ich Assembler am Anfang auch erschreckend fand. Lauter Befehle mit JMP, MOV, CALL, LOOP usw... und das in einer Schlange untereinander. Wie soll denn da was gehen? Hmmm... warum schauen wir uns das nicht mal an ;)
Es gibt ein Programm, dass jeder Programmierer am Anfang schreiben muss. Ich rede von dem Programm "Hello world!". Das ist so Tradition! übrigens: Alle Programme, die man hier sieht, findet man im Ordner "Beispiele".
Geh doch mal in die Eingabeaufforderung und ruf den Editor auf. Gib danach den folgenden Code ein. Wie er funktioniert, erklauml;re ich dann. Gleich zum Merken: In Assembler funktioniert sehr viel mit Adressen.
hello.asm (Windows-1250 Zeichenkodierung)
.MODEL
Small
;wir basteln eine kleine EXE Datei :) .STACK 100h ;100h (im Dezimalen 256) gibt die Grouml;ße des Stacks an .DATA ;hier beginnt der Datenabschnitt Nachricht DB "Hello world!$" die Zeile, die wir ausgeben wollen .CODE ;hier beginnt der Codeabschnitt Start:
;Einsprungspunkt (wo gestartet wird)
mov dx,OFFSET Nachricht ;Offsetadresse von Nachricht dem
Datenregister mitteilen |
Gehen wir mal
Schritt für Schritt jede Zeile durch. Das ";"
für ein Kommentar steht, ist glaub ich nicht schwer zu erraten ;)
.MODEL Small
Mit .MODEL
wird angegeben, um was für einen Typ von Datei es sich handelt. Da wir wir nur
eine kleine EXE Datei programmieren, die nicht mehr als 64KB hat, wauml;hlen wir Small.
Bei dieser Art von EXE, wird dem Programm im Arbeitsspeicher ein Segment für
die Daten und ein Segment für den Code zugewiesen, was bedeutet, dass die Daten ein
eigenes Segment haben und der Code auch.
.STACK 100h
Mit .STACK
wird die Grouml;ße des Stacks in Byte
angegeben, den wir benouml;tigen. 100h
ist Hexadezimal und hat
im Dezimalen den Wert 256. Wird kein Wert angegeben, werden automatisch 1024
Byte festgelegt.
.DATA
Mit .DATA
teilen wir dem Assembler mit, dass hier der Datenabschnitt beginnt. Das Ende des
Datenabschnittes müssen wir nicht kennzeichnen, da dieser automatisch aufhouml;rt,
sobald ein neuer Abschnitt anfauml;ngt.
Nachricht
DB "Hello world!$"
Hier erstellen wir eine Variable vom Typ Byte (DB=Define
Byte)und weisen dieser den Text "Hello
world!$"
zu. Zuerst kommt der Name der Variable, also "Nachricht"
dann der Typ, was "DB"
ist und dann der Wert der Variable, was in unserem Falle "Hello
World!"
ist. Halt! Da fehlt doch das $
Zeichen. Richtig :), dass gehouml;rt ja nicht zum Text. Das $
Zeichen teilt dem Assembler mit, wo sich das Ende unseres Textes befindet.
Hier der komplette Aufbau einer Variable im überblick:
<Name der Variable>
<Typ>
<Inhalt bzw. Wert>
.CODE
Mit .CODE
teilen wir dem Assembler mit, dass nun der Code kommt, also die Befehle.
Start:
Mit dem Label Start: geben wir den Einsprungspunkt in der Datei an. Was ist ein
Einsprungspunkt? Wenn man ein Programm startet, muss es ja irgendwo in der Datei
mit der Ausführung der Befehle beginnen. Tja, mit Start:
legen wir diesen Punkt fest, also den Punkt, wo in die Datei gesprungen wird und
angefangen wird, Befehle auszuführen. Das Ende dieses Abschnittes geben wir
dann spauml;ter mit END
Start an.
mov ax,@data
Wir müssen jetzt angeben, wo sich was befindet. Die Konstante @data enthauml;lt die
Adresse unseres Datensegmentes, welches wir DS mitteilen müssen.
Nun kouml;nnen wir aber nicht einfach so DS die Adresse unserer Daten mitteilen,
da man einem Register nicht direkt den Wert einer Konstanten mit MOV übertragen
kann. Wir müssen einen Umweg über die Register machen. Dazu nehmen wir
das Register AX.
Mit dem Befehl mov,
kann ich Werte hin und her moven (übertragen). Die Quelle wird dabei nicht
verauml;ndert. mov
ax,@data bedeutet
dann also, dass die Adresse von @data
nach ax
übertragen (eher kopiert) wird. Hier der komplette Aufbau von dem Befehl
mov:
mov
<Ziel>,<Quelle>
mov ds,ax
Hier übertragen wir nun den Wert von AX,
welches die Segmentadresse von unserem Datensegment beinhaltet, nach DS. Wie wir
wissen, ist ja eine Adresse folgend aufgebaut: Segmentadresse:Offsetadresse
Nun ja, die
Segmentadresse hauml;tten wir von @data über den Umweg mit den Registern schon mal
übertragen. Unser Datensegment ist bereit und als nauml;chstes geben wir an, auf
welche Daten wir im Datensegment zeigen wollen.
mov
dx,OFFSET Nachricht
Durch den Befehl OFFSET,
wird die Offsetadresse von Nachricht
nach DX
übertragen, womit wir nun auf unsere Nachricht zeigen. DS
hat ja die Adresse von unserem Datensegment und DX
zeigt im Datensegment auf unsere Nachricht.
Falls es ein wenig verwirrend war, kouml;nnte die folgende Tabelle einen kleinen
überblick vermitteln:
Segmentadresse : Offsetadresse |
DS : DX |
Wie man vielleicht sieht, kouml;nnten wir nun mit DS:DX auf beliebige Daten zeigen. Hauml;tten wir nun noch eine zweite Nachricht, müssten wir nur DX den Offset der zweiten Nachricht übertragen und wir würden auf die zweite Nachricht zeigen. Merken wir uns vorerst nur mal, dass man mit DS:DX auf Daten zeigen kann.
mov ah,09h
Unser Datensegment haben wir bereits angegeben und wir zeigen auch schon auf
unsere Nachricht. Jetzt fehlt nur noch, dass der Datensatz ausgegeben wird. Für
solche Operationen gibt es Interrupts mit Funktionen. Ein Interrupt ist eine
Unterbrechung des Programmes, um ein Unterprogramm (z.B. Text ausgeben) aufzurufen. Es gibt verschiedene Intertupts mit verschiedenen Funktionen. Da
es sehr viele sind, ist es wohl am Besten, sich eine Interruptliste
herunterzuladen. Wir brauchen jetzt die Funktion 9
vom Interrupt 21h,
die Texte ausgibt. Die Funktionsnummer 9
wird mit mov
ah,09h nach AH
übertragen.
int 21h
Mit int
21h führen wir nun
den Interrupt 21h
aus. Der Interrupt schaut in AH
nach, wo er die 9
findet und weis, dass er die Daten ausgeben soll, auf welche DS:DX
zeigt.
mov ah,4Ch
Der Text wurde ausgegeben und nun müssen wir unser Programm noch beenden. Hier
gibt es wieder eine Funktion, die das für uns erledigt. Es ist die Funktion 4Ch
des Interrupts 21h,
also setzen wir in AH
den Wert 4Ch.
int 21h
Wie es zu erwarten war, führen wir den Interrupt 21h
aus, der in AH
4Ch
vorfindet und somit weis, dass er die Funktion 4Ch
ausführen soll => Programm beenden :)
END Start
Diese Zeile haben wir oben schon besprochen. Sie gibt nur an, dass hier der
Einsprungspunkt zu ende ist.
Puhhh... war nicht gerade leicht, oder? Es ist aber wichtig, dieses erste Programm zu verstehen. Lest es so lange durch, bis ihr es verstanden habt. Da kann ich nur hoffen, dass ich keinen Unsinn geschrieben habe ;) - bei mir funktioniert das Programm zumindestens. Ah ja, ausführen wollen wir das Proggi ja auch noch. Speichert die Datei unter dem Namen hello.asm ab und beendet den Editor.
Um das Programm zu assemblieren schreiben wir "ml /c hello.asm". Das /c schreiben wir deshalb, dass er nur die Objektdatei erzeugt und sie nicht mit LINK.EXE linkt, da wir ja DLINK.EXE für's DOS brauchen.
F:\Assembler
Tutorial\Beispiele\Hello_world>ml /c hello.asm Microsoft (R) Macro Assembler Version 6.14.8444 Copyright (C) Microsoft Corp 1981-1997. All rights reserved. Assembling: hello.asm F:\Assembler Tutorial\Beispiele\Hello_world> |
Assembliert hauml;tten wir das Programm. Nun haben wir eine Objekt Datei mit dem Namen hello.obj. Warum macht der eine Objektdatei? Ganz einfach. Man kouml;nnte jetzt verschiedene kleine Programme erstellen und die Objekt Dateien miteinander linken. Das bedeutet, dass man z.B. eine Anwendung schreibt, die auch eine Info ausgibt. Die Objektdatei dafür kouml;nnte info.obj heißen. Wenn man nun irgendwann mal eine Anwendung schreibt, braucht man die Info nicht immer neu zu schreiben, sondern kann die info.obj einfach mitlinken. Man kann die Objektdateien als Bausteine betrachten, die der Linker zusammenfügen kann. Wir haben in unserem Fall nur hello.obj, welche wir durch den Befehl dlink hello linken. Wie man sieht, braucht man hier die Erweiterung bei hello.asm, also das .asm, nicht angeben. Beim Linken wartet er 4 mal auf Eingaben. Einfach immer nur Enter drücken.
F:\Assembler
Tutorial\Beispiele\Hello_world>dlink hello Microsoft (R) Segmented Executable Linker Version 5.60.339 Dec 5 1994 Copyright (C) Microsoft Corp 1984-1993. All rights reserved. Run File [hello.exe]: List File [nul.map]: Libraries [.lib]: Definitions File [nul.def]: F:\Assembler Tutorial\Beispiele\Hello_world> |
Und zum Abschluss probieren wir unser Meisterwerk aus:
F:\Assembler
Tutorial\Beispiele\Hello_world>hello Hello world! F:\ASSEMB~1\BEISPI~1\HELLO_~1> |
Wie man sieht, funktioniert alles :) Gratulation! Du hast soeben dein erstes Assemblerprogramm geschrieben.
Letzte auml;nderung: 29. Mai 2003]