Lavorare con le stringhe

Con il termine stringa non mi riferisco solo alla definizione classica di sequenza di caratteri, ma intendo una qualsiasi sequenza di byte, word,...
Le istruzioni che analizzeremo sono:
        MOVS
        SCAS
        CMPS
        LODS
        STOS
        INS
        OUTS
        REPxx
Queste instruzioni lavorano più o meno tutte nello stesso modo, secondo la seguente procedura:
1.Assicuratevi che il Flag di Direzione sia a posto:
        se è 0 la stringa viene processata dal basso verso l'alto
        se è 1 la stringa viene processata dall'alto verso il basso
        (dove con alto e basso intendo le posizioni di memoria)
 Le istruzioni per azzerare e settare il DF sono CLD e STD rispettivamente.

2.Caricate il numero di iterazioni nel registro CX.

3.Caricate l'indirizzo iniziale della stringa sorgente in DS:SI e quello
  della stringa destinazione in ES:DI

4.Usate le istruzioni di ripetizione al posto giusto
  (guardate la tabella seguente)

5.Mettete l'istruzione appropriata come operando dell'istruzione di
  ripetizione
nota: preciso che "basso" indica le posizioni più basse di memoria e "alto" quelle con indirizzi maggiori.

Vi riporto in una tabella le istruzioni con i prefissi che usano e su che tipo di operandi lavorano.

  Istruzione    Prefisso di rip.    Sorg/Dest           Registri
  -------------------------------------------------------------------------
  MOVS          REP                 Entrambi            DS:SI,ES:DI
  SCAS          REPE/REPNE          Destinazione        ES:DI
  CMPS          REPE/REPNE          Entrambi            ES:DI,DS:SI
  LODS          niente              Sorgente            DS:SI
  STOS          REP                 Destinazione        ES:DI
  INS           REP                 Destinazione        ES:DI
  OUTS          REP                 Sorgente            DS:SI

Queste istruzioni hanno 2 tipi di sintassi:
        [prefisso di ripetizione] istruzione [ES:[dest]],[sorgente]
        [prefisso di ripetizione] istruzione < B|W|niente >
Dove B e W stanno per Byte e Word rispettivamente. Ad esempio, l'istruzione MOVS può essere data con operandi (byte o word), oppure posso usare MOVSB per spostare byte o MOVSW per spostare word.
I prefissi di ripetizione controllano il valore di CX e le eventuali condizioni specificate:
        REP  -  Ripete fino a quando CX=0
        REPE o REPZ  -  Ripete mentre è uguale o fino a quando CX=0
        REPNE o REPNZ  -  Ripete mentre è diverso o fino a quando CX=0
Durante l'esecuzione un'istruzione preceduta da uno di questi prefissi segue il seguente algoritmo:
1.Controlla il valore di CX, se CX=0 esce.
2.Esegue l'operazione sulla stringa.
3.Aumente il valore di SI e/o DI (o diminuisce, dipende dal DF).
  L'incremento è 1 per le operazioni sui byte, 2 per quelle sulle word.
4.Decrementa CX
5.Se l'istruzione è SCAS o CMPS controlla lo ZF (Zero Flag) ed esce se la
  condizione è falsa.
6.Torna al punto 1.
Ora che abbiamo visto in generale come funzionano vediamo a cosa servono e come si usano una ad una.

>MOVS
Questa serve per muovere una stringa da una locazione di memoria ad un altra. La sintassi da usare è:

        [REP]   MOVS    < destinazione >,< sorgente >
        [REP]   MOVSB
        [REP]   MOVSW
Dove i suffissi B e W stanno per Byte e Word.
Al solito ecco un esempio del loro utilizzo:
        .MODEL small
        .DATA
source  DB      10 DUP ('0123456789')
destin  DB      10 DUP (?)
        .CODE
        mov     ax,@data        ;è un altro modo per caricare il segmento
        mov     ds,ax           ;lo metto in DS
        mov     es,ax           ;e anche in ES
        ...
        ...
        ...
        cld
        mov     cx,10               ;imposto il numero di iterazioni
        mov     si,OFFSET source    ;Carico l'ind. del sorgente
        mov     di,OFFSET destin    ;Carico l'ind. della destinazione
        rep     movsb               ;sposto 10 byte
Nota: ho a bella posta inserito una variazione nell'esempio di sopra. Ve ne siete accorti? La direttiva:
source  DB      10 DUP ('0123456789')
definisce 10 stringhe consecutive di 10 caratteri, invece di una stringa di soli 10 car, che invece sarebbe definita tramite
source	DB	"0123456789"
oppure
source	DB	'0123456789'
Spesso è piu efficiente spostare le stringhe come word che non come byte effettuando cosi metà iterazioni. (Attenzione però al numero di byte che compongono la stringa: deve essere pari).

>SCAS
Questa istruzione è usata per cercare in una stringa uno specificato valore.

        [REPE|REPNE]    SCAS    [ES:]< dest >
        [REPE|REPNE]    SCASB
        [REPE|REPNE]    SCASW
La stringa su cui operare deve essere all'indirizzo ES:DI e il valore da cercare deve essere in AL o in AX (byte o word rispettivamente).
Quando trova il valore cercato viente settato lo ZF.
        .DATA
