- Il linguaggio assembly organizza le sue istruzioni in base allo scopo: trasferimento, calcolo, controllo del flusso e altro ancora.
- Ogni istruzione è composta da un codice operativo e da operandi, con formati diversi a seconda dell'architettura.
- Esistono diverse modalità di indirizzamento per l'accesso ai dati: immediato, diretto e indiretto.
Il linguaggio assembly è stato uno dei pilastri fondamentali dello sviluppo del software fin dagli albori dell'informatica. Sebbene oggi siano i linguaggi di alto livello a dominare la scena, l'assembler rimane ampiamente utilizzato e rilevante negli ambienti in cui il controllo dell'hardware, l'ottimizzazione o i vincoli delle risorse sono una priorità.
In questo articolo, analizzeremo le tipologie di istruzioni in linguaggio assembly, esaminandone la struttura, lo scopo, le modalità di indirizzamento e l'organizzazione nelle diverse architetture. Troverete inoltre esempi, formati e una panoramica completa che vi permetterà di comprendere appieno la logica alla base di queste istruzioni, che interagiscono strettamente con l'hardware.
Cos'è un'istruzione in linguaggio assembly?
Un'istruzione assembly è un comando che la CPU può comprendere direttamente (una volta tradotto in codice macchina da un programma assembler). Ogni istruzione ha uno scopo specifico ed è solitamente composta da una sigla mnemonica che rappresenta l'operazione e da uno o più operandi che indicano dove vengono prelevati i dati e/o dove vengono memorizzati i risultati.
Queste istruzioni dipendono fortemente dall'architettura del processore, quindi ogni tipo di CPU (x86, ARM, MIPS, tra gli altri) ha il suo set di istruzioni con sintassi e capacità particolari.
Struttura di un'istruzione
Le istruzioni sono solitamente composte da due parti:
- Codice operativo (opcode): Indica l'azione da eseguire, come l'aggiunta, lo spostamento di dati, il confronto, ecc.
Gli operandi forniscono le informazioni necessarie per eseguire l'operazione e possono essere memorizzati in registri, in memoria o essere costanti. Non tutte le istruzioni richiedono operandi e, in alcuni casi, questi possono essere impliciti nell'istruzione o memorizzati in registri predefiniti.
Tipi di istruzioni nel linguaggio assembly
A seconda della funzionalità che svolgono all'interno di un programma, le istruzioni del linguaggio assembly possono essere classificate in diversi gruppi. Di seguito sono elencati quelli più rilevanti:
1. Istruzioni per il trasferimento dei dati
Queste istruzioni consentono di copiare i dati da una posizione all'altra senza modificarne il contenuto nella sorgente. Sono essenziali per trasferire informazioni tra registri, memoria e periferiche.
Alcune delle istruzioni più comuni in questo gruppo sono:
- MOVIMENTO: Copia il contenuto di un operando in un altro. Per esempio,
MOV AX, BXcopiare il contenuto di BX in AX. - MOVS / MOVSB / MOVSW: Sposta le stringhe da un indirizzo sorgente (SI) a un indirizzo di destinazione (DI), automatizzando il processo di trasferimento.
- LODS / LODSB / LODSW: Carica un valore dall'indirizzo indicato dal registro SI nell'accumulatore (AL o AX).
- LEGGERE: Carica un indirizzo effettivo anziché il valore. Utile per ottenere l'indirizzo di una variabile o di una struttura.
2. Istruzioni per la batteria
Lo stack è una struttura LIFO (ultimo entrato, primo uscito) utilizzata per memorizzare temporaneamente dati, passare parametri o salvare il contesto di esecuzione.
- SPINGERE: Memorizza un valore sullo stack, decrementando il puntatore SP.
- POP: Recupera il valore più recente memorizzato nello stack, incrementando SP.
- PUSHF / POPF: Memorizzano e recuperano lo stato dei flag della CPU sullo stack.
3. Istruzioni aritmetiche e logiche
Consentono di eseguire calcoli e operazioni sui bit. Queste istruzioni sono essenziali per manipolare i dati numerici e prendere decisioni nel flusso del programma.
Tra i più utilizzati troviamo:
- AGGIUNGI / SOTTOSCRIVI: Aggiungi e sottrai.
- MUL / IMUL: Moltiplicazioni binarie.
- DIV / IDIV: Divisione (intera o con segno).
- E / O / XOR / NON: Operazioni logiche bit a bit.
- SHL / SHR / ROL / ROR: Spostamenti e rotazioni di bit.
4. Istruzioni per il controllo del flusso
Consentono di modificare la sequenza di esecuzione del programma , introducendo salti o chiamate condizionali e incondizionali ad altre istruzioni.
- JMP: Salta incondizionatamente a un altro indirizzo nel programma.
- CHIAMATA / RIT: Chiamata a una subroutine e ritorno.
- JE/JNE/JG/JL/JZ/JNZ: Salti condizionali: vengono eseguiti se vengono soddisfatte determinate condizioni (dopo un confronto).
5. Istruzioni di input/output (I/O)
Consentono la comunicazione tra il processore e i dispositivi esterni. Queste istruzioni permettono di leggere e scrivere dati da/verso le porte (hardware).
- IN: Legge i dati da una porta di input.
- SU: Invia dati a una porta di uscita.
6. Istruzioni in virgola mobile
Molte CPU dispongono di coprocessori matematici (o istruzioni speciali) dedicati all'elaborazione di numeri decimali (a virgola mobile). Queste istruzioni consentono operazioni come:
- Addizione, sottrazione, moltiplicazione e divisione dei numeri reali.
- Operazioni trigonometriche (seno, coseno, tangente).
- Operazioni logaritmiche, esponenziali e di radice.
Queste istruzioni sono generalmente conformi allo standard IEEE 754 per la precisione numerica.
Modalità di indirizzamento
Una parte fondamentale della progettazione delle istruzioni riguarda il modo in cui viene identificata la posizione degli operandi. Le modalità di indirizzamento definiscono come la CPU accede ai dati.
1. Indirizzamento immediato
I dati sono inclusi direttamente nell'istruzione. È veloce, ma non consente di modificare i valori senza cambiare l'istruzione.
MOV AX, 5
2. Indirizzamento diretto
L'indirizzo di memoria contenente i dati viene specificato in modo esplicito.
MOV AX,
3. Indirizzamento indiretto
L'indirizzo effettivo si ottiene dal contenuto di un record. È molto flessibile e viene utilizzato per strutture e liste concatenate.
MOV AX,
4. Indirizzamento indicizzato
L'indirizzo viene calcolato sommando una base e un indice (ad esempio, per attraversare gli array). A tale scopo vengono utilizzati registri speciali come SI, DI o registri di base + offset.
MOV AX,
Formati di istruzione: diversi tipi a seconda dell'architettura
La struttura di un'istruzione può variare a seconda dell'architettura. I formati più comuni includono:
Formato a 4 vie
Comprende due operandi, una posizione per il risultato e l'indirizzo dell'istruzione successiva. È ormai obsoleto a causa della sua complessità.
Formato a 3 vie
Specifica due operandi e un indirizzo di destinazione. È comune nelle moderne architetture RISC.
Formato a 2 vie
Utilizza un operando sorgente e un operando di destinazione (anche la destinazione può contribuire con un valore). Diffuso su architetture x86 e altre architetture CISC.
Formato unidirezionale
Viene specificato un solo operando; l'altro è presupposto essere l'accumulatore. Comune nei processori più vecchi e nei sistemi semplici.
Formato indirizzo 0 (basato su stack)
Le operazioni utilizzano lo stack per prelevare e memorizzare operandi e risultati. Molto efficiente per le macchine basate su stack, come alcune calcolatrici programmabili.
Istruzioni macro e pseudo-opcode
Per migliorare la produttività, molti assembler offrono istruzioni macro , che espandono una singola riga in diverse istruzioni effettive. Queste sono utili per evitare di ripetere schemi complessi o per creare strutture di alto livello come cicli o istruzioni condizionali.
I pseudo-codici operativi Sono istruzioni che l'assemblatore traduce internamente in una vera e propria sequenza di istruzioni. Per esempio, NOP (nessuna operazione) può essere tradotto come XCHG AX, AX su CPU x86.
Direttive dell'assembler
Le direttive ( chiamate anche pseudo-operazioni ) non sono istruzioni in senso stretto. Servono a controllare il processo di assemblaggio, a definire dati, sezioni di codice, macro, condizioni di compilazione, ecc.
Esempi comuni includono:
- .dati / .codice: Indicano la sezione dati o codice.
- .org: Imposta l'indirizzo da cui verrà avviato l'assemblaggio.
- .equ: Definisce costanti simboliche.
Casi d'uso del linguaggio assembly
Nonostante la diffusione dei linguaggi di alto livello, il linguaggio assembly rimane fondamentale in diversi ambiti:
- Sviluppo di sistemi embedded e microcontrollori.
- Programmazione di driver, bootloader e BIOS.
- Ottimizzazione estrema in applicazioni quali videogiochi o crittografia.
- Reverse engineering, debugging e hacking di basso livello.
Inoltre, la formazione in linguaggio assembly occupa ancora un posto importante nei corsi di laurea in informatica ed elettronica, poiché fornisce una profonda comprensione del funzionamento di un processore, di come i dati vengono memorizzati e manipolati e di come le istruzioni vengono eseguite a livello binario.
La comprensione di tutti i tipi di istruzioni assembly, della loro struttura, funzionalità e modalità di indirizzamento fornisce una solida base per qualsiasi programmatore che voglia addentrarsi nella programmazione di basso livello o capire come i linguaggi di alto livello vengano infine tradotti in codice macchina. Sebbene il suo utilizzo sia diventato più specifico, resta uno strumento essenziale in molteplici aree critiche dello sviluppo di software e sistemi.