Microprocessori x86-16

ADC u0.6 ADD u0.6 AH u0.2 AL u0.2 AND u0.6 AX u0.2 BH u0.2 BL u0.2 BP u0.2 BX u0.2 CALL u0.6 CALL FAR u0.6 CBW u0.6 CH u0.2 CL u0.2 CLC u0.6 CLD u0.6 CLI u0.6 CMC u0.6 CMP u0.6 CMPSB u0.6 CMPSW u0.6 CWD u0.6 CX u0.2 DEC u0.6 DH u0.2 DI u0.2 DIV u0.6 DL u0.2 DX u0.2 ENTER u0.6 FLAGS u0.2 HLT u0.6 IDIV u0.6 IMUL u0.6 IN u0.6 u0.6 INC u0.6 INT u0.6 INTO u0.6 IP u0.2 IRET u0.6 JA u0.6 JAE u0.6 JB u0.6 JBE u0.6 JC u0.6 JCXZ u0.6 JE u0.6 JG u0.6 JGE u0.6 JL u0.6 JLE u0.6 JMP u0.6 JMP FAR u0.6 JNA u0.6 JNAE u0.6 JNB u0.6 JNBE u0.6 JNC u0.6 JNE u0.6 JNG u0.6 JNGE u0.6 JNL u0.6 JNO u0.6 JNP u0.6 JNS u0.6 JNZ u0.6 JO u0.6 JP u0.6 JPE u0.6 JPO u0.6 JS u0.6 JZ u0.6 LAHF u0.6 LDS u0.6 LEA u0.6 LEAVE u0.6 LES u0.6 LODSB u0.6 LODSW u0.6 LOOP u0.6 LOOPE u0.6 LOOPNE u0.6 LOOPNZ u0.6 LOOPZ u0.6 MOV u0.6 MOVSB u0.6 MOVSW u0.6 MUL u0.6 NEG u0.6 NOP u0.6 NOT u0.6 OR u0.6 OUT u0.6 u0.6 POP u0.6 POPA u0.6 POPF u0.6 PUSH u0.6 PUSHA u0.6 PUSHF u0.6 RCL u0.6 RCR u0.6 REP u0.6 REPE u0.6 REPNE u0.6 REPNZ u0.6 REPZ u0.6 RET u0.6 RET FAR u0.6 RETF u0.6 ROL u0.6 ROR u0.6 SAHF u0.6 SAL u0.6 SAR u0.6 SBB u0.6 SCASB u0.6 SCASW u0.6 SHL u0.6 SHR u0.6 SI u0.2 SP u0.2 STC u0.6 u0.6 STI u0.6 STOSB u0.6 STOSW u0.6 SUB u0.6 TEST u0.6 XCHG u0.6 XLATB u0.6 XOR u0.6

I microprocessori x86-16 sono sostanzialmente costituiti dal 8086 e dal 8088, con la caratteristica di gestire registri a 16 bit e di poter indirizzare complessivamente fino a 1 024 Kibyte, suddividendo però la memoria in segmenti da 64 Kibyte. Questa famiglia ha il limite di disporre di pochi registri per usi generali, spesso vincolati a un ruolo preciso, nell'ambito di certe istruzioni.

Dal momento che esiste una grande quantità di modelli di microprocessori compatibili con la vecchia famiglia a 16 bit e dato che sono disponibili simulatori ed emulatori, può essere ancora interessante lo studio della programmazione a 16 bit, riferita al modello x86-16, se non si devono affrontare problematiche relative alla protezione della memoria e a gestioni sofisticate della stessa.

