Più una calunnia è inverosimile, meglio la ricordano gli ostili
MICROPROCESSORI E MICROCONTROLLORI
… studiare, studiare ed ancora studiare,
è il solo modo di capire quanto possa
essere grande sia la propria ignoranza!
SET DI ISTRUZIONI DEL MICROPROCESSORE 8086-8088
Set di istruzioni
Il set di istruzioni di questo micro non è ortogonale, ovvero non
può usare indifferentemente qualsiasi registro.
Esaminiamo brevemente l’insieme delle istruzioni dell’8086/8088
che possiamo suddividere nei seguenti gruppi:
• trasferimento di dati;
• manipolazione di stringhe;
• aritmetiche e logiche;
• controllo del loop;
• test, confronti e salti;
• shift, rotate e carry;
• chiamate e ritorno;
• controllo della CPU;
• interrupt
Istruzioni di trasferimento di dati
Le istruzioni di trasferimento dei dati servono appunto a trasferire i dati dalla CPU alla
memoria e viceversa, dalla CPU agli I/O e viceversa.
Alcuni esempi di istruzioni sono:
• MOV, sposta;
• LEA, carica l’indirizzo effettivo;
• LDS, carica il segmento dati;
• SAHF, memorizza AH nei flag;
• XCHG, scambio;
• IN, input da un I/O di un byte o di una parola;
• OUT, output in un I/O.
Istruzioni di manipolazione di stringhe
Le istruzioni di questo tipo servono ad analizzare, a effettuare confronti e a muovere
velocemente interi blocchi di byte o di parole.
Con questi comandi sono particolarmente facilitate le operazioni di ricerca in tabelle o in
testi.
Una limitazione di queste istruzioni consiste nella possibilità di muovere interi blocchi di dati
solo all’interno della memoria e non verso i dispositivi di I/O.
Appartengono a questa categoria le seguenti istruzioni:
• CLD, azzeramento del flag di direzione;
• LODS, carica una stringa;
• MOVS, trasferimento di una stringa;
• SCAC, scansione di una stringa.
Istruzioni aritmetiche e logiche
Le istruzioni aritmetiche sono numerose e includono le quattro operazioni con gli interi binari
con e senza segno rappresentati in complemento a 2 e le operazioni di aritmetica decimale
con codifica binaria.
Vediamo alcuni esempi:
• AAA, adattamento ASCII per l’addizione;
• AAD, adattamento ASCII per la divisione;
• ADC, addizione con riporto;
• ADD, addizione;
• CBW, conversione di un byte in una parola;
• DIV, divisione;
• IMUL, moltiplicazione di interi.
Le istruzioni logiche servono a cambiare, bit per bit, i byte o le parole:
• AND;
• NOT;
• OR;
• XOR
Istruzioni per il controllo del loop
In tutti i programmi di una certa complessità riscontriamo la presenza di un loop, ovvero di
un segmento di programma che grazie ad una operazione di controllo, viene eseguito più
volte.
In questa classe sono state incluse quelle istruzioni necessarie ad eseguire il controllo sui
loop e quelle di incremento e decremento:
• LOOP, loop se CX non è 0;
• LOOPE LOOPY, loop se CX non è uguale a 0 e ZF = 1;
• LOOPNY LOOPNE, loop se CX non è uguale a 0 e ZF = 0;
• DEC, decremento;
• INC, incremento.
Istruzioni di test, confronto e salto
A questa categoria appartengono quelle istruzioni di test sui bit e di salto condizionato e non.
Alcune istruzioni sono di aritmetica e logica virtuale; per esempio il confronto (CMP) è una
sottrazione senza conservazione del risultato.
Alcuni esempi di istruzioni di questo tipo sono:
• CMP, confronto;
• JA JNBE, salto se superiore;
• JAE JNB, salto se superiore o uguale;
• JC, salto se il riporto è 1;
• JCXY, salto se il registro CX è 0;
• TEST, test (confronto logico).
Istruzioni di shift, rotate e carry
Le istruzioni di shift, rotate e carry consentono la moltiplicazione e la divisione per potenze
di 2 facendo scorrere i dati contenuti nell’accumulatore.
I bit spostati con operazione logica all’interno di una locazione di memoria assumono lo
stato zero.
Uno spostamento aritmetico di un bit verso destra opera una divisione per 2 del dato,
viceversa uno spostamento aritmetico di un bit verso sinistra equivale a una moltiplicazione
per 2.
Ecco alcuni esempi di istruzioni:
• CLC, azzeramento del riporto;
• RCL, rotazione a sinistra attraverso il riporto;
• RCR, rotazione a destra attraverso il riporto;
• ROL, rotazione a sinistra;
• ROR, rotazione a destra;
• SAR, shift aritmetico a destra;
• SAL, shift aritmetico a sinistra.
Istruzioni di chiamata e ritorno
L’istruzione di chiamata CALL serve ad attivare una subroutine salvando nello stack i dati
necessari che permetteranno in seguito di rientrare con una RET nel programma principale.
Istruzioni di controllo della CPU
A questo gruppo appartengono quelle istruzioni che consentono il controllo del funzioname-
-nto della CPU quando lavora singolarmente o in collaborazione con altri processori.
Le istruzioni di controllo del microprocessore comprendono:
• ESC, abbandona;
• HLT, alt;
• NOP, nessuna operazione;
• WAIT, attesa.
Istruzioni di interrupt
In questo gruppo troviamo le istruzioni per la gestione degli interrupt, in particolare si
segnala l’istruzione INT che può provocare degli interrupt software.
Vediamo alcuni esempi:
• CLI, azzeramento del flag di abilitazione delle interruzioni;
• INT, interruzione;
• INTO, interruzione per overflow;
• IRET, ritorno dall’interruzione;
• STI, set del flag di abilitazione delle interruzioni
Gestione delle interruzioni
La CPU 8086/8088 può riconoscere fino a un massimo di 256 tipi di interruzioni che possono
essere così classificate:
• interruzioni hardware esterne;
• interruzioni interne;
• interruzioni software.
Le interruzioni hardware esterne giungono al microprocessore tramite le tre linee NMI
(interruzione non mascherabile), INTR (interruzione mascherabile) e RESET.
Una richiesta di interruzione avanzata sulla linea NMI non può essere ignorata e pertanto,
alla fine dell’istruzione in corso di esecuzione, il microprocessore esegue i seguenti passi:
•
salva nell’area stack i flag di stato;
•
azzera IF (flag di abilitazione delle interruzioni mascherabili), ciò produce la disabilita-
-zione dell’ingresso INTR;
•
resetta il flag di trappola TF e quindi non può lavorare in modo single step;
•
salva nell’area stack i contenuti dei registri CS e IP;
•
pone nel registro IP il dato contenuto nella memoria all’indirizzo ;
•
pone nel registro CS il dato contenuto nella memoria all’indirizzo ;
•
genera l’indirizzo (a 20 bit) di partenza della routine di servizio usando i contenuti dei
registri CS e IP
Gli indirizzi e fanno parte di una tabella di indirizzi dedicata alla gestione
degli interrupts.
La tabella occupa i primi 1024 byte della
memoria di sistema dall’indirizzo a
.
L’8086/8088 può così gestire in totale 256
tipi di interruzioni, ognuna individuata da
un’etichetta da 0 a 255 che rappresenta un
indice decrescente del livello di priorità e
quindi anche di esecuzione dell’istruzione
stessa.
Se durante l’esecuzione di una routine di
servizio di un interrupt il microprocessore
riceve un’ulteriore richiesta, verifica im-
-mediatamente il livello di priorità di questa
ultima, se è di livello superiore la soddisfa,
se invece è di livello inferiore la maschera.
Ad ogni tipo di interruzione sono dedicati 4
byte della memoria: i due byte con indirizzi
più bassi sono riservati al registro IP i due
byte con indirizzi più alti sono invece dedi-
-cati al registro CS.
Infatti, nel caso di interrupt non maschera-
-bile esaminato precedentemente, le celle di
memoria contengono il dato di IP mentre quello di CS.
Tabella dei vettori delle interruzioni.
Puntatori di
interruzione
dedicati
Puntatore tipo 0
Errore in divisione
Puntatore tipo 1
Single step
Puntatore tipo 2
Non mascherabile
Puntatore tipo 3
Istr. Int. 1 byte
Puntatore tipo 4
Overflow
Puntatori di
interruzione
riservati
Puntatore tipo 5
riservato
…
…
Puntatore tipo 31
disponibile
Puntatori di
interruzione
disponibili
Puntatore tipo 32
disponibile
…
…
Puntatore tipo 255
disponibile
Nella soprastante tabella è riportata la tabella dei vettori delle interruzioni dell’8086/8088.
I puntatori (o vettori di interruzione)
da 0 a 4 sono dedicati alle interruzioni interne e a NMI, da 5 a 31 sono riservati a eventuali
nuovi integrati dell’Intel, da 32 a 255 sono disponibili per l’utente.
Una richiesta di interruzione proveniente dall’esterno può essere mascherata da programma
con l’istruzione CLI che disabilita il flag IF.
Con l’istruzione STI invece la linea INTR è abilitata per un’eventuale richiesta di un
dispositivo esterno; in risposta la CPU esegue i seguenti passi:
•
genera il segnale di riconoscimento interruzione su INTA;
•
legge sul bus dati il codice relativo al tipo di interruzione (da 0 a 255);
•
salva nell’area stack il contenuto dei registri di stato CS e IP;
•
resetta IF e TF per mascherare eventuali altri interrupt e impedire il funzionamento
single step;
•
moltiplica per 4 il codice del tipo di interruzione posto sul data bus dal dispositivo richie-
-dente, con il dato così ottenuto preleva dalla tabella gli indirizzi da memorizzare nei
registri IP e CS; in pratica si avrà:
– (codice ∙ 4), (codice ∙ 4) + 1 per IP;
– (codice ∙ 4) + 2, (codice ∙ 4) + 3 per CS;
•
preleva la prima istruzione (della routine di servizio dell’interrupt) che si trova
all’indirizzo (a 20 bit) ottenuto combinando i registri CS e IP.
Se viene attivata la linea di RESET il microprocessore procede alla inizializzazione
dei registri come intabella a lato.
L’indirizzo della prima istruzione viene ottenuto compo-
-nendo i contenuti di CS e IP:
Le interruzioni interne sono, unitamente alla NMI, quelle
a priorità più elevata e non possono essere disabilitate o
mascherate.
Quando si realizzano le condizioni necessarie, il micro-
-processore interrompe autonomamente l’esecuzione del
programma principale ed esegue una routine di servizio.
Le condizioni che danno luogo ad un interrupt interno
sono in ordine di priorità:
•
un errore in divisione, errore che può manifestarsi in fase di esecuzione di una divisione;
•
una esecuzione single step, che consiste nell’eseguire una istruzione per volta del pro-
-gramma; è utile in fase di debugging (ricerca di eventuali errori hardware o software);
•
la presenza di un’istruzione BREAK nel programma che dà luogo ad un interrupt di tipo
breakpoint.
Le interruzioni software, che non possono essere disabilitate o mascherate, sono generate
da apposite istruzioni quali, ad esempio, INT e INTO.
La CPU 8086/8088 tratta questo tipo di interrupt allo stesso modo di quello generato dalla
linea INTR.
Sommario del set di istruzioni
Nella sottostante figura sono riassunte tutte le istruzioni del micro 8086/8088.
Esempio:
Programma per sommare due operandi a 8 bit.
ORG 100H
IND1 EQU 0100H
IND2 EQU 0120H
IND3 EQU 0150H
MOV AL,IND1
;trasferisce l’operando 1 che si trova in memoria all’indirizzo IND1 nel
;registro AL
ADD AL,IND2
;somma il contenuto di AL (operando 1) al contenuto della memoria
;all’indirizzo IND2 (operando 2)
MOV IND3,AL
;trasferisce il contenuto di AL (risultato della somma)
;in memoria all’indirizzo IND3
Esempio
Programma per sommare due operandi a 16 bit.
ORG 100H
IND1 EQU 0100H
IND2 EQU 0120H
IND3 EQU 0150H
MOV AX,IND1
;trasferisce l’operando 1 che si trova in ;memoria all’indirizzo IND1
;nel registro AX
ADD AX,IND2
;somma il contenuto di AX (operando 1 al contenuto della memoria
;all’indirizzo IND2 (operando 2)
MOV IND3,AX
;trasferisce il contenuto di AX (risultato della somma) in memoria
;all’indirizzo IND3
Inizializzazione dei registri.
Registro
Dato inizializzazione
CS
DS
SS
ES
IP
FLAG
Azzerato
Esempio
Programma per sottrarre due numeri a 16 bit.
ORG 100H
IND1 EQU 0100H
IND2 EQU 0120H
IND3 EQU 0150H
MOV AX,IND1
;trasferisci l’operando 1 che si trova in ;memoria all’indirizzo IND1
;nel registro AX
SUB AX,IND2
;somma il contenuto di AX (operando 1 al ;contenuto della memoria
;all’indirizzo IND2 (operando 2)
MOV IND3,AX
;trasferisce il contenuto di AX (risultato della somma) in memoria
;all’indirizzo IND3
Esempio
Programma per sommare 2 numeri 15H e 39H in BCD.
MOV AL,15H
;pone 15H nel registro AL
ADD AL,30H
;somma 30H con il dato presenti in AL (15H)
DAA
;adattamento decimale della somma
MOV IND1,AL
;trasferisce il risultato della somma in memoria all’indirizzo IND3
Esempio
Programma per sottrarre 31H da 51H in BCD.
MOV AL,51H
;pone in AL il dato 51H
SUB AL,31
;sottrai dal contenuto di AL (51H) il dato 31H
DAS
;adattamento decimale per la sottrazione
MOV IND1,AL
;trasferisci il risultato della sottrazione in memoria all’indirizzo IND1
Esempio
Programma per moltiplicare 2 numeri (145H e 378H) a 16 bit.
ORG 150H
IND1 EQU 200H
IND2 EQU IND1+2
MOV AX, 145H
;pone in AX il dato 145H
MOV CX, 378H
;pone in CX il dato 378H
MUL CX
MOV IND1,AX
;memorizza il contenuto di AX in memoria all’indirizzo IND1
MOV IND2,DX
;memorizza il contenuto di DX in memoria all’indirizzo IND1
Esempio
Programma per dividere 2 numeri: 250H e 10H.
ORG 150H
IND1 EQU 200H
IND2 EQU IND1+2
XOR DX,DX
;azzera il registro DX
MOV AX, 250H
;pone in AX il dato 250
MOV CX, 10H
;pone il dato 10 in CX
DIV CX
;divide il contenuto di DXAX per il contenuto di CX
MOV IND1,AX
;memorizza il contenuto di AX in memoria all’indirizzo IND1
MOV IND2,DX
;memorizza il resto in memoria all’indirizzo IND2
Esempio
La routine di servizio di un’interruzione inizia con il salvataggio dei registri più importanti
nell’area stack.
Segue il codice vero e proprio di servizio dell’interruzione e termina con il ripristino dei
registri salvati all’inizio dello stack.
L’istruzione STI serve per abilitare nuovamente le interruzioni che erano state disabilitate
all’inizio.
L’istruzione RETI infine consente il ritorno al main.
PUSH AX
PUSH BX
PUSH CX
PUSH DX
«codice vero e proprio di gestione dell’interrupt»
POP DX
POP CX
POP BX
POP AX
STI
RETI
Esempio
È possibile durante la routine di servizio di una interruzione consentire ulteriori interrupt.
In tal caso l’inizio della routine è la seguente:
STI
PUSH AX
PUSH BX
PUSH CX
PUSH DX
…
Esempio
Programma che esegue istruzioni IN da una porta variabile.
MOV DX, #071CH
;carica il registro DX con l’indirizzo 071CH
IN AX,DX
;trasferisce il dato dalla porta d’ingresso il cui indirizzo è nel
;registro DX
Esempio
Programma che esegue istruzioni OUT da una porta fissa e una variabile.
MOV DX, 071C
;carica nel registro DX l’indirizzo 071C
OUT DX,AL
;output del dato 071C alla porta variabile
OUT 05H,AL
;output del dato contenuto in AL alla porta fissa con indirizzo 05H
Esempio
Supponiamo di voler inviare un comando dal micro per visualizzare il numero 9 su un
display a 7 segmenti.
Il display viene visto come un dispositivo di uscita, per cui è sufficiente fare un OUT su
una porta alla quale diamo il nome variabile PORT2.
MOV AL,00001001B
;carica AL con il dato binario 9
OUT PORT2,AL
;out del dato sul PORT2
Esempio
Programma che legge un dato dal PORT1 e pone a 1 il bit 2 del PORT2.
IN AL,PORT1
;carica in AL il dato letto dal PORT1
OR AL,00000100B
;il bit 2 del registro AL è settato a 1
OUT PORT2,AL
;tutti i bit del PORT2 rimangono inalterati tranne il bit 2 che viene
;settato
Esempio
Esempio di programma che genera un bit 1 su un port di uscita dopo un certo tempo di
ritardo.
Il ritardo software si realizza inizializzando un
registro/contatore con un dato pari ai cicli di clock
necessari e successivamente decrementando il contatore
fino allo 0 (come mostrato nel diagramma di flusso a lato).
Per programmare esattamente il ritardo è necessario
conoscere la frequenza del clock di sistema e quanti cicli
di clock sono impiegati per eseguire una istruzione.
PORT2 EQU 60H
MOV AL, 01B
;bit di controllo a 1
OUT PORT2,AL
;bit di controllo in uscita
MOV CX, FFH
;inizializza il contatore
Label DEC CX
;CX=CX-1
JNZ Label
;CX=0?
;Se sì esci, altrimenti decrementa
OUT PORT2,AL
;genera il bit di controllo