Assembly Base con NASM

Capitolo 18: Istruzioni aritmetiche per i numeri BCD


Nel precedente capitolo sono stati illustrati diversi esempi relativi ad istruzioni aritmetiche applicate a numeri interi di ampiezza arbitraria; abbiamo potuto così constatare che, se i numeri su cui si deve operare vengono espressi in binario (cioè, nello stesso codice utilizzato dalla CPU), si ottengono procedimenti di calcolo caratterizzati da una notevole semplicità.
La stessa semplicità viene riscontrata anche quando operiamo su numeri interi espressi in notazione esadecimale; del resto, sappiamo bene che lo scopo fondamentale della notazione esadecimale, è quello di permettere la rappresentazione dei numeri binari in un formato molto più compatto e molto più comodo da maneggiare.
La totale compatibilità tra notazione binaria e notazione esadecimale, è una diretta conseguenza del fatto che, come sappiamo, 16=24. Grazie a questa proprietà matematica, risulta che una singola cifra esadecimale, corrisponde ad un gruppo di 4 cifre binarie (1 nibble); viceversa, un gruppo di 4 cifre binarie, corrisponde ad una singola cifra esadecimale.

Il problema che si presenta è dato dal fatto che, mentre il computer è stato progettato per lavorare in base 2, noi esseri umani siamo, invece, abituati a lavorare in base 10 ed incontriamo notevoli difficoltà nell'utilizzo di altre basi numeriche; inoltre, sappiamo che il dialogo tra computer ed esseri umani è reso ancora più difficile dal fatto che il numero 10, non può essere espresso come 2n (potenza di 2 con esponente n intero positivo). Se provassimo allora a riscrivere gli algoritmi presentati nel precedente capitolo, in modo da adattarli ai numeri interi in base 10, andremmo incontro a notevoli complicazioni; si rende necessaria, quindi, la ricerca di una adeguata soluzione a questo importante problema.

In generale, la strada migliore da seguire consiste nel cercare di raggiungere un compromesso, tale da soddisfare le esigenze, sia del computer, sia dell'utente; a tale proposito, nella eventualità di dover compiere delle elaborazioni su una serie di dati numerici, possiamo seguire un procedimento che può essere suddiviso nelle seguenti fasi: Questo procedimento, soddisfa le esigenze dell'utente in quanto, sia la fase di input, sia la fase di output, si svolgono con l'ausilio della base 10; allo stesso tempo, vengono soddisfatte anche le esigenze della CPU che può eseguire le necessarie elaborazioni su dati espressi in base 2.

18.1 Rappresentazione dei numeri interi decimali in formato BCD

Tra i vari metodi adottati per permettere alla CPU di maneggiare facilmente i numeri interi in base 10, si può citare, in particolare, il sistema di rappresentazione in formato BCD; la sigla BCD sta per Binary Coded Decimal (decimale codificato in binario).

Questo sistema di codifica si basa sulla semplice constatazione che un numero esadecimale, formato però esclusivamente da cifre comprese tra 0h e 9h, ci appare formalmente come se fosse espresso in base 10; dato, ad esempio, il numero decimale:
39674253
possiamo pensare di rappresentarlo come:
39674253h
Attraverso questa tecnica quindi, la CPU si serve della base 16 per maneggiare con estrema semplicità ed efficienza, numeri interi decimali di ampiezza arbitraria che, formalmente, ci appaiono come se fossero espressi in base 10; in sostanza, la codifica BCD permette di estendere alla base 10, tutti i vantaggi che derivano dall'uso dei numeri esadecimali (vantaggi che abbiamo studiato nei precedenti capitoli).

La codifica BCD ha avuto una grande diffusione, soprattutto in campo finanziario, dove si presenta la necessità di gestire con il computer i bilanci delle aziende, rappresentati spesso da numeri interi di grosse dimensioni; l'utilizzo della base 10 per la gestione col computer di grossi numeri interi (e delle operazioni matematiche ad essi applicate), comporta notevoli complicazioni che possono essere superate, appunto, con l'ausilio della codifica BCD.
Anche per altri aspetti, come l'interfacciamento tra PC e strumenti di misura o il salvataggio nella memoria CMOS delle informazioni relative alla configurazione hardware del PC, si fa largo uso del sistema di codifica BCD (la memoria CMOS del PC, viene trattata nella sezione Assembly Avanzato); possiamo dire quindi che un programmatore Assembly che si rispetti, non può fare a meno della conoscenza di questo importante argomento.

Tornando all'esempio presentato in precedenza, abbiamo visto che, formalmente, i due numeri 39674253 e 39674253h, appaiono identici; in realtà, sappiamo bene che il numero esadecimale 39674253h, tradotto in base 10 corrisponde, non a 39674253, bensì a:
(3 * 167) + (9 * 166) + (6 * 165) + (7 * 164) + (4 * 163) + (2 * 162) + (5 * 161) + (3 * 160)
cioè:
963068499
Da questa semplice considerazione, si intuiscono subito i problemi che bisogna affrontare con i numeri BCD; infatti, appare evidente il fatto che, eseguendo addizioni, sottrazioni, moltiplicazioni e divisioni sui numeri BCD, si ottengono risultati che hanno senso in base 16, ma non in base 10!
Consideriamo, ad esempio, una somma tra i due numeri 35 e 49; supponendo che questi due numeri siano espressi in base 10, otteniamo:
35 + 49 = 84
Supponendo, invece, che questi due numeri siano espressi in formato BCD (e quindi, in base 16), otteniamo:
35h + 49h = 7Eh
È chiaro quindi che dopo aver eseguito l'addizione tra numeri BCD, abbiamo bisogno di un procedimento che ci permetta di trasformare 7Eh in 84h, simulando così il risultato che si ottiene in base 10; lo stesso discorso vale anche per le sottrazioni, moltiplicazioni e divisioni tra numeri BCD.

Tutte le CPU della famiglia 80x86 e tutte le FPU della famiglia 80x87, sono dotate di apposite istruzioni esplicitamente rivolte alla gestione dei numeri BCD; prima di poter conoscere in dettaglio queste istruzioni, dobbiamo analizzare le convenzioni che vengono adottate per la rappresentazione dei numeri BCD.

18.1.1 Rappresentazione dei numeri interi decimali in formato Unpacked BCD

Come è stato ribadito all'inizio del capitolo, grazie all'eguaglianza 16=24, una qualsiasi cifra esadecimale equivale ad un gruppo di 4 bit (nibble); tale equivalenza è illustrata in Figura 18.1. La Figura 18.1 evidenzia il fatto che se, ad esempio, vogliamo tradurre in binario il numero esadecimale ADh, non dobbiamo fare altro che affiancare i due nibble 1010b e 1101b (che corrispondono a Ah e Dh), in modo da ottenere 10101101b; questa tecnica non può essere, invece, applicata ai numeri in base 10, in quanto 10 non è una potenza di 2 con esponente intero positivo.
Per ovviare a questo problema, possiamo pensare di sfruttare l'equivalenza Hex. Bin. illustrata in Figura 18.1, rappresentando una qualunque cifra decimale compresa tra 0 e 9, attraverso la corrispondente cifra esadecimale; ogni cifra esadecimale così ottenuta, può essere poi memorizzata nel nibble meno significativo di una locazione da 1 byte.

Consideriamo, ad esempio, il numero decimale 38126987; "simulando" questo numero in base 16, otteniamo 38126987h. A questo punto, creiamo una locazione di memoria da 8 byte e disponiamo le 8 cifre esadecimali di 38126987h, negli 8 nibble bassi di ciascun byte della locazione stessa. Nell'ipotesi che la locazione di memoria si trovi all'offset 002Ch di un segmento dati, otteniamo la situazione illustrata in Figura 18.2; osserviamo che, nel rispetto della convenzione little endian, la cifra meno significativa del numero BCD occupa l'indirizzo più basso. Come possiamo notare, il nibble più significativo di ogni BYTE rimane inutilizzato e deve valere 0h (0000b); questo tipo di codifica dei numeri interi in base 10, viene definito Unpacked BCD (BCD scompattato).
Nella terminologia utilizzata dalla Intel, ogni cifra di un numero Unpacked BCD viene definita ASCII digit (cifra ASCII); il perché di questa definizione verrà chiarito nel seguito del capitolo.

18.1.2 Rappresentazione dei numeri interi decimali in formato Packed BCD

Come si può notare in Figura 18.2, la codifica Unpacked BCD comporta un notevole spreco di memoria; tuttavia, i numeri Unpacked BCD presentano il vantaggio di poter essere gestiti in modo molto semplice dalla CPU.
Per garantire una rappresentazione più compatta dei numeri BCD, è stata definita anche un'altra codifica basata sul fatto che, ogni cifra esadecimale occupa un solo nibble; di conseguenza, in ogni locazione da 1 byte possiamo inserire 2 cifre esadecimali.
In questo modo, il numero 38126987 dell'esempio di Figura 18.2, viene disposto in memoria come illustrato in Figura 18.3; si noti che anche in questo caso, viene rispettata la convenzione little endian. Questo tipo di codifica dei numeri interi in base 10, viene definito Packed BCD (BCD compattato); confrontando la Figura 18.3 con la Figura 18.2, si nota che un numero in versione Packed BCD occupa la metà della memoria rispetto alla versione Unpacked BCD.
Nella terminologia utilizzata dalla Intel, ogni cifra di un numero Packed BCD viene definita decimal digit (cifra decimale).

18.1.3 Rappresentazione dei numeri interi decimali in formato Packed BCD standard

Per motivi pratici, la Intel ha stabilito una serie di convenzioni che definiscono un formato standard per i numeri Packed BCD; in base a tali convenzioni, un numero Packed BCD standard occupa in memoria una locazione da 10 byte (1 tenbyte).
Internamente, una locazione di memoria da 10 byte riservata ad un numero Packed BCD standard, viene organizzata secondo lo schema di Figura 18.4; i simboli Ci (con i che va da 0 a 17), rappresentano le cifre (ciascuna compresa tra 0h e 9h) del numero Packed BCD. Come si può notare, i 9 byte meno significativi della locazione di memoria, contengono sino a 18 cifre di un numero in formato Packed BCD; tali cifre risultano disposte, come al solito, secondo la convenzione little endian.
Eventuali cifre non utilizzate, devono valere obbligatoriamente 0h; ad esempio, se il numero utilizza solo le prime 8 cifre (da C0 a C7), tutte le altre cifre (da C8 a C17) devono valere 0h.
Il byte più significativo della locazione di memoria (decimo byte), codifica il segno del numero BCD; il segno positivo è rappresentato dal codice 00h (00000000b), mentre il segno negativo è rappresentato dal codice 80h (10000000b).

