Grundzüge der Assemblerprogrammierung

Werbung
7a. Rechnerarchitektur und Grundzüge der
Assemblerprogrammierung
Inhalt:
 Schichtenmodell
 x86-Architektur und x86-Assemblerprogrammierung
 Assemblersprache und Einbindung in C
Peter Sobe
1
Schichtenmodell
 Strukturierung des Rechensystems (Hardware und Software) in
mehrere aufeinander liegende Schichten.
 Höhere Schichten benutzen darunter liegende über Schnittstellen
 Nur die Schnittstellen sind nach oben sichtbar, die Implementierung
der zugehörigen Schicht bleibt verborgen (’information hiding’).
 Schichten können ausgetauscht werden (unter Beibehaltung ihrer
Schnittstelle), ohne dass die darüber liegenden Schichten geändert
werden müssen.
Beispiel: Verschiedene Sprachschichten
High-level language level (C):
A = B + C;
Peter Sobe
Assembly language level (MC68020):
MOVE.W
B, D1
ADD.W
C, D1
MOVE.W
D1, A
2
Beispiel
Machine language level (MC68020) (in bits):
1000:
MOVE.W
(0x2002).W,D1
0011
0010
0010
0000
0011
0000
1000
0010
1004:
ADD.W
(0x2004).W,D1
1101
0010
0010
0000
0111
0000
1000
0100
1008:
MOVE.W
D1,(0x2000).W
0011
0010
0001
0000
1100
0000
0001
0000
2000:
A
2002:
B
2004:
C
Peter Sobe
3
Beispiel
Die C-Anweisung summe = a + b + c + d;
ist für einen Mikroprozessor zu komplex und muss daher in
mehrere einzelne Anweisungen aufgeteilt werden.
Ein Prozessor kann immer nur zwei Zahlen addieren und
das Ergebnis in einer der beiden verwendeten "Variablen"
(Akkumulatorregister) speichern.
Das Programm unten entspricht daher eher einem
Maschinenprogramm (Assembler):
…das würde beim x86
so aussehen:
summe = a;
mov eax,[a]
summe = summe + b;
add eax,[b]
summe = summe + c;
add eax,[c]
summe = summe + d;
add eax,[d]
Peter Sobe
4
Schichtenmodell
 Schnittstelle zu Level 2 entspricht
einer Rechnerarchitektur
 Schicht Level 1 wird im Allgemeinen zur
Hardware gerechnet, obwohl sie auch
Mikroprogramme (Firmware) enthalten
kann. Heute aber meist fest verdrahtete‚
Ablaufsteuerungen (Hardware).
 Einzelne Schichten sind intern selbst
wieder in Schichten unterteilt.
 Systementwurf muss auch
