L'assembly, tra i linguaggi di programmazione, più vicini al linguaggio macchina vero e proprio

Un assembler è un Programma che trasforma le istruzioni mnemoniche dell'assembly in linguaggio macchina.

E' come se fosse un'interprete diretto tra i comandi che scrive l'uomo e i codici che esegue la macchina. il termine ASSEMBLER deriva dal fatto che le istruzioni vengono convertite e montate una affianco all'altra comme se fossero in fila.

Per ES:
MOV AX,56h Le istruzioni in 8086 che vengono convertite
XOR SI,DI

B8 56 00 in linguaggio macchina (HEX)
33 F7

Molto spesso viene utilizzato impropriamente il termine assembler in riferimento al linguaggio assembly.

Esistono gli assembler per programmare i microcip, per creare programmini sul PC, per quelli sul cellulare, ecc..

Ci sono molti tipi di linguaggi assembly e di conseguenza diversi assemblatori. Questo perché un'assemblatore produce codice assembly per una specifica famiglia di processori. ( intel 8086, 80386, Motorola 68000, ecc... ) Se un programmatore conosce il linguaggio per un certo tipo di processore, è facile impararne un'altro perché i linguaggi si differiscono di poco.

L'assembly, o linguaggio assemblatore (spesso chiamato impropriamente assembler) è, tra i linguaggi di programmazione, quello più vicino al linguaggio macchina vero e proprio.

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 come JMP.

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 compilatore detto assembler.

Esempio di codice

Esempio di programma "Hello world"

IDEAL
MODEL SMALL
STACK 100h
DATASEG
HW DB "hello, world", 13, 10, '$'
CODESEG
Begin:
MOV AX, @data

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 sé 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 è lento, 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 il Commodore Vic-20 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.

MOV DS, AX
MOV DX, OFFSET HW
MOV AH, 09H
INT 21H
MOV AX, 4C00H
INT 21H
END Begin

RISC e CISC

Il linguaggio assembly costituisce il cosiddetto ISA (Instruction Set Architecture) di un processore. I diversi ISA possono essere divisi in due grandi gruppi: i RISC (Reduced Instruction Set Computer) e i CISC (Complex Instruction Set Computer). Il primo gruppo tende ad avere operazioni semplici e veloci, con grande abbondanza di registri per memorizzare i risultati intermedi. Il secondo mette a disposizione del programmatore istruzioni più complesse, che a volte mimano quelle dei linguaggi di livello più alto (ad esempio, la copia di stringhe nei processori x86).

In entrambi i casi, i migliori set di istruzioni tendono ad essere quelli cosiddetti ortogonali, dove i diversi metodi di indirizzamento e i diversi registri possono essere usati indifferentemente in tutte le istruzioni. Famosi set di istruzioni ortogonali sono quelli del Motorola 68000 (CISC) e del MIPS (RISC). L'ISA dei processori Intel x86 era originariamente ben poco ortogonale, ed è andata via via migliorando.

La distinzione tra set di istruzioni RISC e CISC è oggi un po' sfumata, perché la maggior parte dei processori consumer sono oggi dei CRISP, cioè un misto fra i due. Inoltre, alcuni processori traducono l'ISA originale in un set di istruzioni interno, per ragioni diverse e con modalità diverse: nel caso dell'Intel Pentium 4 e dell'AMD Athlon, è per liberarsi dalle limitazioni causate da un'ISA retrocompatibile ormai arcaica, e la conversione è eseguita direttamente da hardware dedicato che effettua la necessaria decodifica.

Nel caso dei processori Transmeta, è per poter "tradurre" ISA di altri processori esistenti come se fossero proprie, e la traduzione è fatta da qualcosa di molto simile a routine firmware (talvolta denominate microcodice) memorizzate in un'area ROM ricavata sul silicio del microprocessore.

Commenti

Post popolari in questo blog

Rilasciano BlenderBot v3, un chatbot che utilizza il loro modello OPT e aprono la demo per testarlo

Componenti aggiuntivi di Google Workspace

Come rimuovere il virus che trasforma le cartelle in collegamenti nella tua pendrive.