Cos'è l'assembly
L'assembly è il linguaggio di programmazione di basso livello più vicino al linguaggio macchina. E' un linguaggio assemblativo che consente comunque al programmatore di scrivere dei comandi operativi in una forma comprensibile per l'uomo.
Perché imparare l'assembly? Perché è il linguaggio più vicino al linguaggio macchina comprensibile anche per l'uomo. Consente di capire meglio come funziona un programma.
E' composto da microistruzioni che operano direttamente sui registri di memoria del computer.
Un esempio pratico di codice assembly
.MODEL SMALL
.STACK 100H
.DATA
MSG1 DB 10
.CODE
.STARTUP
MOV AL, MSG1
ADD MSG1, AL
SUB AL, 1
END
Ogni istruzione assembly equivale a un'operazione della cpu. C'è un equivalenza uno a uno.
Esempio. L'istruzione MOV scrive il valore della locazione di memoria MSG1 nel registro EAX della cpu. L'istruzione ADD addiziona il valore del registro di memoria AL al contenuto della locazione di memoria MSG1 e la sovrascrive. L'istruzione SUB sottrae dal registro AL il valore numerico decimale 1. E così via. I registri di un vecchio processore 8088 sono i seguenti:
Il programma va scritto in un file di testo e non è immediatamente eseguibile dalla macchina. Per essere eseguito deve essere prima trasformato ( assemblato = compilato ) in un file eseguibile tramite un software assemblatore detto assembler.
Nota. Non esiste una sola versione di linguaggio assembly. Ne esistono diverse, una per ogni architettura del processore e per ogni sistema operativo. Hanno struttura e sintassi differenti. Quindi, non sono compatibili tra loro. Ad esempio, il codice scritto in assembly con sintassi Intel è pensato per il sistema operativo DOS e non è compatibile con le versioni assembly dei sistemi Unix e Linux/GNU.
Come funziona l'assembly
I computer che usiamo oggi si basano su un'architettura definita da Jhon Von Neumann nella metà del XX secolo.
La CPU (Central Processing Unit) è collegata con la memoria e con i dispositivi I/O.
Nella memoria sono contenuti sia i dati che i programmi.
Scendendo nel particolare, la CPU, la memoria e i dispositivi sono collegati tra loro tramite interfacce dette bus.
Sottolineo il fatto che il trasferimento dei dati tramite i bus comporta sempre un ritardo di tempo più o meno alto.
A sua volta la CPU è composta da
- unità di calcolo
coordina le attività della cpu - unità aritmetico logica (alu)
esegue le operazioni logico-matematiche - registri
è un banco di memoria locale situato in prossimità del processore (registri general purpose, program counter, stack pointer).
Quando la CPU esegue un calcolo, utilizza prevalentemente i registri.
Quindi, per svolgere un'operazione tra due dati la CPU compie questi passi
- copia i dati dalla memoria ai registri
- esegue l'operazione usando i dati nei registri
- copia il risultato dai registri alla memoria
Queste operazioni sono svolte dall'architettura ISA (Instruction Set Architecture) della CPU.
Le principali istruzioni di un'architettura ISA svolgono queste funzioni
- movimento dati tra registri e tra registri e memoria
- istruzioni aritmetico-logiche (es. x+y, x<y, ecc.)
- istruzioni di salto (es. if then else goto ecc.)
Ogni ISA utilizza un proprio Set istruzioni e una propria sintassi del linguaggio Assembly.
Esistono due distinte sintassi del linguaggio Assembly
- Assembly Intel X86
E' definito dalla Intel e usato sui sistemi operativi DOS. - Assembly AT&T
E' definito dalla AT&T e usato sulle architetture UNIX GNU (Linux).
Nota. L'assembly è un linguaggio di basso livello, tradotto in linguaggio macchina tramite un assemblatore. E' strettamente dipendente dal processore.
La differenza tra assembly e linguaggio macchina
Il linguaggio macchina è composto da sequenze di 0 e 1 (linguaggio binario) che indicano al processore le istruzioni da eseguire.
Il processore legge ed esegue le istruzioni in linguaggio macchina in modo diretto e senza doverle interpretare o tradurre.
Ad esempio, l'istruzione seguente somma il contenuto dei registri EAX e EBX mettendo il risultato nel registro EAX.
00000011 11000011
E' il progettista della CPU l'associazione tra i codici binari (opcode) e le operazioni sul processore.
Il linguaggio Assembly, invece, è una rappresentazione delle stesse istruzioni in una forma testuale più comprensibile dall'uomo.
Ad esempio, per compiere la stessa operazione di somma dei registri EAX e EBX basta scrivere in Assembly
MOV EAX, EBX
Il processore legge le istruzioni in Assembly di un programma, le traduce in linguaggio macchina durante la fase di assemblaggio tramite un programma assemblatore (assembler) e poi le scrive nel file oggetto eseguibile del programma.
Ovviamente, la traduzione da Assembly a linguaggio macchina viene effettuata solo al momento della compilazione del programma.
Il file eseguibile del programma è sempre scritto in linguaggio macchina.
In pratica, il processore non esegue il codice Assembly ma solo il linguaggio macchina.
Nota. La fase opposta dell'assemblaggio è il disassemblaggio. Nel disassemblaggio un programma detto disassembler legge il codice in linguaggio macchina e lo traduce in linguaggio Assembly. Il disassemblaggio è usato soprattutto dagli hacker.
La differenza tra assembly e assembler
Spesso i termini Assembly e Assembler sono erroneamente confusi tra loro.
Sono però due cose diverse.
- L'assembly è il linguaggio di programmazione vero e proprio.
- L'assembler è l'assemblatore, ossia il programma che traduce il codice sorgente in un file oggetto eseguibile dal computer.
Quindi, l'assembly sono le istruzioni e la sintassi del linguaggio di programmazione, mentre l'assembler è il software che lo traduce in linguaggio macchina eseguibile.
La differenza tra assembler e linker. L'assembler non crea direttamente il file eseguibile ma un file oggetto intermedio con estensione .o ( es. prova.o). I file oggetto sono collegati tra loro da un altro software, detto linker, che crea il file eseguibile vero e proprio ( es. prova.exe ).
Ad esempio, quando compilo un programma scritto in linguaggio C il compilatore compie le seguenti operazioni
- Preprocessamento
Il compilatore esegue le direttive (#define) e sostituisce i comandi di preprocessamento con il codice sorgente delle librerie (#include) o delle costanti. Il codice sorgente modificato viene salvato in un file di testo temporaneo. - Assemblaggio
Il compilatore traduce il programma scritto in linguaggio C (linguaggio di alto livello) in un programma in linguaggio Assembly (linguaggio di basso livello). Salva il codice sorgente tradotto in Assembly in un file temporaneo con estensione .s che successivamente il programma assemblatore (Assembler) traduce in un file oggetto in linguaggio macchina con estensione .o - Linker
Il compilatore collega i file oggetto del programma tra loro, creando un unico file binario eseguibile.
Cosa serve per scrivere un programma in assembly
Per sviluppare uno programma basta usare un qualsiasi editor di testo.
Un editor gratuito che permette di sviluppare anche in assembly sui PC Windows è PSDPAD.
Ce ne sono comunque molti altri tra cui scegliere.
Anche il Notepad di Windows va bene.
Come scrivere un programma in assembly
Le istruzioni vanno scritte in sequenza in un file di testo detto codice sorgente.
In ogni riga va scritta una sola istruzione.
MOV DS AX
INT 22H
ADD DS AX
Dopo averlo scritto devo assemblare il codice sorgente e trasformarlo in linguaggio macchina, ossia in una forma direttamente eseguibile dal computer, tramite il programma assemblatore ( assembler ).
Le sintassi Assembly Intel e AT&T
Esistono due sintassi del linguaggio Assembly
- Assembly Intel X86
Definito dalla Intel per i sistemi operativi DOS. - Assembly AT&T
Definito dalla AT&T per le architetture UNIX GNU.
Esempio
Nella sintassi Assembly Intel la destinazione precede la sorgente.
Per scrivere il numero 5 nel registro eax si scrive
mov eax, 5
Nella sintassi Assembly AT&T la sorgente precede la destinazione.
Quindi per scrivere il numero 5 nel registro eax scrivo.
movl $5, %eax
E' soltanto una tra le tante differenze tra le due sintassi.
Nota. Nella sintassi AT&T l'istruzione mov è una famiglia di istruzioni e richiede di specificare il tipo di dato nel suffisso (l=intero). Il valore 5 è preceduto dal simbolo $ mentre il registro eax dal simbolo %.
Quale assembler utilizzare
Per trasformare il codice assembly in un file eseguibile devo usare un programma assembler.
Ce ne sono diversi.
Linux
Sul sistema Linux un assemblatore molto utilizzato è NASM ( Netwide ASseMbler )
Windows
Per programmare in assembly 8088 si può usare tracer con DOSBox.
E' usato in ambito scolastico e universitario per studiare l'assembler sui vecchi processori 8088.
L'assembler NASM ( Netwide ASseMbler ) è disponibile anche per i sistemi operativi Dos e Windows.
Un assembler da riga comando per macchine Windows a 32 bit è MASM 86 di Microsoft. Per installarlo occorre avere già installato Visual C++ 2005 Express Edition sul PC.
Sui PC Windows più recenti si può anche usare MASM for X64. E' incluso gratuitamente in Visual Studio di Microsoft e permette di lavorare anche sulle macchine a 64 bit.2
Per lavorare in assembly sulle vecchie macchine con cpu Intel a 16bit posso usare l'assembler TASM ( Turbo Assembler ).
Nota. Il vecchio assemblatore TASM per l'architettura x86 si può usare anche sui pc più recenti usando un emulatore Dos come DosBox. TASM è ancora oggi utilizzato per finalità didattiche. Integra anche un editor e un debugger assembly che agevola la comprensione del funzionamento e l'apprendimento del linguaggio per chi parte da zero.
Inizialmente, per esercitarsi a programmare in assembly si può utilizzare anche un emulatore Nasm online.
E' un modo facile e immediato per cominciare, senza perdere tempo con la configurazione hardware e senza installare nulla sul proprio PC.
In ogni caso, qualunque strada si prenda, il mio consiglio è di passare prima possibile all'assembly di MASM X64 o NASM su macchine a 64 bit.
E' molto più utile perché quello che si impara si può anche subito applicare.