1.1. Vorraussetzungen für das Programmieren mit Assembler
1.2. Ein paar Fakten zu Assembler/Computern
1.3. Warum Assembler benutzen
2. Assembler Allgemein
2.1. Arbeitsweise von Assembler
2.2. Syntax und Semantik
2.3. Register
3. Erste Assembler Schritte
3.1. Mov
3.2. Add und Sub
3.3. Inc und Dec
3.4. Cmp und Jmp/Jne/Je/Jbe/Jge/Jle/...
3.5. Push/Pop
3.6. Variablen in ASM
3.7. Sonstige wichtige Befehle
4. Assembler-Codes
4.1. Berechnen der Fakultät einer ganzen Zahl
4.2. Potenzieren einer ganzen natürlichen Zahl mit ganzen Exponenten
5. AutoIt und Inline-Assembler
5.1. Erstes Programm mit Assembler und AutoIt
5.2. 2 Zahlen in ASM addieren
5.3. Einen kleinen Taschenrechner mit Parser schreiben
5.4. Eine einfache Verschlüsselungsroutine schreiben
6. AutoIt-Skripts in Assembler-Codes umschreiben
7. Quellen
-------------------------------------------------------------------------------------------------
1. Vorwort
1.1. Vorraussetzungen für das Programmieren mit Assembler
Eine Vorraussetzung wäre auf jeden Fall grob zu wissen wie ein Computer
funktioniert. Wer immer noch denkt, dass im Computer kleine Männchen
herum laufen, der kann sofort aufhören weiter zu lesen.
Assembler ist nur verständlich, wenn man auch weiß wie ein Computer funktioniert.
Wer nicht weiß was Variablen wirklich sind und wer keine Ahnung hat was
AutoIt mit dem Source-Code macht, der ist hier ebenfalls falsch.
Ich kann euch nicht empfehlen als Neuling auf dem Gebiet direkt mit Assembler
ein zu steigen.
Assembler ist zwar auch nicht schwerer als andere Sprachen, aber es dauert etwas länger bis man
sich eingearbeitet hat.
Nachdem wir uns jetzt endlich für bereit halten, brauchen wir noch ein paar
Programme/Skripts:

In dieser Rar-Datei liegt alles benötigte drinnen.
Am besten ist, wenn ihr die Skripts in euren AutoIt-Include Ordner verschiebt.
-------------------------------------------------------------------------------------------------
1. Vorwort
1.2. Ein paar Fakten zu Assembler/Computern
Keine Angst, ich werde euch nicht bombadieren mit Informationen rund um Assembler.
Ich möchte nur kurz ein paar Sachen erwähnen, die ich für interessant/wichtig halte.
1. Der erste Computer war nur über Verbinden von Datenleitungen programmierbar.
Die ersten Rechner waren richtige Monster wogen teilweise über 500kg und hatten
gerade einmal ein paar Herz Takt. Das heißt sie haben für die Addition von
2 Zahlen ungefähr 1/2 - 1 Sekunde gebraucht (Heutzutage geht das in ein paar nanosekunden).
2. Die erste richtige Programmiersprache wurde ca. 1952 erfunden und war Assembler.
3. Es folgten etliche weitere Sprachen in den nächsten Jahren unter anderem auch C (1971–1973).
4. AutoIt wurde 1999 als Skriptsprache von Jonathan Bennett für seine Arbeit entwickelt.
-------------------------------------------------------------------------------------------------
1. Vorwort
1.3. Warum Assembler benutzen
Assembler ist und bleibt die maschinennäheste Sprache, die es gibt.
Außerdem ist Assembler schlicht weg schneller als AutoIt.
Für Zeitkritische und hardwarenahe Funktionen ist Assembler also sehr gut geeignet.
In AutoIt wurde leider sehr viel abstrahiert. Dadurch ist man gewisser Maßen eingeschränkt.
Assembler hat aber auch einen Nachteil, man muss sich um alles selbst kümmern, außerdem vergeht
einem bei größeren Programmen meistens die Lust sich da wieder ein zu lesen.
Die Kombination von Hardwarenähe/Geschwindigkeit und Abstraktion/"Aufräumen" bringt
die Möglichkeit mit sich, je nach Situation, das Problem bestmöglich zu lösen.
-------------------------------------------------------------------------------------------------
2. Assembler Allgemein
2.1. Arbeitsweise von Assembler
Im Gegensatz zu AutoIt ist Assembler keine Interpretsprache.
Das heißt der Code, den wir in ASM (=Assembler) schreiben wird von einem Compiler
später so umgewandelt, dass unsere Befehle ohne Umwege vom Computer ausgeführt werden können.
Dementsprechend Fehleranfällig ist ASM aber auch, 2 eckige Klammern vergessen und unser Programm
crasht mit einer schönen Windows-Fehlermeldung.
Man sollte also besonders bei Assembler auf mögliche Probleme achten und sie möglichst schon
im Keim ersticken.
Assembler arbeitet die Befehle (ähnlich wie AutoIt) im normalfall von oben nach unten ab.
Wir werden später sehen, dass wir auch ganz unten anfangen können und uns dann hoch arbeiten können,
aber das macht selten Sinn und verwirrt nur unnötig.
Der Programmfluss ist also im Regelfall von Oben nach Unten, genau so wie bei AutoIt.
-------------------------------------------------------------------------------------------------
2. Assembler Allgemein
2.2. Syntax und Semantik
Die Syntax von ASM ist im Grunde immer die Gleiche:
Code:
ASM-Befehl Wert1[, Wert2, ...]
einfach hinter den Befehl getrennt mit einem (oder mehreren) Leerzeichen.
In ASM kann maximal ein Befehl pro Zeile ausgeführt werden.
So würde dann ein Teil eines ASM-Codes aussehen:
Code:
ASM-Befehl Wert1[, Wert2, ...] ASM-Befehl Wert1[, Wert2, ...] ASM-Befehl Wert1[, Wert2, ...] ASM-Befehl Wert1[, Wert2, ...] ...
-------------------------------------------------------------------------------------------------
2. Assembler Allgemein
2.3. Register
Nun sind wir schon bei den Registern angekommen, hat man erst einmal verstanden was die Register sind, macht ASM an sich auch keine Probleme mehr.
Register kann man als vordefinierte Variablen sehen.
Die meisten Register mit einem "e" am Anfang haben eine Größe von 32-Bit, das entspricht 32 0en bzw. 1en, damit lassen sich ganze
Zahlen bis 4.294.967.296 darstellen (in den meisten Fällen genug).
Es gibt nicht viele Register, aber jedes Register hat eine gewisse Bedeutung.
Vergleichen kann man das ganze mit @Error in AutoIt.
Man kann @Error nicht umbennen, aber man kann den Wert ändern durch SetError(...).
@Error kann für alles mögliche benutzt werden, wird aber meistens nur für Errors benutzt,
um Verwirrung vorzubeugen.
Die Register in ASM haben auch solche Eigeschaften, sie können nicht umbenannt werden, können aber
ihre Werte ändern.
Die Namen der Register sind wie folgt:
eax allgemein verwendbar, spezielle Bedeutung bei Rechenbefehlen
ebx allgemein verwendbar
ecx allgemein verwendbar, spezielle Bedeutung bei Schleifen
edx allgemein verwendbar
ebp Basepointer
esi Quelle (eng: source) für Stringoperationen
edi Ziel (eng: destination) für Stringoperationen
esp Stackpointer
Wem die oben aufgeführten Begriffe nichts sagen, der braucht sich nicht wundern, denn die meisten
dieser Register werden später in Beispielen benutzt und dort wird oft sehr schnell klar was das Register jetzt macht/wofür es genommen wird.
Ein Register besteht aber wiederum aus Registern.
Die ersten 16-Bit des Registers eax heißen z.b. ax.
Die ersten 8-Bit des Registers ax bestehen wiederum aus al und die 2. 8-Bit bestehen aus ah.
Es ist nicht wichtig zu wissen welches Register aus welchen Registern besteht, es ist nur
wichtig zu wissen, dass es soetwas gibt und dass jedes Register (meistens) aus weiteren Registern besteht.
Anmerkung:
Kommentare können in ASM mit einem ; (Strichpunkt) gemacht werden,
ähnlich wie in AutoIt.
-------------------------------------------------------------------------------------------------
3. Erste Assembler Schritte
3.1. Mov
Vorerst werden wir reinen Assemblercode versuchen zu verstehen und zu schreiben,
da es bei AutoIt und Inline-Assembler noch ein paar Kleinigkeiten zu beachten gibt.
Ich fange mit diesem Befehl an, weil er ein am Anfang für mich extrem schwer zu verstehendes
Phänomen zeigte.
Aber kurz bevor wir zum Befehl kommen, ein paar Informationen:
Wir wissen ja wie Assembler-Befehle aufgebaut sind:
Code:
ASM-Befehl Wert1[, Wert2, ...]
Man kann nämlich auf den Wert der Register zugreifen oder auf die Adresse an der der Wert gespeichert wurde.
Wenn der Computer die Werte abspeichert, versieht er sie automatisch mit einer Nummer.
Diese Nummer ist unsere Adresse. Diese Adresse braucht der Computer, damit wir ihm sagen können:
"Wir wollen den Wert an der Stelle Nummer 10".
Würden wir den Computer nur sagen, dass wir einen Wert wollen, dann wüsste er nicht welchen Wert wir jetzt wollen.
In AutoIt wird uns die Arbeit der "Nummern" durch die Variablennamen abgenommen.
Der Interpret des AutoIt-Codes weiß dann genau, dass wir den Wert mit dem Namen "$Variable1" wollen.
Der Interpret nimmt uns also die Arbeit ab und macht uns das Leben leichter indem er statt Zahlen
Namen nimmt, die wir uns leichter merken können.
So könnte für AutoIt der Namen "$hallo" die Adresse/Nummer "5126" haben.
Aber zurück zu Assembler.
In Assembler müssen wir das selbst erledigen (zumindest bei Registern).
Da wir die Register nicht umbenennen können und die Namen der Register für den
Compiler fast irrelevant sind, müssen wir uns selbst um die Addressierung/Nummerierung kümmern.
Und uns selbst eine passende Speicherstelle suchen.
Das hat aber auch gewisse Vorteile, denn damit haben wir jetzt viel mehr Freiheit als früher,
scheint zwar auf den ersten Blick Blödsinn zu sein, aber im Laufe des Tutorials wird das hoffentlich
klar werden.
So, nachdem wir alles so ausführlich besprochen haben, sind wir bereit für unser erstes Assembler-Programm:
Code:
Mov eax, edi

