Zoom Icon

Istruzioni Spostamento

From UIC

Le Istruzioni Per lo Spostamento Dati

Contents


Infos
Author: b0nu$
Email: bonus@numerica.it
Website:
Date: 01/01/2001 (dd/mm/yyyy)
Level: No brain needed
Language: Italian Image:Flag_Italian.gif
Comments:



Introduzione

Ora che abbiamo un'idea di come è fatto un programma in Assembly vedimo un po' più a fondo come si usano le istruzioni per per realizzare i nostri programmi. In questa prima parte dei tutorial dedicati alle istruzioni tratterò in dettaglio le istruzioni che servono per spostare, salvare e caricare i dati dalla/in memoria.

Muovere i dati è una dei principali compiti di un programma Assembly, abbiamo già visto che sono possibili solo i seguenti spostamenti:

                memoria  <-->  registro
                registro <-->  registro

Non sono possibili spostamenti da memoria a memoria e per far ciò si deve ricorrere all'uso di registri di supporto:

                memoria <--> registro <--> memoria


Istruzioni di movimento

MOV

Il metodo più comune per spostare i dati è quello di utilizzare l'istruzione MOV come abbiamo già visto nei precedenti esempi. La sintassi completa dell'istruzione è le seguente:

        MOV < registro|memoria > , < registro|memoria|valore imm. >

Alcuni esempi sono:

mov ax,7 ;valore --> registro
mov mem,45 ;valore --> memoria
mov ax,bx ;registro --> registro
mov mem[bx],7 ;valore --> memoria (indiretto)
...
...

XCHG

Un'altra istruzione per eseguire spostamenti di dati è XCHG che scambia tra loro i due operandi :

        XCHG < registro|memoria > , < registro|memoria >

Ad esempio:

xchg ax,bx ; pippo:=ax;
; ax:=bx;
; bx:=pippo;

LAHF e SAHF

Per esaminare il registro dei flag esistono queste due instruzioni: 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 quete due instruzioni lavorano solo sugli 8 bit meno significativi del registro. Esistono comunque altre due istruzioni per salvare e ripristinare il 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:

.DATA
mem8 DB -5
mem16 DW -5

.CODE
....
....
mov al,mem8 ;mette in AL -5 (FBh)
cbw ;converte (8-->16)(FBh-->FFFBh) in AX

mov ax,mem16 ;AX=-5 (FFFBh)
cwd ;converte (16-->32)(FFFB-->FFFF:FFFBh) in DS:AX


Usare i puntatori

LEA

L'istruzione LEA (Load Effective Address) carica un puntatore di tipo NEAR nel registro specificato, la sua sintassi è :

        LEA     registro,memoria

Ad esempio:

LEA dx,stringa ;carica in dx l'offset di stringa

equivale a

MOV dx,OFFSET stringa

