Assembler
Inhaltsverzeichnis
1 Die Register
1.1 Rechenregister
1.2 Segmentregister
1.3 Indexregister
1.4 Pointer Register
1.5 Instruction Pointer Register
1.6 Flag Register
2 Der Adreßraum
2.1 Speichersegmentierung
3 Datentypen
4 Adressierungsarten
5 Der Befehlssatz
5.1 Ein- und Ausgabeoperationen
5.2 Vergleichsoperationen
5.3 Sprungbefehle
5.4 Flags
5.5 Bitschiebe-Operationen
5.6 Logische Operationen
5.7 Arithmetische Operationen
5.8 Stringoperationen
1 Die Register
Der Prozessor ist aus mehreren Registern aufgebaut:
1.1 Rechenregister
Es gibt vier Rechenregister:
15 H 7 L 0
A
Akkumulator
B
Basisregister
C
Zählerregister
D
Datenregister
X
In den Rechenregistern findet die Manipulation der Daten statt, mit denen der Prozessor rechnet. Jedes Register hat eine Breite von 16 Bit.
Um ein 16-Bit Wort einzutragen benötigt man ein ganzes Register (X).
Um ein 8-Bit Byte einzutragen, besteht die Möglichkeit, es entweder an die Bitpositionen 0 bis 7, dem sogenannten Low Byte (L), oder an die Bitpositionen 8 bis 15, dem High Byte (H), zu schreiben.
Der Akkumulator enthält in der Regel einen Operanden und nach der Ausführung des Befehls das Ergebnis.
Das Basisregister enthält die Adressen der Daten im Arbeitsspeicher.
Das Zählerregister dient als Zähler von Schleifenbefehlen.
Das Datenregister dient zur Multiplikation und Division von Wortwerten.
1.2 Segmentregister
Segmentregister gibt es auch vier:
15 0
CS
Codesegmentregister
DS
Datensegmentregister
ES
Extrasegmentregister
SS
Stacksegmentregister
Die Segmentregister beinhalten die Anfangsadressen der Segmente, die der Prozessor braucht. Ihre Breite beträgt immer 16 Bit. Man kann sie nur auslesen und beschreiben.
1.3 Indexregister
15 0
SI
source index register
DI
destination index register
Diese Register können benutzt werden, wenn ein Zeiger auf eine Datenstruktur benötigt wird. Sie werden bei String-Befehlen gebraucht.
1.4 Pointer Register
15 0
SP
stack pointer register
BP
base pointer register
Diese beiden sind auch Index-Register, sie zeigen jedoch auf eine Struktur, die der Prozessor intern bei bestimmten Arten der Adressierung benötigt.
1.5 Instruction Pointer Register
15 0
IP
instruction pointer register
Dieses Register ist zwar eines der wichtigsten Register überhaupt, denn in ihm merkt sich der Prozessor, an welcher Stelle im Programm er sich gerade befindet, was also als nächster Befehl abgearbeitet werden soll. Dennoch können wir nichts mit diesem Register anfangen, da es ausschließlich intern durch den Prozessor verändert wird.
1.6 Flag Register
15 0
F
flag register
Bei der Abarbeitung bestimmter Befehle verändert der Prozessor dessen Inhalt. Dies ermöglicht eine gewisse Art der Kommunikation zwischen Prozessor und Programmierer. Das Flag Register lässt sich sogar teilweise manipulieren. Von den 16 Bits haben nicht alle eine Bedeutung.
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
O D I T S Z A P C
An Bit 11 dieses Registers befindet sich das overflow flag. Dieses Flag wird vom Prozessor immer dann gesetzt, wenn nach einer Operation ein Überlauf stattgefunden hat. Das direction flag an Stelle 10, das interrupt enable flag an Stelle 9 und das trap oder single step flag an Stelle 8 haben bestimmte Aufgaben, die jedoch für den Programmierer unwichtig sind. Das sign flag (7), das zero flag (6), das auxiliary flag (4) und das parity flag (2) spiegeln bestimmte Eigenschaften von Registerinhalten nach verschiedenen Operationen wieder. Gemeinsam mit dem carry flag (0) sind sie für den Programmierer die wichtigsten Flags.
2 Der Adreßraum
Der Adreßraum ist der Bereich des Speichers, den der Prozessor ansprechen kann, in dem er also Daten ablegen, und aus dem er sie wieder lesen kann. Eng verknüpft mit dem Begriff Adreßraum sind auch die Begriffe Datenbus und Adreßbus.
Der Datenbus ist die Menge aller Datenleitungen, über die der Prozessor Daten aus dem Speicher holen, oder in den Speicher ablegen kann. Die Architekturen 80386, 80486 und Pentium haben einen 32-Bit-Datenbus.
Der Adreßbus ist die Summe aller Adreßleitungen, über die ein Prozessor verfügt. In unserem Fall sind dies 20 Stück.
Mit 20 Adreßleitungen ist ein MB Speicher ansprechbar, es ergibt sich jedoch das Problem, dass die Register maximal 16 Bit breit sind, deswegen passen die Adressen des gesamten Adreßbus nicht in ein Register. Als Lösung werden zwei Register zu je 16 Bit verwendet (=32 Bit), was jedoch zwölf ungenutzte Bit mit sich bringt. Bei Intel kam man deshalb auf die Idee der Speichersegmentierung.
2.1 Speichersegmentierung
Ein Segment ist eine bestimmte Anzahl von Bytes (ähnlich einem Array [0...X]). Im Prozessor gibt es ein Register, das die Nummer eines solchen Segments aufnehmen kann. Ein solches Register ist 16 Bit breit. Der gesamte Speicher lässt sich so als zweidimensionales Array ansehen. Die Größe eines Segments erhält man durch die Division der maximalen Größe des Adreßraums (2² ) durch die maximale Anzahl von Segmenten (2 ). Als Ergebnis erhält man 16, was bedeutet, dass durch die Segmentierung des Speichers in Blöcke zu je 16 Bytes der gesamte Adreßraum ansprechbar ist. Dazu muss man nur die Nummer des Segments in das gewünschte Segmentregister schreiben, zB Segment 0 für die ersten 16 Bytes, Segment 1 für die nächsten 16 Bytes,...
Die Segmentgrenzen befinden sich zwischen den Bytes 15 und 16, den Bytes 31 und 32, usw. Diese Segmentgrenzen werden in die Segmentregister eingetragen. Um auf ein bestimmtes Byte zuzugreifen benötigt man noch ein zweites Register (Zeigerregister), das auf das gewünschte Byte zeigt.
Die Berechnung der Adresse eines Bytes im Speicher erfolgt folgendermaßen:
Adresse = Segmentnummer * 16 + Zeiger
3 Datentypen
Im Grunde genommen gibt es 5 verschiedene Daten-Typen, durch Programmierung sind aber auch andere erzielbar.
Datentyp
Größe in Bits
Bit
1
Byte
8
Wort
16
langes Wort
32
gepackte, binär-codierte Dezimalzahlen
4
Durch Routinen können noch Datentypen bis zu einer Größe von 128 Bits erreicht werden.
4 Adressierungsarten
a) unmittelbare Adressierung
Hier erfolgt keine Adressierung im eigentlichen Sinne. Der Operand wird direkt im Operandenteil des Befehls angegeben.
z.B. der Befehl ADD AX, 12 addiert 12 zum Inhalt des Registers AX
b) direkte Adressierung
Die Adresse des Operanden wird durch eine symbolische Adresse angegeben.
z.B. der Befehl ADD AX, SUMME bewirkt, dass der Inhalt der Speicherzelle SUMME zum Inhalt des Registers AX addiert wird
c) indirekte Adressierung
Die Adresse des Operanden wird durch den Inhalt eines Registers angegeben.
z.B. der Befehl ADD AX, [SI] bewirkt, dass der Inhalt der Speicherzelle, deren Adresse im Register SI steht, zum Inhalt des Registers AX addiert wird
d) Basis-Adressierung und indizierte Adressierung
Die Adresse des Operanden wird durch eine symbolische Adresse und dem Inhalt eines Registers berechnet.
z.B. der Befehl ADD AX, SUMME[SI] bewirkt, dass der Inhalt der Speicherzelle, deren Adresse, für die SUMME steht, und dem Inhalt des Registers SI ermittelt wird, zum Inhalt des Registers AX addiert wird
5 Der Befehlssatz
5.1 Ein- und Ausgabeoperationen
Die Daten sind im Datensegment aufgehoben. Dessen Adresse wird im data segment register (DS-Register) aufgehoben. Der Prozessor kann anhand des Inhalts des DS-Registers feststellen, in welchem Segment des RAMs die Daten zu suchen sind.
LEA: Dieser Befehl liefert das gewünschte Datum im Segment an den Prozessor
LEA reg, adr. => der Offset-Anteil der Adresse des 2. Operanden wird in das Register eingetragen
LDS und LES bewirken das selbe wie LEA, nur dass sie die vollständige Adresse in das Register eintragen, anstatt nur den Offset-Anteil
MOV: Dies ist ein sehr wichtiger Befehl. Mit ihm können sowohl Daten von einem Register in ein anderes kopiert werden, Daten aus einer Speicherstelle in ein Register des Prozessors geladen werden, als auch Daten von einem Register auf eine Speicherstelle geschrieben werden.
MOV Ziel, Quelle => MOV AH, AL
H
L
H
L
A
?
4711
A
4711
4711
XCHG: Dieser Befehl kopiert nicht Daten von einer Quelle in das Ziel, sondern er vertauscht die beiden Daten.
PUSH: Mit diesem Befehl wird ein Register-/Variableninhalt auf den Stapel (Stack) abgelegt
POP: Dieser Befehl holt das oberste Datum vom Stack herunter
5.2 Vergleichsoperationen
CMP: Dieser Befehl bewirkt einen arithmetischen Vergleich zweier Operanden
Der Befehl arbeitet wie eine Subtraktion, d.h. der 2. Operand wird vom 1. abgezogen. Die Auswertung des Ergebnis erfolgt durch die Veränderung bestimmter Flags. Register und Speicherstellen bleiben unverändert.
z (zero flag) wird gesetzt, wenn das Ergebnis der Subtraktion 0 war
c (carry flag) wird gesetzt, wenn beide Zahlen ohne Vorzeichen waren, und der 1. Operand kleiner war als der 2.
o (overflow flag) wenn der 1. Operand kleiner war als der 2., der Unterschied zum carry flag ist der, dass das overflow flag bei vorzeichenbehafteten Zahlen gesetzt wird
a (auxiliary flag) wird gesetzt, wenn bei BCDs (beanspruchen nur 4 Bits eines Bytes) ein Ãœberlauf stattgefunden hat
p (parity flag) wenn das Ergebnis der Subtraktion eine gerade Anzahl an Bits liefert, spielt nur bei Prüfsummen eine Rolle
s (sign flag) wenn Bit 15, bzw. Bit 7 gesetzt ist, der Programmierer muss wissen, ob das gesetzte Bit das Vorzeichen oder ein Teil der Zahl ist
Welche Flags für den Programmierer wichtig sind, muss er selbst wissen, da nur er weiß mit welchen Zahlen (mit/ohne Vorzeichen, BCD) er gearbeitet hat.
TEST: ist der logische Vergleich, zB "Schau nach, ob im Bitfeld WordVar die Bits 9, 4 und 3 gesetzt sind". => TEST WordVar, $0218 (0000 0010 0001 1000 = $0218)
5.3 Sprungbefehle
unbedingte Sprungbefehle
JMP: Sprungbefehl ohne Bedingung
JMP adr. => Sprung zu Zieladresse
bedingte Sprungbefehle
erfolgen aufgrund einer Bedingung, zB durch Vergleichsbefehle werden Flags gesetzt, die über die Situation Auskunft geben. Diese Flagstellungen können mit bedingten Sprüngen ausgewertet werden:
JA: jump if above: wenn der 1. Operand größer ist als der zweite (zero flag (zf) und carry flag (cf) = 0)
JAE: jump if above or equal: wenn der 1. Operand größer oder gleich dem zweiten ist (cf = 0)
JB: jump if below: wenn der 1. Operand kleiner ist als der zweite (cf = 1)
JBE: jump if below or equal: wenn der 1. Operand kleiner oder gleich dem zweiten ist (cf = 1 oder zf = 1)
JE: jump if equal: wenn der 1. Operand gleich dem zweiten ist (zf = 1)
JNE: jump if not equal: wenn erster und zweiter Operand nicht gleich sind (zf = 0)
JG: jump if greater: wie JA, aber bei Zahlen mit Vorzeichen (zf = 0 und sf = of)
JGE: jump if greater or equal: wie JAE, Zahlen mit Vorzeichen (sf = of)
JL: jump if lower: wie JB, Zahlen mit Vorzeichen (sf <> of)
JLE: jump if lower or equal: wie JBE, Zahlen mit Vorzeichen (zf = 1, sf <> of)
Schleifenbefehle
sind nur Vereinfachungen, weil sie mit anderen Befehlen auch nachbildbar sind
LOOP: Vergleich einer Zählervariable mit einem Wert und Sprung an den Schleifenbeginn
die Zählervariable ist im CX-Register
5.4 Flags
Bisher konnten Flags nur durch Operationen verändert werden, es gibt aber auch eigene Befehle:
LAHF: kopiert die Bits 0 bis 7 des Flagregisters in das AH-Register, um einen bestimmten Zustand einzustellen
SAHF: kopiert die Bits 0 bis 7 des AH-Registers in das Flagregister
CLC: löscht das carry flag
5.5 Bitschiebe-Operationen
ROL, ROR: die Bits werden nach links, bzw. rechts verschoben, das eine Bit, das wegfällt wird auf die freie Stelle geschrieben
SHL, SHR: die Bits werden nach links, bzw. rechts verschoben, das Bit, das wegfällt wird in das carry flag geschrieben und die leere Stelle mit 0 aufgefüllt
SAR: wie SHR, nur bleibt Bit 15 aufgrund des Vorzeichens gleich
5.6 Logische Operationen
AND: UND-Verknüpfung => das Bit wird nur dann gesetzt, wenn beide verknüpfenden Bits gesetzt waren
OR: ODER-Verknüpfung => das Bit wird dann gesetzt, wenn zumindest eines der beiden verknüpfenden Bits gesetzt war
XOR: exklusive ODER-Verknüpfung => das Bit wird dann gesetzt, wenn genau eines der beiden verknüpfenden Bits gesetzt war
NOR: löscht ein Bit wenn es gesetzt war, und umgekehrt
5.7 Arithmetische Operationen
ADD: Addition zweier Werte zB ADD AX, BX => Ergebnis in AX
SUB: Subtraktion zweier Werte zB SUB SI, WordVar => Ergebnis in SI
MUL: Multiplikation (1. Operand ist automatisch AX bzw. AL) zB MUL DH
DIV: Division (Divisor entweder in AX (Byte Divisor) oder in DX:AX (Wort Divisor)), das Ergebnis steht in AX, der Rest in DX zB DIV WordVar => Ergebnis in AX, Rest in DX
INC, DEC: Erhöhung, bzw. Verminderung eines Wertes um 1, besonders bei Schleifen in Verwendung
NEG: ändert das Vorzeichen einer Zahl
5.8 Stringoperationen
LODS: zum Laden eines Wertes aus einem String, das geladene Byte/Wort steht in AL/AX
STOS: speichert einen Wert in einen String, der Inhalt von AX (Wort), bzw. AL (Byte) wird in den String kopiert
MOVS: kopiert Daten von einen in einen anderen String
SCAS: vergleicht AX mit dem Inhalt eines Strings, wie CMP
CMPS: vergleicht zwei Strings miteinander
2043 Worte in "deutsch" als "hilfreich" bewertet