Zuerst bevor wir genauer auf das Beispiel eingehen, will ich kurz die Syntax des Mov-Befehls erklären.
Code:
Mov Destination, Source
Aber was passiert hier?
Eigentlich ist es relativ einfach.
Es wird einfach nur die Adresse von edi in eax kopiert.
Vor dem Ausführen des Codes sieht es folgendermaßen bei uns aus.
(Ich habe Startwerte genommen, die normalerweise falsch sind, aber dadurch
lässt sich das ganze wohl besser nachvollziehen).
Register | Adresse | Wert |
eax | 0 | 5 |
edi | 8 | 1 |
Danach sieht es so aus:
Register | Adresse | Wert |
eax | 8 | 1 |
edi | 8 | 1 |
Aber auch das geht:
Code:
Mov eax, [edi]
Vor dem Codeasuführen:
Register | Adresse | Wert |
eax | 0 | 50 |
edi | 8 | 32 |
Danach:
Register | Adresse | Wert |
eax | 32 | 0 |
edi | 8 | 32 |
Das kann man jetzt so weiter führen.

Jetzt schauen wir uns einmal etwas komplexeres an:
Code:
mov edi, 4 mov edx, edi mov eax, edx
Deswegen benutzen wir hier auch Adressen zum Abspeichern der Werte.
Tabelle vor dem Code (Die Startwerte sind wieder nur ausgedacht und dienen der besseren Verständlichkeit):
Register | Adresse | Wert |
edi | 0 | 0 |
edx | 32 | 5 |
eax | 64 | 12 |
Tabelle danach:
Register | Adresse | Wert |
edi | 4 | 0 |
edx | 4 | 0 |
eax | 4 | 0 |
-------------------------------------------------------------------------------------------------
3. Erste Assembler Schritte
3.2. Add und Sub
2 weitere wichtige Befehle sind Add und Sub.
Die Syntax sieht wie folgt aus:
Code:
Add Destination, Source
Code:
Sub Destination, Source
Sub subtrahiert 2 zahlen und speichert das Ergebniss in "Destination".
Recht viel mehr gibt es darüber nicht zu wissen, also schauen wir uns gleich ein kurzes Beispiel an:
Code:
Mov edi, 17 Add edi, 3
Register | Adresse | Wert |
edi | 0 | 0 |
Danach:
Register | Adresse | Wert |
edi | 20 | 0 |
Ich denke das ist nicht schwer zu verstehen, deswegen gehen wir gleich zu etwas größerem über
(Btw. Die Groß- und Kleinschreibung von ASM-Befehlen ist total egal):
Code:
mov edi, 4 mov edx, 16 sub edi, edx mov eax, edi add eax, edi
nach jeder Zeile Code einmal an.
Ausgang:
Register | Adresse | Wert |
edi | 0 | 0 |
edx | 0 | 0 |
eax | 0 | 0 |
Nach diesen Zeilen Code,
Code:
mov edi, 4 mov edx, 16
Register | Adresse | Wert |
edi | 4 | 0 |
edx | 16 | 0 |
eax | 0 | 0 |
Danach:
Code:
sub edi, edx
Register | Adresse | Wert |
edi | -12 | 0 |
edx | 16 | 0 |
eax | 0 | 0 |
Danach:
Code:
mov eax, edi
Register | Adresse | Wert |
edi | -12 | 0 |
edx | 16 | 0 |
eax | -12 | 0 |
und ganz zum Schluss sieht es so aus (nach "add eax, edi"):
Register | Adresse | Wert |
edi | -12 | 0 |
edx | 16 | 0 |
eax | -24 | 0 |
Das ganze in AutoIt-Code sähe ungefähr so aus:
PHP Code:
$edi=4
$edx=16
$edi=$edi - $edx
$eax=$edi
$eax=$eax + $edi
-------------------------------------------------------------------------------------------------
3. Erste Assembler Schritte
3.3. Inc und Dec
Syntax:
Code:
Inc Destination
Inc ist also fast gleich zu setzen mit
Code:
Add Destination, 1
Code:
Dec Destination
Code:
Sub Destination,1
Die Antwort ist relativ einfach, Inc braucht 2 Taktzyklen weniger als Add.
Add braucht 3 Taktzyklen und Inc nur 1 Taktzyklus.
Dadurch kann man seinem Assembler Programm zusätzlich "Speed" verleihen.
Inc/Dec wird besonders gerne in Schleifen eingesetzt.
Ein Beispiel zu Inc/Dec kommt später.
-------------------------------------------------------------------------------------------------
3. Erste Assembler Schritte
3.4. Cmp und Jmp/Jne/Je/Jbe/Jge/Jle/...
Cmp ist Kurzschreibweise für Compare (=Vergleichen).
Mit dem Befehl cmp werden also 2 Werte verglichen.
Syntax:
Code:
cmp Source1, Source2
Code:
mov eax, 1 cmp eax, 2
welches Ergebniss "cmp" hat gesetzt. Die Funktionsweise von "cmp" ist relativ simpel.
Die beiden zahlen werden von einander abgezogen und anschließend wird geschaut welches
Ergebniss "cmp" erhält. Je nach Ergebniss werden andere Register des Flag-Registers geändert.
Es wird also die Eigenschaft der beiden zahlen im vergleich in einem extra register gespeichert, das
man später abfragen kann.
So bringt uns das aber relativ wenig, in Verbindung mit JNE/JE/JMP/... hingegen
wird der Befehl wichtiger.
Aber zuerst widmen wir uns dem Befehl "Jmp".
Dieser Befehl springt zu einer bestimmten Stelle im Skript.
Man kann entweder zum Jmp-Befehl sagen: "Spring an die 2. Stelle im Code" oder
man sagt dem Jmp-Befehl: "Spring an die Stelle, an der du eine Sprungmarke mit dem Namen "Hier_kann_alles_stehen" findest".
Sprungmarken (=Labels) zu erstellen ist einfach:
Code:
Sprungmarke1:
Code:
Sprungmarke1: Jmp Sprungmarke1
In AutoIt sähe es so aus:
PHP Code:
While 1
wend
Genau deswegen gibt es gewisse Abwandlungen vom Jmp-Befehl:
JE, JNE, JA, JAE, JL, JLE, ...
Wenn wir einen dieser Befehle benutzen, dann müssen wir zuvor erst vergleichen lassen.
Der JE-Befehl springt nämlich nur, wenn das Flag-Register den richtigen Wert für JE hat.
Es muss sich also sozusagen zuvor ein "OK" geholt werden und dieses "OK" steht im Flag-Register.
Jetzt kann man sich auch einmal die Tabelle anschauen, in der man sieht welcher JMP-Befehl wann erfüllt ist:
JMP-Befehl | Ausgeschrieben | Bedeutung |
JE | Jump Equal | Springe nur, wenn beide zahlen gleich groß sind |
JA | Jump Above | Springe nur wenn Source1 größer ist als Source2 |
JAE | Jump Above or Equal | Springe nur wenn Source1 größer oder gleich Source2 ist |
JL | Jump Lesser | Springe nur wenn Source1 kleiner Source2 ist |
JLE | Jump Lesser or Equal | Springe nur wenn Source1 kleiner oder gleich Source2 ist |
Mit diesen Kentnissen können wir auch ein kleines ASM-Programm schreiben.
Code:
mov eax, 30 mov edi, 0 Sprungmarke1: add eax, 10 inc edi cmp eax, 100 jne Sprungmarke1
Register | Adresse | Wert |
eax | 30 | 0 |
edi | 0 | 0 |
Jetzt wird Sprungmarke1 vorerst ignoriert (es wird einfach weiter gemacht mit Zeile 4 "add eax, 10").
Es wird jetzt also zur Adresse von eax 10 addiert und edi wird um eins erhöht.
Anschließend wird geprüft, ob eax gleich 100 ist. ("cmp eax, 100")
Wenn eax jetzt ungleich 100 ist, dann springt das Programm zu "Sprungmarke1" zurück. ("jne Sprungmarke1")
Und am Ende sieht unsere Tabelle so aus:
Register | Adresse | Wert |
eax | 100 | 0 |
edi | 7 | 0 |
-------------------------------------------------------------------------------------------------
3. Erste Assembler Schritte
3.5. Push/Pop
Push und Pop sind die wohl, meiner Meinung nach, wichtigsten Befehle in Assembler.
Mit Push kann man Werte auf dem Stack abspeichern und mit Pop holt man sie wieder runter.
Das war Push/Pop ganz knapp zusammen gefasst, natürlich ist es, wie alles in ASM, etwas komplizierter.
Der Stack arbeitet nach dem First-In-Last-Out Prinzip.
Das heißt im Grunde nichts anderes als, dass das erste Element, das auf den Stack per Push gespeichert wurde
das letzte Element ist, das per Pop runtergenommen werden kann.
Der Stack (zu deutsch: Stapelspeicher) wird insbesondere zum Zwischenspeichern von Werten und zum
Übergeben von Parametern benutzt.
Ich denke momentan sagt uns das nicht viel, aber wir machen es uns an einem Beispiel klar:
Code:
mov eax, 10 push eax mov eax, 3 pop eax
Unsere Tabelle nach der ersten Zeile Code:
Register | Adresse | Wert |
eax | 10 | 0 |
Unser Stack:
Stack |
Nach Push hat sich an der Tabelle nichts verändert, aber unser Stack hat sich verändert:
Stack |
10 |
Nach dem Ausführen von Zeile 3 sieht der Stack immer noch gleich aus, aber unsere Tabelle
hat sich verändert:
Register | Adresse | Wert |
eax | 3 | 0 |
Stack |
10 |
Wenn unser Code komplett ausgeführt wurde, dann sieht das wie folgt aus:
Register | Adresse | Wert |
eax | 10 | 0 |
Stack |
Das Prinzip des Stacks kam bei diesem Beispiel nicht raus, aber die Arbeitsweise sollte jetzt nachvollziehbar sein.
Schauen wir uns das ganze mit mehr Werten auf dem Stack an:
Code:
mov eax, 10 push eax mov eax, 3 push eax mov eax, 8 push eax pop edx pop edi pop eax
Register | Adresse | Wert |
eax | 10 | 0 |
Stack |
10 |
Nach der nächsten Zeile ("mov eax,3"):
Register | Adresse | Wert |
eax | 3 | 0 |
Stack |
10 |
In zeile 4:
Register | Adresse | Wert |
eax | 3 | 0 |
Stack |
3 |
10 |
Hier sieht man, der Stack kann mehrere Werte speichern.
Die Anzahl der Werte ist theoretisch unbegrentzt, aber praktisch natürlich durch Speicherkapazität u.ä. begrentzt.
In Zeile 6 sieht es dann schon so aus:
Register | Adresse | Wert |
eax | 8 | 0 |
Stack |
8 |
3 |
10 |
So ab jetzt wird es interessant:
Ich poste jetzt einfach die 2 Tabellen, wie sie nach jeder Zeile Code aussehen:
Zeile 7:
Register | Adresse | Wert |
eax | 8 | 0 |
edx | 8 | 0 |
Stack |
3 |
10 |
Zeile 8:
Register | Adresse | Wert |
eax | 8 | 0 |
edx | 8 | 0 |
edi | 3 | 0 |
Stack |
10 |
Zeile 9:
Register | Adresse | Wert |
eax | 10 | 0 |
edx | 8 | 0 |
edi | 3 | 0 |
Stack |
Man sieht hier also sehr schön, Wenn man 3 Werte per Push auf den Stack befördert und sie anschließend wieder
mit Pop holt, wird der zu letzt auf den Stack gelegte Wert als erstes geholt und vom Stack gelöscht.
Dazu noch eine kleine Illustration von Wikipedia:
Aber wozu braucht man das jetzt?Quote:
Ein Stapelspeicher ist mit einem Stapel von Umzugskisten vergleichbar. Es kann immer eine neue Kiste oben auf den Stapel gepackt werden (entspricht push) oder eine Kiste von oben heruntergenommen werden (entspricht pop). Der Zugriff ist im Regelfall nur auf das oberste Element des Stapels möglich. Ein Hinzufügen oder Entfernen einer Kiste weiter unten im Stapel ist nicht möglich.
http://de.wikipedia.org/wiki/Stapelspeicher
Die Anzahl an Register sind ja leider begrentzt und viele der Register haben einen extrem wichtigen Startwert,
den man unter gar keinen Umständen löschen sollte.
Wenn man dieses Register jetzt aber doch braucht (z.b. aus Mangel an Registern), dann kann man den Wert des Registers
vor der Rechnung abspeichern und danach wieder laden, somit geht nichts verloren und wir haben trotzdem ein Register mehr:
Code:
push edi ; Code ; ... pop edi
Pushs und Pops seien sollten.
Haben wir ein Pop zu viel stürzt unser Programm ab.
Haben wir ein Push zu viel, wird der Stack unnötig zu gemüllt und bleibt (zumindest vorrübergehend)
unbenutzbar.
Es ist also wichtig den Stack immer auf zu räumen, auch wenn man den gespeicherten Wert wider Erwartens
nicht braucht.
-------------------------------------------------------------------------------------------------
3. Erste Assembler Schritte
3.6. Variablen in ASM
Ich hatte mir überlegt, ob dieses Kapitel wirklich sinnvoll sei oder ob es nicht vielleicht zu sehr verwirrt.
Aber Ich denke, wenn Assembler-Grundlagen lernen, dann sollte man Variablen nicht vergessen.
Das "Problem" hierbei ist, dass man es nicht mehr groß umschreiben kann, wir müssen voll in die Materie eintauchen.
Aber wer es bis hier her geschafft hat, den wird der Rest auch nicht zu schwer fallen.

