3/4
🔌 Hardware

Microprocesoare

Lecția 3 ⏱ 80 min

Curs de Microprocesoare

Cu exemple de cod ARM și x86 Assembly


Cuprins

  1. Introducere în microprocesoare
  2. Arhitectura von Neumann și Harvard
  3. Structura internă a unui microprocesor
  4. Setul de instrucțiuni (ISA)
  5. Moduri de adresare
  6. Arhitectura x86 — registre și organizare
  7. Programare în Assembly x86
  8. Arhitectura ARM — registre și organizare
  9. Programare în Assembly ARM
  10. Sistemul de memorie și ierarhia cache
  11. Pipeline și tehnici de execuție
  12. Sistemul de întreruperi
  13. Interfațarea cu perifericele (I/O)
  14. Exemple comparative ARM vs. x86
  15. 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:

  1. Fetch: microprocesorul citește instrucțiunea de la adresa indicată de Program Counter (PC)
  2. Decode: unitatea de decodificare interpretează codul instrucțiunii și determină operația, operanzii și modul de adresare
  3. 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

  1. CPU termină instrucțiunea curentă
  2. Salvează starea (PC, registrul de stare/FLAGS) pe stivă
  3. Identifică sursa întreruperii (vectorul de întrerupere)
  4. Încarcă adresa handler-ului din tabela de vectori
  5. Execută handler-ul (ISR — Interrupt Service Routine)
  6. 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.

Pe această pagină