Il linguaggio assemblativo o linguaggio assembly (ellissi diffusa nel linguaggio comune: "assembly") è, tra i linguaggi di programmazione, quello più vicino al linguaggio macchina vero e proprio (pur essendo differente rispetto a quest'ultimo). Erroneamente viene spesso chiamato "assembler" anche se quest'ultimo identifica il programma "assemblatore" che converte il linguaggio assembly in linguaggio macchina.

Caratteristiche generali dell'assembly

L'assembly ha lo scopo generale di consentire al programmatore di ignorare il formato binario del linguaggio macchina. Ogni codice operativo del linguaggio macchina viene sostituito, nell'assembly, da una sequenza di caratteri che lo rappresenta in forma mnemonica; per esempio, il codice operativo per la somma potrebbe essere trascritto come ADD e quello per il salto comeJMP. In secondo luogo, i dati e gli indirizzi di memoria manipolati dal programma possono essere scritti, in assembly, nella base numerica più consona al momento: esadecimale, binaria,decimale, ottale ma anche in forma simbolica, utilizzando stringhe di testo (identificatori). Il programma assembly risulta in questo modo relativamente più leggibile di quello in linguaggio macchina, con il quale mantiene però un totale (o quasi totale) isomorfismo. Il programma scritto in assembly non può essere eseguito direttamente dal processore; esso deve essere tradotto nella forma binaria corrispondente, usando un programma compilatore detto assembler.

Non c'è un solo assembly

A causa di questa "vicinanza" all'hardware, non esiste un unico linguaggio assembly. Al contrario, ogni CPU o famiglia di CPU ha un suo proprio assembly, diverso dagli altri. Ad esempio, sono linguaggi assembly ben diversi quelli per i processori Intel x86, per i Motorola 68000 e per i Dec Alpha. Questo significa che conoscere un certo linguaggio assembly significa saper scrivere programmi solo su una determinata CPU o famiglia di CPU. Passare ad altre CPU però è relativamente facile, perché molti meccanismi sono analoghi o del tutto identici, quindi spesso il passaggio si limita all'apprendimento di nuovi codici mnemonici, nuove modalità di indirizzamento ed altre varie peculiarità del nuovo processore.
Molto meno facile è invece portare un programma scritto in assembly su macchine con processori diversi o con architetture diverse: quasi sempre significa dover riscrivere il programma da cima a fondo, perché i linguaggi assembly dipendono completamente dalla piattaforma per cui sono stati scritti. Molti compilatori assembly supportano sistemi di macro che potrebbero essere impiegati per ovviare in parte a questo problema, ma si tratta di una soluzione poco efficace.
Inoltre l'assembly non offre alcun "controllo sui tipi" (non esiste alcunché di vagamente simile al concetto di "tipo" nella programmazione low-level), ma lascia al programmatore la responsabilità di occuparsi di ogni singolo dettaglio della gestione della macchina e richiede molta disciplina e un esteso lavoro di commento per non scrivere codice che risulti assolutamente illeggibile (ad altri programmatori come anche a se stessi dopo qualche tempo).
A fronte di questi svantaggi l'assembly offre un'efficienza senza pari e il controllo completo e assoluto sull'hardware: i programmi in assembly sono, in linea di principio, i più piccoli e veloci che sia possibile scrivere su una data macchina.
Scrivere (buon) codice in assembly è dispendioso in termini di tempo, difficile e quindi molto costoso, soprattutto in prospettiva (future modifiche): per questo, raramente l'assembly è il solo linguaggio usato in un progetto mainstream, a meno che questo non sia di dimensioni e portata limitate. In genere si usa in combinazione con altri linguaggi: la maggior parte del codice viene scritta in un linguaggio ad alto livello, mentre le parti più critiche (per motivi di performance, precisione del timing o affidabilità) si scrivono in assembly.
Tali problematiche sono riscontrabili principalmente su piattaforme come i personal computer attuali, dove la vastità quantitativa e l'enorme gamma qualitativa dell'hardware disponibile crea alle applicazioni low-level un oggettivo problema mai risolto (e presumibilmente non risolvibile) a livello di unificazione e standard. A ciò si aggiunga l'evoluzione costante verso una sempre maggiore stratificazione dei comuni sistemi operativi, caratterizzata da numerosi vincoli e virtualizzazioni delle periferiche fisiche e dei canali di comunicazione, che non rendono agevole lo sviluppo di un software che interagisca direttamente con l'hardware sottostante e ne gestisca direttamente le caratteristiche.
Si possono però citare due esempi, peraltro correlati, di totale inversione di questo paradigma generale:

  • Ha ampiamente senso creare programmi interamente in assembly destinati ad hardware caratterizzato architetturalmente da documentazione esaustiva, grande predicibilità, stabilità e scarsa variabilità temporale del design: per esempio, si possono citare gli home computer degli anni ottanta, come i Commodore Vic-20 e C64 o il Sinclair ZX Spectrum.


  • Ha parimenti senso, ed un forte riscontro nella pratica invalsa negli ultimi trenta anni, operare prevalentemente o esclusivamente in assembly nel vastissimo mercato dei sistemi embedded, per la programmazione di microcontroller e DSP, eventualmente anche sotto forma di core implementati tramite ASIC, CPLD ed FPGA, al fine di massimizzare performance e rispetto dei vincoli temporali, minimizzando nel contempo il footprint. Ciò trova riscontro a tutti i livelli della filiera produttiva, a partire dalla progettazione dei chip e del relativo linguaggio utilizzando ISA di tipo RISC e fortemente ortogonali, la cui ottimizzazione (in spazio o in performance) è altamente semplificata. Questo approccio è fondamentale in quanto consente grandi economie di scala nei progetti tipici del mondo embedded, caratterizzati dalla capacità di assorbire costi iniziali (NRE, non-recurrent engineering costs) anche elevati, purché finalizzati ad una forte compressione del costo unitario del prodotto finale, anche per volumi medio-bassi.

Ecco allora che la possibilità di utilizzare un microcontroller con limitatissime risorse di memoria ROM e RAM scrivendo il firmware integralmente in assembly diventa essenziale al fine di minimizzare i costi, l'ingombro in piastra, la suscettibilità elettromagnetica, aumentando anche l'affidabilità (processori più "datati" hanno un incolmabile vantaggio in termini di milioni di ore di test e funzionamento sul campo, ossia la "merce" di gran lunga più preziosa per i sistemi embedded variamente critici) ed ottimizzando numerosi altri fattori.

FONTE: WIKI