L’Assembler 8086 Istruzioni per la Manipolazione delle Stringhe M. Rebaudengo - M. Sonza Reorda Politecnico di Torino Dip. di Automatica e Informatica 1 M. Rebaudengo, M. Sonza Reorda Istruzioni per la manipolazione delle stringhe L’8086 possiede un gruppo di istruzioni per la manipolazione di stringhe, ossia di sequenze contigue di byte o word. Le operazioni che possono essere effettuate sono: • la copiatura da una stringa sorgente ad una stringa destinazione; • il confronto tra due stringhe; • la ricerca di un valore all’interno di una stringa; • la modifica del contenuto di una stringa. 2 M. Rebaudengo, M. Sonza Reorda Istruzioni per la manipolazione di stringhe (segue) Tutte le istruzioni per la manipolazione delle stringhe hanno alcune caratteristiche comuni: • possono lavorare su una o due stringhe; • i due registri utilizzati come indici all’interno delle due stringhe vengono automaticamente aggiornati al termine dell’operazione elementare, in modo da puntare ciascuno all’elemento successivo all’interno della rispettiva stringa; • ciascuna delle istruzioni di manipolazione esegue una singola operazione: l’assembler 8086 fornisce un meccanismo per la ripetizione di tali operazioni per un numero prefissato di volte o fino al verificarsi di una determinata condizione. 3 M. Rebaudengo, M. Sonza Reorda Preparazione dei registri Tutte le istruzioni per la manipolazione delle stringhe richiedono che: • la stringa sorgente si trovi nel segmento di dato puntato da DS; il registro SI memorizza l’indirizzo di offset dell’elemento da processare all’interno della sequenza. • l’eventuale stringa destinazione si trovi nel segmento extra di dato puntato da ES; il registro DI memorizza l’indirizzo di offset dell’elemento da processare. 4 M. Rebaudengo, M. Sonza Reorda Extra e data segment coincidenti Se i dati sono contenuti in un unico segmento di dato, le operazioni da fare per predisporre i registri per la manipolazione di stringhe sono le seguenti: • copiare il contenuto del registro DS nel registro ES in modo da far coincidere i due segmenti; • copiare gli offset delle due stringhe nei registri SI e DI. Esempio LUNG STR1 STR2 5 EQU 100 .DATA DB LUNG DUP (?) DB LUNG DUP (?) .CODE ... PUSH DS POP ES LEA SI, STR1 LEA DI, STR2 M. Rebaudengo, M. Sonza Reorda Extra e data segment separati La condizione di default prevista è quella di avere la stringa sorgente nel segmento di dato e la stringa destinazione nel segmento dati extra. In questo caso per caricare i registri SI e DI basta eseguire l’istruzione di LEA. 6 .MODEL COMPACT ; 1 segm. di codice, più segm. di dato .FARDATA DSEG STR1 DB 100 DUP (?) .FARDATA ESEG STR2 DB 100 DUP (?) .CODE ... ASSUME DS:DSEG, ES:ESEG MOV AX, DSEG MOV DS, AX MOV AX, ESEG MOV ES, AX LEA SI, STR1 LEA DI, STR2 M. Rebaudengo, M. Sonza Reorda Stringa destinazione nel segmento di dato Nel caso in cui la stringa destinazione si trovi nel segmento di dato ed il segmento extra sia usato per memorizzare un diverso segmento di dati, occorre far coincidere temporaneamente il contenuto dei registri DS ed ES. Le operazioni da eseguire sono le seguenti: • salvare il contenuto di ES • copiare il contenuto di DS in ES • eseguire l’istruzione di manipolazione di stringhe • ripristinare il contenuto del registro ES. 7 M. Rebaudengo, M. Sonza Reorda Esempio LUNG STR1 DB STR2 DB STRADD 8 .MODEL COMPACT EQU 100 .FARDATA ESEG LUNG DUP (?) LUNG DUP (?) DD STR2 ... .CODE ... PUSH ES ; salvataggio di ES LEA SI, STR1 ; caricamento di SI LES DI, STRADD; caricamento di DI e ES ... ; istruzioni di manipolazione POP ES ; ripristino di ES M. Rebaudengo, M. Sonza Reorda La ripetizione delle istruzioni per la manipolazione di stringhe Ogni istruzione per la manipolazione di stringhe opera su un singolo elemento della stringa (byte o word) per volta. Per processare l’intera stringa occorre eseguire un ciclo che permetta di ripetere l’istruzione di manipolazione per tutti gli elementi della stringa. 9 M. Rebaudengo, M. Sonza Reorda La ripetizione delle istruzioni per la manipolazione di stringhe (segue) Il processore mette a disposizione una classe di istruzioni utili per implementare i cicli per la manipolazione delle stringhe: • REP • REPE • REPNE. Queste istruzioni vengono utilizzate in abbinamento con una delle istruzioni di manipolazione ed appaiono sulla stessa riga del codice. 10 M. Rebaudengo, M. Sonza Reorda La ripetizione delle istruzioni per la manipolazione di stringhe (segue) Formato REPxxx string_istr Funzionamento Questo statement ripete l’esecuzione dell’istruzione string_istr per un numero di volte pari al contenuto del registro CX. 11 M. Rebaudengo, M. Sonza Reorda La ripetizione delle istruzioni per la manipolazione di stringhe (segue) Le istruzioni REPE e REPNE sono due varianti che possono essere utilizzate abbinate alle istruzioni di confronto e di ricerca. • L’istruzione REPE ripete il ciclo finché il registro CX ha un valore diverso da 0 e le parole confrontate sono uguali. • L’istruzione REPNE ripete il ciclo finchè il registro CX ha un valore diverso da 0 e le parole confrontate sono diverse. 12 M. Rebaudengo, M. Sonza Reorda La ripetizione delle istruzioni per la manipolazione di stringhe (segue) Per utilizzare le istruzioni REP, REPE e REPNE occorre dunque caricare il registro CX con la dimensione della stringa. Analogamente a quanto avviene nel caso dell’istruzione LOOP, ad ogni esecuzione dell’istruzione il contenuto del registro CX viene decrementato di una unità. La differenza sostanziale tra l’istruzione LOOP e l’istruzione REP è che l’istruzione REP permette la ripetizione di un’unica istruzione di manipolazione di stringhe, mentre la LOOP permette di ripetere una generica sequenza di istruzioni. 13 M. Rebaudengo, M. Sonza Reorda Esempio LUNG STR1 STR2 14 EQU 100 .DATA DB LUNG DUP (?); stringa sorgente DB LUNG DUP (?); stringa destinaz. ... .CODE ... PUSH DS POP ES LEA SI, STR1 LEA DI, STR2 MOV CX, LUNG REP MOVSB ... M. Rebaudengo, M. Sonza Reorda Esempio (II) LUNG STR1 STR2 ciclo: 15 EQU 100 .DATA DB LUNG DUP (?) DB LUNG DUP (?) ... .CODE ... PUSH DS POP ES LEA SI, STR1 LEA DI, STR2 MOV CX, LUNG MOVSB LOOP ciclo ... Equivalente al precedente M. Rebaudengo, M. Sonza Reorda Aggiornamento del contenuto dei registri indice Le istruzioni per la manipolazione delle stringhe prevedono che i registri SI e DI contengano l’offset delle parole da processare. Quando si esegue ripetutamente una stessa istruzione di manipolazione, il contenuto dei registri è automaticamente aggiornato per indirizzare la parola successiva nella stringa. 16 M. Rebaudengo, M. Sonza Reorda Aggiornamento del contenuto dei registri indice (segue) Il contenuto dei registri SI e DI è aggiornato automaticamente dalla stessa istruzione di manipolazione, in base al valore del flag di direzione (DF): • se il flag DF vale 0, dopo ogni esecuzione dell’istruzione di manipolazione il contenuto dei registri di indice è incrementato di una unità per le stringhe di byte e di due unità per le stringhe di word. • se il flag DF vale 1, dopo ogni esecuzione dell’istruzione di manipolazione il contenuto dei registri di indice è decrementato di una unità per le stringhe di byte e di due unità per le stringhe di word. 17 M. Rebaudengo, M. Sonza Reorda Aggiornamento del contenuto dei registri indice (segue) A seconda del valore del flag DF le operazioni sulle stringhe vengono quindi eseguite in avanti con DF = 0 (forward) oppure all’indietro con DF = 1 (backword). Il valore del flag DF è modificato mediante le due istruzioni STD e CLD. L’istruzione STD forza il flag DF ad 1, mentre l’istruzione CLD forza il flag DF a 0. È bene ricordarsi di aggiornare sempre il valore del flag DF prima di eseguire un ciclo di manipolazione di stringhe, in quanto il processore non garantisce di mantenere costante il valore di DF tra due cicli di manipolazione. 18 M. Rebaudengo, M. Sonza Reorda Scansione in avanti LUNG STR1 STR2 19 EQU 100 .DATA DB LUNG DUP (?) DB LUNG DUP (?) ... .CODE PUSH DS POP ES LEA SI, STR1 LEA DI, STR2 MOV CX, LUNG CLD REP MOVSB ... ; flag DF = 0 M. Rebaudengo, M. Sonza Reorda Scansione all’indietro LUNG STR1 STR2 20 EQU 100 .DATA DB LUNG DUP DB LUNG DUP ... .CODE PUSH DS POP ES LEA SI, STR1 LEA DI, STR2 MOV CX, LUNG STD REP MOVSB ... (?) (?) + SIZE STR1 - TYPE STR1 + SIZE STR2 - TYPE STR2 ; flag DF = 1 M. Rebaudengo, M. Sonza Reorda Riepilogo delle operazioni necessarie per la manipolazione delle stringhe • Preparazione dei registri • caricamento del registro CX con il numero di elementi della stringa • aggiornamento del flag DF • esecuzione dell’istruzione REP (o REPE o REPNE), abbinata all’opportuna istruzione di manipolazione. 21 M. Rebaudengo, M. Sonza Reorda Istruzioni per la Manipolazione delle Stringhe e Interrupt Al termine di ogni istruzione primitiva i registri indice ed eventualmente CX (se vi è un prefisso) sono aggiornati; nel caso esista un prefisso, IP non viene aggiornato se non al termine dell'iterazione. In questo modo il processore può rilevare e servire richieste di interrupt senza attendere il termine dell'istruzione, che può essere ripresa dal punto in cui era stata interrotta. 22 M. Rebaudengo, M. Sonza Reorda Copiatura di una stringa Le istruzioni MOVSB e MOVSW permettono di copiare una stringa da un indirizzo sorgente ad un indirizzo destinazione. Formato MOVSB MOVSW Funzionamento L’istruzione MOVSB copia il byte avente indirizzo DS:SI nella locazione di memoria ES:DI. L’istruzione MOVSW copia la word avente indirizzo DS:SI nella locazione di memoria ES:DI. Al termine dell’esecuzione dell’istruzione vengono aggiornati entrambi i registri di indice. 23 M. Rebaudengo, M. Sonza Reorda Esempio: scalamento di una stringa di 5 posizioni LUNG STR 24 EQU 100 .DATA DB LUNG DUP (?) DB 5 DUP (?) ... .CODE PUSH DS POP ES LEA SI, STR LEA DI, STR + 5 MOV CX, LUNG CLD ; scansione in avanti REP MOVSB ... M. Rebaudengo, M. Sonza Reorda Esempio: scalamento di una stringa di 5 posizioni LUNG EQU 100 STR 25 .DATA DB LUNG DUP (?) DB 5 DUP (?) ... .CODE ... PUSH DS POP ES LEA SI, STR + SIZE STR - TYPE STR LEA DI, STR + SIZE STR - TYPE STR + 5 MOV CX, LUNG STD ; scansione all’indietro REP MOVSB ... M. Rebaudengo, M. Sonza Reorda Confronto tra stringhe Le istruzioni CMPSB e CMPSW permettono di confrontare due stringhe tra loro. Formato CMPSB CMPSW Funzionamento L’istruzione CMPSB lavora con stringhe di byte, mentre l’istruzione CMPSW lavora con stringhe di word. Ad ogni esecuzione dell’istruzione CMPSB/CMPSW viene effettuato il confronto tra la locazione di memoria avente indirizzo DS:SI e la locazione di memoria avente indirizzo ES:DI. Al termine dell’esecuzione dell’istruzione vengono aggiornati entrambi i registri di indice. 26 M. Rebaudengo, M. Sonza Reorda Confronto tra stringhe L’istruzione di confronto viene tipicamente usata congiuntamente alle istruzioni REPE e REPNE, ed in particolare: • si usa REPE se si vuole verificare se due stringhe sono uguali; • si usa REPNE se si vuole verificare se due stringhe sono diverse. Il ciclo di confronto termina al verificarsi di una delle seguenti condizioni: • la stringa è terminata (il registro CX ha valore nullo) • è stata trovata una disuguaglianza (con REPE) od una uguaglianza (con REPNE). 27 M. Rebaudengo, M. Sonza Reorda Esempio LUNG STR1 STR2 28 EQU 100 .DATA DB LUNG DUP (?) DB LUNG DUP (?) ... .CODE ... PUSH DS POP ES LEA SI, STR1 LEA DI, STR2 MOV CX, LUNG CLD REPE CMPSB ... M. Rebaudengo, M. Sonza Reorda Confronto tra stringhe (segue) Ogni esecuzione dell’istruzione di confronto aggiorna i flag. È possibile analizzare lo stato del flag ZF per verificare se la condizione di uguaglianza è stata verificata oppure no. Questa operazione può essere eseguita mediante un’istruzione di salto condizionato. All’uscita del ciclo è normalmente utile conoscere quale parola ha causato la terminazione. Tale informazione è contenuta nel registro SI. Poiché il registro SI è aggiornato alla fine di ciascun confronto, all’uscita del ciclo il registro SI contiene l’offset della parola successiva a quella che ha causato la terminazione. 29 M. Rebaudengo, M. Sonza Reorda LUNG STR1 STR2 lab1: lab2: 30 Esempio EQU 100 .DATA DB LUNG DUP (?) DB LUNG DUP (?) .CODE Confronta due stringhe e scrive in AL: … • 0, se sono uguali PUSH DS POP ES • il primo carattere diverso, se non lo LEA SI, STR1 sono. LEA DI, STR2 MOV CX, LUNG CLD REPE CMPSB JE lab1 ; stringhe uguali ? DEC SI ; No: decrementa SI MOV AL, [SI] ; primo carattere diverso JMP lab2 MOV AL, 0 … M. Rebaudengo, M. Sonza Reorda Scansione di una stringa Le istruzioni SCASB e SCASW permettono di scandire una stringa per ricercare un valore specifico. Formato SCASB SCASW Funzionamento L’istruzione SCASB lavora con stringhe di byte, mentre l’istruzione SCASW lavora con stringhe di word. L’istruzione SCASB usa i registri AL e DI, mentre l’istruzione SCASW usa i registri AX e DI. I registri AL e AX contengono il valore da confrontare, mentre DI contiene l’offset della stringa da scandire. Al termine dell’esecuzione dell’istruzione viene aggiornato il contenuto del registro DI. 31 M. Rebaudengo, M. Sonza Reorda Scansione di una stringa (segue) Ad ogni esecuzione dell’istruzione SCASB/SCASW viene effettuato il confronto tra il contenuto della locazione ES:DI ed il contenuto del registro accumulatore. Anche le istruzioni SCASB e SCASW utilizzano le istruzioni REPE e REPNE per generare il ciclo. • per cercare un valore diverso dal contenuto dei registri AL ed AX si usa l’istruzione REPE, • per cercare un valore coincidente al contenuto dei registri AL ed AX si usa l’istruzione REPNE. 32 M. Rebaudengo, M. Sonza Reorda Scansione di una stringa (segue) Il ciclo di scansione termina al verificarsi di una delle seguenti condizioni: • la stringa è terminata (il registro CX ha valore nullo). • è stata trovata una disuguaglianza tra il contenuto del registro accumulatore e la stringa indirizzata da DI (con REPE), oppure è stata trovata una uguaglianza tra il contenuto del registro accumulatore e la stringa indirizzata da DI (con REPNE). 33 M. Rebaudengo, M. Sonza Reorda Esempio Si realizzi un frammento di programma che scandisca una stringa di caratteri e che visualizzi il primo carattere diverso dal carattere spazio. #include <stdio.h> main() { int i; char string[25]; strcpy(string," Fatti non foste a viver come bruti ..."); for (i=0 ; i<25 ; i++) if (string[i] != ' ') { printf("%c",string[i]); break; } ... } 34 M. Rebaudengo, M. Sonza Reorda Soluzione Assembler .MODEL small STRING LUNG ST_ADD 35 esci:... .STACK .DATA DB " Fatti non foste a viver come bruti" EQU $-STRING DD STRING ... .CODE ... MOV AL, " " LES DI, ST_ADD MOV CX, LUNG CLD REPE SCASB JE esci DEC DI MOV DL, [DI] MOV AH, 02H ; visualizza INT 21H M. Rebaudengo, M. Sonza Reorda Inizializzazione di una stringa Le istruzioni STOSB e STOSW permettono di inizializzare tutti gli elementi di una stringa ad un determinato valore. Formato STOSB STOSW Funzionamento L’istruzione STOSB lavora con stringhe di byte, mentre l’istruzione STOSW lavora con stringhe di word. L’istruzione STOSB usa i registri AL e DI, l’istruzione STOSW usa i registri AX e DI. I registri AL e AX contengono il valore con cui viene inizializzata la stringa, DI contiene l’offset della stringa da inizializzare. 36 M. Rebaudengo, M. Sonza Reorda Inizializzazione di una stringa (segue) Al termine dell’esecuzione dell’istruzione viene aggiornato il contenuto del registro DI. L’effetto delle istruzioni STOSB/STOSW è di copiare il valore contenuto nei registri AL (od AX) nella locazione di memoria avente indirizzo ES:DI. 37 M. Rebaudengo, M. Sonza Reorda Esempio Si vuole realizzare un frammento di programma che scriva il carattere spazio in tutti gli elementi di una stringa. main() { int i; char str[26]; ... for (i=0 ; i<25 ; i++) str[i] = ' '; str[25] = '\0'; ... } 38 M. Rebaudengo, M. Sonza Reorda Soluzione Assembler LUNG STR ST_ADD 39 EQU 25 .MODEL small .STACK .DATA DB LUNG DUP(?) DD STR ... .CODE ... MOV AL, " " LES DI, ST_ADD MOV CX, LUNG CLD REP STOSB ... M. Rebaudengo, M. Sonza Reorda Elaborazione di una stringa Le istruzioni LODSB e LODSW permettono di copiare un elemento di una stringa nei registri accumulatore. Formato LODSB LODSW Funzionamento L’istruzione LODSB lavora con stringhe di byte, mentre l’istruzione LODSW lavora con stringhe di word. L’istruzione LODSB usa i registri AL e SI, l’istruzione LODSW usa i registri AX e SI. 40 M. Rebaudengo, M. Sonza Reorda Elaborazione di una stringa (segue) L’effetto delle istruzioni LODSB e LODSW è quello di copiare il contenuto della locazione di memoria avente indirizzo DS:SI nei registri AL o AX. 41 M. Rebaudengo, M. Sonza Reorda Esercizio Conteggio del numero di spazi bianchi in una stringa di caratteri. main() { char sorg[100]; int i, count = 0; ... for (i=0 ; i<100 ; i++) if (sorg[i] == ' ') count++; ... } 42 M. Rebaudengo, M. Sonza Reorda LUNG EQU 100 .MODEL small .STACK .DATA SORG DB LUNG DUP (?) ... .CODE ... LEA SI, SORG MOV CX, LUNG MOV BX, 0 CLD ciclo: LODSB CMP AL, ' ' ; AL = ' ' ? JNE next ; No: va a next INC BX next: LOOP ciclo Soluzione Assembler 43 ... ; Sì: fine M. Rebaudengo, M. Sonza Reorda Elaborazione di una stringa (segue) Le istruzioni LODSB e LODSW sono utili in coppia con le istruzioni STOSB/STOSW per elaborare il contenuto di una stringa, ossia per eseguire la stessa operazione su tutte le parole di una stringa. Per elaborare una stringa: • si copia l’offset della stringa sorgente in SI; • si copia l’offset della stringa destinazione in DI; • si copia la lunghezza della stringa in CX; • si aggiorna il flag DF; • per ogni parola della stringa sorgente: • si copia la parola nel registro accumulatore • si elabora il contenuto del registro • si copia il contenuto accumulatore nella stringa destinazione. 44 M. Rebaudengo, M. Sonza Reorda Esempio 45 Si vuole realizzare un frammento di programma che trasferisca un vettore di interi da una zona di memoria ad un’altra, con la condizione che i termini negativi siano trasformati in termini di valore nullo. main() { int sorg[100], dest[100], i; ... for (i=0 ; i<100 ; i++) if (sorg[i] < 0) dest[i] = 0; else dest[i] = sorg[i]; ... } M. Rebaudengo, M. Sonza Reorda Soluzione Assembler LUNG EQU 100 .MODEL small .STACK .DATA SORG DW LUNG DUP (?) DEST DW LUNG DUP (?) ST_ADD DD DEST ... .CODE ... LEA SI, SORG LES DI, ST_ADD 46 M. Rebaudengo, M. Sonza Reorda MOV CLD ciclo: LODSW CMP JNL MOV lab: STOSW LOOP ... 47 CX, LUNG AX, 0 lab AX, 0 ciclo M. Rebaudengo, M. Sonza Reorda Esercizio Si vuole realizzare un frammento di programma che trasferisca una stringa di caratteri da un’area di memoria sorgente ad un’area di memoria destinazione, eseguendo la conversione da caratteri minuscoli a caratteri maiuscoli. main() { char sorg[100], dest[100]; int i; ... for (i=0 ; i<100 ; i++) if ((sorg[i] <= 'z') && (sorg[i] >= 'a')) dest[i] = sorg[i] + 'A'- 'a'; else dest[i] = sorg[i]; } 48 M. Rebaudengo, M. Sonza Reorda Soluzione Assembler LUNG EQU 100 .MODEL small .STACK .DATA SORG DB LUNG DUP (?) DEST DB LUNG DUP (?) ST_ADD DD DEST .CODE ... LEA SI, SORG LES DI, ST_ADD 49 M. Rebaudengo, M. Sonza Reorda ciclo: copia: 50 MOV CX, LUNG CLD LODSB CMP AL, 'z' JNLE copia CMP AL, 'a' JNGE copia ADD AL, 'A'-'a' STOSB LOOP ciclo ... M. Rebaudengo, M. Sonza Reorda Strlen, Strcpy, Strcat Come esempio di uso delle istruzioni per la manipolazione di stringhe si riporta il codice generato dal compilatore Microsoft C 6.0 per le procedure standard C strlen, strcpy e strcat. 51 M. Rebaudengo, M. Sonza Reorda Strlen PUSH MOV MOV MOV MOV MOV XOR MOV REPNE NOT DEC XCHG MOV POP RET 52 BP BP,SP DX,DI ; salva DI AX,DS ES,AX DI,Word Ptr [BP+04]; puntatore alla stringa AX,AX CX,0FFFFH SCASB ; cerca la fine CX CX AX,CX ; valore di ritorno DI,DX BP M. Rebaudengo, M. Sonza Reorda PUSH BP MOV MOV MOV MOV MOV MOV MOV XOR MOV REPNE NOT 53 Strcpy BP,SP DX,DI BX,SI SI,Word Ptr [BP+06] DI,SI MOV DI,Word Ptr [BP+04] AX,DS MOV AX,DI ES,AX TEST AL,01 AX,AX JZ LAB CX,FFFF MOVSB SCASB DEC CX CX LAB: SHR CX,1 REP MOVSW ADC CX,CX REP MOVSB MOV SI,BX MOV DI,DX POP BP RET M. Rebaudengo, M. Sonza Reorda Nota Se si deve calcolare il numero d'ordine k dell'elemento di un blocco, che ha causato l'uscita dal ciclo di ripetizione, si usano le istruzioni MOV CX,FFFF ; CX N REPNE SCASB ; CX N-k NOT CX ; CX N-(N-k)=k Infatti, eseguire l'istruzione NOT significa sottrarre l'operando al valore massimo rappresentabile (N=FFFF). 54 M. Rebaudengo, M. Sonza Reorda Strcat 55 PUSH MOV MOV MOV MOV MOV MOV XOR MOV REPNE LEA MOV MOV REPNE NOT BP BP,SP DX,DI BX,SI AX,DS ES,AX DI,Word Ptr [BP+04] AX,AX CX,FFFF SCASB ; ricerca fine prima stringa SI,Word Ptr [DI-01] DI,Word Ptr [BP+06] CX,FFFF SCASB ; ricerca fine seconda stringa CX M. Rebaudengo, M. Sonza Reorda Strcat (II) LAB: 56 SUB XCHG MOV TEST JZ MOVSB DEC SHR REP ADC REP MOV MOV POP RET DI,CX DI,SI AX,Word Ptr [BP+04] SI,0001 LAB CX CX,1 MOVSW; copiatura CX,CX MOVSB SI,BX DI,DX BP M. Rebaudengo, M. Sonza Reorda