|
 |
Gli strumenti di Lavoro |
ASSEMBLATORE 2/17
[21 di 87] |
|
Aggiornato 18 settembre 2006 |
 |
|
Come l'Assemblatore traduce il codice Sorgente? |
|
 | Il programma assemblatore (detto anche
semplicemente assembler) è un oggetto
fondamentale ed indispensabile; nulla può essere verificato senza la sua
disponibilità. |
 | Il compito principale dell’assemblatore è quello di
tradurre il testo Ascii
(codice sorgente,
ASM, o codice assembly) che abbiamo appena prodotto in ambiente
Editor
nel corrispondente codice
oggetto, OBJ,
analizzando con cura le
singole parole per controllarne forma e sostanza (sintassi). |
 | Poiché è impensabile che il programmatore
sviluppi un applicativo in codice macchina,
ogni microprocessore deve avere un
traduttore adatto a trasformare le stringhe mnemoniche
dell'assembly nei corrispondenti
codici macchina. |
 |
Questa premessa è una buona occasione
per distinguere, una volta per tutte, 2 parole spesso erroneamente
utilizzate in modo intercambiabile; non si tratta di un peccato
particolarmente grave, ma vale la pena distinguere:
 |
assembly
è l'insieme
delle stringhe mnemoniche utilizzate nel programma sorgente
per creare il programma ASM: in concreto
è il linguaggio
usato per programmare i processori e, per questa caratteristica
essenziale, è detto anche
a basso livello. |
 |
assembler
è invece lo
strumento
che consente di tradurre le
istruzioni assembly
nel
codice macchina
cioè nella sequenza di numeri binari (bytes) da dare in pasto al
processore; si tratta dunque di un programma eseguibile, detto
appunto
assemblatore
per questa sua specificità. |
|
 | Naturalmente ogni processore parla un dialetto
diverso dai suoi simili: tutti comunicano con gli stessi numeri esadecimali compresi tra
00H e
FFH (bytes) ma quasi certamente al medesimo numero fanno corrispondere una
funzione diversa. |
 | Una vera e propria Babele informatica che
molto frequentemente si manifesta anche in piccole, ma fastidiose,
differenze tra gli mnemonici, anche a livello assembly.
|
 |
Il
programmatore non ama cambiare processore: dopo aver imparato
con golosità le stringhe mnemoniche del suo linguaggio assembly, le usa con
accanimento, le gira e le rigira...
Quando è costretto a cambiare, guarda con sospetto le nuove stringhe,
le confronta con le precedenti e maledice intimamente le differenze,
ma alla fine, con molta riluttanza, le accetta rassegnato... e tutto
continua come prima. |
 | Il meccanismo usato da un assemblatore per
tradurre le stringhe in numeri è piuttosto semplice e ripetitivo; esso
contiene delle tabelle di conversione,
con tutte le "paroline" (mnemonici)
da una parte e i numeri esadecimali
corrispondenti (codici operativi) dall'altra. |
 | Esso analizza le righe di codice di ogni stringa
mnemonica (istruzione,
codice assembly) associa loro una sequenza di bytes (codice
macchina) e un numero progressivo a 16 bit (indirizzo
corrente); quest'ultimo serve per localizzare con certezza
l'inizio di determinati gruppi di istruzioni
a cui il programma desidera riferirsi (o saltando
in quel punto o chiamando il gruppo). |
 | Tutto ciò è chiaramente visibile facendo
analizzare il nostro programma da Debug. |
 | L'esempio
di codice assembly proposto
come fil rouge pur essendo molto
semplice può aiutare a capire il funzionamento dell'assemblatore;
lo riportiamo per comodità qui di seguito: |
_prog
SEGMENT BYTE PUBLIC 'CODE'
ASSUME CS:_prog,DS:_prog
ORG
0100H
INIZIO: JMP Main
KeyWait:MOV
AH,00H ;Aspetta la pressione
INT
16H ;di un tasto
RET
;
BiosCls:MOV AH,00H ;
MOV
AL,03H ;Pulisci lo schermo
INT
10H ;(ClearScreen)
RET
;
Main: CALL BiosCls ;
CALL KeyWait
;Chiama le procedure
MOV
AH,4CH ;e poi torna al dos
INT
21H ;
_prog ENDS
END INIZIO |
 | Notiamo subito che
non ci sono
riferimenti irrisolti, cioè tutte le
etichette coinvolte come operando nelle
istruzioni (presenti a destra dei rispettivi
mnemonici, nell'esempio JMP
e
CALL)
sono definite dentro il codice ASM, come
previsto, nella prima colonna a sinistra (vedremo che, in caso contrario,
sarà necessario coinvolgere la direttiva
EXTRN
per assicurare la compilabilità del programma). |
 | In ogni caso l'indirizzo iniziale dal quale comincia la compilazione è di norma