Possiamo dire quindi che il formato Packed BCD standard illustrato in Figura 18.4, permette di rappresentare numeri interi decimali compresi tra:
-999999999999999999
e
+999999999999999999
Risulta, inoltre, che il numero zero può essere rappresentato in due modi e cioè:
-000000000000000000
e
+000000000000000000
Tutte le FPU della famiglia 80x87, sono in grado di gestire, direttamente, numeri interi in formato Packed BCD standard; a tale proposito, vengono rese disponibili le due istruzioni FBLD e FBSTP.
L'istruzione FBLD (Fast Packed BCD Load), legge un numero Packed BCD standard dalla RAM e lo carica in un registro della FPU; qualsiasi dato caricato in un registro della FPU, viene convertito in un formato floating point da 10 byte, chiamato Temporary Real.
I numeri in formato Temporary Real, possono essere utilizzati dalla FPU come operandi di espressioni algebriche, logaritmiche, trigonometriche, esponenziali, etc; i risultati che scaturiscono dalle elaborazioni, possono essere poi convertiti dalla FPU, in diversi formati.
In particolare, l'istruzione FBSTP (Fast Packed BCD Store and Pop), legge un numero Temporary Real da un registro della FPU e lo memorizza nella RAM in formato Packed BCD standard.
Tutti i dettagli relativi al funzionamento della FPU e al relativo set di istruzioni, vengono illustrati nella sezione Assembly Avanzato.

Da quanto è stato appena esposto, appare evidente che se disponiamo di una FPU, possiamo gestire i numeri Packed BCD standard in modo estremamente semplice e veloce; fortunatamente, tutte le CPU, a partire dalla 80486 DX, sono dotate di FPU incorporata.
Anche le CPU della famiglia 80x86, dispongono di apposite istruzioni dedicate espressamente ai numeri BCD; tali istruzioni, però, operano esclusivamente sulle singole cifre di un numero Packed BCD o Unpacked BCD.
Lo scopo di queste istruzioni è quello di "aggiustare" il risultato di addizioni, sottrazioni, moltiplicazioni e divisioni, eseguite su singole cifre di numeri BCD; il compito di scrivere tutto il codice necessario per operare su numeri BCD di ampiezza arbitraria, viene lasciato quindi al programmatore. In particolare, se vogliamo utilizzare la CPU per operare su numeri Packed BCD standard, dobbiamo gestire via software tutte le situazioni che si possono presentare in relazione al segno degli operandi; ad esempio, dati i due numeri +A e -B in formato Packed BCD standard, se vogliamo calcolare una somma tra +A e -B, con B maggiore di A in valore assoluto, dobbiamo sfruttare il fatto che:
(+A) + (-B) = (+A) - (+B) = -((+B) - (+A))
Dopo aver calcolato quindi la differenza tra +B e +A, dobbiamo assegnare il segno negativo (codice 80h) al risultato.
Tutto ciò è una diretta conseguenza del fatto che, il formato Packed BCD standard, ha il solo scopo di rappresentare simbolicamente numeri interi decimali (positivi e negativi); non avrebbe alcun senso quindi, applicare a questo formato concetti come l'aritmetica modulare, il complemento a 2, l'estensione del bit di segno, etc. Nel seguito del capitolo, vedremo degli esempi pratici che chiariranno questo aspetto.

18.2 L'istruzione AAA

Con il mnemonico AAA si indica l'istruzione ASCII Adjust AL After Addition (aggiustamento del contenuto di AL dopo una addizione tra due cifre Unpacked BCD); lo scopo di questa istruzione è quello di aggiustare il risultato di una somma tra due cifre codificate in formato Unpacked BCD, in modo da farlo apparire formalmente identico a quello che si ottiene in base 10.
In sostanza, AAA presuppone che il programmatore abbia appena eseguito una istruzione ADD (o ADC) con operandi rappresentati da cifre in formato Unpacked BCD; in tal caso, AAA legge il risultato della somma e lo converte in formato Unpacked BCD.
L'unica forma lecita per l'istruzione AAA, è la seguente:
AAA
Come si può notare, il programmatore non deve specificare alcun operando esplicito; infatti, la CPU assume che la somma si trovi nel registro AL (operando SRC) e utilizza come operando DEST lo stesso registro AL ed eventualmente anche il registro AH.

Per capire il principio di funzionamento di AAA, osserviamo innanzi tutto che la somma tra due cifre decimali è sempre compresa tra il valore minimo:
0 + 0 = 0
e il valore massimo:
9 + 9 = 18
L'istruzione AAA deve simulare gli stessi risultati che si ottengono in base 10, compreso un eventuale riporto; per raggiungere questo scopo, la CPU segue un preciso procedimento che viene analizzato attraverso gli esempi seguenti. Poniamo AH=0, AL=2, BL=6 ed eseguiamo l'istruzione:
add al, bl
In presenza di questa istruzione, la CPU calcola:
AL = AL + BL = 2 + 6 = 00000010b + 00000110b = 00001000b = 08h = 8, con AF = 0
A questo punto, dobbiamo eseguire l'istruzione:
aaa
L'istruzione ADD ha prodotto AF=0 (nessun riporto dal nibble basso al nibble alto), per cui la CPU capisce che il risultato esadecimale è interamente contenuto nel nibble basso di AL.
La CPU azzera, in ogni caso, il nibble alto di AL e ottiene AL=08h; questo valore non supera 09h, per cui non necessita di alcun aggiustamento (infatti, il risultato esadecimale appare formalmente identico a quello decimale).
La CPU lascia inalterato il contenuto di AH e segnala l'assenza di riporto decimale ponendo AF=0, CF=0; alla fine otteniamo il risultato Unpacked BCD dato da AH:AL=00h:08h. Poniamo AH=0, AL=5, BL=9 ed eseguiamo l'istruzione:
add al, bl
In presenza di questa istruzione, la CPU calcola:
AL = AL + BL = 5 + 9 = 00000101b + 00001001b = 00001110b = 0Eh = 14, con AF = 0
A questo punto, dobbiamo eseguire l'istruzione:
aaa
L'istruzione ADD ha prodotto AF=0 (nessun riporto dal nibble basso al nibble alto), per cui la CPU capisce che il risultato esadecimale è interamente contenuto nel nibble basso di AL.
La CPU azzera, in ogni caso, il nibble alto di AL e ottiene AL=0Eh; questo valore supera 09h, per cui necessita di un aggiustamento (infatti, il risultato esadecimale appare formalmente diverso da quello decimale).
La CPU somma il valore 06h ad AL, e ottiene:
AL = AL + 06h = 0Eh + 06h = 14h
La CPU azzera di nuovo il nibble alto di AL, incrementa di 1 il contenuto di AH e segnala il riporto decimale ponendo AF=1, CF=1; alla fine otteniamo il risultato Unpacked BCD dato da AH:AL=01h:04h. Poniamo AH=0, AL=9, BL=9, ed eseguiamo l'istruzione:
add al, bl
In presenza di questa istruzione, la CPU calcola:
AL = AL + BL = 9 + 9 = 00001001b + 00001001b = 00010010b = 12h = 18, con AF = 1
A questo punto, dobbiamo eseguire l'istruzione:
aaa
L'istruzione ADD ha prodotto AF=1 (riporto dal nibble basso al nibble alto), per cui la CPU capisce che il risultato esadecimale occupa entrambi i nibble di AL; in questo caso, il risultato è sicuramente superiore a 09h, per cui necessita di un aggiustamento (infatti, il risultato esadecimale appare formalmente diverso da quello decimale).
La CPU somma il valore 06h ad AL, e ottiene:
AL = AL + 06h = 12h + 06h = 18h
La CPU azzera il nibble alto di AL, incrementa di 1 il contenuto di AH e segnala il riporto decimale ponendo AF=1, CF=1; alla fine otteniamo il risultato Unpacked BCD dato da AH:AL=01h:08h.

Riassumendo, se AF=0 e CF=0, allora la somma in formato Unpacked BCD è costituita unicamente dalla cifra contenuta nel nibble basso di AL (nessun riporto decimale); se, invece, AF=1 e CF=1, allora la somma in formato Unpacked BCD è costituita dalla cifra contenuta nel nibble basso di AL e da un riporto decimale di 1.

Non è obbligatorio l'azzeramento di AH prima di una istruzione AAA; il programmatore è libero di gestire come meglio crede il contenuto di tale registro.

Ci si può chiedere che legame ci sia tra il codice ASCII e i numeri in formato Unpacked BCD; tale legame è dovuto ad un particolare effetto collaterale prodotto dall'istruzione AAA.
Per chiarire questo aspetto, proviamo a calcolare la somma, in formato Unpacked BCD, tra le due cifre 3 e 5; al loro posto, utilizziamo però i codici ASCII dei corrispondenti simboli '3' e '5' e cioè, 33h e 35h.
Come si può notare, i nibble bassi dei due codici 33h e 35h, valgono, rispettivamente, 3h e 5h, proprio come le due cifre da sommare; eseguendo ora l'istruzione ADD con i due operandi '3' e '5' (o, direttamente, 33h e 35h), otteniamo:
AL = 33h + 35h = 68h, con AF = 0
In presenza ora di una istruzione AAA e in base a quanto abbiamo visto in precedenza, la CPU azzera il nibble alto di AL, lascia inalterato il contenuto di AH e segnala l'assenza di riporto decimale ponendo AF=0, CF=0; alla fine otteniamo il risultato Unpacked BCD dato da AH:AL=00h:08h (assumendo AH=0 prima dell'esecuzione di AAA).
Tale risultato è identico a quello che avremmo ottenuto sommando 3 e 5; in sostanza, l'istruzione AAA opera, indifferentemente, su cifre Unpacked BCD o sui codici ASCII dei simboli corrispondenti alle cifre stesse!

18.2.1 Somma tra numeri Unpacked BCD di ampiezza arbitraria

Sulla base di quanto è stato esposto in precedenza, possiamo intuire che la somma tra numeri Unpacked BCD di ampiezza arbitraria, può essere svolta in modo semplicissimo; a tale proposito, vediamo subito un esempio pratico.

Supponiamo di voler eseguire la seguente somma tra numeri interi decimali da 16 cifre ciascuno: Convertendo tutto in formato Unpacked BCD, dobbiamo ottenere: Eseguendo la prima somma Unpacked BCD con ADD, si ha:
AL = 07h + 02h = 09h, con AF = 0
Una successiva istruzione AAA produce AL=09h, AF=0 e CF=0.

