Kurs: Kleines Assembler ABC.



Kurs: Kleines Assembler ABC.

Frank Abbing, 24.06.2004 22:09: [color=darkred:cd92f15acb]Kurs: Kleines Assembler ABC.[/color:cd92f15acb]Das hier soll keine komplette Anleitung sein, um Assembler zu erlernen.
Aber es sollte einem Profan-User gestatten, erste Assemblerprogämmchen zu erstellen.

Zunächst einmal: Für Assembler benötigt man keine grosse Anzahl von Befehlen, mit 10 Befehlen (insgesamt gibt es über 90) ist man schon gut bedient, alles andere ist unnütz, weil der MASM32 Assembler High-Level-Syntax (.while / .endw / .if / .endif usw.) unterstützt. Und davon ist einem Profan-User schon das meiste bekannt.
In Assembler gibt es Register, das sind Speicherbereiche, die 4 Byte (Long-Int) gross sind. Einige Register dürfen wahlweise auch nur 1 oder 2 Bytes gross sein.
Diese Register heissen eax, ebx, ecx, edx, edi und esi. Dann gibt es noch ebp, das sie aber erstmal ausser acht lassen sollten.
Und es gibt noch esp, das ist auch erstmal tabu für sie. Zudem benutzt Assembler esp als Stackpointer.
Diese Register können auch als Integer oder Byte vorkommen.
Als Integer heissen sie dann ax, bx, cx, dx, di und si.
Als Byte, da gibt es einmal das HiByte und das LoByte, in diesem Format heissen sie dann ah und al, bh und bl, ch und cl und noch dh und dl. Edi und esi können nur als Long-Int oder Integer benutzt werden.
Es dürfen immer nur zwei Register mit der selben Byte-Grösse zusammen benutzt werden !!!
Die Aufgabe eines Registers:
Ein Register kann eine Zahl zwischenspeichern und der Assembler rechnet bei einigen Befehlen mit den Werten einiger Register aktualisiert diese selbstständig. Die Register sind also zum speichern und zum rechnen da.
Variablen gibt es natürlich auch.
Hier ein paar Beipspiele:
[code:1:cd92f15acb]@ASM!
Werte dd 0,0,4,6,874,414425 ;6 Long-Ints
Zahlen dw 45,263,4663 ;6 Integer
dw 364,67,3
Nochmehr db 4,5,1,90,34 ;5 Bytes
Text db "Hallo",0 ;Und ein 6 bytes langer String (immer mit Nullbyte !!!) [/code:1:cd92f15acb]
Kommen wir zum ersten und wichtigsten Befehl: mov.
Mov steht für Move und heisst "verschiebe". Man kann damit Zahlen in ein Register verschieben oder einen Registerwert in ein anderes Register kopieren. Der linke Wert ist das Ziel, der rechte Wert ist die Quelle.

[code:1:cd92f15acb]@ASM!
mov eax,4673052 ;die LongInt-Zahl 4673052 ins Register eax schreiben
mov ebx,eax ;den Wert aus eax nach ebx schreiben, in ebx steht jetzt auch 4673052
mov ax,20457 ;die Integer-Zahl 20457 nach ax
mov ah,238 ;und das Byte 238 nach ah
mov eax,variable ;variablenwert nach eax
mov variable,eax ;und umgekehrt
[/code:1:cd92f15acb]
Add addiert zwei Werte miteinander und schreibt den neuen Wert in das linke Register:

[code:1:cd92f15acb]@ASM!
mov eax,10 ;10 nach eax
add eax,20 ;20 addieren. Der Wert in eax ist jetzt 30
mov ecx,5 ;5 nach ecx
add eax,ecx ;ecx nach eax addieren. In eax steht jetzt der Wert 35
[/code:1:cd92f15acb]
Sub subtrahiert zwei Werte und schreibt den neuen Wert in das linke Register:

[code:1:cd92f15acb]@ASM!
mov eax,100 ;100 nach eax
sub eax,30 ;30 subtrahieren. eax jetzt 70
mov edx,10 ;10 nach edx
sub eax,edx ;10 subtrahieren. eax ist jetzt 60
[/code:1:cd92f15acb]
Mul multipliziert zwei Werte und schreibt den neuen Wert nach eax. Bei einer Multiplikation dürfen nur eax und edx verwendet werden.
Will man vorzeichenbehaftete Zahlen multiplizieren, dann muss anstatt dem mul das imul verwendet werden. mul ist in der Ausführung aber schneller..

[code:1:cd92f15acb]@ASM!
mov eax,7 ;7 nach eax
mov edx,5 ;5 nach edx
mul edx ;in eax steht jetzt 35, in edx steht 0
[/code:1:cd92f15acb]
Div dividiert zwei Zahlen. An der Division sind drei Register daran beteiligt, eax, edx und ein Register ihrer Wahl. edx muss Null sein !

[code:1:cd92f15acb]@ASM!
mov eax,100 ;100 nach eax
mov edx,0 ;edx immer löschen !
mov ebx,3 ;3 nach ebx
div ebx ;eax geteilt durch ebx. Ergebnis (ganze Zahl) nach eax,
;der Restwert geht nach edx[/code:1:cd92f15acb]
So jetzt können wir schonmal rechnen.
Manchmal wünschen wir uns aber noch mehr Register. Die gibt's aber nicht. Aber wir können den Stack als Registerablage missbrauchen.
Mit push legen wir eine Variable oder ein Register auf dem Stack ab.

[code:1:cd92f15acb]@ASM!
push eax ;eax auf den Stack legen
push ebx ;ebx auf den Stack [/code:1:cd92f15acb]
Mit pop können wir den Wert wieder von Stack runternehmen. Das muss aber in umgekehrter Reihenfolge passieren.

