Beruflich Dokumente
Kultur Dokumente
Kapitel 3
Architektur
●
Architektur definiert eine weitere Nicht Stoff
Abstraktionsebene digitaler Anwendung von RS1
Systeme
Betriebssystem
●
Beschreibt die Sicht des Dieses Kapitel
Programmierers auf die Architektur
Maschine
Mikroarchitektur Kommt nach
Architektur
Logische Grundschaltungen
Bekannt aus
Gatter
„Logischer Entwurf“
Transistoren
© Christian Hochberger, TU Darmstadt Architektur-Begriff 2
Elemente der Architektur
●
Definiert durch
– Befehlssatz der Maschine
– Art und Ort von Operanden
●
Befehlssatz
– Beschreibt die Sprache eines Computers
– Befehle (Instruktionen) sind die Worte der Sprache
– Ist für alle Programme gleich
●
Befehle
– Beschreiben Art der Operation (Was zu tun ist)
– Ort der Operanden (Womit es zu tun ist)
●
Operanden kommen aus
– dem Speicher / den Registern / dem Befehl selber
© Christian Hochberger, TU Darmstadt Architektur-Begriff 3
Maschinen- und Assembler-Sprache
●
Befehle sind binär codierte Zahlen
– Bitmuster sind Maschinensprache des Computers
– Mikroprozessoren lesen und verarbeiten Maschinen-Instruktionen
●
Menschen können Bitmuster schlecht lesen
– Alternative Darstellungsform: Assemblersprache
– Symbolische Beschreibung von Instruktionen
●
Fast alle Architekturen enthalten bestimmte Befehle
– Addition, Subtraktion, Sprünge
– Erlauben Zugriff auf Speicher und Register
●
„Kann man eine, kann man alle“
© Christian Hochberger, TU Darmstadt Architektur-Begriff 4
Architektur und Mikroarchitektur
●
Architektur ist unabhängig von konkreter HW-Implementierung
●
Zu einer Architektur können verschiedene Mikroarchitekturen existieren
– AMD und Intel nutzen beide IA32 (x86)
– Jeder stellt verschiedene Varianten her
– Varianten sind optimiert auf Performance, Kosten, Leistung, Energieeffizienz, ...
●
Welche Architektur sollte man als erstes lernen?
– Reale Architekturen?
– Fantasie-Architekturen?
– Gute Architekturen?
●
MIPS stellt guten Kompromiss dar
– Kommerziell erfolgreich
– Aber nicht so kompliziert und mit historischem Ballast wie IA32
●
MIPS
– Erfunden ca. 1984 von John Hennessy in Stanford
– In vielen Geräten benutzt (Router, Spielekonsolen, ...)
●
Assembler Befehle
– sind für Menschen lesbar
– Beschreiben Operation und zu verwendende Operanden
●
Einführung
– Zuerst nur einfache arithmetische Befehle
– Dann Art und Ort von Operanden in MIPS
– Viele Beispiele werden durch Vergleich mit Hochsprachen-Konstrukten erklärt
– Wir verwenden C (sollte aber für jeden verständlich sein)
●
Einfachster Fall: Addition
●
Beispiel zeigt
– Addition von b und c, Ergebnis wird nach a geschrieben
Hochsprache MIPS Assembler
a = b + c; add a, b, c
●
Kommentare
– C-Konstrukte enden mit ;
– add wird Mnemonic genannt (mnemotechnische Abkürzung)
– b und c sind die Quellen, a ist das Ziel
– Reihenfolge der Operanden legt Bedeutung fest
© Christian Hochberger, TU Darmstadt Assembler 8
Design Principle 1
●
„Simplicity favors regularity“
●
Bespiel zeigt Subtraktion
Hochsprache MIPS Assembler
a = b - c; sub a, b, c
●
Regularität
– Befehle mit gleicher Anzahl Operanden haben gleiche Struktur
– Vereinfacht Ausführung und Codierung
– Komplexere Fälle lassen sich so nicht beschreiben
●
„Make the common case fast“ Hochsprache MIPS Assembler
a = b + c - d; # $s0=a, $s1=b, $s2=c, $s3=d
●
Komplexere Konstrukte als add $t0, $s1, $s2 # t=b+c
Komposition von einfachen: sub $s0, $t0, $s3 # a=t-d
●
Ausführung wird zusammengesetzt Kommentare in Assembler
●
Beginnen mit „#“
– t wird als Hilfsvariable benutzt ●
Gehen bis zum Ende der Zeile
●
Wenige Befehle → Dekodierlogik einfach (und schnell)
– Komplexe Fälle seltener
– Einfache Fälle häufiger (common case)
●
MIPS gilt als Reduced Instruction Set Computer (RISC)
●
Alternative: Complex Instruction Set Computer (CISC)
– Beispiel: IA32 → Instruktion „String move“
– Muss in RISC durch viele (evtl. hunderte) Instruktionen ersetzt werden
●
RISC Architekturen minimieren HW Komplexität
– Durch niedrige Zahl unterschiedlicher Instruktionen
– Durch niedrige Zahl von Bits für die Kodierung
© Christian Hochberger, TU Darmstadt Assembler 11
Operanden
●
Befehle arbeiten mit Operanden
– Beispiele benutzen Variablen-Namen
– Computer muss aber wissen wo diese gespeichert sind
●
Operanden
– Können im Speicher oder in Registern liegen
– Können Konstanten sein
●
Sind dann Teil der Instruktion
– Alle Fälle treten auf
●
Schneller Zugriff, wenig Auswahlmöglichkeiten
– Konstanten oder Register
●
Langsamer Zugriff, große Kapazität
– Speicher
© Christian Hochberger, TU Darmstadt Assembler 12
Register
●
Befehle brauchen schnellen Zugriff auf Operanden
– Hauptspeicher langsam
– Nur kleine Zahl von Registern in Befehlen
●
MIPS
– 32 Register mit 32 Bit Breite
– Registersatz oder Register File genannt
●
Zugriffsgeschwindigkeit
– Wenige Register → kleiner Dekoder → Schnell
– Viele Register → großer Dekoder → nicht so schnell
●
Registersatz meist in SRAM realisiert
© Christian Hochberger, TU Darmstadt Assembler 13
Registernamen
●
Durch MIPS Konvention festgelegt
●
Nochmal das Additionsbeispiel:
Hochsprache MIPS Assembler
a = b + c; # $s0=a, $s1=b, $s2=c
add $s0, $s1, $s2 # a = b + c
●
MIPS Register
– Durch „$“ am Anfang gekennzeichnet
– Befehl addiert zwei 32-Bit Zahlen ($s1, $s2) und speichert das Ergebnis in einem
32-Bit Register ($s0)
●
Nochmal das komplexere Beispiel:
Hochsprache MIPS Assembler
a = b + c - d; # $s0=a, $s1=b, $s2=c, $s3=d
add $t0, $s1, $s2 # t=b+c
sub $s0, $t0, $s3 # a=t-d
●
MIPS Konvention
– Variablen werden in 18 von 32 Registern gespeichert
– $s0 - $s7 → Saved Registers (Variablen)
●
Besondere Bedeutung bei Funktionsaufrufen
– $t0 - $t9 → Temporary Registers (Hilfsvariablen)
– $t0 speichert Zwischenergebnis b+c
●
Aufgabe:
– Übersetze folgende Hochsprachenkonstrukte in Assembler
a = b – c;
f = (g + h) – (i + j);
●
Insgesamt 32 Register Name Nummer Bedeutung
verfügbar $0 0 constant 0
$at 1 assembler temporary
●
Spezialfall Register 0: $v0-$v1 2-3 function return value
– Beim Lesen immer 0 $a0-$a3 4-7 function arguments
$t0-$t7 8-15 temporary variables
– Schreiben wird ignoriert $s0-$s7 16-23 saved variables
●
Weitere Register $t8-$t9 24-25 temporary variables
●
Register als Speicher für Variablen zu klein
●
Hauptspeicher
– kann auch zum Speichern von Daten verwendet werden
– Aber: Zugriff langsam
●
Konsequenz:
– Oft benutzte Variablen in Registern halten
– Bedeutung der Register ändert sich während des Programms
●
MIPS:
– Speicher 32 Bit breit, Adressen 32 Bit breit
●
Wortadressierung
– Jedes Wort eine eigene Adresse
– Aufeinander folgende Worte → um 1 erhöhte Adresse
●
Speicheradresse immer eine Summe
– Basisadresse → kommt aus Register
– Offset → Wird hinzu addiert, Teil des Befehls
– Wird Register relative Adressierung genannt
●
Zugriff auf Speicher
– load word Instruktion
# Assembly Code (non MIPS), word addressable memory
lw $s3, 1($0) # Wort 1 aus Speicher lesen
# und in $s3 ablegen
sw $s7, 5($0) # $s7 in Wort 5 abspeichern
●
Byteadressierung
– Jedes Byte eine eigene Adresse
– Aufeinander folgende Bytes → um 1 erhöhte Adresse
– Aufeinander folgende Worte → um 4 erhöhte Adresse
●
Neben load/store word gibt es
dann auch load/store byte (lb, sb) Wortadressen Daten
0000000C 40 F3 07 88 Wort 3
00000008 01 EE 28 42 Wort 2
00000004 F2 F1 AC 07 Wort 1
00000000 AB CD EF 78 Wort 0
●
Welche Adresse gehört denn zu welchem Byte?
– Zwei mögliche Antworten
– MSB auf Adresse 0, LSB auf Adresse 3
→ Big-Endian
– MSB auf Adresse 3, LSB auf Adresse 0
→ Little Endian Wortadressen Daten
0000000C 40 F3 07 88 Wort 3
00000008 01 EE 28 42 Wort 2
00000004 F2 F1 AC 07 Wort 1
0 1 2 3
00000000 AB CD EF 78 Wort 0
3 2 1 0
© Christian Hochberger, TU Darmstadt Assembler 22
Big- und Little-Endian (2)
0x.... übliche Schreibweise
für Hexadezimalzahlen
●
Beispiel:
– $s0 enthält 0x23456789
# Assembly Code, byte addressable memory
sw $s0, 0($0)
lb $s0, 1($0)
●
store word schreibt in Wort 0 (egal ob Big- oder Little)
●
Big-Endian Byte Adresse 0 1 2 3
Datum 23 45 67 89
– $s0 enthält 0x00000045
MSB LSB
●
Little-Endian Byte Adresse 3 2 1 0
– $s0 enthält 0x00000067 Datum 23 45 67 89
MSB LSB
© Christian Hochberger, TU Darmstadt Assembler 23
Ausrichtung
●
Auf welchen Adressen dürfen Worte liegen?
– Nur auf Adressen, die durch 4 teilbar sind
→ Ausrichtung auf Worte (word allignment)
– Auf allen Adressen
→ keine Ausrichtung (unaligned)
●
Von Prozessorarchitektur abhängig
– MIPS: Nur ausgerichtete Zugriffe möglich
●
Vereinfacht Bus-Logik
– IA32: Unausgerichtete Zugriffe möglich
●
Vereinfacht Programmierung
●
lb und sb müssen nicht ausgerichtet werden
© Christian Hochberger, TU Darmstadt Assembler 24
Konstanten
●
load word und store word enthalten Konstanten
– Offset, der zur Basis-Adresse addiert wird
– Teil des Befehlswortes
●
Weitere Instruktionen mit Konstante
– add immediate, addi
High-Level Code Assembly Code
a = a + 4; addi $s0, $s0, 4
b = a – 12; addi $s1, $s0, -12
●
„Good design demands good compromises“
●
add und sub: 3 Register-Operanden
●
lw, sw und addi: 2 Register-Operanden, 1 Konstante
●
Design Principle 1 (Simplicity favors Regularity)
– Alle Befehle müssten gleiches Format haben
– Wäre starke Einschränkung (Konstanten wären kleiner)
– Prinzip wird bewusst verletzt
●
Verschiedene Befehlsformate verbessern die Effizienz des Befehlssatzes
●
MIPS: Insgesamt drei verschiedene Befehlsformate
●
Hardware kann nur Bitmuster verarbeiten
– Assembler muss in Maschinensprache übersetzt werden
●
MIPS:
– Alle Instruktionen 32 Bit breit
– Manche Instruktionen könnte man mit weniger Bit codieren
– Einheitliche Länge hat viele Vorteile
– Drei Befehlstypen (Befehlsformate)
●
Unterscheidung bzgl. der Operanden
●
R-Typ: 3 Register
●
I-Typ: 2 Register und Konstante
●
J-Typ: 1 Konstante mit 26 Bit
●
R-Typ → Register-Typ: 3 Register als Operanden
– 2 Register Quelle (rs, rt)
– 1 Register Ziel (rd)
●
Befehl besteht aus 6 Bit-Gruppen: op, rs, rt, rd, shamt, funct
●
op und funct definieren zusammen die Bedeutung
– op immer 0 (bei R-Typ) R-Typ
– funct: add → 32, sub → 34 op rs rt rd shamt funct
●
shamt 6 Bit 5 Bit 5 Bit 5 Bit 5 Bit 6 Bit
– Nur bei Schiebe-Operationen (sonst immer 0)
– Bestimmt um wie viele Bit geschoben wird
●
Reihenfolge der Register unterschiedlich in Maschinen- und
Assemblersprache
●
Code-Beispiel # MIPS Assembly Code
add $t0, $s1, $s2 # t=b+c
sub $s0, $t0, $s3 # a=t-d
Assembler Code Bit-Felder
add $t0,$s1,$s2 0 17 18 8 0 32
sub $s0,$t0,$s3 0 8 19 16 0 34
Maschinen-Code Speicherwort
© Christian Hochberger, TU Darmstadt Maschinensprache 29
I-Typ
●
I-Typ → Immediate Typ, Konstante im Befehl
– 1 Register Quelle (rs)
– 1 Register Ziel (rt), evtl. auch zweite Quelle (sw)
●
Befehl besteht aus 4 Bit-Gruppen: op, rs, rt, imm
●
op definiert Operation I-Typ
op rs rt imm
– 8: addi, 35: lw, 43: sw
6 Bit 5 Bit 5 Bit 16 Bit
●
imm 16 Bit breit
– Nicht immer K2-Zahl (logische Operationen)
addi $s0,$s1,5 8 17 16 5
lw $t2,32($0) 35 0 10 32
sw $s1,4($t1) 43 9 17 4
Maschinen-Code Speicherwort
© Christian Hochberger, TU Darmstadt Maschinensprache 31
J-Typ
●
J-Typ → Jump Typ, speziell für Sprungbefehle
●
Nur ein Operand
J-Typ
– 26 Bit Adresse
op addr
– Genauere Erklärung später
6 Bit 26 Bit
●
op
– Verschiedene Arten von Sprüngen möglich
– Alle Sprünge sind unbedingt
●
Zunächst: Entschlüsseln der Felder des Befehls
– Verschiedene Formate für verschiedene Befehlstypen
– Befehlstyp durch op festgelegt
– op immer an gleicher Position
→ op zuerst entschlüsseln
– op=0 → R-Typ
– op≠0 → I-Typ oder J-Typ
– Weitere Felder nach Befehlstyp entschlüsseln
●
Beispielanweisungen:
– 0x2237FFF1
– 0x02F34022
●
0010 0010 0011 0111 1111 1111 1111 0001
8 → addi rs = 17 rt = 23 imm = -15
●
0000 0010 1111 0011 0100 0000 0010 0010
0 → R-Typ rs = 23 rt = 19 rd = 8 shamt=0 func=34 → sub
sub $t0,$s7,$s3
●
Jeder Befehl 32 Bit breit
– Programm → Sequenz von 32 Bit Worten
– Können im Speicher abgelegt werden
●
Basis für Mächtigkeit von Computern
– Programm im Speicher kann ausgetauscht werden
– Neue Anwendung → einfach Speicherinhalt austauschen
●
Oft als General Purpose Computing bezeichnet
●
Prozessor:
– Holt Befehle aus dem Speicher
– Führt sie aus
© Christian Hochberger, TU Darmstadt Maschinensprache 35
Gespeicherte Programme (2)
●
Ausführen eines Programms
– Prozessor holt Befehle nacheinander aus Speicher
– Aufeinander folgende Befehle liegen im Speicher hintereinander
– Adresse des aktuellen Befehls liegt in speziellem Register:
Program Counter, PC
●
MIPS:
– PC gehört nicht zum Registersatz
– Programme beginnen bei 0x00400000
– PC wird nach jedem Befehl um 4 erhöht
(Ausnahme: bedingte oder unbedingte Sprünge)
●
Maschinen Status
– Speichert Zustand eines Programms
– MIPS: Registersatz und PC
●
Konsequenz
– Betriebssystem kann Maschinen Status retten (irgendwo abspeichern)
– Programm wird unterbrochen
– Betriebssystem restauriert Maschinen Status
– Programm kann weiterarbeiten (als ob nie was passiert wäre)
●
Typische Elemente von Hochsprachen
– Logische und arithmetische Operationen
– if/else Anweisungen
– for und while Schleifen
– Array-Zugriffe
– Funktionsaufrufe
●
Wie werden diese Elemente in Assembler realisiert?
– Exemplarische Diskussion
– Prinzipien zeigen
●
Unterstützte Operationen:
– Bit-weise Verknüpfung der Quell-Register
– and, or, xor, nor
– R-Typ Befehle Quell-Register
and $s3,$s1,$s2 $s3 0100 0110 1010 0001 0000 0000 0000 0000
or $s4,$s1,$s2 $s4 1111 1111 1111 1111 1111 0000 1011 0111
xor $s5,$s1,$s2 $s5 1011 1001 0101 1110 1111 0000 1011 0111
nor $s6,$s1,$s2 $s6 0000 0000 0000 0000 0000 1111 0100 1000
●
and kann zur Maskierung benutzt werden
– Maske enthält 1 Bits an den gewünschten Stellen
– Unerwünschte Bits werden zu 0
●
or kann zum Kombinieren von Registern benutzt werden
– Bit-Gruppen in einem Register erzeugen
– Zu anderem Register hinzufügen
– Häufig zusammen mit Maskierung
●
MIPS hat kein not
– Kann durch nor mit $0 ersetzt werden
– nor $s1, $0, $s0 → $s1 = (0 or $s0) = $s0
●
and, or und xor auch mit Konstante möglich
→ andi, ori, xori (and immediate, or immediate, …)
●
Befehle dann I-Typ
– Konstante Teil des Befehlswortes
– Konstante nur 16 Bit breit (Keine Vorzeichenerweiterung → mit 0 auffüllen)
Quell-Register
andi $s2,$s1,0xFA34 $s2 0000 0000 0000 0000 0000 0000 0011 0100
ori $s3,$s1,0xFA34 $s3 0000 0000 0000 0000 1111 1010 1111 1111
xori $s4,$s1,0xFA34 $s4 0000 0000 0000 0000 1111 1010 1100 1011
●
Drei Typen von Schiebe-Operationen
– sll (shift left logical) , srl (shift right logical), sra (shift right arithmetic)
– R-Typ Befehl, aber rs wird ignoriert
– Schiebeweite steht in shamt
Quell-Register
shamt 0 0100
sll $t0, $s1, 4 $s2 0011 0000 0000 0000 0010 1010 1000 0000
srl $t0, $s1, 4 $s3 0000 1111 0011 0000 0000 0000 0010 1010
sra $t0, $s1, 4 $s4 1111 1111 0011 0000 0000 0000 0010 1010
●
Schieben mit variabler Bit-Anzahl möglich
– sllv, srlv, srav
– R-Typ Befehle
– rs legt Register fest, in dem die Schiebeweite steht
– shamt wird ignoriert Quell-Register
sllv $s3,$s1,$s2 $s3 0000 0100 0000 0010 1010 1000 0000 0000
srlv $s4,$s1,$s2 $s4 0000 0000 1111 0011 0000 0100 0000 0010
srav $s5,$s1,$s2 $s5 1111 1111 1111 0011 0000 0100 0000 0010
●
16 Bit K2 Konstante mithilfe des addi Befehls
High-Level Code Assembly Code
a = 0x4397; # a = $s0
addi $s0, $0, 0x4397
●
32 Bit Konstante mithilfe zweier Befehle
– lui → lädt obere 16 Bit des Zielregisters
– ori → fügt untere 16 hinzu
High-Level Code Assembly Code
a = 0xDEADBEEF; # a = $s0
lui $s0, 0xDEAD
ori $s0, $s0, 0xBEEF
●
Multiplikation von 32 Bit Zahlen kann 64 Bit Ergebnis liefern
●
Zwei Multiplikationsbefehle
– mult → op1 * op2, Ergebnis in Spezialregistern hi und lo
– hi und lo nicht Teil des Registersatzes
– mul → op1 * op2, untere 32 Bit des Ergebnisses in dest
●
Division
– Quotient und Rest interessant
– div → Berechnet op1/op2, Quotient nach lo, Rest nach hi
●
Zugriff auf hi und lo
– mflo, mfhi (move from hi/lo) → Transportiert hi/lo in Zielregister
© Christian Hochberger, TU Darmstadt Programmierung 45
Verzweigungen
●
Englisch: Branch
– Bedingt/Unbedingt
●
PC Behandlung
– Normalerweise: PC = PC + 4 nach jeder Anweisung
– Bei ausgeführtem Sprung: PC wird manipuliert
●
Programmstücke auslassen (überspringen)
●
Programmstücke wiederholen (Schleifen)
●
Bedingte Sprünge
– Werten Bedingung aus
– Bedingung erfüllt → Sprung ausführen
– Bedingung nicht erfüllt → PC = PC + 4
© Christian Hochberger, TU Darmstadt Programmierung 46
Bedingte Sprünge
●
Zwei Formen, beide I-Typ
– beq (branch equal) → Springt wenn Register gleich sind
– bne (branch not equal)→ Springt wenn Register ungleich sind
# MIPS Assembly Code
Sprungziel
addi $s0, $0, 4 # $s0 = 0 + 4 symbolisch angegeben
addi $s1, $0, 1 # $s1 = 0 + 1
sll $s1, $s1, 2 # $s1 = 1 << 2 = 4
beq $s0, $s1, target # $s0==$s1 → SprungCode
# MIPS Assembly durchführen
addi $s1, $s1, 1 # nicht ausgeführt
addi $s0, $0, 4 # $s0 = 0 + 4
sub $s1, $s1, $s0 # nicht ausgeführt
addi $s1, $0, 1 # $s1 = 0 + 1
sll $s1, $s1, 2 # $s1 = 1 << 2 = 4
target: bne $s0, $s1, target # $s0==$s1 → nicht springen
add $s1, $s1, $s0 # $s1 addi
= $s1$s1,
+ $s0
$s1, 1 # $s1 = $s1 + 1
Definition eines Labels, sub $s1, $s1, $s0 # $s1 = $s1 - $s0
kann als Sprungziel benutzt werden
target:
add $s1, $s1, $s0 # $s1 = $s1 + $s0
© Christian Hochberger, TU Darmstadt Programmierung 47
Bedingte Sprünge (2)
●
Labels
– Symbolische Marken im Assembler-Quelltext
– Werden beim Übersetzen mit einer Adresse belegt
(ergibt sich aus dem PC an dieser Stelle)
– Dürfen keine reservierten Worte sein
●
Konvention
– Code einrücken, Label nicht
– Label kann auch auf gleicher Zeile mit Befehl stehen
●
Unbedingte Sprünge
– Unterschied zum Branch → Absolute Adresse
●
Drei Typen
– j → Setze PC auf Adresse aus Befehl
– jal (jump and link)→ Setze PC auf Adresse und speichere Folgeadresse in R31
– jr → Setze PC auf Adresse aus Register
– j und jal nutzen J-Typ
– jr nutzt R-Typ (auch wenn nur ein Register gebraucht wird)
●
If-Anweisung
– Block von Anweisungen wird nur ausgeführt, wenn Bedingung erfüllt
●
Assembler
– Umgekehrte Bedingung prüfen
– Erfüllt: Über den Anweisungsblock hinweg springen
●
If/else Anweisung
– Nur einer der beiden Blöcke wird ausgeführt
●
Assembler
– Am Ende des then-Blocks hinter den else-Block springen
●
Wiederhole Schleifenkörper bis Bedingung nicht mehr erfüllt ist
●
Assembler
– Umgekehrte Bedingung prüfen, hinter Schleife springen
– Am Ende des Körpers → Zur Prüfung springen
High-Level Code Assembly Code
int pow=1; # $s0=pow, $s1=x
int x=0; addi $s0,$0,1
while (pow!=128) { addi $s1,$0,0
pow=pow*2; addi $t0,$0,128
x++; while:
} beq $s0,$t0,done
sll $s0,$s0,1
addi $s1,$s1,1
j while
done:
●
Ähnlich wie while Schleife
– Schleifenkörper wiederholen, bis Bedingung nicht mehr erfüllt
●
Allgemeine Struktur:
for (Initialisierung; Bedingung; Weiterschaltung) Anweisung
●
Zusätzlich
– Initialisierung → Einmal vor Betreten der Schleife
– Weiterschaltung → Am Ende des Körpers
●
Addiere die Zahlen von 0 - 9
High-Level Code Assembly Code
int sum=0; # $s0=i, $s1=sum
int i; addi $s1,$0,0
addi $t0,$0,10 # Für den Vergleich
●
beq und bne erlauben nur Prüfung auf Identität
●
In realen Programmen:
– Richtige Vergleiche erforderlich: <, ≤, >, ≥
– Neue Befehle erforderlich
●
slt (set less then)
– Setzt Zielregister auf 1, wenn op1 < op2
– Ansonsten Zielregister = 0
●
Andere Vergleiche nicht verfügbar
– > → Operanden vertauschen
– ≥ → Negation von < (auf Ergebnis 0 prüfen)
© Christian Hochberger, TU Darmstadt Programmierung 55
Größenvergleiche (2)
●
Beispiel:
– Summe aller Zweierpotenzen < 101 berechnen
High-Level Code Assembly Code
int sum=0; # $s0=i, $s1=sum
int i; addi $s1,$0,0
addi $t0,$0,101 # Für den Vergleich
●
Häufig benutzte Datenstruktur in Hochsprachen
– Index bestimmt, welches Element benutzt wird
– Anzahl der Elemente definiert Größe des Arrays Adresse Daten
0x10007010 array[4]
0x1000700C array[3]
array[0]=array[0]*8; lw $t0,0($s0)
sll $t0,$t0,3
sw $t0,0($s0)
array[1]=array[1]*8; lw $t0,4($s0)
sll $t0,$t0,3
sw $t0,4($s1)
© Christian Hochberger, TU Darmstadt Programmierung 57
Arrays (2)
●
Beispiel verwendet konstanten Index
●
In realen Anwendungen: Index aus Variable
High-Level Code Assembly Code
int i; # $s0=Basisadresse von array, $s1=i
int array[1000]; lui $s0,0x23BB
ori $s0,0xF000
addi $t2,$0,1000 # Für Vergleich
addi $s1,$0,0 # Initialisierung
for (i=0; i<1000; i++) { for:
array[i]=array[i]*8; slt $t0,$s1,$t2 Index skalieren
} beq $t0,$0,done (mit 4 multiplizieren)
sll $t0,$s1,2
add $t0,$s0,$t0 Effektive Adresse
lw $t1,0($t0) berechnen
sll $t1,$t1,3 Basis + Index(skaliert)
sw $t1,0($t0)
addi $s1,$s1,1 # Weiterschaltung
j for
done:
© Christian Hochberger, TU Darmstadt Programmierung 58
Byte-Arrays
●
Zugriff auf Arrays von Bytes
– lb (load byte) → Ein Byte aus Speicher holen, Vorzeichenerweiterung vornehmen
– lbu (load byte unsigned) → Ein Byte holen, mit 0 erweitern
– sb (store byte) → Ein Byte speichern
(Keine Unterscheidung von signed/unsigned nötig)
●
Byte häufig für Zeichen benutzt
– Kodierung: ASCII (veraltet), ISO-8859-1
– Manchmal auch Mehr-Byte Codes: UTF-8
●
Beispiel: Stringlänge berechnen
– In C: Letztes Zeichen eines String 0-Byte
●
Typische SW-Struktur, um Programmteile wieder zu verwenden
●
Meist in Form von Funktionen
– Übergabe von Parametern/Argumenten
– Rückgabe eines Ergebnisses
●
Hierarchien von Funktionen
– Aus einer Funktion f1 wird f2 aufgerufen
– Aus f2 wird f3 aufgerufen
– …
●
Nomenklatur:
– Aufrufer, Aufgerufener
© Christian Hochberger, TU Darmstadt Programmierung Funktionsaufrufe 61
MIPS: Aufrufkonvention
●
Übergabe von Parametern/Argumenten
– Aufrufer platziert bis zu 4 Parameter in $a0 - $a3
●
Rückgabe von Ergebnissen
– Aufgerufener platziert Ergebnis in $v0 und $v1
●
Konvention erlaubt,
– dass UPs benutzt werden können, ohne sie selbst geschrieben zu haben
– dass UPs in anderen Programmiersprachen geschrieben werden
●
Aufgerufener darf Aufrufer nicht stören
– Programm muss nach Ende von Aufgerufenem korrekt fortgesetzt werden
– Register des Aufrufers müssen erhalten bleiben
– Speicherbereiche des Aufrufers müssen erhalten bleiben
●
Rückkehr zum Aufrufer
– Aufrufer nutzt jal → Rücksprungadresse in $ra (R31)
– $ra darf von Aufgerufenem nicht zerstört werden
●
Register des Aufrufers
– $s0 - $s7 dürfen nicht verändert werden
●
Speicherbereiche
– Stack des Aufrufers muss erhalten bleiben
© Christian Hochberger, TU Darmstadt Programmierung Funktionsaufrufe 63
UPs: Minimalbeispiel
●
jal zum Aufruf von UPs
– Springt zum Aufgerufenen
– Rettet Rücksprungadresse (PC+4) in $ra Speichert
0x00400204 in $ra
●
jr zur Rückkehr zum Aufrufer
High-Level Code Assembly Code
int main() { 0x00400200 main: jal simple
simple(); 0x00400204 …
…
} Springt an Befehl
nach jal simple
●
Spezieller Speicherbereich
– Zum Retten von Registern
– Zur Übergabe von Parametern (wenn mehr als 4)
– Wächst und schrumpft dynamisch
●
Stack (allgemein)
– Last-in-first-Out Warteschlange
– Etwas zum Stack hinzufügen → push
– Etwas vom Stack wegnehmen → pop
– Funktionen können auf dem Stack Platz reservieren
(muss vor Rückkehr wieder freigegeben werden)
●
MIPS: Stack wächst nach unten!
● Spezielles Register $sp
– Stack Pointer
– Zeigt auf niedrigste benutzte Adresse
●
Zwei Worte auf den Stack legen:
– $sp = $sp-8
– 1. Wort adressieren: lw/sw 4($sp)
– 2. Wort adressieren: lw/sw 0($sp)
●
Platz wieder freigeben
– $sp = $sp+8
© Christian Hochberger, TU Darmstadt Programmierung Funktionsaufrufe 67
Stack-Nutzung
●
Hauptaufgabe
– Retten von Registern beim Betreten einer Funktion
– Restaurieren der Register am Ende der Funktion
●
Vorgehensweise
– Platz für alle zu rettenden Register auf dem Stack reservieren
– Register auf den Stack legen
– Funktion ausführen
– Register-Inhalte wieder vom Stack lesen
– Reservierten Platz wieder freigeben
Assembly Code
# $s0 = result Weniger Platz auf Stack reservieren
diffofsums:
addi $sp,$sp,-4
sw $s0,0($sp) Nur $s0 retten
add $t0,$a0,$a1
add $t1,$a2,$a3
sub $s0,$t0,$t1
add $v0,$0,$s0 $s0 restaurieren
lw $s0,0($sp)
addi $sp,$sp,4
jr $ra
●
Wenn Aufrufer ein nicht erhaltenes Register nach Aufruf noch braucht
– Vor Aufruf selber retten
– Nach Aufruf selber restaurieren
●
Aufteilung willkürlich (aber empirisch optimiert)
●
Einteilung von Funktionen
– Leaf Funktionen
●
Rufen keine weiteren Funktionen auf
●
Einfacher zu programmieren
– Non-Leaf Funktionen
●
Rufen andere Funktionen auf
● Müssen nicht erhaltene Register selber retten (z.B. $a0-$a3, $t0-$t9)
●
Rekursive Funktionen
– Non-Leaf Funktion, die sich selbst aufruft
– Beispiel: Rekursive Berechnung der Fakultät
●
fak(x) = x*fak(x-1)
●
Kein rekursiver Aufruf, wenn x ==1
© Christian Hochberger, TU Darmstadt Programmierung Funktionsaufrufe 73
Rekursive Funktionen – Beispiel
●
Bisher:
– Maximal 4 Argumente (mit 32 Bit)
– Maximal 8 lokale Variablen (mit 32 Bit)
●
Mehr als 4 Argumente
– Erste 4 Argumente in $a0-$a3
– Zusätzliche Argumente auf Stack (oberhalb von $sp)
– Vom Aufrufer gemanaged
●
Vor Aufruf Platz auf Stack reservieren
●
Nach Aufruf Platz auf Stack wieder freigeben
●
Mehr als 8 lokale Variable (z.B. lokale Arrays)
– Beim Reservieren des Platzes im Aufgerufenen berücksichtigen
© Christian Hochberger, TU Darmstadt Programmierung Funktionsaufrufe 76
Funktionen: Mehr Argumente und lokale Variablen (2)
Stack Frame
$ra (nur falls gebraucht)
$s0-$s7
(nur falls gebraucht)
Lokale Variablen
und Arrays
← $sp
●
Insgesamt 5 Adressierungsarten in MIPS
●
Drei kennen wir schon
– Register direkt
●
Registerinhalt wird direkt als Operand benutzt
– Immediate
●
Konstante im Befehl wird als Operand genutzt
– Register indirekt mit Offset (Base Addressing)
●
Adressierung von Operanden im Speicher
●
Lesen oder Schreiben
●
Berechnet aus Register und Offset (Konstante im Befehl)
●
Branch Befehle springen relativ zum PC
– Offset im Befehl wird vorzeichenerweitert
– Vorwärts- und Rückwärtssprünge möglich
●
Welcher Wert des PC wird zum Rechnen benutzt
– Intuitiv: PC des aktuellen Befehls
– Aus Mikroarchitektur-Gründen: PC+4
●
Sprungdistanz muss immer durch 4 teilbar sein
– Offset kann mit Faktor 4 skaliert werden
– Erhöht maximale Sprungdistanz
– (Nicht in allen RISC-Prozessoren so)
●
Kodierung des beq Befehls Assembly Code
0xA4 beq $t0,$0,else
– Adresse des Sprungziels: 0xB4 0xA8 addi $v0,$0,1
0xAC addi $sp,$sp,8
– PC des Befehls: 0xA4 0xB0 jr $ra
0xB4 else: addi $a0,$a0,-1
– PC+4: 0xA8 0xB8 jal fak
– Differenz: 0xB4-0xA8 = 0x0C
– Skalieren (durch 4 teilen)
●
Offset im Befehl = 0x0C/4 = 3
I-Typ
beq $t0,$0,else 4 8 0 3
●
Direkte Adressierung (absolute Adressierung)
– Vollständige Adresse Teil des Befehls
– j und jal könnten das nutzen
– Bei 32 Bit Adressen nicht möglich
●
Pseudo-Direct
– Befehl enthält 26 Adressbits
– Unterste zwei Adressbits nicht spezifiziert (weil immer 0)
– Fehlen immer noch Adressbits 31-28
– Werden aus dem aktuellen PC genommen
(Wieder: Aus Mikroarchitektur-Gründen PC+4)
●
Bisher nur kurze Programmsequenzen
●
Echte Programme
– Bestehen aus mehreren Quelldateien/Bibliotheken
– Müssen nach dem Übersetzen richtig in den Speicher geladen werden
●
Aufgaben
– Compilieren
– Assemblieren
– Linken
– Laden
●
Memory Map von MIPS Adresse Segment
– Fünf Bereiche 0xFFFFFFFC
– 2 Reservierte Bereiche Reserviert
0x80000000
– Text Segment (Programm)
0x7FFFFFFC Stack ← $sp=0x7FFFFFFC
– Global Data
Dynamic Data
– Dynamic Data Heap
0x10010000
●
Stack (wächst nach unten)
0x1000FFFC
●
Heap (wächst nach oben)
0x10000000
Global Data ← $gp=0x10008000
0x0FFFFFFC
Text
0x00400000 ← PC = 0x00400000
0x003FFFFC
Reserviert
0x00000000
© Christian Hochberger, TU Darmstadt Programmierung 83
Stadien eines Programms
●
Verschiedene Abstraktionsebenen des High-Level Code
Programms
Compiler
– High-Level Code
Assembly Code
C, C++, Java, …
Assembler
– Assembly Code
Object Files
symbolische Prozessoranweisungen Object File Library Files
– Object File Linker
Binär kodierte Anweisungen,
Executable
kein vollständiges Programm
Loader
– Executable
Vollständiges Programm Speicher
●
z.B. C → MIPS Assembler Assembly Code
.data
– Zusätzlich Assembler Direktiven f:
●
.data → Folgende Anweisungen betreffen globales Segment g:
y:
●
.text → Folgende Anweisungen betreffen Text-Segment
.text
main: addi $sp,$sp,-4
High-Level Code
sw $ra,0($sp)
int f,g,y;
addi $a0,$0,2
sw $a0,f
int main(void) {
addi $a1,$0,3
f=2;
sw $a1,g
g=3;
y=sum(f,g); jal sum
sw $v0,y
return y;
} lw $ra,0($sp)
addi $sp,$sp,4
int sum(int a, int b){ jr $ra
sum: add $v0,$a0,$a1
return (a+b);
jr $ra
}
© Christian Hochberger, TU Darmstadt Programmierung 85
Assemblieren
●
Verwandelt Assembler in Maschinencode
→ Object-File
●
Typischerweise in 2 Passes
– 1. Pass: Festlegen der Adressen (Befehle/Daten)
●
Beginn Code-Segment: 0x00400000
●
Tabelle mit Labels aufbauen
●
Bekannte Adressen eintragen
– 2. Pass: Erzeugen der Maschinenbefehle
●
Bekannte Sprungadressen eintragen
●
Unbekannte Adressen offen lassen (z.B. globale Daten)
●
Zugriff auf globale Daten
– lw/sw Befehl eintragen
●
Offset und Basisregister bleiben offen
© Christian Hochberger, TU Darmstadt Programmierung 86
Assemblieren (2)
●
Object-File
– Enthält auch Verwaltungsinformationen
Maschinen-Code
– Code-Größe, Daten-Größe 0x00400000 main: addi $sp,$sp,-4
– Symbol-Informationen 0x00400004 sw $ra,0($sp)
0x00400008 addi $a0,$0,2
0x0040000C sw $a0,f
Symbol Adresse 0x00400010 addi $a1,$0,3
0x00400014 sw $a1,g
f 0x10000000 0x00400018 jal sum
0x0040001C sw $v0,y
g 0x10000004 0x00400020 lw $ra,0($sp)
0x00400024 addi $sp,$sp,4
y 0x10000008
0x00400028 jr $ra
main 0x00400000 0x0040002C sum: add $v0,$a0,$a1
0x00400030 jr $ra
sum 0x0040002C
●
Zusammenfügen mehrerer Object-Files
– Aus Quell-Programmen
– Aus Bibliotheken
●
Relokation
– Assembler nimmt für alle Code-Segmente gleiche Startadresse an
– Code-Segmente müssen verschoben werden
●
Branch-Befehle bleiben unverändert!
●
Jump-Befehle müssen korrigiert werden (Adress-Verschiebung hinzu addieren)
●
Globale Daten anordnen
– Relativ zu $gp (liegt in der Mitte des reservierten Bereichs)
– Zugriffsbefehle patchen
© Christian Hochberger, TU Darmstadt Programmierung 88
Konstante 0x8000 → negativ!
Linken (2)
●
●
auf 32 Bit erweitern→ 0xFFFF8000
● + $gp=0x10008000
●
Effektive Adresse: 0x10000000
Datei-Header
●
Fertiges Programm Code-Größe: 52 Bytes
Daten-Größe: 12 Bytes
Code-Segment
0x00400000 0x23BDFFFC main: addi $sp,$sp,-4
0x00400004 0xAFBF0000 sw $ra,0($sp)
0x00400008 0x20040002 addi $a0,$0,2
0x0040000C 0xAF848000 sw $a0,0x8000($gp)
0x00400010 0x20050003 addi $a1,$0,3
0x00400014 0xAF858004 sw $a1,0x8004($gp)
0x00400018 0x0C10000B jal sum
0x0040001C 0xAF828008 sw $v0,0x8008($gp)
0x00400020 0x8FBF0000 lw $ra,0($sp)
0x00400024 0x23BD0004 addi $sp,$sp,4
0x00400028 0x03E00008 jr $ra
0x0040002C 0x00851020 sum: add $v0,$a0,$a1
0x00400030 0x03E00008 jr $ra
Daten-Segment
0x10000000 0 f
0x10000004 0 g
© Christian Hochberger, TU Darmstadt 0x10000008 0 Programmierung y 89
Laden
●
Mehrere Schritte
– Einlesen des Code-Segments in den Speicher ab Adresse 0x00400000
– $gp auf 0x10008000 setzen
– $sp auf 0x7FFFFFFC setzen
– jal an Startadresse (0x00400000)
●
In Wirklichkeit komplizierter
– Argumente an Programm übergeben?
– Datenbereiche vorinitialisieren?
– Debugging?
●
Abkürzungen für häufig auftretende Aufgaben
– Lange Konstante laden
li $s0,0x1234AA77 → lui $s0,0x1234
ori $s0,0xAA77
– Register löschen
clear $t0 → add $t0,$0,$0
– Register verschieben
move $s2,$s1 → add $s2,$0,$s1
– Nichts tun (No Operation)
nop → xor $0,$0,$0
– Verzweigung mit konstantem Vergleichswert
beq $t2,imm15:0,label → addi $at,$0,imm15:0
beq $t2,$at,label
–
© Christian Hochberger, TU Darmstadt Programmierung 91
Exceptions
●
Besondere Situationen, die Behandlung durch Prozessor erfordern
– Wie ein unaufgeforderter Funktionsaufruf
– Können durch HW oder SW ausgelöst werden
●
HW Exceptions
– Peripherie des Computers muss bedient werden
– Werden Interrupts genannt
●
SW Exceptions
– Durch Fehler im Programmablauf
(z.B. Division durch 0, unausgerichtete Adresse, …)
– Mit Absicht durch das Programm (Betriebssystem-Aufrufe)
– Werden Traps genannt
© Christian Hochberger, TU Darmstadt Programmierung 92
Exceptions in MIPS
●
Prozessor merkt sich
– PC bei dem die Exception auftrat
● Register EPC
– Ursache der Exception (Cause)
● Register Cause
●
Sprung zum Exception Handler
– Liegt an Adresse 0x80000180
– Wird durch das Betriebssystem definiert
– Handler liest Ursache aus
– Verzweigung je nach Behandlung
●
Basiert auf 8086 Prozessor von Intel aus 1978!
– Damals 16 Bit Prozessor
– Spätere Varianten 80286, 80386 (erstmals richtig 32 Bit)
– Pentium und Athlon
●
CISC Prozessor
– Primäres Ziel der Befehlssatzauswahl: Kompakte Programme
→ Viele verschiedene Befehle
→ Viele Adressierungsarten
→ Viele Datentypen
→ Viele Befehlsformate
●
Prinzipiell 8 Register zu 32 Bit:
EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI
●
Teilweise als 16 Bit oder 8 Bit Register ansprechbar
AX, CX, DX, BX, AH, AL, CH, CL, DH, DL, BH, BL
●
Beinahe alle Register universell
– Nicht alle Befehle können alle Register nutzen
– Bestimmte Befehle nutzen festgelegte Register
●
ESP immer Stack Pointer
●
Loop-Befehl nutzt immer ECX
– EIP → PC, nur indirekt manipulierbar
●
MIPS:
– Zwei Arten von Operanden: Register oder Immediate
– Jeder Befehl spezifiziert 3 Operanden
– Ausnahme Load/Store
●
IA32:
– Drei Arten von Operanden: Register, Immediate, Memory
– Jeder Befehl spezifiziert 2 Operanden!
– Erster Operand immer auch Ziel der Operation
→ Komplizierter zu programmieren
– Einzig Kombination Memory, Memory nicht erlaubt
●
Beispiele für Operanden-Kombinationen
Source/ Source Befehl Bedeutung
Destination
●
Memory als Operand
– Lange Zugriffszeit auf Operand!
– Evtl. sogar zwei Zugriffe auf Speicher in einem Befehl
© Christian Hochberger, TU Darmstadt IA32 - Das wahre Leben 98
IA32 – Operanden (3)
●
Befehle können mit 8, 16 oder 32 Bit arbeiten
(neuere Varianten auch mit 64 Bit)
●
Größe je nach Register-Name
●
Beispiele
– add AH,BL AH ← AH+BL 8 Bit
– add AX,-1 AX ← AX-1 16 Bit
– add EAX,EDX EAX ← EAX+EDX 32 Bit
●
Viel mehr Auswahl als bei MIPS
●
Beispiele (nicht vollständig)
8, 16 oder 32 Bit-Wert
– Displacement
add EAX,[20] EAX ← EAX+MEM[20]
– Base Addressing
add EAX,[ESP] EAX ← EAX+MEM[ESP]
– Base + Displacement
add EAX,[EDX+40] EAX ← EAX+MEM[EDX+40]
Skalierung um 1, 2, 4 oder 8
– Displacement + Scaled Index
add EAX,[60+EDI*4] EAX ← EAX+MEM[60+EDI*4]
– Base + Displacement + Scaled Index
add EAX,[EDX+80+EDI*2] EAX ← EAX+MEM[EDX+80+EDI*2]
© Christian Hochberger, TU Darmstadt IA32 - Das wahre Leben 100
IA32 – Status Flags
●
Bedingungen werden in IA32 besonders behandelt
– Speicherung in Flags
– Beispiele für Flags
●
CF → Carry Flag (Carry Out der letzten Rechnung, kann mit addiert werden)
●
ZF → Zero Flag (Letztes Ergebnis war 0)
●
SF → Sign Flag (Letztes Ergebnis war negativ)
●
OF → Overflow Flag (K2-Zahl übergelaufen)
– Flags werden von Branches ausgewertet
●
Flags gehören zum Maschinen-Status
●
ADD,SUB → Normale Addition/Subtraktion
●
ADDC → Addiert CF mit hinzu
●
INC/DEC → ± 1
●
BT → Bit Test, CF=D[S] (Bit s von D)
●
BTR/BTS → Bit Test + Set/Reset
●
MOV → D=S
●
Loop → Dekrementiert ECX
ECX ≠ 0 → Sprung
●
PUSH/POP→ Benutzt automatisch ESP
●
CALL → ESP=ESP-4, MEM[ESP]=EIP, EIP=S
●
RET → EIP=MEM[ESP], ESP=ESP+4
© Christian Hochberger, TU Darmstadt IA32 - Das wahre Leben 102
IA32 – Befehlskodierung
●
Befehle können zwischen 1 und 15 Byte lang sein
●
Allgemeines Schema
Prefixe Opcode ModR/M SIB Displacement Immediate
Bis zu 4 Prefixe 1,2 oder 3 Byte 1,2 oder 4 Byte 1,2 oder 4 Byte
Jeweils 1 Byte
Reg/
Mod R/M Scale Index Base
Opcode
2 Bits 3 Bits 3 Bits 2 Bits 3 Bits 3 Bits
●
Im Prinzip 17 Byte möglich, aber nicht zulässig
●
Prefix: Operanden-Größe, Adress-Größe, Wiederholung, Speicher-Lock