Eseguendo la seconda somma Unpacked BCD con ADC, si ha:
AL = 09h + 04h + CF = 0Dh + 0h = 0Dh, con AF = 0
Una successiva istruzione AAA produce AL=03h, AF=1 e CF=1.

Eseguendo la terza somma Unpacked BCD con ADC, si ha:
AL = 07h + 01h + CF = 08h + 1h = 09h, con AF = 0
Una successiva istruzione AAA produce AL=09h, AF=0 e CF=0.

A questo punto è inutile continuare in quanto abbiamo capito che il procedimento da seguire è del tutto simile a quello illustrato nel precedente capitolo per l'istruzione ADC; l'unica differenza sta nel fatto che, dopo ogni istruzione ADD (o ADC) con operando AL, dobbiamo aggiungere una istruzione AAA.
Terminata l'ultima somma (tra le cifre più significative), dobbiamo consultare CF; se CF=0, il risultato è valido, mentre se CF=1, il risultato eccede le 16 cifre Unpacked BCD.

Per definire i vari dati da elaborare, possiamo servirci della direttiva DB (Define Byte) nel seguente modo: È importante ricordare che, in questo tipo di definizioni, la cifra meno significativa è quella più a sinistra.

Il codice che esegue la somma assume il seguente aspetto: Dopo l'esecuzione dell'ultima somma (tra le due cifre più significative), consultiamo CF per sapere se c'è stato un riporto finale; nel nostro caso, CF=0, per cui il risultato è valido così com'è.

Se vogliamo visualizzare il risultato in formato Unpacked BCD, possiamo servirci di writeHex8; partendo dalla cifra più significativa di ubcdSomma, possiamo scrivere:

18.2.2 Effetti provocati da AAA sugli operandi e sui flags

Se la somma tra due cifre in formato Unpacked BCD è compresa tra 0 e 9, l'esecuzione dell'istruzione AAA provoca la modifica del solo registro AL; se, invece, la somma tra due cifre in formato Unpacked BCD è compresa tra 10 e 18, l'esecuzione dell'istruzione AAA provoca la modifica dei registri AL e AH.

L'esecuzione dell'istruzione AAA provoca la modifica dei campi CF, PF, AF, ZF, SF e OF del Flags Register; i campi PF, ZF, SF e OF, assumono un valore indeterminato e non hanno quindi alcun significato.
La CPU pone AF=0 e CF=0, per segnalare che la somma tra due cifre in formato Unpacked BCD è compresa tra 0 e 9; la CPU pone, invece, AF=1 e CF=1, per segnalare che la somma tra due cifre in formato Unpacked BCD è compresa tra 10 e 18 (riporto decimale).

18.3 L'istruzione AAS

Con il mnemonico AAS si indica l'istruzione ASCII Adjust AL After Subtraction (aggiustamento del contenuto di AL dopo una sottrazione tra due cifre Unpacked BCD); lo scopo di questa istruzione è quello di aggiustare il risultato di una differenza tra due cifre codificate in formato Unpacked BCD, in modo da farlo apparire formalmente identico a quello che si ottiene in base 10.
In sostanza, AAS presuppone che il programmatore abbia appena eseguito una istruzione SUB (o SBB) con operandi rappresentati da cifre in formato Unpacked BCD; in tal caso, AAS legge il risultato della sottrazione e lo converte in formato Unpacked BCD.
L'unica forma lecita per l'istruzione AAS, è la seguente:
AAS
Come si può notare, il programmatore non deve specificare alcun operando esplicito; infatti, la CPU assume che la differenza si trovi nel registro AL (operando SRC) e utilizza come operando DEST lo stesso registro AL ed eventualmente anche il registro AH.

Per capire il principio di funzionamento di AAS, osserviamo innanzi tutto che, nel calcolo della differenza tra due cifre decimali, se il minuendo è maggiore o uguale al sottraendo, si ottiene sempre un risultato compreso tra 0 e 9, senza prestito dalla cifra successiva del minuendo; se, invece, il minuendo è minore del sottraendo, si ottiene sempre un risultato compreso tra 1 e 9, con prestito di 1 dalla cifra successiva del minuendo.
L'istruzione AAS deve simulare gli stessi risultati che si ottengono in base 10, compreso un eventuale prestito; per raggiungere questo scopo, la CPU segue un preciso procedimento che viene analizzato attraverso gli esempi seguenti. Poniamo AH=0, AL=8, BL=3 ed eseguiamo l'istruzione:
sub al, bl
In presenza di questa istruzione, la CPU calcola:
AL = AL - BL = 8 - 3 = 00001000b - 00000011b = 00000101b = 05h = 5, con AF = 0
A questo punto, dobbiamo eseguire l'istruzione:
aas
L'istruzione SUB ha prodotto AF=0 (nessun prestito dal nibble alto al nibble basso), per cui la CPU capisce che il risultato non necessita di alcun aggiustamento (infatti, il risultato esadecimale appare formalmente identico a quello decimale).
La CPU azzera, in ogni caso, il nibble alto di AL e ottiene AL=05h.
La CPU lascia inalterato il contenuto di AH e segnala l'assenza di prestito decimale ponendo AF=0, CF=0; alla fine otteniamo il risultato Unpacked BCD dato da AH:AL=00h:05h. Poniamo AH=0, AL=5, BL=9, ed eseguiamo l'istruzione:
sub al, bl
In presenza di questa istruzione, la CPU calcola:
AL = AL - BL = 5 - 9 = 00000101b - 00001001b = 11111100b = FCh, con AF = 1
In base 10, la sottrazione 5-9 produce il risultato 6, con prestito di 1.
A questo punto, dobbiamo eseguire l'istruzione:
aas
L'istruzione SUB ha prodotto AF=1 (prestito dal nibble alto al nibble basso), per cui la CPU capisce che il risultato necessita di un aggiustamento (infatti, il risultato esadecimale appare formalmente diverso da quello decimale).
La CPU sottrae il valore 06h ad AL, e ottiene:
AL = AL - 06h = FCh - 06h = F6h
La CPU azzera il nibble alto di AL, decrementa di 1 il contenuto di AH e segnala il prestito decimale ponendo AF=1, CF=1; alla fine otteniamo il risultato Unpacked BCD dato da AH:AL=FFh:06h.

Riassumendo, se AF=0 e CF=0, allora la differenza in formato Unpacked BCD è costituita unicamente dalla cifra contenuta nel nibble basso di AL (nessun prestito decimale); se, invece, AF=1 e CF=1, allora la differenza in formato Unpacked BCD è costituita dalla cifra contenuta nel nibble basso di AL e da un prestito decimale di 1.

Non è obbligatorio l'azzeramento di AH prima di una istruzione AAS; il programmatore è libero di gestire come meglio crede, il contenuto di tale registro.

Anche per AAS, vale l'effetto collaterale già descritto per AAA; in sostanza, l'istruzione AAS opera, indifferentemente, su cifre Unpacked BCD o sui codici ASCII dei simboli corrispondenti alle cifre stesse!

18.3.1 Differenza tra numeri Unpacked BCD di ampiezza arbitraria

Sulla base di quanto è stato esposto in precedenza, possiamo intuire che la differenza tra numeri Unpacked BCD di ampiezza arbitraria, può essere svolta in modo semplicissimo; a tale proposito, vediamo subito un esempio pratico.

Supponiamo di voler eseguire la seguente differenza tra numeri interi decimali da 20 cifre ciascuno: Convertendo tutto in formato Unpacked BCD, dobbiamo ottenere: Eseguendo la prima differenza Unpacked BCD con SUB, si ha:
AL = 02h - 01h = 01h, con AF = 0
Una successiva istruzione AAS produce AL=01h, AF=0 e CF=0.

Eseguendo la seconda differenza Unpacked BCD con SBB, si ha:
AL = 04h - 07h - CF = FDh - 0h = FDh, con AF = 1
Una successiva istruzione AAS produce AL=07h, AF=1 e CF=1.

Eseguendo la terza differenza Unpacked BCD con SBB, si ha:
AL = 09h - 08h - CF = 01h - 1h = 00h, con AF = 0
Una successiva istruzione AAS produce AL=00h, AF=0 e CF=0.

A questo punto è inutile continuare in quanto abbiamo capito che il procedimento da seguire, è del tutto simile a quello illustrato nel precedente capitolo per l'istruzione SBB; l'unica differenza sta nel fatto che, dopo ogni istruzione SUB (o SBB) con operando AL, dobbiamo aggiungere una istruzione AAS.
Terminata l'ultima differenza (tra le cifre più significative), dobbiamo consultare CF; se CF=0, il risultato è valido, mentre se CF=1, si è verificato un prestito finale (minuendo minore del sottraendo).

Come è stato già detto, la codifica BCD ha il solo scopo di permettere una rappresentazione simbolica dei numeri interi decimali; di conseguenza, il programmatore ha il compito di gestire le varie situazioni che si possono presentare in relazione al segno degli operandi.
Ad esempio, prima di eseguire una sottrazione il cui minuendo è minore del sottraendo, dobbiamo scambiare di posto i due operandi, in modo da ottenere un risultato positivo; a questo risultato, dobbiamo poi assegnare il segno negativo. Naturalmente, è nostro compito definire anche il metodo per la codifica del segno; prendendo spunto, ad esempio, dai numeri Packed BCD standard, possiamo utilizzare la cifra più significativa di un numero Unpacked BCD (di ampiezza definita), per memorizzare il segno del numero stesso.

Se vogliamo implementare in pratica l'esempio precedente, possiamo procedere con la definizione dei dati nel seguente modo: Il codice che esegue la differenza assume il seguente aspetto: Dopo l'esecuzione dell'ultima differenza (tra le due cifre più significative), consultiamo CF per sapere se c'è stato un prestito finale; nel nostro caso, CF=0, per cui il risultato è valido così com'è.

Se vogliamo visualizzare il risultato in formato Unpacked BCD, possiamo procedere come nell'esempio su AAA; in questo caso, l'output inizia da [ubcdDiff+19].

18.3.2 Effetti provocati da AAS sugli operandi e sui flags

Se la differenza tra due cifre in formato Unpacked BCD non richiede alcun prestito, l'esecuzione dell'istruzione AAS provoca la modifica del solo registro AL; se, invece, la differenza tra due cifre in formato Unpacked BCD richiede un prestito, l'esecuzione dell'istruzione AAS provoca la modifica dei registri AL e AH.