Also gut fangen wir an.
Variablen in ASM können genauso wie in AutoIt benutzt werden.
Sie können Zahlen speichern, können aber auch Zeichenketten (=Strings) "speichern".
Wobei genau genommen Speichern diese Variablen garnichts außer die Adresse an der die Werte liegen.
Erstellen wir also eine Zeichenkette in ASM und speichern sie in einer Variable und holen uns anschließend
wieder den Wert dieser Variable haben wir lediglich den ersten Buchstaben unserer Zeichenkette.
Wir speichern in unserer Variable also nur die Adresse, wo unsere Werte liegen.
Nehmen wir an wir weisen unserer Variable den Wert "Hallo" zu.
Wenn wir jetzt alle Zeichen haben wollen (und nicht nur "H"), dann müssen wir das anders machen.
Mithilfe einer Tabelle sollte das etwas klarer werden:
Unsere Variable heißt jetzt einmal "var1" (in ASM wird kein $ vor den Variablen benötigt).
Variable | Adresse | Wert |
var1 | 0 | H |
var1 | 8 | a |
var1 | 16 | l |
var1 | 24 | l |
var1 | 32 | o |
Wir sehen also unsere Variable "var1" nimmt insgesamt 5 (0-4)Byte Speicherplatz ein.
Aber warum ausgerechnet 5 Byte? Und nicht 8 oder 10?
Die Adresse, die wir oben in der Tabelle angeben ist in Bits gehalten, wobei 8 Bit immer 1 Byte sind.
Buchstaben jeglicher Art brauchen immer 1 Byte um sie darstellen zu können.
Wie ihr hoffentlich von AutoIt aus wisst, wird jedem Buchstaben eine Zahl zugewiesen, so hat der Buchstabe
"E" z.b. die Zahl 69.
Wenn wir uns die obere Tabelle mit der Adresse in Bytes anschauen, dann sieht man eine gewisse Ähnlichkeit zu AutoIt.
Variable | Adresse | Wert |
var1 | 0 | H |
var1 | 1 | a |
var1 | 2 | l |
var1 | 3 | l |
var1 | 4 | o |
Man sieht, unsere Zeichenkette ist in Wirklichkeit ein Array mit der Größe 5.
Das heißt unsere Variable "var1" speichert nur die Adresse vom 1. Element, also 0.
Wenn wir jetzt das "a" haben wollen, müssen wir unsere Adresse der Variable um 1Byte erhöhen.
Variablen benutzt man ähnlich wie Register, nur bei Variablen wird nicht mit den Adressen gerechnet, sondern
mit den Speicherwerten.
Schauen wir uns die Tabelle am Anfang an:
Register/Variable | Adresse | Wert |
eax | 0 | 0 |
edx | 0 | 0 |
edi | 0 | 0 |
var1 | 8 | H |
Code:
mov eax, [var1] ; Wert der Adresse von var1 kopieren in eax mov edx, [var1+2] ; Wert der Adresse von var1 +2 kopieren in edx mov edi, var1 ; Adresse von var1 verschieben in edi
Register/Variable | Adresse | Wert |
eax | H | 0 |
edx | l | 0 |
edi | 8 | H |
var1 | 8 | H |
Jetzt wissen wir wie wir Variablen benutzen und was Variablen wirklich sind, aber wie erstellen wir Variablen?
Das Erstellen der Variablen ist für einen AutoItler wohl eine krasse Umstellung,
aber wir schaffen das trotzdem.