Questo e gli altri capitoli dedicati alla programmazione con i microprocessori x86-16 e l'architettura dell'elaboratore IBM PC dei primi anni 1980, si limitano ad affrontare le questioni che consentono di lavorare con i registri di segmento posti tutti allo stesso valore (salva la possibilità di travasare dei dati da una parte della memoria all'altra). Molte questioni importanti non vengono affrontate e si rimanda ai riferimenti posti alla fine dei capitoli, per gli approfondimenti eventuali, oltre che al capitolo 64, in cui si fa riferimento ai microprocessori x86-32.

Segmenti

Prima di considerare i registri di un microprocessore x86-16, è importante comprendere il concetto di segmento, utilizzato in questo contesto.

Dal momento che i registri sono a 16 bit, con questi si possono rappresentare valori senza segno da zero a 65 535; pertanto, dato che la memoria è organizzata in byte, con un registro si può scandire soltanto un intervallo di 64 Kibyte. Per poter scandire lo spazio di 1 024 Kibyte, occorrono due registri, in modo da comporre assieme un indirizzo da 20 bit.

Per indirizzare la memoria, a qualunque titolo, nei microprocessori x86-16 è necessario un registro di segmento e un altro valore che esprima lo scostamento dall'inizio del segmento a cui il contesto si riferisce.

I segmenti possono collocarsi in memoria con una certa libertà, pertanto possono sovrapporsi, parzialmente o completamente. In pratica la memoria viene suddivisa idealmente in paragrafi (o click) da 16 byte ciascuno e i segmenti possono iniziare soltanto all'inizio di un paragrafo. Per garantire che ciò avvenga in questo modo, i registri che sono dedicati a rappresentare l'inizio di un segmento, riportano il numero del paragrafo, ovvero l'indirizzo assoluto di memoria diviso per 16. Questa divisione si ottiene con un semplice scorrimento a destra di quattro bit; pertanto, per ritrovare il valore originale è sufficiente fare lo scorrimento opposto, verso sinistra. Per esempio, il valore 159D16 contenuto in un registro di segmento, individua in realtà l'indirizzo 159D016, pari a 8852810.

Come accennato, per individuare una certa posizione in memoria si usa sempre un registro di segmento e un altro valore che rappresenta lo scostamento a partire dall'inizio del segmento a cui si riferisce il contesto. Per esempio, se il registro di segmento contiene il valore 159D16 e si specifica lo scostamento FFFE16, si sta in pratica facendo riferimento alla posizione di memoria 159D016+FFFE16, pari a 259CE16, ovvero 15406210.

Stante questa organizzazione, per indicare in un documento un certo indirizzo di memoria, si può usare la definizione di «indirizzo efficace» e si può scrivere un solo numero, come per esempio 159DF16.

Riquadro u167.1. Valori affiancati e divisi dal simbolo :.

Nella programmazione a 16 bit, con i microprocessori della famiglia x86, per affiancare due valori a 16 bit si usa normalmente il segno di due punti, come per esempio DX:AX o 159D16:FFFE16.

In generale, questa rappresentazione indica soltanto che si vuole fare riferimento a un numero a 32 bit, formato dall'unione delle due parti indicate, ma il significato che questo numero deve avere va interpretato in base al contesto. Per esempio, DX:AX potrebbe essere il risultato di una moltiplicazione, da prendere numericamente tale e quale, nel senso che il registro DX rappresenta i 16 bit più significativi; ma in un altro contesto, DS:SI può fare riferimento a un indirizzo che si interpreta come DS·16+SI. Nello stesso modo, il numero rappresentato come 100016:59DF16, potrebbe indicare precisamente il valore 100059DF16, oppure, se si tratta di un indirizzo, composto da segmento e scostamento, andrebbe inteso come 159DF16.

Registri

I registri dei microprocessori x86-16 sono schematizzati dalla figura successiva. I registri per uso generale, denominati AX, BX, CX e DX, possono essere utilizzati nella loro interezza o divisi in byte; per esempio si può intervenire nel byte meno significativo di AX con il nome AL (low) e si può accedere al byte più significativo con il nome AH (high).

Figura u167.2. I registri dei microprocessori x86-16.

registri x86-16

I registri di segmento sono: CS, DS, SS e ES. Il segmento individuato dal registro CS (code segment) è quello in cui si svolge il codice in corso di esecuzione, e il puntatore all'istruzione da eseguire, nell'ambito del segmento codice, è contenuta nel registro IP (instruction pointer, ma noto anche come program counter e indicato a volte con la sigla «PC»). Il segmento individuato dal registro SS (stack segment) è quello in cui si trova la pila dei dati, ovvero quella struttura che consente il trasferimento delle variabili alle funzioni o la creazione di variabili locali. L'indice della pila è costituito dal registro SP (stack pointer) e l'indirizzo della base della pila, nell'ambito della funzione in corso di esecuzione, viene annotato convenzionalmente nel registro BP (base pointer). Il segmento individuato dal registro DS (data segment) è quello in cui si trovano i dati correnti, mentre il segmento del registro ES (extra segment) riguarda un'area dati alternativa, utile soprattutto quando si vogliono fare dei trasferimenti di dati tra segmenti differenti.

Convenzionalmente è stato adottato il registro BP per annotare il riferimento all'inizio della pila di una funzione, per poter accedere agli argomenti attuali o alle variabili locali con un riferimento relativo a tale puntatore. Tuttavia, va osservato che il segmento a cui si riferisce il registro BP è quello dei dati, ovvero DS, per cui, quando si utilizza BP per accedere al contenuto della pila, è indispensabile che DS sia uguale a SS.

Il registro FLAGS raccoglie gli indicatori disponibili, come descritto nella tabella successiva. In alcuni documenti, tale registro è chiamato program status word e abbreviato come «PSW».

Tabella u167.3. Gli indicatori principali contenuti nel registro FLAGS.

Indicatore
(flag)
Bit Descrizione
C carry 0 È l'indicatore del riporto per le operazioni con valori senza segno. In particolare si attiva dopo una somma che genera un riporto e dopo una sottrazione che richiede il prestito di una cifra (in tal caso si chiama anche borrow).
1 1 Riservato.
P parity 2 Si attiva quando l'ultima operazione produce un risultato i cui otto bit meno significativi contengono una quantità pari di cifre a uno.
0 3 Riservato.
A auxiliary carry 4 È un tipo di riporto ausiliario.
0 5 Riservato.
Z zero 6 Viene impostato dopo un'operazione che dà come risultato il valore zero.
S sign 7 Riproduce il bit più significativo di un valore, dopo un'operazione. Se il valore è da intendersi con segno, l'indicatore serve a riprodurre il segno stesso.
T trace 8 Se è attivo, fa in modo che il microprocessore possa funzionare un passo alla volta.
I interrupt 9 Se è attivo, le interruzioni hardware sono abilitate, diversamente risultano bloccate.
D direction 10 Si usa per automatizzare le operazioni relative alle stringhe. Se è a zero, indica che la scansione della memoria deve procedere incrementando gli indici; se invece è pari a uno, la scansione deve proseguire decrementando gli indici.
O overflow 11 È l'indicatore di traboccamento per le operazioni che riguardano valori con segno.
0 12 Riservato.
0 13 Riservato.
0 14 Riservato.
0 15 Riservato.

I registri che sono definiti «per usi generali», hanno comunque un ruolo predominante. Tra questi si includono anche SI e DI:

Registro Definizione Scopo prevalente
AX accumulatore Usato soprattutto nei calcoli e per l'input e output. Nelle convenzioni di chiamata comuni, si usa AX per restituire un valore attraverso una funzione.
BX base Viene usato particolarmente come indice da sommare ad altri, per individuare una posizione in memoria.
CX contatore Usato come contatore nei cicli.
DX dati Si affianca a AX, soprattutto nelle divisioni e moltiplicazioni.
SI source index Usato prevalentemente come indice dell'origine, nell'ambito di un segmento dati (DS o ES).
DI destination index Usato prevalentemente come indice della destinazione, nell'ambito di un segmento dati (DS o ES).

Trasferimento di dati tra due segmenti differenti

Il trasferimento di dati tra segmenti di memoria differenti richiede l'uso di istruzioni apposite, con cui il registro DS individua il segmento di origine e ES quello di destinazione. Viene mostrato un esempio, con una porzione di codice, che ha lo scopo di copiare un intero segmento, dall'indirizzo efficace 1000016, a 1FFFF16 incluso, a partire dall'indirizzo 3000016, fino a 3FFFF16. La notazione è quella «Intel».

cld             ; Azzera l'indicatore di direzione.
mov   ax, 3000h ; Assegna a ES il segmento di destinazione,
mov   es, ax    ; attraverso AX.
mov   ax, 1000h ; Assegna a DS il segmento di origine,
mov   ds, ax    ; attraverso AX.
mov   cx, 8000h ; Imposta il contatore a 32768.
mov   si, 0h    ; Indice iniziale nel segmento di origine.
mov   di, 0h    ; Indice iniziale nel segmento di
                ; destinazione.
rep             ; Ripete l'istruzione successiva finché
                ; CX != 0; riducendo CX di una unità a ogni
                ; ciclo.
movsw           ; Copia 16 bit da DS:SI a ES:DI,
                ; incrementando di due unità sia SI, sia DI
                ; (in base all'indicatore di direzione).

Va osservato che CX riceve inizialmente un valore pari a metà della dimensione di un segmento, perché la copia avviene a coppie di byte, ovvero a interi di 16 bit. Si può notare anche che i registri di segmento coinvolti ricevono il valore attraverso la mediazione di AX, perché non gli si può assegnare direttamente un valore immediato.

Riferimenti a indirizzi di memoria con i registri

Per indicare un indirizzo di memoria, generalmente si può utilizzare una costante numerica pura e semplice, ovvero un valore immediato, ma spesso è possibile combinare il valore di uno o più registri. Nella notazione Intel, per specificare che il risultato di un'espressione rappresenta un indirizzo di memoria, la si racchiude tra parentesi quadre. Per esempio, -2[DX+SI] fa riferimento all'indirizzo di memoria efficace che si ottiene come DS·16+DX+SI-2 (DS partecipa in quanto si fa riferimento a un segmento e può trattarsi solo di quello dei dati). Le combinazioni ammissibili sono rappresentate dal modello seguente, tenendo conto che qui le parentesi quadre indicano un blocco opzionale:

[costante]+[BX|BP]+[SI|DI]

Lo specchietto successivo riepiloga tutte le combinazioni ammissibili, dove la sigla imm rappresenta un valore immediato (una costante numerica letterale) che può essere sia positivo, sia negativo:

Notazione Indirizzo efficace corrispondente Notazione Indirizzo efficace corrispondente
[SI]
[DI]
[BP]
[BX]
DS·16+SI
DS·16+DI
DS·16+BP
DS·16+BX
imm[SI]
imm[DI]
imm[BP]
imm[BX]
DS·16+SI+imm
DS·16+DI+imm
DS·16+BP+imm
DS·16+BX+imm
[BX+SI]
[BX+DI]
[BP+SI]
[BP+DI]
DS·16+BX+SI
DS·16+BX+DI
DS·16+BP+SI
DS·16+BP+DI
imm[BX+SI]
imm[BX+DI]
imm[BP+SI]
imm[BP+DI]
DS·16+BX+SI+imm
DS·16+BX+DI+imm
DS·16+BP+SI+imm
DS·16+BP+DI+imm

Si osservi che la costante letterale che precede il gruppo tra parentesi quadre può essere sostituita da un nome simbolico, con il quale si indica una variabile in memoria (preferibilmente un array). In tal modo, la notazione richiama quella degli array, come si fa con il linguaggio C. Per esempio, x[SI], individua così il byte SI-esimo a partire dall'indirizzo a cui si riferisce x.

Convenzioni di chiamata

Le convenzioni di chiamata adottate per i microprocessori x86-16 sono le stesse di quelle usate per x86-32:

Al termine della funzione si fa in modo di ripristinare la situazione precedente alla chiamata, restituendo eventualmente un valore attraverso il registro AX o eventualmente la coppia DX:AX;

Sintesi delle istruzioni x86-16

Nelle tabelle successive vengono annotate le istruzioni che possono essere utilizzate con i microprocessori x86-16, raggruppate secondo il contesto a cui appartengono. Sono però escluse le istruzioni AAx e DAx, relative alla gestione dei numeri in formato BCD (Binary coded decimal).

L'ordine in cui sono specificati gli operandi è quello «Intel», ovvero appare prima la destinazione e poi l'origine. Le sigle usate per definire i tipi di operandi sono: reg per «registro»; mem per «memoria»; imm per «immediato» (costante numerica).

Quando appare la pseudocodifica che deve spiegare l'effetto di un'istruzione, i riferimenti agli indirizzi in memoria vengono fatti in modo inusuale. Per esempio, (DS·16+SI) indica un indirizzo in memoria, individuato dal registro SI che si riferisce al segmento annotato in DS. In modo analogo, *(DS·16+SI) individua il contenuto della memoria al tale indirizzo, mentre &nome rappresenta l'indirizzo in memoria del simbolo nome.

Nella colonna degli indicatori appare: il simbolo «#» per annotare che l'indicatore relativo può essere modificato dall'istruzione; il simbolo «t» per annotare che lo stato precedente dell'indicatore viene considerato dall'istruzione; zero o uno se l'indicatore viene impostato in un certo modo; il simbolo «?» se l'effetto dell'istruzione sull'indicatore è indefinito.

Tabella u167.16. Assegnamenti, scambi, conversioni e istruzione nulla.

Nome Operandi:
dstorg1org2
Descrizione Indicatori
NOP

not operate
Istruzione nulla.

cpazstido
·········
MOV
reg, reg
reg, mem
reg, imm
mem, reg
mem, imm

move
Copia il valore dell'origine nella destinazione. Consente la copia da e verso i registri di segmento, ma per assegnare un valore a un registro di segmento occorre eseguire un passaggio intermedio attraverso un registro per usi generali. Origine e destinazione devono avere la stessa quantità di bit.

dst := org
cpazstido
·········
LEA
reg, mem

load effective address
Mette nel registro l'indirizzo della memoria, inteso come scostamento dall'inizio del segmento dati.

dst := &org
cpazstido
·········
LDS
reg, mem

load pointer using DS
Carica dalla memoria un valore a 32 bit, diviso in due blocchi da 16 bit, mettendo il primo blocco nel registro indicato e mettendo il secondo nel registro DS (segmento dati).

DS:dst := org
cpazstido
·········
LES
reg, mem

load pointer using ES
Carica dalla memoria un valore a 32 bit, diviso in due blocchi da 16 bit, mettendo il primo blocco nel registro indicato e mettendo il secondo nel registro ES.

ES:dst := org
cpazstido
·········
XCHG
reg, reg
reg, mem
mem, reg

exchange data
Scambia i valori.

dst :==: org
cpazstido
·········
CBW

convert byte to word
Converte un intero con segno, della dimensione di 8 bit, contenuto in AL, in modo da occupare tutto AX (da 8 bit a 16 bit). L'espansione tiene conto del segno.

AX := AL
cpazstido
·········
CWD

convert word to double word
Converte un intero con segno, della dimensione di 16 bit, contenuto in AX, in modo da estendersi anche in DX, tenendo conto del segno.

IF AX >= 0
THEN
    DX := 0
ELSE
    DX := FFFF16
cpazstido
·········
LAHF

load flags into AH
Carica i primi otto indicatori in AH, escludendo quelli riservati.

AHbit0:=c
AHbit1:=1
AHbit2:=p
AHbit3:=0
AHbit4:=a
AHbit5:=0
AHbit6:=z
AHbit7:=s
cpazstido
#########
SAHF

store AH into flags
Modifica il valore dei primi otto indicatori, esclusi i bit 1, 3 e 5 (il secondo, il quarto e il sesto, che sono riservati e a loro non si attribuisce un significato particolare), scrivendoci sopra il contenuto di AH.

c:=AHbit0
p:=AHbit2
a:=AHbit4
z:=AHbit6
s:=AHbit7
cpazstido
#########

Tabella u167.17. Movimento di dati.

Nome Operandi:
dstorg1org2
Descrizione Indicatori
LODSB

load string byte
Dall'indirizzo a cui punta la coppia DS:SI (DS·16+SI), viene letto un byte e copiato in AL. Se l'indicatore di direzione è pari a zero, SI viene incrementato di una unità, altrimenti viene decrementato di una unità.

AL:=*(DS·16+SI)
IF d==0
THEN
    SI++
ELSE
    SI--
cpazstido
·······t·
·········
LODSW

load string word
Dall'indirizzo a cui punta la coppia DS:SI (DS·16+SI), viene letto un blocco da 16 bit e copiato in AX. Se l'indicatore di direzione è pari a zero, SI viene incrementato di due unità, altrimenti viene decrementato di due unità.

AL:=*(DS·16+SI)
IF d==0
THEN
    SI:+=2
ELSE
    SI:-=2
cpazstido
·······t·
·········
STOSB

store string byte
All'indirizzo a cui punta la coppia ES:DI (ES·16+DI), viene scritto il valore contenuto in AL, aggiornando DI in base al contenuto dell'indicatore di direzione.

*(ES·16+DI):=AL
IF d==0
THEN
    DI++
ELSE
    DI--
cpazstido
·······t·
·········
STOSW

store string word
All'indirizzo a cui punta la coppia ES:DI (ES·16+DI), viene scritto il valore contenuto in AX, aggiornando DI in base al contenuto dell'indicatore di direzione.

*(ES·16+DI):=AX
IF d==0
THEN
    DI:+=2
ELSE
    DI:-=2
cpazstido
·······t·
·········
MOVSB

move string byte
Copia un byte, dall'indirizzo a cui punta la coppia DS:SI (DS·16+SI), all'indirizzo a cui punta la coppia ES:DI (ES·16+DI), aggiornando SI e DI in base al valore dell'indicatore di direzione.

*(ES·16+DI):=*(DS·16+SI)
IF d==0
THEN
    SI++
    DI++
ELSE
    SI--
    DI--
cpazstido
·······t·
·········
MOVSW

move string word
Copia un blocco di 16 bit, dall'indirizzo a cui punta la coppia DS:SI (DS·16+SI), all'indirizzo a cui punta la coppia ES:DI (ES·16+DI), aggiornando SI e DI in base al valore dell'indicatore di direzione.

*(ES·16+DI):=*(DS·16+SI)
IF d==0
THEN
    SI:+=2
    DI:+=2
ELSE
    SI:-=2
    DI:-=2
cpazstido
·······t·
·········
REP

repeat
Ripete l'istruzione successiva (che può essere una tra: LODSB, LODSW, STOSB, STOSW, MOVSB, MOVSW), per CX volte.

IF CX!=0
THEN
    istruzione successiva
    CX--
ELSE
    break
cpazstido
·········
XLATB

translate table to byte
Assegna a AL il valore che si può raggiungere all'indirizzo composto da DS:BX+AL (DS·16+BX+AL), dove AL va inteso come valore senza segno.

AL:=*(DS·16+BX+AL)
cpazstido
·········

Tabella u167.18. Confronti con la memoria.

Nome Operandi:
dstorg1org2
Descrizione Indicatori
SCASB

compare string byte
Confronta il contenuto di AL con il valore a cui punta la coppia ES:DI (ES·16+DI), aggiornando di conseguenza gli indicatori e anche il registro DI in base all'indicatore di direzione.

*(ES·16+DI)-AL
IF d==0
THEN
    DI++
ELSE
    DI--
cpazstido
·······t·
#####···#
SCASW

compare string word
Confronta il contenuto di AX con il valore a cui punta la coppia ES:DI (ES·16+DI), aggiornando di conseguenza gli indicatori e anche il registro DI in base all'indicatore di direzione.

*(ES·16+DI)-AX
IF d==0
THEN
    DI:+=2
ELSE
    DI:-=2
cpazstido
·······t·
#####···#
CMPSB

compare string byte in memory
Confronta il byte a cui punta la coppia ES:DI (ES·16+DI), con quello a cui punta la coppia DS:SI (DS·16+SI), aggiornando di conseguenza gli indicatori e anche i registri DI e SI in base all'indicatore di direzione.

*(DS·16+SI)-*(ES·16+DI)
IF d==0
THEN
    SI++
    DI++
ELSE
    SI++
    DI--
cpazstido
·······t·
#####···#
CMPSW

compare string word in memory
Confronta il blocco da 16 bit a cui punta la coppia ES:DI (ES·16+DI), con quello a cui punta la coppia DS:SI (DS·16+SI), aggiornando di conseguenza gli indicatori e anche i registri DI e SI in base all'indicatore di direzione.

*(DS·16+SI)-*(ES·16+DI)
IF d==0
THEN
    SI++
    DI++
ELSE
    SI++
    DI--
cpazstido
·······t·
#####···#
REPE
REPZ

repeat while equal
repeat while zero
Ripete l'istruzione successiva (che può essere una tra: SCASB, SCASW, CMPSB, CMPSW), fino a che l'indicatore z è pari a uno (rappresentante l'uguaglianza di una comparazione, ovvero che la sottrazione dà zero), fino a un massimo di CX volte.

IF CX!=0
THEN
    istruzione successiva
    CX--
    IF z==1
    THEN
        continue
    ELSE
        break
ELSE
    break
cpazstido
···#·····
REPNE
REPNZ

repeat while not equal
repeat while not zero
Ripete l'istruzione successiva (che può essere una tra: SCASB, SCASW, CMPSB, CMPSW), fino a che l'indicatore z è pari a zero (rappresentante la disuguaglianza della comparazione, ovvero che la sottrazione non dà zero), fino a un massimo di CX volte.

IF CX!=0
THEN
    istruzione successiva
    CX--
    IF z==0
    THEN
        continue
    ELSE
        break
ELSE
    break
cpazstido
···#·····

Tabella u167.19. Operazioni aritmetiche.

Nome Operandi:
dstorg1org2
Descrizione Indicatori
NEG
reg
mem

negation
Inverte il segno di un numero, attraverso il complemento a due.

operand := -operand
cpazstido
##·##···#
ADD
reg, reg
reg, mem
reg, imm
mem, reg
mem, imm

addition
Somma di interi, con o senza segno, ignorando il riporto precedente. Se i valori si intendono con segno, è importante l'esito dell'indicatore di traboccamento (overflow), se invece i valori sono da intendersi senza segno, è importante l'esito dell'indicatore di riporto (carry).

dst := org + dst
cpazstido
##·##···#
SUB
reg, reg
reg, mem
reg, imm
mem, reg
mem, imm

subtraction
Sottrazione di interi con o senza segno, ignorando il riporto precedente.

dst := org - dst
cpazstido
##·##···#
ADC
reg, reg
reg, mem
reg, imm
mem, reg
mem, imm

addition with carry
Somma di interi, con o senza segno, aggiungendo anche il riporto precedente (l'indicatore carry).

dst := org + dst + c
cpazstido
t········
##·##···#
SBB
reg, reg
reg, mem
reg, imm
mem, reg
mem, imm

subtraction with borrow
Sottrazione di interi, con o senza segno, tenendo conto del «prestito» precedente (l'indicatore carry).

dst := org + dst - c
cpazstido
t········
##·##···#
INC
reg
mem

increment
Incrementa di una unità un intero.

operand++
cpazstido
·#·##···#
DEC
reg
mem

decrement
Decrementa di una unità un valore intero.

operand--
cpazstido
·#·##···#
MUL
reg
mem

multiply
Moltiplicazione intera senza segno. L'operando è il moltiplicatore, mentre il moltiplicando è costituito da registri prestabiliti.

AX := AL*operand
DX:AX := AX*operand
cpazstido
#?·??···#
DIV
reg
mem

division
Divisione intera senza segno. L'operando è il divisore, mentre il dividendo è costituito da registri prestabiliti.

AL := AX/operand
AH := AX%operand
AX := (DX:AX)/operand
DX := (DX:AX)%operand
cpazstido
??·??···?
IMUL
reg
mem

signed multiply
Moltiplicazione intera con segno. In questo caso l'operando è il moltiplicatore, mentre il moltiplicando è costituito da registri prestabiliti.

AX := AL*operand
(DX:AX) := AX*operand
cpazstido
#?·??···#
IDIV
reg
mem

signed division
Divisione intera con segno. L'operando è il divisore, mentre il dividendo è costituito da registri prestabiliti.

AL := AX/operand
AH := AX%operand
AX := (DX:AX)/operand
DX := (DX:AX)%operand
cpazstido
??·??···?

Tabella u167.20. Operazioni logiche.

Nome Operandi:
dstorg1org2
Descrizione Indicatori
NOT
reg
mem

NOT di tutti i bit dell'operando.

dst := NOT dst
cpazstido
·········
AND
OR
XOR
reg, reg
reg, mem
reg, imm
mem, reg
mem, imm

AND, OR, o XOR, tra tutti i bit dei due operandi.

dst := org AND dst
dst := org OR dst
dst := org XOR dst
cpazstido
0#·##···0

Tabella u167.21. Scorrimenti e rotazioni.

Nome Operandi:
dstorg1org2
Descrizione Indicatori
SHL
SHR
reg, 1
mem, 1
reg
mem

shift left
shift right
Fa scorrere i bit, rispettivamente verso sinistra o verso destra (l'ultima cifra perduta finisce nell'indicatore del riporto). Se appare un solo operando, la rotazione viene eseguita CL volte. Se il valore immediato è maggiore di uno, è il compilatore che ripete l'istruzione per più volte.

cpazstido
#·······#
SAL
SAR
reg, 1
mem, 1
reg
mem

shift arithmetically left
shift arithmetically right
Fa scorrere i bit, rispettivamente verso sinistra o verso destra (l'ultima cifra perduta finisce nell'indicatore del riporto), mantenendo il segno originale (logicamente SAL è identico a SHL). Se appare un solo operando, la rotazione viene eseguita CL volte. Se il valore immediato è maggiore di uno, è il compilatore che ripete l'istruzione per più volte.

cpazstido
#········
RCL
RCR
reg, 1
mem, 1
reg
mem

rotate left with carry
rotate right with carry
Ruota i bit, rispettivamente verso sinistra o verso destra, utilizzando anche l'indicatore di riporto (carry). Se appare un solo operando, la rotazione viene eseguita CL volte. Se il valore immediato è maggiore di uno, è il compilatore che ripete l'istruzione per più volte.

cpazstido
t········
#·······#
ROL
ROR
reg, 1
mem, 1
reg
mem

rotate left
rotate right
Ruota i bit, rispettivamente verso sinistra o verso destra. Se appare un solo operando, la rotazione viene eseguita CL volte. Se il valore immediato è maggiore di uno, è il compilatore che ripete l'istruzione per più volte.

cpazstido
#·······#

Tabella u167.22. Chiamate e gestione della pila.

Nome Operandi:
dstorg1org2
Descrizione Indicatori
CALL
reg
mem
imm

Inserisce nella pila l'indirizzo dell'istruzione successiva e salta all'indirizzo indicato, che si riferisce allo scostamento a partire dall'inizio del segmento codice (CS). Pertanto, l'indirizzo a cui ci si riferisce è a 16 bit.

push indirizzo_successivo
IP := operand
cpazstido
·········
CALL FAR
imm:imm

call procedure
Inserisce nella pila il valore di CS e poi l'indirizzo dell'istruzione successiva (IP dell'istruzione successiva) e salta all'indirizzo indicato. L'indirizzo deve essere di quattro byte (32 bit), in quanto deve specificare anche il segmento codice da raggiungere.

push CS
push indirizzo_successivo
CS:IP := operand
cpazstido
·········
RET

return from call
Estrae dalla pila l'indirizzo dell'istruzione da raggiungere (IP) e salta a quella (serve a concludere una chiamata eseguita con CALL).

pop IP
cpazstido
·········
RETF
RET FAR

return from far call
Estrae dalla pila il valore di CS e quindi l'indirizzo dell'istruzione da raggiungere (IP) e salta a quella (serve a concludere una chiamata eseguita con CALL FAR).

pop IP
pop CS
cpazstido
·········
PUSH
reg
mem

push data onto stack
Inserisce nella pila il valore (della dimensione di un registro comune).

SP:-=2
*(SS·16+SP):=operand
cpazstido
·········
POP
reg
mem

pop data from stack
Estrae dalla pila l'ultimo valore inserito (della dimensione di un registro comune).

operand:=*(SS·16+SP)
SP:+=2
cpazstido
·········
PUSHF

push flags onto stack
Inserisce nella pila l'insieme del registro degli indicatori (FLAGS).

push FLAGS
cpazstido
·········
POPF

pop flags from stack
Estrae dalla pila l'insieme del registro degli indicatori (FLAGS), aggiornando di conseguenza il registro stesso.

pop FLAGS
cpazstido
?????????
ENTER
imm8, 0

enter stack frame
Questa funzione esiste a partire dai microprocessori i186.
Inserisce nella pila il valore di BP, poi assegna a BP il valore di SP e infine decrementa SP del valore fornito come immediato. Serve a predisporre BP e SP all'inizio di una funzione, specificando lo spazio necessario per le variabili locali nella pila.

push BP
BP:=SP
SP:-=2*dst
cpazstido
·········
PUSHA

push all registers onto stack
Questa funzione esiste a partire dai microprocessori i186.
Inserisce nella pila i registri principali: AX, CX, DX, BX, SP, BP, SI, DI.

push AX
push CX
push DX
push BX
push SP
push BP
push SI
push DI
cpazstido
·········
POPA

pop all registers from stack
Questa funzione esiste a partire dai microprocessori i186.
Ripristina i registri principali, estraendo i contenuti dalla pila: DI, SI, BP, SP viene eliminato senza aggiornare il registro, BX, DX, CX, AX. Come si vede, anche se PUSHA salva l'indice della pila, in pratica questo indice non viene ripristinato.

pop DI
pop SI
pop BP
SP:+=2
pop BX
pop DX
pop CX
pop AX
cpazstido
·········
LEAVE

leave stack frame
Questa funzione esiste a partire dai microprocessori i186.
Ripristina i valori di BP e di SP, allo stato che avevano prima dell'uso dell'istruzione ENTER.

SP:=BP
pop BP
cpazstido
·········

Tabella u167.23. Interruzioni.

Nome Operandi:
dstorg1org2
Descrizione Indicatori
INT
imm8

call to interrupt
Esegue una chiamata attraverso un'interruzione.
Prima di saltare alla codice relativo all'interruzione selezionata, inserisce nella pila FLAGS, CS e IP. Azzera anche l'indicatore IF (interrupt flag), mentre gli altri indicatori rimangono inalterati.

pusf
push CS
push IP
i:=0
jmp far 0:(operand·4)
cpazstido
·········
IRET

return from interrupt
Conclude l'esecuzione del codice relativo a un'interruzione recuperando dalla pila i valori inseriti alla chiamata con INT: IP, CS e FLAGS. Pertanto, il valore degli indicatori viene ripristinato allo stato precedente alla chiamata.

pop IP
pop CS
popf
cpazstido
##·##···#
CLI

clear interrupt flag
Azzera l'indicatore di abilitazione delle interruzioni (interrupt flag), disabilitando di conseguenza le interruzioni hardware.

i:=0
cpazstido
······0··
STI

set interrupt flag
Attiva l'indicatore di abilitazione delle interruzioni (interrupt flag), abilitando di conseguenza le interruzioni hardware.

i := 1
cpazstido
······1··
HLT

enter halt state
Ferma il sistema, fino a quando viene ricevuta un'interruzione hardware.

cpazstido
·········
INTO

interrupt if overflow
Se l'indicatore di straripamento è attivo, esegue la chiamata dell'interruzione numero 4 (la quale dovrebbe gestore il problema).

cpazstido
········t
·········

Tabella u167.24. Indicatori e confronti tra registri.

Nome Operandi:
dstorg1org2
Descrizione Indicatori
CLC

clear carry flag
Azzera l'indicatore del riporto (carry), senza intervenire negli altri indicatori.

c:=0
cpazstido
0········
CLD

clear direction flag
Azzera l'indicatore di direzione (direction), senza intervenire negli altri indicatori.

d:=0
cpazstido
·······0·
STC

set carry flag
Attiva l'indicatore di riporto (carry).

c:=1
cpazstido
1········
STD

set direction flag
Attiva l'indicatore di direzione (direction).

d:=1
cpazstido
·······1·
CMC

complement carry flag
Inverte il valore dell'indicatore del riporto (carry).

cpazstido
#········
CMP
reg, reg
reg, mem
reg, imm
mem, reg
mem, imm

compare operands
Confronta due valori interi. La comparazione avviene simulando la sottrazione dell'origine dalla destinazione, senza però modificare gli operandi, ma aggiornando gli indicatori, come se fosse avvenuta una sottrazione vera e propria.

dst - org
cpazstido
##·##···#
TEST
reg, reg
reg, imm
mem, reg
mem, imm

logical compare
AND dei due valori senza conservare il risultato. Serve solo a ottenere l'aggiornamento degli indicatori.

dst AND org
cpazstido
0#·##···0

Tabella u167.25. Salti.

Nome Operandi:
dstorg1org2
Descrizione Indicatori
JMP
reg
mem
imm

jump
Salto incondizionato all'indirizzo indicato, che si intende relativo al segmento codice (CS).

IP:=operand
cpazstido
·········
JMP FAR
imm:imm

far jump
Salto incondizionato all'indirizzo indicato, costituito sia dal segmento codice, sia dall'indirizzo relativo, all'interno di questo.

CS:IP:=operand
cpazstido
·········
JA
JNBE
imm

conditional jump
Dopo un confronto di valori senza segno, salta se la destinazione era maggiore dell'origine. Il salto riguarda solo l'ambito del segmento codice attuale.

CMP dst, org
IF dst > org
THEN
    go to imm
cpazstido
t··t·····
JAE
JNB
imm

conditional jump
Dopo un confronto di valori senza segno, salta se la destinazione era maggiore o uguale all'origine. Il salto riguarda solo l'ambito del segmento codice attuale.

CMP dst, org
IF dst >= org
THEN
    go to imm
cpazstido
t··t·····
JB
JNAE
imm

conditional jump
Dopo un confronto di valori senza segno, salta se la destinazione era minore dell'origine. Il salto riguarda solo l'ambito del segmento codice attuale.

CMP dst, org
IF dst < org
THEN
    go to imm
cpazstido
t··t·····
JBE
JNA
imm

conditional jump
Dopo un confronto di valori senza segno, salta se la destinazione era minore o uguale all'origine. Il salto riguarda solo l'ambito del segmento codice attuale.

CMP dst, org
IF dst <= org
THEN
    go to imm
cpazstido
t··t·····
JE
imm

conditional jump
Dopo un confronto, indipendentemente dal segno, salta se la destinazione era uguale all'origine. Il salto riguarda solo l'ambito del segmento codice attuale.

CMP dst, org
IF dst == org
THEN
    go to imm
cpazstido
···t·····
JNE
imm

conditional jump
Dopo un confronto, indipendentemente dal segno, salta se la destinazione era diversa dall'origine. Il salto riguarda solo l'ambito del segmento codice attuale.

CMP dst, org
IF dst != org
THEN
    go to imm
cpazstido
···t·····
JG
JNLE
imm

conditional jump
Dopo un confronto con segno, salta se la destinazione era maggiore dell'origine. Il salto riguarda solo l'ambito del segmento codice attuale.

CMP dst, org
IF dst > org
THEN
    go to imm
cpazstido
···tt···t
JGE
JNL
imm

conditional jump
Dopo un confronto con segno, salta se la destinazione era maggiore o uguale all'origine. Il salto riguarda solo l'ambito del segmento codice attuale.

CMP dst, org
IF dst >= org
THEN
    go to imm
cpazstido
···tt···t
JL
JNGE
imm

conditional jump
Dopo un confronto con segno, salta se la destinazione era minore dell'origine. Il salto riguarda solo l'ambito del segmento codice attuale.

CMP dst, org
IF dst < org
THEN
    go to imm
cpazstido
···tt···t
JLE
JNG
imm

conditional jump
Dopo un confronto con segno, salta se la destinazione era minore o uguale all'origine. Il salto riguarda solo l'ambito del segmento codice attuale.

CMP dst, org
IF dst <= org
THEN
    go to imm
cpazstido
···tt···t
JC
JNC
imm

conditional jump
Salta se l'indicatore del riporto (carry), rispettivamente, è attivo, oppure non è attivo. Il salto riguarda solo l'ambito del segmento codice attuale.

cpazstido
t········
JO
JNO
imm

conditional jump
Salta se l'indicatore di traboccamento (overflow), rispettivamente, è attivo, oppure non è attivo.

cpazstido
········t
JS
JNS
imm

conditional jump
Salta se l'indicatore di segno (sign), rispettivamente, è attivo, oppure non è attivo.

cpazstido
····t····
JZ
JNZ
imm

conditional jump
Salta se l'indicatore di zero, rispettivamente, è attivo, oppure non è attivo.

cpazstido
···t·····
JP
JPE
imm

conditional jump
Salta se l'indicatore di parità è attivo.

cpazstido
·t·······
JNP
JPO
imm

conditional jump
Salta se l'indicatore di parità non è attivo.

cpazstido
·t·······
JCXZ
imm

conditional jump
Salta se il valore contenuto nel registro CX è pari a zero.

cpazstido
·········

Tabella u167.26. Iterazioni

Nome Operandi:
dstorg1org2
Descrizione Indicatori
LOOP
imm8

loop
Senza alterare gli indicatori, decrementa di una unità il registro CX, quindi, se il registro è ancora diverso da zero, salta all'indirizzo cui fa riferimento l'operando. Tale indirizzo non può essere molto lontano dalla posizione corrente.

cpazstido
·········
LOOPE
LOOPZ
imm8

conditional loop
Senza alterare gli indicatori, decrementa di una unità il registro CX, quindi, se il registro è ancora diverso da zero e l'indicatore «zero» è attivo, salta all'indirizzo cui fa riferimento l'operando. Tale indirizzo non può essere molto lontano dalla posizione corrente.

cpazstido
···t·····
LOOPNE
LOOPNZ
imm8

conditional loop
Senza alterare gli indicatori, decrementa di una unità il registro CX, quindi, se il registro è ancora diverso da zero e l'indicatore «zero» non è attivo, salta all'indirizzo cui fa riferimento l'operando. Tale indirizzo non può essere molto lontano dalla posizione corrente.

cpazstido
···t·····

Tabella u167.27. Input e output.

Nome Operandi:
dstorg1org2
Descrizione Indicatori
IN
AL, imm8
AX, imm8

input
Assegna a AL o AX il valore letto dalla porta specificata; in tal caso il numero di porta non può essere superiore a 255.

cpazstido
·········
IN
AL, DX
AX, DX

input
Assegna a AL o AX il valore letto dalla porta specificata da DX; in tal caso il numero di porta può essere superiore a 255.

cpazstido
·········
OUT
imm8, AL
imm8, AX

output
Scrive nella porta specificata il valore contenuto in AL o AX; in tal caso il numero di porta non può essere superiore a 255.

cpazstido
·········
OUT
DX, AL
DX, AX

output
Scrive nella porta indicata da DX il valore contenuto in AL o AX; in tal caso il numero di porta può essere superiore a 255.

cpazstido
·········

Sostituzione delle istruzioni per i186

Nella sezione precedente sono state menzionate delle istruzioni che non fanno parte dei microprocessori 8086/8088, ma queste possono essere ottenute facilmente attraverso altre istruzioni elementari, tanto che l'assemblatore potrebbe provvedervi direttamente. A ogni modo viene annotato qui come possono essere sostituite.

Listato u167.28. Sostituzione per l'istruzione PUSHA.

push ax
push cx
push dx
push bx
push sp
push bp
push si
push di

Listato u167.29. Sostituzione per l'istruzione POPA. Il registro SP non viene ripristinato, di conseguenza si riduce l'indice della pila (si incrementa SP) senza estrarne il valore.

pop di
pop si
pop bp
add sp, 2       ; non ripristina SP
pop bx
pop dx
pop cx
pop ax

Listato u167.30. Sostituzione per l'istruzione ENTER. La riduzione di SP dipende dalla quantità di variabili locali che si vogliono gestire. Usando interi da 16 bit, si tratta di moltiplicare la quantità di variabili locali per due. Va ricordato che il segmento a cui si riferisce BP è DS, per cui è indispensabile che DS sia uguale a SS, essendo usato in questo modo come riferimento alla pila.

push bp
mov  bp, sp
sub  sp, 2      ; 0, 2, 4, 6,...

Listato u167.31. Sostituzione per l'istruzione LEAVE.

mov sp, bp
pop bp

Riferimenti

«a2» 2013.11.11 --- Copyright © Daniele Giacomini -- appunti2@gmail.com http://informaticalibera.net