Una breve introduzione sul linguaggio assemblativo e le caratteristiche principali da conoscere per affrontare semplici programmi
Indice dei contenuti
Il linguaggio Assembly
Sappiamo bene che il linguaggio del computer e quindi di qualsiasi processore è il bit. Le cpu elaborano sequenze di bit tipo 100110101 scritte in stringhe di determinata lunghezza. Ovviamente per un programmatore è impossibile scrivere in binario anche sapendo che dietro una porzione di quella stringa c’è un comando piuttosto che un dato o un indirizzo di memoria.
100110101 ad esempio potrebbe significare una istruzione SOMMA
Occorre una semplificazione in un linguaggio che non sia il binario ma che sia semplificato per interagire e comandare le operazioni da far eseguire alla CPU. Da qui la creazione dei linguaggi assemblativi, set di comandi molto a basso livello per interagire direttamente con la CPU e la sua struttura logica hardware direttamente per sfruttarne le caratteristiche e le operazioni per sviluppare applicazioni. Ogni linguaggio assembler quindi è scritto per sfruttare una CPU e le sue caratteristiche architetturali e non sono propriamente interscambiabili. Ogni CPU ha una sua struttura, un set di istruzioni hardware più o meno evoluto che altre CPU non hanno o per motivi progettuali sono organizzate in modi differenti. Quello che sappiamo è che esistono tante architetture come CISC, RISC, MIPS ognuna con le sue specificità e quindi un linguaggio assembler su misura. Sappiamo anche che l’architettura x86 è la più longeva ed è quella che in certa misura è ancora presente dalla prima delle sue cpu, la x8086. Per questo vediamo l’assembly x8086, uno perché è il più istruttivo, due perché è semplificato come semplice era l’architettura della CPU x8086 e tre, fondamentalmente le CPU attuali, seppur con doverose differenze, mantengono una retro-compatibilità con questo assembly. In parallelo, dove possibile, confrontiamo le stesse istruzioni con assembly x86 a 32bit (detta IA-32) per dare un approccio più moderno e contemporaneo alle architetture in commercio ai giorni nostri.
Come è tradotto
Come detto, la cpu interpreta le sequenze di bit che le sono date in pasto. Da qui la necessità di trasformare ciò che scriviamo con assembly in quello che è comprensibile alla cpu.
File sorgente -> Assemblatore (Assembler) -> File oggetto -> (Linker altri oggetti se presenti) -> Eseguibile (.EXE)
Questo è quello che succede un po’ come visto con i linguaggi di alto livello come il C/C++ o Cobol, Pascal. Questi linguaggi superano l’imbarazzante difficoltà di interagire direttamente con la struttura della CPU e permettono di programmare e eseguire le istruzioni ad un livello più alto, molto similmente a quello che è il modo di ragionare algoritmico e matematico. La cosa non ci spaventa: i compilatori dei linguaggi ad alto livello, altro non fanno che tradurre il linguaggio in istruzioni assembly specifiche per la cpu su cui vengono compilati e quindi assemblati in file oggetto prima ed eseguibili. Gli assemblatori sono molti. I più noti: NASM, GAS, MASM, FASM. MASM gira solo Windows e non supporta per licenza software open source, mentre gli altri supportano tutte le piattaforme. Uno vale l’alto? NI, nel senso che differiscono per lo stile di alcune istruzioni, soprattutto nell’intestazione del software.
Come usarlo
Teoricamente, per sviluppare una applicazione in assembly non occorrono software complicati o ambienti di sviluppo come Visual Studio per il C# o il più modesto DEV Cplusplus per C/C++. Basterebbe un semplice blocco note o editor di testo da assemblare poi con il programma NASM. Per questo si rimanda ad altra guida. Per scopi didattici e per un debug più agevole però è meglio avvalersi di un IDE o ambiente di sviluppo con molti strumenti ed anche un simulatore per lanciare i nostri eseguibili progettati per x8086 ma che dovrebbero invece girare su una cpu moderna come Intel o Amd che sono compatibili visto che sono dette x86-64. Nel nostro caso, abbiamo scelto l’IDE EMU8086, software proprietario a pagamento ma molto completo e potente per vedere in tempo reale l’esecuzione degli opcode, registri e flag. Ne esistono altri come WinAsm per Windows o testuali come TASM o SASM, che è un IDE leggerissimo, open-source e cross-platform che permette di fare debug sui registri a 32 e 64 bit di Intel.
Le memorie del pc
Conosciamo molto bene le memorie del pc e siamo abituati a leggere delle loro dimensioni sui volantini delle catene commerciali di elettronica e d informatica. Poche volte però abbiamo sentito parlare della loro gerarchia. L’hard disk, detto memoria di massa, è lo spazio di memoria più capiente, oggi dell’ordine di 500 GigaByte, 1TeraByte e superiori. E’ un componente meccanico se si escludono i dischi di nuova generazione a Stato Solido, quindi con limiti fisici che implicano una lentezza nel reperimento e trasferimento dei bit ovvero dei dati da e verso di esso. A seguire nella gerarchia ci sono le RAM, memorie elettroniche quindi molto veloci nel trasferimento delle informazioni, ma costose decisamente rispetto agli hard disk meccanici: un hd da 500 GB costa intorno ai 20/30 euro nel 2017 mentre occorre la stessa cifra per prendere un banco di memoria ram di 4GB (ovviamente è un ordine di grandezza variabibile tra modelli di caratteristiche e performance differenti). Quindi con le ram sale la velocità, ma aumenta il prezzo e diminuiscono le dimensioni. All’interno della CPU esistono altre memorie che forse abbiamo sentito. Una velocissima memoria generica detta Cache, dell’ordine dei 2/3 MegaByte che non è acquistabile separatamente alla CPU ma che ne determina il prezzo e le performance. Sempre nella CPU ci sono i registri, velocissimi spazi di memoria dove la CPU esegue di fatto le operazioni elementari ma hanno una dimensione limitatissima di 16,32,64bit soltanto e ne sono in genere una decina o poco più su una CPU!
I registri
Per poter scrivere un programmino assembly dobbiamo conoscere prima di tutto la struttura della CPU Intel x8086. Sappiamo che è il primo processore a 16 bit di Intel (1978). Era un’evoluzione dell’Intel 8085 a 8 bit. Questo è un numero significativo poiché ci indica che i registri hanno una capienza di 16 bit.

