Zahlen
- Hexadezimal:
0xABC8,4ABCh,0ABC8h(ABC8hist Falsch) - Oktal:
0o712 - Binär:
0b101101011
Variablen
<Name> <Größe> <Initialwert>- Werden in der
section .datadefiniert
Strings
; Null-terminated string
; DB define byte - 1 char = 1 byte
myString DB 'Hello, NASM!', 0Variablengrößen
| DB | DW | DD | DQ |
|---|---|---|---|
| Define Byte | Define Word | Define Double word | Define quad word |
| 1 Byte | 2 Byte | 4 Byte | 8 Byte |
| 8 Bit | 16 Bit | 32 Bit (Standard-Registergröße) | 64 Bit |
Big und Little Endian
- Big Endian ist die “normale” Speicherweise, bei der der höherwertigere Teil zuerst gespeichert wird
- Little Endian speichert den höherwertigeren Teil zuletzt
Speichere Zahl: 0A0B0C0D
Adresse 0 1 2 3
-------------------------------
Big Endian: 0A 0B 0C 0D
Little Endian: 0D 0C 0B 0A
Größenangaben
- Bei unbekannten Größen muss diese vor dem Befehl angegeben werden:
// ERG ist in diesem Fall ein Quadword
mov DWORD [ERG+4], edxFunktionen
MOV
MOV <Ziel>, <Quelle>- Setzt den Wert von
ZielaufQuelle
mov eax, 12
mov eax, [VARIABLE]
mov [VARIABLE], eax
mov eax, 15
mov [VARIABLE], 15ADD
ADD <Ziel>, <Quelle>- Setzt
ZielaufZiel+Quelle
add eax, ebx
add eax, 1
add [VARIABLE], 1MUL
MUL <Register>EAXwird mit dem Wert ausRegistermultipliziert- Der niederwertigere Teil wird in
EAXgespeichert - Der höherwertigere Teil wird in
EDXgespeichert
DIV
DIV <Register>- Die Zusammengesetzte Zahl aus
EDX(höherwertigerer Teil) undEAX(niederwertigerer Teil) wird durch den Wert ausRegistergeteilt - Das Ergebnis wird in
EAXgespeichert - Der Rest wird in
EDXgespeichert - Bei Division durch 0 oder wenn das Ergebnis nicht in
EAXpasst, stürzt das Programm ab - Um Quad-Words zu teilen, kann folgender Hack verwendet werden:
; ERG ist ein Quad-Word.
; Divisor
mov ebx, [DIVISOR]
; Rest-Register auf 0
xor edx, edx
; Höherwertigen Teil dividieren
mov DWORD eax, [ERG+4]
div ebx
; Ins Ergebnis speichern
mov DWORD [ERG+4], eax
; Niederwertigen Teil (jetzt mit Rest in edx) dividieren
mov DWORD eax, [ERG]
div ebx
; Ins Ergebnis schreiben
add DWORD [ERG+4], edx
mov DWORD [ERG], eaxINC
INC <Adresse/Register>SHL
SHL <Register>, <Anzahl>Logische Operatoren
AND/OR/XOR/NOT/NEG <Register>, <Quelle>Wichtig
- Bei Funktionen, die zwei Werte nehmen, kann maximal einer dieser Werte eine Variable sein. Wenn der eine eine Variable ist, muss der andere ein Register oder eine Konstante sein
Stack
Siehe auch
- Basepointer
EBPzeigt auf Anfang vom Stack - Stackpointer
ESPzeigt auf Ende vom Stack- Werden Elemente auf den Stack geschrieben, verringert sich dieser
- Wird der Platz wieder frei gemacht, erhöht er sich
ESP=EBP=> Stack ist leer
PUSH
PUSH <Wert>Wertwird auf den Stack geschriebenESPverringert sich
POP
POP <Ziel>- Oberster Wert aus dem Stack wird in
Zielgeschrieben
Makros
- Codestellen, die dann vom Compiler ersetzt werden
- Code wird dadurch länger
- Es ist aber effizienter, da keine Funktionen aufgerufen werden sondern der Code direkt in line ausgeführt wird
- Beispiel:
%include "io.inc"
Flags
- Trap Flag - Wenn man debuggen will
- Overflow Flag - Wenn kein Überlauf stattfindet aber das Sign sich ändert
- Carry Flag - Wenn ein Überlauf stattfindet
- Sign Flag - Wenn Ergebnis von Add/Sub negativ ist
- Zero Flag - Wenn ein Ergebnis 0 ist
- Auxiliary Carry - Wenn Überlauf der ersten 4 Bits stattfindet
- Parity Flag - Wenn das Ergebnis eine gerade Einsenzahl hat
Flags verändern
PUSHFschreibt die Flags auf den Stack- Mit z.B.
POP axlassen sich die Flags in ein Register schreiben - Darauf kann man dann z.B. mit logischen Operatoren diese Ändern
- Am Schluss wieder mit
PUSH axundPOPFdie Flags zurückschreiben
Vergleiche
CMP und TEST
- CMP zieht dst von src ab
- TEST verbindet dst und src durch eine bitweise AND-Operation
CMP/TEST <dst>, <src>
| dst = src | dst < src | dst > src | |
|---|---|---|---|
| Zero Flag | 1 | 0 | 0 |
| Carry Flag | 0 | 1 | 0 |
Andere Arithmetische und Logische Operatoren
- Setzen Flags auch, ändern aber auch den Wert im Register
Sprungbefehle
jmp Stelle
Stelle:JMP- UnbedingtJO- Overflow (OF = 1)JS- Vorzeichen (SF = 1)JE- Gleich (ZF = 1)JL,JG- Kleiner, GrößerJLE,JGE- Kleiner oder Gleich, Größer oder Gleich
LOOP
MOV CX, 100
Schleife:
LOOP Schleife- Obiges Beispiel führt den Code nach Schleife 100 mal aus
- Beim Aufruf von
LOOP:- Verringerung
CXum1 - Bei
0weiterführung, sonst Sprung zuSchleife
- Verringerung
Unterprogramme (Funktionen)
<Funktionsname>:
<Statements>
retAufruf:
CALL <Funktionsname>Stackframe
Funktion:
push ebp
mov ebp, esp
...
mov esp, ebp
pop ebp
retPrintf
- Argumente werden erst in umgekehrter Reihenfolge auf den Stack gepusht
- Dann der Formatstring
- Dann
call printf
Assembler in C
ASM
asm(code : outputs : inputs : clobbers);Carry
32 Bit kennt kein Quad word. Bei Addition zu einem Quad word muss auf das Carry flag geprüft werden und dann ggf auf den oberen Teil vom Quadword 1 addiert werden:
section .data
PROD DQ 0xFFFFFFFF
section .text
global main
main:
mov eax, [PROD]
add eax, 0x1
jnc nocarry
add DWORD [PROD+4], 1
nocarry:
mov [PROD], eaxErweiterungen
SIMD
- MMX (Multi Media Extensions)
- SSE (Streaming SIMD Extensions)
- AVX (Advanced Vector Extensions)
- Verarbeitung von 256-Bit und 512-Bit-Werten
- Eigener Befehlssatz, Datentypen und Register
CPUID
- Gibt Daten über die CPU aus
x86-Betriebsmodi
- 16-Bit (Real Mode)
- Kein Speicherschutz, Interrupt-Vector-Table, BIOS-Interrupts
- 32-Bit (Protected Mode)
- Speicherschutz, Interrupt-Descriptor-Table, Keine BIOS-Interrupts
- 64-Bit (Long Mode)
BIOS-Boot
- Master Boot Record (Erste 512 Bytes) werden geprüft
- Müssen mit 0xaa55 enden
- MBR wird an Stelle 0x7c00 in RAM geladen
- Springe zu 0x7c00
Interrupts
- Interrupt-Flag wird deaktiviert
- Register werden auf Stack gespeichert
- Interrupt-Routine wird nachgeschlagen und ausgeführt
- Am Schluss wird das Flag wieder gesetzt
- Und die Register vom Stack geholt
- Das Programm wird fortgesetzt
Interrupt Vector Table
- Liegt am Anfang des RAM
- Wird beim Booten vom BIOS gefüllt
- EInträge verweisen auf Interrupt-Routinen
CLI / STI
CLI/STI- Setzt bzw. Cleart das Interrrupt Flag