ESEMPI

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>soluz1
0
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
r bx
0
r cx
C
w
q

(mettere un invio dopo q)

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>soluz1v
0
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
INT 10
==> Utilizzo il servizio 10 del BIOS (ed esattamente la funzione 0Eh - modalità TTY) che stampa a video un singolo carattere il cui codice ascii è posto nel registro AL.

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
r bx
0
r cx
A
w
q

(mettere un invio dopo q)

 

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>soluz1v
0
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
DIV DL ==> Divido per 10. Essendo il divisore a 16 bit ne consegue che nel registro AH venga riposto il resto della divisione mentre in AL il quoziente.
ADD AH,30 ==> Aggiungo 48 (30 in hex) per ottenere in AH il codice ascii del numero da stampare.
PUSH AX
XOR AH,AH
==> Accantono nello stack il resto ottenuto (non è ammesso il push solo di AL) e ripulisco il registro AH
INCR CX ==> Incremento CX che conteggia il numero di digit memorizzati nello stack e che rappresentano i simboli della rappresentazione in base 10 del numero, all'indirizzo DS:0080, fino a quel momento estratti
CMP AL,0 ==> Se l'ultimo DIV ha determinato in AL il valore 0 significa che ho terminato l'estrazione dei digit poiché l'ultimo quoziente (registrato in AL) è zero.
JNZ 0116 ==> Se nell'ultimo CMP il registro AL è diverso da 0 allora l'operazione di estrazione dei digit continua

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.
POP AX
MOV DL,AH

MOV AH,2
INT 21
==> Estraggo il digit dallo stack e lo registro in AX, copio in DL il codice ascii del simbolo da mostrare a video ed imposto la funzione dell'interrupt 21 che stampa il singolo carattere ascii (AH=2h)
DEC CX
CMP CX,0
JNZ CX,0
==> Decremento CX e ripeto l'operazione di estrazione finchè non diventa 0 (CX contiene il numero di digit della rappresentazione in base 10 del numero collocato all'indirizzo DS:0080). Quando CX diventa 0 il programma termina.
 

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 marco
ocram 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 3
Ciao|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:
MOV BX, AX ==> Poichè AX è utilizzato per richiamare la funzione di "stampa stringa" del servizio 21 del DOS salvo in BX il valore di AX.
CMP BX,0
JZ 135
==> BX è usato per contare i "ciao" stampati per cui quando diventa 0 il programma termina.
MOV AH,9
MOV DX,0136
INT 21
==> Stampo la stringa "ciao" (ubicata all'indirizzo DS:0136) utilizzando il servizio 21 del DOS. Questo servizio richiede in AH il valore 9 mentre in DX il puntatore alla stringa da stampare. La stringa deve terminare con $.
DEC BX ==> Decremento BX tenendo il conteggio dei "ciao" stampati



 

 

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:
PUSH AX ... ==> Salvo il valore dei registri utilizzati nello stack
MOV AL,[0080]
DEC AL ==> Registro in AL il numero di caratteri digitati nella stringa dei parametri
MOV SI,0082
MOV DI,0200
==> Punto i registri indice sulla zona dove è ubicata la stringa dei parametri (SI) e su quella dove copierò tale stringa (DI).
MOV AH, [SI]
MOV [DI], AH
==> Scorro carattere per carattere la stringa dei parametri e li copio nella zona di memoria puntata da DI.
CMP AL,0
JLE 026C
==> Termino quando AL diventa zero (in questo caso ho copiato tutti i caratteri della stringa dei parametri in DS:0200).
INC SI
INC DI
==> Ad ogni iterazione sposto il registro indice sul byte successivo della stringa dei parametri.
DEC AL ==> Decremento il registro che conta il numero di caratteri che restano da copiare (AL)
MOV AH,24
MOV [DI],AH
==> Aggiungo il "$" in fondo alla stringa copiata in DS:0200.
POP DI ... ==> Ripristino i valori originali nei registri utilizzati e con RET restituisco l'esecuzione sulla riga successiva quella della CALL 250.

PROCEDURA 0350:
PUSH AX ... ==> Salvo il valore dei registri utilizzati nello stack:
MOV AL,[0080]
MOV SI,0200
MOV DI,0300
==> Punto i registri indice sulla zona dove è ubicata la copia della stringa dei parametri (SI) e su quella dove copierò la stringa composta dai soli numeri (DI).
MOV AH,[SI] ==> Leggo un carattere della stringa in DS:0200 e lo pongo in AH.
CMP AH,24
JE 0375 ==> Se AH è "$" passo alla parte finale della routine dove aggiungo "$" alla fine (in DS:0300)
CMP AH,2D
JE 036F ==> Se AH è "-" passo alla parte dove copio il carattere nell'area di destinazione puntata da DI.
CMP AH,39
JE 0372

CMP AH,30
JE 0372
==> Se il codice Ascii in AH è inferiore a quello del simbolo "0" oppure è superiore a quello di "9" allora ignoro il carattere e passo alla lettera successiva (INC SI). In caso contrario copio il simbolo numerico nell'area di destinazione puntata da DI (MOV [DI], AH).
POP DI ... ==> Ripristino i valori originali nei registri utilizzati e con RET restituisco l'esecuzione sulla riga successiva quella della CALL 350.
 

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:
La procedura all'indirizzo CS:0300  si preoccupa di posizionare il cursore sulla riga indicata in DL e sulla colonna indicata in DH. Le coordinate sono passate in hex alla procedura mettendole in DX prima della chiamata della routine (esempio:
MOV DX,0520)

