Avviare il "Blocco note" e scrivere quanto segue:
.model small
ASSUME DS:frase
.stack 100H
frase SEGMENT
testo db "Questo testo è lungo 78 caratteri, nella posizione 42 contiene il carattere p.",13,10,"$"
msg db "Carattere non trovato $"
msg1 db "Il carattere cercato si trova in posizione x $"
frase ENDS
.code
inizio:
mov ax, frase
mov ds, ax
mov es, ax
MOV dx, offset testo
mov ah, 09h
int 21h
CLD
MOV DI, offset testo
MOV CX, offset msg
MOV AL, 'p'
REPNE SCASB
JCXZ noTrovato
MOV DX, offset msg1
JMP mostra
noTrovato: MOV DX, offset msg
mostra: MOV AH, 09
INT 21h
MOV AX, 4C00h
int 21h
end inizio
Salvare il file con il nome: cerca.asm.
Il carattere $ al termine di queste righe è utilizzato (dal servizio di stampa del DOS) per segnalare la fine della stringa da mostrare. È una convenzione impiegata solo in ambiente DOS.
Dopo aver assemblato il programma: tasm cerca.asm - ed averlo linkato: tlink cerca.obj, richiamare il debugger: td cerca.exe
Il debugger mostra l'assegnazione di memoria del codice macchina, con le relative istruzioni in linguaggio mnemonico:
Nr. | Indirizzo | Istruzione Macchina | Istruzione Asm | Commento |
1 | CS:0000 | B8 45 14 | mov ax, 1445 | inizializzazione dei registri segmento |
2 | CS:0003 | 8E D8 | mov ds, ax | |
3 | CS:0005 | 8E C0 | mov es, ax | i due registri segmento puntano alla base dello stesso segmento |
4 | CS:0007 | BA 00 00 | mov dx, 0000 | puntatore alla stringa testo |
5 | CS:000A | B4 09 | mov ah, 09 | servizio mostra stringa |
6 | CS:000C | CD 21 | int 21 | richiesta del servizio al sistema operativo |
7 | CS:000E | FC | cld | direzione ricerca: da sinistra a destra |
8 | CS:000F | BF 00 00 | mov di, 0000 | l'offset della stringa si copia in DI |
9 | CS:0012 | B9 51 00 | mov cx, 0051 | la stringa è lunga 81 (=51 esa) caratteri |
10 | CS:0015 | B0 70 | mov mov al, 70 | il carattere da ricercare è p (codice ASCII=70 esa) |
11 | CS:0017 | F2 AE | repnz SCASB | continua la ricerca finchè non trovi p (flag Z=1) |
12 | CS:0019 | E3 06 | jcxz 0021 | se non è stata trovata alcuna p CX=0 |
13 | CS:001B | BA 68 00 | mov dx, 0068 | il carattere cercato è stato trovato |
14 | CS:001E | EB 04 | jmp 0024 | ;stampa il messaggio 'carattere trovato' |
15 | CS:0020 | BA 51 00 | mov dx, 0051 | ;stampa il messaggio 'carattere non trovato' |
16 | CS:0023 | B4 09 | mov ah, 09 | ;si richiede al DOS di mostrare una stringa |
17 | CS:0025 | CD 21 | int 21 | ;chiamata ai servizi del DOS |
Le istruzioni 1, 2 e 3 inizializzano i registri segmento DS ed ES per puntare entrambi alla base dello stesso segmento.
Le istruzioni 4, 5 e 6 producono la stampa della stringa testo.
Le istruzioni 7, 8, 9 e 10 preparano la ricerca del carattere:
CLD specifica che si vuole procedere per indirizzi crescenti,
DI contiene l'offset della stringa misurato nel segmento ES, (per questo motivo ES e DS puntano allo stesso segmento)
CX contiene il numero di caratteri che compongono la stringa,
AL contiene il codice del carattere da cercare.
L'istruzione SCASB (Scan String Byte) scandisce la stringa confrontando ciascun carattere con quello contenuto in AL, il prefisso di ripetizione REPNE (Repeat if Not Equal) dopo ogni confronto incrementa DI, per puntare al carattere successivo e decrementa CX per contare i caratteri confrontati. Quindi l'istruzione si arresta in due casi: 1) la stringa è terminata e il carattere non è stato trovato - questo caso è segnalato da CX=0 e dal valore 0 della flag di Z; 2) il carattere è stato trovato - CX è maggiore di zero e Z=1.
L'istruzione Nr. 12 significa Jump if CX is zero (salta all'indirizzo 0021 se CX=0).
Il servizio numero 9 dell'interrupt 21 (istruzioni 13 e 14) fornisce la stampa sullo schermo di una sequenza di caratteri il cui indirizzo iniziale è specificato in DX mentre la fine è segnalata dal carattere $. Se il carattere specificato in AL è stato trovato allora viene stampata la stringa che inizia dall'offset 0068, rispetto a DS, altrimenti viene stampata la stringa all'offset 51, rispetto a DS. Le stringhe possono essere lette tramite il comando View → Dump.
Eseguire il programma Passo-Passo: posizionare IP alla prima istruzione del programma, premere F7
Osservare l'avanzamento dei registri CX, DI e lo stato delle flag.
Se il carattere viene trovato come si può risalire alla sua posizione in memoria?
Con quali valori il debugger indica lo stato della flag di direzione?
Verificare il corretto funzionamento del programma anche nel caso di un carattere non presente nella stringa.
Apportare le modifiche necessarie per sperimentare il funzionamento del prefisso di ripetizione REPE (Repeat if Equal).
Scrivere un programma per copiare la stringa in un'altra area di memoria.
In questo esempio si mostra come ricercare una word (2 byte) in una stringa usando SCASW:
.model small
.code
ORG 100h
inizio:
MOV AX, @data
MOV DS, AX
CLD ; imposta la flag di direzione (0: indirizzi crescenti)
MOV CX, 4 ; inizializza il contatore con la lunghezza della stringa
MOV AX, DS ; inserisci in ES:DI il puntatore alla stringa
MOV ES, AX
LEA DI, numeri
MOV AX, 9012h ; La word da cercare nella stringa è 9012h
REPNE SCASW
JZ trovata
nonTrovata:
MOV AL, 'N' ; "No" - non Trovata
MOV AH, 0Eh
INT 10h
JMP esci
trovata:
MOV AL, 'S' ; "Sì" - trovata!
MOV AH, 0Eh
INT 10h
DEC DI ; DI contiene l'offset della word 9012h nella stringa:
esci:
MOV AX, 4C00h
int 21h
.data
numeri DW 1234h, 5678h, 9012h, 3456h
END inizio
La direttiva ORG (ORiGin) specifica l'offset, nel segmento codice, da cui deve iniziare la memorizzazione del programma
Avviare il debugger ed eseguire il programma passo passo.
Verificare che l'offset indicato dal programma corrisponda alla posizione della word all'interno della stringa
In qualsiasi linguaggio di programmazione una stringa è un array di caratteri dichiarato con una dimensione massima, ma usato solo parzialmente. Quindi si deve adottare un metodo per riconoscere il numero di caratteri effettivamente presenti nella stringa.
Un primo metodo consiste nello scrivere la lunghezza della stringa (cioè il numero di caratteri che la compongono) nel primo elemento dell'array. L'altro metodo ricorre a un carattere per marcare la fine, ad esempio il linguaggio C interpreta il carattere NUL (codice ASCII 0) come Fine Stringa.
Preparare, con il blocco note, il file seguente:
Il seguente sottoprogramma Assembler mostra sullo schermo una stringa di caratteri terminata con NUL. Riceve come parametro l'indirizzo della stringa sorgente nel registro BX.
.model small
.data
latino db 'Vulpem pilum mutare, non mores', 10, 13, 0
italiano db 'La volpe cambia il pelo, non i costumi', 10, 13, 0
autore db 'Svetonio', 10, 13, 0
.code
stampa PROC
ciclo:
MOV DL, [BX] ; BX punta alla stringa da mostrare
; il carattere puntato viene copiato in DL.
AND DL, DL ; Se in DL è stato scritto il valore 0 (NUL) …
JZ esci ; salta alla fine del sottoprogramma.
INC BX ; altrimenti punta al carattere successivo
MOV AH, 2 ; la funzione 2 del servizio 21
; mostra sullo schermo il carattere contenuto in DL.
INT 21h
JMP ciclo ; prelevare il prossimo carattere della stringa
esci:
RET ; Ritorna al programma chiamante
stampa ENDP
inizio:
MOV AX, @data
MOV DS, AX
MOV BX, offset latino ; indirizzo della prima stringa
CALL stampa ; mostra la prima stringa sullo schermo
MOV BX, offset italiano ; indirizzo della seconda stringa
CALL stampa ; mostra la seconda stringa sullo schermo
MOV BX, offset autore ; indirizzo della terza stringa
CALL stampa ; mostra la terza stringa sullo schermo
MOV AX, 4C00h
int 21h
END inizio
Il programma precedente contiene tre stringhe terminate dal carattere NUL.
La sezione codice contiene una procedura e il programma principale.
Richiamare il debugger ed eseguire il programma passo passo:
Osservare il valore del registro SP (Stack Pointer) prima e dopo l'istruzione CALL.
Scrivere il seguente programma:
.model small
.data
latino db 'FACTA, NON VERBA', 10, 13, 0
.code
Il programma utilizza la funzione di stampa di una stringa già proposta nell'esercizio precedente:
stampa PROC
ciclo:
MOV DL, [BX]
AND DL, DL
JZ esci
INC BX
MOV AH, 2
INT 21h
JMP ciclo
esci:
RET
stampa ENDP
Nello stesso segmento codice viene definita una procedura che trasforma un carattere in minuscolo:
Minus PROC
ripeti:
LODSB ; si copia il carattere puntato da SI in AL e SI si incrementa per puntare al carattere successivo
AND AL, AL ; Se si è letto il carattere NUL
JZ fine ; Allora salta all'istruzione fine
CMP AL, 'A'
JB riscrivi
CMP AL, 'Z'
JA riscrivi
OR AL, 20h ; forza il bit 5 a 1
riscrivi:
STOSB ; memorizza il carattere da AL all'indirizzo puntato da DI
JMP ripeti
fine:
RET
Minus ENDP
Il programma principale:
inizio:
MOV AX, @data
MOV DS, AX
MOV ES, AX
MOV BX, offset latino
CALL stampa
CLD ; Direzione di scansione della stringa
MOV SI, offset latino ; Stringa sorgente
MOV DI, offset latino ; e stringa destinazione coincidono
CALL Minus ; chiama il sottoprogramma.
MOV BX, offset latino
CALL stampa
mov AX, 4C00h
int 21h
END inizio
Modificare il programma per trasformare la stringa in maiuscolo.