[code:1:cd92f15acb]@ASM!
push eax
push ebx
push ecx
pop ecx
pop ebx
pop eax[/code:1:cd92f15acb]
Stellt euch den Stack als ein Stapel mit Briefen vor. Wenn ich drei Briefe daraufpacke, dann muss ich die beiden obersten wieder wegräumen, um an den ersten der drei Briefe zu gelangen.

Jmp gleicht Profan's Goto und wird für Schleifen und Sprünge benutzt.

[code:1:cd92f15acb]@ASM!
jmp hierhin
mov eax,0
mov ebx,0
hierhin:
mov ebx,0[/code:1:cd92f15acb]
Lea ist ähnlich wie mov. Nur übergibt lea nicht den Wert einer Variablen oder eines Registers, sondern den Zeiger auf die Variable/Register.

[code:1:cd92f15acb]@ASM!
jmp weiter ;Datenbereich überspringen
data dd 333,444,555,666 ;Hier 4 Longint's
weiter:
mov eax,data ;In eax steht jetzt 333. Der erste Wert aus data wird übergeben
lea edx,data ;In edx steht jetzt der Zeiger auf data
mov eax,[edx+0] ;333 nach eax. Das +0 könnte hier weggelassen werden
mov ebx,[edx+4] ;444 nach ebx
mov ecx,[edx+8] ;555 nach ecx
mov esi,[edx+12] ;666 nach esi
mov eax,222 ;222 nach eax
mov [edx+4],eax ;222 nach daten+4. Die 444 wird also durch 222 ausgetauscht. [/code:1:cd92f15acb]
Ups, die eckigen Klammern bedeuten, das nicht der Registerinhalt übergeben wird, sondern der Inhalt, auf den das Register zeigt.

Invoke ist eigentlich kein richtiger Befehl, sondern auch schon High-Level-Syntax. invoke startet eine API oder eine Dll-Funktion. Übergeben werden alle nötigen Parameter.

[code:1:cd92f15acb]@ASM!
invoke GetCurrentDirectory,255,addr speicherbereich[/code:1:cd92f15acb]
Wenn die API einen Rückgabewert übergibt, steht er nach dem Aufruf im Register eax.
addr speicherbereich übergibt den Zeiger auf einen Variablenspeicher, ist aber nur bei invoke gestattet.
Ebensogut hätte man schreiben können:

[code:1:cd92f15acb]@ASM!
lea edx,speicherbereich
invoke GetCurrentDirectory,255,edx[/code:1:cd92f15acb]
So, das wären die versprochenen 10 Befehle
Aber wie können wir jetzt Register und Variablen vergleichen und Bedingungen und Verzweigungen programmieren ?
Ganz einfach, den Rest können wir mit High-Level-Syntax machen. Ähnlich wie in Profan...

.while und .endw ist das Assemblergegenstück zu Profan's While und EndWhile.

[code:1:cd92f15acb]@ASM!
mov eax,0
.while eax == 1
mov eax,[edx]
add edx,4
.endw [/code:1:cd92f15acb]
.break und .continue gibt es auch, genauso wie in Profan.

Für Bedingungen und Verzweigungen gibt es .if, .elsif, .else und .endif.
Und das funktioniert so wie in Profan

[code:1:cd92f15acb]@ASM!
.if eax==0
mov ebx,1
.elseif ecx==0
mov edx,1
.else
mov ebx,2
mov edx,2
.endif [/code:1:cd92f15acb]
Das == ist übrigens das Gleichzeichen.Weitere Vergleichsoperatoren sind:

[code:1:cd92f15acb]@ASM!
== Gleich
!= Ungleich
> Grösser als
>= Grösser gleich
< Kleiner als
<= Kleiner gleich
! Logisches NOT
&& Logisches AND
|| Logisches OR [/code:1:cd92f15acb]
Sollen mehrere Bedingungen abgefragt werden, dann sind die einzelnen Bedingungen in Klammen zu setzen, z.B. .if (eax==0) && (ebx>=0).

Ein Synonym für Profan's whileloop gibt es ebenfalls.Das geht mittels einer .repeat / .until Schleife:

[code:1:cd92f15acb]@ASM!
.repeat
;irgendein Sourcecode
.until ebx<1 ;Schleife wird nicht verlassen, solange ebx kleiner ist als 1.[/code:1:cd92f15acb]
Eine .repeat / .until Schleife kann ebenfalls mittels .break verlassen, und mittels .continue neu gestartet werden.

So, hier endet unser Assembler Crashkurs.
Ich hab' wirklich alles Unnötige weggelassen. und somit dem User die Möglichkeit gegeben, von Grund auf anzufangen.
Es gibt noch viele viele Befehle mehr, aber die meisten sind wirklich durch konfortablen High-Level Befehle ersetzt worden.
Ein gute Auflistung aller Befehle gibt es hier: [...] (auch im Anhang zum Runterladen)
Ich habe hier auch darauf verzichtet, irgendwelche Optimierungen vorzunehmen (xor eax,eax anstatt mov eax,0), nur weil es ein paar Taktzyklen schneller ist. Assembler ist wirklich so sauschnell und die neuen Prozessoren arbeiten so gut, das solche Optimierungen kaum noch was hermachen.

Im XPIA Paket sind ja schon allerhand Inc's und Libraries eingebettet. Es lohnt sich aber, das komplette MASM32 Paket (Freeware) runterzuladen.
Dort gibt es eine Menge Beispiele, Anleitungen und noch mehr Libraries. Hier die offizielle MASM32 Page: [...]

IntelCodeTable.pdf


Dies ist die Offlinevariante vom Thread [Kurs: Kleines Assembler ABC.].

Valid CSS!

©2006 XProfan.Com