PROGRAMMAZIONE IN ASSEMBLY

Architettura dei processori Intel


Questi file sono protetti dal diritto d'autore e sono liberamente
riproducibili a condizione di non farne uso comemrciale o economico o per
fine di lucro senza la preventiva autorizzazione dell'autore. In ogni caso
deve sempre essere indicato il nome dell'autore e il suo indirizzo. Ogni
altra modalita' di utilizzo deve considerarsi contraria alla volonta'
dell'Autore.


Il primo vero microprocessore prodotto dalla Intel è l'8088 che entrò in
produzione nel Giugno del 1978 !!!
L'8088 è un processore a 16 bit con un BUS esterno a 8 bit e con un clock da
4.77MHz (8-|).
Da allora se ne' fatta di strada (basti pensare al processore Pentium a 32 bit)
ma in questo tutorial per capire a grandi linee l'architettura di un processore
Intel farò riferimento all' 8086 (non preoccupatevi tutto quello che dirò vale
anche per i nuovi processori...purtroppo)
Allora per cominciare diamo un'occhiata al disegno qui sotto che rappresenta la
struttura interna di un 8086 :

Bus Dati
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
^ ^ | ^ ^ ^ ^ | ^ ^ ^ |
| | | | | | ----- | V V V |
| | | | | || AX | | BX CX DX |
V V | V | | ----- | |
------ ------ | ------ ------ | | | ------
| SI | | DI | | | SP | | IP | | V V | IR |
------ ------ | ------ ------ | ------ ------ ------
| | | | | | \ \ / / |
| | | | | | \ \/ / |
| | | | | | \ ALU / ------ ----------
| | | | | | \------/--->|FLAG|----->|Control |
| | | | | | | ------ ----------
| | | | | ----------|
| | | | |
V V V V V
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Bus Indirizzi