I registri del x8086 sono 14, i più interessanti per le nostre applicazioni sono 4, detti General Purpose o generici in italiano. Sono quelli utilizzati per eseguire le operazioni elementari anche se, come vedremo più avanti, ognuno di loro ha una serie di utilizzi predefiniti. Sono chiama AX, BX, CX, DX e sono divisi e quindi utilizzabili in due parti dette alta o high e basso o low ognuna di 8 bit
AX -> AH e AL
BX -> BH e BL
CX ->CH e CL
DX ->DH e DL
gli altri registri hanno funzioni specifiche e li approfondiremo in altre circostanze. Per completezza: SP, BP, SI, DI, DS, ES, SS, CS, IP ognuno ovviamente di 16 bit non suddiviso o accedibile in parte alta o bassa.
Salendo di complessità nelle versioni x86 successive, i registri sono rimasti sostanzialmente gli stessi, cambiando denominazione con una E o una R a seconda della versione a 32bit o 64bit. Gli assemblatori sono comunque “istruiti” a riconoscere in modo retroattivo anche le denominazioni precedenti ma occhio ad usare la capienza giusta di bit per non mandare in overflow la capienza stessa del registro. Di seguito una tabellina che riassume le evoluzioni dei nomi per i registri significativi con i quattro generici nella parte di sinistra. Sono generici ma il loro utilizzo è spesso annoverato alla funzione segnata in tabella: A per accumulatore, C per contatore, D per i dati, B come base. E’ una distinzione doverosa ma non incide in maniera decisiva sulle nostre esercitazioni se non come buone prassi.
Capienza | Accumulatore | Contatore | Dati | Base | Stack Pointer | Stack Base Pointer | Source | Destinazione | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
64-bit | RAX | RCX | RDX | RBX | RSP | RBP | RSI | RDI | ||||||||||||
32-bit | EAX | ECX | EDX | EBX | ESP | EBP | ESI | EDI | ||||||||||||
16-bit | AX | CX | DX | BX | SP | BP | SI | DI | ||||||||||||
8-bit | AH | AL | CH | CL | DH | DL | BH | BL | SPL | BPL | SIL | DIL |
L’architettura Intel
Spendiamo qualche riga per spiegare l’architettura Intel, ovviamente semplificata per i nostri scopi didattici. Online potete trovare gli schemi dei processori moderni e decisamente non è facile leggerli in ogni loro componente. Si noti la seguente immagine sotto riportata:
Ultima modifica 19 Febbraio 2022