NOTA:
il secondo modo (con l'uso di MOV) è più veloce essendo l'OFFSET una costante nota all'assembler che quindi non deve calcolarla. L'istruzione LEA è più utile per calcolare l'indirizzo indiretto:

LEA dx,stringa[si] ;mette in dx l'indirizzo di stringa[si]

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 (indovinate un po !!) in ES. Queste istruzioni sono spesso usate con le stringhe come vedremo più avanti. Vediamo comunque un esempietto:

.DATA

stringa DB "A me piace la Nutella"
fpStr DD stringa ;Puntatore FAR a stringa
punt DD 100 DUP(?)
.CODE
...
...
les di,fpStr ;mette l'indirizzo in ES:DI
lds si,punt[bx] ;mette l'indirizzo in DS:SI


Operazioni sullo stack

Lo stack è un'area di memoria di fondamentale imporatanza nei processo 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:

        PUSH    < registro|memoria|valore >
        POP     < registro|memoria >

NOTA:
PUSH valore è disponibile solo sui processori 80186 e superiori. Attenzione che le dimensioni di una "cella" di stack è di 2 byte. Ho già spiegato in un precedente tutorial l'uso dello stack (struttura LIFO) e qui non mi soffermo oltre e vi lascio con qualche esempio:

mov bp,sp ;preparo bp alla base dello stack
push ax ;salva il primo
push bx ;salva il secondo
push cx ;...
...
...
pop cx ;prelevo il terzo
pop bx ;prelevo il secondo
pop ax

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:

mov bp,sp
push ax ;salvo i 3 regsitri
push bx
push cx
...
...
mov ax,[bp-2] ;prelevo ax
mov bx,[bp-4] ;prelevo bx
mov cx,[bp-6] ;...

A questo punto però non ho vuotato lo stack (ho solo letto i valori) per ripristinarlo come prima devo aggiungere l'istruzione:

sub sp,6

sottraggo cioè 6 byte dallo Stack Pointer (2 byte per ogni registro). Graficamente la situazione è questa:

Stack
High Mem ---------
^ |  ??? | <-- BP
| |-------|
| | ax | <-- BP-2
| |-------|
| | bx | <-- BP-4
| |-------|
| | cx | <-- BP-6 = SP
Low Mem |-------|
| |

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 : PUSFD 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 è:

        IN      accumulatore,< numero porta|DX >
        OUT     < numero porta|DX >,accumulatore

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:

sound EQU 61h ;porta che controlla lo speaker
timer EQU 42h ;porta che fa suonare lo speaker
on EQU 00000011b ;i 2 LSB attivano lo speaker

in al,sound ;preleva lo stato attuale della porta
or al,on ;attiva lo speaker
out sound,al ;rimanda il valore

mov al,250 ;comincia da 250
suona: out timer,al
mov cx,0FFFFh ;aspetta "FFFF volte"
aspetta:loop aspetta

dec al
jnz suona

in al,sound
and al,NOT on ;spegne lo speaker
out sound,al ;ripristina la porta

Nei processori 80186 e superiori sono state introdotte altre istruzioni per leggere e scrivere stringhe :

        INS     < [ES:]destinazione >,DX
        INSB
        INSW

e le rispettive out:

        OUTS
        OUTSB
        OUTSW

La INS e la OUTS non accettato un valore immediato per l'indirizzo e si deve far uso di DX. Vedremo meglio queste istruzioni quando parleremo di stringhe.


Note Finali

Per adesso mi fermo qui, questa volta non vi presento nessun programma di più che altro perché non mi viene in mente niente su questo argomento; comunque potete sempre provare a scrivere qualcosa voi e se incontrate difficoltà, beh fatemelo sapere cercherò di aiutarvi.


Disclaimer

I documenti qui pubblicati sono da considerarsi pubblici e liberamente distribuibili, a patto che se ne citi la fonte di provenienza. Tutti i documenti presenti su queste pagine sono stati scritti esclusivamente a scopo di ricerca, nessuna di queste analisi è stata fatta per fini commerciali, o dietro alcun tipo di compenso. I documenti pubblicati presentano delle analisi puramente teoriche della struttura di un programma, in nessun caso il software è stato realmente disassemblato o modificato; ogni corrispondenza presente tra i documenti pubblicati e le istruzioni del software oggetto dell'analisi, è da ritenersi puramente casuale. Tutti i documenti vengono inviati in forma anonima ed automaticamente pubblicati, i diritti di tali opere appartengono esclusivamente al firmatario del documento (se presente), in nessun caso il gestore di questo sito, o del server su cui risiede, può essere ritenuto responsabile dei contenuti qui presenti, oltretutto il gestore del sito non è in grado di risalire all'identità del mittente dei documenti. Tutti i documenti ed i file di questo sito non presentano alcun tipo di garanzia, pertanto ne è sconsigliata a tutti la lettura o l'esecuzione, lo staff non si assume alcuna responsabilità per quanto riguarda l'uso improprio di tali documenti e/o file, è doveroso aggiungere che ogni riferimento a fatti cose o persone è da considerarsi PURAMENTE casuale. Tutti coloro che potrebbero ritenersi moralmente offesi dai contenuti di queste pagine, sono tenuti ad uscire immediatamente da questo sito.

Vogliamo inoltre ricordare che il Reverse Engineering è uno strumento tecnologico di grande potenza ed importanza, senza di esso non sarebbe possibile creare antivirus, scoprire funzioni malevoli e non dichiarate all'interno di un programma di pubblico utilizzo. Non sarebbe possibile scoprire, in assenza di un sistema sicuro per il controllo dell'integrità, se il "tal" programma è realmente quello che l'utente ha scelto di installare ed eseguire, né sarebbe possibile continuare lo sviluppo di quei programmi (o l'utilizzo di quelle periferiche) ritenuti obsoleti e non più supportati dalle fonti ufficiali.