PUSH AX ... ==> Salvo il valore dei registri utilizzati nello stack
MOV BH,0 ==> Seleziono la pagina video 0
MOV AH,2 ==> Seleziono il servizio di riposizionamento del cursore
INT 10 ==> Richiamo l'interrupt 10 - Le coordinate relative alla riga (DH) e alla colonna (DL) vengono inserite prima della chiamata della procedura CS:0300 nel registro DX.
POP DI ... ==> Ripristino i valori originali nei registri utilizzati e con RET restituisco l'esecuzione sulla riga successiva a quella della CALL 300.

PROCEDURA 0250:
La seconda procedura (CS:0250) aggiunge alla stringa "Colonna: ___" o "Riga: ___" la coordinata caricata nel registro DL. La trasformazione del numero da hex a decimale avviene mediante una serie di divisioni per 10. Il resto ottenuto viene copiato nella cella puntata da SI . Predispongo la scrittura della decina decrementando SI.

PUSH AX ... ==> Salvo il valore dei registri utilizzati nello stack:
MOV AL,DL
XOR AH,AH
MOV DL,A
  ==> In DL ho la coordinata. Lo copio in AL per preparare la sequenza di divisioni per 10 (A in hex) che mi consente di estrarre la rappresentazione decimale della coordinata da aggiungere in fondo alla stringa "Colonna: ___" o "Riga: ___" a secondo.
DIV DL
ADD AH, 30
MOV [SI],AH
XOR AH,AH
  ==> Divido per 10. In AH ho il resto della divisione. Ad esso aggiungo 48 (30 in hex) per ottenere il codice ascii della cifra che metto nella cella puntata da SI.
DEC SI  ==> Decremento SI per passare alla cella precedente dove scriverò la cifra della coordinata corrispondente alle decine
CMP AL,0
JNZ 0259
  ==> Se AL è 0 ho terminato la decodifica da hex a decimale del numero in AL.
POP DI ... ==> Ripristino i valori originali nei registri utilizzati e con RET restituisco l'esecuzione alla riga successiva a quella della CALL 250.
 

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:
La procedura all'indirizzo CS:0270  si preoccupa di stampare SI byte nel buffer situato all'indirizzo SI+2

