L’ISA — Instruction Set Architecture
L’ISA (Instruction Set Architecture) è l’insieme completo di istruzioni che un processore è in grado di eseguire — è il “vocabolario” della CPU. L’ISA dell’8086 definisce le istruzioni, i formati, i modi di indirizzamento e il comportamento dei flag: è il contratto tra il software e l’hardware.
I flag — il registro di stato FLAGS
Molte istruzioni non restituiscono il risultato solo in un registro — modificano anche i flag nel registro FLAGS. I flag sono bit singoli che memorizzano informazioni sul risultato dell’ultima operazione e guidano i salti condizionati.
| Flag | Nome | Si attiva quando… | Usato da |
|---|---|---|---|
ZF | Zero Flag | Il risultato è zero (o i due operandi di CMP sono uguali) | JE, JNE, LOOPE, LOOPNE |
SF | Sign Flag | Il bit più significativo del risultato è 1 (risultato negativo in complemento a 2) | JG, JL, JGE, JLE |
CF | Carry Flag | C’è riporto/prestito fuori dal bit più significativo (overflow senza segno) | JA, JB, ADC, SBB |
OF | Overflow Flag | Il risultato non entra nel tipo con segno (overflow con segno) | JO, JNO |
PF | Parity Flag | Il numero di bit a 1 nel risultato è pari | Controllo parità nelle comunicazioni seriali |
Istruzioni di trasferimento dati
MOV — copia di dati
MOV è l’istruzione più usata dell’ISA — copia il valore del secondo operando nel primo. Non è uno spostamento: la sorgente rimane invariata. Non modifica i flag.
; MOV destinazione, sorgente
MOV AL, 34d ; AL ← 34 decimale (0x22) — immediato
MOV AX, BX ; AX ← BX — registro a registro
MOV AX, [6Ah] ; AX ← Mem[DS:6Ah] — da memoria
MOV [0200h], BX ; Mem[DS:0200h] ← BX — in memoriaPUSH e POP — gestione dello stack
PUSH inserisce un valore a 16 bit in cima allo stack decrementando SP di 2, POP preleva il valore dalla cima incrementando SP di 2. Lo stack cresce verso il basso in memoria.
PUSH AX ; SP ← SP - 2 · Mem[SS:SP] ← AX
PUSH BX ; salva BX sopra AX
POP BX ; BX ← Mem[SS:SP] · SP ← SP + 2 (LIFO!)
POP AX ; AX ripristinatoXCHG e LEA
XCHG AX, BX ; scambia AX e BX senza registro temporaneo
LEA SI, [array] ; SI ← indirizzo di 'array', NON il suo valore
LEA DI, 04h[BX] ; DI ← BX + 4 (indirizzo effettivo calcolato)MOV SI, [array] carica il dato contenuto in array. LEA SI, [array] carica l’indirizzo di array — equivale a &array in C. Indispensabile per passare puntatori a stringhe o buffer alle routine di I/O.Istruzioni aritmetiche
ADD e SUB — addizione e sottrazione
MOV AL, 34d ; AL = 0x22
ADD AL, 5d ; AL = AL + 5 = 39d = 0x27
SUB AL, 10d ; AL = 39 - 10 = 29d = 0x1D256 - |risultato| per operandi a 8 bit.SUB AL, 50d ; AL era 29d → 29 - 50 = -21d
; -21 in complemento a 2 su 8 bit:
; 21d = 0001 0101
; NOT = 1110 1010 (complemento a 1)
; + 1 = 1110 1011 (complemento a 2) = EBh
; AL conterrà EBh — SF=1 (bit7=1 → numero negativo)ADC e SBB — con riporto e prestito
Per somme e sottrazioni su operandi più grandi del registro (es. 32 bit su una CPU a 16 bit), si usano ADC e SBB che includono il valore del CF dell’operazione precedente.
; somma a 32 bit: (DX:AX) + (CX:BX) → risultato in (DX:AX)
ADD AX, BX ; somma i 16 bit bassi — può generare carry
ADC DX, CX ; somma i 16 bit alti + eventuale carry dal passo precedenteMUL — moltiplicazione
MUL moltiplica sempre usando AL o AX come operando implicito. Il risultato è sempre il doppio della dimensione degli operandi — per evitare overflow.
MOV AL, 50d ; AL = moltiplicando
MOV BL, 10d ; BL = moltiplicatore
MUL BL ; AX ← AL × BL
; AX = 500d = 0x01F4MOV AX, 1000d
MOV BX, 500d
MUL BX ; DX:AX ← AX × BX
; DX:AX = 500000d = 0x7A120
; DX=0007h · AX=A120hDIV — divisione
DIV funziona in modo speculare a MUL — il dividendo è sempre il doppio della dimensione del divisore, e il risultato viene suddiviso tra quoziente e resto.
MOV AX, 100d ; dividendo in AX
MOV BL, 30d ; divisore in BL
DIV BL
; AL = quoziente = 3d (0x03)
; AH = resto = 10d (0x0A)MOV DX, 1h ; parte alta dividendo
MOV AX, 86A0h ; parte bassa (DX:AX=100000d)
MOV BX, 100d
DIV BX
; AX = 1000d = 0x03E8
; DX = 0 (resto)INC, DEC, NEG, CMP
INC AL ; AL = AL + 1 (non modifica CF — utile nei cicli)
DEC CX ; CX = CX - 1
NEG AL ; AL = -AL (complemento a 2 di AL)
; CMP sottrae senza salvare il risultato — aggiorna solo i flag
MOV AL, 50d
MOV BL, 10d
CMP BL, AL ; BL - AL = 10 - 50 = -40 → CF=1, SF=1, ZF=0CMP op1, op2 esegue internamente op1 - op2 ma scarta il risultato — aggiorna solo ZF, SF, CF e OF. Se op1 = op2: ZF=1. Se op1 < op2: CF=1, SF=1. Se op1 > op2: tutti a 0. Sempre seguita da un’istruzione di salto condizionato.Istruzioni logiche e di shift
Operazioni bitwise — AND, OR, XOR, NOT
Le istruzioni logiche operano bit per bit tra i due operandi. Sono fondamentali per gestire flag di stato, maschere di bit e operazioni su singoli bit di un registro.
MOV AL, 0b11001010 ; AL = 0xCA
AND AL, 0b00001111 ; maschera nibble basso
; AL = 0b00001010 = 0x0A
; I bit alti sono stati azzeratiMOV AL, 0b10110000 ; AL = 0xB0
OR AL, 0b00000111 ; forza i 3 bit bassi a 1
; AL = 0b10110111 = 0xB7XOR AX, AX ; AX = 0 (idioma classico, più veloce di MOV AX,0)
XOR AL, 0FFh ; inverte tutti i bit di AL (complemento a 1)MOV AL, 0b10110001
NOT AL
; AL = 0b01001110 (ogni bit invertito)SHL, SHR, ROL, ROR — shift e rotazione
Lo shift sposta i bit verso sinistra o destra — i bit che escono da un lato vengono persi (o vanno nel CF). La rotazione sposta i bit ma quelli che “escono” da un lato rientrano dall’altro.
MOV AL, 5d ; AL = 0000 0101
SHL AL, 1 ; AL = 0000 1010 = 10d (×2)
SHL AL, 2 ; AL = 0010 1000 = 40d (×4)
; SHL di N = moltiplicazione per 2ⁿMOV AL, 40d ; AL = 0010 1000
SHR AL, 1 ; AL = 0001 0100 = 20d (÷2)
SHR AL, 2 ; AL = 0000 0101 = 5d (÷4)
; SHR di N = divisione intera per 2ⁿ; ROL — rotazione sinistra: il bit uscente a sinistra rientra a destra
MOV AL, 10110001b
ROL AL, 1 ; AL = 01100011b (il bit7=1 è rientrato come bit0)
; ROR — rotazione destra: il bit uscente a destra rientra a sinistra
MOV AL, 10110001b
ROR AL, 1 ; AL = 11011000b (il bit0=1 è rientrato come bit7)SHL AL, 3 è equivalente a AL × 8 ma richiede 1 ciclo di clock invece dei molti cicli di MUL. Nei loop ad alte prestazioni, moltiplicazioni e divisioni per potenze di 2 si esprimono sempre con shift.Controllo del flusso — salti e cicli
JMP — salto incondizionato
JMP carica una nuova label nell’instruction pointer — tutte le istruzioni tra JMP e la label vengono saltate e non eseguite.
MOV AL, 100d
MOV BL, 30d
JMP fine ; salta direttamente a 'fine'
MOV AL, 35d ; NON eseguita
MOV BL, 40d ; NON eseguita
fine:
MOV AL, 10d ; esecuzione riprende da quiCMP + salti condizionati — struttura if/else
La coppia CMP + istruzione di salto condizionato è il mattone base di ogni struttura di selezione in Assembly. CMP imposta i flag, il salto li legge.
MOV AL, 50d
MOV BL, 30d
CMP AL, BL ; AL - BL → imposta flag
JG ramo_vero ; salta se AL > BL (con segno)
; --- ramo_falso ---
MOV CX, 0d ; AL ≤ BL
JMP fine_if
ramo_vero:
MOV CX, 1d ; AL > BL
fine_if:Tabella dei salti condizionati
| Istruzione | Condizione | Flag controllati | Interpretazione |
|---|---|---|---|
JE / JZ | op1 = op2 | ZF = 1 | Uguale / Zero |
JNE / JNZ | op1 ≠ op2 | ZF = 0 | Diverso / Non zero |
JG | op1 > op2 | ZF=0 e SF=OF | Maggiore — con segno |
JA | op1 > op2 | CF=0 e ZF=0 | Maggiore — senza segno |
JL | op1 < op2 | SF ≠ OF | Minore — con segno |
JB | op1 < op2 | CF = 1 | Minore — senza segno |
JGE / JAE | op1 ≥ op2 | SF=OF / CF=0 | Maggiore o uguale (con/senza segno) |
JLE / JBE | op1 ≤ op2 | ZF=1 o SF≠OF / CF=1 o ZF=1 | Minore o uguale (con/senza segno) |
JNG / JNL ecc. | Negazione | Negazione del corrispondente | Prefisso JN nega l’istruzione base |
MOV AL, 10d ; AL = 0x0A
MOV BL, -30d ; BL = 0xE2 (226 senza segno / -30 con segno)
CMP AL, BL
; Con segno (JG/JL): AL=10 > BL=-30 → JG salta ✓
; Senza segno (JA/JB): AL=10 < BL=226 → JA NON saltaLOOP — cicli con contatore
LOOP è l’istruzione dedicata ai cicli a contatore. Usa CX come contatore implicito: decrementa CX e salta alla label se CX ≠ 0. Equivale a un ciclo for con CX iterazioni.
; somma 1+2+3+4+5 usando LOOP
MOV CX, 5d ; CX = contatore — eseguirò 5 iterazioni
MOV AX, 0d ; AX = accumulatore
ciclo:
ADD AX, CX ; AX = AX + CX (5, poi 4, poi 3, poi 2, poi 1)
LOOP ciclo ; CX-- · se CX≠0 → salta a 'ciclo'
; AX = 15d (1+2+3+4+5) · CX = 0CX ≠ 0 dopo decrementoCX ≠ 0 e ZF = 1 — preceduta da CMPCX ≠ 0 e ZF = 0 — preceduta da CMPCALL e RET — subroutine
CALL mia_funzione ; salva IP nello stack · salta a mia_funzione
; ← esecuzione riprende qui dopo il RET
MOV BX, AX ; usa il valore restituito dalla funzione
...
mia_funzione:
MOV AX, 42d ; convenzione: valore di ritorno in AX
RET ; recupera IP dallo stack · torna al chiamanteInterrupt e I/O
I programmi Assembly devono interagire con tastiera, schermo e altri dispositivi. In x86 questo avviene attraverso il meccanismo degli interrupt software.
INT N — N è il numero dell’interrupt (0–255)0000:0000h — ogni entry occupa 4 byte (2 per IP, 2 per CS) → handler N si trova all’offset N×4IRET ripristina IP e FLAGS dallo stack — il programma riprende esattamente dove era stato interrottoINT 21h — i servizi DOS
L’interrupt più usato nei programmi DOS è INT 21h (il dispatcher dei servizi di sistema). Il servizio specifico da eseguire si seleziona caricando un codice in AH prima di chiamare l’interrupt.
| AH | Servizio | Input | Output |
|---|---|---|---|
01h | Leggi carattere da tastiera con echo | — | AL = codice ASCII del tasto |
02h | Scrivi carattere a video | DL = codice ASCII | — |
09h | Stampa stringa a video | DS:DX = indirizzo stringa terminata da $ | — |
0Ah | Leggi stringa da tastiera con buffer | DS:DX = indirizzo buffer | Buffer riempito con la stringa |
4Ch | Termina programma | AL = codice di uscita | — |
; esempio completo: leggi un carattere e stampalo a video
MOV AH, 01h ; servizio: leggi carattere da tastiera
INT 21h ; AL ← codice ASCII del tasto premuto
MOV DL, AL ; preparo il carattere da stampare
MOV AH, 02h ; servizio: scrivi carattere a video
INT 21h ; stampa il carattere in DL
MOV AH, 4Ch ; servizio: termina programma
MOV AL, 0 ; codice di uscita = 0 (OK)
INT 21h; stampa una stringa — il messaggio deve terminare con '$'
msg DB 'Ciao, mondo!', '$'
MOV AH, 09h ; servizio: stampa stringa
LEA DX, msg ; DX ← indirizzo di 'msg'
INT 21h ; stampa fino al carattere '$'Riepilogo — quadro delle istruzioni ISA 8086
| Categoria | Istruzioni principali | Nota chiave |
|---|---|---|
| Trasferimento | MOV PUSH POP XCHG LEA | MOV non modifica i flag · LEA carica indirizzi non valori |
| Aritmetica | ADD ADC SUB SBB MUL DIV INC DEC NEG CMP | MUL: AL×op→AX · AX×op→DX:AX · CMP scarta il risultato |
| Logica | AND OR XOR NOT | XOR reg,reg azzera il registro · AND per maschere |
| Shift/Rotazione | SHL SHR ROL ROR | SHL/SHR: ×/÷ per potenze di 2 in 1 ciclo |
| Salto incond. | JMP CALL RET | CALL salva IP nello stack · RET lo recupera |
| Salto condiz. | JE JNE JG JA JL JB JGE JLE… | J* con segno (JG/JL) · J* senza segno (JA/JB) |
| Cicli | LOOP LOOPE LOOPNE | Usano CX come contatore implicito — CX– poi controlla |
| Interrupt | INT N IRET | INT 21h + AH = servizio DOS per I/O tastiera/video |