|
Assembler in Mnemonics umwandeln
Autor: Andre Klein
Vorwort
|
letzte Aktualisierung: 10.08.2003
| |
In diesem Tutorial geht es darum, wie man Assembler in Benutzerverständliche Mnemonics umwandelt.
Da das hier sehr kompliziert werden kann, solltest du dich ein wenig mit dem HEX-Zahlensystem und mit Assembler auskennen.
Als Kurzerklärung kann man sagen das Assembler die Sprache ist die nur aus 0en und 1en besteht und Mnemonics quasi Alternativ-Befehle für diese 0en und 1en sind. Ein Beispiel wäre MOV AX,BX.
Alle Erklärungen beziehen sich auf meinen 5x86.
Abschnitt 1
|
Erste Schritte
| |
Schauen wir uns mal folgendes Programm an:
MOV AX, 0001
MOV BX, FF02
ADD CX, 20
SUB DX, 50
Da der Prozessor damit nichts anfangen kann, muß dieses Programm (z.B. mit DEBUG.EXE) vorher in Assembler umgewandelt werden. Das daraus resultierende Assemblerprogramm sieht dann in HEX-Form so aus:
B80100
BB02FF
83C120
83EA50
Aber woher weiß der Prozessor welche Hex-Kombination welcher Befehl ist?
Die eine Möglichkeit ist, das der Prozessor quasi "IF"-Befehle ausführt.
Also: IF befehl$="B80100" THEN PRINT "MOV AX,0001"
Da ist aber das Problem das man für jeden MOV-Befehl einen IF-Befehl benutzen muß. Sobald man dann z.B. MOV AX,0002 schreibt muß es auch dafür ein IF geben.
Und wenn man da weiterüberlegt heißt das, das man mehrere Millionen IF-Befehle bräuchte. Und das ist Schwachsinn. Also muß man da irgendwie anders rangehen.
Um die ganze Befehlsflut bewältigen zu können, hat man eine bestimmte ORDNUNG in die Reihenfolge der Befehle gebracht.
Dazu wird zuerst das allererste Byte eines Hex-Befehls gelesen.
In unserem Beispielprogramm wäre das dann B8, BB, 83, 83.
Aus diesem ersten Byte weiß der Prozessor ob es sich um einen 1-Byte-Befehl, um einen 2-Byte-Befehl oder um einen 3-Byte-Befehl handelt. Es können natürlich auch noch mehr Byte sein.
hier eine Liste von festdefinierten 1-Byte-Befehlen:
06 -> PUSH ES
07 -> POP ES
0E -> PUSH CS
--------------------
16 -> PUSH SS
17 -> POP SS
1E -> PUSH DS
1F -> POP DS
--------------------
26 -> ES:
2E -> CS:
--------------------
36 -> SS:
3E -> DS:
--------------------
40 -> INC AX
41 -> INC DX
42 -> INC CX
43 -> INC BX
44 -> INC SP
45 -> INC BP
46 -> INC SI
47 -> INC DI
48 -> DEC AX
49 -> DEC DX
4A -> DEC CX
4B -> DEC BX
4C -> DEC SP
4D -> DEC BP
4E -> DEC SI
4F -> DEC DI
--------------------
50 -> PUSH AX
51 -> PUSH CX
52 -> PUSH DX
53 -> PUSH BX
54 -> PUSH SP
55 -> PUSH BP
56 -> PUSH SI
57 -> PUSH DI
58 -> POP AX
59 -> POP CX
5A -> POP DX
5B -> POP BX
5C -> POP SP
5D -> POP BP
5E -> POP SI
5F -> POP DI
--------------------
60-6F -> nichts
--------------------
C3 -> RET
CB -> RETF
CC -> INT 3
CE -> INTO
CF -> IRET
--------------------
F8 -> CLC
F9 -> STC
FA -> CLI
FB -> STI
FC -> CLD
FD -> STD
Jetzt kommen wir zu den Befehlen die mehr als 1 Byte brauchen
Als Beispiel nehmen wir den 1. Befehl aus unserem Beispielprogramm. Das wäre dann: MOV AX,0001 also B80100.
Wie du vieleicht schon siehst, erkennt der Prozessor die B8 als einen MOV-Befehl mit AX. Durch B8 weiß der Prozessor aber auch das die nächsten 2 Byte mit zum Befehl gehören.
Denn dort befindet sich ja der Wert der nach AX geMOVt werden soll.
Desweiteren ist darauf zu achten das die 2 Bytes des Wertes verdreht werden!
Eine alternative Schreibweise für diese Kombination wäre: B8 = MOV AX, WORD. Da WORD für 2 Byte steht wissen wir was gemeint ist.
B9 = MOV CX, WORD
BA = MOV DX, WORD
BB = MOV BX, WORD
BC = MOV SP, WORD
BD = MOV BP, WORD
BE = MOV SI, WORD
BF = MOV DI, WORD
Wenn du dir jetzt mal den 2. Befehl aus unserem Beispielprogramm anschaust dann fällt dir vieleicht was auf!? (BB02FF)
Es gibt natürlich auch die Möglichkeit Teil-Register (L/H) mit einem Wert zu laden. Da ein Teilregister aber nur 8 BIT hat, können wir logischerweise kein WORD benutzen sondern dürfen nur 1 BYTE nehmen.
Die HEX-Werte hierfür sind:
B0 = MOV AL, BYTE
B1 = MOV CL, BYTE
B2 = MOV DL, BYTE
B3 = MOV BL, BYTE
B4 = MOV AH, BYTE
B5 = MOV CH, BYTE
B6 = MOV DH, BYTE
B7 = MOV BH, BYTE
Damit haben wir alle HEX-Kombinationen mit denen man WERTE in die Register laden kann.
So jetzt wird es aber richtig kompliziert!
Die nächsten Befehle aus unserem Beispielprogramm lauten ADD CX,20 und SUB DX,50.
Die Hex-Kombinationen sind 83C120 und 83EA50.
Obwohl es zwei verschiedene Befehle sind, ist das erste Byte bei beiden gleich (83). Also muß die weitere Befehls-Erkennung im 2. Byte untergebracht sein.
Und zwar ist es so das das zweite Byte (fast) immer in 4 Blöcke unterteilt ist.
Der erste Block geht von 00-3F
Der zweite Block geht von 40-7F
Der dritte Block geht von 80-BF
und der vierte Block geht von C0-FF
In jedem dieser Blöcke ist immer eine ganz bestimmte Befehlsanordnung. (Es gibt mehrere Befehlsanordnungen)
Wenn wir uns jetzt unsere beiden Befehle anschauen, sehen wir das beide im vierten Block liegen.
Das heißt das es in diesem Block irgendwie mehrere Befehle geben muß.
Damit der Prozessor aber jetzt weiß welcher Befehl es sein soll, unterteilt er diesen Block nochmal. Und zwar in
8 gleichgroße Unter-Blöcke.
Da wir im vierten Block sind lauten die Unter-Blöcke wie folgt:
1. UnterBlock: -> C0-C7
2. UnterBlock: -> C8-CF
3. UnterBlock: -> D0-D7
4. UnterBlock: -> D8-DF
5. UnterBlock: -> E0-E7
6. UnterBlock: -> E8-EF
7. UnterBlock: -> F0-F7
8. UnterBlock: -> F8-FF
Und jedem dieser Unter-Blöcke ist ein bestimmter Befehl zugeordnet.
Bei unserem Befehl ADD CX,20 mit den HEX-Werten 83C120 sehen wir das sich dieser Befehl im ersten Unter-Block befindet. Folglich
können wir sagen das der erste Unter-Block für "ADD Register, BByte" steht. Wenn wir uns SUB DX,50 mit dem HEX-Wert 83EA50 anschauen. Dann sehen wir
das sich dieser im 6. Unterblock befindet. Daraus folgt das der 6. Unter-Block für "SUB Register, BByte" steht.
hier eine Liste von den Unter-Block-Befehlen:
1. Unter-Block: -> ADD Register, BByte
2. Unter-Block: -> OR Register, BByte
3. Unter-Block: -> ADC Register, BByte
4. Unter-Block: -> SBB Register, BByte
5. Unter-Block: -> AND Register, BByte
6. Unter-Block: -> SUB Register, BByte
7. Unter-Block: -> XOR Register, BByte
8. Unter-Block: -> CMP Register, BByte
Damit hätten wir jetzt schonmal den Befehl und wissen was wir für Werte einsetzen müssen.
Aber woher wissen wir jetzt um welches Register es sich handelt? Es gibt ja schließlich mehrere davon.
Und zwar ist es so das es bei den Registern IMMER eine bestimmte Reihenfolge gibt. Wenn es sich um 16-Bit-Register handelt dann ist das folgende:
AX, CX, DX, BX, SP, BP, SI, DI (Vieleicht hast du sie ja schon im Tutorial gesehen!? :-))
Wie kommen wir jetzt von 83C120 (ADD CX,20) auf unser Register CX?.
Unser Unter-Block fängt ja bei C0 an. Stimmts? Das heißt: Wenn die zweite Zahl (in unserem Fall die 0) am Anfang eines Unter-Blocks steht, dann
ist das gesuchte Register auch das erste aus unserer Reichenfolge.
Das ergibt dann folgendes:
C0 -> AX
C1 -> CX
C2 -> DX
C3 -> BX
C4 -> SP
C5 -> BP
C6 -> SI
C7 -> DI
Für den zweiten Unterblock ergibt es das folgende:
C8 -> AX
C9 -> CX
CA -> DX
CB -> BX
CC -> SP
CD -> BP
CE -> SI
CF -> DI
So und jetzt wo du das weißt, versuche mal rauszufinden welches Register denn zu EA gehört.
Richtig! DX ist die Antwort!
Jetzt kommen wir zu BByte. Wie du siehst sind dort zwei B's. Das hat die Bedeutung das das Byte welches Addiert oder Subtrahiert werden soll, ein "Berechnetes" Byte ist.
Soll heißen das Byte kann einen Wert von -128 bis + 127 annehmen. Das heißt das Werte von 00-7F positive Zahlen sind und alles was darüber ist, sind negative Zahlen.
In Qbasic kann man negative Zahlen so berechnen: BByte = (256 - BByte) * -1
So, damit haben wir jetzt schon mal die Grundkentnisse der Aufteilungen.
Und jetzt geht es weiter.
Dazu nehmen wir uns ein neues Programmbeispiel. Und zwar folgendes:
MOV [SI] , AX
MOV [SI + 20], AX
MOV [SI + 2030], AX
MOV DX, AX
Der daraus resultierende Hex-Code lautet:
8904
894420
89843020
89C2
Anhand des Hex-Codes können wir erkennen das das erste Byte immer 89 ist, daraus folgt das die 89 IMMER ein MOV-Befehl ist.
Jetzt müssen wir uns wieder das 2. Byte vornehmen und es wieder in unsere 4 Blöcke teilen.
Zur Erinnerung:
1. Block 00-3F
2. Block 40-7F
3. Block 80-BF
4. Block C0-FF
Wenn wir uns jetzt mal unser Beispiel genauer anschauen, dann sehen wir das jeder Befehl in jeweils einem Block liegt. Daraus können für schon mal Rückschlüsse ziehen.
Und zwar alles was im ersten Block liegt lautet: MOV [Adresse], Register
Alles was im zweiten Block liegt lautet: MOV [Adresse + Byte], Register
Alles was im dritten Block liegt lautet: MOV [Adresse + Word], Register
Alles was im vierten Block liegt lautet: MOV Register, Register
Als erstes wollen wir jetzt das Register rausfinden was rechts vom Komma steht.
Dazu nehmen wir unseren ersten Befehl MOV [SI], AX mit den Hex-Werten 8904.
Dadurch das das zweite Byte eine 04 ist, sehen wir das es sich um den 1. Block handelt.
Diesen ersten Block unterteilen wir jetzt wieder in unsere 8 Unter-Blöcke wie wir es schon mal gemacht haben.
Wo wir das erste Mal einen Block in seine Unter-Blöcke aufgeteilt haben, haben wir jedem Unter-Block einen Befehl zugeordnet. Hier ist es genau das
gleiche, nur das wir anstatt eines Befehls ein Register zuordnen. Und wie soll es auch anders sein steht der erste Unter-Block für das erste Register aus unserer Register-Reihenfolge.
Am Ende lautet das für die Unterblöcke:
1. Unter-Block: -> AX
2. Unter-Block: -> CX
3. Unter-Block: -> DX
4. Unter-Block: -> BX
5. Unter-Block: -> SP
6. Unter-Block: -> BP
7. Unter-Block: -> SI
8. Unter-Block: -> DI
Da wir mit 04 im ersten Unterblock liegen, ist unser Register in diesem Fall AX.
Jetzt zu [Adresse].
Auch hier gibt es eine Adressen-Reihenfolge. Sie lautet:
1. [BX + SI]
2. [BX + DI]
3. [BP + SI]
4. [BP + DI]
5. [SI]
6. [DI]
7. [WORD]
8. [BX]
Um jetzt die Richtige Adressenbezeichnung rauszufinden, müssen wir jetzt wieder schauen an welcher Position im Unter-Block unser Wert liegt.
Wir haben 04. Das heißt das es sich um die Adresse handelt die an Position 5 steht. (zu beachten ist, das bei HEX immer von 0 an gezählt wird!)
Also in diesem Fall [SI]
bei 894420 ist es auch die Adresse SI aber diesmal so: [SI + Byte]
So, damit hast du die meistverwendeten Befehlsanordnungen kennengelernt, und kannst dir jetzt einen Disassembler schreiben! :-)
Du brauchst immer nur wissen um was für einen Befehl es sich handelt,
und kannst dann die ganzen Register und Adressen automatisch raussuchen lassen, da du ja jetzt weißt wie sie angeordnet sind.
Abschluss
|
letzte Aktualisierung: 10.08.2003
| |
Puh, geschafft. Wenn du weitere Fragen hast oder ich irgend etwas falsch beschrieben habe, dann
Mail an Webmaster.
|
|
|