Microprocesoare
Curs de Microprocesoare¶
Cu exemple de cod ARM și x86 Assembly¶
Cuprins¶
- Introducere în microprocesoare
- Arhitectura von Neumann și Harvard
- Structura internă a unui microprocesor
- Setul de instrucțiuni (ISA)
- Moduri de adresare
- Arhitectura x86 — registre și organizare
- Programare în Assembly x86
- Arhitectura ARM — registre și organizare
- Programare în Assembly ARM
- Sistemul de memorie și ierarhia cache
- Pipeline și tehnici de execuție
- Sistemul de întreruperi
- Interfațarea cu perifericele (I/O)
- Exemple comparative ARM vs. x86
- Arhitecturi moderne și tendințe
1. Introducere în microprocesoare¶
1.1 Definiție și rol¶
Microprocesorul este un circuit integrat care implementează funcțiile unei unități centrale de procesare (CPU) pe un singur chip de siliciu. El preia instrucțiuni din memorie, le decodifică și le execută, operând asupra datelor conform unui program stocat.
Ciclul fundamental de funcționare este Fetch → Decode → Execute:
- Fetch: microprocesorul citește instrucțiunea de la adresa indicată de Program Counter (PC)
- Decode: unitatea de decodificare interpretează codul instrucțiunii și determină operația, operanzii și modul de adresare
- Execute: unitatea de execuție (ALU, unitate de deplasare, unitate de acces la memorie) realizează operația
1.2 Scurt istoric¶
| An | Procesor | Biți | Frecvență | Tranzistoare | Notabil |
|---|---|---|---|---|---|
| 1971 | Intel 4004 | 4 | 740 kHz | 2.300 | Primul microprocesor comercial |
| 1974 | Intel 8080 | 8 | 2 MHz | 4.500 | Baza pentru CP/M, Altair 8800 |
| 1978 | Intel 8086 | 16 | 5–10 MHz | 29.000 | Fondatorul arhitecturii x86 |
| 1985 | Intel 80386 | 32 | 16–33 MHz | 275.000 | Primul x86 pe 32 biți |
| 1993 | Intel Pentium | 32 | 60–66 MHz | 3.1M | Pipeline superscalar dual |
| 2003 | AMD Opteron | 64 | 1.4–2.4 GHz | 105M | Primul x86-64 (AMD64) |
| 1985 | ARM1 | 32 | 8 MHz | 25.000 | Primul procesor ARM |
| 2012 | ARM Cortex-A15 | 32 | 2.5 GHz | ~2B | Smartphone high-end |
| 2020 | Apple M1 | 64 | 3.2 GHz | 16B | ARM pe desktop/laptop |
1.3 RISC vs. CISC¶
| Caracteristică | CISC (x86) | RISC (ARM) |
|---|---|---|
| Filosofie | Instrucțiuni complexe, variabile | Instrucțiuni simple, lungime fixă |
| Lungime instrucțiune | Variabilă (1–15 bytes x86) | Fixă (4 bytes ARM, 2 bytes Thumb) |
| Operații cu memorie | Orice instrucțiune poate accesa memoria | Doar LOAD/STORE accesează memoria |
| Nr. registre | Puține (8 GPR pe x86-32) | Multe (16+ GPR) |
| Decodificare | Complexă (micro-ops) | Simplă, rapidă |
| Pipeline | Complex, multe etaje | Simplu, eficient |
| Consum de putere | Mai mare | Mai mic |
Astăzi granița s-a estompat: procesoarele x86 moderne decodifică instrucțiunile CISC în micro-operații RISC interne, iar ARM a adăugat instrucțiuni mai complexe.
2. Arhitectura von Neumann și Harvard¶
2.1 Arhitectura von Neumann¶
Program și date partajează aceeași memorie și aceeași magistrală.
┌──────────┐ Magistrală unică ┌──────────┐
│ │◄────────────────────────► │ │
│ CPU │ Adrese + Date + Control │ Memorie │
│ │ │ (cod+date)|
└──────────┘ └──────────┘
Avantaje: simplitate, flexibilitate (codul poate fi tratat ca date și invers).
Dezavantaj: bottleneck-ul von Neumann — CPU-ul nu poate citi simultan instrucțiunea și datele.
Exemplu: arhitectura x86 este von Neumann la nivel logic (spațiu de adresare unificat).
2.2 Arhitectura Harvard¶
Memorii și magistrale separate pentru instrucțiuni și date.
┌──────────┐ Magistrală instrucțiuni ┌────────────┐
│ │◄──────────────────────────► │ Memorie │
│ CPU │ │ instrucț. │
│ │ Magistrală date ├────────────┤
│ │◄──────────────────────────► │ Memorie │
│ │ │ date │
└──────────┘ └────────────┘
Avantaj: acces simultan la cod și date — debit dublu.
Dezavantaj: complexitate hardware, memorii dedicate.
Exemplu: ARM Cortex-M (Harvard pur), procesoarele moderne x86 (Harvard modificat — cache-uri separate L1 pentru instrucțiuni și date, dar memorie principală unificată).
2.3 Magistrala de sistem¶
Magistrala de sistem (system bus) constă din trei submagistrele:
- Magistrala de adrese: unidirecțională (CPU → memorie/periferice), determină locația accesată. Lățime = n biți → spațiu de adresare = 2ⁿ bytes.
- Magistrala de date: bidirecțională, transportă datele citite sau scrise. Lățime tipică: 8, 16, 32, 64 biți.
- Magistrala de control: semnale de coordonare (RD, WR, MREQ, IORQ, READY, WAIT, IRQ, DMA etc.).
3. Structura internă a unui microprocesor¶
3.1 Diagrama bloc generală¶
┌─────────────────────────────────────────────────┐
│ MICROPROCESOR │
│ │
│ ┌──────────────┐ ┌───────────────────────┐ │
│ │ Unitate de │ │ Fișier de registre │ │
│ │ Control (CU)│ │ (Register File) │ │
│ │ │ │ R0, R1, ... Rn │ │
│ │ - Decodif. │ │ SP, LR, PC │ │
│ │ - Secvențor │ │ Flags/Status │ │
│ └──────┬───────┘ └───────┬───────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────────────────────────────────┐ │
│ │ ALU + Shifter + MUL │ │
│ │ (Unitatea aritmetică-logică) │ │
│ └──────────────────────────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌───────────────────┐ │
│ │ Interfață │ │ Interfață I/O │ │
│ │ memorie │ │ │ │
│ └──────┬───────┘ └───────┬───────────┘ │
└─────────┼────────────────────┼───────────────────┘
▼ ▼
Magistrala de Magistrala I/O
memorie
3.2 Unitatea de control (CU)¶
Generează secvența de semnale de control care coordonează toate componentele procesorului. Implementarea poate fi:
- Cablată (hardwired): logică combinațională rapidă, greu de modificat. Tipică pentru RISC (ARM).
- Microprogramată: fiecare instrucțiune este executată de o secvență de micro-instrucțiuni stocate în ROM intern. Flexibilă, tipică pentru CISC (x86).
3.3 ALU (Arithmetic Logic Unit)¶
Realizează operații aritmetice (adunare, scădere, înmulțire) și logice (AND, OR, XOR, NOT, deplasări). Generează flag-uri de stare:
| Flag | Nume | Semnificație |
|---|---|---|
| Z | Zero | Rezultatul este zero |
| N/S | Negative/Sign | Bitul MSB al rezultatului (1 = negativ) |
| C | Carry | Transport din/în bitul MSB (operații fără semn) |
| V/O | Overflow | Depășire aritmetică (operații cu semn) |
3.4 Fișierul de registre¶
Registrele sunt cele mai rapide locații de stocare, integrate direct în CPU. Tipuri:
- Registre de uz general (GPR): stochează operanzi și rezultate
- Program Counter (PC / IP): adresa instrucțiunii curente sau următoare
- Stack Pointer (SP): vârful stivei
- Link Register (LR): adresa de retur (ARM)
- Registrul de stare (FLAGS / CPSR): flag-urile ALU și biți de control
3.5 Ciclul instrucțiunii — detaliat¶
1. FETCH: MAR ← PC; IR ← Memorie[MAR]; PC ← PC + dim_instr
2. DECODE: CU decodifică IR, identifică operația și operanzii
3. EXECUTE: ALU execută operația
4. MEMORY: (dacă e cazul) citire/scriere memorie
5. WRITEBACK: rezultatul se scrie în registrul destinație
4. Setul de instrucțiuni (ISA)¶
4.1 Clasificarea instrucțiunilor¶
Instrucțiuni de transfer de date:
; x86 ; ARM
mov eax, ebx MOV R0, R1
mov eax, [ebx] LDR R0, [R1]
mov [ebx], eax STR R0, [R1]
push eax PUSH {R0}
pop eax POP {R0}
Instrucțiuni aritmetice:
; x86 ; ARM
add eax, ebx ADD R0, R1, R2 ; R0 = R1 + R2
sub eax, 5 SUB R0, R0, #5
imul eax, ebx MUL R0, R1, R2
inc ecx ADD R3, R3, #1
neg eax RSB R0, R0, #0 ; R0 = 0 - R0
Instrucțiuni logice și pe biți:
; x86 ; ARM
and eax, 0xFF AND R0, R0, #0xFF
or eax, ebx ORR R0, R0, R1
xor eax, eax EOR R0, R0, R0
not eax MVN R0, R0
shl eax, 3 LSL R0, R0, #3
shr eax, 1 LSR R0, R0, #1
Instrucțiuni de comparație și test:
; x86 ; ARM
cmp eax, ebx CMP R0, R1
test eax, 0x01 TST R0, #0x01
Instrucțiuni de salt (branșament):
; x86 ; ARM
jmp eticheta B eticheta
je eticheta BEQ eticheta
jne eticheta BNE eticheta
jg eticheta BGT eticheta
jl eticheta BLT eticheta
call functie BL functie
ret BX LR
Instrucțiuni de control al procesorului:
; x86 ; ARM
nop NOP
hlt WFI ; Wait For Interrupt
cli CPSID i ; dezactivare întreruperi
sti CPSIE i ; activare întreruperi
4.2 Codificarea instrucțiunilor¶
x86 (lungime variabilă, 1–15 bytes):
┌────────┬────────┬─────────┬──────────┬──────────────┐
│ Prefix │ Opcode │ ModR/M │ SIB │ Displacement │ Immediate │
│ 0-4 B │ 1-3 B │ 0-1 B │ 0-1 B │ 0/1/2/4 B │ 0/1/2/4 B│
└────────┴────────┴─────────┴──────────┴──────────────┘
ARM (lungime fixă, 4 bytes / 32 biți):
Instrucțiune de procesare date:
┌──────┬───┬────────┬───┬──────┬──────┬──────────────┐
│ Cond │ 00│ Opcode │ S │ Rn │ Rd │ Operand2 │
│ 4 biți│2b│ 4 biți │1b │ 4 b │ 4 b │ 12 biți │
└──────┴───┴────────┴───┴──────┴──────┴──────────────┘
31-28 27-26 24-21 20 19-16 15-12 11-0
Lungimea fixă ARM simplifică dramatic decodificarea și pipeline-ul.
4.3 Condiții de execuție (ARM — unică printre ISA-uri)¶
Fiecare instrucțiune ARM poate fi condiționată cu un sufix de 4 biți:
| Sufix | Condiție | Flag-uri testate |
|---|---|---|
| EQ | Equal | Z = 1 |
| NE | Not Equal | Z = 0 |
| CS/HS | Carry Set / ≥ unsigned | C = 1 |
| CC/LO | Carry Clear / < unsigned | C = 0 |
| MI | Minus (negativ) | N = 1 |
| PL | Plus (pozitiv/zero) | N = 0 |
| VS | Overflow Set | V = 1 |
| VC | Overflow Clear | V = 0 |
| HI | Higher (unsigned) | C=1 și Z=0 |
| LS | Lower or Same | C=0 sau Z=1 |
| GE | Greater or Equal (signed) | N = V |
| LT | Less Than (signed) | N ≠ V |
| GT | Greater Than (signed) | Z=0 și N=V |
| LE | Less or Equal (signed) | Z=1 sau N≠V |
| AL | Always (implicit) | — |
Exemplu — eliminarea unui branch:
; Clasic (cu branch): ; Cu execuție condiționată:
CMP R0, #0 CMP R0, #0
BEQ skip ADDNE R1, R1, R0
ADD R1, R1, R0
skip:
5. Moduri de adresare¶
5.1 Modurile fundamentale¶
Adresare imediată — operandul este o constantă inclusă în instrucțiune:
; x86 ; ARM
mov eax, 42 MOV R0, #42
add ecx, 10 ADD R1, R1, #10
Adresare prin registru — operandul este în registru:
; x86 ; ARM
mov eax, ebx MOV R0, R1
add eax, ecx ADD R0, R1, R2
Adresare directă (absolută) — adresa de memorie este specificată explicit:
; x86
mov eax, [0x1000] ; citește de la adresa 0x1000
mov [variabila], eax ; scrie la adresa variabilei
Adresare indirectă prin registru — registrul conține adresa:
; x86 ; ARM
mov eax, [ebx] LDR R0, [R1] ; R0 = Mem[R1]
mov [ebx], eax STR R0, [R1] ; Mem[R1] = R0
Adresare cu deplasament (bază + offset):
; x86 ; ARM
mov eax, [ebx + 8] LDR R0, [R1, #8] ; R0 = Mem[R1+8]
mov eax, [ebp - 4] LDR R0, [R11, #-4] ; variabilă locală
Adresare indexată (bază + index × scală):
; x86 (puternic — SIB encoding)
mov eax, [ebx + ecx*4] ; acces array: baza + index * sizeof(int)
mov eax, [ebx + ecx*4 + 12] ; bază + index*scală + deplasament
; ARM (registru + registru deplasat)
LDR R0, [R1, R2, LSL #2] ; R0 = Mem[R1 + R2*4]
Adresare relativă la PC:
; x86 (în modul 64-bit, RIP-relative)
mov eax, [rip + offset] ; acces date relative la instrucțiunea curentă
; ARM
LDR R0, [PC, #offset] ; citire constantă din pool-ul de literale
ADR R0, eticheta ; încarcă adresa unei etichete
Adresare cu pre/post-incrementare (ARM):
; Pre-indexare cu writeback:
LDR R0, [R1, #4]! ; R1 = R1 + 4, apoi R0 = Mem[R1]
; Post-indexare:
LDR R0, [R1], #4 ; R0 = Mem[R1], apoi R1 = R1 + 4
; Echivalent x86 (nu există nativ, se simulează):
; mov eax, [ebx]
; add ebx, 4
5.2 Moduri de adresare pentru stivă¶
x86 — operații explicite cu stiva:
push eax ; ESP = ESP - 4; Mem[ESP] = EAX
pop ebx ; EBX = Mem[ESP]; ESP = ESP + 4
ARM — stiva se gestionează manual:
STR R0, [SP, #-4]! ; push R0 (pre-decrement)
LDR R0, [SP], #4 ; pop R0 (post-increment)
; Sau cu instrucțiuni multiple:
PUSH {R0-R3, LR} ; salvează mai multe registre simultan
POP {R0-R3, PC} ; restaurează + return (PC ← LR salvat)
6. Arhitectura x86 — registre și organizare¶
6.1 Registrele x86-32 (IA-32)¶
Registre de uz general (32 biți):
┌─────────────────────────────────┐
│ EAX │ Accumulator (rezultate aritmetice, return value)
├────────────────┬────────────────┤
│ │ AX │
│ ├────────┬───────┤
│ │ AH │ AL │
└────────────────┴────────┴───────┘
31 15 7 0
EAX — Accumulator ECX — Counter (bucle, shifts)
EBX — Base (pointer) EDX — Data (extensie EAX, I/O)
ESI — Source Index EDI — Destination Index
EBP — Base Pointer (frame pointer)
ESP — Stack Pointer
Registre de segment (16 biți):
CS (Code), DS (Data), SS (Stack), ES/FS/GS (Extra). În modul protejat, sunt selectori care indexează tabela de descriptori.
Registre speciale:
EIP — Instruction Pointer (adresa instrucțiunii curente)
EFLAGS — registrul de stare și control:
Bit 0: CF (Carry Flag)
Bit 2: PF (Parity Flag)
Bit 6: ZF (Zero Flag)
Bit 7: SF (Sign Flag)
Bit 11: OF (Overflow Flag)
Bit 9: IF (Interrupt Flag)
Bit 10: DF (Direction Flag)
6.2 Extensia la 64 biți (x86-64 / AMD64)¶
Registrele se extind la 64 biți (prefix R): RAX, RBX, RCX, RDX, RSI, RDI, RSP, RBP.
Se adaugă 8 registre noi: R8–R15 (accesibile și ca R8D, R8W, R8B pentru 32/16/8 biți).
RIP devine accesibil (RIP-relative addressing).
┌───────────────────────────────────────────────────┐
│ RAX (64 biți) │
├───────────────────────┬───────────────────────────┤
│ │ EAX (32 biți) │
│ ├─────────────┬─────────────┤
│ │ │ AX (16b) │
│ │ ├──────┬──────┤
│ │ │ AH │ AL │
└───────────────────────┴─────────────┴──────┴──────┘
63 31 15 7 0
6.3 Organizarea memoriei x86¶
Modelul de memorie plată (flat model): în modul protejat modern, toate segmentele acoperă întregul spațiu de adresare (4 GB pe 32 biți, 256 TB virtual pe 64 biți). Paginarea (4 KB per pagină) gestionează memoria virtuală.
Convenția de stivă x86: stiva crește în jos (spre adrese mai mici). ESP/RSP indică elementul din vârful stivei.
Adrese mari ┌──────────────┐
│ .text │ Cod (instrucțiuni)
├──────────────┤
│ .data │ Date inițializate
├──────────────┤
│ .bss │ Date neinițializate
├──────────────┤
│ │
│ Heap ↓ │ Alocare dinamică (malloc)
│ │
│ │
│ Stack ↑ │ Variabile locale, cadre
├──────────────┤
│ Env/Args │
Adrese mici └──────────────┘
6.4 Cadrul de stivă (Stack Frame) x86¶
; Prologul funcției:
push ebp ; salvează EBP-ul vechi
mov ebp, esp ; EBP = cadrul curent
sub esp, 16 ; alocă 16 bytes pentru variabile locale
; Corpul funcției:
mov [ebp-4], eax ; variabila locală 1
mov eax, [ebp+8] ; primul argument (transmis pe stivă)
mov ebx, [ebp+12] ; al doilea argument
; Epilogul:
mov esp, ebp ; dealocare variabile locale
pop ebp ; restaurare EBP
ret ; return (pop EIP)
Adrese mari
┌─────────────────┐
│ Arg 2 │ [EBP+12]
├─────────────────┤
│ Arg 1 │ [EBP+8]
├─────────────────┤
│ Return Addr │ [EBP+4] (pus de CALL)
├─────────────────┤
│ EBP vechi │ [EBP] (pus de PUSH EBP)
├─────────────────┤
│ Local var 1 │ [EBP-4]
├─────────────────┤
│ Local var 2 │ [EBP-8]
├─────────────────┤ ← ESP
Adrese mici
7. Programare în Assembly x86¶
7.1 Structura unui program NASM (Linux, 32-bit)¶
; hello.asm — Hello World, Linux x86-32, NASM syntax
section .data
mesaj db "Hello, World!", 10 ; 10 = '\n'
len equ $ - mesaj ; lungimea mesajului
section .bss
buffer resb 64 ; 64 bytes neinițializați
section .text
global _start
_start:
; write(1, mesaj, len)
mov eax, 4 ; syscall nr. 4 = sys_write
mov ebx, 1 ; fd = 1 (stdout)
mov ecx, mesaj ; pointer la buffer
mov edx, len ; lungime
int 0x80 ; apel sistem Linux
; exit(0)
mov eax, 1 ; syscall nr. 1 = sys_exit
xor ebx, ebx ; cod de ieșire = 0
int 0x80
; Asamblare și link:
; nasm -f elf32 hello.asm -o hello.o
; ld -m elf_i386 hello.o -o hello
7.2 Operații aritmetice — exemplu complet¶
; aritmetic.asm — calculează (a + b) * c - d
section .data
a dd 10
b dd 20
c dd 3
d dd 5
section .bss
rezultat resd 1
section .text
global _start
_start:
mov eax, [a] ; EAX = 10
add eax, [b] ; EAX = 10 + 20 = 30
imul eax, [c] ; EAX = 30 * 3 = 90
sub eax, [d] ; EAX = 90 - 5 = 85
mov [rezultat], eax ; salvează rezultatul
; exit cu codul = rezultat (pentru verificare)
mov ebx, eax
mov eax, 1
int 0x80
7.3 Structuri de control¶
If-Else:
; if (eax >= 10) { ebx = 1; } else { ebx = 0; }
cmp eax, 10
jge mai_mare_egal
mov ebx, 0 ; else: ebx = 0
jmp endif
mai_mare_egal:
mov ebx, 1 ; then: ebx = 1
endif:
Buclă for (numără de la 0 la 9):
xor ecx, ecx ; ECX = 0 (contor)
bucla:
cmp ecx, 10
jge sfarsit ; dacă ECX >= 10, ieși
; === corpul buclei ===
; ... operații cu ECX ca index ...
; =========================
inc ecx ; ECX++
jmp bucla
sfarsit:
Buclă while:
; while (eax > 0) { eax = eax - 3; ecx++; }
xor ecx, ecx
while_start:
cmp eax, 0
jle while_end
sub eax, 3
inc ecx
jmp while_start
while_end:
Buclă do-while cu LOOP:
mov ecx, 5 ; ECX = număr de iterații
bucla_loop:
; === corpul buclei ===
; ...
; =========================
loop bucla_loop ; ECX--, dacă ECX != 0 → salt
7.4 Lucrul cu tablouri (arrays)¶
section .data
tablou dd 10, 20, 30, 40, 50 ; array de 5 int-uri (32 biți)
n equ 5
section .bss
suma resd 1
section .text
global _start
_start:
xor eax, eax ; EAX = 0 (acumulator pentru sumă)
xor ecx, ecx ; ECX = 0 (index)
suma_loop:
cmp ecx, n
jge done
add eax, [tablou + ecx*4] ; EAX += tablou[ECX]
inc ecx
jmp suma_loop
done:
mov [suma], eax ; salvează suma
mov ebx, eax ; exit code = suma
mov eax, 1
int 0x80
7.5 Funcții și convenții de apel (cdecl)¶
; Funcție: int aduna(int x, int y)
; Convenția cdecl: argumente pe stivă (dreapta→stânga), apelantul curăță stiva
section .text
global _start
; === Funcția aduna ===
aduna:
push ebp
mov ebp, esp
mov eax, [ebp+8] ; primul argument (x)
add eax, [ebp+12] ; al doilea argument (y)
; rezultatul se returnează în EAX
pop ebp
ret
; === Programul principal ===
_start:
push dword 20 ; argument y = 20
push dword 10 ; argument x = 10
call aduna ; apelează funcția
add esp, 8 ; curăță stiva (2 argumente × 4 bytes)
; EAX conține acum 30
mov ebx, eax
mov eax, 1
int 0x80
7.6 Funcții recursive — factorial¶
; int factorial(int n)
; factorial(0) = 1
; factorial(n) = n * factorial(n-1)
factorial:
push ebp
mov ebp, esp
mov eax, [ebp+8] ; EAX = n
cmp eax, 0
je caz_baza ; dacă n == 0, returnează 1
; n > 0: returnează n * factorial(n-1)
dec eax ; EAX = n - 1
push eax ; argument pentru apelul recursiv
call factorial
add esp, 4 ; curăță stiva
imul eax, [ebp+8] ; EAX = factorial(n-1) * n
jmp fact_end
caz_baza:
mov eax, 1 ; returnează 1
fact_end:
pop ebp
ret
7.7 Operații pe biți — aplicații practice¶
; Setează bitul k din EAX
bts eax, 5 ; Set bit 5 (EAX |= (1 << 5))
; Șterge bitul k
btr eax, 5 ; Reset bit 5 (EAX &= ~(1 << 5))
; Testează bitul k
bt eax, 5 ; CF = bit 5 din EAX
jc bit_setat ; salt dacă bitul e 1
; Numără biții setați (x86 modern cu POPCNT)
popcnt ecx, eax ; ECX = numărul de biți 1 din EAX
; Swap fără variabilă temporară
xor eax, ebx
xor ebx, eax
xor eax, ebx ; acum EAX și EBX sunt interschimbate
; Verifică dacă un număr este putere a lui 2
test eax, eax
jz nu_e_putere ; 0 nu este putere a lui 2
mov ecx, eax
dec ecx ; ECX = n - 1
and ecx, eax ; n & (n-1) == 0 ⟺ putere a lui 2
jnz nu_e_putere
; este putere a lui 2
7.8 x86-64 — diferențe esențiale¶
; hello_64.asm — Hello World, Linux x86-64, NASM
section .data
mesaj db "Hello, x86-64!", 10
len equ $ - mesaj
section .text
global _start
_start:
; Syscall-urile x86-64 folosesc registre, nu stiva
; Convenția: RAX=nr_syscall, RDI, RSI, RDX, R10, R8, R9
mov rax, 1 ; sys_write (nr. 1 pe x86-64)
mov rdi, 1 ; fd = stdout
lea rsi, [rel mesaj] ; pointer (RIP-relative)
mov rdx, len ; lungime
syscall ; instrucțiune syscall (nu int 0x80)
mov rax, 60 ; sys_exit (nr. 60 pe x86-64)
xor edi, edi ; cod = 0
syscall
; nasm -f elf64 hello_64.asm && ld hello_64.o -o hello_64
Convenția de apel System V AMD64 (Linux):
Primele 6 argumente întregi: RDI, RSI, RDX, RCX, R8, R9.
Returnare: RAX (și RDX pentru valori de 128 biți).
Registre volatile (caller-saved): RAX, RCX, RDX, RSI, RDI, R8–R11.
Registre nevolatile (callee-saved): RBX, RBP, R12–R15.
8. Arhitectura ARM — registre și organizare¶
8.1 Registrele ARM (AArch32 — ARMv7)¶
R0 — Argument 1 / Return value
R1 — Argument 2 / Return value (64-bit)
R2 — Argument 3
R3 — Argument 4
R4 — Variable register (callee-saved)
R5 — Variable register (callee-saved)
R6 — Variable register (callee-saved)
R7 — Variable register (callee-saved) / Syscall number (Thumb)
R8 — Variable register (callee-saved)
R9 — Platform register (variabil)
R10 — Variable register (callee-saved)
R11 — Frame Pointer (FP) — callee-saved
R12 — Intra-Procedure-call scratch (IP)
R13 — Stack Pointer (SP)
R14 — Link Register (LR) — adresa de retur
R15 — Program Counter (PC)
CPSR — Current Program Status Register (flags + mod de execuție)
Registrul CPSR:
┌───┬───┬───┬───┬─────────┬───┬───┬───┬──────────┐
│ N │ Z │ C │ V │ ... │ I │ F │ T │ Mode[4:0]│
└───┴───┴───┴───┴─────────┴───┴───┴───┴──────────┘
31 30 29 28 7 6 5 4-0
N = Negative, Z = Zero, C = Carry, V = Overflow
I = IRQ disable, F = FIQ disable
T = Thumb state (0=ARM, 1=Thumb)
Mode = User, FIQ, IRQ, Supervisor, Abort, Undefined, System
8.2 Moduri de operare ARM¶
| Mod | Cod | Utilizare | Registre banked |
|---|---|---|---|
| User (USR) | 10000 | Aplicații normale (neprivilegiat) | — |
| FIQ | 10001 | Întrerupere rapidă | R8_fiq – R14_fiq, SPSR |
| IRQ | 10010 | Întrerupere normală | R13_irq, R14_irq, SPSR |
| Supervisor | 10011 | Apeluri sistem (SVC/SWI) | R13_svc, R14_svc, SPSR |
| Abort | 10111 | Excepții de memorie | R13_abt, R14_abt, SPSR |
| Undefined | 11011 | Instrucțiuni nedefinite | R13_und, R14_und, SPSR |
| System | 11111 | OS privilegiat (aceleași registre ca USR) | — |
8.3 AArch64 (ARMv8-A, 64 biți)¶
Registrele se extind la 64 biți: X0–X30 (sau W0–W30 pentru acces la jumătatea inferioară de 32 biți). SP nu mai este un GPR aliased pe R13, ci un registru dedicat. PC nu mai este accesibil ca GPR.
X0–X7 — argumente funcție / return value
X8 — indirect result register
X9–X15 — temporare (caller-saved)
X16–X17 — intra-procedure-call (IP0, IP1)
X18 — platform register
X19–X28 — callee-saved
X29 — Frame Pointer (FP)
X30 — Link Register (LR)
SP — Stack Pointer (dedicat)
PC — Program Counter (nu e GPR, accesat indirect)
8.4 Setul de instrucțiuni Thumb și Thumb-2¶
ARM (32 biți): instrucțiuni de 4 bytes, set complet de funcționalități.
Thumb (16 biți): subset comprimat, instrucțiuni de 2 bytes. Densitate de cod mai mare (~65% din dimensiunea ARM), performanță ușor redusă.
Thumb-2: mix de instrucțiuni de 16 și 32 biți. Combină densitatea Thumb cu funcționalitatea ARM. Standard pe Cortex-M (care execută exclusiv Thumb-2).
8.5 Barrel Shifter — puterea unică a ARM¶
Operandul 2 al instrucțiunilor de procesare date poate fi deplasat/rotit gratuit (fără ciclu suplimentar):
; Operații de shift:
; LSL #n — Logical Shift Left
; LSR #n — Logical Shift Right
; ASR #n — Arithmetic Shift Right (păstrează semnul)
; ROR #n — Rotate Right
ADD R0, R1, R2, LSL #3 ; R0 = R1 + (R2 << 3) = R1 + R2*8
SUB R0, R1, R2, ASR #1 ; R0 = R1 - (R2 / 2)
MOV R0, R1, ROR #8 ; R0 = rotire dreapta R1 cu 8 biți
RSB R0, R1, R1, LSL #3 ; R0 = R1*8 - R1 = R1 * 7
9. Programare în Assembly ARM¶
9.1 Hello World (Linux, ARM 32-bit, GNU syntax)¶
@ hello.s — Hello World, Linux ARM, GAS syntax
.data
mesaj: .asciz "Hello, ARM!\n"
len = . - mesaj
.text
.global _start
_start:
@ write(1, mesaj, len)
MOV R7, #4 @ syscall nr. 4 = sys_write
MOV R0, #1 @ fd = stdout
LDR R1, =mesaj @ pointer la mesaj
MOV R2, #len @ lungime
SVC #0 @ supervisor call (apel sistem)
@ exit(0)
MOV R7, #1 @ syscall nr. 1 = sys_exit
MOV R0, #0 @ cod de ieșire = 0
SVC #0
@ Asamblare: as -o hello.o hello.s && ld -o hello hello.o
9.2 Operații aritmetice¶
@ calcul.s — (a + b) * c - d
.data
a: .word 10
b: .word 20
c: .word 3
d: .word 5
rezultat: .word 0
.text
.global _start
_start:
LDR R4, =a
LDR R0, [R4] @ R0 = a = 10
LDR R4, =b
LDR R1, [R4] @ R1 = b = 20
LDR R4, =c
LDR R2, [R4] @ R2 = c = 3
LDR R4, =d
LDR R3, [R4] @ R3 = d = 5
ADD R0, R0, R1 @ R0 = a + b = 30
MUL R0, R0, R2 @ R0 = 30 * 3 = 90
SUB R0, R0, R3 @ R0 = 90 - 5 = 85
LDR R4, =rezultat
STR R0, [R4] @ salvează rezultatul
@ exit cu codul = rezultat
MOV R7, #1
SVC #0
9.3 Structuri de control ARM¶
If-Else (cu execuție condiționată):
@ if (R0 > R1) R2 = R0; else R2 = R1;
@ (echivalent: R2 = max(R0, R1))
CMP R0, R1
MOVGT R2, R0 @ dacă R0 > R1, R2 = R0
MOVLE R2, R1 @ dacă R0 <= R1, R2 = R1
If-Else (cu branch, pentru cod mai complex):
CMP R0, #0
BEQ este_zero
@ R0 != 0:
ADD R1, R1, R0
B endif
este_zero:
MOV R1, #-1
endif:
Buclă for (suma primelor N numere):
@ R0 = N (intrare), R1 = suma (ieșire)
MOV R1, #0 @ suma = 0
MOV R2, #1 @ contor i = 1
for_loop:
CMP R2, R0
BGT for_end @ dacă i > N, ieși
ADD R1, R1, R2 @ suma += i
ADD R2, R2, #1 @ i++
B for_loop
for_end:
@ R1 conține suma 1 + 2 + ... + N
Buclă while (numără biții setați):
@ R0 = număr de intrare, R1 = nr. biți setați
MOV R1, #0
while_loop:
CMP R0, #0
BEQ while_end
AND R2, R0, #1 @ R2 = ultimul bit
ADD R1, R1, R2 @ numără dacă e 1
LSR R0, R0, #1 @ shift dreapta cu 1
B while_loop
while_end:
9.4 Lucrul cu tablouri¶
@ Suma elementelor unui array
.data
tablou: .word 10, 20, 30, 40, 50
n: .word 5
.text
.global _start
_start:
LDR R4, =tablou @ R4 = adresa tabloului
LDR R5, =n
LDR R5, [R5] @ R5 = n = 5
MOV R0, #0 @ R0 = suma = 0
MOV R1, #0 @ R1 = index = 0
suma_loop:
CMP R1, R5
BGE suma_done
LDR R2, [R4, R1, LSL #2] @ R2 = tablou[R1] (offset = R1*4)
ADD R0, R0, R2 @ suma += tablou[R1]
ADD R1, R1, #1 @ index++
B suma_loop
suma_done:
@ R0 conține suma = 150
MOV R7, #1
SVC #0
9.5 Funcții și convenția AAPCS¶
@ Convenția AAPCS (ARM Architecture Procedure Call Standard):
@ R0-R3: argumente (și return value în R0)
@ R4-R11: callee-saved
@ R12: scratch (IP)
@ R13: SP, R14: LR, R15: PC
@ Funcție: int aduna(int x, int y)
aduna:
@ R0 = x, R1 = y (argumente în registre, nu pe stivă!)
ADD R0, R0, R1 @ R0 = x + y (rezultat în R0)
BX LR @ return (branch to link register)
@ Apel din main:
MOV R0, #10 @ argument x = 10
MOV R1, #20 @ argument y = 20
BL aduna @ apel (LR = adresa de retur)
@ R0 conține acum 30
9.6 Funcții cu salvare pe stivă¶
@ Funcție mai complexă care folosește registre callee-saved
@ int suma_patrate(int n) — returnează 1² + 2² + ... + n²
suma_patrate:
PUSH {R4-R6, LR} @ salvează registrele pe stivă
MOV R4, R0 @ R4 = n (salvat)
MOV R5, #0 @ R5 = suma = 0
MOV R6, #1 @ R6 = i = 1
.Lsp_loop:
CMP R6, R4
BGT .Lsp_done
MUL R0, R6, R6 @ R0 = i * i
ADD R5, R5, R0 @ suma += i²
ADD R6, R6, #1 @ i++
B .Lsp_loop
.Lsp_done:
MOV R0, R5 @ return value = suma
POP {R4-R6, PC} @ restaurează registrele, PC ← LR (return)
9.7 Factorial recursiv ARM¶
@ int factorial(int n)
factorial:
PUSH {LR} @ salvează adresa de retur
CMP R0, #0
BNE .Lfact_recurse
@ Caz de bază: factorial(0) = 1
MOV R0, #1
POP {PC} @ return 1
.Lfact_recurse:
PUSH {R4} @ salvează R4
MOV R4, R0 @ R4 = n (salvat)
SUB R0, R0, #1 @ R0 = n - 1
BL factorial @ apel recursiv: R0 = factorial(n-1)
MUL R0, R4, R0 @ R0 = n * factorial(n-1)
POP {R4} @ restaurează R4
POP {PC} @ return
9.8 Accesarea perifericelor ARM (bare-metal, Cortex-M)¶
@ Exemplu: aprinderea unui LED pe STM32 (Cortex-M3/M4)
@ LED pe portul GPIOA, pinul 5
.equ RCC_BASE, 0x40021000
.equ RCC_AHB1ENR, RCC_BASE + 0x30
.equ GPIOA_BASE, 0x40020000
.equ GPIOA_MODER, GPIOA_BASE + 0x00
.equ GPIOA_ODR, GPIOA_BASE + 0x14
.text
.global _start
.thumb @ Cortex-M execută doar Thumb-2
_start:
@ 1. Activează ceasul pentru GPIOA
LDR R0, =RCC_AHB1ENR
LDR R1, [R0]
ORR R1, R1, #(1 << 0) @ Bit 0 = GPIOAEN
STR R1, [R0]
@ 2. Configurează PA5 ca ieșire (MODER5 = 01)
LDR R0, =GPIOA_MODER
LDR R1, [R0]
BIC R1, R1, #(3 << 10) @ Șterge biții 11:10
ORR R1, R1, #(1 << 10) @ Setează bitul 10 (output mode)
STR R1, [R0]
@ 3. Aprinde LED-ul (setează PA5 HIGH)
LDR R0, =GPIOA_ODR
LDR R1, [R0]
ORR R1, R1, #(1 << 5) @ Bit 5 = 1
STR R1, [R0]
@ 4. Buclă infinită
loop:
B loop
10. Sistemul de memorie și ierarhia cache¶
10.1 Ierarhia de memorie¶
Viteză Capacitate Cost/byte
──────► ──────► ◄──────
┌───────────────────┐
│ Registre CPU │ < 1 ns ~ 100 B $$$$$
├───────────────────┤
│ Cache L1 │ 1–2 ns 32–64 KB $$$$
├───────────────────┤
│ Cache L2 │ 3–10 ns 256 KB–1 MB $$$
├───────────────────┤
│ Cache L3 │ 10–30 ns 2–64 MB $$
├───────────────────┤
│ RAM (DRAM) │ 50–100 ns 4–128 GB $
├───────────────────┤
│ SSD (Flash) │ 25–100 μs 256 GB–8 TB ¢
├───────────────────┤
│ HDD │ 3–10 ms 1–20 TB ¢/10
└───────────────────┘
10.2 Memoria cache — principii¶
Cache-ul exploatează două proprietăți ale programelor:
- Localitate temporală: datele accesate recent vor fi probabil accesate din nou în curând
- Localitate spațială: datele aflate la adrese vecine vor fi probabil accesate în curând
Structura cache-ului:
- Memoria este împărțită în blocuri (linii de cache), tipic de 64 bytes
- Fiecare linie conține: tag (identificator), date (conținutul blocului), flag-uri (valid, dirty)
10.3 Organizarea cache-ului¶
Direct Mapped:
Fiecare adresă de memorie se mapează la exact o linie de cache.
Index = (Adresa / Dim_linie) mod Nr_linii
Rapid, simplu, dar vulnerabil la conflicte.
Fully Associative:
Un bloc poate fi plasat în orice linie. Necesită comparare cu toate tag-urile (scump hardware).
Set Associative (N-way):
Compromis: memoria e împărțită în seturi de N linii. Un bloc se mapează la un set, dar poate ocupa oricare din cele N locuri.
Set = (Adresa / Dim_linie) mod Nr_seturi
Tipic: L1 = 4–8 way, L2 = 8–16 way, L3 = 12–20 way.
10.4 Politici de înlocuire¶
Când un set este plin și trebuie adus un bloc nou: LRU (Least Recently Used) — cel mai puțin recent folosit, Random — alegere aleatorie, FIFO — primul intrat, primul ieșit.
10.5 Politici de scriere¶
Write-Through: fiecare scriere actualizează și cache-ul și memoria principală. Simplu, dar lent.
Write-Back: scrierile se fac doar în cache (bitul dirty marcat). Memoria se actualizează doar când linia este evacuată. Rapid, dar complex.
Write Allocate: la un write miss, blocul este adus în cache, apoi scris. Combinat uzual cu Write-Back.
10.6 Impactul cache-ului asupra programării¶
// Exemplu: parcurgerea unei matrice
// Varianta RAPIDĂ — acces pe rânduri (localitate spațială)
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
sum += matrix[i][j]; // adrese consecutive
// Varianta LENTĂ — acces pe coloane (distruge localitatea)
for (int j = 0; j < N; j++)
for (int i = 0; i < N; i++)
sum += matrix[i][j]; // salt de N*sizeof(int) la fiecare acces
Pe o matrice de 1024×1024, varianta pe coloane poate fi de 10-50× mai lentă.
10.7 Memoria virtuală¶
Procesorul traduce adresele virtuale în adrese fizice prin tabela de pagini (page table). Hardware-ul dedicat (MMU — Memory Management Unit) și TLB-ul (Translation Lookaside Buffer) accelerează traducerea.
Adresă virtuală (32 biți, pagini de 4 KB):
┌───────────────────┬────────────────┐
│ Nr. pagină (20b) │ Offset (12b) │
└───────────────────┴────────────────┘
│
▼ (TLB/Page Table lookup)
┌───────────────────┬────────────────┐
│ Nr. cadru (20b) │ Offset (12b) │
└───────────────────┴────────────────┘
Adresă fizică
11. Pipeline și tehnici de execuție¶
11.1 Pipeline-ul clasic pe 5 etaje (RISC)¶
Ciclu: 1 2 3 4 5 6 7 8
Instr 1: [IF] [ID] [EX] [ME] [WB]
Instr 2: [IF] [ID] [EX] [ME] [WB]
Instr 3: [IF] [ID] [EX] [ME] [WB]
Instr 4: [IF] [ID] [EX] [ME] [WB]
IF = Instruction Fetch
ID = Instruction Decode / Register Read
EX = Execute (ALU)
ME = Memory Access
WB = Write Back
Fără pipeline: 5 cicluri per instrucțiune. Cu pipeline ideal: 1 instrucțiune per ciclu (throughput).
11.2 Hazarduri în pipeline¶
Hazardul de date (Data Hazard):
O instrucțiune depinde de rezultatul uneia anterioare care nu s-a terminat.
ADD R1, R2, R3 @ scrie R1
SUB R4, R1, R5 @ citește R1 — dar R1 nu e încă scris!
Soluții:
- Forwarding (bypassing): rezultatul ALU se trimite direct la intrarea etajului EX al instrucțiunii dependente, fără a aștepta WB
- Stall (bubble): pipeline-ul se oprește 1–2 cicluri
- Reordonarea instrucțiunilor: compilatorul sau hardware-ul rearanjează instrucțiunile
Hazardul de control (Branch Hazard):
La un salt condiționat, direcția nu se cunoaște până la etajul EX. Instrucțiunile după branch pot fi deja în pipeline.
CMP R0, #0
BEQ target @ direcția necunoscută pentru 2 cicluri
ADD R1, R2, R3 @ aceste instrucțiuni pot fi deja fetch-uite
SUB R4, R5, R6 @ dar poate nu trebuie executate
Soluții:
- Branch prediction: predicția direcției saltului (static sau dinamic)
- Branch delay slot: instrucțiunea de după branch se execută mereu (MIPS, SPARC)
- Execuție speculativă: se execută calea prezisă, se anulează dacă predicția e greșită
- Execuție condiționată ARM: elimină branchuri scurte complet
Hazardul structural:
Două instrucțiuni necesită aceeași resursă hardware simultan (ex. acces la memorie de date și de instrucțiuni pe aceeași magistrală). Soluție: resurse separate (Harvard cache).
11.3 Predicția salturilor¶
Predicție statică:
- Always Not Taken (cele mai simple)
- Backward Taken / Forward Not Taken (bun pentru bucle)
Predicție dinamică:
- 1-bit predictor: reține ultima direcție
- 2-bit predictor (saturating counter): necesită două predicții greșite consecutive pentru a schimba direcția. Acuratețe ~85–90%.
- Predictori corelați: țin cont de istoria ultimelor N branch-uri
- Tournament predictor: alege dinamic între mai mulți predictori
- TAGE predictor: cel mai avansat, acuratețe >95%
11.4 Execuție superscalară¶
Procesorul emite și execută multiple instrucțiuni pe ciclu:
- Dual-issue: 2 instrucțiuni/ciclu
- 4-wide superscalar: 4 instrucțiuni/ciclu (x86 modern)
- 6-8 wide: procesoare ARM de ultimă generație
11.5 Execuție Out-of-Order (OoO)¶
Instrucțiunile sunt executate în ordinea în care operanzii devin disponibili, nu în ordinea din program. Necesită: tabelă de redenumire a registrelor (register renaming), stație de rezervare (reservation station), buffer de reordonare (reorder buffer — ROB).
Pipeline modern x86 (simplificat):
Fetch → Decode → μop cache → Rename → Schedule → Execute → Retire
↓
Micro-ops RISC interne
12. Sistemul de întreruperi¶
12.1 Conceptul de întrerupere¶
O întrerupere este un mecanism prin care un eveniment extern (hardware) sau intern (software/excepție) provoacă suspendarea programului curent și executarea unui handler dedicat.
Program normal Eveniment Handler Revenire
────────────► ──┐ ┌────────►
│ │
└──► Salvare │
stare │
↓ │
Handler ISR │
↓ │
Restaurare │
stare │
↓ │
◄────────────┘
────────────────────────────────────────────────────►
12.2 Tipuri de întreruperi¶
Întreruperi hardware (asincrone):
- Mascabile (IRQ): pot fi dezactivate individual sau global (flag IF/I)
- Nemascabile (NMI/FIQ): nu pot fi dezactivate — erori critice, watchdog
Întreruperi software (sincrone):
- Excepții: generate de CPU la erori (Division by Zero, Page Fault, Invalid Opcode)
- Apeluri de sistem: INT 0x80 (x86 Linux), SVC (ARM), syscall (x86-64)
12.3 Procesul de tratare a unei întreruperi¶
- CPU termină instrucțiunea curentă
- Salvează starea (PC, registrul de stare/FLAGS) pe stivă
- Identifică sursa întreruperii (vectorul de întrerupere)
- Încarcă adresa handler-ului din tabela de vectori
- Execută handler-ul (ISR — Interrupt Service Routine)
- Restaurează starea și reia programul (IRET/RFE)
12.4 Tabela de vectori x86¶
Vector 0: Division Error
Vector 1: Debug
Vector 2: NMI
Vector 3: Breakpoint (INT 3)
Vector 4: Overflow
Vector 6: Invalid Opcode
Vector 8: Double Fault
Vector 13: General Protection Fault
Vector 14: Page Fault
Vectori 32+: Întreruperi hardware (IRQ 0–15) și software
IDT (Interrupt Descriptor Table): în modul protejat, fiecare vector pointează la un descriptor de poartă (gate descriptor) care specifică adresa handler-ului, segmentul de cod și nivelul de privilegiu.
12.5 Tabela de vectori ARM (ARMv7-M / Cortex-M)¶
.section .isr_vector, "a"
.word _estack @ 0x00: Valoarea inițială a SP
.word Reset_Handler @ 0x04: Reset
.word NMI_Handler @ 0x08: NMI
.word HardFault_Handler @ 0x0C: Hard Fault
.word MemManage_Handler @ 0x10: Memory Management Fault
.word BusFault_Handler @ 0x14: Bus Fault
.word UsageFault_Handler @ 0x18: Usage Fault
.word 0 @ 0x1C: Rezervat
.word 0 @ 0x20: Rezervat
.word 0 @ 0x24: Rezervat
.word 0 @ 0x28: Rezervat
.word SVC_Handler @ 0x2C: Supervisor Call
.word DebugMon_Handler @ 0x30: Debug Monitor
.word 0 @ 0x34: Rezervat
.word PendSV_Handler @ 0x38: PendSV
.word SysTick_Handler @ 0x3C: System Tick Timer
@ Vectori de periferice (UART, SPI, GPIO etc.) de la 0x40 în sus
12.6 NVIC (Nested Vectored Interrupt Controller) — ARM Cortex-M¶
Controlerul NVIC al Cortex-M suportă:
- Până la 240 de întreruperi externe cu priorități configurabile
- Preemptare pe bază de prioritate: o întrerupere cu prioritate mai mare poate întrerupe un ISR cu prioritate mai joasă (nesting)
- Tail-chaining: dacă o altă întrerupere este pendinte când ISR-ul curent se termină, se trece direct la ea fără salvare/restaurare completă
- Late-arriving: dacă în timpul salvării contextului apare o întrerupere cu prioritate mai mare, se servește aceasta prima
@ Exemplu: configurare NVIC pentru EXTI0 (întrerupere GPIO)
.equ NVIC_ISER0, 0xE000E100 @ Interrupt Set Enable Register 0
.equ EXTI0_IRQn, 6 @ Numărul întreruperii EXTI0
LDR R0, =NVIC_ISER0
MOV R1, #(1 << EXTI0_IRQn) @ Activează EXTI0
STR R1, [R0]
12.7 Exemplu ISR complet x86¶
; Handler pentru timer interrupt (IRQ 0, vector 32)
; x86-32, mod protejat
timer_handler:
pushad ; salvează toate registrele generale
push ds
push es
mov ax, 0x10 ; selectorul segmentului de date kernel
mov ds, ax
mov es, ax
; === Codul handler-ului ===
inc dword [tick_count] ; incrementează contorul de tick-uri
; ===========================
; Confirmă întreruperea la PIC (8259A)
mov al, 0x20 ; EOI (End of Interrupt)
out 0x20, al ; trimite la PIC master
pop es
pop ds
popad
iret ; return din întrerupere
13. Interfațarea cu perifericele (I/O)¶
13.1 I/O mapată în memorie vs. I/O izolat¶
I/O mapată în memorie (Memory-Mapped I/O):
Registrele perifericelor sunt accesate la adrese de memorie. Se folosesc aceleași instrucțiuni LOAD/STORE ca pentru memorie. Standard pe ARM.
@ ARM: citire registru de stare UART la adresa 0x40011000
LDR R0, =0x40011000
LDR R1, [R0] @ citește registrul
I/O izolat (Port-Mapped I/O):
Spațiu de adresare separat, accesat prin instrucțiuni speciale IN/OUT. Specific x86.
; x86: citire port serial COM1 (port I/O 0x3F8)
in al, 0x3F8 ; citește un byte de la port
out 0x3F8, al ; scrie un byte la port
; Port I/O cu adresă în DX (porturi > 0xFF)
mov dx, 0x3F8
in al, dx
out dx, al
13.2 Tehnici de transfer de date¶
Polling (interogare):
CPU-ul verifică periodic un registru de stare al perifericului. Simplu, dar risipește cicluri CPU.
@ Polling UART — așteaptă caracter recepționat
poll_loop:
LDR R0, =UART_SR @ Status Register
LDR R1, [R0]
TST R1, #(1 << 5) @ Bitul RXNE (Receive Not Empty)
BEQ poll_loop @ dacă nu e gata, mai încearcă
LDR R0, =UART_DR @ Data Register
LDR R2, [R0] @ citește caracterul
Întreruperi:
Perifericul semnalează CPU prin IRQ. CPU execută alt cod între transferuri.
@ Handler UART — apelat automat la recepția unui caracter
UART_IRQ_Handler:
PUSH {R0-R2, LR}
LDR R0, =UART_DR
LDR R1, [R0] @ citește data
LDR R0, =buffer
LDR R2, =buf_index
LDR R2, [R2]
STRB R1, [R0, R2] @ buffer[index] = data
ADD R2, R2, #1
LDR R0, =buf_index
STR R2, [R0] @ index++
POP {R0-R2, PC}
DMA (Direct Memory Access):
Un controller DMA dedicat transferă date între memorie și periferic fără intervenția CPU. CPU doar inițiază transferul și este notificat la sfârșit prin întrerupere.
@ Configurare DMA (pseudocod STM32)
@ DMA Stream: UART_RX → buffer în memorie
LDR R0, =DMA_S0CR @ Control Register
MOV R1, #0 @ Dezactivează stream-ul
STR R1, [R0]
LDR R0, =DMA_S0PAR @ Peripheral Address
LDR R1, =UART_DR
STR R1, [R0]
LDR R0, =DMA_S0M0AR @ Memory Address
LDR R1, =dma_buffer
STR R1, [R0]
LDR R0, =DMA_S0NDTR @ Number of Data items
MOV R1, #256
STR R1, [R0]
LDR R0, =DMA_S0CR
MOV R1, #(DMA_EN | PERIPH_TO_MEM | CIRC_MODE | TCIE)
STR R1, [R0] @ Pornește DMA
13.3 Timer-e hardware — generare PWM¶
@ Configurare Timer 2 pentru PWM pe STM32 Cortex-M
.equ TIM2_BASE, 0x40000000
.equ TIM2_CR1, TIM2_BASE + 0x00
.equ TIM2_PSC, TIM2_BASE + 0x28 @ Prescaler
.equ TIM2_ARR, TIM2_BASE + 0x2C @ Auto-Reload (perioadă)
.equ TIM2_CCR1, TIM2_BASE + 0x34 @ Capture/Compare (duty)
.equ TIM2_CCMR1,TIM2_BASE + 0x18 @ Mod canal
.equ TIM2_CCER, TIM2_BASE + 0x20 @ Enable canal
@ Prescaler = 83 → clock timer = 84MHz/(83+1) = 1 MHz
LDR R0, =TIM2_PSC
MOV R1, #83
STR R1, [R0]
@ Perioadă = 999 → frecvența PWM = 1MHz/1000 = 1 kHz
LDR R0, =TIM2_ARR
MOV R1, #999
STR R1, [R0]
@ Duty cycle = 50% → CCR1 = 500
LDR R0, =TIM2_CCR1
MOV R1, #500
STR R1, [R0]
@ Configurare PWM Mode 1 pe canalul 1
LDR R0, =TIM2_CCMR1
MOV R1, #0x0060 @ OC1M = 110 (PWM mode 1)
STR R1, [R0]
@ Enable ieșirea canalului 1
LDR R0, =TIM2_CCER
MOV R1, #0x0001 @ CC1E = 1
STR R1, [R0]
@ Pornește timerul
LDR R0, =TIM2_CR1
MOV R1, #0x0001 @ CEN = 1
STR R1, [R0]
14. Exemple comparative ARM vs. x86¶
14.1 Căutare liniară într-un array¶
// Versiunea C (referință):
int cauta(int *arr, int n, int val) {
for (int i = 0; i < n; i++)
if (arr[i] == val) return i;
return -1;
}
x86-32:
; int cauta(int *arr, int n, int val)
; [ebp+8]=arr, [ebp+12]=n, [ebp+16]=val
cauta:
push ebp
mov ebp, esp
mov esi, [ebp+8] ; ESI = arr
mov ecx, [ebp+12] ; ECX = n
mov edx, [ebp+16] ; EDX = val
xor eax, eax ; EAX = i = 0
.Lloop:
cmp eax, ecx
jge .Lnot_found
cmp [esi + eax*4], edx
je .Lfound
inc eax
jmp .Lloop
.Lnot_found:
mov eax, -1
.Lfound:
pop ebp
ret
ARM (AArch32):
@ int cauta(int *arr, int n, int val)
@ R0=arr, R1=n, R2=val
cauta:
MOV R3, #0 @ R3 = i = 0
.Lloop:
CMP R3, R1
BGE .Lnot_found
LDR R4, [R0, R3, LSL #2] @ R4 = arr[i]
CMP R4, R2
BEQ .Lfound
ADD R3, R3, #1
B .Lloop
.Lnot_found:
MVN R0, #0 @ R0 = -1 (MVN = NOT 0 = 0xFFFFFFFF)
BX LR
.Lfound:
MOV R0, R3 @ R0 = i (return index)
BX LR
14.2 Sortare cu bule (Bubble Sort)¶
void bubble_sort(int *arr, int n) {
for (int i = 0; i < n-1; i++)
for (int j = 0; j < n-1-i; j++)
if (arr[j] > arr[j+1]) {
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
x86-32:
; void bubble_sort(int *arr, int n)
bubble_sort:
push ebp
mov ebp, esp
push ebx
push esi
push edi
mov esi, [ebp+8] ; ESI = arr
mov ecx, [ebp+12] ; ECX = n
dec ecx ; ECX = n-1
.Louter:
test ecx, ecx
jle .Ldone
xor edi, edi ; EDI = j = 0
mov edx, ecx ; EDX = limita buclei interioare
.Linner:
cmp edi, edx
jge .Lnext_outer
mov eax, [esi + edi*4] ; EAX = arr[j]
mov ebx, [esi + edi*4 + 4] ; EBX = arr[j+1]
cmp eax, ebx
jle .Lno_swap
; swap
mov [esi + edi*4], ebx
mov [esi + edi*4 + 4], eax
.Lno_swap:
inc edi
jmp .Linner
.Lnext_outer:
dec ecx ; n-1-i scade automat
jmp .Louter
.Ldone:
pop edi
pop esi
pop ebx
pop ebp
ret
ARM:
@ void bubble_sort(int *arr, int n)
@ R0 = arr, R1 = n
bubble_sort:
PUSH {R4-R8, LR}
MOV R4, R0 @ R4 = arr
SUB R5, R1, #1 @ R5 = n-1 (bucla externă)
.Louter:
CMP R5, #0
BLE .Ldone
MOV R6, #0 @ R6 = j = 0
MOV R7, R5 @ R7 = limita buclei interne
.Linner:
CMP R6, R7
BGE .Lnext_outer
LDR R0, [R4, R6, LSL #2] @ R0 = arr[j]
ADD R8, R6, #1
LDR R1, [R4, R8, LSL #2] @ R1 = arr[j+1]
CMP R0, R1
BLE .Lno_swap
@ swap
STR R1, [R4, R6, LSL #2]
STR R0, [R4, R8, LSL #2]
.Lno_swap:
ADD R6, R6, #1
B .Linner
.Lnext_outer:
SUB R5, R5, #1
B .Louter
.Ldone:
POP {R4-R8, PC}
14.3 Conversie string la integer (atoi simplificat)¶
x86-32:
; int my_atoi(char *str)
; presupune: caractere '0'-'9' urmate de terminator 0
my_atoi:
push ebp
mov ebp, esp
mov esi, [ebp+8] ; ESI = str
xor eax, eax ; EAX = rezultat = 0
.Latoi_loop:
movzx ecx, byte [esi] ; ECX = *str (zero-extended)
test cl, cl
jz .Latoi_done ; dacă '\0', termină
sub cl, '0' ; convertește ASCII → cifră
imul eax, eax, 10 ; rezultat *= 10
add eax, ecx ; rezultat += cifra curentă
inc esi ; str++
jmp .Latoi_loop
.Latoi_done:
pop ebp
ret
ARM:
@ int my_atoi(char *str)
@ R0 = str
my_atoi:
MOV R1, R0 @ R1 = pointer la string
MOV R0, #0 @ R0 = rezultat = 0
.Latoi_loop:
LDRB R2, [R1], #1 @ R2 = *str++ (post-increment)
CMP R2, #0
BEQ .Latoi_done
SUB R2, R2, #'0' @ cifra = caracter - '0'
MOV R3, #10
MUL R0, R0, R3 @ rezultat *= 10
ADD R0, R0, R2 @ rezultat += cifra
B .Latoi_loop
.Latoi_done:
BX LR @ return R0
15. Arhitecturi moderne și tendințe¶
15.1 Procesoare multi-core¶
Creșterea frecvenței a atins limita termică (~4 GHz). Soluția modernă: mai multe nuclee pe același chip.
Provocări: programarea paralelă, coerența cache-ului (protocoale MESI/MOESI), sincronizarea (mutexuri, bariere, operații atomice).
; x86: instrucțiune atomică (compare-and-swap)
; if (*ptr == expected) { *ptr = new_val; return true; }
lock cmpxchg [edi], ecx ; atomic: compară EAX cu [EDI],
; dacă egal, [EDI] = ECX, ZF=1
; altfel, EAX = [EDI], ZF=0
@ ARM: exclusive load/store (echivalent CAS)
.Lretry:
LDREX R1, [R0] @ exclusive load
CMP R1, R2 @ compară cu expected
BNE .Lfail
STREX R3, R4, [R0] @ exclusive store new_val
CMP R3, #0
BNE .Lretry @ dacă a eșuat (altcineva a scris), reîncearcă
.Lfail:
15.2 SIMD (Single Instruction, Multiple Data)¶
O singură instrucțiune operează simultan pe mai multe elemente de date:
x86 — SSE/AVX:
; Adunarea a 4 float-uri simultan (SSE)
movaps xmm0, [esi] ; încarcă 4 float-uri din arr_a
movaps xmm1, [edi] ; încarcă 4 float-uri din arr_b
addps xmm0, xmm1 ; adună cele 4 perechi simultan
movaps [ebx], xmm0 ; stochează rezultatul
; AVX-256: 8 float-uri simultan
vmovaps ymm0, [rsi]
vaddps ymm0, ymm0, [rdi]
vmovaps [rbx], ymm0
; AVX-512: 16 float-uri simultan (servere, HPC)
ARM — NEON:
@ Adunarea a 4 int32 simultan (NEON)
VLD1.32 {Q0}, [R0] @ Q0 = 4 × int32 din arr_a
VLD1.32 {Q1}, [R1] @ Q1 = 4 × int32 din arr_b
VADD.I32 Q2, Q0, Q1 @ Q2 = Q0 + Q1 (4 adunări paralele)
VST1.32 {Q2}, [R2] @ stochează rezultatul
15.3 Extensii de securitate hardware¶
ARM TrustZone: separă procesorul în două lumi: Secure World și Normal World, cu hardware dedicat de izolare. Folosit pentru chei criptografice, boot securizat, DRM.
Intel SGX / AMD SEV: enclave securizate pentru date sensibile, protejate chiar și de sistemul de operare.
ARM MTE (Memory Tagging Extension): etichetează pointerii cu tag-uri de 4 biți, detectând buffer overflow-uri și use-after-free la nivel hardware.
15.4 Arhitectura RISC-V¶
RISC-V este o arhitectură ISA deschisă (open-source), dezvoltată la UC Berkeley. Modulară: setul de bază (RV32I/RV64I) plus extensii opționale (M — multiplicare, A — atomice, F/D — virgulă mobilă, C — instrucțiuni comprimate, V — vectori).
; RISC-V Assembly — adunare simplă
addi x1, x0, 10 # x1 = 10
addi x2, x0, 20 # x2 = 20
add x3, x1, x2 # x3 = x1 + x2 = 30
Avantaje: fără licențiere (spre deosebire de ARM), proiectare curată, suport academic și industrial crescând.
15.5 Tendințe viitoare¶
- Chiplet-uri și packaging 3D: mai multe die-uri specializate în același pachet (AMD Zen, Intel tile architecture)
- Procesare heterogenă: nuclee mari (performanță) + nuclee mici (eficiență) pe același chip (ARM big.LITTLE, Intel P-cores/E-cores)
- Acceleratoare dedicate: NPU (AI/ML), GPU compute, DSP, hardware criptografic — integrate pe același SoC
- Compute-in-Memory / Processing-in-Memory: reducerea bottleneck-ului de memorie prin calcul direct în chip-urile de memorie
- Procesoare cuantice: aflate în fază experimentală, promit avantaje pentru probleme specifice (criptografie, optimizare, simulare moleculară)
Anexe¶
A. Tabel comparativ registre x86-32 vs. ARM¶
| Funcție | x86-32 | ARM (AArch32) | x86-64 | ARM (AArch64) |
|---|---|---|---|---|
| Acumulator | EAX | R0 (return val) | RAX | X0 |
| Bază/Pointer | EBX | R4–R8 | RBX | X19–X28 |
| Contor | ECX | — | RCX | — |
| Date | EDX | — | RDX | — |
| Stack Pointer | ESP | R13 (SP) | RSP | SP |
| Frame Pointer | EBP | R11 (FP) | RBP | X29 (FP) |
| Instruction Pointer | EIP | R15 (PC) | RIP | PC (nu e GPR) |
| Link Register | — (pe stivă) | R14 (LR) | — (pe stivă) | X30 (LR) |
| Argumente funcție | pe stivă | R0–R3 | RDI,RSI,RDX… | X0–X7 |
| Flags | EFLAGS | CPSR | RFLAGS | PSTATE/NZCV |
| Nr. GPR | 8 | 16 | 16 | 31 |
B. Rezumat apeluri de sistem Linux¶
x86-32 (int 0x80):
| EAX | Syscall | EBX | ECX | EDX |
|---|---|---|---|---|
| 1 | exit | cod | — | — |
| 3 | read | fd | buffer | count |
| 4 | write | fd | buffer | count |
| 5 | open | filename | flags | mode |
| 6 | close | fd | — | — |
x86-64 (syscall):
| RAX | Syscall | RDI | RSI | RDX |
|---|---|---|---|---|
| 0 | read | fd | buffer | count |
| 1 | write | fd | buffer | count |
| 2 | open | filename | flags | mode |
| 3 | close | fd | — | — |
| 60 | exit | cod | — | — |
ARM 32 (SVC #0, nr. syscall în R7):
| R7 | Syscall | R0 | R1 | R2 |
|---|---|---|---|---|
| 1 | exit | cod | — | — |
| 3 | read | fd | buffer | count |
| 4 | write | fd | buffer | count |
| 5 | open | filename | flags | mode |
| 6 | close | fd | — | — |
C. Instrucțiuni x86 frecvente — referință rapidă¶
| Instrucțiune | Operație | Flag-uri afectate |
|---|---|---|
| MOV dst, src | dst = src | Niciuna |
| ADD dst, src | dst = dst + src | CF, ZF, SF, OF |
| SUB dst, src | dst = dst - src | CF, ZF, SF, OF |
| CMP op1, op2 | op1 - op2 (fără stocare) | CF, ZF, SF, OF |
| AND dst, src | dst = dst & src | ZF, SF |
| OR dst, src | dst = dst | src |
| XOR dst, src | dst = dst ^ src | ZF, SF |
| NOT dst | dst = ~dst | Niciuna |
| SHL/SHR dst, cnt | Shift stânga/dreapta logic | CF, ZF, SF |
| LEA dst, [expr] | dst = adresa calculată (fără acces mem) | Niciuna |
| TEST op1, op2 | op1 & op2 (fără stocare) | ZF, SF |
| JMP target | Salt necondiționat | Niciuna |
| Jcc target | Salt condiționat (JE, JNE, JG etc.) | Niciuna |
| CALL target | Push EIP; JMP target | Niciuna |
| RET | Pop EIP | Niciuna |
| PUSH src | ESP-=4; [ESP]=src | Niciuna |
| POP dst | dst=[ESP]; ESP+=4 | Niciuna |
| NOP | No operation | Niciuna |
| INT n | Întrerupere software | IF, TF |
D. Instrucțiuni ARM frecvente — referință rapidă¶
| Instrucțiune | Operație | Notă |
|---|---|---|
| MOV Rd, Op2 | Rd = Op2 | Op2 poate fi shiftat |
| MVN Rd, Op2 | Rd = NOT Op2 | |
| ADD Rd, Rn, Op2 | Rd = Rn + Op2 | |
| SUB Rd, Rn, Op2 | Rd = Rn - Op2 | |
| RSB Rd, Rn, Op2 | Rd = Op2 - Rn (reverse subtract) | |
| MUL Rd, Rm, Rs | Rd = Rm × Rs | |
| AND Rd, Rn, Op2 | Rd = Rn & Op2 | |
| ORR Rd, Rn, Op2 | Rd = Rn | Op2 |
| EOR Rd, Rn, Op2 | Rd = Rn ^ Op2 | |
| BIC Rd, Rn, Op2 | Rd = Rn & ~Op2 (bit clear) | |
| CMP Rn, Op2 | Rn - Op2 (setează flags) | Nu stochează rezultat |
| TST Rn, Op2 | Rn & Op2 (setează flags) | Nu stochează rezultat |
| LDR Rd, [Rn, offset] | Rd = Mem[Rn + offset] | Load word |
| STR Rd, [Rn, offset] | Mem[Rn + offset] = Rd | Store word |
| LDRB / STRB | Load/Store byte | |
| LDM / STM | Load/Store Multiple | Block transfer |
| PUSH | Push registre pe stivă | = STMDB SP!, |
| POP | Pop registre de pe stivă | = LDMIA SP!, |
| B target | Branch (salt necondiționat) | |
| BL target | Branch with Link (apel funcție) | LR = adresa de retur |
| BX Rm | Branch and Exchange (ARM↔Thumb) | Tipic: BX LR = return |
| SVC #imm | Supervisor Call (apel sistem) | |
| NOP | No operation |
Curs realizat ca material de referință pentru studenții de calculatoare, electronică și sisteme embedded.