A supporto utilizza questi link:
http://www.giobe2000.it/Tutorial/Schede/07-IstruzioniCpu/703.asp
http://www.shsu.edu/~csc_tjm/spring2005/cs272/intro_to_asm.html
Esercizio 1 (INPUT - OUTPUT - CICLO)
Costruire un programma che visualizza il numero di caratteri scritti nella stringa dei parametri.
Prima di procedere vediamo un pò di teoria
Il PSP (prefisso del segmento di programma) è un'area di memoria di 256 bytes (100h) collocata all'inizio del code segmento (area di memoria destinata, dal loader del dos, al codice del programma eseguibile.
Le ultime 128 locazioni del PSP
(da 0080H a 00FFH) sono usate dal Dos per 2 funzioni molto
specifiche:
- come Buffer temporaneo di default per i dati da trasferire da e verso un
disco: quest'area è significativa solo con le (obsolete) funzioni DOS che
gestiscono i files mediante la tecnica dei File Control Block; in questo caso
questa zona assume il nome di Area di Trasferimento per il Disco (DTA, Disk
Transfer Area).
- come area di memoria per la stringa dei parametri eventualmente passati al nostro
programma sulla linea di comando; In particolare il primo byte (all'indirizzo
0080H) contiene la lunghezza della coda del comando (cioè il numero di
caratteri della stringa dei parametri) aumentata di 1.
ESEMPIO | Osservazioni: |
C:\asm>debug
leggi.com ciao -d 0080 182C:0080 05 20 63 69 61 6F 0D 00-00 00 00 00 00 00 00 00 . ciao.......... 182C:0090 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 182C:00A0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 182C:00B0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 182C:00C0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 182C:00D0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 182C:00E0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 182C:00F0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ -q C:\asm>debug leggi.com ciao marco sechi -d 0080 182C:0080 11 20 63 69 61 6F 20 6D-61 72 63 6F 20 73 65 63 . ciao marco sec 182C:0090 68 69 0D 00 00 00 00 00-00 00 00 00 00 00 00 00 hi.............. 182C:00A0 78 65 0D 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 182C:00B0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 182C:00C0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 182C:00D0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 182C:00E0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 182C:00F0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ -q |
Nel primo esempio i caratteri presenti nella stringa dei parametri ("ciao") sono 4
Nel secondo esempio i caratteri presenti nella stringa dei parametri ("ciao marco sechi") è 16. Il numero decimale 16+1=17 corrisponde 11 in esadecimale. |
SOLUZIONE:
a.1) Se pongo a 9 il limite del numero di caratteri presente nella stringa dei parametri la soluzione risulta molto semplice:
SOLUZIONE | Osservazioni: | CREA CON: DEBUG<SOLUZ1.ASM |
C:\asm>debug -a 181B:0100 MOV AH,02 181B:0102 MOV DL,[0080] 181B:0106 ADD DL,30 181B:0109 INT 21 181B:010B RET 181B:010C -r bx BX 0000 :0 -r cx CX 0000 :c -n soluz1.com -w Writing 0000C bytes -q C:\asm>soluz10 C:\asm>soluz1 ciao 5 C:\asm>soluz1 ciao caio : C:\asm> |
MOV DL, [0080]
==> Carico il numero di caratteri della
stringa dei parametri (ubicato all'indirizzo DS:0080) nel registro DL.
ADD DL,30 ==>Aggiungo 48 (30 in hex) per ottenere il codice ascii del carattere numerico ('0' ... '9') corrispondente a tale numero MOV AH, 02 INT 21 ==> Utilizzo il servizio 21 del DOS per la stampa di un singolo simbolo Ascii (richiede in AH il valore 2 mentre in DL il codice ascii del simbolo da stampare). Chiaramente la soluzione non è adatta quando il numero di caratteri presenti nella stringa dei parametri supera il nove. |
a MOV AH,02 MOV DL,[0080] ADD DL,30 INT 21 RET n
soluz1.com |
a.2) La stessa soluzione poteva essere proposta utilizzando il pacchetto di procedure del BIOS relative al video noto come interrupt 10h.
Ricordo che:
- l'interrupt 10h contiene le procedure del BIOS relative al video,
INT 10H |
Funzione 00H | Gestione del Modo Video |
INT 10H |
Funzione 01H | Controllo della dimensione del Cursore |
INT 10H |
Funzione 02H | Fissa la posizione del Cursore |
INT 10H |
Funzione 03H | Legge posizione/dimensione del Cursore |
INT 10H |
Funzione 04H | Controllo della Penna Ottica |
INT 10H |
Funzione 05H | Controllo Veloce delle Pagine Video |
INT 10H |
Funzione 06H | Spostamento verso l'alto di Aree video (Finestre) |
INT 10H |
Funzione 07H | Spostamento verso il basso di Aree video (Finestre) |
INT 10H |
Funzione 08H | Lettura del carattere nella posizione corrente del Cursore |
INT 10H |
Funzione 09H | Stampa uno o più caratteri a colori |
INT 10H |
Funzione 0AH | Stampa uno o più caratteri |
INT 10H |
Funzione 0BH | Fissa il Colore del Bordo del Video |
INT 10H |
Funzione 0CH | Stampa un Pixel |
INT 10H |
Funzione 0DH | Lettura di un Pixel |
INT 10H |
Funzione 0EH | Stampa un carattere in Modo TTY |
INT 10H |
Funzione 0FH | Acquisisce il Modo Video corrente |
INT 10H |
Funzione 10H/00H | Scrive un singolo Registro di Tavolozza |
INT 10H |
Funzione 10H/01H | Scrive il Registro di Sovrascansione (Bordo del Video) |
INT 10H |
Funzione 10H/02H | Scrive tutti Registri Tavolozza + Bordo |
INT 10H |
Funzione 10H/03H | Controlla intensità e flash dello Sfondo |
INT 10H |
Funzione 10H/07H | Legge un singolo Registro di Tavolozza |
INT 10H |
Funzione 10H/08H | Legge il Registro di Sovrascansione (Bordo del Video) |
INT 10H |
Funzione 10H/09H | Legge tutti Registri Tavolozza + Bordo |
INT 10H |
Funzione 10H/10H | Scrive un singolo Registro di Colore del DAC |
INT 10H |
Funzione 10H/12H | Scrive un blocco di Registri di Colore del DAC |
INT 10H |
Funzione 10H/13H | Imposta la Pagina di colore DAC attiva e/o il loro numero |
INT 10H |
Funzione 10H/15H | Legge un singolo Registro di Colore del DAC |
INT 10H |
Funzione 10H/17H | Legge un blocco di Registri di Colore del DAC |
INT 10H |
Funzione 10H/18H | Scrive il registro di maschera Colore del DAC |
INT 10H |
Funzione 10H/19H | Legge il registro di maschera Colore del DAC |
INT 10H |
Funzione 10H/1AH | Legge la Pagina di colore DAC attiva e/o il loro numero |
INT 10H |
Funzione 10H/1BH | Trasforma i colori in scala di grigi |
INT 10H |
Funzione 11H/00H | Carica il Font di caratteri creato dall'utente |
INT 10H |
Funzione 11H/01H | Carica il Font 8*14 del Rom Bios |
INT 10H |
Funzione 11H/02H | Carica il Font 8*8 del Rom Bios (Caratteri Double-Dot) |
INT 10H |
Funzione 11H/03H | Seleziona le Tabelle di uno o più Font di caratteri da visualizzare |
INT 10H |
Funzione 11H/04H | Carica il Font 8*16 del Rom Bios |
INT 10H |
Funzione 11H/10H | Carica il Font di caratteri creato dall'utente e riprogramma CRT |
INT 10H |
Funzione 11H/11H | Carica il Font 8*14 del Rom Bios e riprogramma il Controller CRT |
INT 10H |
Funzione 11H/12H | Carica il Font 8*8 del Rom Bios (Caratteri Double-Dot) e riprogramma CRT |
INT 10H |
Funzione 11H/14H | Carica il Font 8*16 del Rom Bios e riprogramma il Controller CRT |
INT 10H |
Funzione 11H/20H | Associa il Font Grafico 8*8 Esteso, creato dall'utente, al puntatore INT 1FH |
INT 10H |
Funzione 11H/21H | Associa un Font Grafico Completo, creato dall'utente, al puntatore INT 43H |
INT 10H |
Funzione 11H/22H | Associa il Font BIOS 8*14 al puntatore INT 43H |
INT 10H |
Funzione 11H/23H | Associa il Font Rom Bios 8*8 (Caratteri Double-Dot) al puntatore INT 43H |
INT 10H |
Funzione 11H/24H | Associa il Font Rom Bios 8*16 al puntatore INT 43H |
INT 10H |
Funzione 11H/30H | Assume informazioni sul Font di caratteri desiderato |
INT 10H |
Funzione 12H/10H | Dà informazioni su configurazione video EGA |
INT 10H |
Funzione 12H/20H | Seleziona il PrintScreen Alternativo |
INT 10H |
Funzione 12H/30H | Selezione e controllo del numero di linee di Scansione |
INT 10H |
Funzione 12H/31H | Abilita/Disabilita il caricamento tavolozza |
INT 10H |
Funzione 12H/32H | Controlla l'abilitazione del Video |
INT 10H |
Funzione 12H/33H | Controlla la somma dei grigi |
INT 10H |
Funzione 12H/34H | Emulazione CGA del Cursore |
INT 10H |
Funzione 12H/35H | Commuta tra 2 Adattatori, MCGA e VGA |
INT 10H |
Funzione 12H/36H | Abilita/disabilita il Refresh del Video |
INT 10H |
Funzione 13H | Stampa di stringhe |
INT 10H |
Funzione 1AH/00H | Assume informazioni su Adattatore/Monitor |
INT 10H |
Funzione 1AH/01H | Dà informazioni su Adattatore/Monitor |
INT 10H |
Funzione 1BH | Dà informazioni su Sistema Visualizzazione |
- l'interrupt 14h contiene le procedure del BIOS relative alla porta seriale,
INT 14H |
Funzione 00H | Inizializza i parametri della porta seriale |
INT 14H |
Funzione 01H | Trasmette un carattere a una porta seriale |
INT 14H |
Funzione 02H | Riceve un carattere da una porta seriale |
INT 14H |
Funzione 03H | Restituisce lo stato di una porta seriale |
INT 14H |
Funzione 04H | Inizializzazione estesa di una porta seriale (PS2) |
INT 14H |
Funzione 05H | Controllo esteso di una porta seriale (PS2) |
- l'interrupt 17h contiene le procedure del BIOS relative alla porta parallela e stampante
INT 17H |
Funzione 00H | Trasmette un carattere a una stampante |
INT 17H |
Funzione 01H | Inizializza una stampante |
INT 17H |
Funzione 02H | Restituisce lo stato di una stampante |
- l'interrupt 21h contiene le procedure di sistema a livello DOS. Sono decisamente più numerose ma in questa pagina sono citate quelle più importanti.
INT 21H |
Funzione 03H |
Legge un carattere al dispositivo standard ausiliario, COM1 |
INT 21H |
Funzione 04H |
Scrive un carattere verso il dispositivo standard ausiliario, COM1 |
INT 21H |
Funzione 05H |
Trasmette un carattere al dispositivo standard di stampa |
INT 21H |
Funzione 3FH |
Legge da COM1 con la tecnica dei gestori (handle) standard |
Legge uno o più bytes da un file o da un dispositivo inserendoli in un buffer | ||
INT 21H |
Funzione 40H |
Scrive verso COM1 con la tecnica dei gestori (handle) standard |
Scrive uno o più bytes in un file o verso un dispositivo, prelevandoli da un buffer | ||
INT 21H |
Funzione 44H/02H |
Legge una stringa di bytes da COM1 prelevandoli da un buffer |
Legge una stringa di bytes da un dispositivo di tipo "carattere", prelevandoli da un buffer | ||
INT 21H |
Funzione 44H/03H |
Scrive una stringa di bytes verso COM1 inserendola in un buffer |
Scrive una stringa di bytes in un dispositivo di tipo "carattere" inserendola in un buffer | ||
INT 21H |
Funzione 75H |
Accede ad una porta seriale (non documentato) |
INT 21H |
Funzione 01H |
Gestisce la stampa in background, provvedendo al controllo dello Spooler di stampa |
INT 21H |
Funzione 03H |
Legge un carattere al dispositivo standard ausiliario, COM1 |
INT 21H |
Funzione 04H |
Scrive un carattere verso il dispositivo standard ausiliario, COM1 |
INT 21H |
Funzione 3FH |
Legge da COM1 con la tecnica dei gestori (handle) standard |
INT 21H |
Funzione 40H |
Scrive verso COM1 con la tecnica dei gestori (handle) standard |
INT 21H |
Funzione 44H/02H |
Legge una stringa di bytes da COM1 prelevandoli da un buffer |
INT 21H |
Funzione 44H/03H |
Scrive una stringa di bytes verso COM1 inserendola in un buffer |
INT 21H |
Funzione 75H |
Accede ad una porta seriale (non documentato) |
Maggiori dettagli possono essere
trovati all'indirizzo:
http://www.giobe2000.it/Tutorial/LivelloBios/LivelloBios01.asp
SOLUZIONE | Osservazioni: | CREA CON: DEBUG<SOLUZ1V.ASM |
C:\asm>debug -a 182C:0100 MOV AL,[0080] 182C:0103 ADD AL,30 182C:0105 MOV AH,0E 182C:0107 INT 10 182C:0109 RET 182C:010A -r bx BX 0000 :0 -r cx CX 000A :A -n soluz1v.com -w Writing 0000A bytes -q C:\asm>soluz1v0 C:\asm>soluz1v arrivederci < C:\asm>soluz1v ciao 5 C:\asm> |
MOV AL, [0080]
==> Carico il numero di caratteri della
stringa dei parametri (ubicato all'indirizzo DS:0080) nel registro DL.
ADD AL,30 ==>Aggiungo 48 (30 in hex) per ottenere il codice ascii del carattere numerico ('0' ... '9') corrispondente a tale numero
MOV AH, 0E Chiaramente la soluzione non è adatta quando il numero di caratteri presenti nella stringa dei parametri supera il nove. |
a MOV AL,[0080] ADD AL,30 MOV AH,0E INT 10 RET n
soluz1v.com |
a.3) Altra soluzione era quella di scrivere direttamente nella RAM video il carattere da visualizzare.
Prima di procedere vediamo un pò di teoria
La Ram Video è posta nel primo megabyte della zona della memoria convenzionale di Sistema; la sua dimensione è di 128kBytes, a partire dall’indirizzo fisico A0000H fino all’indirizzo fisico BFFFFH; la pratica della programmazione a basso livello consente di esprimere questi indirizzi fisici nei corrispondenti indirizzi logici, da A000:0000 a B800:7FFF (un indirizzo logico si esprime sempre nella forma Segmento:Offset).
Questa area di memoria è a sua
volta divisa in 3 parti:
- nella prima (da A000:0000 a A000:FFFF) trovano posto i 64K bytes destinati
alla gestione del video in Modo Grafico. L'unità di
informazione è il pixel (semplificando ogni pixel può essere descritto con un byte
(8 bit) quando assume 28=256 colori
. Lo stesso byte può essere usato per descrivere otto pixel
ma in questo caso ogni pxel può assumere solo 2 colori, bianco o
nero, cioè acceso o spento).
- nella seconda (da B000:0000 a B000:7FFF) sono disponibili i 32K bytes dedicati
al modo Monocromatico, ormai in disuso.
- nell'ultima parte (da B000:8000 a B000:FFFF) sono allocati i 32K bytes
necessari alla gestione del video in Modo Testo. L'unità di informazione
è il carattere (vedremo che per descrivere le caratteristiche di ogni carattere saranno necessari 2 byte: simbolo ascii + colore).
Quando si lavora in Modo Testo la zona di Ram Video che ci interessa è quella compresa tra B000:8000 e B000:FFFF (32Kbyte in tutto); poiché la tecnica della segmentazione della memoria ci consente di esprimere il medesimo indirizzo logico in molti modi diversi, per motivi di opportunità conviene modificare il contenuto Segment:Offset di questo intervallo, trasformandolo in B800:0000 - B800:7FFF.
I
32 kBytes della Ram Video
destinata al Modo Testo sono divisi in 8 zone uguali, dette Pagine Video,
ciascuna di 4 kBytes (4096 bytes).
Ciascuna delle 8 Pagine Video, numerate da 0 a 7, copre quindi l'area di memoria
Ram corrispondente agli indirizzi:
Numero Pagina | Primo Address | Range Indirizzi Fisici |
Pagina 0 | B800:0000 | da B8000 a B8FFF |
Pagina 1 | B800:1000 | da B9000 a B9FFF |
Pagina 2 | B800:2000 | da BA000 a BAFFF |
Pagina 3 | B800:3000 | da BB000 a BBFFF |
Pagina 4 | B800:4000 | da BC000 a BCFFF |
Pagina 5 | B800:5000 | da BD000 a BDFFF |
Pagina 6 | B800:0600 | da BE000 a BEFFF |
Pagina 7 | B800:0700 | da BF000 a BFFFF |
Il concetto principale sta
nel fatto che solo la Pagina 0 (cioè i primi 4096 bytes di questa area) viene
letta continuamente dalla scheda video e il suo contenuto viene interpretato e
tradotto direttamente sul monitor. Ogni modifica (scrittura) eseguita sui primi
4000 bytes si traduce in una modifica in tempo reale del testo
mostrato sul monitor. Possiamo dunque concludere: solo quello che viene scritto
in Pagina 0 produce un effetto a video.
lL eventuali modifiche eseguite sul contenuto delle rimanenti 7 Pagine (da
Pagina 1 a Pagina 7) non si vedono!
Il monitor mostra un'area (gestita dalla Pagina 0) organizzata in 25
righe, ciascuna di 80 caratteri. Si può quindi scrivere fino a 2000 (=25x80)
caratteri, per i quali occorre prevedere sia il colore di primo piano (cioè il
colore del carattere stesso) sia quello dello sfondo (cioè la tinta del rettangolino che ospita ciascun carattere). Ogni Pagina Video ha quindi a
disposizione più bytes di quanti ne servano effettivamente.
Il codice di colore (detto anche attributo) del carattere è un byte costruito
dividendo gli otto bit in 3 campi, secondo il seguente schema:
bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 |
flash | Sfondo | Primo Piano | |||||
F | S2 | S1 | S0 | P3 | P2 | P1 | P0 |
I 16 colori di primo piano sono
bit3 |
bit2 |
bit1 |
bit0 |
Primo Piano |
||
0 |
0 |
0 |
0 |
Black |
0 |
Nero |
0 |
0 |
0 |
1 |
Blue |
1 |
Blu |
0 |
0 |
1 |
0 |
Green |
2 |
Verde |
0 |
0 |
1 |
1 |
Cyan |
3 |
Azzurro |
0 |
1 |
0 |
0 |
Red |
4 |
Rosso |
0 |
1 |
0 |
1 |
Magenta |
5 |
Magenta |
0 |
1 |
1 |
0 |
Brown |
6 |
Marrone |
0 |
1 |
1 |
1 |
LightGray |
7 |
Bianco |
1 |
0 |
0 |
0 |
darkGray |
8 |
Grigio |
1 |
0 |
0 |
1 |
LightBlue |
9 |
Blu Elettrico |
1 |
0 |
1 |
0 |
LightGreen |
10 |
Verde Chiaro |
1 |
0 |
1 |
1 |
LightCyan |
11 |
Celeste |
1 |
1 |
0 |
0 |
Ligh3d |
12 |
Rosa |
1 |
1 |
0 |
1 |
LightMagenta |
13 |
Magenta Chiaro |
1 |
1 |
1 |
0 |
Yellow |
14 |
Giallo |
1 |
1 |
1 |
1 |
White |
15 |
Bianco Brillante |
mentre ecco quelli di sfondo:
bit6 |
bit5 |
bit4 |
Sfondo |
||
0 |
0 |
0 |
Black |
0 |
Nero |
0 |
0 |
1 |
Blue |
1 |
Blu |
0 |
1 |
0 |
Green |
2 |
Verde |
0 |
1 |
1 |
Cyan |
3 |
Azzurro |
1 |
0 |
0 |
Red |
4 |
Rosso |
1 |
0 |
1 |
Magenta |
5 |
Magenta |
1 |
1 |
0 |
Brown |
6 |
Marrone |
1 |
1 |
1 |
LightGray |
7 |
Bianco |
Ecco quindi tutte le possibili combinazioni di colore (valori esadecimali da 00H a 7FH) valide per i codici di colore senza flash (byte d'attributo) usate nel Modo Testo. Naturalmente la simulazione non consente di vedere i codici dei caratteri scritti nello stesso colore dello sfondo (esempio: nero su nero: 00H - blu su blu: 11H ...).
00H | 01H | 02H | 03H | 04H | 05H | 06H | 07H | 08H | 09H | 0AH | 0BH | 0CH | 0DH | 0EH | 0FH |
10H | 11H | 12H | 13H | 14H | 15H | 16H | 17H | 18H | 19H | 1AH | 1BH | 1CH | 1DH | 1EH | 1FH |
20H | 21H | 22H | 23H | 24H | 25H | 26H | 27H | 28H | 29H | 2AH | 2BH | 2CH | 2DH | 2EH | 2FH |
30H | 31H | 32H | 33H | 34H | 35H | 36H | 37H | 38H | 39H | 3AH | 3BH | 3CH | 3DH | 3EH | 3FH |
40H | 41H | 42H | 43H | 44H | 45H | 46H | 47H | 48H | 49H | 4AH | 4BH | 4CH | 4DH | 4EH | 4FH |
50H | 51H | 52H | 53H | 54H | 55H | 56H | 57H | 58H | 59H | 5AH | 5BH | 5CH | 5DH | 5EH | 5FH |
60H | 61H | 62H | 63H | 64H | 65H | 66H | 67H | 68H | 69H | 6AH | 6BH | 6CH | 6DH | 6EH | 6FH |
70H | 71H | 72H | 73H | 74H | 75H | 76H | 77H | 78H | 79H | 7AH | 7BH | 7CH | 7DH | 7EH | 7FH |
a.3) Sulla base delle considerazioni enunciate la soluzione diventa:
ESEMPI | Osservazioni: | CREA CON: DEBUG<SOLUZ1RV.ASM |
C:\asm>debug<soluz1rv.asm
-a
1810:0100 ; --- LEGGO POSIZIONE CURSORE
1810:0100 MOV AH,3
1810:0102 MOV BH,0
1810:0104 INT 10
1810:0106 ; --- CALCOLO LA CELLA DI MEMORIA
1810:0106 MOV AX,A0
1810:0109 XOR DL,DL
1810:010B MUL DH
1810:010D ; --- RIPOSIZIONO L'EXTENDED DATA
1810:010D ; --- SEGMENT SULLA RAM VIDEO
1810:010D MOV CX,B800
1810:0110 MOV ES,CX
1810:0112 MOV SI,AX
1810:0114 ; --- SCRIVO IL NUMERO
1810:0114 ; --- NELLA RAM VIDEO B800:SI
1810:0114 MOV AL,[0080]
1810:0117 ADD AL,30
1810:0119 MOV AH,7
1810:011B ES:MOV [SI],AX
1810:011E RET
1810:011F
-n soluz1rv.com
-r bx
BX 0000
:0
-r cx
CX 0000
:1F
-w
Writing 0001F bytes
-q
C:\asm>soluz1rv
0
C:\asm>soluz1rv arrivederci
<
C:\asm>soluz1rv ciao
5
C:\asm> |
MOV AH,3 MOV BH,0 INT 10 ==> Inizio utilizzando il servizio 10 del BIOS (ed esattamente la funzione 03h - modalità TTY o testo) che mi consente di leggere la posizione del cursore nella pagina video indicata in BH. Il servizio restituisce in DH il numero della riga e in DL quello della colonna. MOV AX, A0 XOR DL,DL MUL DH ==> Poichè ogni riga della modalità testo occupa 160 byte (A0 in hex) moltiplico AX per il numero di riga indicato in DH ottenendo così il corretto offset rispetto all'indirizzo iniziale B800 MOV CX,B800 MOV ES,CX MOV SI,AX ==> Riposiziono l'extended data segment sull'area video della modalità testo (B800) e imposto l'offset (SI) in modo che corrisponda con la riga successiva a quella dove ho digitato il comando MOV AL,[0080] ADD AL,30 ==> Carico il numero di caratteri della stringa dei parametri (ubicato all'indirizzo DS:0080) nel registro AL. Aggiungo 48 (30 in hex) per ottenere il codice ascii del carattere numerico ('0' ... '9') corrispondente a tale numero MOV AH,7 ==> Imposto il colore (bianco su sfondo nero) ES:MOV [SI],AX ==> Scrivo nell'extended data segment - Offset SI il numero e il suo colore Chiaramente la soluzione non è adatta quando il numero di caratteri presenti nella stringa dei parametri supera il nove. |
a ; --- LEGGO POSIZIONE CURSORE MOV AH,3 MOV BH,0 INT 10 ; --- CALCOLO LA CELLA DI MEMORIA MOV AX,A0 XOR DL,DL MUL DH ; --- RIPOSIZIONO L'EXTENDED DATA ; --- SEGMENT SULLA RAM VIDEO MOV CX,B800 MOV ES,CX MOV SI,AX ; --- SCRIVO IL NUMERO ; --- NELLA RAM VIDEO B800:SI MOV AL,[0080] ADD AL,30 MOV AH,7 ES:MOV [SI],AX RET n soluz1rv.com r bx 0 r cx 1F w q (mettere un invio dopo q) |
b) Se il numero di caratteri nella stringa dei parametri può superare i dieci allora la soluzione diventa più complessa:
SOLUZIONE | Osservazioni: | CREA CON: DEBUG<SOLUZ1B.ASM |
C:\asm>debug a 1836:0100 MOV DL,A 1836:0102 XOR CX,CX 1836:0104 XOR AH,AH 1836:0106 MOV AL,[0080] 1836:0109 ;---------- INIZIO: 0109 1836:0109 DIV DL 1836:010B ADD AH,30 1836:010E PUSH AX 1836:010F XOR AH,AH 1836:0111 INC CX 1836:0112 CMP AL,0 1836:0114 JNZ 0109 1836:0116 ;---------- STAMPA: 0116 1836:0116 POP AX 1836:0117 MOV DL,AH 1836:0119 MOV AH,2 1836:011B INT 21 1836:011D DEC CX 1836:011E CMP CX,0 1836:0121 JNZ 0116 1836:0123 RET 1836:0124 -r bx BX 0000 :0 -r cx CX 000A :24 -n soluz1b.com -w Writing 0000A bytes -q C:\asm>soluz1v0 C:\asm>soluz1v arrivederci < C:\asm>soluz1v ciao 5 C:\asm> |
Le prime 4 istruzioni sono di
inizializzazione: MOV DL, A ==> carico 10 (A in hex) in DL. Infatti per ottenere i digit della rappresentazione in base 10 effettuo una serie di divisioni per 10 dove il resto è usato come come digit da visualizzare. XOR CX, CX ==> inizializzo il contatore CX a 0. CX conteggia il numero di digit della rappresentazione in base 10 del numero registrato all'indirizzo DS:0080 XOR AH,AH MOV AL, [0080] ==> carico in AX il numero di caratteri (+1!)presenti nella stringa dei parametri
Il secondo blocco, mediante delle divisioni successive, estrae in
sequenza i digit componenti la rappresentazione in base 10 del numero
registrato all'indirizzo DS:0080 e li memorizza nello stack
mediante l'istruzione PUSH Il terzo blocco, recupera i digit salvati nello stack mediante
l'istruzione POP e li manda in stampa utilizzando la funzione
2h (registrata in AL) dell'interrupt 21. |
a MOV DL,A XOR CX,CX XOR AH,AH MOV AL,[0080] ;---------- INIZIO: 0109 DIV DL ADD AH,30 PUSH AX XOR AH,AH INC CX CMP AL,0 JNZ 0109 ;---------- STAMPA: 0116 POP AX MOV DL,AH MOV AH,2 INT 21 DEC CX CMP CX,0 JNZ 0116 RET n soluz1b.com r cx 24 w q (mettere un invio dopo q) |
Esercizio 2 (INPUT - OUTPUT - CICLO)
Costruire un programma "inverte" che stampa la stringa degli argomenti al rovescio
SOLUZIONE:
La soluzione suggerita gestisce anche l'assenza di parametri
SOLUZIONE | Osservazioni: | CREA CON: DEBUG<SOLUZ2.ASM |
C:\asm>debug -a 181B:0100 XOR AH,AH 181B:0102 MOV AL,[0080] 181B:0105 MOV SI,0080 181B:0108 ADD SI,AX 181B:010A ;---------- INIZIO STAMPA: 010A 181B:010A CMP SI,0081 181B:010E JL 119 181B:0110 MOV DL,[SI] 181B:0112 MOV AH,2 181B:0114 INT 21 181B:0116 DEC SI 181B:0117 JMP 010A 181B:0119 ;---------- FINE PROGRAMMA: 0119 181B:0119 RET 181B:011A -r bx BX 0000 :0 -r cx CX 0000 :1A -n inverte.com -w Writing 0001A bytes -q C:\asm>inverte ciao marcoocram oaic C:\asm>inverte C:\asm> |
XOR AH,AH MOV AL,[0080] ==> Carico in AX il numero di caratteri presenti nella stringa dei parametri MOV SI,0080 ADD SI,AX ==> Carico in SI l'indirizzo dell'ultima lettera relativa alla stringa degli argomenti registrata nel PSP. MOV DL,[SI] ==> Mediante un indirizzamento indiretto leggo il carattere corrente e lo carico in DL. Utilizzo il servizio 21 del DOS per la stampa di un singolo simbolo Ascii (richiede in AH il valore 2 mentre in DL il codice ascii del simbolo da stampare). Decremento SI fino ad arrivare all'indirizzo DS:0081 (punto nel PSP dove inizia la stringa dei parametri). |
a XOR AH,AH MOV AL,[0080] MOV SI,0080 ADD SI,AX ;---------- INIZIO STAMPA: 010A CMP SI,0081 JL 119 MOV DL,[SI] MOV AH,2 INT 21 DEC SI JMP 010A ;---------- FINE PROGRAMMA: 0119 RET n inverte.com r cx 1A w q (mettere un invio dopo q) |
Esercizio 3 (INPUT - OUTPUT - CICLO)
Costruire un programma "Saluta" che stampa la
parola "Ciao" un numero di volte pari al valore indicato sulla stringa degli
argomenti (esempio: Saluta 3 ==> "Ciao|Ciao|Ciao|")
SOLUZIONE:
La soluzione suggerita gestisce l'assenza di parametri ma non la casistica non numerica
SOLUZIONE | Osservazioni: | CREA CON: DEBUG<SOLUZ3.ASM |
C:\asm>debug -a 181B:0100 MOV DL,[0080] 181B:0104 XOR DH,DH 181B:0106 MOV SI,0082 181B:0109 XOR AX,AX 181B:010B MOV CL,A 181B:010D DEC DL 181B:010F ;---- INIZIO ESTRAZIONE: 010F 181B:010F CMP DL,0 181B:0112 JLE 0124 181B:0114 MUL CL 181B:0116 MOV BL,[SI] 181B:0118 SUB BL,30 181B:011B XOR BH,BH 181B:011D ADD AX,BX 181B:011F INC SI 181B:0120 DEC DL 181B:0122 JMP 010F 181B:0124 MOV BX,AX 181B:0126 MOV AH,9 181B:0128 ;---------- INIZIO STAMPA: 0128 181B:0128 CMP BX,0 181B:012B JZ 135 181B:012D MOV DX,0136 181B:0130 INT 21 181B:0132 DEC BX 181B:0133 JMP 128 181B:0135 ;---------- FINE PRG: 0135 181B:0135 RET 181B:0136 DB 'Ciao|$' 181B:013C -n saluta.com -r bx BX 0000 :0 -r cx CX 0000 :3C -w Writing 0003C bytes -q C:\asm>saluta 3Ciao|Ciao|Ciao| C:\asm>saluta C:\asm> |
XOR AX,AX
==> Azzero AX. Questo registro
conterrà il valore numerico associato alla stringa passata come
parametro. XOR DH,DH MOV DL,[0080] DEC DL ==> Carico in DL il numero di caratteri presenti nella stringa dei parametri MOV SI,0082 ==> Carico in SI l'indirizzo della prima lettera relativa alla stringa dei parametri registrata nel PSP. MOV CL,A ==> Imposto CX a 10 (A in hex) CX è usato (durante la procedura che trasforma la stringa nel suo corrispondente numerico) per decuplicare (MUL CL) il contenuto di AX prima di aggiungerci il valore associato al digit corrente (memorizzato in BX). MOV BL,[SI] SUB BL,30 XOR BH,BH ADD AX,BX ==> Carico in BL il digit corrente (puntato da SI) e lo decremento del codice ascii di '0' ovvero di 48 (in hex 30). Il numero ottenuto viene poi aggiunto a AX (ADD AX,BX). INC SI ==> incremento SI puntando così al carattere successivo DEC DL ==> decremento DL indicando la diminuzione del numero di digit che restano ancora da analizzare. JMP 010F CMP DL,0 JLE 0124 ==> Se DL è minore o uguale a 0 significa che ho letto tutte le cifre sulla stringa dei parametri per cui passo alla stampa La sezione
relativa alla stampa è così predisposta:
|
a MOV DL,[0080] XOR DH,DH MOV SI,0082 XOR AX,AX MOV CL,A DEC DL ;---- INIZIO ESTRAZIONE: 010F CMP DL,0 JLE 0124 MUL CL MOV BL,[SI] SUB BL,30 XOR BH,BH ADD AX,BX INC SI DEC DL JMP 010F ;---- INIZIO STAMPA: 0124 MOV BX,AX MOV AH,9 CMP BX,0 JZ 135 MOV DX,0136 INT 21 DEC BX JMP 128 ;---------- FINE PRG: 0135 RET DB 'Ciao|$' n saluta.com r bx 0 r cx 3C w q (mettere un invio dopo q) |
Esercizio 4
(INPUT - OUTPUT - CICLO)
Costruire un programma Numeri che elimina i caratteri non numerici dalla stringa dei parametri. La sezione che legge l'argomento deve essere inglobata in una procedura
SOLUZIONE | Osservazioni: | CREA CON: DEBUG<SOLUZ4.ASM |
C:\asm>debug -a 1810:0100 ; --- PROC LETTURA PARAMETRO 1810:0100 CALL 250 1810:0103 ; --- ESTRAI NUMERI 1810:0103 CALL 350 1810:0106 ; --- LI STAMPO 1810:0106 MOV AH,9 1810:0108 MOV DX,0300 1810:010B INT 21 1810:010D RET 1810:010E ;--- FINE MAIN: 010E - -f 0115 024F 0 -a 0200 180E:0200 ;--- COPIA STRINGA PARAMETRI 180E:0200 ;--- NEL BUFFER 30 BYTE: 200F 180E:0200 DB ' ' 180E:021E - -a 0250 1810:0250 PUSH AX 1810:0251 PUSH SI 1810:0252 PUSH DI 1810:0253 ;--- INIZIALIZZAZIONE: 0253 1810:0253 MOV AL,[0080] 1810:0256 MOV SI,0082 1810:0259 MOV DI,0200 1810:025C DEC AL 1810:025E ;--- SCORRO STRINGA: 025E 1810:025E CMP AL,0 1810:0260 JLE 026C 1810:0262 MOV AH,[SI] 1810:0264 MOV [DI],AH 1810:0266 INC SI 1810:0267 INC DI 1810:0268 DEC AL 1810:026A JMP 025E 1810:026C ;--- CHIUSURA: 026C 1810:026C MOV AH,24 1810:026E MOV [DI],AH 1810:0270 POP DI 1810:0271 POP SI 1810:0272 POP AX 1810:0273 RET 1810:0274 ;--- CHIUSURA: 0273 1810:0274 - -f 0274 034F 0 -a 0300 1810:0300 ;--- ESTRAI NUMERI 1810:0300 ;--- NEL BUFFER 30 BYTE: 300F 1810:0300 DB ' ' 1810:031E - -a 0350 1810:0350 PUSH AX 1810:0351 PUSH SI 1810:0352 PUSH DI 1810:0353 MOV SI,0200 1810:0356 MOV DI,0300 1810:0359 ;--- INIZIALIZZAZIONE: 0356 1810:0359 MOV AH,[SI] 1810:035B CMP AH,24 1810:035E JE 0375 1810:0360 CMP AH,2D 1810:0363 JE 036F 1810:0365 CMP AH,39 1810:0368 JA 0372 1810:036A CMP AH,30 1810:036D JB 0372 1810:036F ;--- REGISTRA 036F 1810:036F MOV [DI],AH 1810:0371 INC DI 1810:0372 ;--- SUCCESSIVO 0372 1810:0372 INC SI 1810:0373 JMP 0359 1810:0375 ;--- CHIUSURA: 375 1810:0375 MOV [DI],AH 1810:0377 POP DI 1810:0378 POP SI 1810:0379 POP AX 1810:037A RET 1810:037B ;--- CHIUSURA: 037B 1810:037B -n numeri.com -r bx BX 0000 :0 -r cx CX 0000 :37B -w Writing 0027B bytes -q C:\asm>numeri c1i2a3o4 1234 C:\asm>numeri ciao C:\asm>numeri 123 123 C:\asm> |
La soluzione proposta utilizza le chiamate
a due procedure (sottoprogrammi che terminano con RET). La procedura all'indirizzo CS:0250 si preoccupa di: 1) leggere la stringa dei parametri (all'indirizzo 0082) 2) copiarla nella zona di memoria DS:0200-024F 3) aggiungere un "$" alla fine. La seconda procedura (CS:0350) scorre la stringa nella zona di memoria DS:0200 e ricopia in DS:0300 solo i caratteri numerici (compreso il segno). Al termine utilizzo il servizio 21 del DOS di "stampa stringa". Richiede in AH il valore 9 mentre in DX il puntatore alla stringa da stampare. La stringa deve terminare con "$". PROCEDURA 0250: PROCEDURA 0350: |
a ; --- PROC LETTURA PARAMETRO CALL 250 ; --- ESTRAI NUMERI CALL 350 ; --- LI STAMPO MOV AH,9 MOV DX,0300 INT 21 RET ;--- FINE MAIN: 010E f 010E 024F 0 a 0200 ;--- COPIA STRINGA PARAMETRI ;--- NEL BUFFER 30 BYTE: 200F DB ' ' a 0250 PUSH AX PUSH SI PUSH DI ;--- INIZIALIZZAZIONE: 0253 MOV AL,[0080] MOV SI,0082 MOV DI,0200 DEC AL ;--- SCORRO STRINGA: 025E CMP AL,0 JLE 026C MOV AH,[SI] MOV [DI],AH INC SI INC DI DEC AL JMP 025E ;--- CHIUSURA: 026C MOV AH,24 MOV [DI],AH POP DI POP SI POP AX RET ;--- CHIUSURA: 0273 f 0274 034F 0 a 0300 ;--- ESTRAI NUMERI ;--- NEL BUFFER 30 BYTE: 300F DB ' ' a 0350 PUSH AX PUSH SI PUSH DI MOV SI,0200 MOV DI,0300 ;--- INIZIALIZZAZIONE: 0356 MOV AH,[SI] CMP AH,24 JE 0375 CMP AH,2D JE 036F CMP AH,39 JA 0372 CMP AH,30 JB 0372 ;--- REGISTRA 036F MOV [DI],AH INC DI ;--- SUCCESSIVO 0372 INC SI JMP 0359 ;--- CHIUSURA: 375 MOV [DI],AH POP DI POP SI POP AX RET ;--- CHIUSURA: 037B n numeri.com r bx 0 r cx 37B w q (mettere un invio dopo q) |
Esercizio 5
(SERVIZI BIOS 10)
Costruire un programma "cursore" che legge la posizione del cursore nel video, lo pulisce e poi scrive al centro del video le coordinate.
SOLUZIONE | Osservazioni: | CREA CON: DEBUG<CURSORE.ASM |
C:\asm>debug -a 1810:0100 ;--- LEGGO LA POSIZIONE CURSORE 1810:0100 MOV AH,3 1810:0102 MOV BH,0 1810:0104 INT 10 1810:0106 ;--- MEMORIZZO LE COORDINATE 1810:0106 PUSH DX 1810:0107 ;--- PULISCE LO SCHERMO E IMPOSTA 1810:0107 ;--- MODE TTY 25x80 1810:0107 ; MOV AL,2 - ;MOV AH,0 1810:0107 MOV AX,0002 1810:010A INT 10 1810:010C ;--- POSIZIONO CURSORE: R.5-C.32 1810:010C MOV DX,0520 1810:010F CALL 300 1810:0112 ;--- AGGIUNGO A "COLONNA: ___" 1810:0112 ;--- LA COORDINATA RELATIVA 1810:0112 POP DX 1810:0113 MOV SI,020B 1810:0116 CALL 250 1810:0119 PUSH DX 1810:011A ;--- STAMPA LA COLONNA 1810:011A MOV AH,9 1810:011C MOV DX,0200 1810:011F INT 21 1810:0121 ;--- POSIZIONO CURSORE: R.5-C.32 1810:0121 MOV DX,0620 1810:0124 CALL 300 1810:0127 ;--- AGGIUNGO ALLA STRINGA "RIGA: ___" 1810:0127 ;--- LA COORDINATA RELATIVA 1810:0127 POP DX 1810:0128 MOV DL,DH 1810:012A MOV SI,0218 1810:012D CALL 250 1810:0130 ;--- STAMPA LA RIGA 1810:0130 MOV DX,020D 1810:0133 INT 21 1810:0135 RET 1810:0136 -a 200 1810:0200 ; --- DS:0200 1810:0200 DB 'Colonna: ___$' 1810:020D ; --- DS:020D 1810:020D DB 'Riga: ___$' 1810:021A -a 250 1810:0250 ; PROCEDURA 250: AGGIUNGE LA 1810:0250 ; COORDINATA ALLA STRINGA DI OUTPUT 1810:0250 PUSH AX 1810:0251 PUSH DX 1810:0252 PUSH SI 1810:0253 MOV AL,DL 1810:0255 XOR AH,AH 1810:0257 MOV DL,A 1810:0259 ; LOOP 259 1810:0259 DIV DL 1810:025B ADD AH,30 1810:025E MOV [SI],AH 1810:0260 XOR AH,AH 1810:0262 DEC SI 1810:0263 CMP AL,0 1810:0265 JNZ 0259 1810:0267 POP SI 1810:0268 POP DX 1810:0269 POP AX 1810:026A RET 1810:026B -a 300 1810:0300 ; PROCEDURA 300: POSIZIONA IL 1810:0300 ; CURSORE AH: RIGA - AL: COLONNA 1810:0300 PUSH AX 1810:0301 PUSH BX 1810:0302 PUSH DX 1810:0303 MOV BH,0 1810:0305 MOV AH,2 1810:0307 ; Colonna: MOV DL,20 1810:0307 ; Riga: : MOV DH,5 1810:0307 INT 10 1810:0309 POP DX 1810:030A POP BX 1810:030B POP AX 1810:030C RET 1810:030D -n cursore.com -r bx BX 0000 :0 -r cx CX 0000 :20D -w Writing 0020D bytes -q C:\asm> |
La soluzione proposta utilizza delle chiamate
a procedure (sottoprogrammi che terminano con RET). MOV AH,3 MOV BH,0 INT 10 PUSH DX ==> Leggo la posizione del cursore (In DL ho il numero di colonna mentre in DH il numero di riga!). Salvo nello stack le coordinate. MOV AX,0002 INT 10 ==> Impostando la modalità video (AH=0) 80x25 (AL=2) sfrutto l'effetto collaterale che pulisce il video MOV DX, 0520 CALL 300 ==> Posiziono il cursore sulla riga 5 e colonna 32 (20 in hex) POP DX ==> Ripristino in DX le coordinate precedentemente lette MOV SI, 020B CALL 250 PUSH DX ==> Posiziono SI sul punto finale della stringa "Colonna: ___$". La procedura inserirà il numero di colonna in fondo alla stringa. Al termine rimetto nello stack le coordinate. MOV AH,9 MOV DX, 0200 INT 21 ==> La stampa avviene mediante l'utilizzo dell'interrupt 21 ed esattamente del servizio DOS di "stampa stringa". Richiede in AH il valore 9 mentre in DX il puntatore alla stringa da stampare. La stringa deve terminare con "$". MOV DX, 0620 CALL 300 ==> Posiziono il cursore sulla riga 6 e colonna 32 (20 in hex) POP DX ==> Ripristino in DX le coordinate lette MOV DL,DH ==> Metto in DL la coordinata della riga (che è in DH). DL è uno dei parametri della procedura CS:0250 MOV SI, 0218 CALL 250 ==> Posiziono SI sul punto finale della stringa "Riga: ___$". La procedura inserirà il numero di riga in fondo alla stringa. MOV DX, 020D INT 21 ==> La stampa avviene mediante l'utilizzo dell'interrupt 21 ed esattamente del servizio DOS di "stampa stringa". Richiede in AH il valore 9 mentre in DX il puntatore alla stringa da stampare. La stringa deve terminare con "$". PROCEDURA 0300: PROCEDURA 0250: |
a ;--- LEGGO LA POSIZIONE CURSORE MOV AH,3 MOV BH,0 INT 10 ;--- MEMORIZZO LE COORDINATE PUSH DX ;--- PULISCE LO SCHERMO E IMPOSTA ;--- MODE TTY 25x80 ; MOV AL,2 - ;MOV AH,0 MOV AX,0002 INT 10 ;--- POSIZIONO CURSORE: R.5-C.32 MOV DX,0520 CALL 300 ;--- AGGIUNGO A "COLONNA: ___" ;--- LA COORDINATA RELATIVA POP DX MOV SI,020B CALL 250 PUSH DX ;--- STAMPA LA COLONNA MOV AH,9 MOV DX,0200 INT 21 ;--- POSIZIONO CURSORE: R.5-C.32 MOV DX,0620 CALL 300 ;--- AGGIUNGO A "RIGA: ___" ;--- LA COORDINATA RELATIVA POP DX MOV DL,DH MOV SI,0218 CALL 250 ;--- STAMPA LA RIGA MOV DX,020D INT 21 RET a 200 ; --- DS:0200 DB 'Colonna: ___$' ; --- DS:020D DB 'Riga: ___$' a 250 ; PROCEDURA 250: AGGIUNGE LA ; COORDINATA ALLA STRINGA DI OUTPUT PUSH AX PUSH DX PUSH SI MOV AL,DL XOR AH,AH MOV DL,A ; LOOP 259 DIV DL ADD AH,30 MOV [SI],AH XOR AH,AH DEC SI CMP AL,0 JNZ 0259 POP SI POP DX POP AX RET a 300 ; PROCEDURA 300: POSIZIONA IL ; CURSORE AH: RIGA - AL: COLONNA PUSH AX PUSH BX PUSH DX MOV BH,0 MOV AH,2 ; Colonna: MOV DL,20 ; Riga: : MOV DH,5 INT 10 POP DX POP BX POP AX RET n cursore.com r bx 0 r cx 20D w q (mettere un invio dopo q) |
Esercizio 6 (MicroChip 8253 -DA CONTROLLARE)
Prima di procedere vediamo un pò di teoria
Un PIT (programmable interval timer) è un contatore che emette un interrupt quando esso raggiunge un determinato valore. L'Intel 8253 e la sua evoluzione l'8254 sono dei PIT, che svolgono funzioni di temporizzazione e conteggio. Essi o equivalenti sono installati in tutti i PC IBM compatibili. I PIT vengono utilizzati per:
• Generare dei ritardi sotto il controllo software
• Generare dei segnali (onde quadre, impulsi) con frequenza programmabile
• Contatore di eventi
• Misuratore di intervalli
• Divisore di frequenza.
L'8253 ha 3 contatori chiamati
canali. Ogni canale può essere programmato per operare in 6 modi. Una volta
programmato, i canali svolgono la loro attività in maniera indipendente. Al
timer viene assegnato l'IRQ-0 (che corrisponde all'interrupt hardware a più alta
priorità) per l'alto livello di criticità svolto e perchè molte device dipendono
da esso. I 3 contatori da 16 bit (identificati con counter 0, counter 1, counter
2 e selezionati mediante i pin A0 e A1) hanno 2 pin di input (Clock e Gate) e un
pin di Output.
Nei primi PC IBM il primo
contatore (A0=0 e A1=0) era utilizzato per la gestione dell'orario, il secondo
(A1=0, A0=1) per innescare il refresh della DRAM mentre il terzo (A1=1, A0=0)
per generare suoni tramite lo speaker del PC. Gli indirizzi dei contatori 0, 1 e
2 sono rispettivamente 40h, 41h e 42h
Il microchip 8253 contiene un
Data/Bus Buffer che è costituito da un buffer bidirezionale di 8 bit che può
assumere il valore 3-state. E' composto da 8 pin etichettati con D0...D7 (D7 è
il bit + significativo) collegati al Data Bus del sistema. Il Data Bus è
utilizzato principalmente per:
• programmare l’8253 (definendo il modo di funzionamento per ogni contatore)
• inizializzare i contatori
• leggere i valori dei contatori.
Il microchip viene programmato agendo sul Registro di Controllo (A1=1,
A0=1) del dispositivo scegliendo tra 6 modi diversi di funzionamento. Il
Registro di controllo ha indirizzo 43h. Quando il registro di controllo viene
impostato i contatori vengono resettati a 0000h. Il
microchip viene attivato tramite il segnale CS. Quindi l’8253 è programmabile
via software. Un insieme di parole di controllo devono essere scritte dalla CPU
per inizializzare ciascun contatore. Prima dell’inizializzazione, il modo di
funzionamento, il contenuto e l’uscita di ogni contatore sono indefiniti.
Quindi per ciascun contatore, il valore nel Registro di Controllo
determina:
• il modo di funzionamento
• le modalità di caricamento del valore di inizializzazione
• il tipo di conteggio (binario o BCD).
La codifica Binary-coded decimal
(BCD) è un modo comunemente utilizzato in informatica ed elettronica per
rappresentare le cifre decimali in codice binario.
In questo formato ogni cifra di un numero è rappresentata da un codice binario
di quattro bit, il cui valore è compreso tra 0 (0000) e 9 (1001). Il codice BCD
è molto usato in elettronica, specialmente in circuiti digitali privi di
microprocessore, perché facilita la visualizzazione di lunghe cifre su display a
sette segmenti, infatti ad ogni display fisico corrisponde esattamente una
cifra.
Le operazioni di input/output
sono riassunte in questa tabella:
mentre i 6 modi di funzionamento sono:
Nel modo 0 il conteggio parte non appena termina l'operazione di caricamento del contatore. Durante il conteggio il pin di OUT resta basso. Al termine diventa alto e ci resta fino al caricamento di un nuovo valore. Il pin GATE abilita il conteggio quando è alto, lo disabilita quando è basso.
Nel modo 1 il conteggio
inizia in corrispondenza del fronte di salita di GATE. Il conteggio non inizia
quindi con l'operazione di caricamento del contatore. Durante il conteggio OUT
resta basso, altrimenti è alto. Se nel contatore viene caricato un nuovo valore
, questo non modifica il conteggio in corso, ma quello successivo. Se si ha un
fronte di salita di GATE durante il conteggio, il conteggio viene fatto
ripartire da capo. L'operazione di WR rappresenta l'operazione di caricamento
del contatore.
Nel modo 2 il contatore funziona come divisore di frequenza. Ogni n cicli (n è il valore del counter) su CLK, OUT resta basso per un ciclo. Il conteggio parte al caricamento del contatore; un nuovo caricamento non interessa il conteggio in corso, ma il successivo. Quando il segnale di GATE è basso, OUT rimane fisso al valore alto e il contatore rimane fisso; il successivo fronte di salita fa ripartire il conteggio da capo.
Verrà descritto il modo in cui all’interno di un PC è connesso lo speaker, e come il suo funzionamento possa essere programmato via software.
Su un IBM-PC il canale 2 del timer è connesso, tramite una porta AND, allo speaker del computer.
Lo starter del timer è connesso al bit 0 della porta 61h (SPEAKER DATA). Mandando 1 a tale bit della porta implica l'attivazione del timer. Se la modalità è un'onda quadra (mode 3) allora tale segnale verrà inviato (tramite il canale 2) come input alla porta AND che alimenta lo speaker. L'altro input della porta AND è rappresentato dal bit 1 della porta 61h (chiamato GATE2). La frequenza dell'onda quadra è determinato da come il timer è stato programmato.
Il canale 2 del timer (contatore 2 dell'8253) è connesso tramite una porta AND allo speaker del computer.
Lo starter del timer è connesso al bit 0 della porta 61h (SPEAKER DATA). Mandando 1 a tale bit della porta implica l'attivazione del timer. Se la modalità è un'onda quadra (mode 3) allora tale segnale verrà inviato (tramite il canale 2) come input alla porta AND che alimenta lo speaker. L'altro input della porta AND è rappresentato dal bit 1 della porta 61h (chiamato GATE2). La frequenza dell'onda quadra è determinato da come il timer è stato programmato.
ù
Il computer ha uno speaker interno che è in grado di generare dei beep a diversa frequenza. Essendo la frequenza rappresentata da un numero a 16 bit segue che è possibile generare una qualsiasi frequenza dal 18,21hz (se vale 65.535) a 1.193.180 hz (se vale 1). Le frequenze abbinate alle 7 note nell'ottava centrale del pianoforte sono: DO 261,7 Hz (4560) - RE 293,7 Hz (4063) - MI 329,6 Hz (3619)- FA 349,2 Hz (3416) - SOL 391,0 Hz (3043) - LA 440,0 Hz (2711) - SI 493,9 Hz (2415).
Esercizio 6.a (IN-OUT 8253)
Costruire un programma che inizializza i 3
contatori del microchip 8253 in questo modo:
• Contatore 0: Conteggio Binario, modo 0 con valore
iniziale 1234H
• Contatore 1: Conteggio BCD, modo 2 con valore iniziale 100H
• Contatore 2: Conteggio Binario, modo 4 con valore iniziale 1FFFH.
SOLUZIONE:
Partiamo con il primo contatore e consideriamo il registro di controllo:
Per selezionare il counter 0 devo porre SC1 e SC0 uguale entrambe a 0. Il modo richiesto è 0 per cui M2,M1 e M0 devono essere posti a 0. Essendo richiesto il conteggio binario nel bit BCD devo mettere 0. Poiché devo caricare il valore 1234h che richiede 2 byte imposto RL1 e RL0 entrambe a 1. In questo modo posso impostare prima il byte meno significativo e poi quello più significativo. Il registro di controllo deve essere quindi inizializzato a 00110000 che corrisponde a 30h. Tenendo presente che il registro di controllo ha indirizzo 43h e il contatore 0 l'indirizzo 40h ne consegue:
; Contatore 0 - modo 0 - conteggio binario - valore iniziale 1234h
MOV AL, 30 ;
Imposto il registro di controllo
OUT 43, AL
MOV AL, 34
; Carico il byte meno significativo
OUT 40, AL
MOV AL, 12
; Carico il byte più significativo
OUT 40, AL
Per selezionare il counter 1 devo porre SC1=0 e SC0=1. Il modo richiesto è 2 per cui pongo M2=0,M1=1 e M0=0 . Essendo richiesto il conteggio BCD nel bit BCD devo mettere 1. Considerando che i contatori vengono resettati durante la scrittura del registro di controllo per caricare 100h mi basta impostare solo il byte più significativo (MSB). Quindi imposto RL1=1 e RL0=0. Il registro di controllo deve essere quindi inizializzato a 01100101 che corrisponde a 75h. Tenendo presente che il registro di controllo ha indirizzo 43h e il contatore 1 l'indirizzo 41h ne consegue:
; Contatore 1 - modo 2 - conteggio BCD - valore iniziale 100h
MOV AL, 65 ;
Imposto il registro di controllo
OUT 43, AL
MOV AL, 1
; Carico il byte più significativo
OUT 41, AL
Per selezionare il counter 2 devo porre SC1=1 e SC0=0. Il modo richiesto è 4 per cui pongo M2=1,M1=0 e M0=0 . Essendo richiesto il conteggio binario nel bit BCD devo mettere 0. Poiché devo caricare il valore 01FFh che richiede 2 byte imposto RL1 e RL0 entrambe a 1. In questo modo posso impostare prima il byte meno significativo e poi quello più significativo. Il registro di controllo deve essere quindi inizializzato a 10111000 che corrisponde a 30h. Tenendo presente che il registro di controllo ha indirizzo 43h e il contatore 1 l'indirizzo 41h ne consegue:
; Contatore 2 - modo 4 - conteggio Binario - valore iniziale 01FFh
MOV AL, B8 ;
Imposto il registro di controllo
OUT 43, AL
MOV AL, FF
; Carico il byte meno significativo
OUT 42, AL
MOV AL, 1F
; Carico il byte più significativo
OUT 42, AL
Esercizio 6.b (IN-OUT 8253)
Si programmi un 8253 in modo da generare un ritardo
di 5 msec. Si supponga di avere a disposizione un clock da 1 Mhz.
SOLUZIONE:
Con un Mhz il ciclo di clock è 1 microsecondo per cui per avere un ritardo di 5 millisecondi devo far trascorrere 5000 cicli di clock. Determiniamo come settiamo il registro di controllo. Immaginiamo di utilizzare il contatore 1
Per selezionare il counter 1 devo porre SC1=0 e SC0=1. Il modo richiesto è 0 per cui M2,M1 e M0 devono essere posti a 0. Se utilizzo il conteggio BCD posso inizializzare il contatore a 0101 0000 0000 0000 ovvero a 5000h. Imposto quindi il conteggio BCD per cui nel bit BCD devo mettere 1. Poiché devo caricare il valore 50h nel byte più significativo e tenendo conto che i contatori vengono resettati durante la scrittura del registro di controllo mi basta impostare imposto RL1=1 e RL0=0 (scrittura/lettura MSB). Il registro di controllo deve essere quindi inizializzato a 01100001 che corrisponde a 61h. Tenendo presente che il registro di controllo ha indirizzo 43h e il contatore 0 l'indirizzo 40h ne consegue:
; Contatore 1 - modo 0 - conteggio BC - valore iniziale 5000h
MOV AL, 61 ;
Imposto il registro di controllo
OUT 43, AL
MOV AL, 50
; Carico il byte più significativo
OUT 42, AL
Esercizio 6.c (IN-OUT 8253 - DA VERIFICARE SU UN VECCHIO PC)
Costruire un programma che genera un beep la cui
durata è determinata da un doppio loop di 16x65535 cicli. La
gestione dello speaker deve avvenire utilizzando le istruzioni IN e OUT
SOLUZIONE:
SOLUZIONE | Osservazioni: | CREA CON: DEBUG<SOLUZ1.ASM |
C:\asm>debug -a -a 180E:0100 ; Setup speaker 180E:0100 MOV AL,B6 180E:0102 OUT 43,AL 180E:0104 ; Imposto la frequenza 180E:0104 MOV AX,11D0 180E:0107 OUT 42,AL 180E:0109 MOV AL,AH 180E:010B OUT 42,AL 180E:010D ; Attivo il beep 180E:010D IN AL,61 180E:010F OR AL,3 180E:0111 OUT 61,AL 180E:0113 ; Doppio Loop 180E:0113 MOV BX,0010 180E:0116 ; CS:0116 180E:0116 MOV CX,FFFF 180E:0119 ; CS:0119 180E:0119 DEC CX 180E:011A JNZ 0119 180E:011C DEC BX 180E:011D JNZ 0116 180E:011F ; Spengo il beep 180E:011F IN AL,61 180E:0121 AND AL,FC 180E:0123 OUT 61,AL 180E:0125 RET 180E:0126 -r bx BX 0000 :0 -r cx CX 0000 :23 -n sound.com -w Writing 0000C bytes -q C:\asm>sound C:\asm> |
MOV AL, B6 OUT 43,AL ==> Setto lo speaker inviando il valore 182 (10110110b quindi Contatore 2 - r/w LSB sequito da MSB - modo 3 - conteggio binario) alla porta 43h. MOV AX,11D0 OUT 42, AL MOV AL, AH OUT 42, AL ==>Imposto una frequenza di 261.63 Hz (DO centrale) inviandola alla porta 42h (11D0h corrisponde al numero decimale 4560). Poiché la porta dello speaker è a 8 bit sono necessarie due istruzioni IN AL,61 OR AL,3 OUT 61,AL ==> Per attivare il beep devo accendere i bit 0 e 1 sulla porta 61h (OUT 61,AL). Poichè tutti gli altri bit della porta devono rimanere inalterati deve essere fatta prima una lettura (IN AL,61) . Con OR AL,3 accendo i bit 0 e 1 (3h corrisponde a 00000011b). MOV BX,0011 MOV CX,FFFF DEC CX JNZ 0119 DEC BX JNZ 0116 ==> Doppio loop (16x65535) per determinare una pausa prima della disattivazione del beep. Un'altro modo per controllare la durata del beep è utilizzate il timer interrupt. Questa tecnica consente un maggior controllo della durata e permette l'esecuzione di altri task mentre viene suonata la nota. IN AL,61 AND AL,FC OUT 61,AL ==> Disattivo il beep riportando a 0 i bit 0 e 1 della porta 61h (FCh corrisponde a 11111100b) utilizzando l'istruzione AND. Poichè tutti gli altri bit della porta devono rimanere inalterati deve essere fatta prima una lettura (IN AL,61) . |
a ; Setup speaker MOV AL,B6 OUT 43,AL ; Imposto la frequenza MOV AX,11D0 OUT 42,AL MOV AL,AH OUT 42,AL ; Attivo il beep IN AL,61 OR AL,3 OUT 61,AL ; Doppio Loop MOV BX,0010 ; CS:0116 MOV CX,FFFF ; CS:0119 DEC CX JNZ 0119 DEC BX JNZ 0116 ; Spengo il beep IN AL,61 AND AL,FC OUT 61,AL RET n sound.com r cx 26 w q (mettere un invio dopo q) |
Esercizio 6.d
(IN-OUT 8253 - DA VERIFICARE)
Costruire la procedura beep, che riceve in DI la
frequenza in Hz, ed in BX la durata in centesimi di
secondo del suono desiderato.
SOLUZIONE | Osservazioni: | CREA CON: DEBUG<SOLUZ1.ASM |
C:\asm>debug -a -a 180E:0100 ; Setup speaker 180E:0100 MOV AL,B6 180E:0102 OUT 43,AL 180E:0104 ; Imposto la frequenza 180E:0104 MOV AX,11D0 180E:0107 OUT 42,AL 180E:0109 MOV AL,AH 180E:010B OUT 42,AL 180E:010D ; Attivo il beep 180E:010D IN AL,61 180E:010F OR AL,3 180E:0111 OUT 61,AL 180E:0113 ; Doppio Loop 180E:0113 MOV BX,0010 180E:0116 ; CS:0116 180E:0116 MOV CX,FFFF 180E:0119 ; CS:0119 180E:0119 DEC CX 180E:011A JNZ 0119 180E:011C DEC BX 180E:011D JNZ 0116 180E:011F ; Spengo il beep 180E:011F IN AL,61 180E:0121 AND AL,FC 180E:0123 OUT 61,AL 180E:0125 RET 180E:0126 -r bx BX 0000 :0 -r cx CX 0000 :23 -n sound.com -w Writing 0000C bytes -q C:\asm>sound C:\asm> |
MOV DI,500 MOV BX,15 ==> Carico in DI la frequenza in hz. In BX metto la durata in centesimi di secondo. CALL 200 ==> Richiamo la mia procedura BEEP ADD DI,20 CMP DI,2000 JB 106 ==> Incremento DI e passo alla successiva frequenza. Comparo DI e se è minore di 2000 ripeto il loop. CS:0200 PUSH AX ... ==> Salvo il valore dei registri utilizzati nello stack MOV AL, B6 OUT 43,AL ==> Setto lo speaker inviando il valore 182 (10110110b quindi Contatore 2 - r/w LSB seguito da MSB - modo 3 - conteggio binario) alla porta 43h. MOV DX,14 MOV AX, 43F8 ==> Carico la frequenza di clock del microchip 8253 DIV DI ==> Divido per la frequenza voluta e ripongo la parte intera in AX (il resto in DX) OUT 42, AL MOV AL, AH OUT 42, AL ==>Imposto la frequenza inviandola alla porta 42h. Poiché la porta dello speaker è a 8 bit sono necessarie due istruzioni MOV CX,FFFF LOOP 21E DEC BX JNZ 21B ==>Imposto un'attesa di 150 millisecondi IN AL,61 AND AL,FC OUT 61,AL ==> Disattivo il beep riportando a 0 i bit 0 e 1 della porta 61h (FCh corrisponde a 11111100b) utilizzando l'istruzione AND. Poichè tutti gli altri bit della porta devono rimanere inalterati deve essere fatta prima una lettura (IN AL,61) . |
a ; Suona una scala che va da ; 500 a 2000 Hz. Ogni nota ; dura 150 msec. MOV DI,500 MOV BX,30 CALL 200 ADD DI,20 CMP DI,2000 JB 106 RET a 200 PUSH AX PUSH BX PUSH CX PUSH DX ; contatore 2 - r/w 2 byte ; modo 3 - binary count MOV AL,B6 OUT 43,AL ; Carico la Frequenza di clock ; 14 4F38 = 1.331.000 MOV DX,14 MOV AX,4F38 DIV DI ;Frequenza LSB e MSB OUT 42,AL MOV AL,AH OUT 42,AL ; Abilito il beeper IN AL,61 OR AL,03 OUT 61,AL ; CS:021C ; LOOP PRINCIPALE MOV CX,FFFF ; loop a vuoto 021F LOOP 21F DEC BX JNZ 21C ;Disabilito il beeper IN AL,61 AND AL,FC OUT 61,AL POP DX POP CX POP BX POP AX RET n sound3.com r cx 12f r bx 0 w q (mettere un invio dopo q) |
Esercizio 7 (SERVIZI DOS 21 SUI GESTORI {handler} )
Esercizio 7.a
(lettura file, caricamento in un buffer e stampa a video)
Costruire un programma "leggi" che legge
i primi 80 byte del file c:\sechi.txt e lo stampa a video
Prima di procedere vediamo un po' di teoria
Per scrivere la soluzione è necessario ricorrere alle Funzioni DOS che si affidano ai gestori (handle). Sono numerose e, di norma, destinate alla gestione dei files; appartengono a questa serie la Funzione 3CH (per "creare"), la Funzione 3DH (per "aprire"), la Funzione 3EH (per "chiudere"), la Funzione 3FH (per "leggere") e la Funzione 40H (per "scrivere").
In altri termini, con questa tecnica il DOS tratta allo stesso modo (virtualizza) sia i principali dispositivi con cui ha a che fare (CON, COM, ERR) che i files presenti nelle cartelle delle memorie di massa (ai quali assegna i gestori successivi, di numero maggiore o uguale a 5), trattandoli come semplici oggetti da aprire, leggere o scrivere, chiudere.
INT 21H | Funzione 3CH | Crea un file restituendone il gestore (handler) |
Questa funzione DOS permette di
creare un file. I registri in ingresso coinvolti sono:
AH: che viene posto a 3CH proprio per richiamare la funzione di
creazione.
CX: viene impostato con i flag relativi agli attirbuti del file:
BIT 0 = 1 READ-ONLY
BIT 1 = 1 HIDDEN FILE
BIT 2 = 1 SYSTEM FILE
BIT 3 = 1 VOLUME (IGNORED)
BIT 4 = 1 RESERVED (0)- DIRECTORY
BIT 5 = 1 ARCHIVE BIT
BIT 6 TO 15 RESERVED (0)
DX: indica l'indirizzo logico dell'area di memoria dove si trova il nome
del file. Il nome deve terminare con il carattere '\0'.
Il registro coinvolto in uscita è AX . Esso contiene il descrittore (handle)
associato al file creato. Il flag CF (carry flag) contiene 0 se la
funzione è terminata con successo altrimenti se CF contiene 1 allora si è
verificato un errore e in AX viene inserito il codice di errore:
03h: path not found
04h: handle non disponibile
05h: accesso negato
INT 21H | Funzione 3DH | Apre un file o un dispositivo restituendone il gestore (handler) |
Questa funzione DOS permette di aprire un dispositivo restituendone il
descrittore. I registri in ingresso coinvolti sono:
AH: che viene posto a 3DH proprio per richiamare la funzione di apertura
- AL a 00.
AL: contiene i flag relativi alla modalità di apertura.
Dal bit 0 al bit 2 la modalità di accesso: 000: read only - 001: write only -
010: read and write
Dal bit 4 al bit 6 la modalità di condivisione: 000: compatibility mode - 001:
deny all - 010: dedy write - 011: deny read - 100 deny none.
DX: indica l'indirizzo logico dell'area di memoria dove si trova il nome
del file. Il nome deve terminare con il carattere '\0'.
Il registro coinvolto in uscita è AX . Esso contiene il descrittore (handle)
associato al file aperto. Il flag CF (carry flag) contiene 0 se la
funzione è terminata con successo altrimenti se CF contiene 1 allora si è
verificato un errore e in AX viene inserito il codice di errore:
01H MISSING FILE SHARING SOFTWARE
02H FILE NOT FOUND
03H PATH NOT FOUND/ FILE DOESN'T EXIST
04H NO HANDLE AVAILABLE
05H ACCESS DENIED
0CH ACCESS MODE NOT PERMITTED
INT 21H | Funzione 3EH | Chiude un file o un dispositivo |
Questa funzione DOS permette di
chiudere un dispositivo il cui descrittore è inserito nel registro BX.
il descrittore. I registri in ingresso coinvolti sono:
AH: che viene posto a 3EH proprio per richiamare la funzione di chiusura
- AL a 00.
BX: contiene il descrittore del file o del dispositivo da chiudere.
Se CF contiene 0 allora la funzione ha avuto successo altrimenti se
vale 1 in AX viene messo il codice di errore: 06h: file non
aperto/gestore non autorizzato. Attenzione se uso come handle lo zero chiudo lo
STDIN per cui non potrò inserire più niente da tastiera.
INT 21H | Funzione 3FH | Legge uno o più bytes da un file o da un dispositivo, inserendoli in un buffer |
Questa funzione DOS permette di
leggere uno o più bytes in un file o in un dispositivo, inserendoli in un
buffer. I registri in ingresso coinvolti sono:
AH: che viene posto a 3FH proprio per richiamare la funzione di lettura
BX: indica il numero del gestore (handle) coinvolto:
0000=handle 0, associato al dispositivo standard d'ingresso,
tastiera, CON
0002=handle 2, associato al dispositivo standard d'errore,
errore, ERR
0003=handle 3, associato al dispositivo standard ausiliario,
seriale, AUX, COM
0005=handle 5, primo gestore da associare ad un file
attualmente aperto
CX: indica il numero di bytes da leggere
DS:DX: indica l'indirizzo logico dell'area di memoria (buffer) in
cui salvare i bytes.
Il registro coinvolto in uscita è AX . Esso contiene (se CF=0) il
numero di bytes effettivamente letti, consentendo una eventuale verifica a
posteriori.
Se CF=1 allora in AX ho il codice d'errore (05h: accesso
negato - 06h: handle illegale, file non aperto) che consente di
risalire alla causa del mancato funzionamento. Se AX=0 e CF=0 significa che sono
arrivato alla fine del file e non ci sono più dati da leggere.
INT 21H | Funzione 40H | Scrive uno o più bytes in un file o verso un dispositivo, prelevandoli da un buffer |
Questa funzione DOS permette
di scrivere uno o più bytes in un file o in un dispositivo, prelevandoli da un
bufferl. I registri in ingresso coinvolti sono:
AH: viene posto a 40H per richiamare la funzione di scrittura
BX: indica il numero del gestore (handle) coinvolto:
0001=handle 1, associato al dispositivo standard d'uscita,
video, CON
0002=handle 2, associato al dispositivo standard d'errore,
errore, ERR
0003=handle 3, associato al dispositivo standard ausiliario,
seriale, AUX, COM
0004=handle 4, associato al dispositivo standard di stampa,
stampante, PRN
0005=handle 5, primo gestore da associare ad un file
attualmente aperto
CX: indica il numero di bytes da scrivere
DS:DX: indica l'indirizzo logico dell'area di memoria (buffer) da
cui prelevare i bytes da scrivere.
In uscita usa AX dove lascia (se CF=0) il numero di bytes
effettivamente scritti, consentendo una eventuale verifica a posteriori:
mentre se CF=1 mette in AX il codice d'errore che consente di
risalire alla causa del mancato funzionamento.
INT 21H | Funzione 44H/02H | Legge una stringa di bytes in un dispositivo di tipo "carattere", prelevandola da un buffer |
Questa funzione DOS permette
di leggere una stringa di bytes da un dispositivo di tipo "carattere",
inserendola in un buffer. L'effetto di questa SottoFunzione è sostanzialmente
identico a quello della Funzione 3FH. I registri in ingresso coinvolti sono:
AH: è posto a
44H
AL: è posto a 02H
BX: indica il numero del gestore (handle) coinvolto:
0000=handle 0, associato al dispositivo standard d'ingresso,
tastiera, CON
0002=handle 2, associato al dispositivo standard d'errore,
errore, ERR
0003=handle 3, associato al dispositivo standard ausiliario,
seriale, AUX, COM
0005=handle 5, primo gestore da associare ad un file
attualmente aperto
CX: indica il numero di bytes della stringa da leggere
DS:DX: indica l'indirizzo logico dell'area di memoria (buffer) in cui
salvare i bytes della stringa
Il registro coinvolto in uscita
è AX . Esso contiene (se CF=0) il numero di bytes effettivamente
letti, consentendo una eventuale verifica a posteriori.
Se CF=1 allora in AX ho il codice d'errore che consente di
risalire alla causa del mancato funzionamento.
INT 21H | Funzione 44H/03H | Scrive una stringa di bytes in un dispositivo di tipo "carattere", prelevandola da un buffer |
Questa funzione DOS permette di
scrivere una stringa di bytes su un dispositivo di tipo carattere, prelevandola
da un buffer. L'effetto di questa SottoFunzione è sostanzialmente identico
a quello della Funzione 40H. I registri in ingresso coinvolti sono:
AH: è posto a 44H
AL: è posto a 03H
BX: indica il numero del gestore (handle) coinvolto:
0001=handle 1, associato al dispositivo standard d'uscita,
video, CON
0002=handle 2, associato al dispositivo standard d'errore,
errore, ERR
0003=handle 3, associato al dispositivo standard ausiliario,
seriale, AUX, COM
0004=handle 4, associato al dispositivo standard di stampa,
stampante, PRN
0005=handle 5, primo gestore da associare ad un file
attualmente aperto
CX: indica il numero di bytes della stringa da scrivere
DS:DX: indica l'indirizzo logico dell'area di memoria (buffer) da cui
prelevare i bytes della stringa.
In uscita usa AX dove
lascia (se CF=0) il numero di bytes effettivamente scritti, consentendo
una eventuale verifica a posteriori:
mentre se CF=1 mette in AX il codice d'errore che consente di
risalire alla causa del mancato funzionamento.
SOLUZIONE:
Nella soluzione proposta si è deciso di ricorrere alle CALL per poter riutilizzare lo stesso codice negli esempi successivi
SOLUZIONE | Osservazioni (7.a): | CREA CON: DEBUG<FILE.ASM |
C:\asm>debug -a 100 180F:0100 ; Carico nel buffer nr byte del 180F:0100 ; file letto 180F:0100 MOV CX,0080 180F:0103 MOV DX,0150 180F:0106 MOV DI,0300 180F:0109 CALL 200 180F:010C ; Stampo il buffer che si 180F:010C ; trova all'indirizzo 0300 180F:010C MOV SI,0300 180F:010F CALL 270 180F:0112 INT 20 180F:0114 -a 150 180F:0150 ; Definisco il nome del 180F:0150 ; file da leggere. Lo 0 180F:0150 ; equivale a \0 finale del C 180F:0150 db 'c:\sechi.txt',0 180F:015D -a 200 180F:0200 ; Procedura Lettura files - Parametri: 180F:0200 ; DX: indirizzo nome file 180F:0200 ; CX: byte da leggere 180F:0200 ; DI: indirizzo buffer di output 180F:0200 PUSH AX 180F:0201 PUSH BX 180F:0202 PUSH CX 180F:0203 PUSH DX 180F:0204 PUSH DI 180F:0205 ; Apro il file il cui path 180F:0205 ; Þ puntato DX - salvo l'handle 180F:0205 XOR AL,AL 180F:0207 MOV AH,3D 180F:0209 INT 21 180F:020B PUSH AX 180F:020C ; Leggo un nr di byte indicato 180F:020C ; in CX e li scarico nel buffer 180F:020C ; indicato in DX+2 180F:020C ; del file 180F:020C MOV DX, DI 180F:020E ADD DX,2 180F:0211 MOV BX, AX 180F:0213 MOV AH, 3F 180F:0215 INT 21 180F:0217 ; Salvo il numero di byte 180F:0217 ; letti all'indirizzo DI 180F:0217 ; e ripristino l'handle in AX 180F:0217 MOV [DI],AX 180F:0219 POP AX 180F:021A ; Chiudo il file - in BX ho 180F:021A ; l'handle 180F:021A MOV BX,AX 180F:021C MOV AH,3E 180F:021E INT 21 180F:0220 ; Ripristino i registri 180F:0220 POP DI 180F:0221 POP DX 180F:0222 POP CX 180F:0223 POP BX 180F:0224 POP AX 180F:0225 RET 180F:0226 -a 270 180F:0270 ; Analizzo la sequenza e la stampo 180F:0270 ; Salvo i registri 180F:0270 PUSH AX 180F:0271 PUSH CX 180F:0272 PUSH DX 180F:0273 PUSH SI 180F:0274 MOV CX,[SI] 180F:0276 ADD SI,2 180F:0279 ; Stampo il carattere corrente 180F:0279 MOV AH,02 180F:027B MOV DL,[SI] 180F:027D INT 21 180F:027F ; Passo al successivo 180F:027F INC SI 180F:0280 DEC CX 180F:0281 JNZ 279 180F:0283 POP SI 180F:0284 POP DX 180F:0285 POP CX 180F:0286 POP AX 180F:0287 RET 180F:0288 -r cx CX 0000 :2FF -n file0.com -w Writing 002FF bytes -q C:\asm> |
MOV CX,0080 MOV DX,0150 MOV DI,0300 ==> Preparo la chiamata alla call 200 CX contiene il nr di byte da leggere - DX: l'indirizzo del nome del file - DI: l'indirizzo del buffer dove scaricare i byte letti CALL 200 ==> Vedi spiegazione procedura 0200 MOV SI,0300 CALL 200 ==> Preparo la seconda CALL che deve stampare il buffer puntato da DI. Nei primi 2 byte di tale area ho il nr di byte contenuti nel buffer. INT 20 ==> Fine programma. DB 'c:\sechi.txt',0 ==> Definisco all'indirizzo CS:0150 il nome del file da leggere. Il nome del file deve terminare con il carattere '\0'. PROCEDURA 0200: La procedura all'indirizzo CS:0200 si preoccupa di leggere CX byte dal file il cui nome è puntato da DX e di scaricarli nel buffer puntato da DI. I primi 2 byte del buffer contengono il nr di caratteri letti. PUSH AX ... ==> Salvo il valore dei registri utilizzati nello stack XOR AL,AL MOV AH,3D INT 21 PUSH AX ==> Utilizzo la funzione DOS che mi consente di aprire il file il cui nome è puntato dal registro DX. Salvo il descrittore in AX per usarlo poi alla chiusura del file MOV DX,DI ADD DX,2 MOV BX,AX MOV AH,3F INT 21 ==> Uso la funzione DOS 3F che consente di leggere un numero di byte indicato in CX dal file il cui descrittore è caricato in BX. La parte letta verrà copiata a partire dall'indirizzo indicato in DX (DI+2 - lo spostamento di 2 byte consente di saltare l'area contenente il nr di byte letti). In AX verrà indicato il numero di byte realmente letti. PUSH AX ==> Memorizzo il nr di caratteri letti nello stack MOV AH,3E MOV BX,DI INT 21 ==> Utilizzo la funzione DOS che mi consente di chiudere il file il cui descrittore è in BX. POP DI ... ==> Ripristino i valori originali nei registri.
PROCEDURA 0270: |
a 100 ; Carico nel buffer nr byte del ; file letto MOV CX,0080 MOV DX,0150 MOV DI,0300 CALL 200 ; Stampo il buffer che si ; trova all'indirizzo 0300 MOV SI,0300 CALL 270 INT 20 a 150 ; Definisco il nome del ; file da leggere. Lo 0 ; equivale a \0 finale del C db 'c:\sechi.txt',0 a 200 ; Procedura Lettura files - Parametri: ; DX: indirizzo nome file ; CX: byte da leggere ; DI: indirizzo buffer di output PUSH AX PUSH BX PUSH CX PUSH DX PUSH DI ; Apro il file il cui path ; Þ puntato DX - salvo l'handle XOR AL,AL MOV AH,3D INT 21 PUSH AX ; Leggo un nr di byte indicato ; in CX e li scarico nel buffer ; indicato in DX+2 ; del file MOV DX, DI ADD DX,2 MOV BX, AX MOV AH, 3F INT 21 ; Salvo il numero di byte ; letti all'indirizzo DI ; e ripristino l'handle in AX MOV [DI],AX POP AX ; Chiudo il file - in BX ho ; l'handle MOV BX,AX MOV AH,3E INT 21 ; Ripristino i registri POP DI POP DX POP CX POP BX POP AX RET a 270 ; Analizzo la sequenza e la stampo ; Salvo i registri PUSH AX PUSH CX PUSH DX PUSH SI MOV CX,[SI] ADD SI,2 ; Stampo il carattere corrente MOV AH,02 MOV DL,[SI] INT 21 ; Passo al successivo INC SI DEC CX JNZ 279 POP SI POP DX POP CX POP AX RET r cx 2FF n file.com w q (mettere un invio dopo q) |
Esercizio 7.b
(lettura file, caricamento in un buffer e stampa a video)
Costruire un programma "leggi" che legge
i primi 128 byte (80hex) del file c:\sechi.txt e stampa il numero A,I, ... (vocali) presenti.
SOLUZIONE:
Nella soluzione proposta la procedura di
lettura del file è identica al precedente esercizio
SOLUZIONE | Osservazioni (7b): | CREA CON: DEBUG<SOLUZ1.ASM |
C:\asm>debug -a 100 1810:0100 ; Carico nel buffer nr byte del 1810:0100 ; file letto 1810:0100 MOV CX,0080 1810:0103 MOV DX,0150 1810:0106 MOV DI,0300 1810:0109 CALL 200 1810:010C ; LOOP NELL'AREA DI MEMORIA 1810:010C MOV CX, [0300] 1810:0110 MOV SI, 0302 1810:0113 ; Carico il carattere 1810:0113 ; corrente da analizzare 1810:0113 MOV AL,[SI] 1810:0115 ; Aggiorno la stastistica vocali 1810:0115 CALL 400 1810:0118 INC SI 1810:0119 DEC CX 1810:011A JNZ 113 1810:011C ; stampa risultati 1810:011C CALL 500 1810:011F INT 20 1810:0121 -a 130 1810:0130 DB 'A','E','I','U','O' 1810:0135 -a 140 1810:0140 DW 0,0,0,0,0 1810:014A -a 150 1810:0150 ; Definisco il nome del 1810:0150 ; file da leggere. Lo 0 1810:0150 ; equivale a \0 finale del C 1810:0150 db 'c:\sechi.txt',0 1810:015D -a 200 1810:0200 ; Procedura Lettura files - Parametri: 1810:0200 ; DX: indirizzo nome file 1810:0200 ; CX: byte da leggere 1810:0200 ; DI: indirizzo buffer di output 1810:0200 PUSH AX 1810:0201 PUSH BX 1810:0202 PUSH CX 1810:0203 PUSH DX 1810:0204 PUSH DI 1810:0205 ; Apro il file il cui path 1810:0205 ; Þ puntato DX - salvo l'handle 1810:0205 XOR AL,AL 1810:0207 MOV AH,3D 1810:0209 INT 21 1810:020B PUSH AX 1810:020C ; Leggo un nr di byte indicato 1810:020C ; in CX e li scarico nel buffer 1810:020C ; indicato in DX+2 1810:020C ; del file 1810:020C MOV DX, DI 1810:020E ADD DX,2 1810:0211 MOV BX, AX 1810:0213 MOV AH, 3F 1810:0215 INT 21 1810:0217 ; Salvo il numero di byte 1810:0217 ; letti all'indirizzo DI 1810:0217 ; e ripristino l'handle in AX 1810:0217 MOV [DI],AX 1810:0219 POP AX 1810:021A ; Chiudo il file - in BX ho 1810:021A ; l'handle 1810:021A MOV BX,AX 1810:021C MOV AH,3E 1810:021E INT 21 1810:0220 ; Ripristino i registri 1810:0220 POP DI 1810:0221 POP DX 1810:0222 POP CX 1810:0223 POP BX 1810:0224 POP AX 1810:0225 RET 1810:0226 -a 270 1810:0270 ; Procedura stampa numero 1810:0270 ; in decimale 1810:0270 ; AX: numero in hex 1810:0270 PUSH AX 1810:0271 PUSH CX 1810:0272 PUSH DX 1810:0273 ; metto 10 nel divisore 1810:0273 ; azzero il contatore 1810:0273 MOV DL,A 1810:0275 XOR CX,CX 1810:0277 ; Divisione per 10 e 1810:0277 ; trasformo il resto in 1810:0277 ; ascii 1810:0277 DIV DL 1810:0279 ADD AH,30 1810:027C PUSH AX 1810:027D INC CX 1810:027E ; Elimino il resto 1810:027E XOR AH,AH 1810:0280 CMP AL,0 1810:0282 JNZ 277 1810:0284 ; Stampo i numeri messi 1810:0284 ; nello stack 1810:0284 POP AX 1810:0285 MOV DL,AH 1810:0287 MOV AH,2 1810:0289 INT 21 1810:028B DEC CX 1810:028C JNZ 0284 1810:028E MOV DL,0a 1810:0290 MOV AH,2 1810:0292 INT 21 1810:0294 MOV DL,0d 1810:0296 MOV AH,2 1810:0298 INT 21 1810:029A POP DX 1810:029B POP CX 1810:029C POP AX 1810:029D RET 1810:029E -a 400 1810:0400 PUSH AX 1810:0401 PUSH BX 1810:0402 PUSH CX 1810:0403 PUSH DX 1810:0404 PUSH SI 1810:0405 PUSH DI 1810:0406 ; In AL ho la lettera corrente 1810:0406 ; Punto ai simboli delle vocali 1810:0406 MOV SI, 0130 1810:0409 ; e ai contatori delle vocali 1810:0409 MOV DI, 0140 1810:040C ; Inizio LOOP - carico la vocale 1810:040C MOV BL,[SI] 1810:040E CMP AL, BL 1810:0410 JZ 425 1810:0412 ADD BL, 20 1810:0415 CMP AL, BL 1810:0417 JZ 425 1810:0419 ; Passo al contatore della 1810:0419 ; vocale successiva 1810:0419 ADD DI,2 1810:041C INC SI 1810:041D CMP SI,135 1810:0421 JE 42A 1810:0423 JMP 40C 1810:0425 ; Incremento e chiudo la CALL 1810:0425 MOV DX,[DI] 1810:0427 INC DX 1810:0428 MOV [DI],DX 1810:042A ; Ripristino i registri 1810:042A POP DI 1810:042B POP SI 1810:042C POP DX 1810:042D POP CX 1810:042E POP BX 1810:042F POP AX 1810:0430 RET 1810:0431 -a 500 1810:0500 PUSH AX 1810:0501 PUSH BX 1810:0502 PUSH DX 1810:0503 PUSH SI 1810:0504 MOV SI,130 1810:0507 MOV BX,140 1810:050A ; Stampo 'Lettera: ' 1810:050A MOV AH,02 1810:050C MOV DL,[SI] 1810:050E INT 21 1810:0510 ; stampo : 1810:0510 MOV DL,3A 1810:0512 INT 21 1810:0514 ; stampo spazio 1810:0514 MOV DL,20 1810:0516 INT 21 1810:0518 ; stampo il numero 1810:0518 MOV AX, [BX] 1810:051A CALL 270 1810:051D ADD BX,2 1810:0520 INC SI 1810:0521 CMP SI,135 1810:0525 JNE 50A 1810:0527 POP SI 1810:0528 POP DX 1810:0529 POP BX 1810:052A POP AX 1810:052B RET 1810:052C -r cx CX 0000 :4FF -n filea.com -w Writing 004FF bytes -q C:\asm> |
MOV CX,0080 MOV DX,0150 MOV DI,0300 ==> Preparo la chiamata alla call 200 CX contiene il nr di byte da leggere - DX: l'indirizzo del nome del file - DI: l'indirizzo del buffer dove scaricare i byte letti CALL 200 ==> Vedi spiegazione procedura 0200 nell'esercizio 7.a MOV CX,[0300] MOV SI,302 ==> Carico il nr di byte letti in CX e mi posiziono all'inizio del buffer (0302) MOV AL,SI CALL 400 ==> Carico la lettera corrente dal buffer in AL e richiamo la procedura 400 che aggiorna il conteggio delle vocali gestito nell'area 0140. INC SI DEC CX JNZ 113 CALL 500 ==> Passo alla successiva lettera nel buffer incrementando il puntatore SI - decremento il conteggio CX in modo che in esso abbia il nr di byte ancora da analizzare. Se CX non è zero ripeto le operazioni sul successivo carattere altrimenti passo a stampare il conteggio (CALL 500) INT 20 ==> Fine programma. DB 'c:\sechi.txt',0 ==> Definisco all'indirizzo CS:0150 il nome del file da leggere. Il nome del file deve terminare con il carattere '\0'. DB 'A', 'E','I','U','O' ==> Definisco in CS:0130 i simboli (vocali maiuscole) da conteggiare DW 0,0,0,0,0 ==> Definisco i contatori all'indirizzo CS:0140 PROCEDURA 0200: E' la stessa dell'esercizio 7.a - Legge CX byte dal file il cui nome è puntato da DX e li scarica in DI. PROCEDURA 0270: Stampa un numero composto da 2 byte, scritto in base 10 memorizzato nel registro AX. PUSH AX ... ==> Salvo il valore dei registri utilizzati nello stack MOV DL, A ==> Carico il divisore Ah = 10 XOR CX,CX ==> Azzero il contatore che registra il numero di digit della rappresentazione decimale DIV DL ==> Divido AX (numero da stampare) per DL. In AH ho il resto della divisione mentre in AL il risultato della divisione intera tra AX e DL. ADD AH,30 ==> In AH metto il codice ASCII corrispondente al resto della divisione per 10 (30h = 48 in decimale) PUSH AX INC CX ==> Metto nello stack AX e incremento il contatore dei digit della rappresentazione decimale. XOR AH,AH ==> Elimino il vecchio resto CMP AL,0 JNZ 277 ==> Se in AL non ho 0 ripeto la divisione altrimenti passo a stampare i digit memorizzati nello stack. POP AX ==> Estraggo un digit della rappresentazione decimale MOV DL,AH MOV AH,2 INT 21 ==>Stampo il digit corrente usando il servizio DOS AH=02 DEC CX JNZ 0284 ==> decremento il conteggio dei digit da stampare e se è 0 stampo invio e termino la CALL MOV DL,0a MOV AH,2 INT 21 MOV DL,0d MOV AH,2 INT 21 ==>Stampo invio POP DI ... ==> Ripristino i valori originali nei registri.
PROCEDURA 0400: |
a 100 ; Carico nel buffer nr byte del ; file letto MOV CX,0080 MOV DX,0150 MOV DI,0300 CALL 200 ; LOOP NELL'AREA DI MEMORIA MOV CX, [0300] MOV SI, 0302 ; Carico il carattere ; corrente da analizzare MOV AL,[SI] ; Aggiorno la stastistica vocali CALL 400 INC SI DEC CX JNZ 113 ; stampa risultati CALL 500 INT 20 a 130 DB 'A','E','I','U','O' a 140 DW 0,0,0,0,0 a 150 ; Definisco il nome del ; file da leggere. Lo 0 ; equivale a \0 finale del C db 'c:\sechi.txt',0 a 200 ; Procedura Lettura files - Parametri: ; DX: indirizzo nome file ; CX: byte da leggere ; DI: indirizzo buffer di output PUSH AX PUSH BX PUSH CX PUSH DX PUSH DI ; Apro il file il cui path ; è puntato DX - salvo l'handle XOR AL,AL MOV AH,3D INT 21 PUSH AX ; Leggo un nr di byte indicato ; in CX e li scarico nel buffer ; indicato in DX+2 ; del file MOV DX, DI ADD DX,2 MOV BX, AX MOV AH, 3F INT 21 ; Salvo il numero di byte ; letti all'indirizzo DI ; e ripristino l'handle in AX MOV [DI],AX POP AX ; Chiudo il file - in BX ho ; l'handle MOV BX,AX MOV AH,3E INT 21 ; Ripristino i registri POP DI POP DX POP CX POP BX POP AX RET a 270 ; Procedura stampa numero ; in decimale ; AX: numero in hex PUSH AX PUSH CX PUSH DX ; metto 10 nel divisore ; azzero il contatore MOV DL,A XOR CX,CX ; Divisione per 10 e ; trasformo il resto in ; ascii DIV DL ADD AH,30 PUSH AX INC CX ; Elimino il resto XOR AH,AH CMP AL,0 JNZ 277 ; Stampo i numeri messi ; nello stack POP AX MOV DL,AH MOV AH,2 INT 21 DEC CX JNZ 0284 ; Stampo invio MOV DL,0a MOV AH,2 INT 21 MOV DL,0d MOV AH,2 INT 21 POP DX POP CX POP AX RET a 400 PUSH AX PUSH BX PUSH CX PUSH DX PUSH SI PUSH DI ; In AL ho la lettera corrente ; Punto ai simboli delle vocali MOV SI, 0130 ; e ai contatori delle vocali MOV DI, 0140 ; Inizio LOOP - carico la vocale MOV BL,[SI] CMP AL, BL JZ 425 ADD BL, 20 CMP AL, BL JZ 425 ; Passo al contatore della ; vocale successiva ADD DI,2 INC SI CMP SI,135 JE 42A JMP 40C ; Incremento e chiudo la CALL MOV DX,[DI] INC DX MOV [DI],DX ; Ripristino i registri POP DI POP SI POP DX POP CX POP BX POP AX RET a 500 PUSH AX PUSH BX PUSH DX PUSH SI MOV SI,130 MOV BX,140 ; Stampo 'Lettera: ' MOV AH,02 MOV DL,[SI] INT 21 ; stampo : MOV DL,3A INT 21 ; stampo spazio MOV DL,20 INT 21 ; stampo il numero MOV AX, [BX] CALL 270 ADD BX,2 INC SI CMP SI,135 JNE 50A POP SI POP DX POP BX POP AX RET r cx 4FF n filea.com w q (mettere un invio dopo q) |
Esercizio 7.c (lettura file, caricamento in un buffer e stampa a video)
Costruire un programma "leggi" che legge
l'intero file c:\sechi.txt e lo stampa a video.
SOLUZIONE:
SOLUZIONE | Osservazioni (7.c): | CREA CON: DEBUG<FILE.ASM |
C:\asm>debug -a 100 1810:0100 ; Apertura File 1810:0100 ; - in AX ho l'handler 1810:0100 MOV DX,0150 1810:0103 CALL 200 1810:0106 PUSH AX 1810:0107 ; Lettura del file 1810:0107 ; - in DI indirizzo del buffer 1810:0107 ; - in [DI] ho il nr di byte 1810:0107 MOV DI,0300 1810:010A CALL 250 1810:010D MOV CX,[DI] 1810:010F CMP CX,0 1810:0112 JZ 11C 1810:0114 ; Stampa buffer 1810:0114 ; - in SI indirizzo del buffer 1810:0114 MOV SI,0300 1810:0117 CALL 270 1810:011A JMP 10A 1810:011C ; Chiusura File 1810:011C ; - in AX ho l'handler 1810:011C POP AX 1810:011D CALL 220 1810:0120 ; Fine programma 1810:0120 INT 20 1810:0122 -a 150 1810:0150 ; Definisco il nome del 1810:0150 ; file da leggere. Lo 0 1810:0150 ; equivale a \0 finale del C 1810:0150 db 'c:\sechi.txt',0 1810:015D -a 200 1810:0200 ; Procedura Apertura file - Parametri: 1810:0200 ; IN --> DX: indirizzo nome file 1810:0200 ; OUT --> AX: handler del file 1810:0200 XOR AL,AL 1810:0202 MOV AH,3D 1810:0204 INT 21 1810:0206 RET 1810:0207 -a 220 1810:0220 ; Procedura Chiusura file - Parametri: 1810:0220 ; IN --> AX: handler del file 1810:0220 PUSH AX 1810:0221 PUSH BX 1810:0222 MOV BX,AX 1810:0224 MOV AH,3E 1810:0226 INT 21 1810:0228 POP BX 1810:0229 POP AX 1810:022A RET 1810:022B 1810:0250 ; Procedura Lettura file - Parametri: 1810:0250 ; IN --> AX: handler del file 1810:0250 ; IN --> DI: Indirizzo buffer 1810:0250 ; OUT --> [DI]: nr byte letti 1810:0250 PUSH AX 1810:0251 PUSH CX 1810:0252 PUSH BX 1810:0253 PUSH DX 1810:0254 MOV CX,0080 1810:0257 MOV DX,DI 1810:0259 ADD DX,2 1810:025C MOV BX, AX 1810:025E MOV AH, 3F 1810:0260 INT 21 1810:0262 MOV [DI],AX 1810:0264 POP DX 1810:0265 POP CX 1810:0266 POP BX 1810:0267 POP AX 1810:0268 RET 1810:0269 1810:0270 ; Analizzo la sequenza e la stampo 1810:0270 ; Salvo i registri 1810:0270 PUSH AX 1810:0271 PUSH CX 1810:0272 PUSH DX 1810:0273 PUSH SI 1810:0274 MOV CX,[SI] 1810:0276 ADD SI,2 1810:0279 ; Stampo il carattere corrente 1810:0279 MOV AH,02 1810:027B MOV DL,[SI] 1810:027D INT 21 1810:027F ; Passo al successivo 1810:027F INC SI 1810:0280 DEC CX 1810:0281 JNZ 279 1810:0283 POP SI 1810:0284 POP DX 1810:0285 POP CX 1810:0286 POP AX 1810:0287 RET 1810:0288 -r cx CX 0000 :2FF -n file0.com -w Writing 002FF bytes -q C:\asm> |
MOV CX,0150 CALL 200 PUSH AX ==> Apro il file ( il cui nome è puntato da DX) e registro nello stack l'handler del file contenuto in AX. MOV DI,0300 CALL 250 MOV CX,[DI] CMP CX,0 JZ 11C ==> Imposto l'indirizzo del buffer e lo salvo in DI. Leggo N byte (128 per la precisione) e scrivo in CX il nr di byte effettivamente letti che è stato memorizzato all'indirizzo puntato da DI. Se non ho letto alcun byte significa che è terminato il file. MOV SI,0300 CALL 270 JMP 10A ==> Stampo il contenuto del buffer e passo a leggere la parte restante del file. POP AX CALL 220 ==> Ripristino l'hander del file e lo chiudo. INT 20 ==> Fine programma. DB 'c:\sechi.txt',0 ==> Definisco all'indirizzo CS:0150 il nome del file da leggere. Il nome del file deve terminare con il carattere '\0'. Le procedure sono estratte dal 1° esempio (7.a) per cui si rimanda a tale esercizio le spiegazioni dettagliate.
PROCEDURA 0200: |
a 100 ; Apertura File ; - in AX ho l'handler MOV DX,0150 CALL 200 PUSH AX ; Lettura del file ; - in DI indirizzo del buffer ; - in [DI] ho il nr di byte MOV DI,0300 CALL 250 MOV CX,[DI] CMP CX,0 JZ 11C ; Stampa buffer ; - in SI indirizzo del buffer MOV SI,0300 CALL 270 JMP 10A ; Chiusura File ; - in AX ho l'handler POP AX CALL 220 ; Fine programma INT 20 a 150 ; Definisco il nome del ; file da leggere. Lo 0 ; equivale a \0 finale del C db 'c:\sechi.txt',0 a 200 ; Procedura Apertura file - Parametri: ; IN --> DX: indirizzo nome file ; OUT --> AX: handler del file XOR AL,AL MOV AH,3D INT 21 RET a 220 ; Procedura Chiusura file - Parametri: ; IN --> AX: handler del file PUSH AX PUSH BX MOV BX,AX MOV AH,3E INT 21 POP BX POP AX RET a 250 ; Procedura Lettura file - Parametri: ; IN --> AX: handler del file ; IN --> DI: Indirizzo buffer ; OUT --> [DI]: nr byte letti PUSH AX PUSH CX PUSH BX PUSH DX MOV CX,0080 MOV DX,DI ADD DX,2 MOV BX, AX MOV AH, 3F INT 21 MOV [DI],AX POP DX POP CX POP BX POP AX RET a 270 ; Analizzo la sequenza e la stampo ; Salvo i registri PUSH AX PUSH CX PUSH DX PUSH SI MOV CX,[SI] ADD SI,2 ; Stampo il carattere corrente MOV AH,02 MOV DL,[SI] INT 21 ; Passo al successivo INC SI DEC CX JNZ 279 POP SI POP DX POP CX POP AX RET r cx 2FF n fileb.com w q (mettere un invio dopo q) |