Wichtig ist zuerst einmal, dass Variablen in ASM immer erstellt (=deklariert) werden müssen.
In AutoIt kann man es sich ja aussuchen, wie man das haben möchte, entweder so:
PHP Code:
$var=1
PHP Code:
Local $var
$var=1
In ASM ist es Pflicht die Variable zu erstellen.
Außerdem muss die Erstellung der Variablen immer ganz am Ende des ASM-Programmes passieren.
Scheinbar wissen wir jetzt genug über Variablen...
Oder vielleicht auch nicht...
Variablen in ASM haben immer eine "Grenze", die beim Erstellen (Deklarieren) der Variablen mit angegeben werden muss.
Somit können wir dem Computer z.b. sagen: "Ich möchte eine Variable erstellen, die nur Zahlen von -127 bis +127 halten kann".
Aber wir können das dem Computer nicht nur sagen, wir müssen es sogar.
Wir müssen uns also festlegen wie groß wir unsere Variablen gerne hätten, in AutoIt passiert das alles von alleine.
Das kann ganz schön mühsam sein, aber mit der Zeit wird es zur Routine und man regt sich auf, dass AutoIt soetwas nicht hat.

Was das bringt werden sich sicherlich einige Fragen.
Nun, damit können wir genau steuern wie viel Speicherplatz wir belegen.
Haben wir eine Variable in AutoIt, die nur 1 und 0 speichern soll, dann braucht die Variable trotzdem
8 Byte Speicherplatz. Und das ist bei jeder Variable so.
Auf den neuen Rechnern mag das für unsere Zwecke wohl völlig egal sein, aber
man sollte immer im Hinterkopf behalten, dass ASM eine der ersten Programmiersprachen war und da
gab es nunmal noch keine 8GB Arbeitsspeicher, da war man froh, wenn man 1MB hatte.
Wir müssen uns damit leider trotzdem noch rumschlagen.
Fast genau so schlimm wie die Register fand ich immer das Erstellen der Variablen, ihr werdet gleich sehen warum (oder auch nicht

Eine Variable erstellt man in ASM ganz grob so:
Code:
Variablenname Datentyp Startwert
Unser Variablenname ist egal, der kann frei gewählt werden (es gelten die selben Bedingungen wie in AutoIt,
keine Sonderzeichen usw.)
Der Datentyp ist das Ding, was dem Computer sagt wie groß unsere Variable werden soll, also ob sie
Werte von 10000000000000000 bis 0 speichern soll oder nur Kommazahlen oder nur Zahlen zwischen 1 und 0...
Unser Startwert ist der Wert, den die variable am Anfang bekommen soll.
Ein konkretes Beispiel sieht in ASM so aus:
Code:
test db 50
db kann zahlen von -128 bis +127 abspeichern.
Es gibt aber noch einige mehr Datentypen, hier einmal eine Auflistung einiger Datentypen (wie immer ist die
Groß und Kleinschreibung egal):
Bezeichnung | Bedeutung | Größe in Bits | Wertebereich |
DB | Byte/Char | 8 | -128 bis +127 |
DW | Word | 16 | -32768 bis +32767 |
DD | Double Word | 32 | -2.147.483.648 bis +2.147.483.647 |
DQ | Quad Word | 64 | -2^63 bis +2^63-1 |
DP | Far Pointer | 48 | -2^47 bis +2^47-1 |
TBYTE | Ten Byte | 80 | -2^79 bis +2^79-1 |

Wollen wir jetzt also in einer Variable einen Wert von 90000 speichern, dann sollten wir
DD (Double Word) hernehmen und nicht gleich DQ (Quad Word), um Speicher zu sparen.
Schauen wir uns das alles in einem kleinen Code einmal an:
Code:
mov eax, [MyVar2] add eax, [MyVar] MyVar dd 65 MyVar2 dd 9000
Ich denke das sollte nicht all zu schwer zu verstehen sein.
Wie alles in Assembler wird uns das ganze jetzt aber zusätzlich erschwert,
wenn wir eine Variable haben, die nur eine Größe von 16 Bit hat und den Wert dieser Variable
in ein Register schreiben wollen, dann können wir das nicht so machen:
Code:
mov eax, [MyVar] MyVar dw 65
Wir können nicht in einer Zahl, die bis zu 32Bit lang sein kann (eax) eine Zahl mit nur 16Bit länge speichern.
Aber wenn wir uns zurück erinnern, hat jedes Register Unterregister.
So auch eax, es besteht zur Hälfte aus ax.
Und ax ist 16 Bit groß, genau so wie unsere Variable, also können wir da getrost den Wert rein verschieben.
Aber zeurst müssen wir eax auf 0 setzen, sonst könnten undefinierte Zahlen bei rauskommen:
Code:
mov eax, 0 mov ax, [MyVar] MyVar dw 65
Andersrum ist es natürlich genauso, wollen wir einen 64-Bit Wert in ein Register speichern, dann müssen
wir da auch erst wieder etwas tricksen.
Aber das ist sogar angenehmer als unser oberes Problem, man muss einfach nur davor schreiben welchen
Datentyp man gerne hätte:
Code:
mov eax, dword[MyVar] MyVar dq 65
Bezeichnung | Keyword |
DB | byte |
DW | word |
DD | dword |
DQ | qword |
Es ist nicht schlimm, wenn ihr die ganzen Zusammenhänge nicht auf Anhieb verstanden habt, lest es euch einfach
noch ein zwei mal durch und google eventuell.

-------------------------------------------------------------------------------------------------
3. Erste Assembler Schritte
3.7. Sonstige wichtige Befehle
Weitere wichtige Befehle sind:
Mul/Div und Loop:
Mul ist der Befehl für Multiplizieren und Div für Dividieren.
Die Syntax sieht folgendermaßen aus:
Code:
Mul Source
Code:
Div Source
Destination ist automatisch eax.
ASM äquivalenter AutoIt-Code:
PHP Code:
$eax = $eax*Source
Hier sieht man schon den ersten Verwendungszweck von Push.
Hat eax einen wichtigen Wert, dann muss dieser Wert irgendwo gespeichert werden,
bevor mit dem Multiplizieren weiter gemacht werden kann. Ansonsten verschwindet der Wert.
Nun fehlt uns nur noch der Befehl Loop.
Der Befehl Loop ist mit diesem Assembler-Code gleich zu setzen:
Code:
Sprungmarke: ; Code... dec ecx cmp ecx, 0 jne Sprungmarke
Code:
Sprungmarke: ; Code... Loop Sprungmarke
Wenn der Wert von ecx ungleich ("jne") 0 ist, dann wird zur Sprungmarke gesprungen.
Ansonsten geht es normal im Programmcode weiter.
Weitere wichtige Befehle findet ihr hier, wobei ihr die Registernamen meist um ein "e" am Anfang ergänzen müsst:

-------------------------------------------------------------------------------------------------
4. Assembler-Codes
4.1. Berechnen der Fakultät einer ganzen Zahl
So jetzt legen wir mit einem kleinen "sinnvollem" Programm los.
Wir wollen nur den Wert von 10! (also 10 Fakultät) berechnen.
Fakültät von 10 bedeutet nichts anderes als das:
Das lässt sich sogar relativ einfach in Assembler umsetzen:Quote:
10*9*8*7*6*5*4*3*2*1
Code:
mov eax, 10 mov ecx, eax dec ecx fakul: mul ecx LOOP fakul
der Schleifen-Durchläufe mit dem ecx Register fest.
Wir brauchen für die Fakultät nur 10-1 Durchläufe, deswegen auch "dec ecx".
Anschließend durchlaufen wir unsere Schleife solange, bis ecx gleich 0 ist.
Und da Mul automatisch den Wert von eax nimmt, ihn mit ecx multipliziert und ihn wieder in eax legt,
brauchen wir gar nicht groß schieben/speichern.
-------------------------------------------------------------------------------------------------
4. Assembler-Codes
4.2. Potenzieren einer ganzen natürlichen Zahl mit ganzen Exponenten
Es läuft ähnlich ab wie bei der Fakultät, wer sich selbst einmal probieren will, der kann die Lösung
(im Spoiler) auch später nach sehen:
-------------------------------------------------------------------------------------------------
5. AutoIt und Inline-Assembler
5.1. Erstes Programm mit Assembler und AutoIt
AutoIt an sich kann kein Inline-ASM, aber dank einer UDF lässt sich das ganze schön in AutoIt einbauen.
Wenn ihr die Rar-Datei mit der Datei "FASM.au3" noch nicht heruntergeladen habt, dann solltet ihr das jetzt machen,
denn ohne geht es ab jetzt nicht mehr.
Zuerst einmal ganz grob:
Die UDF erzeugt für uns aus den Assembler-Befehlen Maschinencode, den wir dann
ausführen lassen können.
Dann fangen wir einmal unser 1. AutoIt Inline-ASM Skript an.
Zuerst brauchen wir natürlich die FASM.au3.
Wir includen sie also.
PHP Code:
#include <FASM.au3>
$Fasm = FasmInit()
FasmReset($Fasm)
Zuerst includen wir unsere FASM.au3 und anschließend initialisieren wir
unseren Compiler, danach wird der ASM-Code mithilfe von "FasmReset($Fasm)" resetted, um
undefinierte Verhalten auszuschließen.
Das war es im Grunde schon, jetzt können wir anfangen unseren ASM-Code hinzuzufügen:
PHP Code:
#include <FASM.au3>
$Fasm = FasmInit()
FasmReset($Fasm)
FasmAdd($Fasm, "use32")
FasmAdd($Fasm, "mov eax, 3")
FasmAdd($Fasm, "ret")
PHP Code:
FasmAdd($Fasm, "use32")
Ich würde immer den 32Bit Modus nehmen, auch wenn das Betriebssystem 64Bit hat.
Denn 32Bit-Programme laufen auch auf 64Bit, aber andersrum nicht.
Anschließend kommt unser eigentlicher ASM-Code:
PHP Code:
FasmAdd($Fasm, "mov eax, 3")
Und wir müssen unseren ASM-Code auch wieder abschließen, das machen wir mit einem "ret".
Wenn unser Assembler bei "ret" ankommt, ist der ASM-Code zu Ende und es wird mit dem AutoIt-Skript
weitergemacht.
Um das ganze jetzt ausführen zu lassen, könnte ich eine Funktion schreiben, aber manchmal ist es
besser etwas selbst zu erarbeiten als sich alles geben zu lassen.
Um unsere ASM-Code jetzt ausführen zu lassen brauchen wir ersteinmal den kompilierten ASM-Code, den
bekommen wir damit:
PHP Code:
$code=FasmGetBinary($Fasm)
Als nächstes brauchen wir einen Speicherplatz, der ähnlich wie eine Variable in ASM ist.
Wir brauchen also ein Array an Buchstaben.
Das machen wir aber nicht indem wir jetzt ein Array erstellen und alle Buchstaben reinspeichern,
nein wir nehmen "DllStructCreate".
Dieser Befehle reserviert eigentlich nur Speicher im C/C++ bzw. ASM-Stil.
Und weil der Befehl im ASM/C-Style ist müssen wir auch die Größe im ASM/C-Style angeben:
PHP Code:
$Codebuffer=DllStructCreate("byte["&stringlen($code)/2-1&"]")
Länge_des_Codes/2 Byte an speicher?
Der Maschinencode ist wenn er aus der FasmGetBinary(...)-Funktion kommt automatisch in Hexadezimalschreibweise,
der Code wird aber nicht im Hexadezimal-Stil abgespeichert und da 2 Hexzahlen immer ein Buchstabe/Zeichen sind,
brauchen wir nur die Hälfte.
Das "byte" im AutoIt-befehl sollte uns ja bereits von ASM bekannt vorkommen.
Jeder Buchstabe braucht genau ein Byte um dargestellt werden zu können.
Nachdem wir unsere Variable im C/C++ Stil, eine sog. Structure, jetzt erstellt haben, müssen wir sie aber
auch mit unserem Code füllen:
PHP Code:
dllstructsetdata($CodeBuffer,1,$code)
anfangen wollen zu kopieren. Wir wollen beim 1. anfangen.
Und der letzte Parameter "$code" ist unser Maschinencode.
Nachdem wir das geschafft haben, ist der Großteil schon geschafft.
Jetzt müssen wir unseren Code nur noch aufrufen:
PHP Code:
DllCall("user32.dll", "int", "CallWindowProcW", "ptr", DllStructGetPtr($CodeBuffer), "int", 0, "int", 0, "int", 0, "int", 0)

Aber wir schaffen auch das:
DllCall ruft eine Funktion einer Dll auf.
Damit AutoIt weiß in welcher Dll er die Funktion suchen soll, müssen wir ihm das sagen:
"user32.dll".
Der 2. Parameter ist im Grunde nur der Datentyp des Rückgabewertes.
Da wir im moment noch keinen Rückgabewert haben, kann uns das vorerst egal sein.
Der 3. Parameter ist die Funktion, die wir aufrufen wollen.
Die Funktion "CallWindowProcW" macht nichts anderes als Maschinencode auszuführen, hier wird also
unser eigentlicher Maschinencode ausgeführt.
Anschließend müssen wir der Funktion noch mitteilen welchen Maschinencode sie ausführen soll.
Aber wir kennen das ja bereits, wir können keine Zeichenketten übergeben, sondern müssen
die Adresse des ersten Wertes übergeben.
Unser Maschinencode steht ja in unserer Structure "$CodeBuffer", wir brauchen jetzt nur noch die Adresse vom 1. Wert.
Diese Adresse lässt sich mit DllStructGetPtr(...) herausfinden.
Damit die Funktion jetzt weiß, dass wir eine Adresse übergeben und keine nutzlose Zahl, müssen wir
davor noch mithilfe von "ptr" (=Pointer) der Funktion sagen, dass sie jetzt eine Adresse bekommt und
dass wir den Wert wollen, der an der Adresse steht.
Die restlichen Parameter sind Eingangsparameter für unseren ASM-Code, aber dazu später mehr.
-------------------------------------------------------------------------------------------------
5. AutoIt und Inline-Assembler
5.2. 2 Zahlen in ASM addieren
Zuerst einmal müssen wir uns überlegen wie unser ASM-Code aussehen soll, bei uns wird er so aussehen:
Code:
use32 add eax,edi ret
PHP Code:
#include <FASM.au3>
$Fasm = FasmInit()
FasmReset($Fasm)
FasmAdd($Fasm, "use32")
FasmAdd($Fasm, "add eax,edi")
FasmAdd($Fasm, "ret")
$bytecode=FasmGetBinary($Fasm)
$tCodebuffer=dllstructcreate("byte["&stringlen($Bytecode)/2-1&"]")
dllstructsetdata($tCodeBuffer,1,$Bytecode)
$Ret= DllCall("user32.dll", "int", "CallWindowProcW", "ptr", DllStructGetPtr($tCodeBuffer), "int", 12, "int", 33, "int", 0, "int", 0)
machen wir das in "$Ret".
Die eigentliche Frage ist jetzt wie man in unser ASM-Programm 2 von uns auswählbare Zahlen einfügt.
Aber zeurst erweitern wir unseren Code so:
PHP Code:
#include <FASM.au3>
$zahl1=Inputbox("Zahl","Zahl1")
$zahl2=Inputbox("Zahl","Zahl2")
$Fasm = FasmInit()
FasmReset($Fasm)
FasmAdd($Fasm, "use32")
FasmAdd($Fasm, "add eax,edi")
FasmAdd($Fasm, "ret")
$bytecode=FasmGetBinary($Fasm)
$tCodebuffer=dllstructcreate("byte["&stringlen($Bytecode)/2-1&"]")
dllstructsetdata($tCodeBuffer,1,$Bytecode)
$Ret= DllCall("user32.dll", "int", "CallWindowProcW", "ptr", DllStructGetPtr($tCodeBuffer), "int", $zahl1, "int", $zahl2, "int", 0, "int", 0)
Wir wollten für unseren Assemblercode ja 2 Eingangsparameter, nämlich $zahl1 und $zahl2, also haben
wir das auch gleich reingesetzt.
Das "int" davor ist wieder nur der Datentyp, er sagt uns also, dass es eine Zahl ist und zwar eine ganze Zahl
mit einer maximalen Größe von 32Bit.
Wenn wir unsere Funktion ("CallWindowProcW") aufrufen, werden alle Adressen der Eingangsparameter auf dem Stack
gespeichert und bleiben da auch erst einmal.
Damit wir jetzt aber wissen wie wir unsere Werte trotzdem benutzen können, wird die Adresse des
Stackpointers (dem Register esp) zu der Adresse des ersten Eingangparameters gesetzt.
Jetzt muss man wissen, dass eine Adresse immer 4Byte (=32Bit) groß ist.
Um die Werte jetzt also benutzen zu können müssen wir nur die Adresse von "esp" nehmen und
je nachdem welchen Parameter wir wollen die Adresse erhöhen.
Unser fertige Code sähe dann also so aus:
PHP Code:
#include <FASM.au3>
$zahl1=Inputbox("Zahl","Zahl1")
$zahl2=Inputbox("Zahl","Zahl2")
$Fasm = FasmInit()
FasmReset($Fasm)
FasmAdd($Fasm, "use32")
FasmAdd($Fasm, "mov eax, [esp+4]")
FasmAdd($Fasm, "mov edi, [esp+8]")
FasmAdd($Fasm, "add eax, edi")
FasmAdd($Fasm, "ret")
$bytecode=FasmGetBinary($Fasm)
$tCodebuffer=dllstructcreate("byte["&stringlen($Bytecode)/2-1&"]")
dllstructsetdata($tCodeBuffer,1,$Bytecode)
$Ret= DllCall("user32.dll", "int", "CallWindowProcW", "ptr", DllStructGetPtr($tCodeBuffer), "int", $zahl1, "int", $zahl2, "int", 0, "int", 0)
MsgBox(0,"Ergebnis",$Ret[0])
Das Register "esp" zeigt also jetzt an die erste Adresse unserer Eingangsparameter, aber warum müssen
wir esp dann um 4 erhöhen, obwohl wir doch den 1. Eingangsparameter haben wollen?
Nunja, das ist schnell erklärt, streng genommen ist unser erster Eingangsparameter nämlich:
"DllStructGetPtr($tCodeBuffer)", wenn wir esp nicht um 4Byte (=Größe der Adressen) erhöhen würden, dann
bekämen wir die Adresse von unserem Maschinencode.
Also müssen wir +4 machen und +8 für unseren nächsten Eingangsparameter und hätten wir noch einen Parameter,
dann müssten wir + 12 machen.
Und nun zum DllCall:
DllCall liefert immer ein Array zurück, wobei das Array eigentlich nur aus den auf dem Stack liegenden Werte besteht.
Das heißt $Ret[1]=$zahl1, $Ret[2]=$zahl2, $Ret[3]=0 ...
Und $Ret[0] wäre unsere Adresse an der unser Maschinencode steht, wenn nicht das ret da wäre,
das automatisch den Wert von eax an die Stelle der Adresse des Maschinencodes setzt.
$Ret[0] ist also unser eax und da in eax unser Ergebnis gespeichert wurde, ersparen wir uns Arbeit.

-------------------------------------------------------------------------------------------------
5. AutoIt und Inline-Assembler
5.3. Einen kleinen Taschenrechner mit einfachen Parser schreiben
Zuerst einmal sollten wir das Wort "Parser" klären.
Ein Parser ist nichts anderes als ein Programm, das eine Zeichenkette in ihre Bestandteile
aufsplittet und dann je nachdem welche Bestandteile vorhanden sind ein Ergebnis zurück liefert.
Wir wollen uns in diesem Beispiel nur auf das Parsen von Additionen und Subtraktion ohne Klammern und ohne Kommata beschränken.
Alles andere würde zu weit führen.
Unser ASM-Programm soll also, wenn wir das Eingeben:
Das Ergebnis 124 zurückgeben.Quote:
10+5+23+88+6-8
Bevor wir drauf losschreiben, müssen wir uns eine Art Pseudo-Code überlegen.
Ich mach das immer sehr gerne indem ich mir die Frage stelle:
"Woran erkennst du, dass da jetzt 124 rauskommt?"
Die Antwort darauf ist recht banal:
"Ich sehe die Zahlen und die Plus/Minus-Zeichen"
Die nächste Frage muss dann sein:
"Woran erkenne ich, dass die Zahl jetzt 10 ist und nicht 1+0?"
Antwort:
"Die Zahlen werden automatisch aneinander gereiht, wenn kein Zeichen dazwischen ist"
Damit haben wir uns klar gemacht was die Grundlagen sind.
Wir brauchen ein Programm, das zuerst einmal alles aufsplittet und danach alles je nach Art des Zeichens/Buchstabens zusammenfügt.
In AutoIt-Code sähe das so aus (Ihr solltet zuerst das Skript verstehen, bevor ihr weiter mit dem ASM-Teil macht):
PHP Code:
Func Parse($var)
Dim $zahlen[20] ; Maximal 20 Zahlen können Addiert/Subtrahiert werden
Dim $zeichen[19] ; Maximal 19 Zeichen können in der Rechnung vorkommen
$splited = StringSplit($var, "", 2) ; Wir brauchen jedes Zeichen einzeln
$mom_zeichen = 0 ; Momentane Anzahl an gefundenen Zeichen
$mom_zahl = 0 ; Momentane Anzahl an gefundenen Zahlen
$zeichen[0] = "" ; Den Inhalt des ersten Elements auf "" setzen
$zahlen[0] = "" ; Den Inhalt des ersten Elements auf "" setzen
For $i = 0 To UBound($splited) - 1
If Asc($splited[$i]) >= Asc("0") And Asc($splited[$i]) <= Asc("9") Then
; Wenn wir eine Zahl gefunden haben, dann wird sie in unserem Array "$zahlen" gespeichert
$zahlen[$mom_zahl] &= $splited[$i]
Else
$mom_zahl += 1 ; Wenn wir ein Zeichen gefunden haben, dann ist die vorherige Zahl zu Ende
$zahlen[$mom_zahl] = "" ; Der Inhalt muss auf "" gesetzt werden
$zeichen[$mom_zeichen] = $splited[$i] ; Wir speichern unser Zeichen in unserem Array
$mom_zeichen += 1 ; Und erhöhen unsere Anzahl an gefundenen Zeichen um 1
EndIf
Next
$ergebnis = $zahlen[0] ; Unser Ausgangspunkt ist das zuerst gefundene Ergebnis
For $i = 0 To UBound($zeichen) - 1 ; Solange wie wir Zeiche haben, können wir auch rechnen lassen
If $zahlen[$i + 1] <> "" Then ; Wenn wir eine Zahl haben und kein leeres Feld
If $zeichen[$i] = "+" Then ; Wenn unser Zeichen an der Stelle $i ein "+" ist
$ergebnis += $zahlen[$i + 1] ; wird die momentane Zahl auf unser Ergebnis drauf gezählt
ElseIf $zeichen[$i] = "-" Then ; Wenn unser Zeichen an der Stelle $i ein "-" ist
$ergebnis -= $zahlen[$i + 1] ; wird die momentane Zahl von unserem Ergebniss abgezogen
EndIf
EndIf
Next
return $ergebnis ; Das Ergebnis wird zurückgegeben
EndFunc ;==>Parse