L'esecuzione dell'istruzione AAS provoca la modifica dei campi CF, PF, AF, ZF, SF e OF del Flags Register; i campi PF, ZF, SF e OF, assumono un valore indeterminato e non hanno quindi alcun significato.
La CPU pone AF=0 e CF=0, per segnalare che la differenza tra due cifre in formato Unpacked BCD non ha richiesto alcun prestito; la CPU pone, invece, AF=1 e CF=1, per segnalare che la differenza tra due cifre in formato Unpacked BCD ha richiesto un prestito.

18.4 L'istruzione AAM

Con il mnemonico AAM si indica l'istruzione ASCII Adjust AX After Multiply (aggiustamento del contenuto di AX dopo una moltiplicazione tra due cifre Unpacked BCD); lo scopo di questa istruzione è quello di aggiustare il risultato di un prodotto tra due cifre codificate in formato Unpacked BCD, in modo da farlo apparire formalmente identico a quello che si ottiene in base 10.
In sostanza, AAM presuppone che il programmatore abbia appena eseguito una istruzione MUL con operandi rappresentati da cifre in formato Unpacked BCD; in tal caso, AAM legge il risultato della moltiplicazione e lo converte in formato Unpacked BCD.
In virtù del fatto che i numeri BCD non seguono le leggi dell'aritmetica modulare, è necessario sottolineare che AAM produce un risultato valido solo se viene eseguita subito dopo una istruzione MUL (prodotto tra numeri interi senza segno); se, invece, si esegue AAM dopo una istruzione IMUL, si ottengono risultati privi di senso!

L'unica forma lecita per l'istruzione AAM è la seguente:
AAM
Come si può notare, il programmatore non deve specificare alcun operando esplicito; infatti, la CPU assume che il prodotto si trovi nel registro AX (operando SRC) e utilizza come operando DEST lo stesso registro AX.

Per capire il principio di funzionamento di AAM, osserviamo innanzi tutto che il prodotto tra due cifre decimali è un numero formato, al massimo, da due cifre. Tale prodotto, è sempre compreso tra il valore minimo:
0 * 0 = 0
e il valore massimo:
9 * 9 = 81
Per convertire il risultato decimale in formato Unpacked BCD, l'istruzione AAM si serve del metodo delle divisioni successive, già illustrato nel Capitolo 4; tale metodo prevede che, dividendo ripetutamente un numero intero in base b1, per una qualsiasi base b2 (divisione intera), si ottengono una serie di resti che rappresentano la sequenza delle cifre in base b2 del numero stesso. Le divisioni terminano quando si ottiene un quoziente nullo; l'ultimo resto ottenuto rappresenta la cifra più significativa del numero appena convertito in base b2.
Applichiamo ora questo metodo ad un numero intero decimale a due sole cifre, da dividere per la sua stessa base b=10; a tale proposito, consideriamo il seguente prodotto decimale:
6 * 9 = 54
Dividendo ora 54 per la sua base 10 (divisione intera), si ottiene:
54 / 10, Q = 5, R = 4
Come possiamo notare, il quoziente e il resto rappresentano, rispettivamente, la cifra più significativa e quella meno significativa di 54!
Una volta che il numero è stato scomposto nelle sue due cifre, è facile codificarlo in formato Unpacked BCD; vediamo subito alcuni esempi pratici.

Poniamo AL=3, BL=2 ed eseguiamo l'istruzione:
mul bl
In presenza di questa istruzione, la CPU calcola:
AH:AL = AL * BL = 3 * 2 = 6
A questo punto, dobbiamo eseguire l'istruzione:
aam
La CPU divide il prodotto 6 per la sua base 10 e ottiene:
Temp8:Temp8 = AH:AL / 10 = 6 / 10, Q = 0, R = 6
Successivamente, la CPU pone AL=R=06h e AH=Q=00h; alla fine otteniamo il risultato Unpacked BCD dato da AH:AL=00h:06h.

Poniamo AL=9, BL=6 ed eseguiamo l'istruzione:
mul bl
In presenza di questa istruzione, la CPU calcola:
AH:AL = AL * BL = 9 * 6 = 54
A questo punto, dobbiamo eseguire l'istruzione:
aam
La CPU divide il prodotto 54 per la sua base 10 e ottiene:
Temp8:Temp8 = AH:AL / 10 = 54 / 10, Q = 5, R = 4
Successivamente, la CPU pone AL=R=04h e AH=Q=05h; alla fine otteniamo il risultato Unpacked BCD dato da AH:AL=05h:04h.

A differenza di quanto accade per AAA e AAS, l'istruzione AAM opera, esclusivamente, su cifre in formato Unpacked BCD; non è possibile quindi utilizzare i codici ASCII dei simboli corrispondenti alle cifre stesse!

18.4.1 Codifica binaria di numeri interi non decimali

L'istruzione AAM si serve della base predefinita 10; esiste però la possibilità di codificare in binario, numeri espressi in qualsiasi altra base. A tale proposito, è necessario servirsi, obbligatoriamente, di un apposito codice macchina rappresentato dalla sequenza:
11010100b Imm8 = D4h Imm8
Il valore immediato Imm8 rappresenta la base da utilizzare (ad esempio, 8, 8h o 10q per la notazione ottale); ovviamente, l'utilizzo dell'Opcode D4h con base 0, provoca un overflow di divisione!
Supponiamo, ad esempio, di voler codificare in binario scompattato, numeri espressi in base 8; in tal caso, potremmo parlare di formato Unpacked BCO (Binary Coded Octal). In particolare, consideriamo il seguente prodotto:
3 * 7 = 21 = 25q
(è ovvio che, in notazione ottale, il prodotto deve svolgersi tra cifre comprese tra 0 e 7).

Se ora vogliamo convertire il risultato in formato Unpacked BCO, possiamo scrivere le seguenti istruzioni: Provando ora a visualizzare il contenuto dei due registri AH e AL, otteniamo proprio AH:AL=02h:05h!

18.4.2 Prodotto tra numeri Unpacked BCD di ampiezza arbitraria

Sulla base di quanto è stato esposto in precedenza, possiamo intuire che il prodotto tra numeri Unpacked BCD di ampiezza arbitraria, può essere svolto in modo semplicissimo; a tale proposito, vediamo subito un esempio pratico.

Supponiamo di voler eseguire il seguente prodotto:
6973 * 8 = 55784
Moltiplicando un numero a 4 cifre per un numero a 1 cifra, otteniamo un risultato che richiede, al massimo, 4+1=5 cifre.
Convertendo tutto in formato Unpacked BCD, dobbiamo ottenere:
06090703h * 08h = 0505070804h
Moltiplicando un numero da 4 BYTE per un numero da 1 BYTE, otteniamo un risultato che richiede, al massimo, 4+1=5 BYTE.

Eseguendo il primo prodotto Unpacked BCD con MUL, si ha:
AH:AL = 03h * 08h = 18h = 24
Una successiva istruzione AAM produce AH:AL=02h:04h, cioè, 04h con riporto di 02h.

Eseguendo il secondo prodotto Unpacked BCD con MUL, si ha:
AH:AL = 07h * 08h = 38h = 56
Una successiva istruzione AAM produce AH:AL=05h:06h, cioè, 06h con riporto di 05h.
Alla cifra 06h dobbiamo sommare il precedente riporto 02h; a tale proposito, servendoci dell'istruzione ADD, otteniamo:
AL = 06h + 02h = 08h, con AF = 0
Una successiva istruzione AAA produce AL=08h, con AF=0 e CF=0.
La precedente somma non provoca un carry, per cui il riporto 05h (incrementato con ADC) rimane invariato.

Eseguendo il terzo prodotto Unpacked BCD con MUL, si ha:
AH:AL = 09h * 08h = 48h = 72
Una successiva istruzione AAM produce AH:AL=07h:02h, cioè, 02h con riporto di 07h.
Alla cifra 02h dobbiamo sommare il precedente riporto 05h; a tale proposito, servendoci dell'istruzione ADD, otteniamo:
AL = 02h + 05h = 07h, con AF = 0
Una successiva istruzione AAA produce AL=07h, con AF=0 e CF=0.
La precedente somma non provoca un carry, per cui il riporto 07h (incrementato con ADC) rimane invariato.

Eseguendo il quarto prodotto Unpacked BCD con MUL, si ha:
AH:AL = 06h * 08h = 30h = 48
Una successiva istruzione AAM produce AH:AL=04h:08h, cioè, 08h con riporto di 04h.
Alla cifra 08h dobbiamo sommare il precedente riporto 07h; a tale proposito, servendoci dell'istruzione ADD, otteniamo:
AL = 08h + 07h = 0Fh, con AF = 0
Una successiva istruzione AAA produce AL=05h, con AF=1 e CF=1.

L'ultimo riporto 04h (incrementato con ADC) diventa 05h e rappresenta la cifra più significativa del risultato.

Unendo le 5 cifre appena calcolate, otteniamo proprio 0505070804h!.

Come possiamo notare, il procedimento da seguire è del tutto simile a quello utilizzato nel precedente capitolo per l'istruzione MUL; il codice è molto più lungo a causa del fatto che dobbiamo operare sulle singole cifre Unpacked BCD.

Il procedimento appena illustrato, può essere notevolmente semplificato eliminando tutte le istruzioni AAA e ADC e posizionando AAM dopo ADD; infatti, osserviamo innanzi tutto che AAM opera correttamente su un qualsiasi prodotto compreso tra 0 e 99. Il prodotto massimo che possiamo ottenere tra due cifre decimali è:
9 * 9 = 81 (cioè, 1 con riporto di 8)
Se il precedente riporto era 9, cioè il massimo possibile, l'istruzione ADD produce:
81 + 9 = 90 (cioè, 0 con riporto di 9)
Tale risultato è chiaramente inferiore a 99; a questo punto, una istruzione AAM produce:
AH:AL = 09h:00h (cioè, 0 con riporto di 9)
Se vogliamo implementare in pratica l'esempio precedente, possiamo procedere con la definizione dei dati nel seguente modo: In questo caso particolare, il dato ubcdFatt1 può essere definito anche come:
ubcdFatt1 dd 06090703h
Il codice che esegue il prodotto assume il seguente aspetto: Se vogliamo visualizzare il risultato in formato Unpacked BCD, possiamo procedere come nell'esempio sull'istruzione AAA; in questo caso, l'output inizia da [ubcdProd+4].