Lo schema è abbastanza semplice ma analizziamolo nel dettaglio.
A sinistra ci sono i due registri indice SI (Source Index) e DI (Destination
Index) che vengono utilizzati come puntatori rispettivamente a dati sorgente e
a dati destinazione. Questi sono registri a 16 bit e possono essere utilizzati
anche come registri di OFFSET (Vedremo piu' avanti di cosa si tratta).

Il registro SP è lo Stack Pointer (Puntatore allo Stack). Lo stack è un'area di
memoria in cui si possono memorizzare dei dati da passare alle procedure (o anche
per altri scopi).
La sua struttura è di tipo LIFO (Last In First Out) cioè : l'ultimo inserito è il
primo ad essere estratto.
Vediamo meglio come funziona con un esempio:
Supponiamo di chiamare una funzione che somma due numeri, il prototipo in C
sarebbe:
int Somma (int a,int b);
Ebbene quando viene chiamata la procedura i due addendi vengono salvati nello
stack tramite l'operazione che prende il nome di PUSH , quindi:

|______|
| |
|______|
SP --> | b |
|______|
| a |
|______|

Ora la procedura preleverà i due numeri dallo Stack con l'operazione di POP
(attenzione all'ordine in cui vengono prelevati : prima b poi a !!!) ne
effettuerà la somma e riporrà il risultato nello Stack:


|______|
SP --> | ris |
|______| ris = a + b



A questo punto il programma chiamante dovrà solo prelevare il risultato dallo
stack.
In realtà quando si chiama una procedura oltre ai parametri nello stack si
salvano anche cose come ad esempio l'indirizzo della chiamata per poterci
tornare una volta finita la procedura e altri registri necessari a ripristinare
il flusso del programma.
C'è da sottolineare che il contenuto dello Stack Pointer viene aggiornato
automaticamente dalla CPU quando si eseguono le operazioni di PUSH e di POP e
non andrebbe modificato manualmente a meno di sapere bene cosa si sta facendo!!

Il registro IP (Instruction Pointer) contiene l'indirizzo della prossima
istruzione da eseguire e il registro IR (Instruction Register) tiene il codice
dell'istruzione in esecuzione, insieme eseguono il fetch dell'istruzione da
eseguire :

IR <- [[IP]] ; metto in IR il dato puntato dal contenuto di IP
IP <- [IP] + 1 ; incremento il contenuto di IP

Per chi non lo sapesse il fetch dell'istruzione consiste nel prelevamento e
riconoscimento dell'istruzione da eseguire.
Tutti i microprocessori Intel sono strutturati come una catena di montaggio
suddivisa in stadi successivi, la CPU legge i codici operativi (OPCODE) delle
istruzioni qualche ciclo di clock prima della loro reale esecuzione e sfrutta
questo intervallo per eseguire la decodifica (oltre a qualche altro calcolo
accessorio). Le istruzioni lette vengono messe nella PREFETCH QUEUE formata
appunto da tutte le istruzioni lette ma non ancora eseguite, la dimensione
della coda influisce sulla velocità (+ grande --> + veloce) anche se oltre un
certo valore non si registra alcun miglioramento.
Le dimensioni della Prefetch Queue di un 8088 erano di 4 byte, quella di un 486
è di 32 byte.
I registri IP e IR non sono direttamente accessibili dal programma, anche se IP
può essere indirettamente modificato tramite istruzioni di salto.

Ci sono poi i registri General Purpose : AX, BX, CX, DX che possono essere usati
dai nostri programmi per memorizzare dati, puntatori e risultati.

AX - E' un registro a 16 bit divisibile in due registri a 8 bit : AH, AL in
questo modo

---------------------------------
| | | | | | | | | | | | | | | | |
---------------------------------
|_____AH_______||_____AL________|
|_______________AX________________|

AX viene usato come destinazione nei calcoli matematici ( viene chiamato
accumulatore). Va notato che cambiando il valore di AH e/o AL viene anche
modificato il valore di AX !!

BX - Come AX si divide in BH e BL può essere usato come registro di OFFSET o
come registro indice.

CX - (CH/CL) 16 bit (8+8) viene spesso usato come contatore nei cicli, infatti
l'istruzione LOOP (che vedremo piu' avanti) decrementa il valore di CX fino a
quando è uguale a zero.

DX - (DH/DL) 16 bit (8+8) viene usato come contenitore per il resto nelle
divisioni o nel caso di operazioni a 32 bit per contenere la parte alta del
risultato. Inoltre viene usato come puntatore nelle operazioni di Input e
Output.

Oltre a questi registri presenti nello schema ne esistono altri:

BP - (Base Pointer 16 bit) Viene usato come puntatore alla base dello stack.


Registri di Segmento:

CS - Code Segment : punta alla zona di memoria che contiene il codice, durante
l'esecuzione del programma contiene la prossima istruzione da eseguire.
(Attenzione: non può essere modificato)

DS - Data Segment : punta alla zona di memoria adibita al contenimento dei dati

ES - Extra Segment : lo dice il nome, può essere usato come registro di segmento
ausiliario.

SS - Stack Segment : punta alla zona di memoria in cui risiede lo
stack.Attenzione a "giocare" con questo segmento!!

Per processori 386 e superiori esistono altri due registri di segmento FS e GS
che possono essere usati come supporto per le operazioni.

Esiste inoltre il registro di FLAG in cui vengono riportate alcune informazioni
circa le istruzioni in svolgimento, la sua struttura è la seguente:

_______________________________________
|11|10|F|E|D|C|B|A|9|8|7|6|5|4|3|2|1|0|
---------------------------------------
| | | | | | | | | | | | | | | | | +--- CF Carry Flag
| | | | | | | | | | | | | | | | +----- 1
| | | | | | | | | | | | | | | +------- PF Parity Flag
| | | | | | | | | | | | | | +--------- 0
| | | | | | | | | | | | | +----------- AF Auxiliary Flag
| | | | | | | | | | | | +------------- 0
| | | | | | | | | | | +--------------- ZF Zero Flag
| | | | | | | | | | +----------------- SF Sign Flag
| | | | | | | | | +------------------- TF Trap Flag (Single Step)
| | | | | | | | +--------------------- IF Interrupt Flag
| | | | | | | +----------------------- DF Direction Flag
| | | | | | +------------------------- OF Overflow flag
| | | | +----------------------------- IOPL I/O Privil. Level(286+ only)
| | | +------------------------------- NT Nested Task Flag (286+ only)
| | +--------------------------------- 0
| +----------------------------------- RF Resume Flag (386+ only)
+-------------------------------------- VM Virtual Mode Flag (386+ only)


Così se ad esempio dopo una sottrazione matematica il risultato è zero il bit
relativo allo Zero Flag (bit 6)viene settato a 1.

Nei processori 386 e superiori sono presenti registri a 32 bit (!)con le
funzionalità di quelli visti essi sono : EAX, EBX, ECX, EDX, ESI, EDI, EBP,
CR0,CR2, CR3, DR0, DR1, DR2, DR3, DR6, DR7, dove CR sta per Control Register
e DR Debug Register.
Nei programmi posso comunque usare AX che viene considerato come registro a 16
bit.
Dalla generazione 486 in poi sono stati aggiunti : TR3, TR4, TR5 (Test
Register).


SEGMENTO e OFFSET

Vi prego di prestare molta attenzione a questo argomento che ritengo di cruciale
importanza: il modo in cui la CPU vede ed accede alla memoria.
I progettisti dei processori 8088 e 8086 nati anni e anni fa non potevano
prevedere (???) che la tecnologia arrivasse a costruire macchine dotate di
decine di MegaByte di memoria e cosi si sono limitati a progettare una CPU che
riuscisse ad indirizzare solo un misero MegaByte :-(
Per indirizzare 1Mb sono necessari 20 bit (2 ^ 20 = 1Mb) e noi abbiamo a
disposizione solo registri a 16 bit, e allora come facciamo ?
La soluzione adottata dai progettisti del'Intel è quella di adottare un
indirizzo costituito da 2 parti : un SEGMENTO e un OFFSET di 16 bit ciascuno ,
che uniti nel giusto modo danno origine all'indirizzo effettivo. Vediamo un
esempio.
L'indirizzo 21F2:C01E è nella forma SEGMENT:OFFSET (gli indirizzi vengono dati
sempre in base esadecimale per facilitare i calcoli) dove il segmento vale 21F2
e l'offset C01E entrambi di 16 bit, quindi un totale di 32 bit che non sono
tutti necessari per indirizzare un solo Mega di memoria, e qui sta il bello !!!
L'indirizzo nella forma SEG:OFF viene "trattato" per dare origine al vero
indirizzo cioè il segmento viene moltiplicato per 16 (10h) e il risultato viene
sommato all'offset. Nell'esempio precedente:

21F2 * 10 = 21F20 +
C0E1 =
------
2E001

Otteniamo così un indirizzo a 20 bit !! (Semplice vero: beh forse non tanto !).
Spesso invece di moltiplicare il numero per 16 (10h) si effettua uno SHIFT a
sinistra di 4 bit ma come vedremo è la stessa cosa.

Come abbiamo visto prima i registri dedicati a contenere il valore del segmento
so CS,DS,ES e SS, sono registri a 16 bit ciò vuol dire che un segmento è lungo
64Kb.
Del Mega che si può indirizzare se ne può usare solo i primi 640Kb perchè i
rimanenti sono occupati dalla memoria video della VGA (a partire da A000:0000) e
dal BIOS e quindi non ce ne rimane molto !!!
Naturalmente esistono oggi tecniche per andare oltre il Megabyte come il
modo protetto o il metodo di indirizzamento FLAT che vedremo più avanti.

Bene anche questa puntata è terminata abbiamo conosciuto alcuni elementi
fondamentali che ci serviranno per scrivere programmi in Assembly. So che non
vedete l'ora di scriverne uno ma dovete avere un po' di pazienza: per
programmare in Assembly (non mi stancherò mai di ripeterlo) bisogna conoscere a
fondo l'architettura del computer visto che è proprio quello che andiamo a
programmare!!

Per richieste, consigli, suggerimenti, aiuti, contributi, ecc..., contattatemi

b0nu$,e-mail :bonus@numerica.it

hAck & c ya :-)

TORNA INDIETRO