indicato dalla direttiva
ORG:
nel nostro esempio (ORG
0100H) il primo byte della
prima istruzione sarà dunque associato all'indirizzo
0100H
(pari a 256, in decimale, sebbene convenga abituarsi a considerare
irrilevante questa precisazione), valore con cui sarà caricato anche il
contatore di programma, cioè il
registro IP (Instruction
Pointer) con cui la CPU punterà le istruzioni da eseguire. Vediamo che cosa succede:
 | prima istruzione
(INIZIO: JMP Main):
 | l'assemblatore riconosce
la parola INIZIO
come etichetta
e le associa subito il valore corrente del contatore (0100H). |
 | la parola che segue (JMP,
salto) deve essere
contenuta nella
tabella
di conversione: naturalmente la trova e le associa subito il numero (codice operativo) E9H
o EBH di solito ad essa associato |
 | poiché sa che la parola JMP corrisponde ad un'istruzione che ha bisogno di un operando
che esprime un indirizzo, annota la parola che segue (Main)
e poiché non conosce ancora il numero esadecimale a 16 bit che le
corrisponde (deve ancora trovare l'etichetta
con il
medesimo nome) la traduce momentaneamente con i 2 bytes 00H,
00H; l'istruzione
è comunque codificata con 3 bytes
|
 | come
decritto qui in dettaglio,
è curioso osservare come l'assemblatore sia in grado di capire (prima di
ultimare il suo lavoro) se si tratta di un salto
vicino (NEAR, nel range 32767 in avanti e 32768 indietro) o
corto (SHORT, nel range 127 in avanti e
128 indietro) |
 | nel primo caso
l'istruzione è codificata con codice operativo E9H
seguito da una word con segno |
 | nel secondo caso
l'istruzione è codificata con codice operativo EBH
seguito da un byte con segno; poichè il terzo
bytes previsto dal codice mnemonico non è necessario, l'assemblatore lo
codifica con 90H,
NOP. |
 | in entrambi i casi sia
la word che il byte
esprimono il numero di locazioni da saltare in avanti o indietro per
raggiungere la locazione associata all'etichetta
(Main,
nel nostro caso) |
 | poiché sa che l'istruzione JMP Main
è comunque codificata con 3 bytes (E9H,
loH,
hiH o EBH,
xyH,
90H) aggiorna il
contatore portandolo
al valore 0103H (appunto 0100H + 3) |
|
 | seconda
istruzione (KeyWait: MOV AH,00H ;Aspetta la pressione):
 | la parola KeyWait
è riconosciuta come etichetta:
l'assemblatore le associa subito il numero corrente del
contatore (0103H). |
 | la parola che segue (MOV)
viene trovata in tabella; l'assemblatore capisce che si tratta di
un'istruzione che ha diverse possibilità di codifica e stabilisce il
corretto codice operativo in funzione
dei 2 operandi che seguono: se le parole che seguono
sono AH,00H
il codice operativo che corrisponde alla
stringa è B4H e il secondo operando è
numerico, il byte 00H. |
 | nella analisi della riga di istruzione
trova
;Aspetta la pressione,
ma non cerca di interpretarne il significato perchè è istruito ad
ignorare qualunque stringa Ascii successiva a ;
(punto e virgola),
ritenendola un commento. |
 | poiché sa che l'istruzione
MOV AH,00H
è esprimibile con 2 bytes (B4H,
00H) aggiorna il
contatore
portandolo
al valore 0105H (appunto 0103H + 2).
|
|
 | Tutte le altre
istruzioni sono trattate con la medesima filosofia. |
|
 |
L'assemblatore
si comporta dunque in modo esattamente duale rispetto
al processore che rappresenta: quando sarà chiamata a leggere,
interpretare ed eseguire il codice macchina finale, la CPU farà le
stesse cose:
 |
terrà
aggiornato un
contatore (il suo registro IP, Instruction
Pointer) per puntare
di volta in volta i bytes che esprimono un'istruzione |
 |
capirà
dal codice operativo quanti (eventuali) bytes operandi
dovranno essere interpretati dopo di esso, come dovranno
essere coinvolti nell'esecuzione e di quanti bytes dovrà
essere aumentato il contatore d'istruzione. |
|
©
2001-2010 - Studio Tecnico
ing. Giorgio OBER
Tutti i diritti sono riservati
|