Cosa succede se anche il moltiplicatore è formato da due o più cifre decimali?
Anche in un caso del genere dobbiamo ricorrere allo stesso algoritmo che si segue con carta e penna; il codice da scrivere diventa notevolmente più lungo, principalmente a causa del fatto che, i vari prodotti parziali, devono essere sommati tra loro sempre in formato Unpacked BCD.
Consideriamo, ad esempio, il seguente prodotto: Moltiplicando un numero a 8 cifre per un numero a 3 cifre, otteniamo un prodotto che richiede, al massimo, 8+3=11 cifre; traducendo tutto in formato Unpacked BCD, dobbiamo ottenere: Moltiplicando un numero da 8 BYTE per un numero da 3 BYTE, otteniamo un prodotto che richiede, al massimo, 8+3=11 BYTE.
Applicando ora il metodo illustrato nel precedente esempio, otteniamo tre prodotti parziali (uno per ogni cifra del moltiplicatore); la somma, in formato Unpacked BCD, tra i primi due prodotti parziali, produce: La somma, in formato Unpacked BCD, tra il risultato appena ottenuto e il terzo prodotto parziale, produce: Analizzando la struttura delle somme appena svolte, si può notare che per far scorrere verso sinistra i BYTE dei vari prodotti parziali, non abbiamo la necessità di ricorrere alle istruzioni di shifting; tutto ciò è una diretta conseguenza del fatto che i numeri Unpacked BCD, sono suddivisi appunto in BYTE e quindi sono facilmente maneggiabili anche senza l'ausilio delle istruzioni logiche (illustrate nel capitolo successivo).

Un'ultima considerazione riguarda l'eventualità di dover moltiplicare tra loro, numeri Unpacked BCD con segno.
Come è stato già sottolineato, tutte le operazioni in formato Unpacked BCD devono svolgersi tra numeri senza segno; alla fine, si deve procedere alla opportuna codifica del segno dei risultati che scaturiscono dalle operazioni stesse.

18.4.3 Effetti provocati da AAM sugli operandi e sui flags

L'esecuzione dell'istruzione AAM provoca la modifica del solo registro AX.

L'esecuzione dell'istruzione AAM provoca la modifica dei campi CF, PF, AF, ZF, SF e OF del Flags Register; i campi CF, AF, OF, assumono un valore indeterminato e non hanno quindi alcun significato logico.
I campi PF, ZF e SF forniscono, invece, informazioni ordinarie relative al numero binario memorizzato in AL da AAM; in sostanza, PF indica se AL contiene un numero pari o dispari di bit a livello logico 1, ZF indica se il contenuto di AL vale zero, SF indica se il bit più significativo di AL vale 1. Generalmente, il contenuto di questi tre flags viene ignorato.

18.5 L'istruzione AAD

Con il mnemonico AAD si indica l'istruzione ASCII Adjust AX Before Division (aggiustamento del contenuto di AX prima di una divisione); lo scopo di questa istruzione è quello di predisporre un dividendo formato da due cifre Unpacked BCD, affinché una successiva divisione con un divisore formato da una cifra Unpacked BCD, produca un risultato formalmente identico a quello che si ottiene in base 10.
In sostanza, AAD presuppone che il programmatore stia per eseguire una istruzione DIV tra un dividendo compreso tra 00h:00h e 09h:09h e un divisore compreso tra 01h e 09h; in tal caso, AAD modifica la struttura del dividendo, in modo che DIV fornisca un quoziente e un resto, entrambi in formato Unpacked BCD.
In virtù del fatto che i numeri BCD non seguono le leggi dell'aritmetica modulare, è necessario sottolineare che AAD produce un risultato valido solo se viene eseguita prima di una istruzione DIV (divisione tra numeri interi senza segno); se, invece, si esegue AAD prima di una istruzione IDIV, si ottengono risultati privi di senso!

È importante ribadire che, a differenza di quanto accade con AAA, AAS e AAM, l'istruzione AAD deve essere eseguita prima e non dopo l'operazione da effettuare (che in questo caso è DIV)!
L'unica forma lecita per l'istruzione AAD, è la seguente:
AAD
Come si può notare, il programmatore non deve specificare alcun operando esplicito; infatti, la CPU assume che il dividendo si trovi nel registro AX (operando SRC) e utilizza come operando DEST lo stesso registro AX (quoziente in AL e resto in AH).

Per svolgere il proprio lavoro, l'istruzione AAD non fa altro che compattare in un solo BYTE, le due cifre Unpacked BCD del dividendo; a tale proposito, viene utilizzata una nota proprietà dei sistemi di numerazione posizionale. Nel caso della base 10 possiamo scrivere, ad esempio:
35 = (3 * 101) + (5 * 100) = (3 * 10) + 5
Traducendo tutto in formato Unpacked BCD, otteniamo:
03h:05h = (03h * 10) + 05h = (03h * 0Ah) + 05h = 1Eh + 05h = 23h = 35
Una volta che il dividendo è stato compattato, possiamo dividerlo per un divisore formato da una sola cifra Unpacked BCD, ottenendo così un quoziente e un resto, entrambi in formato Unpacked BCD; vediamo subito alcuni esempi pratici.

Vogliamo calcolare:
58 / 7, Q = 8, R = 2
Poniamo AX=AH:AL=05h:08h ed eseguiamo l'istruzione:
aad
In presenza di questa istruzione, la CPU calcola:
Temp8 = (AH * 10) + AL = (05h * Ah) + 08h = 32h + 08h = 3Ah = 58
La CPU carica il valore Temp8=58=3Ah in AL e azzera il contenuto di AH in modo da ottenere AH:AL=00h:3Ah; a questo punto, poniamo BL=07h ed eseguiamo l'istruzione:
div bl
In presenza di questa istruzione, la CPU calcola:
AH:AL = AH:AL / 7 = 58 / 7 = 02h:08h
Vogliamo calcolare:
35 / 5, Q = 7, R = 0
Poniamo AX=AH:AL=03h:05h, ed eseguiamo l'istruzione:
aad
In presenza di questa istruzione, la CPU calcola:
Temp8 = (AH * 10) + AL = (03h * Ah) + 05h = 1Eh + 05h = 23h = 35
La CPU carica il valore Temp8=35=23h in AL e azzera il contenuto di AH in modo da ottenere AH:AL=00h:23h; a questo punto, poniamo BL=05h ed eseguiamo l'istruzione:
div bl
In presenza di questa istruzione, la CPU calcola:
AH:AL = AH:AL / 5 = 35 / 5 = 00h:07h
A differenza di quanto accade per AAA e AAS, l'istruzione AAD opera, esclusivamente, su cifre in formato Unpacked BCD; non è possibile quindi utilizzare i codici ASCII dei simboli corrispondenti alle cifre stesse!

18.5.1 Codifica binaria di numeri interi non decimali

L'istruzione AAD si serve della base predefinita 10; esiste però la possibilità di codificare in binario, numeri espressi in qualsiasi altra base. A tale proposito, è necessario servirsi, obbligatoriamente, di un apposito codice macchina rappresentato dalla sequenza:
11010101b Imm8 = D5h Imm8
Il valore immediato Imm8 rappresenta la base da utilizzare (ad esempio, 8, 8h o 10q per la notazione ottale); utilizzando, ad esempio, Imm8=8, otteniamo un quoziente e un resto, entrambi in formato Unpacked Binary Coded Octal.

18.5.2 Divisione tra numeri Unpacked BCD di ampiezza arbitraria

Sulla base di quanto è stato esposto in precedenza, possiamo intuire che la divisione tra numeri Unpacked BCD di ampiezza arbitraria, può essere svolta in modo semplicissimo; a tale proposito, vediamo subito un esempio pratico.

Supponiamo di voler eseguire la seguente divisione:
3578 / 9, (Q = 397, R = 5)
Dividendo un numero a 4 cifre per un numero a 1 cifra, otteniamo un quoziente che richiede, al massimo, 4 cifre e un resto che richiede, al massimo, 1 cifra.
Convertendo tutto in formato Unpacked BCD, dobbiamo ottenere:
03050708h / 09h, (Q = 00030907h, R = 05h)
Dividendo un numero da 4 BYTE per un numero da 1 BYTE, otteniamo un quoziente che richiede, al massimo, 4 BYTE e un resto che richiede, al massimo, 1 BYTE.

Come al solito, la divisione inizia con la cifra più significativa del dividendo; nel nostro caso quindi, il primo quoziente parziale da calcolare, in formato Unpacked BCD, è:
00h:03h / 09h, (Q = 00h, R = 03h)
Una istruzione AAD produce:
AH:AL = 00h:03h
Una successiva istruzione DIV con divisore 09h, produce:
Q3 = AL = 00h, R3 = AH = 03h
Il quoziente parziale indicato con Q3 rappresenta la cifra più significativa del quoziente finale; al resto parziale R3 dobbiamo affiancare la seconda cifra (da sinistra) del dividendo, in modo da ottenere 03h:05h.

Il secondo quoziente parziale da calcolare, in formato Unpacked BCD, è:
03h:05h / 9h, (Q = 03h, R = 08h)
Una istruzione AAD produce:
AH:AL = 00h:23h
Una successiva istruzione DIV con divisore 09h, produce:
Q2 = AL = 03h, R2 = AH = 08h
Il quoziente parziale indicato con Q2 rappresenta la seconda cifra (da sinistra) del quoziente finale; al resto parziale R2 dobbiamo affiancare la terza cifra (da sinistra) del dividendo, in modo da ottenere 08h:07h.

Il terzo quoziente parziale da calcolare, in formato Unpacked BCD, è:
08h:07h / 9h, (Q = 09h, R = 06h)
Una istruzione AAD produce:
AH:AL = 00h:57h
Una successiva istruzione DIV con divisore 09h, produce:
Q1 = AL = 09h, R1 = AH = 06h
Il quoziente parziale indicato con Q1 rappresenta la terza cifra (da sinistra) del quoziente finale; al resto parziale R1 dobbiamo affiancare la quarta cifra (da sinistra) del dividendo, in modo da ottenere 06h:08h.

Il quarto e ultimo quoziente parziale da calcolare, in formato Unpacked BCD, è:
06h:08h / 9h, (Q = 07h, R = 05h)
Una istruzione AAD produce:
AH:AL = 00h:44h
Una successiva istruzione DIV con divisore 09h, produce:
Q0 = AL = 07h, R0 = AH = 05h
Il quoziente parziale indicato con Q0 rappresenta cifra meno significativa del quoziente finale; il resto parziale R0 rappresenta il resto finale della divisione.

Unendo i 4 quozienti parziali otteniamo il quoziente finale 00030907h; inoltre, abbiamo appena visto che l'ultimo resto parziale R0, ci fornisce il resto finale 05h.