Wechselwirkung zwischen Schichten
berücksichtigen.
Peter Sobe
5
Konvertierung zwischen Schichten (1)
- Compilierung von Programm Phigh auf Plow (Sprache Lhigh auf Llow)
(Beispiel: C-Compiler)
Source
program
Compiler
Object
program
Plow (Llow)
Phigh (Lhigh)
Peter Sobe
Object
program
Hardware
Hardware
Execution
Execution
6
Konvertierung zwischen Schichten (2)
Interpretation von Lhigh auf Llow (Beispiele: Mikroprogramme, BasicInterpreter)
Source
program
Lhigh
Machine
instructions
Interpreter
Phigh (Lhigh)
Llow
Hardware
Execution
Eine umgekehrte Konvertierung von einer tieferen auf eine höhere Schicht
ist im Allgemeinen nicht mehr möglich, da Semantik verloren geht
(’semantic gap’)
Peter Sobe
7
x86 Architektur (1)
Betrachtet ausgehend vom äußeren Erscheinungsbild:
Registersatz: Anzahl der Register, Freiheiten bzw.
Beschränkungen bei deren Verwendung
Befehlssatz: Befehlsliste und evtl. verschiedene Varianten der
Befehle, wenn unterschiedliche Adressierungsarten
zugelassen sind.
Alles andere betrifft die Implementierung und Realisierung
Peter Sobe
8
x86 Architektur (2)
Intel 80x86-Familie
80186
8086
Co-Proz. Busbreite
Daten/Adress.
(Bit)
16/20
8087
80188
80286
80287
80386DX
80386SX
80387
80486DX
80486SX
80487
Pentium
Verbesserung der
Implementierung der Architektur
von 12 CPI beim 8086 auf 1.5 - 3
CPI beim Pentium
(CPI = Cycles Per Instruction).
Peter Sobe
Große Bedeutung durch Einsatz
in IBM-kompatiblen PCs.
Aufgrund der hohen
16/24
Stückzahlen ‚Mainstream’ der
16/24 (SX) derzeitigen
32/32 (DX) Rechnerentwicklung.
32/32
64/32
CISC-Prozessoren aus
historischen Gründen „binär
abwärtskompatibel“
zum ‚Urahnen’ 8086
Stetige Verbesserung der
Technologie (Taktfrequenz von
4.77 bis 10 MHz beim
8086/8088 auf über 3 GHz beim
Pentium).
9
Instruction Set Architecture (Intel IA-32)
32
AH
AX
AL
EAX
BH
BX
BL
EBX
CH
CX
CL
ECX
DH
DX
DL
EDX
16 15
SI
ESI
DI
ESI
BP
EBP
SP
ESP
87
8 x 32-Bit-Register mit 16-BitRegistern des 8086 in unteren
beiden Bytes
0
Register dienen zur kurzfristigen Speicherung von Operanden, Adressen,
Indexwerten innerhalb des Prozessors
Peter Sobe
10
Instruction Set Architecture (Intel IA-32)
V
Exponent Signifikant
R0
R1
8 x 80-Bit-Gleitkommaregister
(internes IEEE-Format)
R2
R3
R4
R5
R6
R7
79 78
64 63
0
Gleitkomma-Register dienen zur kurzfristigen Speicherung
Fließkomma-Variablenwerten, um vom Fließkomma-Rechenwerk
verknüpft zu werden
Peter Sobe
11
Instruction Set Architecture (Intel IA-32)
CS
SS
DS
ES
FS
6 x 16-Bit-Segmentregister
(8086 hatte nur 4):
Codesegment CS,
Stacksegment SS,
4 Datensegmente
DS, ES, FS und GS
GS
15
31
31
Peter Sobe
0
32-Bit-Befehlszähler, 32-BitFlagregister (8086: je 16 Bit)
16 15
0
16 15
Diverse Zusatzregister z. B.
für Kontrolle und Ausnahme0 behandlung
12
IA-32 Datentypen
CISC-Befehlsformat (variable Länge)
Adressierungsarten
- unmittelbar
- Register indirekt
- direkt
- indiziert
- Register
Peter Sobe
13
IA-32 Befehlssatz ohne Gleitkommabefehle
Transferoperationen (s.g. Moves)
MOV DST, SRC
Move SRC to DST
PUSH SRC
Push SRC onto Stack
POP DST
Pop value from Stack to DST
XCHG DS1, DS2
Exchange DS1 and DS2
Diese Befehle werden benutz, um Variablenwerte vor deren
Verwendung in Rechenoperationen in die Register zu holen,
oder von den Registern wieder zurück in den Hauptspeicher zu
kopieren.
Oben ist nur eine Auswahl der gebräuchlichsten Befehle
angegeben.
Peter Sobe
14
IA-32 Befehlssatz ohne Gleitkommabefehle
Arithmetische Operationen
ADD DST,SRC
Add SRC to DST
SUB DST,SRC
Subtract SRC from DST
MUL SRC
Multiply EAX by SRC (unsigned)
IMUL SRC
Multiply EAX by SRC (signed)
DIV SRC
Divide EDX-EAX by SRC (unsigned)
IDIV SRC
Divide EDX-EAX by SRC (signed)
INC DST
Add 1 to DST
DEC DST
Subtract 1 from DST
NEG DST
Negate DST (subtract it from 0)
Peter Sobe
15
IA-32 Befehlssatz ohne Gleitkommabefehle
Steuerfluss
JMP ADDR
Jump to ADDR
Jxx ADDR
Conditional Jump to ADDR
(xx zzum Beispiel NE für ‚NOT Equal‘ als
Ergebnis des letzten Vergleichs mit CMP
(Compare)
CALL ADDR
Call Procedure at ADDR
RET
Return from Procedure
LOOPxx
Loop until Condition is met
Peter Sobe
16
IA-32 Befehlssatz ohne Gleitkommabefehle
Boolean
AND DST,SRC
Boolean AND SRC into DST
OR DST,SRC
Boolean OR SRC into DST
XOR DST,SRC
Boolean Exclusive OR SRC to DST
NOT DST
Replace DST with 1‘s complement
Shift/Rotate
SAL/SAR DST,#
Shift DST left/right # of Bits
SHL/SHR DST,#
Shift logical DST left/right # of Bits
ROL/ROR DST,#
Rotate DST left/right # of Bits
RCL/RCR DST,#
Rotate DST through carry # of Bits
Vergleichsoperationen
TST SRC1,SRC2
Boolean And Operands, Set Flags
CMP SRC1,SRC2
Set Flags based on SRC1-SRC2
Peter Sobe
17
Mikroarchitektur Pentium 4
 Umsetzung der IA-32 CISC-Befehle in 1
bis 4 Ops (interne RISC-Befehle) durch
Decoder.
 Ausführung in supersklarer RISCArchitektur
 Trace Execution Cache (TEC) für Ops
mit eigener Sprungvorhersage,
 3 Ops pro Takt wie PentiumIII
 Verbesserte Sprungvorhersage für x86Befehle mit größerem BTB
 20-stufige I-Pipe, Taktraten bis über 3
GHz
 13 Funktionseinheiten, davon max. 6
gleichzeitig aktivierbar
 8 KB Datencache (klein, aber schnell);
Hardware-Prefetching mit Quad Pumped
Speicherschnittstelle (3,2 GByte/s)
 Befehlssätze (MMX, SSE, SSE2)
 Optional: Hyperthreading (SMT)
Peter Sobe
18
Hyperthreading
Intels Implementierung von SMT: 2-fach Hyper-Threading für den P4, auch für
Atom CPUs,
Verhält sich für das Betriebssystem wie zwei logische Prozessoren, d. h.
Multiprozessor-Software ist ohne Änderung lauffähig.
P4-Pipeline mit SMT
Pipeline-Register (Queues) und
einige Pipelinestufen verdoppelt,
die meisten Stufen werden
abwechselnd von beiden Threads
genutzt. Verdoppelung der
Register durch RegisterRenaming implementiert.
Nur 5% zusätzliche Chipfläche.
Konflikte beim Nutzen der
gemeinsamen Caches (Cache
Aliase) können Leistung
einschränken.
Peter Sobe
19
x86 Architektur (2)
Allgemeine Register
AX – Akkumulator-Register, Ziel und Quelle für Rechenoperationen
Teilung in hohes Byte (AH) und niedriges Byte (AL)
BX - Basis-Register für Anfangsadressen,
Teilung in hohes Byte (BH) und niedriges Byte (BL)
CX – Count Register,
Teilung in hohes Byte (CH) und niedriges Byte (CL),
allgemein verwendbar, spezielle Bedeutung bei Schleifen
DX - Daten-Register , Teilung in hohes Byte (DH) und
niedriges Byte (DL)
EAX EAX
RAX (bei x86-64)
AX AX
AH AH
64 Bit
Peter Sobe
32 Bit
16 Bit
ALAL
8 Bit
20
x86 Architektur (3)
Pointer-Register
SP Stack-Pointer: zur Adressierung des Stacks verwendet
BP Base-Pointer: zur Adressierung des Stacks verwendet
IP Instruction-Pointer: Offset des nächsten Befehls
Index-Register
SI Source-Index: Unterstützung von Adressierungen
esi Quelle (eng: source) für Stringoperationen
DI Destination-Index: Unterstützung von Adressierungen
edi Ziel (eng: destination) für Stringoperationen
Segment-Register
CS Code-Segment: zeigt auf aktuelles Codesegment
DS Daten-Segment: zeigt auf aktuelles Datensegment
SS Stack-Segment: zeigt auf aktuelles Stapelsegment
ES Extra-Segment: zeigt auf weiteres Datensegment
Peter Sobe
21
x86 Architektur (4)
Statusflags
CF Carry-Flag Übertragflag
AF Auxiliary Carry-Flag Hilfsübertragflag
ZF Zero-Flag Nullflag
SF Sign-Flag Vorzeichenflag
PF Parity-Flag Paritätsflag
OF Overflow-Flag Überlaufflag
Kontrollflags
TF Trap-Flag Einzelschrittflag
IF Interrupt Enable-Flag Interruptflag
Peter Sobe
22
x86 Assembler-Programmierung (1)
Die C-Anweisung
summe = a + b + c + d;
würde beim 80x86 Assembler so aussehen:
mov eax,[a]
add eax,[b]
add eax,[c]
add eax,[d]
mov [s], eax
Mit eax ist das 32 Bit breite AX Register gemeint. Alle
Operationen beziehen sich damit auf 32 Bit
Verarbeitungsbreite.
Peter Sobe
23
x86 Assembler-Programmierung (2)
Einfache if-then-else Konstrukte müssen in der AssemblerSprache in Compare und einen bedingten Sprung
umgewandelt werden …
if (a == 4711) {...}
else { ... }
Im x86 Assembler sieht das dann so aus:
cmp eax,4711
jne ungleich
gleich: ...
jmp weiter
ungleich: ...
weiter: ...
Peter Sobe
24
x86 Assembler-Programmierung (3)
Einfache Zählschleifen werden von einem x86 Prozessor
besser unterstützt. Das folgende C-Programm
for (i=0; i<100; i++)
{ summe = summe + a;
}
sieht im 80x86 Assembler so aus:
mov ecx,100
schleife: add eax,[a]
loop schleife
Der Loop-Befehl dekrementiert implizit das ecx Register
und führt den Sprung nur aus, wenn der Inhalt des ecx
Registers anschließend nicht 0 ist.
Peter Sobe
25
x86 Assembler-Programmierung (4)
Speicherzugriff
Meistens reichen die Register nicht aus, um ein Problem zu
lösen. In diesem Fall muss auf den Hauptspeicher des
Computers zugegriffen werden, der erheblich mehr
Information speichern kann.
Für den Assemblerpogrammierer sieht der Hauptspeicher wie
ein riesiges Array von Registern aus, die je nach Wunsch 8, 16
oder 32 Bits "breit" sind (je nach Datentyp).
Die kleinste adressierbare Einheit ist ein Byte (= 8 Bits).
Um auf einen bestimmten Eintrag des
Arrays "Hauptspeicher" zugreifen zu können, muss der
Programmierer die Adresse des Eintrages
kennen. Das erste Byte des Hauptspeichers bekommt dabei
die Adresse 0, das zweite die Adresse 1 usw.
Peter Sobe
26
x86 Assembler-Programmierung (5)
In einem Assemblerprogramm können Variablen angelegt
werden, indem einer Speicheradresse ein Label zugeordnet
und dabei Speicherplatz in der gewünschten Größe
reserviert wird.
[SECTION .data]
gruss:
db 'hello, world'
unglueck:
dw 13
million:
dd 1000000
[SECTION .text]
mov ax,[million]
...
db … define byte, dw … define word (2 Bytes),
dd … define double word
Peter Sobe
27
x86 Assembler-Programmierung (6)
Stack
Nicht immer will man sich ein neues Label ausdenken, nur um kurzfristig
mal den Wert eines Registers zu speichern, beispielsweise, weil man das
Register für eine bestimmte Anweisung benötigt, den alten Wert aber
nicht verlieren möchte. In diesem Fall wünscht man sich sowas wie einen
„Ablagehaufen“. Den bekommt man mit dem Stack. Der Stack
ist eigentlich nichts weiter als ein Stück des
Hauptspeichers, nur dss dort nicht mit festen Adressen
gearbeitet wird, sondern die zu sichernden Daten einfach
immer oben drauf geschrieben (push) bzw. von oben
heruntergeholt werden (pop). Der Zugriff ist also ganz
einfach, vorausgesetzt man erinnert sich daran, in welcher Reihenfolge
die Daten auf den Stapel gelegt wurden.
Ein spezielles Register, der Stackpointer esp zeigt stets auf das oberste
Element des Stacks. Da push und pop immer nur 32 Bits auf einmal
transferieren können, ist der Stack in der folgenden Abbildung vier Bytes
breit dargestellt.
Peter Sobe
28
x86 Assembler-Programmierung (7)
Adressierungsarten
Die meisten Befehle des x86 können ihre Operanden
wahlweise aus Registern, aus dem Speicher oder
unmittelbar einer Konstante entnehmen. Beim mov
Befehl sind (u. a.) folgende Formen möglich, wobei
der erste Operand stets das Ziel und der zweite stets
die Quelle der Kopieraktion angeben:
Registeradressierung:
Der Wert eines Registers wird in ein anderes übertragen.
mov ebx,edi
Peter Sobe
29
x86 Assembler-Programmierung (8)
Unmittelbare Adressierung:
Die Konstante wird in das Register übertragen.
mov ebx,1000
Direkte Adressierung:
Der Wert der an der angegebenen Speicherstelle
steht, wird in das Register übertragen.
mov ebx,[1000]
Register-Indirekte Adressierung:
Der Wert, der an der Speicherstelle steht, die durch
das zweite Register bezeichnet wird, wird in das
erste Register übertragen.
mov ebx,[eax]
Peter Sobe
30
x86 Assembler-Programmierung (9)
Basis-Register Adressierung:
Der Wert, der an der Speicherstelle steht, die sich
durch die Summe des Inhalts des zweiten Registers
und der Konstanten ergibt, wird in das erste
Register übertragen.
mov eax,[10+esi]
Peter Sobe
31
Assembler-Einbindung in C (1)
In einem C-Programm kann jede Anweisung durch einen Block von
Assembler-Befehlen durch folgende Syntax ersetzt werden:
_asm { <Folge von Assembler-Befehlen> } ;
Jeder Assemblerbefehl muss durch Semikolon abgeschlossen
sein.
Die in den Assembler-Befehlen vorkommenden
Hauptspeicheroperanden können Bezeichnungen des C-Programms
sein. Die interne Darstellung und vor allem die Länge der
Operanden muss gemäß der C-Deklaration so sein, dass sie
kompatibel zum angewandten Befehl ist.
Damit kann ein Datenaustausch zwischen den Assembler- und den
C-Passagen erfolgen.
Peter Sobe
32
Assembler-Einbindung in C (2)
Beispiel:
mov buf,cx;
Mit cx ist das counter-Register (16 Bit) bezeichnet, folglich muss die
angenommene C-Variable buf auch als eine vorzeichenlose Variable
mit 16 Bit deklariert sein, d.h.
unsigned short buf;
Soll dagegen das 32-Bit-counter-Register adressiert werden (ecx):
mov buf,ecx;
so ist buf folgendermaßen zu deklarieren:
unsigned int buf;
Wird dies nicht beachtet, treten beim kompilieren Fehler auf. Da der
C-Compiler einen Inline-Assembler benutzt, sind nicht alle Codes,
wie bei einem eigenständigen Assembler zugelassen.
Peter Sobe
33
Assembler-Einbindung in C (3)
Beispiel:
#include <stdio.h>
void main()
{ unsigned short erg;
unsigned short eingabe = 2;
unsigned char z;
unsigned int buf;
_asm { //xor cx,cx;
// cx=0
mov cx, eingabe;
inc cx;
// cx++
inc cx;
// cx++
shl cx,3;
// *8
mov erg,cx;
// erg=cx
mov bl,102;
// bl='f‘
mov z,bl;
// z=bl
};
printf("\n erg=%u z=%c \n",erg,z);
}
Peter Sobe
34
Herunterladen