PUSH AX ... ==> Salvo il valore dei registri utilizzati nello stack
MOV CX, [SI]
ADD SI,2
==> Carico in CX il nr di byte da stampare e posiziono SI all'inizio del buffer.
MOV AH, 02
MOV DL,[SI]
INT 21
==> Stampo (usando la funzione DOS) il carattere puntato da SI.
INC SI
DEC CX
JNZ 279
==> Incremento SI in modo da passare al carattere successivo, decremento CX e se non è 0 ritorno alle istruzioni di stampa carattere.
POP SI ... ==> Ripristino i valori originali nei registri.

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:
Aggiorna i contatori delle vocali. La lettera analizzata è posta in AL mentre la vocale da confrontare è in BL.

PUSH AX ... ==> Salvo il valore dei registri utilizzati nello stack
MOV SI, 0130
MOV DI, 0140
==> Faccio puntare DI all'elenco dei contatori e SI all'elenco delle vocali.
MOV BL,[SI] ==> Carico la vocale da confrontare
CMP AL,BL
JZ 425
ADD BL,20
CMP AL,BL
JZ 425
==> Comparo AL (carattere corrente preso dal buffer) con la vocale corrente in BL. Se non è uguale aggiungo 32 (20hex) a BL per trasformare la vocale (maiuscola!) nella sua corrispondente minuscola e ripeto il confronto tra BL e AL. Se nei due confronti ho l'uguaglianza salto a 0425 dove effettuo l'incremento del contatore corrispondente
ADD DI,2
INC SI
CMP SI,135
JE 42A
JMP 40C
==> Se non si verifica l'uguaglianza tra AL e BL passo ad analizzare la successiva vocale (JMP CS:040C) ma prima incremento il puntatore SI e mi sposto anche sul contatore successivo aggiungendo 2 a DI poichè il contatori occupano 2 byte. Se ho confrontato tutte le vocali salto a CS:042A che corrisponde alla fine della procedura di conteggio
MOV DX,[DI]
INC DX
MOV [DI],DX
==> Sequenza che incrementa il contatore corrente puntato da DI.
POP DI ... ==> Ripristino i valori originali nei registri.

PROCEDURA 0500:
Effettua la stampa del report. Mostra la vocale seguita da : e dal numero di occorrenze

PUSH AX ... ==> Salvo il valore dei registri utilizzati nello stack
MOV SI, 0130
MOV DI, 0140
==> Faccio puntare DI all'elenco dei contatori e SI all'elenco delle vocali.
MOV AH,02
MOV DL,[SI]
INT 21
==> Uso il servizio dos per stampare un singolo carattere (AH=02h). In DL carico la vocale da stampare puntata da DI.
MOV DL,3A
INT 21
MOV DL,20
INT 21
==> Riuso il servizio dos per stampare un singolo carattere (non ricarico AH che vale già 02h). In DL carico prima ":" e poi " ".
MOV AX,[BX]
CALL 270
==> Carico in AX il numero di 2 byte puntato da BX.
ADD BX,2
INC SI
CMP SI,135
JNE 50A
==> Passo al successivo numero puntato da BX e aggiorno la vocale incrementando il suo puntatore SI. Se SI è CS:0135 significa che tutte le vocali sono state stampate e quindi esco dalla procedura.
POP DI ... ==> Ripristino i valori originali nei registri.

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:
La procedura all'indirizzo CS:0200  si preoccupa aprire il file il cui nome è puntato da DX. Restituisce in AX l'handler al file

PROCEDURA 0220:
La procedura all'indirizzo CS:0220  si preoccupa di chiudere il file il cui handler è in AX.

PROCEDURA 0250:
La procedura all'indirizzo CS:0250  si preoccupa di leggere 128 byte dal file il cui handler è in AX e li scarica nel buffer puntato da DI. I primi 2 byte del buffer contengono il nr di caratteri letti.

PROCEDURA 0270:
La procedura all'indirizzo CS:0270  si preoccupa di stampare SI byte nel buffer situato all'indirizzo SI+2
 

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)