Come possiamo notare, il procedimento da seguire è del tutto simile a quello utilizzato nel precedente capitolo per l'istruzione DIV; come al solito, il codice è molto più lungo a causa del fatto che dobbiamo operare sulle singole cifre Unpacked BCD.

Se vogliamo implementare in pratica l'esempio precedente, possiamo procedere con la definizione dei dati nel seguente modo: In questo caso particolare, il dato ubcdDividendo può essere definito anche come:
ubcdDividendo dd 03050708h
Il codice che esegue la divisione assume il seguente aspetto: Come al solito, è fondamentale che nella prima divisione, il contenuto di AH venga rigorosamente azzerato!

Se vogliamo visualizzare il risultato in formato Unpacked BCD, possiamo procedere come nell'esempio su AAA; in questo caso, l'output del quoziente inizia da [ubcdQuoziente+3].

Cosa succede se anche il divisore è formato da due o più cifre decimali?
Anche in un caso del genere dobbiamo ricorrere allo stesso algoritmo che si segue con carta e penna; il codice da scrivere diventa però notevolmente complesso!
In situazioni del genere, può diventare conveniente operare una conversione da BCD a binario, eseguire i calcoli in binario e riconvertire infine i risultati da binario a BCD; più avanti verranno illustrati ulteriori dettagli su questo aspetto.

In relazione all'eventualità di dover dividere tra loro numeri Unpacked BCD con segno, valgono le stesse considerazioni già svolte per AAM.

18.5.3 Effetti provocati da AAD sugli operandi e sui flags

L'esecuzione dell'istruzione AAD provoca la modifica del solo registro AX.

L'esecuzione dell'istruzione AAD provoca la modifica dei campi CF, PF, AF, ZF, SF e OF del Flags Register; i campi CF, AF, OF, assumono un valore indeterminato e non hanno quindi alcun significato logico.
I campi PF, ZF e SF forniscono, invece, informazioni ordinarie relative al numero binario memorizzato in AL da AAD; in sostanza, PF indica se AL contiene un numero pari o dispari di bit a livello logico 1, ZF indica se il contenuto di AL vale zero, SF indica se il bit più significativo di AL vale 1. Generalmente, anche il contenuto di questi tre flags viene ignorato.

Ovviamente, in relazione all'istruzione DIV da eseguire dopo AAD, valgono tutte le considerazioni esposte nel precedente capitolo; in presenza quindi di un dividendo troppo grande e/o di un divisore nullo, si verifica un overflow di divisione!

18.6 L'istruzione DAA

Con il mnemonico DAA si indica l'istruzione Decimal Adjust AL after Addition (aggiustamento del contenuto di AL dopo una addizione tra due numeri Packed BCD a 8 bit); lo scopo di questa istruzione è quello di aggiustare il risultato di una somma tra due numeri Packed BCD a 8 bit, in modo da farlo apparire formalmente identico a quello che si ottiene in base 10.
In sostanza, DAA presuppone che il programmatore abbia appena eseguito una istruzione ADD (o ADC) tra due numeri Packed BCD a 8 bit; in tal caso, DAA legge il risultato della somma e lo converte in formato Packed BCD a 8 bit.
L'unica forma lecita per l'istruzione DAA, è la seguente:
DAA
Come si può notare, il programmatore non deve specificare alcun operando esplicito; infatti, la CPU assume che la somma si trovi nel registro AL (operando SRC) e utilizza come operando DEST lo stesso registro AL.

L'istruzione DAA deve simulare, in formato Packed BCD, gli identici risultati che si ottengono sommando due numeri interi decimali, ciascuno compreso tra 0 e 99; di conseguenza, DAA deve anche simulare un eventuale riporto dal nibble basso al nibble alto (AF) e un eventuale riporto al byte successivo (CF).
Per capire il principio di funzionamento di DAA, osserviamo innanzi tutto che la somma tra numeri decimali a due cifre, è sempre compresa tra il valore minimo:
0 + 0 = 0
e il valore massimo:
99 + 99 = 198
(cioè, 98 con riporto di 1).

Si possono presentare allora diverse possibilità, che vengono segnalate dalla CPU attraverso la modifica (separata) dei flags AF e CF; a tale proposito, analizziamo alcuni esempi pratici.

Vogliamo calcolare:
2 + 6 = 8
Poniamo AL=02h, BL=06h, ed eseguiamo l'istruzione:
add al, bl
In presenza di questa istruzione, la CPU calcola:
AL = AL + BL = 02h + 06h = 00000010b + 00000110b = 00001000b = 08h, con AF = 0, CF = 0
A questo punto, dobbiamo eseguire l'istruzione:
daa
L'istruzione ADD ha prodotto AF=0 (nessun riporto dal nibble basso al nibble alto) e inoltre, la cifra 8h contenuta nel nibble basso di AL, non supera 9h; di conseguenza, tale cifra non necessita di alcun aggiustamento (AF e CF rimangono a 0).
I calcoli precedenti hanno prodotto CF=0 (nessun riporto al byte successivo) e inoltre, la cifra 0h contenuta nel nibble alto di AL, non supera 9h; di conseguenza, tale cifra non necessita di alcun aggiustamento (CF rimane a 0).
Le elaborazioni eseguite dalla CPU hanno prodotto AF=0 e CF=0; alla fine otteniamo il risultato Packed BCD dato da AL=08h.

Vogliamo calcolare:
5 + 9 = 14
Poniamo AL=05h, BL=09h, ed eseguiamo l'istruzione:
add al, bl
In presenza di questa istruzione, la CPU calcola:
AL = AL + BL = 05h + 09h = 00000101b + 00001001b = 00001110b = 0Eh, con AF = 0, CF = 0
A questo punto, dobbiamo eseguire l'istruzione:
daa
L'istruzione ADD ha prodotto AF=0 (nessun riporto dal nibble basso al nibble alto), ma la cifra Eh contenuta nel nibble basso di AL, supera 9h; di conseguenza, tale cifra necessita di un opportuno aggiustamento.
La CPU somma 06h al contenuto di AL e ottiene:
AL = AL + 06h = 0Eh + 06h = 14h, con TempCF = 0
Inoltre, la CPU pone:
AF = 1
e
CF = CF OR TempCF = 0 OR 0 = 0
I calcoli precedenti hanno prodotto CF=0 (nessun riporto al byte successivo) e inoltre, la cifra 1h contenuta nel nibble alto di AL, non supera 9h; di conseguenza, tale cifra non necessita di alcun aggiustamento (CF rimane a 0).
Le elaborazioni eseguite dalla CPU hanno prodotto AF=1 e CF=0; alla fine otteniamo il risultato Packed BCD dato da AL=14h.

Vogliamo calcolare:
95 + 10 = 105
Poniamo AL=95h, BL=10h, ed eseguiamo l'istruzione:
add al, bl
In presenza di questa istruzione, la CPU calcola:
AL = AL + BL = 95h + 10h = 10010101b + 00010000b = 10100101b = A5h, con AF = 0, CF = 0
A questo punto, dobbiamo eseguire l'istruzione:
daa
L'istruzione ADD ha prodotto AF=0 (nessun riporto dal nibble basso al nibble alto) e inoltre, la cifra 5h contenuta nel nibble basso di AL, non supera 9h; di conseguenza, tale cifra non necessita di alcun aggiustamento (AF e CF rimangono a 0).
I calcoli precedenti hanno prodotto CF=0 (nessun riporto al byte successivo), ma la cifra Ah contenuta nel nibble alto di AL, supera 9h; di conseguenza, tale cifra necessita di un aggiustamento.
La CPU somma 60h al contenuto di AL e ottiene:
AL = AL + 60h = A5h + 60h = 05h
Inoltre, la CPU pone:
CF = 1
Le elaborazioni eseguite dalla CPU hanno prodotto AF=0 e CF=1; alla fine otteniamo il risultato Packed BCD dato da AL=05h.

Vogliamo calcolare:
89 + 79 = 168
Poniamo AL=89h, BL=79h ed eseguiamo l'istruzione:
add al, bl
In presenza di questa istruzione, la CPU calcola:
AL = AL + BL = 89h + 79h = 10001001b + 01111001b = 00000010b = 02h, con AF = 1, CF = 1
A questo punto, dobbiamo eseguire l'istruzione:
daa
L'istruzione ADD ha prodotto AF=1 (riporto dal nibble basso al nibble alto); di conseguenza, la cifra contenuta nel nibble basso di AL necessita sicuramente di un aggiustamento.
La CPU somma 06h al contenuto di AL e ottiene:
AL = AL + 06h = 02h + 06h = 08h, con TempCF = 0
Inoltre, la CPU pone:
AF = 1
e
CF = CF OR TempCF = 1 OR 0 = 1
I calcoli precedenti hanno prodotto CF=1 (riporto al byte successivo); di conseguenza, la cifra contenuta nel nibble alto di AL necessita sicuramente di un aggiustamento.
La CPU somma 60h al contenuto di AL e ottiene:
AL = AL + 60h = 08h + 60h = 68h
Inoltre, la CPU pone:
CF = 1
Le elaborazioni eseguite dalla CPU hanno prodotto AF=1 e CF=1; alla fine otteniamo il risultato Packed BCD dato da AL=68h.

Riassumendo, la CPU utilizza AF per indicarci se la somma Packed BCD ha prodotto un riporto dal nibble basso al nibble alto; analogamente, la CPU utilizza CF per indicarci se la somma Packed BCD ha prodotto un riporto verso un ipotetico gruppo successivo di cifre Packed BCD. Il risultato della somma, memorizzato in AL, è identico a quello che si ottiene in base 10; inoltre, attraverso CF, possiamo sommare numeri Packed BCD di ampiezza arbitraria.

18.6.1 Somma tra numeri Packed BCD di ampiezza arbitraria

Sulla base di quanto è stato esposto in precedenza, possiamo intuire che la somma tra numeri Packed BCD di ampiezza arbitraria, può essere svolta in modo ancora più semplice rispetto al caso dei numeri Unpacked BCD; in relazione all'esempio presentato per l'istruzione AAA, le uniche differenze sono le seguenti: Vediamo subito un esempio pratico che ci permette di illustrare l'utilizzo dei numeri Packed BCD standard; a tale proposito, supponiamo di voler eseguire la seguente somma tra numeri interi decimali da 18 cifre ciascuno: Convertendo tutto in formato Packed BCD standard, dobbiamo ottenere: Ricordiamo che, il segno di un numero Packed BCD standard, viene codificato nel BYTE più significativo; il codice 00h rappresenta il segno positivo, mentre il codice 80h rappresenta il segno negativo.

