Zahlen

  • Hexadezimal: 0xABC8, 4ABCh, 0ABC8h (ABC8h ist Falsch)
  • Oktal: 0o712
  • Binär: 0b101101011

Variablen

<Name> <Größe> <Initialwert>
  • Werden in der section .data definiert

Strings

; Null-terminated string
; DB define byte - 1 char = 1 byte
myString DB 'Hello, NASM!', 0

Variablengrößen

DBDWDDDQ
Define ByteDefine WordDefine Double wordDefine quad word
1 Byte2 Byte4 Byte8 Byte
8 Bit16 Bit32 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], edx

Funktionen

MOV

MOV <Ziel>, <Quelle>
  • Setzt den Wert von Ziel auf Quelle
mov eax, 12
mov eax, [VARIABLE]
mov [VARIABLE], eax
mov eax, 15
mov [VARIABLE], 15

ADD

ADD <Ziel>, <Quelle>
  • Setzt Ziel auf Ziel + Quelle
add eax, ebx
add eax, 1
add [VARIABLE], 1

MUL

MUL <Register>
  • EAX wird mit dem Wert aus Register multipliziert
  • Der niederwertigere Teil wird in EAX gespeichert
  • Der höherwertigere Teil wird in EDX gespeichert

DIV

DIV <Register>
  • Die Zusammengesetzte Zahl aus EDX (höherwertigerer Teil) und EAX (niederwertigerer Teil) wird durch den Wert aus Register geteilt
  • Das Ergebnis wird in EAX gespeichert
  • Der Rest wird in EDX gespeichert
  • Bei Division durch 0 oder wenn das Ergebnis nicht in EAX passt, 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], eax

INC

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

  • Basepointer EBP zeigt auf Anfang vom Stack
  • Stackpointer ESP zeigt 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>
  • Wert wird auf den Stack geschrieben
  • ESP verringert sich

POP

POP <Ziel>
  • Oberster Wert aus dem Stack wird in Ziel geschrieben

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

  • PUSHF schreibt die Flags auf den Stack
  • Mit z.B. POP ax lassen sich die Flags in ein Register schreiben
  • Darauf kann man dann z.B. mit logischen Operatoren diese Ändern
  • Am Schluss wieder mit PUSH ax und POPF die 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 = srcdst < srcdst > src
Zero Flag100
Carry Flag010

Andere Arithmetische und Logische Operatoren

  • Setzen Flags auch, ändern aber auch den Wert im Register

Sprungbefehle

jmp Stelle
 
Stelle:
  • JMP - Unbedingt
  • JO - Overflow (OF = 1)
  • JS - Vorzeichen (SF = 1)
  • JE - Gleich (ZF = 1)
  • JL, JG - Kleiner, Größer
  • JLE, 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 CX um 1
    • Bei 0 weiterführung, sonst Sprung zu Schleife

Unterprogramme (Funktionen)

<Funktionsname>:
    <Statements>
    ret

Aufruf:

CALL <Funktionsname>

Stackframe

Funktion:
    push ebp
    mov ebp, esp
    ...
    mov esp, ebp
    pop ebp
    ret

Printf

  • 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], eax

Erweiterungen

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