stringa DB      "Ken il guerriero"      
lung    EQU     $-stringa               ;lunghezza della stringa
pstringa DD     stringa                 ;puntatore alla stringa
        .CODE
        ...
        ...
        cld                             ;direzione dx-->sx
        mov     cx,lung                 
        les     di,pstringa
        mov     al,'r'                  ;carattere da cercare
        repne   scasb                   ;cerco
        jnz     non_trovato             ;se non lo trovo salto
        ...                         ;ES:DI-1 punta al carattere che cercavo
        ...                         ;in questo caso la prima r
        ...
        ...

non_trovato:

Anche qui una novità: la definizione
lung    EQU     $-stringa               ;lunghezza della stringa
fa calcolare all'assemblatore la lunghezza di una stringa. In tal caso quindi lung rappresenta il valore 16. In che modo viene calcolata la lunghezza? $ rappresenta il puntatore far alla posizione corrente; anche stringa è un puntatore far (infatti l'istruzione seguente inizializza pstringa con questo valore). Perciò $-stringa è un'espressione dell'aritmetica dei puntatori che viene valutata dall'assemblatore. Chi conosce il C, sa bene che
		puntatore-puntatore=numero
e il numero rappresenta in tal caso la lunghezza della stringa.
Se avessi scritto:
        .DATA
stringa DB      "Ken il guerriero"
io      DB      "Antonio"
lung    EQU     $-stringa
è tutto giusto, però lung rappresenta ora il valore 23 e non 17 (la somma delle lunghezze delle due stringhe stringa e io).

Notate che la scasb decrementa DI ogni volta, anche quando imposta il flag di zero, perciò non è ES:DI che punta al carattere che cercavo, bensì ES:DI-1

>CMPS
Questa è usata per confrontare due stringhe.

        [REPE|REPNE]    CMPS    < sorg >,[ES:]< dest >
        [REPE|REPNE]    CMPSB
        [REPE|REPNE]    CMPSW
NOTA: Attenzione che in CMPS il sorgente è l'operatore a sinistra !!!!
CMPS lavora confrontando uno ad uno i valori puntati da DI con quelli puntati da SI, se sono uguali viene settato lo ZF.

Esempio:

        .MODEL  Large
        .DATA
string1 DB      "Ken il guerriero"
        .FARDATA                     ;uso un altro segmento dati di tipo FAR
string2 DB      "Ken il pippero"
lung    EQU      $-string2
        .CODE
        mov     ax,@data                ;carico i due segmenti in DS e ES
        mov     ds,ax
        mov     ax,@fardata
        mov     es,ax
        ...
        ...
        cld
        mov     cx,lung
        mov     si,OFFSET string1
        mov     di,OFFSET string2
        repe    cmpsb           ;confronto
        jz      sono_uguali     ;se ZF=0 sono uguali, altrimenti...
        dec     di              ;posiziono i puntatori sul carattere diverso
        dec     si
        ...
        ...
sono_uguali:
>STOS
Questa serve per riempire una stringa con un determinato valore. Sintassi:
        [REP]   STOS    [ES:]< destinazione >
        [REP]   STOSB
        [REP]   STOSW
Il valore con cui riempire le stringhe va messo in AL o AX (byte o word risp.)

Esempio:

        .MODEL  small
        .DATA
stringa DB      100 DUP(?)
        .CODE
        ...
        ...
        cld                       ;direzione dx-->sx
        mov     ax,'aa'           ;valore con cui riempire
        mov     cx,50             ;numero di iterazioni
        mov     di,OFFSET stringa ;puntatore alla stringa
        rep     stosw             ;riempio con word
Notate che usando la STOSW che riempie con word, facendo 50 iterazioni riempio tutti i 100 byte !!!

>LODS
Questa serve per caricare un valore da una stringa ad un registro. La sintassi:

        LODS    [seg:]< sorgente >
        LODSB
        LODSW
Il valore che carichiamo va a finire in AL o AX e la stringa deve essere puntata da DS:SI.
Al contrario delle altre istruzioni LODS non è usata con i prefissi, non avrebbe molto senso!

Esempio :

        .DATA
numeri  DB      0,1,2,3,4,5,6,7,8,9
        .CODE
        ...
        ...
        cld
        mov     cx,10           ;numero di iterazioni
        mov     si,OFFSET numeri
        mov     ah,2
prendi: lodsb

        add     al,'0'  ;converte in ASCII
        mov     dl,al   
        int     21h     ;lo visualizza
        loop    prendi  ;torna a cercare
>OUTS, INS
Queste istruzioni servono per trasferire stringhe dalle/alle porte. La loro sintassi:
        OUTS DX,[seg:]< sorgente >
        OUTSB
        OUTSW

        INS  [ES:]< destinazione >,DX
        INSB
        INSW
Il numero della porta va specificato in DX e non puo' essere specificato per valore.

Esempio:

        .DATA
count   EQU     100
buffer  DB      count DUP(?)
inport  DW      ?
        .CODE
        ...
        ...
        cld                        ;direzione
        mov     cx,count           ;numero iterazioni     
        mov     di,OFFSET buffer   ;puntatore al buffer     
        mov     dx,inport          ;numero della porta     
        rep     insb               ;carica nel buffer
Si usa CX come contatore del numero di byte/word da trasferire.


Anche per stavolta avrei terminato, ho aggiunto al vostro bagaglio parecchie nuove istruzioni molto utili per copiare spostare ... dati, che è poi la cosa principale che un programma fa !!!


Assembly Page di Antonio
<< Indice >>