Bisogna anche ricordare che con le direttive DQ e DT, il NASM accetta solo numeri reali come valori inizializzanti; non è possibile quindi utilizzare DT per definire numeri codificati in formato Packed BCD.
Possiamo procedere allora nel seguente modo: Il codice che esegue la somma assume il seguente aspetto: Dopo l'esecuzione dell'ultima somma (tra gli ottavi BYTE), consultiamo CF per sapere se c'è stato un riporto finale; nel nostro caso, CF=0, per cui il risultato è valido così com'è.
Osserviamo che, sommando due numeri positivi, otteniamo un risultato positivo; di conseguenza, dobbiamo memorizzare il valore 00h nel BYTE più significativo del risultato.

Se vogliamo visualizzare il risultato in formato Packed BCD, possiamo utilizzare lo stesso procedimento illustrato per AAA; in questo caso, l'output inizia da [pbcdSomma+9].

18.6.2 Effetti provocati da DAA sugli operandi e sui flags

L'esecuzione dell'istruzione DAA provoca la modifica del solo registro AL.

L'esecuzione dell'istruzione DAA provoca la modifica dei campi CF, PF, AF, ZF, SF e OF del Flags Register; il solo campo OF assume un valore indeterminato e non ha quindi alcun significato.
I campi PF, ZF e SF forniscono informazioni ordinarie relative al numero binario memorizzato in AL da DAA; in sostanza, PF indica se AL contiene un numero pari o dispari di bit a livello logico 1, ZF indica se il contenuto di AL vale zero, SF indica se il bit più significativo di AL vale 1. Generalmente, anche il contenuto di questi tre flags viene ignorato.
Se la somma Packed BCD provoca un riporto dal nibble basso al nibble alto, la CPU pone AF=1; in caso contrario si ha AF=0. Se la somma Packed BCD provoca un riporto al byte successivo, la CPU pone CF=1; in caso contrario si ha CF=0.

18.7 L'istruzione DAS

Con il mnemonico DAS si indica l'istruzione Decimal Adjust AL after Subtraction (aggiustamento del contenuto di AL dopo una sottrazione tra due numeri Packed BCD a 8 bit); lo scopo di questa istruzione è quello di aggiustare il risultato di una differenza tra due numeri Packed BCD a 8 bit, in modo da farlo apparire formalmente identico a quello che si ottiene in base 10.
In sostanza, DAS presuppone che il programmatore abbia appena eseguito una istruzione SUB (o SBB) tra due numeri Packed BCD a 8 bit; in tal caso, DAS legge il risultato della differenza e lo converte in formato Packed BCD a 8 bit.
L'unica forma lecita per l'istruzione DAS è la seguente:
DAS
Come si può notare, il programmatore non deve specificare alcun operando esplicito; infatti, la CPU assume che la differenza si trovi nel registro AL (operando SRC) e utilizza come operando DEST, lo stesso registro AL.

L'istruzione DAS deve simulare, in formato Packed BCD, gli identici risultati che si ottengono sottraendo due numeri interi decimali, ciascuno compreso tra 0 e 99; di conseguenza, DAS deve anche simulare un eventuale prestito dal nibble alto al nibble basso (AF) e un eventuale prestito dal byte successivo (CF).

Si possono presentare allora diverse possibilità, che vengono segnalate dalla CPU attraverso la modifica (separata) dei flags AF e CF; a tale proposito, analizziamo alcuni esempi pratici.

Vogliamo calcolare:
37 - 12 = 25
Poniamo AL=37h, BL=12h ed eseguiamo l'istruzione:
sub al, bl
In presenza di questa istruzione, la CPU calcola:
AL = AL - BL = 37h - 12h = 00110111b - 00010010b = 00100101b = 25h, con AF = 0, CF = 0
A questo punto, dobbiamo eseguire l'istruzione:
das
L'istruzione SUB ha prodotto AF=0 (nessun prestito dal nibble alto al nibble basso) e inoltre, la cifra 5h contenuta nel nibble basso di AL, non supera 9h; di conseguenza, tale cifra non necessita di alcun aggiustamento (AF e CF rimangono a 0).
I calcoli precedenti hanno prodotto CF=0 (nessun prestito dal byte successivo) e inoltre, la cifra 2h contenuta nel nibble alto di AL, non supera 9h; di conseguenza, tale cifra non necessita di alcun aggiustamento (CF rimane a 0).
Le elaborazioni eseguite dalla CPU hanno prodotto AF=0 e CF=0; alla fine otteniamo il risultato Packed BCD dato da AL=25h.

Vogliamo calcolare:
45 - 18 = 27
Poniamo AL=45h, BL=18h, ed eseguiamo l'istruzione:
sub al, bl
In presenza di questa istruzione, la CPU calcola:
AL = AL - BL = 45h - 18h = 01000101b - 00011000b = 00101101b = 2Dh, con AF = 1, CF = 0
A questo punto, dobbiamo eseguire l'istruzione:
das
L'istruzione SUB ha prodotto AF=1 (prestito dal nibble alto al nibble basso); di conseguenza, la cifra contenuta nel nibble basso di AL necessita sicuramente di un aggiustamento.
La CPU sottrae 06h al contenuto di AL e ottiene:
AL = AL - 06h = 2Dh - 06h = 27h, con TempCF = 0
Inoltre, la CPU pone:
AF = 1
e
CF = CF OR TempCF = 0 OR 0 = 0
I calcoli precedenti hanno prodotto CF=0 (nessun prestito dal byte successivo) e inoltre, la cifra 2h contenuta nel nibble alto di AL, non supera 9h; di conseguenza, tale cifra non necessita di alcun aggiustamento (CF rimane a 0).
Le elaborazioni eseguite dalla CPU hanno prodotto AF=1 e CF=0; alla fine otteniamo il risultato Packed BCD dato da AL=27h.

Vogliamo calcolare:
38 - 71 = 67 (con prestito di 1)
Poniamo AL=38h, BL=71h, ed eseguiamo l'istruzione:
sub al, bl
In presenza di questa istruzione, la CPU calcola:
AL = AL - BL = 38h - 71h = 00111000b - 01110001b = 11000111b = C7h, con AF = 0, CF = 1
A questo punto, dobbiamo eseguire l'istruzione:
das
L'istruzione SUB ha prodotto AF=0 (nessun prestito dal nibble alto al nibble basso) e inoltre, la cifra 7h contenuta nel nibble basso di AL, non supera 9h; di conseguenza, tale cifra non necessita di alcun aggiustamento (AF rimane a 0 e CF rimane a 1).
I calcoli precedenti hanno prodotto CF=1 (prestito dal byte successivo); di conseguenza, la cifra contenuta nel nibble alto di AL necessita sicuramente di un aggiustamento.
La CPU sottrae 60h al contenuto di AL e ottiene:
AL = AL - 60h = C7h - 60h = 67h
Inoltre, la CPU pone:
CF = 1
Le elaborazioni eseguite dalla CPU hanno prodotto AF=0 e CF=1; alla fine otteniamo il risultato Packed BCD dato da AL=67h.

Vogliamo calcolare:
31 - 79 = 52 (con prestito di 1)
Poniamo AL=31h, BL=79h, ed eseguiamo l'istruzione:
sub al, bl
In presenza di questa istruzione, la CPU calcola:
AL = AL - BL = 31h - 79h = 00110001b - 01111001b = 10111000b = B8h, con AF = 1, CF = 1
A questo punto, dobbiamo eseguire l'istruzione:
das
L'istruzione SUB ha prodotto AF=1 (prestito dal nibble alto al nibble basso); di conseguenza, la cifra contenuta nel nibble basso di AL necessita sicuramente di un aggiustamento.
La CPU sottrae 06h al contenuto di AL, e ottiene:
AL = AL - 06h = B8h - 06h = B2h, con TempCF = 0
Inoltre, la CPU pone:
AF = 1
e
CF = CF OR TempCF = 1 OR 0 = 1
I calcoli precedenti hanno prodotto CF=1 (prestito dal byte successivo); di conseguenza, la cifra contenuta nel nibble alto di AL necessita sicuramente di un aggiustamento.
La CPU sottrae 60h al contenuto di AL, e ottiene:
AL = AL - 60h = B2h - 60h = 52h
Inoltre, la CPU pone:
CF = 1
Le elaborazioni eseguite dalla CPU hanno prodotto AF=1 e CF=1; alla fine otteniamo il risultato Packed BCD dato da AL=52h.

Riassumendo, la CPU utilizza AF per indicarci se la differenza Packed BCD ha prodotto un prestito dal nibble alto al nibble basso; analogamente, la CPU utilizza CF per indicarci se la differenza Packed BCD ha prodotto un prestito da un ipotetico gruppo successivo di cifre Packed BCD. Il risultato della differenza, memorizzato in AL, è identico a quello che si ottiene in base 10; inoltre, attraverso CF, possiamo sottrarre numeri Packed BCD di ampiezza arbitraria.

18.7.1 Differenza tra numeri Packed BCD di ampiezza arbitraria

Sulla base di quanto è stato esposto in precedenza, possiamo intuire che la differenza tra numeri Packed BCD di ampiezza arbitraria, può essere svolta in modo ancora più semplice rispetto al caso dei numeri Unpacked BCD; in relazione all'esempio presentato per l'istruzione AAS, le uniche differenze sono le seguenti: Vediamo subito un esempio pratico che ci permette di illustrare l'utilizzo dei numeri Packed BCD standard; a tale proposito, supponiamo di voler eseguire la seguente somma tra numeri interi decimali da 18 cifre ciascuno: Convertendo tutto in formato Packed BCD standard, dobbiamo ottenere: Osserviamo subito che il primo numero è negativo (80h), mentre il secondo è positivo (00h) e inoltre, il primo numero è maggiore del secondo in valore assoluto; di conseguenza, il procedimento da seguire consiste nel calcolare una differenza tra numeri positivi, assegnando poi il segno negativo (80h) al risultato.

Procediamo allora nel seguente modo: Il codice che esegue la differenza assume il seguente aspetto: Dopo l'esecuzione dell'ultima differenza (tra gli ottavi BYTE), consultiamo CF per sapere se c'è stato un prestito finale; nel nostro caso, CF=0, per cui il risultato è valido così com'è.

Se vogliamo visualizzare il risultato in formato Packed BCD, possiamo utilizzare lo stesso procedimento illustrato per AAA; in questo caso, l'output inizia da [pbcdDiff+9].

18.7.2 Effetti provocati da DAS sugli operandi e sui flags

