![]() | ![]() |
|
|
sono possibili solo i seguenti spostamenti:
memoria <--> registroNon sono possibili spostamenti da memoria a memoria e per far ciò si deve ricorrere all'uso di registri di supporto:
memoria <--> registro <--> memoriaEsempi di spostamenti memoria , NON AMMESSI, sono (in linguaggio macchina):
Esercizio (facile!): espandere questi spostamenti usando ad es. al/ax come registro di supporto.
> MOV
Il metodo più comune per spostare i dati è quello di utilizzare l'istruzione
MOV. La sintassi completa dell'istruzione è la seguente:
Alcuni esempi sono:
mov ax,7 ;valore --> registro> XCHG
Un'altra istruzione per eseguire spostamenti di dati è XCHG che scambia tra
loro i due operandi :
Ad esempio:
xchg ax,bx ; pippo:=ax;Attenzione che non è possibile fare lo scambio di due dati entrambi in memoria.
> LAHF e SAHF
Per esaminare il registro dei flag esistono queste due istruzioni: LAHF che
carica in AH, SAHF che li salva. Spero vi sia sorto un dubbio. Come faccio a
far stare l'intero registro di flag in 8 bit ??
Bene queste due istruzioni lavorano solo sugli 8 bit meno significativi del
registro.
Esistono comunque altre due istruzioni per salvare e ripristinare l'intero
registro di flag nello Stack ma le vedremo dopo.
-- CONVERTIRE LE DIMENSIONI
DI UN DATO --
Siccome trasferire dati tra registri di diversa dimensione non è consentito,per
fare questo tipo di operazione si devono prendere alcuni accorgimenti.
Prima di tutto ci si comporta in modo diverso a seconda che il dato abbia o no
il segno.
Nel caso di SIGNED VALUE l'Assembly mette a disposizione due istruzioni:CBW
(Convert Byte to Word) e CWD (Convert Word to Doubleword).
CBW converte il dato a 8 bit con segno contenuto in AL mettendolo in AX
(16bit). CWD come la precendente prende il dato in AX (16bit) e lo mette in
DX:AX.
Per capire meglio vediamo un esempio:
-- USARE I PUNTATORI --
>LEA
L'istruzione LEA (Load Effective Address) carica un puntatore di tipo NEAR nel
registro specificato, la sua sintassi è :
Ad esempio:
LEA dx,stringa ;carica in dx l'offset di stringaequivale a
MOV dx,OFFSET stringaNOTA : il secondo modo (con l'uso di MOV) in tal caso
è più veloce essendo l'OFFSET una costante nota all'assembler che quindi non
deve essere calcolata.
Nota: più veloce in tal caso significa risparmiare qualche ciclo di clock
(!!!), ma ciò avviene solo sugli 8088 (!!!) Chi più usa un 8088? (io ce l'ho
uno, per ricordo)
E comunque in tal caso TASM è in grado di tradurre una
L'istruzione LEA è più utile per calcolare l'indirizzo indiretto:
LEA dx,stringa[si] ;mette in dx l'indirizzo di stringa[si]equivale in linguaggio macchina a
LEA dx,[si+OFFSET stringa]>LDS e LES
Queste sono le istruzioni analoghe a LEA solo che si usano per puntatori FAR.
La loro sintassi è uguale all'istruzione LEA.
Il registro segmento nel quale viene messo il dato dipende dall'istruzione: LDS
mette il segmento in DS e LES lo mette in ES.
Queste istruzioni sono spesso usate con le stringhe come vedremo più avanti.
Vediamo comunque un esempietto:
-- OPERAZIONI SULLO STACK --
Lo stack è un'area di memoria di fondamentale importanza nei processori 80x86 e
molte istruzioni quali CALL, INT, RET, IRET fanno uso dello stack per salvare
parametri, indirizzi e registri.
>PUSH e POP
Queste sono le due istruzioni base rispettivamente per scrivere e leggere nello
stack, esse modificano automaticamente il valore di SP. La loro sintassi è la seguente:
NOTA: PUSH valore è disponibile solo sui processori
80186 e superiori. Attenzione che le dimensioni di una "cella" di
stack è di 2 byte.
qualche esempio:
Una breve nota: lo stack a differenza di altre
strutture comincia a memorizzare i dati nelle locazioni alte di memoria e via
via scende verso il basso man mano che aggiungo dati.
Quindi se decido di accedere allo stack tramite indirizzamento indiretto dovrò
fare nel seguente modo:
A questo punto però non ho vuotato lo stack (ho solo letto i valori) per ripristinarlo come prima devo aggiungere l'istruzione:
sub sp,6sottraggo cioè 6 byte dallo Stack Pointer (2 byte per
ogni registro).
Graficamente la situazione è questa:
Questo prima della sub.
>PUSHF e POPF
Sono istruzioni che salvano nello stack e ripristano il valore del registro di
flag.
Per processori 386 e sup. ci sono anche le due istruzioni rispettive per i
registri a 32 bit : PUSHFD e POPFD.
>PUSHA e POPA
Salvano e ripristinano tutti i registri nel seguente ordine: AX,CX,DX,BX,SP,BP,
SI e DI. Il valore di SP che viene salvato è quello prima dell'istruzione PUSHA
Naturalmente la POPA li estrae in ordine inverso.
Per processori 386 e sup. ci sono anche le due istruzioni rispettive per i
registri a 32 bit : PUSHAD e POPAD.
-- SCRIVERE E LEGGERE SULLE
PORTE DI I/O --
Le porte sono il mezzo per far comunicare la CPU con le schede presenti nel
computer. Ogni porta ha un suo numero che può essere usato per accedervi.
>IN e OUT
IN legge il dato dalla porta e OUT lo manda alla porta, la loro sintassi è:
NOTA: ricordo che l'accumulatore è per le famiglie
80x86 il registro ax che può essere usato a 8,16 o 32 bit (al/ah,ax,eax).
Si deve necessariamente usare il registro DX per accedere ad una porta il cui
numero è maggiore di 256.
Vediamo un esempio dell'uso di queste istruzioni con questa sequenza di
istruzioni che fanno suonare lo speaker:
Nei processori 80186 e superiori sono state introdotte altre istruzioni per leggere e scrivere stringhe :
INS < [ES:]destinazione >,DXe le rispettive out:
OUTSLa INS e la OUTS non accettato un valore immediato per l'indirizzo e si deve far uso di DX.
In questo aticolo analizzeremo le istruzioni che l'Assembly mette a disposizione per effettuare calcoli matematici(somme, sottrazioni, ...) nonchè quelle che permettono di manipolare i dati a livello di bit (AND, OR, ...)
>ADD, ADC e INC
La prima operazione matematica che si impara alle elementari è senza dubbio la
somma ed anche io comincio con quella. Le istruzioni che eseguono la somma sono
tre :
NOTA: dato che i trasferimenti diretti memoria-memoria non sono possibili i due operandi non possono essere entrambi locazioni di memoria.
Perchè 3 istruzioni ?
ADD op1,op2 effettua la somma tra op1 e op2 e mette il risultato in op1
ADC op1,op2 effettua la somma tra op1 e op2, mette il risultato in op1 e se c'è
riporto somma 1 al risultato
INC op incrementa di 1 op; il numero è considerato senza segno quindi non viene
modificato il valore del CF in caso di riporto.
Queste operazioni
effettuano una somma a prescindere dal fatto che si stia lavorando con numeri
con segno o senza segno, sta al programmatore interpretare il valore della
somma.
Sta anche al programmatore il compito di controllare il risultato in caso di
OVERFLOW (quando il valore della somma non sta nei registri) analizzando il
valore del bit di overflow nel Flag Register.
Vediamo alcuni esempi
Tramite l'istruzione ADC è possibile effettuare somme che non stanno in un unico registro:
.DATAQuesto programma effettua la somma 316423+43981
In questo caso il risultato è contenuto in DX:AX
>SUB, SBB, DEC e NEG
Dopo l'addizione non poteva mancare la sottrazione!
La sintassi per queste operazioni è la seguente:
Cominciamo dalla SUB che funziona esattamente come la
ADD: sottrae dal primo operando il secondo e mette il risultato nel primo.
Valgono le stesse osservazioni fatte per la somma quindi se non ve le ricordate
andate a rileggerle.
Idem per quanto riguarda la DEC che altro non fa che sottrarre 1 all'operando,
anche qui il numero è trattato come senza segno.
Vi riporto anche qui qualche esempio per capire meglio:
Quando il risultato va sotto lo zero viene settato il
SF (sign flag).
Per poter effettuare le sottrazioni vere e proprie con tanto di riporto si usa
la SBB (subtract with borrow) che funziona esattamente come la SUB solo che in
caso di riporto viene sottratto 1 al risultato.
Esempio:
.DATAL'ultima istruzione che rientra nella categoria delle
sottrazioni è la NEG che cambia il segno dell'operando e che quindi deve essere
usata solo per i numeri con segno.
ES:
NOTA: Vi ricordo che i numeri negativi sono rappresentati in complemento a 2.
>MUL e IMUL
La terza operazione che vediamo è la moltiplicazione, senza segno (MUL) e con
segno (IMUL).
La loro sintassi è la seguente:
Vi chiederete dove sta il secondo fattore. Bene
l'operando specificato nella MUL (o nella IMUL) verrà moltiplicato per il
numero che è contenuto nell'accumulatore (AX per i 16bit, AL per gli 8bit o EAX
per i 32bit).
E il risultato?
Quello dipende dagli operandi:
Anche qui vediamo alcuni esempi:
.DATADa questo esempio si capisce anche come funziona la IMUL.
Per processori 80186 e superiori la IMUL prevede anche le seguenti sintassi:
IMUL < registro >,< valore >In questo caso il risultato viene messo nel registro
specificato e nel caso non ci stia viene settato CF e OF.
Quando specifico 3 parametri il primo è la destinazione e gli altri due sono i
fattori del prodotto.
Esempi:
>DIV e IDIV
Anche per la divisione esistono 2 istruzioni una per i numeri senza segno (DIV)
e una per quelli col segno (IDIV).
Entrambe ritornano il quoziente e il resto, e la loro sintassi è:
Per effettuare una divisione di un numero a 16bit per
uno di 8bit devo mettere il numero da dividere in AX e specificare il divisore
(cioè l'altro numero) in un altro registro. Il risultato della divisione sarà
in AL e il resto in AH ; quindi fate attenzione il dividendo viene perso.
Se invece voglio dividere un numero a 32 bit per uno di 16 si deve mettere il
dividendo in DX:AX, specificare il divisore in un registro a 16 bit (che non
sia AX o DX) ed effettuare la divisione: avremo il risultato in AX e il resto
in DX.
Per dividere tra loro due numeri a 16 bit si deve prima trasformare il
dividendo in un numero a 32 bit tramite l'istruzione CWD (andate a rivederla)
se ci interessa il segno oppure nel modo visto nel tutorial precedente se
stiamo lavorando con numeri senza segno.
Se si cerca di effettuare una divisione per 0 o se il quoziente non sta nei
registri viene generata una chiamata all'int 0 ,il programma termina e il
controllo torna al DOS.
Per evitare questo problema ci sono due modi: o si controlla il valore del
divisore prima di effettuare l'istruzione DIV, oppure intercettare l'int 0 e
riscriversi una routine di interrupt per gestire l'evento ma questo lo vedremo
più avanti!!
Vi riporto quelche esempio:
Ora che abbiamo visto le 4 operazioni fondamentali
analizzeremo alcune istruzioni che lavorano con i numeri binari in notazione
BCD.
Vi chiederete perchè; beh sappiate che lavorare con in numeri in BCD spesso è
più facile per fare calcoli.
Queste istruzioni non hanno operandi perche lavorano sempre con al:
>AAA (Adjust after an
Addition)
Per capire cosa fa vediamo un esempio:
Come si vede dall'esempio il risulato (in
"unpacked" BCD) l'ho in ax: in ah ci sono le decine e in al le unità.
Notate che setta anche il Carry a 1 (questo indica che il risultato in decimale
non sta in una cifra, ma è composto da 2 cifre).
Ricordatevi che una cifra in decimale viene qui codificata con 1byte.
>AAS (Adjust After a Subtraction)
mov ax,103h ;ax=13h (in BCD : ah=1 al=3 !!!)Dopo la prima istruzione mov, ax è ovviamente uguale a 103h; tuttavia un'altra interpretazione è: contiene la versione (non compattata) del valore BCD 13.
>AAM (Adjust After a Multiplication)
mov ax,903hAttenzione : questa istruzione si può usare solo per le moltiplicazioni senza segno !!!
>AAD (Adjust BEFORE a
division)
A differenza delle altre 3 questa va usata prima di una divisione infatti
trasforma il numero in BCD in codice binario prima dell'operazione. Dopo la
divisione il risultato va risistemato tramite un AAM. Vediamo ad esempio
(25/2):
Notate che il resto viene perso: se vi serve ricordatevi di salvarlo prima dell'istruzione AAM.
Abbiamo visto come si
lavora con numeri a 2 cifre ma se devo usare numeri più grossi?
Semplice devo iterare il metodo per ogni cifra del numero !!
Esistono poi altre due
istruzioni per lavorare con un altro tipo di numeri BCD cosiddetti
"packed" in cui ogni cifra occupa solo 4 bit.
A differenza delle altre non cambiano il valore di ah ma lavorano sul registro
di flag ogni volta che c'è un riporto.
>DAA (Adjust After an Addition)
mov ax,8833h ;carica 88 e 33 in un unico registro>DAS (Adjust After an Subtraction)
mov ax,3883h ;carica 38 e 83 in BCDVediamo ora le istruzioni che il processore ci mette
a disposizione per effettuare operazioni logiche.
Qui andrò abbastanza veloce, il significato degli operatori l'ho già spiegato
perciò mi limiterò a elencare la sintassi delle istruzioni e a fare qualche
breve commento.
>AND <
registro|memoria >,< registro|memoria|valore >
Effettua un AND bit a bit degli operandi.
L'AND può essere utile per azzerare alcuni bit di un registro tramite una
"maschera" in cui i bit da azzerare sono 0 e gli altri sono 1:
I bit di solito vengono convenzionalmente contati a partire da destra e da 0.
>OR <
registro|memoria >,< registro|memoria|valore >
Effettua l'OR bit a bit.
Può essere utile per settare solo alcuni bit di un registro con un'opportuna
"maschera" di bit.
>XOR <
registro|memoria >,< registro|memoria|valore >
Effettua l'OR esclusivo bit a bit.
E' molto usata per azzerare il valore di un registro facendo XOR con se stesso:
è molto veloce!!
>NOT <
registro|memoria >
Effettua il NOT logico sui bit dell'operando.
Vediamo infine alcune istruzioni non prettamente matematiche ma che vengono spesso utilizzate per questo scopo.
>SHL e SHR
Queste due istruzioni shiftano a sinistra e destra (risp.) l'argomento. Innanzi
tutto la sintassi è la seguente:
Vediamo cosa vuol dire con un esempio:
mov al,01001011 ;al=01001011In pratica è successo che tutti i bit sono stati
spostati di una posizione verso sx, il bit più significativo è andato nel CF e
quello meno significativo è stato messo a zero.
Dal punto di vista matematico ho effettuato una moltiplicazione per 2!! Lo
stesso discorso simmetrico vale per lo Shift a dx e in questo caso il numero
viene diviso per 2.
Se sposto di due posizioni il numero verra moltiplicato/diviso per 4, se sposto
di 3 per 8,di 4....
>ROR e ROL
Queste servono per far ruotare i bit a destra e sinistra (risp.) La loro
sintassi è uguale a shl e shr . Vediamo subito un esempio:
|
|
Privacy |
Articolo informazione
Commentare questo articolo:Non sei registratoDevi essere registrato per commentare ISCRIVITI |
Copiare il codice nella pagina web del tuo sito. |
Copyright InfTub.com 2025