L'esecuzione dell'istruzione DAS provoca la modifica del solo registro AL.

L'esecuzione dell'istruzione DAS provoca la modifica dei campi CF, PF, AF, ZF, SF e OF del Flags Register; il solo campo OF assume un valore indeterminato e non ha quindi alcun significato.
I campi PF, ZF e SF forniscono informazioni ordinarie relative al numero binario memorizzato in AL da DAS; in sostanza, PF indica se AL contiene un numero pari o dispari di bit a livello logico 1, ZF indica se il contenuto di AL vale zero, SF indica se il bit più significativo di AL vale 1. Generalmente, anche il contenuto di questi tre flags viene ignorato.
Se la differenza Packed BCD provoca un prestito dal nibble alto al nibble basso, la CPU pone AF=1; in caso contrario si ha AF=0. Se la differenza Packed BCD provoca un prestito dal byte successivo, la CPU pone CF=1; in caso contrario si ha CF=0.

18.8 Conversione da Packed BCD a Unpacked BCD e viceversa

Le CPU della famiglia 80x86, non dispongono di istruzioni per l'aggiustamento decimale di moltiplicazioni e divisioni; per i numeri Packed BCD quindi, non esiste alcuna istruzione equivalente a AAM e AAD. Eventualmente, il programmatore può scrivere apposite procedure che svolgono questo tipo di operazioni; a tale proposito, è necessario servirsi degli stessi concetti su cui si basa il principio di funzionamento delle istruzioni AAM e AAD.
In alternativa, si può anche seguire un'altra strada che consiste nel convertire i numeri BCD, da un formato all'altro; volendo eseguire, ad esempio, una moltiplicazione tra numeri Packed BCD di ampiezza arbitraria, possiamo pensare di convertire i due fattori, da Packed BCD a Unpacked BCD, svolgere i calcoli con MUL e AAM e poi convertire il risultato da Unpacked BCD a Packed BCD.

18.8.1 Conversione da Packed BCD a Unpacked BCD

La conversione da Packed BCD a Unpacked BCD si svolge in modo semplicissimo grazie all'istruzione AAM; infatti, sappiamo che AAM legge da AL il risultato di una moltiplicazione tra fattori Unpacked BCD, lo converte in formato Unpacked BCD e lo memorizza in AH:AL.
Consideriamo, ad esempio, il seguente prodotto:
AL = 03h * 09h = 1Bh = 27
Una successiva istruzione AAM calcola, come sappiamo:
1Bh / Ah, AH = Q = 02h, AL = R = 07h
L'istruzione AAM utilizza la base predefinita 10 e proprio per questo motivo, ottiene un risultato decimale; il trucco da utilizzare, consiste allora nel servirsi di AAM con base 16!
Come già sappiamo, per imporre a AAM l'utilizzo della base 16, dobbiamo servirci del codice macchina D4h, 16; nel caso dell'esempio precedente, otteniamo allora:
1Bh / 10h, AH = Q = 01h, AL = R = 0Bh
Come si può notare, abbiamo convertito 1Bh in 01h:0Bh!

Una volta chiarito questo trucco, possiamo passare ad un esempio pratico; a tale proposito, supponiamo di voler convertire in formato Unpacked BCD, il numero Packed BCD standard 80743251687690345672h.
Come sappiamo, la versione Unpacked BCD di un numero, occupa il doppio dei byte rispetto alla versione Packed BCD; di conseguenza, possiamo procedere con le seguenti definizioni: Il seguente codice esegue la conversione da Packed BCD standard a Unpacked BCD: Per ogni conversione, il puntatore (SI) a pbcdNumber deve essere incrementato di 1 byte, mentre il puntatore (DI) a ubcdNumber deve essere incrementato di 2 byte; infatti, ogni BYTE di pbcdNumber (che contiene una coppia di cifre in formato Packed BCD), deve essere convertito in una WORD di ubcdNumber (che contiene una coppia di cifre in formato Unpacked BCD).
Osserviamo che scompattando anche il segno 80h di pbcdNumber, otteniamo 0800h; il programmatore deve quindi tenere presente che la WORD più significativa di ubcdNumber, contiene la codifica del segno del numero appena convertito.

Se vogliamo visualizzare il risultato in formato Unpacked BCD, possiamo utilizzare lo stesso procedimento illustrato per AAA; in questo caso, l'output inizia da [ubcdNumber+19].

18.8.2 Conversione da Unpacked BCD a Packed BCD

La conversione da Unpacked BCD a Packed BCD si svolge in modo semplicissimo grazie all'istruzione AAD; infatti, sappiamo che AAD prepara il contenuto Unpacked BCD della coppia AH:AL, in modo che una successiva istruzione DIV fornisca un quoziente e un resto, entrambi in formato Unpacked BCD.
Supponiamo, ad esempio, di avere AH:AL=01h:08h; una successiva istruzione AAD calcola, come sappiamo:
(01h * Ah) + 08h = 0Ah + 08h = 12h = 18
L'istruzione AAD utilizza la base predefinita 10 e proprio per questo motivo, ottiene un risultato decimale; il trucco da utilizzare, consiste allora nel servirsi di AAD con base 16!
Come già sappiamo, per imporre a AAD l'utilizzo della base 16, dobbiamo servirci del codice macchina D5h, 16; nel caso dell'esempio precedente, otteniamo allora:
(01h * 10h) + 08h = 10h + 08h = 18h
Come si può notare, abbiamo convertito 01h:08h in 18h!

Una volta chiarito questo trucco, possiamo passare ad un esempio pratico; a tale proposito, supponiamo di voler convertire in formato Packed BCD standard, il numero Unpacked BCD dell'esempio precedente, rappresentato da 0800070403020501060807060900030405060702h.
Come sappiamo, la versione Packed BCD standard di un numero, occupa la metà dei byte rispetto alla versione Unpacked BCD; di conseguenza, possiamo procedere con le seguenti definizioni: Il seguente codice esegue la conversione da Unpacked BCD a Packed BCD standard: Per ogni conversione, il puntatore (SI) a ubcdNumber deve essere incrementato di 2 byte, mentre il puntatore (DI) a pbcdNumber deve essere incrementato di 1 byte; infatti, ogni WORD di ubcdNumber (che contiene una coppia di cifre in formato Unpacked BCD), deve essere convertita in un BYTE di pbcdNumber (che contiene una coppia di cifre in formato Packed BCD).

Se vogliamo visualizzare il risultato in formato Packed BCD, possiamo utilizzare lo stesso procedimento illustrato per AAA; in questo caso, l'output inizia da [pbcdNumber+9].

18.9 Conversione da BCD a binario e viceversa

Quando le elaborazioni da svolgere sui numeri BCD diventano troppo complesse, potrebbe risultare conveniente una conversione in binario dei numeri stessi; in questo modo, si effettuano le varie elaborazioni sui numeri binari e si provvede poi a riconvertire in formato BCD i risultati appena ottenuti.
Per effettuare le conversioni da BCD a binario e viceversa, possiamo ricorrere, ad esempio, al metodo delle divisioni successive; come è stato già ricordato anche in questo capitolo, dividendo ripetutamente un numero in base b1, per una base b2, si ottengono una serie di resti che formano la sequenza delle cifre, in base b2, del numero stesso. Le divisioni terminano non appena si ottiene un quoziente nullo; l'ultimo resto ottenuto rappresenta la cifra più significativa del numero appena convertito in base b2.

Il problema fondamentale consiste nel fatto che dobbiamo cercare di ricondurci sempre al caso di una divisione tra un dividendo di ampiezza arbitraria e un divisore formato da una sola cifra (che rappresenta la base di destinazione); in questo modo, possiamo applicare gli stessi procedimenti già illustrati in questo capitolo e nel capitolo precedente.

18.9.1 Conversione da BCD a binario

Per la conversione da BCD a binario, dobbiamo utilizzare la cifra 2 (base dei numeri binari) come divisore; in questo modo, è possibile applicare lo stesso procedimento già illustrato in questo capitolo per l'istruzione AAD (ovviamente, se il dividendo è in formato Packed BCD, dobbiamo prima convertirlo in formato Unpacked BCD).

Supponiamo, ad esempio, di voler convertire in binario il numero decimale 07050301h (in formato Unpacked BCD); dividendo ripetutamente tale numero per 02h con l'ausilio di AAD e DIV, otteniamo:
07050301h / 02h, Q = 03070605h, R = 01h
03070605h / 02h, Q = 01080802h, R = 01h
01080802h / 02h, Q = 00090401h, R = 00h
00090401h / 02h, Q = 00040700h, R = 01h
00040700h / 02h, Q = 00020305h, R = 00h
00020305h / 02h, Q = 00010107h, R = 01h
00010107h / 02h, Q = 00000508h, R = 01h
00000508h / 02h, Q = 00000209h, R = 00h
00000209h / 02h, Q = 00000104h, R = 01h
00000104h / 02h, Q = 00000007h, R = 00h
00000007h / 02h, Q = 00000003h, R = 01h
00000003h / 02h, Q = 00000001h, R = 01h
00000001h / 02h, Q = 00000000h, R = 01h
Unendo assieme i vari resti (a partire dall'ultimo), otteniamo il numero binario a 13 cifre 1110101101011b; come verifica, osserviamo che traducendo tale numero in base 10, risulta: Per poter memorizzare i vari resti nei singoli bit di un numero binario, abbiamo bisogno delle istruzioni logiche; tali istruzioni, vengono illustrate nel capitolo successivo.

18.9.2 Conversione da binario a BCD

Per la conversione da binario a BCD, dobbiamo utilizzare la cifra Ah (base dei numeri decimali) come divisore; in questo modo, è possibile applicare lo stesso procedimento già illustrato nel capitolo precedente per l'istruzione DIV.

Supponiamo, ad esempio, di voler convertire in BCD il numero binario 1110101101011b=1D6Bh che abbiamo ottenuto in precedenza; dividendo ripetutamente tale numero per Ah con l'ausilio di DIV, otteniamo:
1DB6h / Ah, Q = 02F1h, R = 1h
02F1h / Ah, Q = 004Bh, R = 3h
004Bh / Ah, Q = 0007h, R = 5h
0007h / Ah, Q = 0000h, R = 7h
Unendo assieme i vari resti (a partire dall'ultimo), otteniamo il numero decimale a 4 cifre 7531; avendo a disposizione le singole cifre del numero, possiamo facilmente memorizzarle in formato Unpacked BCD (per il formato Packed BCD abbiamo bisogno delle istruzioni logiche).