Che materia stai cercando?

Anteprima

ESTRATTO DOCUMENTO

In caso di errore questo viene riportato dal registro AH solo che

i bit indicanti il tipo sono solo il 7,4,3,2,1.

Il settimo bit indica un errore di timeout che se si verifica

rende inutile la lettura dei rimanenti bit di stato.

AH = 3 Riporta lo stato della linea e del modem.

Il valore riportato e' contenuto nel registro AX.

La parte alta del registro (AH) contiene lo stato della linea e i

bit testabili sono i seguenti :

bit 7 = Timeout

bit 6 = Registro di scorrimento trasmettitore vuoto

bit 5 = Registro memorizzazione trasmettitore vuoto

bit 4 = Rivelazione di break

bit 3 = Errore di framing

bit 2 = Errore di parita'

bit 1 = Errore di sovrapposizione

bit 0 = Dato ricevuto pronto

Il registro AL conterra' invece lo stato del modem e

l'interpretazione dei bit e' la seguente :

bit 7 = Data carrier detect

bit 6 = Ring indicator

bit 5 = Data set ready

bit 4 = Clear to send

bit 3 = Delta data carrier detect

bit 2 = Delta ring indicator

bit 1 = Delta data set ready

bit 0 = Delta clear to send

Le routine dell'interrupt si supportano sulle seguenti porte di

I/O per l'esecuzione dei servizi richiesti.

E' possibile utilizzarle per la stesura dei programmi di

comunicazione scritti in linguaggio C senza dover far uso dei

servizi elencati precedentemente.

L'integrato 8250 della scheda RS 232 possiede 10 registri.

Alcuni di questi sono testabili in lettura mentre ad altri

potremo scriverci sopra per settare alcuni parametri quali il

baud rate, la parita' ecc.

Iniziamo ad elencare le varie porte legate alle comunicazioni

seriali.

Indirizzo porta Input o Output Servizio

-----------------------------------------------------------------

0x3F8 Out Registro memorizzazione TX

0x3F8 In Registro dati RX

0x3F8 Out Divisore baud rate (LSB)

0x3F9 Out Divisore baud rate (MSB)

0x3F9 Out Abilitazione interruzioni

0x3FA In Identificazione interruz.

0x3FB Out Controllo linea

0x3FC Out Controllo modem

0x3FD In Stato linea

0x3FE In Stato modem

Questi indirizzi sono relativi alla porta di comunicazione COM1.

Gli indirizzi relativi alla porta COM2 si possono ottenere

sotraendo 256 agli indirizzi visti precedentemente.

Un esempio :

main()

{ int indirizzo;

int porta;

do { printf("\nQuale porta (1 o 2) : ");

porta = getch();

} while(porta != 1 && porta != 2);

indirizzo = 0x2F8 + 256 * (2 - porta); /* 0x3F8 se 1 */

}

Come avrete notato nell'elenco precedente alcune porte descritte

con funzioni diverse tra di loro possedevano lo stesso indirizzo.

Lo scopo di queste e' selezionabile mediante l'apposito

settaggio di un bit all'interno del registro di controllo della

linea.

Vediamolo.

Registro di controllo della linea.

+---+---+---+---+---+---+---+---+

0x3FB : 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+

: : : : : : : :

: : : : : : ---Bit data---

: : : : : : 0 0 = 5 bits

: : : : : : 0 1 = 6 bits

: : : : : : 1 0 = 7 bits

: : : : : : 1 1 = 8 bits

: : : : :-Bit stop-

: : : : : 0 = 1

: : : : : 1 = 1.5 se 5 bits data

: : : :-Parity-

: : : : 0 = Nessun bit

: : : : 1 = Bit generato

: : : -Tipo-

: : : 0 = dispari

: : : 1 = pari

: :-Test.-

: : 0 = disabilitato

: : 1 = abilitato

:-Break-

: 0 = disabilitato

: 1 = forzato condizione di space

---I/O---

0 = Condizione normale

1 = Per indirizzare divisori baud rate

Come potete vedere l'ultimo bit e' quello che fa si' che se

impostato ad 1 i registri 0x3F8 e 0x3F9 vengano utilizzati per

settare i divisori del baudrate.

Da questo e' facile comprendere che e' possibile impostare

momentaneamente il valore di questo per poter settare la porta al

baudrate desiderato per poi riportarlo al valore normale (0) per

utilizzare la porta per funzioni di ricezione.

Appena avremo finito di parlare delle altre porte vedremo un

esempio che probabilmente chiarira' le idee nel caso che cio' che

e' stato detto precedentemente non sia risultato sufficentemente

chiaro.

I registri 0x3F8 e 0x3F9, nel caso che bit di cui abbiamo parlato

e' a 1, servono per settare i divisori del baud rate.

Il primo conterra' il byte meno significativo (LSB) mentre la

seconda quello piu' significativo (MSB).

I valori sono quelli riportati in tabella.

Baud rate MSB LSB

-----------------------------

50 09H 00H

75 06H 00H

110 04H 17H

150 03H 00H

300 01H 80H

600 00H C0H

1200 00H 60H

2400 00H 30H

4800 00H 18H

9600 00H 0CH

Supponiamo di voler creare una funzione da abbinare a un nostro

programma di comunicazione che serva a settare il baud rate in

funzione di una scelta offerta da menu.

set_baud()

{ int key;

puts("1 .. 300 bauds");

puts("2 .. 1200 bauds");

puts("3 .. 2400 bauds");

puts("\nScelta : ");

key = getch();

outp(0x3FB,0x80); /* A 1 l'ultimo bit a sinistra */

switch(key) {

case 1:

outp(0x3F8,0x80);

outp(0x3F9,0x01);

break;

case 2:

outp(0x3F8,0x60);

outp(0x3F9,0x00);

break;

case 3:

outp(0x3F8,0x30);

outp(0x3F9,0x00);

break;

default:

puts("\nScelta errata");

break;

}

}

Il servizio 3 dell'interrupt 14H visto prima riportava lo stato

della linea e del modem.

La stessa funzione puo' essere eseguita testando direttamente i

due registri del 8250 che svolgono questo compito.

0x3FD Stato linea

+---+---+---+---+---+---+---+---+

: 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+

: : : : : : : :

: : : : : : : Dato ricevuto

: : : : : : Sovrapposizione

: : : : : Errore parita

: : : : Errore di framing

: : : Break rivelato

: : Registro memorizzazione TX vuoto

: Registro scorrimento TX vuoto

Timeout

Uno bit a 1 indica che un determinato stato esiste.

0x3FE Stato del modem

+---+---+---+---+---+---+---+---+

: 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+

: : : : : : : :

: : : : : : : Delta clear to send

: : : : : : Delta data set ready

: : : : : Delta ring indicator

: : : : Delta carrier detect

: : : Clear to send

: : Data set ready

: Ring indicator

Carrier detect

Oltre ai due registri precedenti, relativi allo stato della linea

e a quello del modem, esistono due registri per il controllo di

questi.

Il primo lo abbiamo gia' visto e precisamente quello relativo

al controllo della linea.

Vediamo ora il registro di controllo del modem.

0x3FC Registro controllo modem

+---+---+---+---+---+---+---+---+

: 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+

0 0 0 : : : : :

: : : : DTR attivo

: : : RTS attivo

: : OUT1 Non usato

: OUT2 Deve essere 1 per

: mandare interruzioni

0 = Normale

1 = Cortocircuito

Gli ultimi 3 bit sono forzati a 0 mentre il numero 4 deve essere

a 0 per trasmissioni normali sulla porta.

Nel caso che per motivi di test si voglia cortocircuitare la

porta bastera' metterlo ad 1 .

In questo modo ogni carattere trasmesso verra' restituito di

ritorno come se fosse stato ricevuto.

Vediamo ora un esempio di utilizzo di questo registro mediante

una funzione che setta il DTR a on o ad off a seconda che

l'argomento passato sia 1 o 0.

set_dtr(stato)

int stato;

{ char byte_on;

char byte_of;

byte_on=0x03;

byte_of=0x00;

if(stato)

outp(0x3FC,byte_on);

else outp(0x3FC,byte_of);

}

Esistono ancora i registri di abilitazione e di identificazione

interruzione che vedremo dopo il seguente esempio.

Si tratta di un programmino che mostra l'utilizzo di quanto detto

fino ad ora costituito da un modulo base che potrete voi stessi

ampliare con funzioni per vari scopi quale, ad esempio, per

catturare o trasmettere un file ASCII.

#include <stdio.h>

#define LSR 0x3FD /* Indirizzi registri 8250 */

#define DLL 0x3F8

#define MCR 0x3FC

#define LCR 0x3FB

#define DLM 0x3F9

#define MSR 0x3FE

#define CLS puts("\33[2J")

#define BEL putchar('\007');

char *msr=(char*)MSR;

char *dlm=(char*)DLM;

char *lcr=(char*)LCR;

char *lsr=(char*)LSR;

char *dll=(char*)DLL;

char *mcr=(char*)MCR;

int ind,baud,parity;

main(argc,argv)

int argc;

char *argv[];

{ baud =2400;

CLS;

puts("\33[3;7p"); /* Assegna al carettere 3 il 7 */

set_dtr();

puts("****** MTRM ******");

puts(" Opus Free Soft");

puts("******************");

if(argc>1) {

switch(*argv[1]) {

case '1':

puts("\n2400 bauds");

baud = 2400;

break;

case '2':

puts("\n1200 bauds");

baud = 1200;

break;

case '3':

puts("\n300 bauds");

baud = 300;

break;

default:

puts("\n\nERRORE: Argomenti errati");

puts("\nUso : mtrm [bauds] ");

puts(" Es: mtrm 1 ");

puts(" bauds: 1 = 2400");

puts(" 2 = 1200");

puts(" 3 = 300");

puts("\nDefault = 2400 bauds");

exit(0);

break;

}

}

baud_set(baud);

parity_set();

colloquia();

}

colloquia() /* Ciclo ricevi/trasmetti */

{ int key;

for(;;) {

while(!kbhit()) { /* Finche nessun tasto.. */

ind = rx_char(); /* guarda se e' ricevuto */

if(ind != NULL) /* un carattere */

putchar(ind); /* Se si lo stampa */

}

key = getch(); /* Se e' stato premuto */

if(key == '\033') /* guarda se e' ESC */

exit(0); /* Se si esce a DOS */

tx_char(key); /* se no lo trasmette */

}

}

parity_set() /* Setta la par., dati,stop */

{ outp(lcr,0x3A); /* N,8,1 */

}

rx_char() /* Riceve un carattere */

{ int util,key;

if(((util=inp(lsr)) & 0x1E) != 0) { /* Test per errore */

key = inp(dll); /* C'e' un errore */

return(NULL); /* ritorna NULL */

}

else

if((util & 0x01) != 0) {

key = inp(dll); /* Prende il carattere */

return(key); /* e lo ritorna */

}

else

return(NULL); /* Non e' stato ricevuto */

}

tx_char(var) /* Trasmette un carattere */

char var;

{ register util;

while(((util=inp(lsr)) & 0x20) == 0); /* Test reg. tx */

outp(dll,var); /* Trasmette il carat. */

}

baud_set(speed) /* Setta il baudrate */

{ outp(lcr,0x80); /* Setta bit 7 porta 0x3FB */

switch(speed) {

case 300:

outp(dlm,0x01); /* MSB 300 bauds */

outp(dll,0x80); /* LSB 300 bauds */

break;

case 1200:

outp(dlm,0x00); /* MSB 1200 bauds */

outp(dll,0x60); /* LSB 1200 bauds */

break;

case 2400:

outp(dlm,0x00); /* MSB 2400 bauds */

outp(dll,0x30); /* LSB 2400 bauds */

break;

default:

break;

}

}

set_dtr() /* Mette ad on il DTR */

{ char byte_on = 0x03;

outp(mcr,byte_on);

}

Il commento iniziale "assegna il carattere 3 a 7" sara' risultato

oscuro.

Si tratta di un semplice metodo per assegnare al carattere ASCII

3 (CTRL C) il carattere 7 relativo al BEEP in modo da far si che

battendo CTRL C non si esca dal programma.

Per fare questo bisognera' invece battere ESC.

Il discorso relativo ai registri dedicati all'abilitazione e

all'identificazione dell'interruzione e' particolare in quanto

permette al programmatore di evitare di testare in continuazione

il registro di stato della linea per sapere se un determinato

evento si e' verificato o meno.

Per ora vedremo solo il funzionamento generale in quanto per la

stesura di un programma che sfrutti questa particolarita' bisogna

avere la conoscenza relativa al settaggio nella tavola degli

indirizzi dei servizi di interrupt del valore relativo alla

routine di gestione dell'interrupt stesso.

Nei capitoli precedenti abbiamo parlato del metodo seguito per

l'identificazione dell'indirizzo di una routine di servizio di un

interrupt.

Mediante l'opportuno settaggio di un registro del 8250 possiamo

attivare o disabilitare le interruzioni associate a particolari

eventi.

Vediamo quali sono.

0x3F9 Registro abilitazione interruzioni

+---+---+---+---+---+---+---+---+

: 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+

0 0 0 0 : : : :

: : : Dato pronto

: : Registro TX vuoto

: Errore carattere ricevuto

Cambiamento stato modem

Il registro che permette di identificare l'interruzione e' invece

il seguente. 0x3FA Identificazione interruzione

+---+---+---+---+---+---+---+---+

: 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+

0 0 0 0 0 : : :

: : 0 = Interrupt

: : 1 = No interrupt

^ ^

Identificazione interruzione+---+

0 0 Cambiamento stato mdm

0 1 Registro TX vuoto

1 0 Dato ricevuto

1 1 Errore carattere RX

Come dicevamo, parlando della gestione degli interrupt, viene

abbinata a questi una priorita' per far si che il sistema possa,

in caso che due o piu' vengano richiesti conteporaneamente,

accodare e quindi eseguire successiavamente quelle a priorita'

piu' bassa.

Lo stesso discorso vale per questi appena visti.

Errore carattere ricevuto ha la priorita' piu' alta, dato

ricevuto la seconda, trasmettitore pronto la terza e stato modem

la quarta.

L'esempio riportato precedentemente non e' dei piu' corretti

perlomeno per quanto riguarda il settaggio di velocita' superiori

a 1200 baud.

Il motivo e' che in ricezione potrebbero verificarsi degli errori

di sovrapposizione dei caratteri in quanto non esiste alcun

buffer predisposto a conservare i caratteri ricevuti nel mentre

che il programma e' intento ad assolvere altri compiti.

La soluzione del problema sta' appunto nella programmazione di

una routine d'interrupt che assolva a determinati compiti.

In altre parole quello eseguito dal programma esempio viene

definito polling.

Questo consiste in un ciclo in cui vengono svolte le seguenti

operazioni in modo ripetitivo.

inizio: E' stato premuto un tasto ?

Se si trasmetti il carattere

Altrimenti

Guarda se e' stato ricevuto un carattere.

Se si controlla che non ci sia stato un errore

Se errore ritorna NULL

Altrimenti ritorna il carattere

Se non e' stato ricevuto nulla ritorna a inizio

L'alternativa sta' appunto nel fare in modo che gli eventi

controllati dal ciclo vengano eseguiti automaticamente a seguito

di una richiesta di interruzione.

La routine dell'interrupt (0BH) di cui abbiamo visto

precedentemente i registri di controllo e' un interruzione

hardware e quindi prima dell'istruzione IRET deve finire con le

istruzioni MOV AL,20H e OUT 20H,AL.

Come abbiamo visto prima i casi che possono causare un'

interruzione sono quattro.

Questi possono essere abilitati indipendentemente oppure anche

piu'di uno contemporaneamente.

Nel secondo caso la routine di gestione dell'interrupt dovra'

provvedere all'identificazione del tipo mediante il test eseguito

sull' apposito registro, anche questo visto prima.

Anche in questo caso la routine d'interruzione andrebbe settata

con una call del Dos che ancora non abbiamo trattato ovvero il

servizio 25H dell' interrupt 21H.

Nella schematizzazione successiva vengono distinti il programma

principale dalla routine di servizio dell'interrupt.

Il programma sara' quello che si incarica di eseguire l'input dei

caratteri da trasmettere, di analizzarli , di visualizzare i dati

e di controllare il buffer di input per vedere se ci sono

all'interno caratteri inseriti dalla routine d'interrupt

incaricata della ricezione.

Quest'ultima sara' quella che si incarichera' effettivamente

della trasmissione dei caratteri inseriti nel buffer di output

dal programma principale, dell'immagazzinamento dei caratteri

ricevuti correttamente nel buffer di input, come dicevamo prima,

dell'intercettazione del cambiamento di stato del modem,

dell'individuazione di un errore di ricezione e della rilevazione

di uno stato di break.

Ad ogni blocco corrisponde un azione intrappresa dalla routine di

gestione dell'interrupt.

Evento Interrupt Azione intrappresa

--------------------------------------------------

TX pronto Trasmetti carattere

Carattere ricevuto Salva nel buffer

Stato modem cambiato Testa e intrapprendi azione

Break ricevuto Finisci comunicazione

Errore carattere Segnala

Programma +----------------------------+

: Input, salvataggio e :

: visualizzazione :

+----------------------------+

: :

: :

: +------+

Routine Interrupt : :

: :

INT 0BH ----->+--------+ +----------+ +------+ +------+

:Analizza: : TX ready :----->:buffer: :buffer:

+---->:tipo int:--->: :<-----: OUT : : IN :

: +--------+ +----------+---+ +------+ +------+

: : : : : ^ :

: : : : +----------+ : : :

: : : +----->: RX data :---:---------------+ :

: : : : :<--:------------------+

: : : +----------+---+-------+

: : : :

: : : +----------+ :

: : +------->: Stato : +-------------+no

: : : modem :--->: Altro INT ? :---->

: : +----------+ +-------------+ IRET

: : ^ :si

: : +----------+ : :

: +--------->: Break o :------+ :

: : errore : :

: +----------+ :

: :

+------------------------------------------+

Un esempio di routine di gestione interrupt e' la seguente.

Sulle motivazioni per cui utilizzo un file .COM non diro' nulla

per ora e rimando il tutto ai capitoli in cui verra' trattata la

call relativa al settaggio di una routine d' interrupt.

_text segment para 'code'

assume cs:_text

org 100H

jmp init ;salta alla routine di settaggio

;

new_int proc far ;procedura servizio interrupt

al_int: in al,03FAH ;porta identificazione INT COM1

and al,110b

cmp al,2 ;se 0 = registro TX vuoto

jz tx_char

cmp al,4 ;se 0 = dato ricevuto

jz rx_char

cmp al,6 ;se 0 = errore dato ricevuto

jz error

.... ...... ;se arriva qui e' cambiato lo

.... ...... ;stato del modem

jmp short altro ;guarda se c'e'altro INT

tx_char: .... ...... ;routine TX carattere

jmp short altro ;salta a vedere se c'e un altro

rx_char: ;interrupt pendente

.... ...... ;routine ricezione carattere

jmp short altro ;salta a controllo altro int

error: .... ...... ;routine errore ricevuto

altro: in al,3FAH ;la routine testa il primo bit

test al,1 ;per vedere se c'e'un altro INT

jnz al_int ;se <> 0 allora c'e' un altro INT

mov al,20H ;se no fine int hardware

out 20H,al

fine equ $

iret ;e ritorna a programma

new_int endp

;

init: ;abilita interrupt e setta routine

push ds ;di servizio interrupt

mov dx,offset new_int

mov ax,seg new_int

mov ds,ax ;setta offset e seg routine

mov al,0BH ;vettore int per COM1

mov ah,25H ;call SET INTERRUPT VECTOR

int 21H ;dell'int 21H del DOS

;

; Dopo aver settato l'indirizzo della routine di servizio

; dell' interrupt abilita l' interruzione della porta di

; comunicazione

; mov al,0FH ;1111 binario = tutte le inter.

;abilitate

out 03F9H,al ;porta abilitazione interrupt

lea dx,fine ;offset fine routine interrupt

;che deve rimanere residente

pop ds ;ripristina ds salvato all'inizio

;della routine di settaggio

;dell'interrupt

int 27H ;termina e rimani residente

;

_text ends

end

Come avrete notato, a parte la call 25H dell' int 21H, vi

risultera' nuova anche la chiamata all'interrupt 27H.

Anche questo verra' trattato successivamente.

Per ora vi accenno solo che questo interrupt permette a un

programma di terminare ma di rimanere residente in memoria.

Il discorso relativo alla trasmissione dati potrebbe continuare

ancora per un bel pezzo in quanto le problematiche sono

moltissime.

Ultimamente con Sergio Villone e Giorgio Griffini stiamo cercando

di portare avanti un progetto di programma di BBS con

caratteristiche innovative.

Se il tutto andra' avanti come si spera e' facile che a lavoro

compiuto venga scritto un testo in cui verranno affrontate tutte

le problematiche.

Oltre alla gestione a livello hardware dei programmi di

trasmissione esiste la pur complessa progettazione dei protocolli

destinati alla trasmissione binaria di programmi nei casi in cui

sia necessaria una verifica dei dati.

Protocolli come Xmodem, Kermit, Sealink (TM), Telink ecc.

pretenderebbero per la descrizione della parte algoritmica un

notevole spazio.

Man mano che il tempo passa fanno comparsa nuovi protocolli del

tipo degli Sliding Window.

Alle persone interessate alla progettazione dei protocolli di

comunicazione consiglio uno dei volumi di Andrew S. Tanenbaum

intitolato "Computer Networks" edito dalla Prentice Hall

International.

In molti BBS italiani utilizzanti il software OPUS (Tm) potrete

reperire i sorgenti del protocollo SEAlink (Tm) oltre a testi

recanti le caratteristiche di vecchi protocolli quali Xmodem,

Kermit ecc. Interrupt di tastiera (16H)

Questo interrupt svolge alcuni servizi legati all'input da

tastiera.

La modifica di questo costituisce uno dei metodi per la creazione

di programmi residenti in memoria attivabili mediante

l'intercettazione di un determinato tasto premuto.

Di questo parleremo tra breve mentre per ora vediamo i compiti

svolti.

Come per gli altri interrupts i servizi sono selezionabile

mediante l'opportuno settaggio di valore all'interno del registro

AH.

AH = 0 Legge un carattere dalla tastiera

Quando questo servizio viene richiesto inputa un carattere dalla

tastiera del PC.

Nel caso che non ci sia nessun carattere a disposizione aspetta

che ne venga inserito uno.

Il seguente esempio ne mostra l'utilizzo.

union REGS inregs, outregs;

inchar()

{ inregs.h.ah = 0x00;

int86(0x16, &inregs, &outregs);

return(outregs.h.al);

}

AL, come si sara' potuto notare, contiene il codice principale

del carattere.

AH potrebbe contenere il byte ausiliario.

Nel caso che il tasto premuto sia, ad esempio, un tasto funzione

allora AL conterra' 0 mentre AH il codice secondario di questo.

Sono codici secondari i seguenti :

3 Carattere nullo (NUL)

15 Prima tabulazione

16-25 Alt - Q,W,E,R,T,Y,U,I,O,P

30-38 Alt - A,S,D,F,G,H,J,K,L

44-50 Alt - Z,X,C,V,B,N,M

59-68 Tasti funzione F1-F10

71 Home

72 Cursore in alto

73 Pg Up

75 Cursore a sinistra

77 Cursore a destra

79 End

80 Cursore in basso

81 Pg Dn

82 Ins

83 Del

84-93 Tasti funzione F11-F20 (Shift+Fx)

94-103 Tasti funzione F21-F30 (Ctrl +Fx)

104-113 Tasti funzione F31-F40 (Alt +Fx)

114 Ctrl-PrtSc

115 Ctrl-cursore a sinistra

116 Ctrl-cursore a destra

117 Ctrl-End

118 Ctrl-PgDn

119 Ctrl-Home

120-131 Alt -1,2,3,4,5,6,7,8,9,0,-,=

132 Ctrl-PgUp

AH = 1 Guarda se c'e' un carattere nel buffer

Mentre il servizio precedente aspettava che ci fosse un carattere

in input dalla tastiera questo controlla solo la sua presenza.

Ad indicarne la presenza ci pensera' il flag di Zero.

Se il flag e' a 0 significa che il carattere e' disponibile e

quindi viene restituito nel registro AX mentre il caso contrario

e cioe' il flag di Zero a 1 indichera' un assenza di input.

Per la suddivisione di questo in AH e AL vedi il servizio

precedente.

Una funzione di questo tipo e' utile per la scrittura di funzioni

quali kbhit() gia' presente nella libreria del compilatore

Microsoft.

AH = 2 Acquisisce lo stato dello Shift

Il servizio riporta nel registro AL lo stato di shift.

I bit testabili per riconoscere lo stato della tastiera sono i

seguenti : +---+---+---+---+---+---+---+---+

: 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+

: : : : : : : :

: : : : : : : Shift di destra ON

: : : : : : Shift sinistra ON

: : : : : Ctrl ON

: : : : Alt ON

: : : Scroll lock ON

: : Num lock ON

: Caps lock ON

Ins ON

A indicare lo stato ON sara' il relativo bit a 1.

Questo valore viene letto dal servizio dalla cella di memoria

0x417 relativa agli indicatori di stato della tastiera.

Un esempio di routine atta ad indicare lo stato, ad esempio,

dell' insert e' :

/*

* Insert.c

*

* Riporta 0 se lo stato del tasto insert e' OFF

* 1 se e' ON

*/

char far *status = 0x00400017;

insert()

{ int result;

result = 128;

result &= *status;

return(result ? 1 : 0);

}

Il puntatore *status viene inizializzato a puntare al segmento,

offset della locazione dove sono conservati i bit indicanti gli

stati dei vari tasti.

Eseguendo un operazione di AND con 128 (10000000) si puo' sapere

lo stato del tasto insert.

Eseguendo operazioni di AND con altri valori si puo' risalire

allo stato di ogni singolo tasto elencato con il servizio 02H

dell'interrupt 16H.

Esiste anche la locazione 0040:0018 che indica altri stati

relativi a tasti shift , Ctrl-Num-Lock ecc.

Possiamo schematizzare mediante due tabelle i tasti di cui e'

possibile testare lo stato:

0040:0017 bit Tasto Se == 1

7 Insert Insert 'ON'

6 CapsLock CapsLock 'ON'

5 NumLock NumLock 'ON'

4 ScrollLock ScrollLock 'ON'

3 Alt shift Tasto premuto

2 Ctrl shift " "

1 Left shift " "

0 Right shift " "

0040:0018 bit Tasto Se == 1

7 Insert Tasto premuto

6 CapsLock " "

5 NumLock " "

4 ScrollLock " "

3 Ctrl_Numlock Stato 'ON'

La modifica di questo vettore di interruzione costituisce uno dei

metodi per la scrittura di programmi residenti in memoria.

Non si parla ancora di programmi in grado di richiamare funzioni

esterne in quanto il richiamo di queste dall'interno di una

routine di servizio di un interrupt presenta alcuni problemi non

facili da risolvere.

Sarebbe molto facile modificare il vettore di interruzione della

tastiera in modo che questo non legga solo il carattere premuto

ma che controlli anche il suo codice e nel caso che questo sia il

tasto da noi aspettato richiami una determinata routine.

Un piccolo esempio scritto in assembler su cui si potra' lavorare

per evolverlo e' il seguente.

In pratica il programma modifica il vettore di interrupt 16H per

far si che il carattere inputato venga controllato per vedere se

corrisponde a CTRL A.

In caso affermativo controlla un flag che, mediante apposito

settaggio, indica lo stato del DTR.

Se questo risulta disattivato lo attiva e viceversa.

_TEXT SEGMENT PUBLIC

assume cs:_text,ds:_text ;assume CS e DS

org 100H ;origine a 100H per .COM

start:

jmp init ;salta all' inizializzazione

old_k dd 0 ;conterra' vecchio vettore

flag db 0 ;flag DTR

;

k_int proc far

assume cs:_text, ds:_text

sti ;attiva interrupt

or ah,ah ;e'una richiesta di lettura?

jnz ki2 ;se no salta al vecchio vet.

pushf ;simula una chiamata

assume ds:nothing ;

call old_k ;di interrupt

cmp al,01H ;e' CTRL A ?

jne ki1 ;se no ritorna dall'interrupt

mov dx,3FCH ;metti in DX reg. cont.modem

cmp flag,01H ;e' il flag DTR a 1 ?

jne off ;se no vai a off

mov flag,00H ;se si metti flag a 0

mov ax,00H ;metti valore DTR OFF in AX

out dx,ax ;e setta la porta

mov al,01H ;rimetti CTRL A in AL

jmp ki1 ;ritorna dall'interrupt

off: mov flag,01H ;se DTR ON allora metti a 0

mov ax,03H ;AX = DTR attivo

out dx,ax ;setta la porta 3FC

mov al,01H ;metti CTRL A in AL

ki1: iret ;ritorna dall'interrupt

ki2: assume ds:nothing

jmp old_k ;chiama vecchio vettore

;

k_int endp

;

init: mov bx,cs

mov ds,bx

; mov al,16H ;interrupt di cui si vuole

mov ah,35H ;l'indirizzo con call 35H

int 21H ;dell'interrupt 21H

mov word ptr old_k,bx ;mette l'indirizzo ottenuto

mov word ptr old_k[2],es ;nella variabile old_k

; mov bx,cs

mov ds,bx

mov dx,offset k_int ;mette in DX l'indirizzo

mov al,16H ;del nuovo vettore 16H che

mov ah,25H ;viene settato con call 25H

int 21H ;dell'interrupt 21H

; mov bx,cs

mov ds,bx

mov dx,offset init

int 27H ;termina ma rimani residente

;

_TEXT ENDS

END START

Di alcuni servizi dell'interrupt 21 ne avevamo solo accennato

qualche cosa parlando del programma in C che riportava i segmenti

e gli offset dei vettori di interrupts.

Per ora solo un altro accennno in quanto ne parleremo piu' avanti

in modo dettagliato.

La call 35H dell'interrupt 21H riporta in ES:BX il segmento e

l'offset di un interrupt specificato in AL prima della chiamata

dello stesso.

La call 25H invece setta il segmento e l'offset di un interrupt.

Nel programma precedente abbiamo prima preso l'indirizzo del

vecchio vettore dell'interrupt 16H e lo abbiamo salvato nella

variabile old_k e dopo abbiamo settato con l'indirizzo relativo

alla nostra procedura il riferimento al nuovo interrupt.

Vedremo piu' avanti come e' possibile scrivere una procedura come

questa in linguaggio C.

Gestione dischi (13H)

A livello di BIOS la gestione dei dischi, per tutte le operazioni

riguardanti la lettura, la scrittura, la formattazione e la

verifica, viene eseguita dall'interrupt 13H selezionando come al

solito il servizio da svolgere mediante il registro AH.

In questo caso altri registri dovranno, come vedremo, essere

settati con gli opportuni parametri indicanti, ad esempio, il

drive interessato, la testina ecc.

Vediamo i servizi e i registri interessati alle varie operazioni.

AH = 0 Resetta il disco

Serve a ricalibrare il dischetto per iniziare un servizio.

Puo' essere utilizzato anche quando si e' verificato un errore

prima di ripartire con un ulteriore tentativo.

Coinvolge solo il drive senza influenzare effettivamente il

dischetto inserito in questo.

AH = 1 Acquisisce lo stato del dischetto

Mediante questo servizio e' possibile avere restituito un byte

che indica lo stato del dischetto.

Questo byte viene restituito in AL e puo' assumere i seguenti

valori : Valore Stato indicato

---------------------------------------------

128 Timeout del lettore

64 Spostamento su una traccia fallito

32 Errore del controller

16 Cattivo CRC

9 Errore margine DMA

8 Errore DMA

4 Settore errato

3 Errore protezione da scrittura

2 Indirizzo marcato errato

1 Comando errato

AH = 2 Lettura settori disco

In questo servizio iniziamo a vedere l'utilizzo degli altri

registri.

Riportero' una sola volta la mappa di utilizzo dei vari registri

senza piu' specificarla in seguito.

Registro Scopo NOTE

-----------------------------------------------------

DL Numero drive (0-3) Nessuna

DH Facciata (0-1) Nessuna

CH Traccia (0-39) Nessuna

CL Settore (1-8) Non usato da format

AL Numero settori Non usato da format

ES:BX Indirizzo buffer Non usato da verify

Come dicevo, se non specificato diversamente nelle note, questi

registri sono utilizzati da tutti i servizi che stiamo vedendo.

Lo scopo penso che sia sufficentemente chiaro a parte la copia di

registri ES:BX di cui parleremo.

Nelle operazioni di lettura bisogna creare una memoria di

transito in cui il sistema depositera' i dati durante le sue

operazioni.

Il registro ES conterra' l'indirizzo del segmento relativo a

questa memoria mentre BX conterra' il suo offset.

Un altro punto comune ai vari servizi riguarda il flag del carry

che normalmente e' a 0.

Uno stato a 1 successivo a uno di questi servizi indica un errore

dell'operazione.

Penso che vi ricordiate che uno dei membri della struttura

WORDREGS dichiarata in dos.h e' appunto il flag di carry (cflag).

Vedremo l'utilizzo mediante un esempio dopo aver parlato della

funzione di formattazione di una traccia.

E' interessante notare che l'interrupt 1E ha il vettore contenuto

all'indirizzo 78H (1E * 4).

Questo punta alla locazione 522H in cui sono contenuti 10 byte

che riguardano determinati parametri del disco.

Questi sono utilizzati per le operazioni su disco.

Indirizzo Scopo Default

-----------------------------------------------------------------

522 bit 7-4 Passi con incrementi di tempo di 2 ms. D0

522 bit 0-3 Tempo scarico testina 0F

523 Tempo caricamento testina 02

524 Tempo per lo spegnimento del motore 25

525 Byte per settore (0=128,1=256,2=512,3=1024) 02

526 Settori per traccia 09

527 Lunghezza gap tra due settori 2A

528 Lunghezza dei dati FF

529 Lunghezza gap formattato 50

52A Byte riempimento formattazione F6

52B Tempo di arresto della testina 0F

52C Tempo di avvio del motore 02

Questi valori sono settabili per ottenere ad esempio

formattazioni inconsuete o per creare programmi di format piu'

veloci, come vedremo tra breve.

Vediamo ora un esempio che legge i settori della directory e

mostra il contenuto in esadecimale e in ASCII.

/* Readdir.c */

#include <stdio.h>

#include <dos.h>

#include <malloc.h>

union REGS inregs, outregs;

struct SREGS segregs;

unsigned char far *buffer;

char *calloc();

main()

{ int n;

buffer = calloc(2048,sizeof(char));

inregs.x.bx = FP_OFF(buffer);

segregs.es = FP_SEG(buffer);

inregs.h.dl = 0x00; /* Drive A: */

inregs.h.dh = 0x00; /* Head */

inregs.h.ch = 0x00; /* Track */

inregs.h.cl = 0x06; /* Sector */

inregs.h.al = 0x04; /* Num. sect. */

inregs.h.ah = 0x02; /* Funct.read */

int86x(0x013,&inregs,&outregs,&segregs);

puts("Funzione read con INT 13H");

puts("Disk Directory Esa -----------------\n");

for(n=0;n!=2048;n++)

printf("%02x ",*buffer++);

buffer -= 2048;

puts("Disk Directory Char -----------------\n");

for(n=0;n!=2048;n++)

printf("%c",*buffer++);

}

Potreste modificare l'esempio in modo da poter leggere qualsiasi

settore del disco.

Specificando come codice disco 80H farete in modo che il servizio

sia riguardante l'hard disk.

Sinceramente non mi sento di portare esempi in quanto per poterlo

fare dovrei sperimentarli sul mio hard (!!!).

AH = 3 Scrive un settore o piu' su disco

Anche per questo servizio valgono i registri visti

precedentemente.

AH = 4 Verifica i settori del dischetto

Il servizio esegue il controllo ciclico di rindondanza (CRC) per

scoprire se esistono errori nei dati registrati nei settori

specificati.

Anche in questo caso i registri sono gli stessi utilizzati per

gli altri servizi a parte ES:BX che non viene settato in quanto

inutile per il verify.

AH = 5 Formatta una traccia

Il servizio merita due parole in quanto alcuni dei registri

precedentemente trattati non vengono utilizzati ma in

particolar modo si fa' dell'area puntata da ES:BX un uso

particolare.

ES:BX deve puntare ad un area in cui sono memorizzati i marcatori

di settore.

Questi sono gruppi di 4 byte per ogni settore della traccia in

cui vengono inseriti il numero di traccia (C), la testina (H), il

numero del settore (R) e il numero di byte per settore (N).

Se si vuole formattare, ad esempio, la traccia 1 faccia 1 a 9

settori per traccia si dovra' impostare ES:BX a puntare a :

CHRN CHRN CHRN CHRN CHRN CHRN CHRN CHRN CHRN

ES:BX 0112 0122 0132 0142 0152 0162 0172 0182 0192

---- ---- ---- ---- ---- ---- ---- ---- ----

Settore 00 01 02 03 04 05 06 07 08

Questo lavoro deve essere eseguito per ogni traccia.

E' possibile anche eseguire, a scopi di protezione dei dischi,

delle formattazioni con i numeri di settore all'interno dei

marcatori errati o non in ordine crescente.

Per il servizio di format e' possibile anche alterare i valori

relativi ai dieci bytes visti in precedenza (Loc. 522).

Vediamo ora un programma che esegue la formattazione di un

dischetto a 360K e a 320K.

Il tempo impiegato e' meno della meta' di quello del format IBM.

Il programma l'ho tradotto in C e modificato da un programma

uplodato nel BBS da Giorgio Griffini scritto originariamente in

Turbo Pascal e Assembler dallo stesso.

#include <stdio.h>

#include <dos.h>

#define AT(x,y) printf("\33[%d;%dH", x, y) /* Macro */

#define CLS puts("\33[2J");

#define FLASH puts("\33[5m");

#define NORM puts("\33[0m");

union REGS inregs, outregs;

struct SREGS segregs;

unsigned char table[37]; /* Tavola CHRN */

unsigned char disktable[12]; /* Tavola parametri disco */

unsigned char sector[512]; /* Matrice contenente boot */

char far *tab = table; /* Puntatore far a table */

char far *dle = disktable; /* a disktable e a sector per */

char far *sct = sector; /* utilizzare macro FP_SEG/OFF */

char bootmsg[255]; /* Messaggio no boot */

unsigned dto, dts; /* Offset e segmento tavola disco*/

int drive; /* Disco A: o B: */

int size;

int status; /* Flag di errore */

main(argc,argv)

int argc;

char *argv[];

{ int key;

drive = 0;

size = 1;

set_cur();

ol_table();

if(argc > 1) {

for(key=1;key != argc;key++) {

if(strcmp(argv[key],"B:") == 0 || strcmp(argv[key],"b:")

== 0) drive = 1;

if(strcmp(argv[key],"/s") == 0 || strcmp(argv[key],"/S")

== 0) size = 0;

}

}

init();

}

init() /* Cicli traccia, side */

{ register track, side;

int key;

status= 0;

CLS;

puts("FAST FORMAT 1.0 Maxpirata");

printf("\nInsert a new disk to format into drive ");

FLASH;

AT(6,43);

printf("%c:\33[1A", 65+drive);

NORM;

puts("\nand press any key to start or ESC to abort.");

key = getch();

if(key == '\033') {

puts("\n\nFormat aborted by operator");

exit(0);

}

AT(9,1);

printf("Formatting track ->");

disk_reset();

setdtab();

disktable[8] = 0x00;

disktable[10]= 0x04;

for(track=0;track!=40;track++) {

AT(9,21);

printf("%02d", track);

for(side=0;side != size+1;side++) {

mk_table(track,side);

format(track,side);

disktable[10]= 0x00;

if(status==1) {

puts("\n\nUnable to format diskette.");

resetdtab();

abort();

}

}

if(track==0) {

disktable[8] = 0xF6;

AT(9,21);

printf(" *");

writesysblock();

if(status==1) {

puts("\n\nUnable to write boot");

resetdtab();

abort();

}

}

}

resetdtab();

puts("\n\n<FORMAT COMPLETE>\n");

printf("Format another diskette (y/n) ? ");

key = getch();

if(key == 'Y' || key == 'y') init();

CLS;

}

writesysblock() /* Scrive traccia 0 */

{ setupfat();

if(status==0)

setupboot();

}

ol_table() /* Vecchi parametri tavola disco */

{ dto = 0x0522;

dts = 0x0000;

disktable[0] = 0xDF;

disktable[1] = 0x02;

disktable[2] = 0x25;

disktable[3] = 0x02;

disktable[4] = 0x09;

disktable[5] = 0x2A;

disktable[6] = 0xFF;

disktable[7] = 0x50;

disktable[8] = 0xF6;

disktable[9] = 0x0F;

disktable[10] = 0x02;

}

format(track,side) /* Formatta tr/si specificati come arg. */

int track,side;

{ register retry;

retry = 3; /* Come dice IBM prova tre volte al massimo */

do {

inregs.h.ah = 0x05;

inregs.h.dl = drive;

inregs.h.dh = side;

inregs.h.ch = track;

inregs.h.cl = 0x01;

inregs.h.al = 0x09;

segregs.es = FP_SEG(tab);

inregs.x.bx = FP_OFF(tab);

int86x(0x13,&inregs,&outregs,&segregs);

status = outregs.x.cflag;

if(status != 0) {

disk_reset();

--retry;

}

else

retry = 0;

} while(retry != 0);

}

write_sector(numsect) /* Scrive con INT 26 un settore */

int numsect;

{ segregs.ds = FP_SEG(sct);

inregs.x.bx= FP_OFF(sct);

inregs.x.cx= 0x01;

inregs.x.dx= numsect;

inregs.h.al= drive;

int86x(0x26,&inregs,&outregs,&segregs);

status=outregs.x.cflag;

}

setdtab() /* Setta la tavola disco in disktable */

{ inregs.h.ah = 0x25;

inregs.h.al = 0x1E;

inregs.x.dx = FP_OFF(dle);

segregs.ds = FP_SEG(dle);

intdosx(&inregs, &outregs, &segregs);

}

setupfat() /* Marca sulla il tipo di disco */

{ clrsector();

if(size == 0)

sector[0] = 0xFF;

else

sector[0] = 0xFD;

sector[1] = 0xFF;

sector[2] = 0xFF;

write_sector(1);

if(size == 1)

write_sector(3);

}

resetdtab() /* Ripristina vecchia tavola disco */

{ inregs.h.ah = 0x25;

inregs.h.al = 0x1E;

inregs.x.dx = dto;

segregs.ds = dts;

intdosx(&inregs, &outregs, &segregs);

}

clrsector() /* Azzera 512 elementi di sector */

{ register key;

for(key=0;key!=512;key++)

sector[key] = 0;

}

setupboot() /* Setta il settore del boot */

{ register key;

clrsector();

strcpy(bootmsg,"\007\n\rThis disk has not boot.");

strcat(bootmsg,"\n\rPress any key to restart...");

for(key = 0;key != strlen(bootmsg)+1;key++)

sector[0x80+key] = bootmsg[key];

sector[0] = 0xEB;

sector[1] = 0x29;

strcpy(bootmsg,"CFORMAT ");

for(key = 0;key != strlen(bootmsg)+1;key++)

sector[0x03+key] = bootmsg[key];

if(size==1) {

sector[11] = 0x00;

sector[12] = 0x02;

sector[13] = 0x02;

sector[14] = 0x01;

sector[15] = 0x00;

sector[16] = 0x02;

sector[17] = 0x70;

sector[18] = 0x00;

sector[19] = 0xD0;

sector[20] = 0x02;

sector[21] = 0xFD;

sector[22] = 0x02;

sector[23] = 0x00;

sector[24] = 0x09;

sector[25] = 0x00;

sector[26] = 0x02;

sector[27] = 0x00;

sector[28] = 0x00;

sector[29] = 0x00;

}

sector[43] = 0xFA;

sector[44] = 0x33;

sector[45] = 0xC0;

sector[46] = 0x8E;

sector[47] = 0xD0;

sector[48] = 0xBC;

sector[49] = 0x00;

sector[50] = 0x7C;

sector[51] = 0x16;

sector[52] = 0x1F;

sector[53] = 0xBE;

sector[54] = 0x80;

sector[55] = 0x7C;

sector[56] = 0xAC;

sector[57] = 0x0A;

sector[58] = 0xC0;

sector[59] = 0x74;

sector[60] = 0x09;

sector[61] = 0xB4;

sector[62] = 0x0E;

sector[63] = 0xBB;

sector[64] = 0x07;

sector[65] = 0x00;

sector[66] = 0xCD;

sector[67] = 0x10;

sector[68] = 0xEB;

sector[69] = 0xF2;

sector[70] = 0x32;

sector[71] = 0xE4;

sector[72] = 0xCD;

sector[73] = 0x16;

sector[74] = 0xCD;

sector[75] = 0x19;

sector[510]= 0x55;

sector[511]= 0xAA;

write_sector(0);

}

disk_reset() /* Resetta il controller */

{ inregs.h.ah = 0x00;

int86(0x13,&inregs,&outregs);

}

mk_table(track,side) /* Crea marcatori di traccia CHRN */

int track,side;

{ register sect, s;

s = 0;

for(sect=1;sect!=10;sect++) {

table[s++] = track;

table[s++] = side;

table[s++] = sect;

table[s++] = 0x02;

}

}

set_cur() /* Setta il cursore a 0 di dimensioni */

{ inregs.h.ah=0x01;

inregs.h.cl=0x13;

inregs.h.ch=0x13;

int86(0x10,&inregs,&outregs);

}

Il programma memorizza in una matrice la vecchia tabella dei

parametri del disco per poi modificare il decimo byte

(disktable[10]) per metterlo a 0.

La funzione setupboot serve a inizializzare la tabella dei dati

da registrare nell'area di boot mediante un interrupt di cui non

abbiamo parlato prima e precisamente del 26H.

L'interrupt 25H (Absolute Disk Read) e il 26H (Absolute Disk

Write) costituiscono due metodi per trasferire il controllo al

DOS BIOS per quanto riguarda operazioni di scrittura e di

lettura.

Prendiamo ad esempio il 25H.

I registri interessati a questa operazione sono :

AL = Numero del drive (0=A:, 1=B: ecc.)

CX = Numero dei settori da leggere

DX = Settore logico d'inizio

DS:DX = Indirizzo memoria di trasferimento.

Se in uscita dall'interrupt il flag di carry e' a zero significa

che l'operazione ha avuto successo.

Se questo si trova invece ad 1 significa che c'e' stato un errore

il cui codice sara' testabile all'interno del registro AX.

La parte alta (AH) contiene il codice di errore del DOS mentre la

parte bassa (AL) contiene uno dei seguenti valori :

80H Il drive non risponde

40H Operazione di posizionamento fallita

08H Cattivo CRC

04H Settore richiesto non trovato

03H Disco protetto in scrittura

02H Altro tipo di errore non elencato

L'interrupt 26H svolge l'operazione di scrittura invece che di

lettura come il 25H.

Valgono per questo i registri e i codici di errore riportati in

uscita per l'interrupt 25H.

Nei capitoli successivi tratteremo la struttura di un disco

parlando della FAT e dell' organizzazione della directory.

Colgo l'occasione portata dall'esempio precedente per parlare

di come e' strutturato il record di boot.

Si sarebbe potuto optare per una trattazione unica di quanto

riguarda l'organizzazione logica del disco.

Il problema e' dovuto al fatto che se non si parlasse a questo

punto di come e' strutturato il record di boot gran parte

dell'esempio rimarrebbe oscuro.

Infatti, come avrete notato, una funzione del programma serve al

settaggio dei parametri relativi a questo settore.

Il record di boot occupa 1 settore del dischetto e precisamente

il primo.

In questo settore e' contenuto un breve programma che serve per

il caricamento del sistema operativo che varia leggermente a

seconda del formato (in genere la prima parte occupa i byte 0, 1

e 3).

Oltre a cio' si trovano nel record di boot delle informazioni del

Bios che servono a controllare il disco stesso.

L'offset di questi dati e' a partire dal byte 4 e precisamente:

Byte Lunghezza Descrizione

-----------------------------------------------------------------

4 8 Identificatore di sistema

12 2 Numero di byte per settore

14 1 Numero settori per cluster

15 2 Numero settori riservati boot

17 1 Numero copie FAT

18 2 Numero voci nella directory

20 2 Numero dei settori su disco

22 1 ID formato (FF, FE ...Vedi avanti FAT)

23 2 Numero settori FAT

25 2 Numero settori per traccia

27 2 Numero dei lati del disco

29 2 Numero settori speciali

Il settore di boot e' sempre presente indipendentemente dal fatto

che sul disco sia presente il sistema operativo o meno.

Nel caso che si tenti di caricare il dos da un disco su cui non

e' presente sara' compito del boot visualizzare un messaggio di

errore.

Terminiamo questa panoramica sui servizi riguardante i dischi

parlando di alcune porte che riguardano questo argomento.

0x3F2 Controllo del controllore

Vediamo una descrizione degli 8 bit di questo registro.

+---+---+---+---+---+---+---+---+

: 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+

: : : : : : : :

: : : : : : Selezione drive

: : : : : : 0 0 = A

: : : : : : 0 1 = B

: : : : : : 1 0 = C

: : : : : : 1 1 = D

: : : : : 0 = reset controllore

: : : : 0 = sconnessione dal BUS

: : : : 1 = abilitazione interruz.

: : : : e richieste DMA

: : : 1 = abilitazione motore A

: : 1 = abilitazione motore B

: 1 = abilitazione motore C

1 = abilitazione motore D

Penso che lo schema possa essere sufficentemente chiaro.

Questa porta e' possibile settarla.

0x3F4 Stato del controllore

Mediante questo registro e' possibile acquisire lo stato del

controllore.

Un determinato evento e' indicato da uno stato alto (1) sui vari

bit. +---+---+---+---+---+---+---+---+

: 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+

: : : : : : : :

: : : : : : : Drive a in modo SEEK

: : : : : : Drive b in modo SEEK

: : : : : Drive c in modo SEEK

: : : : Drive d in modo SEEK

: : : Comando in esecuzione

: : Modo non DMA

: Direzione dati dal controller al processore

Pronto a comunicare al controller

C'e' un termine di cui non abbiamo mai visto il significato e

precisamente DMA (Direct Memory Access).

Vedremo, dopo la descrizione della seguente porta, di accennare

a qualche informazione riguardante quest'ultimo.

L'interrupt 13H, per l'esecuzione dei suoi servizi, sfrutta la

porta 0x3F5.

Il settaggio in questo registro dei seguenti valori corrisponde

alla funzione descritta sul lato :

0x02 = Lettura traccia

0x03 = Specifica SRT,HUT,HLT,DMA

0x04 = Riporta stati del drive

0x05 = Scrittura dati

0x06 = Lettura dati

0x07 = Ricalibrazione

0x08 = Stato interruzioni

0x09 = Scrittura dati cancellati

0x0A = Lettura ID

0x0C = Lettura dati cancellati

0x0D = Formattazione traccia

0x0F = SEEK testina

0x11 = Ricerca per uguale

0x19 = Ricerca per minore o uguale

0x1D = Ricerca per maggiore o uguale

Parlavamo prima del DMA.

Dal suo stesso nome e' facile capire che si tratta di sistema di

accesso diretto in memoria.

In molte operazioni di trasferimento dati, ad esempio tra memoria

e dispositivi veloci di memorizzazione, il processore puo' essere

completamente escluso e il controllo delle operazioni viene preso

dal DMA che in pratica potremmo considerarlo come un ulteriore

blocco del sistema hardware visto precedentemente.

Infatti la tecnica di accesso diretto in memoria permette la

gestione di un trasferimento veloce di dati fra la stessa memoria

interna del sistema e le periferiche di I/O.

Un controllore DMA, in genere, per ogni linea gestita possiede

tre registri.

Un registro di indirizzo che contiene in ogni istante l'indirizzo

della prossima locazione di memoria che deve essere trasferita,

un contatore che contiene il numero di parole che devono essere

ancora trasferite e un registro di stato che segnala la

condizione del dispositivo.

In pratica il processore 8088/86 possiede un piedino di HOLD che

se portato ad un livello opportuno trasmette una richiesta di

bus.

In questo caso, dopo aver terminato il ciclo corrente, si mette

in una fase in cui sospende le sue operazioni fino alla rimozione

del segnale da parte del controller.

Il PC IBM possiede quattro canali di DMA per i quali valgono le

seguenti porte per il loro controllo.

Uno dei canali del DMA serve per il "rinfresco" della memoria

RAM che altro non e' che una continua lettura e scrittura di

questa allo scopo di evitare la perdita delle informazioni

contenute.

Le RAM dinamiche, infatti, possono mantenere solo per un breve

tempo le informazioni e quindi questa operazione si rivela

indispensabile.

La porta 0x000 (Registro di indirizzo del canale 0) viene

utilizzata appunto per questo scopo e cioe' il refresh di

memoria.

La porta 0x001 (Contatore di parola del canale 0) e' impostato a

0xFFFF (64 K) per il rinfresco di memoria.

L'integrato controllore dell'accesso diretto in memoria e'

siglato 8237.

Il canale 1 di questo non viene utilizzato dal PC in quanto

questo non supporta il trasferimento da memoria a memoria.

Il registro 0x004 (Registro indirizzi del canale 2) e' utilizzato

per i trasferimenti dei dati dai dischetti mentre il 0x005 e' il

contatore di parola dello stesso canale.

I registri 0x006 e 0x007 sono i corrispondenti del canale 0

relativi al canale 3 utilizzato per trasferimenti tra memoria e

disco fisso.

La lettura sulla porta 0x008 riporta lo stato del controller

mentre la sua scrittura imbastisce un comando per questo.

Il chip 8237 considera a priorita' in 4 canali assegnando al

canale 0 la priorita' piu' alta mentre al 3 quella piu' bassa.

Il discorso delle priorita' diventa complesso se trattato a

livello hardware.

Infatti le priorita' possono essere fisse o rotanti.

Nell'ultimo caso il canale appena servito diventa quello a

priorita' piu' bassa.

La lettura del registro di stato e di comando (0x008) puo'

indicare : +---+---+---+---+---+---+---+---+

: 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+

: : : : : : : :

: : : : : : : terminato canale 0

: : : : : : terminato canale 1

: : : : : terminato canale 2

: : : : terminato canale 3

: : : richiesta canale 0

: : richiesta canale 1

: richiesta canale 2

richiesta canale 3

Come dicevo prima la scrittura su questa porta costituisce anche

un modo per inviare un comando relativo al controller del DMA.

Questo viene inizializzato a 0x00.

+---+---+---+---+---+---+---+---+

: 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+

: : : : : : : :

: : : : : : : Non usato

: : : : : : Non usato

: : : : : Abilita il controllore

: : : : Scansione del tempo (*)

: : : Priorita' (*)

: : Ultima stampa

: DREQ alto

DACK basso

(*) E' consigliato non modificarli

E' possibile abilitare o disabilitare i canali del DMA settando

in modo opportuno il registro di maschera.

La porta che si occupa di questo e' la 0x00A e la descrizione dei

sui bit e' la seguente :

+---+---+---+---+---+---+---+---+

: 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+

: : : : : : : :

+ Non utilizzati+ : 0 0 = canale 0

: 0 1 = canale 1

: 1 0 = canale 2

: 1 1 = canale 3

1 = attiva bit di maschera

0 = azzera bit di maschera

Esistono altri registri che si interessano del DMA ad esempio :

0x0B Registro di modalita'

+---+---+---+---+---+---+---+---+

: 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+

: : : : : : : :

: : : : : : 0 0 = canale 0

: : : : : : 0 1 = canale 1

: : : : : : 1 0 = canale 2

: : : : : : 1 1 = canale 3

: : : : 0 0 = verifica

: : : : 0 1 = scrittura

: : : : 1 0 = lettura

: : : 1 = reinizializzazione auto.

: : 0 = incremento indirizzo

: : 1 = secremento indirizzo

0 0 = modalita' demand

0 1 = " singola

1 0 = " blocchi

1 1 = " cascata

Nel caso del PC viene utilizzata la modalita' singola per ogni

canale.

Come vedremo in seguito l'interrupt 21H del DOS ci mettera' a

disposizione molti servizi specifici per lavorare sui file del

disco. Gestione stampante (INT 17H)

L'interrupt che ora vedremo e' quello che gestisce le

comunicazioni con la stampante.

Le funzioni svolte sono esattamente tre.

AH = 0 Stampa il carattere presente in AL e ritorna in AH lo

stato del servizio. In altre parole se di ritorno AH

contiene 1 significa che il carattere non e' stato

stampato.

AH = 1 Inizializza la porta della stampante. AH ritorna lo

stato.

AH = 3 Il servizio tre riporta lo stato della stampante in

AH. L'interpretazione degli 8 bit e' la seguente.

+---+---+---+---+---+---+---+---+

AH : 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+

: : : : : : : :

: : : : : : : Time out

: : : : : Non usati

: : : : 1 = I/O error

: : : 1 = select

: : 1 = out of paper

: 1 = acknowledge

1 = not busy

Legate agli adattatori per stampanti parallele troviamo anche tre

porte. Stampante parallela

Prima Seconda Terza Scopo

0x3BC 0x378 0x278 Dati in uscita dalla stampante

0x3BD 0x379 0x279 Registro di stato

0x3BE 0x37A 0x27A Registro di controllo

Le porte 0x3BC/0x378/0x278 contengono i dati leggibili

indirizzati alla stampante.

Ogni bit del registro equivale a uno degli otto bit del dato in

uscita.

Le porte 0x3BD/0x379/0x279 corrispondono al registro di stato

della stampante mediante le quali e' possibile ricavare le

condizioni di questa.

Il significato di ogni bit del registro e' il seguente :

+---+---+---+---+---+---+---+---+

: 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+

: : : : : : : :

: : : : : : : 1 = Timeout

: : : : : Non usati

: : : : 0 = Errore

: : : : 1 = Normale

: : : 0 = Non in linea

: : : 1 = In linea

: : 0 = Carta OK

: : 1 = Fine della carta

: 0 = Segnale di riconoscimento

: 1 = Normale

0 = Busy (occupata)

1 = Libera

Le porte 0x3BE e quelle relative al secondo e al terzo adattatore

stampante si incaricano del controllo della stampante.

+---+---+---+---+---+---+---+---+

: 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+

: : : : : : : :

Non usati : : : : 0 = Normale

: : : : 1 = Dati in uscita

: : : 0 = Nessun cambio linea

: : : automatico

: : : 1 = Cambio linea aut.

: : 0 = Inizializza stampante

: : 1 = Impostazione normale

: Sempre a 1

0 = Interrupt disabilitati

1 = IRQ7 abilitato

Come nel caso delle funzioni di controllo legate ad una

determinata periferica, viste in precedenza, anche in questo caso

ne esistono alcune legate all' INT 21H del Dos che permettono di

gestire i files che debbono essere stampati.

Oltre a queste esiste un ulteriore interrupt, precisamente il

2FH, che si interessa della stampante.

Vedremo tutti nei capitoli riguardanti le function calls del Dos.

Prima di terminare l'argomento riguardante la stampante ancora

due parole sui codici di controllo.

In Linguaggio C come per gli altri linguaggi potete utilizzare i

codici di comando delle stampanti per eseguire determinati

settaggi.

Riporto i seguenti codici che potranno essere inviati alla

stampante mediante la macro SETPRINT.

#define SETPRINT(x) putc(x,stdprn)

Ad ogni invio di un codice corrisponde una chiamata del tipo

SETPRINT(27); ecc.

Paper Movements

10 Line feed

11 Tab vertically

12 Form feed

13 Carriage return

27,56 Ignore paper end

27,57 Cancel ignor paper end

27,66 Set vertical tabs

27,78 Set skip perforation

27,79 Cancel skip perforation

Print Head Movements

8 Backspace

9 Tab horizontally

27,60 Move print head to left

27,68 Set horizontal tabs

27,80 Proportional spacing ON/OFF

27,85 Unidirectional print ON/OFF

Line/Character Spacing

27,48 1/8 inch line spacing

27,49 7/72 " " "

27,50 1/6 " " "

27,51 Variable line feed (n/216)

27,65 Variable line feed (n/72)

27,67 Set page lenght

Fonts

11 15 char per inch ON

14 Double width print ON

15 Compressed print ON

18 Compressed print OFF

20 Double width print OFF

27,45 Underline ON/OFF

27,69 Emphasized print ON

27,70 Emphasized print OFF

27,71 Double strike print ON

27,72 Double strike print OFF

27,83 Subscript/superscript ON

27,84 Subscript/superscript OFF

Special character

27,54 Select set 1

27,55 Select set 2

Graphics Modes

27,75 480 dot bit image graphics

27,76 960 dot bit image graphics

27,89 960 dot normal speed

27,90 1920 dot bit image graphics

Miscellaneous

7 Bell

17 Select printer

19 Deselect printer

24 Clear buffer

Gestione MOUSE (INT 33H)

Ed eccoci al primo degli interrupts non documentati dalla

Microsoft.

L'interrupt 33H gestisce tutto quello che riguarda il mouse, una

delle periferiche che stanno avendo sempre piu' applicazioni

all'interno dei nuovi programmi.

WINDOW (Tm) della Microsoft e' appunto uno di quei package in cui

l'uso del mouse e' importantissimo se non indispensabile.

Le coordinate sul video del mouse possono essere calcolate a

seconda del settaggio dello screen mediante i seguenti calcoli.

Modo Coordinate

40x25 CO & BW x = 16 * colonna, y = 8 * riga

80x25 CO & BW x = 8 * colonna, y = 8 * riga

320x200 x = 2 * coordinata x, y = coordinata y

640x200 x = coordinata x , y = coordinata y

Monocromatico x = 8 * colonna, y = 8 * riga

EGA & 3270 x = coordinata x, y = coordinata y

Riporto i seguenti servizi relativi all'interrupt 33H interessati

nella gestione del mouse.

Li ho riportati a tipo schede per una facile consultazione.

Call : 00H

Scopo : Check if mouse is installed

In ingresso :

AX = 00H

In uscita :

AX = -1 se il mouse e' installato, 0 in caso negativo

BX = Numero dei bottoni

Call : 03H

Scopo : Get mouse position

In ingresso :

AX = 03H

In uscita :

BX = Stato dei pulsanti

Bit 0 = 1 se pulsante sinistro premuto

Bit 1 = 1 se pulsante destro premuto

CX = Coordinata X

DX = Coordinata Y

Call : 04H

Scopo : Set mouse position

In ingresso :

AX = 04H

CX = Nuova coordinata X

DX = Nuova coordinata Y

Call : 07H

Scopo : Set mouse X bounds

In ingresso :

AX = 07H

CX = Limite minimo X

DX = Limite massimo X

Call : 08H

Scopo : Set mouse Y bounds

In ingresso :

AX = 08H

CX = Limite minimo Y

DX = Limite massimo Y

Call : 09H

Scopo : Set graphics cursor

In ingresso :

AX = 09H

BX = Posizione centrale X

CX = Posizione centrale Y

ES:DX = Puntano alla maschera cursore.

Call : 0AH

Scopo : Set text cursor

In ingresso :

AX = 0AH

BX = 0 per selezionare attributo cursore

1 per settare tipo cursore

CX = Maschera schermo o linea iniziale di scansione

DX = Maschera cursore o linea finale di scansione

Call : 01H

Scopo : Make mouse cursor displayed

In ingresso :

AX = 01H

Call : 02H

Scopo : Make mouse cursor not displayed

In ingresso :

AX = 02H

Call : 0BH

Scopo : Determine number of phisical positions moved.

In ingresso :

AX = 0BH

In uscita :

CX = Numero di posizioni su X mosse dal ultima chiamata alla

funzione 0BH

DX = Numero di posizioni su Y mosse dal ultima chiamata alla

funzione 0BH

Call : 0FH

Scopo : Set physical to screen movement ratios.

In ingresso :

AX = 0FH

CX = Numero di posizioni fisiche per indicare un cambio di 8 X

DX = Numero di posizioni fisiche per indicare un cambio di 8 Y

NOTA: I valori iniziali sono X=8 Y=16

Call : 0DH

Scopo : Set light pen emulation

In ingresso :

AX = 0DH

Call : 0EH

Scopo : Stop light pen emulation

In ingresso :

AX = 0EH

Mediante questi servizi potrete creare nei vostri programmi le

apposite opzioni dedicate alla gestione video mediante mouse.

Un altro interrupt non documentato dalla Microsoft riguarda la

gestione della memoria estesa.

L' AT Ibm puo' superare come quantita di memoria i 640 Kbytes in

cui era normalmente relegato il vecchio PC.

Ultimamente anche per quanto riguarda quest' ultimo sono comparse

sul mercato delle schede per l'espansione a oltre 2 Mbytes.

Personalmente sul PC che ho utilizzato per la scrittura di

questo testo ho installato una scheda Flash Pack AST per la

velocizzazione dei processi e un espansione, sempre AST, con su

2 Mbytes di memoria.

L'interrupt che si interessa della gestione della memoria estesa

e' il 67H.

Anche in questo caso i servizi li riporto a tipo scheda.

Ad essere sinceri ho utilizzato questo modo per rappresentarli

in quanto ho eseguito una lettura dai dati di un programma che ho

scritto per la documentazione di questi interrupts.

Per non riscriverli ho optato per il mantenimento di questo

formato che dopo tutto non risulta neppure essere malvagio per

una facile consultazione.

Call : 40H

Scopo : Check expanded memory status

In ingresso :

AH = 40H

In uscita :

AH = Codice d'errore. (128 129 e 132), 0 se tutto OK.

Call : 41H

Scopo : Get window location

In ingresso :

AH = 41H

In uscita :

AH = Codice d'errore. (128 129 e 132)

BX = Segmento dove parte la finestra.

NOTA: La prima finestra e' all'offset 0, la seconda a 4000H ecc.

Call : 42H

Scopo : Determine free & total expanded memory pages

In ingresso :

AH = 42H

In uscita :

AH = Codice d'errore. (128 129 e 132)

BX = Numero di pagine libere

DX = Numero totale di pagine

Call : 43H

Scopo : Allocate expanded memory pages

In ingresso :

AH = 43H

BX = Numero pagine da allocare

In uscita :

AH = Codice d'errore. (128 129 132 133 135 136 e 137)

DX = Handle

Call : 44H

Scopo : Get expanded memory page

In ingresso :

AH = 44H

AL = Numero window (0 - 3)

BX = Pagina da prendere

DX = Handle

In uscita :

La pagina data indirizzabile mediante la locazione della

finestra.

AH = Codice d'errore. (128 129 131 132 o 134)

Call : 45H

Scopo : Free expanded memory

In ingresso :

AH = 45H

DX = Handle del blocco da liberare

In uscita :

AH = Codice d'errore. (128 129 131 132 o 134)

Call : 46H

Scopo : Get version number of EM software driver

In ingresso :

AH = 46H

In uscita :

AH = Codice d'errore. (128 129 e 132)

BX = Numero versione.

Call : 4BH

Scopo : Determine number of active handles.

In ingresso :

AH = 4BH

In uscita :

AH = Codice d'errore. (128 129 131 e 132)

BX = Numero di handles attivi. BH = 0

Call : 4CH

Scopo : Determine block size

In ingresso :

AH = 4CH

DX = Handle

In uscita :

AH = Codice d'errore. (218 129 131 e 132)

BX = Numero di pagine allocate per l'handle

Call : 4DH

Scopo : Get array of all handle sizes

In ingresso :

AH = 4DH

ES:DI = Puntano all'area dove deve essere salvato il risultato.

La grandezza deve essere (4 * num_handle_attivi) bytes.

Mai piu' di 1 Kbyte.

In uscita :

AH = Codice d'errore. (128 128 129 e 132)

ES:DI = Puntano all'area con le informazioni.

Call : 47H

Scopo : Save expanded memory board status

In ingresso :

AH = 47H

DX = Handle al quale si vuole associare lo stato.

In uscita :

AH = Codice d'errore. (128 129 131 132 e 140)

Call : 48H

Scopo : Restore expanded memory status

In ingresso :

AH = 48H

DX = Handle con cui era stato salvato lo stato.

In uscita :

AH = Codice d'errore. (128 129 131 132 e 142)

Come avrete potuto notare ad ogni servizio dell' int 67H

corrispondono alcuni codici d'errore che potrebbero essere

restituiti dalla call stessa.

L'elenco di questi e' il seguente.

128 - Malfunction in EM softw. 129 - Malfunction in EM hard

131 - Invalid handle 132 - Invalid function code

133 - No available EMM handles 134 - Page mapping context

error.

135 - Not enough exp.memory pages 136 - Not enough free memory

pages.

137 - Can't allocate 0 pages 138 - Request greater than the

number of pages

allocated to the handle.

139 - The phisichal memory page 140 - No room to save the

to which the handles page the expanded memory

is mapped is invalid. board status.

141 - Expanded memory board status 142 - No status information is

information already associa- associated with this

ted with this handle number. handle number.

143 - Invalid subfunction.

Nella creazione di software con la possibilita' di vedere una

memoria estesa testate sempre prima di richiamare qualsiasi

funzione destinata a questa la call relativa alla verifica della

presenza di una scheda EMM.

Potrete trovare maggiori specifiche sul "Lotus Intel Microsoft

Expanded Memory Specification".

In alcuni casi la memoria estesa puo' assumere dimensioni

notevoli.

Sempre portando ad esempio la scheda che utilizzo personalmente

e' possibile installarne fino a 4 per un totale di oltre 8 Mbyte.

Questa potrebbe essere configurata sia come EMM che come hard

disk RAM.

Molti pacchetti software in commercio, Symphony (Tm) per fare un

nome, vedono gia' la memoria estesa.

Concludiamo con questo il discorso per passare all' interrupt del

Bios per la gestione dello schermo.

Gestione video (INT 10H)

Parlando delle sequenze di escape per il controllo del video

avevo rimandato il proseguimento del discorso alla trattazione

dell'interrupt 10H che e' quello che presiede a tutte le funzioni

BIOS relative alla gestione dello schermo.

I servizi svolti dall'interrupt sono molti e lo stesso dicasi per

quanto riguarda le porte che svolgono la gestione del controller

video.

Questo per dire che sara' un paragrafo "intenso" di argomenti.

La gestione del video e' un argomento delicato in quanto le

possibilita' date all'utente sono complesse e in alcuni casi,

dicono, anche pericolose.

C'e' chi si chiedera' cosa significa "dicono".

Mi pare di aver letto una volta qualche cosa relativa al

settaggio di alcuni registri del controller.

Si diceva, a proposito, che l'uso incauto di questi avrebbe

potuto danneggiare il video e il controller stesso.

Sinceramente posso dire di aver provato qualsiasi sorta di

settaggio riguardante lo schermo senza che mi sia mai capitato

qualche inconveniente grave se non quello di dover resettare il

sistema per poter riiniziare il lavoro.

Esistono in commercio molti tipi di controller destinati al

video.

I piu' comuni, quelli che sicuramente tutti conoscete, sono il

monocromatico, la CGA (Color Graphics Adapter), la EGA e la

Hercules.

Originariamente il vecchio PC IBM nasceva con in dotazione la

scheda monocromatica.

Questa era orientata alla gestione dei testi in quanto, pur

disponendo di un ottima risoluzione per quanto riguardava i

caratteri, non aveva la possibilita' di gestire la grafica.

La Hercules e' possibile considerarla un estensione della scheda

monocromatica in quanto ha la possibilita' di gestire la grafica

ad una risoluzione di pixel maggiore di quella della CGA.

Chiaramente questa differenza di risoluzione porta ad un

incompatibilita' dei programmi destinati alla CGA con quelli per

la Hercules.

Personalmente, sul computer che uso per lavoro, utilizzo la

Hercules in quanto non avendo l'abitudine di usare giochi o

programmi grafici i vantaggi offerti da questa sono notevoli.

Il discorso non e' poi' cosi' categorico per quanto riguarda la

disponibilita' di programmi grafici che supportano la Hercules.

I maggiori programmi reperibili in commercio dispongono della

possibilita' di essere configurati su questa vedi ad esempio

ACAD, SYMPHONY, LOTUS ecc.

Esistono schede installabili su PC dedicate alla grafica ad alta

risoluzione.

Purtroppo dati i costi relativi alla scheda, al software e al

monitor dedicato non sono di uso comune.

Possedendo una scheda CGA avremo a disposizione le seguenti

possibilita' di configurazione :

40x25 Testo bw

40x25 Testo 16 colori

80x25 Testo bw

80x25 Testo 16 colori

320x200 Grafica bw

320x200 Grafica 4 colori

640x200 Grafica bw

Una nota particolare la merita l' Olivetti M24.

Questo oltre ai modi appena visti possiede un ulteriore modalita'

e precisamente il modo grafico 640x400.

La scheda Hercules possiede, oltre al modo testo 80x25, la

modalita' grafica 720x348 pixel.

Penso che non sia il caso di dilungarmi con la spiegazione di

termini come ad esempio pixel in quanto sicuramente sono tutti al

corrente del significato.

Secondo la modalita' video selezionata il controller ci mette a

disposizione di un certo numero di buffer di schermo normalmente

definite pagine.

Il numero di queste riferite alle caratteristiche del video sono

le seguenti :

40x25 Testo BW e colore 8 pagine

80x25 Testo BW e colore 4 pagine

320x200 Grafica BW e colore 1 pagina

640x200 Grafica BW e colore 1 pagina

720x348 Hercules 2 pagine

Penso che vi ricordiate che quando ho parlato del suffisso far

avevo portato un esempio di come scrivere direttamente sul video

della CGA.

L'inizializzazione char far *var = 0xB8000000;

era relativa al buffer principale della scheda CGA.

Nel caso del video monocromatico questo buffer e' posizionato nel

segmento 0xB000.

Parlando di buffer del video bisogna fare una precisazione.

Si potrebbe anche pensare che scrivendo direttamente in locazione

consecutive si possa ottenere come risultato la visualizzazione

della stringa voluta.

Questo non e' vero in quanto le celle di memoria che fanno parte

del buffer di schermo hanno fondamentalmente due compiti ed

esattamente :

+-----------+-----------+-----------+-----------+

: carattere : attributo : carattere : attributo : ecc.

+-----------+-----------+-----------+-----------+

I byte di numero pari contengono il codice del carattere mentre

quelli di numero dispari gli attributi.

I bytes di attributo vengono interpretati come segue :

Video monocromatico

+---+---+---+---+---+---+---+---+

: 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+

: : : : : : : :

: : : : : R G B

: : : : : Primo piano

: : : : 1 = intensita' alta

: R G B

: Sfondo

1 = lampeggiante

Le combinazioni possibili su RGB sono :

(1) RGB bit 6,5,4 = Sfondo

(2) RGB bit 2,1,0 = Primo piano

+---(1)---+ +---(2)---+

R G B R G B

0 0 0 0 0 0 Nero su nero

0 0 0 0 0 1 Sottolineato

0 0 0 1 1 1 Normale

1 1 1 0 0 0 Reverse

Nel caso dell'adattatore colore/grafica l'interpretazione del

byte di attributo e' sempre la stessa solo che valgono i seguenti

colori. R G B

+----+----+

0 0 0 Nero

0 0 1 Blu

0 1 0 Verde

0 1 1 Blu chiaro

1 0 0 Rosso

1 0 1 Porpora

1 1 0 Arancio

1 1 1 Bianco

Faccio notare che RGB sono le iniziali dei tre colori

fondamentali che possono comporre i vari colori.

Le dimensioni della pagina video per ogni modalita' sono le

seguenti : 40x25 Testo BW 800H

40x25 Testo 16 col. 800H

80x25 Testo BW 1000H

80x25 Testo 16 col. 1000H

320x200 Grafica BW 4000H

320x200 Grafica 4 col. 4000H

640x200 Grafica BW 4000H

Hercules 8000H

A questo punto iniziamo a vedere i servizi dell'interrupt 10H.

Come per tutti gli altri il numero del servizio legato ad una

certa funzione si ottiene settando in AH il valore corrispondente

alla funzione stessa e precisamente :

AH = 0 Setta il modo video

Il valore relativo al tipo di schermo va' inserito in AL.

AL = 0 40x25 BW AL = 1 40x25 Colore

AL = 2 80x25 BW AL = 3 80x25 Colore

AL = 4 320x200 Colore AL = 5 320x200 BW

AL = 6 640x200 BW AL = 7 Monocromatico

AL = 13 16 colori media ris.EGA AL = 14 16 colori alta ris. EGA

AL = 15 grafica monocrom. EGA AL = 16 Super high graphics EGA

AL = 48 3270 graphics AL = 72 640x400 Olivetti

Un esempio di utilizzo :

#include <dos.h>

union REGS inregs, outregs;

setta()

{ inregs.h.ah = 0x00;

inregs.h.al = 0x01; /* Setta a 40x25 colore */

int86(0x10,&inregs,&outregs);

}

AH = 1 Setta il tipo di cursore

Permette di settare il tipo di cursore stabilendo la linea

d'inizio e quella di fine.

Cosa si intende per riga ?

Ogni carattere che compare sullo schermo e' costruito su una

matrice di punti nel seguente modo :

0 1 2 3 4 5 6 7

0 . . . X . . . .

1 . . X X X . . .

2 . X X . X X . .

3 . X X . X X . .

4 . X X X X X . .

5 . X X . X X . .

6 . X X . X X . .

7 . . . . . . . .

Il cursore viene costruito sulla stessa matrice.

Specificando in CH la posizione di partenza (riga) e in CL quella

di arrivo si puo' fare in modo che il cursore sia piu' o meno

evvidente (al limite si puo' fare in modo che non compaia a

video).

#include <dos.h>

union REGS inregs, outregs;

set_cur()

{ inregs.h.ah=0x01;

inregs.h.cl=0x13;

inregs.h.ch=0x13;

int86(0x10,&inregs,&outregs);

}

Nell'esempio precedente il cursore viene fatto sparire

specificando una linea di partenza e di arrivo in effetti non

esistente sulla matrice di costruzione del carattere.

Se utilizzate una funzione del genere nei Vs. programmi

ricordatevi, cosa che in verita io mi dimentico sempre di fare,

in uscita di resettare il cursore in modo visibile per non

ostacolare il normale lavoro a Dos.

AH = 2 Setta la posizione del cursore

Questo servizio permette di creare delle funzioni simili al

locate del Basic o al Gotoxy del Turbo Pascal.

DH e DL contengono in ordine il numero di riga e quello della

colonna dove deve essere posizionato il cursore.

BH contiene il numero della pagina sulla quale si vuole eseguire

il posizionamento.

Mediante il passaggio di argomenti e' possibile creare una

funzione come nel seguente esempio.

#include <dos.h>

union REGS inregs, outregs;

at(xpos,ypos)

int xpos,ypos;

{ inregs.h.dh=xpos;

inregs.h.dl=ypos;

inregs.h.bh=0x00;

int86(0x10,&inregs,&outregs);

}

AH = 3 Legge la posizione del cursore

BH in questo caso contiene il numero della pagina video a cui ci

si vuole riferire con il servizio.

In uscita i registri DH e DL contengono la posizione del cursore

e precisamente, in ordine, riga e colonna.

Il servizio restituisce anche il settaggio del cursore.

I registri CH e CL conterranno i valori relativi al settaggio del

cursore che sono praticamente quelli utilizzati dal servizio

AH = 1.

AH = 4 Riporta la posizione della penna ottica

Come avrete capito questo e' un servizio particolare in quanto

serve a leggere la posizione della penna ottica.

Un valore restituito in AH pari a 0 indica che la penna ottica

non e' stata attivata.

Nel caso in cui la penna sia attivata allora DH e DL conterranno

precisamente la riga e la colonna, CH la linea di raster mentre

BX il numero del pixel (0-319, 0-639).

AH = 5 Seleziona la pagina attiva

Parlando dei vari modi video avevamo detto che in modo testo era

possibile indirizzare piu' pagine.

Questo servizio ci permette di selezionare il numero di pagina a

da rendere attiva.

Chiaramente in modo grafico, avendo a disposizione solo una

pagina, non possiamo sfruttare questo servizio.

AL conterra' il numero della pagina desiderata e precisamente da

0 a 7 per i modi 40x25 mentre da 0 a 3 per quelli 80x25.

AH = 6 Esegue lo scroll in alto

Una funzione utile per eseguire delle cancellazioni di schermo

parziali o totali ci e' fornito dai due servizi che vedremo.

Il servizio AH = 6 e' uno dei due ed esegue lo scroll verso

l'alto della parte di video specificata dalle coordinate relative

all'angolo superiore a sinistra e quello inferiore a destra.

AL in questo caso conterra' il numero di righe che devono essere

fatte scorrere verso l'alto.

Un valore pari a 0 in AL fara' si che tutta la finestra venga

"scrollata".

CH e CL conterranno le coordinate dell'angolo in alto a sinistra,

riga e colonna, mentre DH e DL quelle relative all'angolo in

basso a destra.

BH conterra' il valore dell'attributo da visualizzare nelle righe

nuove vuote.

Vediamo come esempio una funzione che permetta il passaggio

come argomenti dei valori appena visti.

#include <dos.h>

#define SCREEN 0x10

union REGS inregs, outregs;

clear_up(x_u,y_u,x_d,y_d,n_r)

int x_u,y_u,x_d,y_d,n_r;

{ inregs.h.ah=0x06;

inregs.h.ch=y_u; /* Riga angolo in alto a sinistra */

inregs.h.cl=x_u; /* Colonna " " " " " */

inregs.h.dh=y_d; /* Riga angolo in basso a destra */

inregs.h.dl=x_d; /* Colonna " " " " " */

inregs.h.al=n_r; /* Numero di righe da scorrere */

inregs.h.bh=0x00; /* Attributo NERO */

int86(SCREEN,&inregs,&outregs);

}

Un esempio in cui il numero di righe su cui eseguire lo scroll

non corrisponde a tutta la finestra video lo riportero' con la

descrizione del servizio AH = 8.

AH = 7 Esegue lo scroll in basso

Mentre AH = 6 faceva scorrere il contenuto della finestra

specificata in alto questo esegue la stessa funzione ma con verso

di scorrimento opposto e cioe' verso il basso.

I registri utilizzati dal servizio sono gli stessi di quello

precedente.

Anche se inutile in quanto di fatto e' diverso solo il settaggio

del registro AH riporto un esempio di utilizzo.

#include <dos.h>

#define SCREEN 0x10

union REGS inregs, outregs;

clear_dw(x_u,y_u,x_d,y_d,n_r)

int x_u,y_u,x_d,y_d,n_r;

{ inregs.h.ah=0x07;

inregs.h.ch=y_u;

inregs.h.cl=x_u;

inregs.h.dh=y_d;

inregs.h.dl=x_d;

inregs.h.al=n_r;

inregs.h.bh=0x00;

int86(SCREEN,&inregs,&outregs);

}

Mediante le due funzione dell' int 10H appena viste e' possibile

fare degli scherzetti allo scopo di simulare, ad esempio, lo

scroll laterale a sinistra o a destra cosa normalmente non

facilitata dal BIOS.

Il main del programma ha uno scopo solo dimostrativo.

Guardate il seguente esempio.

#include <dos.h>

char far *scr1 = 0xB0000000; /* B8000000 Se su scheda CGA */

char far *scr2 = 0xB0000000; /* Idem come sopra */

union REGS inregs, outregs;

main()

{ int c,b;

for(c=0;c!=79;c++) {

scrolleft(1);

b = getch();

}

}

scrolleft(x)

int x;

{ int c;

x *= 2;

scr2 += x;

for(c=0;c!=4000-x;c++) {

*scr1 = *scr2;

++scr1;

++scr2;

}

scr1 -= 4000-x;

scr2 -= 4000;

clear(x);

}

clear(x_u)

int x_u;

{ inregs.h.ah=0x06;

inregs.h.ch=0;

inregs.h.cl=80-x_u;

inregs.h.dh=24;

inregs.h.dl=80;

inregs.h.al=0x00;

inregs.h.bh=0x00;

int86(0x10,&inregs,&outregs);

}

AH = 8 Riporta il carattere alla posizione del cursore

Questo servizio si mostra particolarmente utile in tutte le

situazioni in cui serve sapere quale carattere e' memorizzato

nella posizione in cui si trova il cursore.

L'unico parametro in ingresso e' il valore, settato in BH,

relativo alla pagina a cui ci si vuole riferire.

Anche in questo caso BH potra' contenere da 0 a 3 nel caso di un

modo video 80x25 mentre da 0 a 7 in modo 40x25.

In uscita AL contiene il codice del carattere mentre AH

l'attributo.

L'utilizzo di questo servizio e' indispensabile nel momento in

cui si vogliono, ad esempio, gestire delle finestre sul video

facendo in modo che il vecchio contenuto dello schermo non venga

perso ma al contrario ripristinato nell'istante in cui si esce

dalla procedura di gestione della finestra stessa.

Quali sono le applicazioni ?

Ad esempio si potrebbe abbinare ad un tasto il richiamo di un

help che invece di comparire sullo schermo cancellando il vecchio

testo presente faccia il tutto in una finestra che verra'

ricostruita con il testo memorizzato precedentemente.

Un esempio di programma per gestire una finestra video e' il

seguente.

/*

* Window.c salva in matrice tutti i caratteri presenti

* nella finestra specificata nei parametri passati alla

* funzione wind(x_up,y_up,x_dw,y_dw) dove :

*

* x_up e y_up sono le coordinate dell'angolo in alto a

* sinistra della finestra

*

* x_dw e y_dw sono le coordinate dell'angolo in basso

* a destra della finestra

*

* Dopo il richiamo di wind() la finestra compresa nelle

* coordinate specificate viene cancellata.

* La riscrittura del testo memorizzato viene eseguita

* mediante il richiamo della funzione scrivi().

* La funzione main() e' messa solo a scopo dimostrativo

* e non esegue alcun controllo sull'esattezza dei dati

* passati come coordinate.

*/

#include <stdio.h>

#include <dos.h>

#define SCREEN 0x10 /* Int 10H */

#define AT(x,y) printf("\33[%d;%dH",x,y) /* Macro */

union REGS inregs,outregs;

int flag, riga, xu, yu;

char matrice[25][80];

main() /* Solo dimostrativo. */

{ int risp;

flag=0;

set_cur();

wind(1,1,20,20);

AT(11,8);

puts("ESEMPIO"); /* Scrive ESEMPIO nella finestra e */

risp=getchar(); /* aver premuto un tasto ripristina */

scrivi(); /* il vecchio contenuto */

}

scrivi() /* Riscrive il contenuto della finestra */

{ register indice1, indice2;

indice2=0;

if(flag==0)

{ printf("\n\nNon c'e' nulla da stampare\n\n");

return;

}

for(indice1=yu;indice1!=yu+riga;indice1++)

{ AT(indice1,xu);

printf("%s", matrice[indice2]);

++indice2;

;

}

printf("\n\n");

}

wind(x_up,y_up,x_dw,y_dw) /* Cicli di lettura finestra */

int x_up,y_up,x_dw,y_dw;

{ register indice1, indice2;

int colonna;

riga=0;

xu=x_up;

yu=y_up;

for(indice1=y_up;indice1 != y_dw+2;indice1++)

{ colonna=0;

for(indice2=x_up;indice2 != x_dw+2;indice2++)

{ AT(indice1,indice2); /* Posiziona curs */

matrice[riga][colonna]=legge();

++colonna;

}

matrice[riga][colonna+1] = '\0';

++riga;

}

flag=1;

clear_up(x_up,y_up,x_dw,y_dw);

}

set_cur() /* Setta il cursore in modo che non si veda */

{ inregs.h.ah=0x01;

inregs.h.cl=0x13;

inregs.h.ch=0x13;

int86(0x10,&inregs,&outregs);

}

clear_up(x_u,y_u,x_d,y_d) /* Cancella zona video specificata */

int x_u,y_u,x_d,y_d;

{ inregs.h.ah=0x06;

inregs.h.ch=y_u;

inregs.h.cl=x_u;

inregs.h.dh=y_d;

inregs.h.dl=x_d;

inregs.h.al=0x00;

inregs.h.bh=0x00;

int86(SCREEN,&inregs,&outregs);

}

legge() /* Riporta il carattere letto alla posizione curs */

{ inregs.h.ah=0x08;

inregs.h.bh=0x00;

int86(SCREEN,&inregs,&outregs);

return(outregs.h.al);

}

La maggior parte delle funzioni utilizzate nell'esempio

precedente le avevamo gia' viste precedentemente con altri

servizi.

Il funzionamento del programma e' molto semplice.

Esistono due cicli for annidati che si interessano

rispettivamente della colonna e della linea.

La macro AT(x,y) servira' a posizionare il cursore utilizzando

come argomenti relativi ad x e a y i valori relativi alle

variabili utilizzate all'interno dei cicli for.

Ad ogni posizionamento del cursore corrisponde una lettura del

carattere mediante la funzione leggi() che e' quella in cui viene

sfruttato il servizio di cui stiamo parlando.

La matrice (matrice[25][80]) e' dimensionata per poter contenere

al limite tutto lo schermo (80 colonne per 25 righe).

La fase di meorizzazione del contenuto di una finestra, come ad

esempio quella utilizzata nel programma appena visto, e'

abbastanza lenta.

Un miglioramento, dal punto di vista della velocita', potrebbe

essere ottenuto mediante la scrittura delle funzione wind() e

legge() in assembler.

Senza doverle scrivere e' possibile fare produrre al compilatore

il testo relativo al codice e ottimizzarlo manualmente.

Sicuramente il fatto di riportare nuovamente il listato del

programma precedente con le due funzioni legge() e wind() scritte

in assembler appesantira' le dimensioni gia' eccessive di questo

file ma sicuramente chiarira' le idee a riguardo del discorso

fatto nei capitoli precedenti a sulla fase di link di un modulo

in assembler con uno in linguaggio C.

#include <stdio.h>

#include <dos.h>

#define SCREEN 0x10 /* Int 10H */

#define AT(x,y) printf("\33[%d;%dH",x,y) /* Macro */

extern legge(), wind(); /* Legge() e wind() sono esterne */

union REGS inregs,outregs;

int flag, riga, xu, yu;

char matrice[25][80];

main() /* Solo dimostrativo. */

{ int risp;

flag=0;

set_cur();

wind(1,1,20,20);

clear_up(1,1,20,20);

AT(11,8);

puts("ESEMPIO"); /* Scrive ESEMPIO nella finestra e */

risp=getchar(); /* aver premuto un tasto ripristina */

scrivi(); /* il vecchio contenuto */

}

scrivi() /* Riscrive il contenuto della finestra */

{ register indice1, indice2;

indice2=0;

if(flag==0)

{ printf("\n\nNon c'e' nulla da stampare\n\n");

return;

}

for(indice1=yu;indice1!=yu+riga;indice1++)

{ AT(indice1,xu);

printf("%s", matrice[indice2]);

++indice2;

;

}

printf("\n\n");

}

set_cur() /* Setta il cursore in modo che non si veda */

{ inregs.h.ah=0x01;

inregs.h.cl=0x13;

inregs.h.ch=0x13;

int86(0x10,&inregs,&outregs);

}

clear_up(x_u,y_u,x_d,y_d) /* Cancella zona video specificata */

int x_u,y_u,x_d,y_d;

{ inregs.h.ah=0x06;

inregs.h.ch=y_u;

inregs.h.cl=x_u;

inregs.h.dh=y_d;

inregs.h.dl=x_d;

inregs.h.al=0x00;

inregs.h.bh=0x00;

int86(SCREEN,&inregs,&outregs);

}

Le due funzioni in assembler invece sono :

TITLE wind

; NAME wind.asm

.287

_TEXT SEGMENT BYTE PUBLIC 'CODE'

_TEXT ENDS

_DATA SEGMENT WORD PUBLIC 'DATA'

_DATA ENDS

CONST SEGMENT WORD PUBLIC 'CONST'

CONST ENDS

_BSS SEGMENT WORD PUBLIC 'BSS'

_BSS ENDS

DGROUP GROUP CONST, _BSS, _DATA

ASSUME CS: _TEXT, DS: DGROUP, SS: DGROUP, ES: DGROUP

EXTRN _printf:NEAR

EXTRN _flag:WORD

EXTRN _riga:WORD

EXTRN _xu:WORD

EXTRN _yu:WORD

EXTRN _matrice:BYTE

;

; Data Segment (DS)

;

_DATA SEGMENT

ESCAPE DB 1BH, '[%d;%dH', 00H

_DATA ENDS

;

; Code Segment (CS)

;

_TEXT SEGMENT

;

; Funzione wind()

;

PUBLIC _wind

_wind PROC NEAR

push bp

mov bp,sp

push di

push si

mov _riga,0

mov ax,[bp+4] ;x_up

mov _xu,ax

mov ax,[bp+6] ;y_up

mov _yu,ax

mov si,ax

jmp SHORT LAB2

L60: mov WORD PTR [bp-2],0 ;colonna

mov di,[bp+4] ;x_up

jmp SHORT LAB1

L64: push di

push si

mov ax,OFFSET DGROUP:ESCAPE ; macro AT(x,y)

push ax

call _printf

add sp,6

call _legge

mov cx,ax

mov ax,80

imul _riga

mov bx,ax

add bx,[bp-2] ;colonna

mov BYTE PTR _matrice[bx],cl

inc WORD PTR [bp-2] ;colonna

inc di

LAB1: mov ax,[bp+8] ;x_dw

add ax,2

cmp ax,di

jne L64

mov ax,80

imul _riga

mov bx,ax

add bx,[bp-2] ;colonna

mov BYTE PTR _matrice[bx+1],0

inc _riga

inc si

LAB2: mov ax,[bp+10] ;y_dw

add ax,2

cmp ax,si

jne L60

mov _flag,1

pop si

pop di

mov sp,bp

pop bp

ret

_wind ENDP

;

; Funzione legge()

;

PUBLIC _legge

_legge PROC NEAR

push bp

mov bp,sp

mov ah,08H

mov bh,00H

int 10H

add sp,6

sub ah,ah

mov sp,bp

pop bp

ret

_legge ENDP

_TEXT ENDS

END

In questo caso le funzioni in linguaggio C potranno essere

compilate con il comando

MSC /Ox window.c

Verra' in questo modo prodotto un file con il codice oggetto

chiamato window.obj.

La compilazione del source assembler avverra' invece con

MASM wind.asm

In questo caso il file prodotto sara' chiamato wind.obj.

Ora non resta che unire i due moduli utilizzando il link.

LINK window.obj wind.obj , window.exe;

Il prodotto sara' il file eseguibile window.exe.

A seguito di un analisi obbiettiva del programma, compreso quello

con la funzione in assembler, mettera' alla luce una notevole

lentezza in fase di meorizzazione del contenuto della finestra

specificata.

Il seguente programma mostra come la velocita' puo' essere

incrementata in modo notevole mediante la lettura diretta di

tutto lo schermo video senza l'utilizzo di interrupt.

Sostituendo ??? con le iniziali della scheda CGA oppure della

Hercules (HER) si fa' in modo che il programma funzioni su

ambedue senza nessuna modifica.

/* window.c

Gestione finestra virtuale mediante memorizzazione

conenuto screen con lettura diretta della memoria

video.

*/

#include <stdio.h>

#include <dos.h>

#define AT(x,y) printf("\33[%d;%dH", x, y)

#define ??? /* ??? = CGA o HER */

#ifdef HER

char far *screen = 0xB0000000;

#endif

#ifdef CGA

char far *screen = 0xB8000000;

#endif

char matrix[25][80];

char attrib[25][80];

union REGS inregs, outregs;

main()

{ int c;

register riga, colonna;

set_cur();

for(riga=0;riga != 25;riga++)

for(colonna=0;colonna != 80;colonna++) {

matrix[riga][colonna] = *screen;

++screen;

attrib[riga][colonna] = *screen;

++screen;

}

clear();

AT(14,25);

puts(" Hello! ");

c = getch();

screen -= 4000;

for(riga=0;riga != 25;riga++)

for(colonna=0;colonna != 80;colonna++) {

*screen = matrix[riga][colonna];

++screen;

*screen = attrib[riga][colonna];

++screen;

}

AT(24,1);

}

clear()

{ inregs.h.ah = 0x07;

inregs.h.ch = 0x08;

inregs.h.cl = 0x08;

inregs.h.dh = 0x12;

inregs.h.dl = 0x30;

inregs.h.al = 0x00;

inregs.h.bh = 112;

int86(0x10, &inregs, &outregs);

}

set_cur()

{ inregs.h.ah=0x01;

inregs.h.cl=0x13;

inregs.h.ch=0x13;

int86(0x10,&inregs,&outregs);

}

L'istruzione screen -= 4000 riposiziona il puntatore all'origine

prima di riutilizzarlo per riscrivere il vecchio contenuto dello

schermo.

Elaborate la funzione e adattatela per i vostri scopi specifici.

Anche in questo caso un miglioramento potrebbe derivare dal fatto

di scrivere in assembler alcune parti dello stesso anche se

sinceramente, essendo gia' la velocita' notevole, penso che non

sia il caso.

Come ho gia' detto precedentemente, l'uso di macro per il

controllo dello schermo possiede il vantaggio di essere comoda da

scrivere ma ha anche lo svantaggio di pretendere l'ansi.sys

specificato come device nel config.sys.

Con questo ho concluso il discorso relativo alla funzione svolta

dal servizio 8 dell'interrupt 10H sperando di non essermi

dilungato eccessivamente.

Vediamo un altro servizio.

AH = 9 Scrive un carattere nella posizione del cursore

La funzione scrive a partire dalla posizione corrente del cursore

un numero n di volte il carattere specificato nel registro AL.

AH conterra' il numero del servizio e precisamente 9, BL

l'attributo del carattere che deve essere scritto, BH il numero

della pagina e CX il numero di volte che questo deve essere

stampato.

Il cursore, di fatto, non viene spostato ma il carattere, se in

CX viene specificato un numero maggiore di 1, viene scritto tante

volte, a partire dalla posizione attuale, quante sono quelle

indicate da CX.

Ripeto che il servizio pur stampando in posizioni consecutive non

muove il cursore.

Il seguente esempio e' abbastanza banale in quanto richiede da

tastiera il carattere e il numero di volte che deve ripetere la

stampa.

Un applicazione piu' utile potrebbe essere, ad esempio, per la

stampa ripetuta di caratteri che devono comporre la cornice di

una maschera.

#include <stdio.h>

#include <dos.h>

union REGS inregs, outregs;

main()

{ int car, num;

printf("\n\n\nQuale carattere vuoi : ");

car = getche();

printf("\nQuante volte lo vuoi : ");

scanf("%d", &num);

puts("\n\n");

st_car(car,num);

}

st_car(c,n)

int c, n;

{ inregs.h.ah = 0x09; /* Servizio */

inregs.h.bh = 0x00; /* Pagina */

inregs.x.cx = n; /* Numero volte */

inregs.h.al = c; /* Carattere */

inregs.h.bl = 0x03; /* Attributo */

int86(0x10, &inregs, &outregs);

}

L'attributo specificato in BL e' calcolato mediante le

combinazioni che avevamo visto parlando dei bytes di attributo

nei paragrafi precedenti.

Nel caso che lo screen sia settato in modo grafico il valore in

BL corrispondera' al colore del o dei caratteri.

AH = 0AH Scrive un carattere alla posizione del cursore

E' praticamente identico al servizio precedente solo che in

questo caso non e' possibile settare l'attributo in BL.

AH = 0BH Setta la tavolozza dei colori

Mediante questo servizio e' possibile selezionare la tavolozza

dei colori dai cui verranno presi quelli da utilizzare come

background e come colore carattere.

BH contiene l' ID della tavolozza (0 - 127, 0 - 1 per la grafica

320x200).

BL invece specifica il colore da utilizzare con l' ID specificato

in BH.

Di fatto il Technical Reference avverte che con l'attuale scheda

CGA l'ultimo settaggio ha significato solo in risoluzione

320x200.

Selezionando come ID la tavolozza 0, in BH, i colori disponibili

settando in BL 0 sono verde, rosso e giallo.

Un valore pari a 1 in BL invece ci mettera' a disposizione

azzurro, magenta e bianco.

AH = 0CH Scrive un punto (pixel).

Il registro DX contiene in questo servizio il numero della riga

mentre CX il numero della colonna dove deve essere stampato il

pixel.

AL contiene il valore del colore.

Se il bit piu' significativo di AL, il numero 7, e' a 1 allora

il colore specificato si combina mediante un' operazione di or

esclusivo (XOR) con il colore gia' presente.

AH = 0DH Legge la posizione di un pixel

Mentre il servizio precedente stampava un punto questo ne riporta

la posizione.

In ingresso DL contiene la riga mentre CX la colonna del punto di

cui si vuole riportato il colore in AL.

AH = 0EH Scrive un carattere in modo teletype

Il servizio 0EH scrive scrive in modo TTY dei singoli caratteri

sullo schermo.

Il codice del carattere e' specificato nel registro AL mentre BL

contiene il valore del colore.

Quest' ultimo valore deve essere specificato se si lavora in modo

grafico.

Oltre a stampare il carattere viene fatto anche avanzare il

cursore.

AH = 0FH Riporta il settaggio corrente dello schermo

AL riporta il valore relativo al settaggio del video.

Per l'identificazione del tipo guardate i valori utilizzati per

la funzione di selezione del modo video che abbiamo visto con il

servizio 00H di questo interrupt.

BH restituisce il numero della pagina attiva mentre AH il numero

di caratteri per riga posseduto dallo specifico tipo di

settaggio screen.

Con questo abbiamo terminato il discorso relativo ai servizi

offerti dall'interrupt 10H.

Nel caso del BIOS dell' AT IBM esistono vari interrupts che

possiedono delle funzioni aggiuntive rispetto a quelli del PC.

In questo caso so' che legata all'INT 10H esiste un ulteriore

funzione che permette di stampare una stringa.

Purtroppo il Technical Reference di cui sono in possesso non fa'

nessun riferimento a questa e quindi non saprei darne

informazioni.

Come nel caso degli interrupts visti precedentemente colgo

l'occasione fornitami dall' int 10H per parlare anche delle

locazioni di memoria e dei registri del controller che si

interessano del video.

Ho utilizzato gli interrupts del BIOS per fare un raggruppamento

di tutti gli argomenti che riguardano determinate periferiche.

La locazione 449H contiene la modalita' CRT del BIOS.

Le funzioni dell'int 10H (00H e 0FH) per settare un determinato

modo video o per avere di ritorno l'attuale utilizzano questa

locazione dove : 0 = 40x25 BW

1 = 40x25 C

2 = 80x25 BW

3 = 80x25 C

4 = 320x200 C

5 = 320x200 BW

6 = 640x200 BW

7 = Monocromatico

La locazione 44AH contiene il numero di colonne

28H = 40 colonne

50H = 80 colonne

Le dimensioni del buffer del video sono contenute alla locazione

44CH.

Le dimensioni possibili sono quelle che abbiamo riportato

precedentemente e cioe' 800H per il modo testo a colori, 4000H

per la grafica a colori ecc.

La locazione 44EH contiene l'offset della pagina video attiva che

viene calcolato aggiungendo all'offset 0H un multiplo della

lunghezza video contenuta in 44CH.

La locazione dalla 450H alla 45FH si occupano del mantenimento

della posizione del cursore per ogni pagina video.

Il formato e' di due byte che contengono rispettivamente la

colonna e la riga.

Ad esempio le locazioni 450H e 451H sono relative alla pagina 0,

le 452H e 453H alla pagina 1 ecc. fino ad arrivare alle 45EH e

45FH relative alla pagina 7.

Il servizio 01H dell'interrupt 10H (Set Cursor Type) utilizza le

locazioni 460H e 461H per determinare il tipo di cursore.

Il contenuto e' il seguente :

460H = Linea finale del cursore

461H = bit 5 se 0 il cursore e' visualizzato

bit 4-0 linea iniziale cursore

I servizi per settare la pagina attiva dell'interrupt 10H (05H) e

per conoscerla (OFH) utilizzano la locazione 462H.

Il chip del controller video e cioe' 6845 mediante l'utilizzo di

vari registri permette di eseguire i vari settaggi dei parametri

screen senza doversi supportare al BIOS.

L'esistenza e l'utilizzo da parte di un programma di questi rende

insormontabile l'incompatibilita' del software scritto per CGA

con quello per Hercules.

Se un programma si supportasse per il settaggio dei modi video e

per il controllo di questo sull'interrupt 10H allora non sarebbe

un problema eccessivamente grosso l'adattamento su scheda

Hercules in quanto basterebbe, in questo caso, modificare la

routine di servizio dell'interrupt stesso per rendere compatibile

il software per CGA con la scheda Hercules.

Che io sappia solo le routine grafiche del Lisp si supportano per

tutte le funzioni sull'int 10H.

Incominciamo a vedere l'utilizzo che viene fatto dei registri del

controller video.

I registri 3B4H, relativo al controller monocromatico, e il 3D4H

permettono la selezione di uno dei registri a cui e' possibile

accedere mediante la porta 3B5H (monocromatico) e 3D5H (CGA).

Infatti le porte 3B5H e 3D5H permettono di controllare mediante

un registro o l'altro varie caratteristiche video e precisamente

le seguenti :

Registro Scopo

---------------------------------------------

0-3 Caratteristiche orizzontali

del video quali caratteri/linea

4-9 Caratteristiche verticali quali

linee visualizzate ecc.

10 Linea iniziale cursore

11 Linea finale cursore

I seguenti registri sono sempre accessibili mediante le porte 3B5

e 3D5 ma sono solo leggibili.

Registro Scopo

---------------------------------------------

12 Indirizzo alto della memoria

dell'adattatore

13 Indirizzo basso della memoria

dell'adattatore

14 Indirizzo alto della posizione

del cursore

15 Indirizzo basso della posizione

del cursore

Ad esempio de volessimo settare un valore nel registro 10 delle

porte 3B5H o 3D5H dovremmo specificare 10 in 3B4H o in 3D4H.

Il registro 3B8H, nel caso del video monocromatico, costituisce

il registro di controllo delle modalita'.

0x3B8

+---+---+---+---+---+---+---+---+

: 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+

: : :

: : 80x25 Testo

: Abilita segnale video

Abilita lampeggiamento

Come avrete potuto osservare non tutti i bit della porta vengono

utilizzati.

Nel caso della porta con uguale significato ma riferita al

controller della CGA, la 3D8, i bit utilizzati sono i seguenti :

0x3D8

+---+---+---+---+---+---+---+---+

: 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+

: : : : : :

: : : : : Testo 80x25

: : : : Selezione modalita'

: : : : grafica

: : : Selezione modalita' BW

: : Abilita segnale video

: 640x200

1 = lampeggiamento

0 = sfondo per 16 colori

I registri 3BAH e 3DAH corrispondono ai registri di stato

relativi alla scheda monocromatica e a quella della CGA.

Il bit 3 a stato 1 indica la scansione verticale, il bit 0 a 0

indica l'abilitazione del video mentre a 1 la scansione

orizzontale.

Per ora concludo l'argomento relativo alla gestione del video

riproponendomi di riportare qualche esempio piu' avanti.

Parlando dei vari interrupts del BIOS tralasciero' il 15H in

quanto e' relativo al controllo del registratore a cassette che

avrebbe dovuto essere disponibile con le prime versioni del PC

IBM ma di cui, sinceramente, non ho mai avuto alcuna notizia

sulla possibilita' o meno di reperirlo in commercio.

Esistono particolari situazioni in cui e' necessario sapere con

quale tipo di scheda si ha a che fare.

L'esempio seguente mostra un programma completo che puo' essere

adattato alle vostre esigenze mediante l'opportuna rimozione

della funzione main.

/* Ritorna : 1 = EGA

* 2 = Monocromatico

* 3 = CGA

*/

#include <stdio.h>

char far *ega = 0x00400087;

char far *alt = 0x00400010;

main()

{ int c;

puts("\n\n\n");

c = scheda();

switch(c) {

case 1:

puts("Scheda EGA");

break;

case 2:

puts("Monocromatico");

break;

default:

puts("CGA");

break;

}

}

scheda()

{ if(*ega != 0)

return(1);

else

if((*alt &= 48) == 48)

return(2);

else

return(3);

}

Giocando su alcuni registri o mediante l'accesso diretto alla

memoria e' possibile creare routines particolari.

Nel seguente esempio il contenuto dello schermo viene fatto

scivolare a tipo di "serpente" sulle schermo fino alla sua

completa sparizione.

char far *scr1 = 0xB0000000; /* B8000000 se su scheda CGA */

char far *scr2 = 0xB0000002; /* B8000002 se su scheda CGA */

union REGS inregs, outregs;

main()

{ serp();

}

serp()

{ register c, b;

int d,n;

d = 2;

for(n=0;n!=25;n++) {

for(b=0;b!=80;b++) {

for(c=0;c!=4000-d;c++) {

*scr1 = *scr2;

++scr1;

++scr2;

}

scr1 -= 4000-d;

scr2 -= 4000-d;

}

d += 160;

}

}

Ho detto prima dell'esempio "giocando su alcuni registri".

Sul video colore grafica in modo testo abbiamo a disposizione 4 o

8 pagine video che possono essere attivate anche mediante i

servizi dell'interrupt 10H visti precedentemente.

Potremmo anche scrivere un testo distribuendolo su tutte le

pagine a nostra disposizione e, modificando mediante la porta

0x3D4 l'indirizzo di partenza del buffer video, eseguire un

scroll su queste.

Basterebbe incrementare l'indirizzo di partenza del buffer di una

riga per volta ovvero di 80 o 40 caratteri (bytes).

Le varie pagine video sono infatti consecutive come locazioni di

memoria.

Gli offset a partire dal segmento video sono i seguenti.

Pagina 40 Colonne 80 Colonne

0 0000H 0000H

1 0400H 0800H

2 0800H 1000H

3 0C00H 1800H

4 1000H

5 1400H

6 1800H

7 1C00H

Una routine in assembler, facilmente traducibile in C, per

eseguire lo scroll di una riga sarebbe :

mov bx,b800H

add bx,80 ;incremento 1 linea

mov dx,3d4H ;porta 3d4 in DX

mov al,12 ;indirizzo registro 12

out dx,al

inc dx, ;porta 3d5H

mov al,bh ;MSB indirizzo partenza

out dx,al

dec dx ;porta 3d4H

mov al,13 ;indirizzo registro 13

out dx,al

inc dx ;porta 3d5H

mov al,bl ;LSB indirizzo partenza

out dx,al

Una schematizzazione dello scroll e' il seguente.

Buffer

Video

+---------+ Schermo visualizzato

: Riga 1: - - +---------+

: Riga 2: : :

: Ecc. : : :

Pagina 0 +---------+ : :

: Riga 1: - - +---------+

: Riga 2:

: Ecc. :

+---------+

: :

Potrete utilizzare una routine del tipo visto per applicazioni,

ad esempio, in word processor o editor.

Per la scrittura in altre pagine diverse da quella attiva vedete

i servizi del BIOS anche se non sarebbe scorretta una scrittura

diretta nella memoria di cui conoscete gli offset.

Determina dimensione memoria (INT 12H)

Questo interrupt permette di avere di ritorno in AX il numero di

blocchi da 1 K di memoria disponibile.

Un esempio :

#include <stdio.h>

#include <dos.h>

union REGS inregs, outregs;

main()

{ int86(0x12, &inregs, &outregs);

printf("\n\nMemoria vista nel sistema : %d KBytes\n\n",

outregs.x.ax);

}

Il valore potrebbe essere anche letto dalla locazione di memoria

413H.

Questa locazione contiene anch'essa il numero di blocchi da

1 Kbytes.

Determinazione dei dispositivi installati (INT 11H)

Il servizio svolto dall'interrupt 11H e' quello di restituire nel

registro AX il numero delle periferiche installate sul PC.

I sedici bit sono interpretabili nel seguente modo :

+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

:15 :14 :13 :12 :11 :10 : 9 : 8 : 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

: : : : : : : : : : : : : : : :

: : : : : : : : : : : : : : : (*)

: : : : : : : : : : : : : : NU

: : : : : : : : : : : : RAM piastra sist.

: : : : : : : : : : : : 0 0 = 16 K

: : : : : : : : : : : : 0 1 = 32 K

: : : : : : : : : : : : 1 0 = 48 K

: : : : : : : : : : : : 1 1 = 64 K

: : : : : : : : : : Modo video iniziale

: : : : : : : : : : 0 0 = NU

: : : : : : : : : : 0 1 = 40x25 CGA

: : : : : : : : : : 1 0 = 80x25 CGA

: : : : : : : : : : 1 1 = 80x25 Monocrome

: : : : : : : : Numero dischetti se (*) = 1

: : : : : : : : 0 0 = 1

: : : : : : : : 0 1 = 2

: : : : : : : : 1 0 = 3

: : : : : : : : 1 1 = 4

: : : : : : : NU

: : : : Numero di RS 232 collegate (bit 9,10,11)

: : : Porta giochi collegata

: : NU

Numero di stampanti collegate (bit 14,15)

Il primo bit, quello contrassegnato da (*), e' a 1 se ci sono

collegate al sistema delle unita' a disco.

E' possibile, dopo aver richiamato l'interrupt, eseguire delle

operazioni di AND per testare ogni singolo bit allo scopo di

vedere se esistono collegate le periferiche di cui ci interessa

verificare l'esistenza.

Ad esempio, nel caso di un programma di comunicazione, potrebbe

rivelarsi utile, prima di rendere attive le sue funzioni, testare

la presenza della porta a cui il programma stesso tentera' di

riferirsi.

Nel caso che una determinata opzione permetta di configurare il

tutto sulla porta COM2, sarebbe inutile proseguire se questa non

fosse presente nel sistema.

Il seguente esempio si riferisce appunto al caso appena riportato

in quanto sfruttando l'int 11H determina il numero di seriali

collegate al PC.

Dato che il registro AX puo' essere suddiviso in parte alta (AH)

e parte bassa (AL) ho eseguito un operazione di shift di una

posizione a destra del contenuto di AH, mediante l'operatore >>,

e poi mediante un operazione di AND ho isolato i primi tre bit

che sono appunto quelli che indicano, dopo lo shift di una

posizione, il numero di porte RS 232 collegate al sistema.

#include <stdio.h>

#include <dos.h>

#define INT_RS_232 0x11

union REGS inregs, outregs;

main()

{ int porte;

porte = rs_232();

printf("\n\nNumero seriali = %d", porte);

}

rs_232()

{ int num_porte;

int86(INT_RS_232,&inregs,&outregs);

num_porte=(outregs.h.ah >> 1) & 7;

return(num_porte);

}

Alcuni dei bit che sono stati segnati come NU (Non Utilizzati) in

effetti non hanno alcun significato per il PC mentre lo

acquistano in caso di un AT.

L'elenco completo dell' int 11H e' il seguente:

Bit Significato

-----------------------------------------------------------------

0 Settato se ci sono floppy installati

1 Non usato (PC)

Coprocessore matematico installato (AT)

4 - 5 Settaggio iniziale video

1 = 40x25 CGA

2 = 80x25 CGA

3 = 80x25 Monocromatico

Bit Significato

-----------------------------------------------------------------

6 - 7 Numero di dischetti (Aggiungere 1)

9 - 11 Numero RS 232

12 Settato se c'e' la porta games (PC)

Non usato (AT)

13 Non usato (PC)

Modem interno installato (AT)

14 - 15 Numero stampanti

Per scrivere software in base al tipo di sistema utilizzato e'

possibile vedere alla locazione FFFF:000E il modello.

Il seguente esempio mostra come sia possibile individuare se il

sistema su cui gira un programma e' un PC, un XT o un AT.

/*

* TYPEPC.C

*/

#include <stdio.h>

unsigned char far *var = 0xFFFF000E;

main()

{ switch(*var) {

case 252:

puts("AT o compatibile");

break;

case 254:

puts("XT o compatibile");

break;

case 255:

puts("PC o compatibile");

break;

default:

break;

}

} Setta e legge l'orologio di sistema (INT 1AH)

Durante la trattazione delle funzioni del compilatore avevamo

visto alcune funzioni che riportavano informazioni sul tempo.

L'interrupt 1AH permette di settare l'orologio di sistema o di

leggerlo.

I servizi svolti, selezionabili sempre mediante il solito

registro AH, sono due.

AH = 0 Legge l'orologio

CX contiene, dopo l'interrupt, la parte alta del conteggio mentre

DX quella bassa.

AL riporta 0 se non sono passate 24 ore dall'ultima lettura.

Il Technical Reference specifica che l'orologio ticchetta ad una

frequenza pari a 1.193.180/65.536 e cioe' circa 18.2 volte al

secondo.

Le ore, i minuti e i secondi possono essere calcolati nel

seguente modo : ORE = CONTEGGIO/65543

RESTO = CONTEGGIO mod 65543

MINUTI = RESTO/1092

RESTO = RESTO mod 1092

SECONDI = RESTO/18.21

Ora, dato che i valori del clock si trovano in due registri

separati, si presenta il problema di unirli per avere il valore

del conteggio all'interno di una variabile long per poter

eseguire i calcoli indicati.

Per eseguire questa funzione ho trasformato, nell'esempio

seguente, la parte alta e quella bassa del valore in stringhe in

modo di potergli fare un operazione di concatenamento.

Successivamente la stringa cosi' ottenuta viene trasformata

nuovamente in long.

#include <stdio.h>

#include <math.h>

#include <dos.h>

union REGS inregs, outregs;

main()

{ int ore, minuti, secondi, resto;

unsigned long hight, low, result;

char lo[8], all[16];

inregs.h.ah = 0x00;

int86(0x1A,&inregs,&outregs);

hight = outregs.x.cx;

low = outregs.x.dx;

ltoa(hight,all,10);

ltoa(low,lo,10);

strcat(all,lo);

result = atol(all);

ore = result/65543;

resto = result % 65543;

minuti = resto/1092;

resto %= 1092;

secondi= resto/18;

printf("%02d:%02d:%02d", ore, minuti, secondi);

}

La funzione apparira' ostica e non sensata in quanto con il

Microsoft C esistono, come abbiamo visto, funzioni che svolgono

questo compito senza il bisogno di fare una cosa del genere.

La lettura dei cicli di orologio potrebbe verificarsi utile per

altre funzioni.

AH = 1 Setta il conteggio del clock

Mentre nella funzione precedente CX e DX riportavano in uscita il

valore del contatore di clock in questo caso accetteranno in

ingresso i valori settati dall'utente prima che venga richiamato

l'interrupt. Stampa dello schermo (INT 5H)

Il richiamo di questo interrupt fa si che quello che e' sullo

schermo venga stampato allo stesso modo come se si battesse da

tastiera Shift-PrtSc.

Boot strap loader (INT 19H)

La fase di bootstrap e' costituita dall'esecuzione della seguente

sequenza.

1 - Il sistema cerca di leggere il dischetto e se trova il

sistema operativo lo carica a partire dall'indirizzo

0000:7C00.

Nel caso che il dischetto non sia presente :

2 - Cerca di leggere dal disco fisso.

Se anche su questo la ricerca risulta negativa passa a:

3 - Il controllo e' passato, se presenti le ROM del basic, al

basic residente.

Anche in questo caso poco da dire.

Basta richiamare l'interrupt con la funzione int86().

Con questo ritengo conclusa la parte relativa al controllo

dell'hardware e al BIOS.

Nella seconda parte vedro' di esporre le funzioni svolte dai vari

interrupt del Dos e quindi di approfondire concetti non trattati

qui come ad esempio la FAT, il PSP e altre cose del genere.

Interrupts DOS

Durante la trattazione precedente avevo suddiviso i vari tipi di

interrupts in harware, del BIOS e del DOS.

Gli interrupts hardware sono costituiti da precise richieste che

vengono fatte dalle varie periferiche affacciate sul bus di

controllo.

Quelli del BIOS si incaricano invece della gestione dell'

hardware mediante richieste software.

Gli interrupt che vedremo ora sono quelli del DOS.

Il primo tipo di interrupts, come abbiamo visto, possono solo

essere mascherati in modo di farli ignorare al processore.

Con gli ultimi due invece possiamo modificare le routine software

di servizio in modo da adattarli ai nostri scopi.

Gli interrupt del DOS sono dedicati a scopi orientati piu' alla

gestione dei file e dei programmi che verso l'hardware del

sistema.

Durante la trattazione di questi interrupts vedremo anche i

concetti legati agli argomenti a cui questi si interessano.

Vedremo i concetti realtivi alla FAT, al PSP, al FCB e al Dos

Critical Flag, solo per portare alcuni esempi.

Nei paragrafi precedenti avevo utilizzato i vari interrupt che si

interessavano a determinate periferiche per raggruppare sotto

questi anche le porte e le locazioni di memoria che svolgevano

particolari compiti o che contenevano informazioni relative a

queste.

Nei capitoli seguenti utilizzero' lo stesso metodo per trattare i

concetti legati alle argomentazioni relative ai servizi degli

interrupts stessi.

Sono da considerarsi interrupts del DOS i seguenti :

20H - Program terminate

21H - Function request

22H - Terminate address

23H - Ctrl-break exit address

24H - Critical error handler vector

25H - Absolute disk read

26H - Absolute disk write

27H - Terminate but stay resident

2FH - Printer

L'interrupt piu' importante, quello che tratteremo per primo, e'

il 21H al quale sono collegati un grosso numero di servizi

svolti.

Due sole parole prima di iniziare a descrivere le varie funzioni

svolte da questo.

Come nel caso degli interrupts del Bios che svolgono piu' servizi

anche in questo caso la selezione della funzione viene fatta

mediante il registro AH.

Ricordo che le funzioni del compilatore Microsoft C che si

interessano all'interrupt 21H sono esattamente bdos(), intdos() e

intdosx(). Servizi interrupt 21H

00H - Program terminate

Questa call esegue la stessa funzione dell'interrupt 20H, che

vedremo piu' avanti.

Fate attenzione in quanto una chiamata a questo servizio non

chiude automaticamente gli eventuali files che avevate aperto

durante l'esecuzione del programma.

Il servizio non restituisce alcun valore.

01H - Keyboard input

La funzione legge un carattere in input e lo visualizza sullo

standard di output.

Il codice del carattere viene restituito in AL a patto che questo

sia un codice ASCII.

Nel caso che invece il valore restituito da AL sia 0 sara'

necessaria una successiva chiamata al servizio per la lettura del

codice secondario.

Infatti AL = 0 significa che il carattere battuto e' speciale

(vedi paragrafo relativo ai codici secondari).

02H - Display output

In questo sevizio il carattere inserito in DL viene visualizzato

sullo schermo.

03H - Auxiliary Input

Il servizio inputa un carattere da un dispositivo ausiliario

(AUX, COM1, COM2).

I parametri relativi possono essere settati mediante alcune

funzioni di interrupt che abbiamo visto precedentemente.

Il carattere e' ritornato nel registro AL.

04H - Auxiliary Ooutput

Mentre il servizio precedente inputava (termine inglese

italianizzato) questo manda in uscita sulla porta ausiliaria il

carattere specificato in DL.

Non ci sono registri che riportano valori di ritorno.

Il discorso relativo al settaggio della porta e' lo stesso fatto

per la funzione precedente.

05H - Printer Output

Invia il carattere specificato in DL sulla porta della stampante.

06H - Direct console I/O

In questo servizio i registri svolgono i seguenti compiti.

AH, come al solito, contiene l'identificatore del servizio e

precisamente 6.

Se DL contiene 0xFF allora AL ritorna il flag di zero che se e' a

0 indica la presenza di un dato pronto in ingresso.

Nel caso che il flag di zero sia a 1 allora significa che non e'

pronto nessun dato.

Se DL non e' 0xFF allora il codice sara' relativo a un dato che

deve essere inviato in uscita (caratteri da 0x00 a 0xFE).

Durante l'esecuzione di questo servizio il break e' sospeso.

07H - Direct Console Input Without Echo

Attende un carattere in input e lo restituisce in AL.

Come nel caso della call 01H anche in questo servizio se il

valore ritornato e' 0 significa che e' necessaria una successiva

lettura per avere il codice secondario del carattere.

08H - Console Input Without Echo

AL ritorna il carattere letto dalla tastiera senza visualizzarlo

sullo schermo.

09H - Print String

Fino a questo punto non ho riportato esempi in quanto le funzioni

dei servizi erano banali.

Anche in questo caso non e' che il tutto sia piu' complicato ma

e' in ogni caso meglio indicare come possano essere passati gli

indirizzi delle stringhe da stampare a DS:DX.

Prima di vedere l'esempio riporto l'utilizzo che viene fatto dai

registri in questo servizio.

Dicevo prima DS:DX.

DS conterra' il segmento in cui e' allocata la stringa mentre DX

il suo offset.

Un esempio simile lo avevo gia' riportato parlando delle funzioni

del linguaggio C per il richiamo dell'interrupt 21H.

#include <dos.h>

union REGS inregs, outregs;

struct SREGS segregs;

char var[] = "Pippo$";

char far *var_2 = var;

main()

{ inregs.h.ah = 0x09;

segregs.ds = FP_SEG(var_2);

inregs.x.dx = FP_OFF(var_2);

intdosx(&inregs,&outregs,&segregs);

}

La stringa che deve essere stampata a video deve terminare con il

carattere '$' (codice ASCII 24H).

0AH - Buffered Keyboard Input

La call 0AH permette di eseguire un input da tastiera

bufferizzato.

DS:DX in questo caso puntano al buffer.

Il primo byte del buffer indica al DOS quanti bytes dovra'

accettare in input mentre il secondo verra' utilizzato per

ritornare, dopo che e' stato battuto il tasto CR (ASCII 13), il

numero di caratteri effettivamente letti.

In questo ultimo valore non e' incluso il carattere CR benche'

questo venga collocato all'interno del buffer.

0BH - Check Standard Input Status

Questo servizio serve solo a testare se l'input da tastiera e'

pronto o meno.

AL ritorna 0xFF se un carattere e' disponibile mentre 0x00 in

caso contrario.

0CH - Clear Keyboard Buffer and Invoke a Keyboard Function

La funzione svuota il buffer di tastiera e chiama una funzione.

AL contiene il numero della funzione da chiamare.

Fate attenzione in quanto i valori ammessi, relativi alle call,

sono solo 01H, 06H, 07H, 08H e 0AH.

0DH - Disk Reset

Vengono chiusi tutti i buffer dei file.

Anche per questo servizio bisogna fare attenzione in quanto un

eventuale file modificato e non chiuso non verrebbe

salvato automaticamente.

Prima di eseguire la call e' conveniente chiudere tutti i files

aperti.

0EH - Select Disk

DL contiene il numero di riferimento del drive selezionato (0=A:,

1=B:, ecc.)

AL riporta in uscita il numero dei drive presenti.

0FH - Open File

In questa funzione salta fuori, per la prima volta, un argomento

che non avevamo trattato prima d'ora e precisamente quello

relativo al FCB (File Control Block).

Il file control block e' un area di 44 bytes che il DOS utilizza

per contenere le informazioni relative al file utilizzato.

Possiamo suddividere il FCB in due blocchi uno di 7 e uno di 37

bytes.

Il primo blocco, quello di 7 bytes, viene utilizzato solo in casi

particolari come ad esempio in quello in cui lavoriamo su file

hidden o di sistema.

Il blocco piu' lungo, quello da 37 bytes, viene invece utilizzato

tutte le volte per mantenere dati quali il nome del file, le

dimensioni e altri che ora vedremo mediante una schematizzazione.

Il primo blocco viene normalmente chiamato Extended File Controll

Block mentre il secondo Standard File Controll Block.

Vediamo lo schema schema riportato dal Technical Reference :

+---------+----------------------------------+---------+

: FFH : Zeri :Attributi:

+--------+---------+----------------------------------+---------+

: Drive : Nome file (8 bytes) o nome device :

+--------+---------------------------------+----------+---------+

: : Estensione :Curr.block: Dimens. :

+--------+---+--------------+--------------+----------+---------+

:Dimen. low :Dimens. hight : Data : :

+------------+--------------+--------------+ :

: Riservato per uso del DOS :

+------------+--------------+----------------+------------------+

:Curr.Record :Random rec low:Random rec hight:

+------------+--------------+----------------+

Vediamo ora di descrivere ogni campo del FCB.

Spiazzamento Dimensione Note

--------------+----------------+---------------------------------

-7 1 Flag di presenza extended FCB.

E' presente se contiene FFH.

-6 5 Contiene tutti 0 (00000).

-1 1 Attributo del file

0 1 (*1*) Numero del drive

1 8 Nome del file giustificato a

sinistra o nome device.

9 3 Estensione del file giustificata

a sinistra.

12 2 Numero blocco corrente

14 2 Dimensioni record

16 4 Dimensione del file

20 2 (*2*) Data del file

22 10 Riservati al DOS

32 1 Numero del record (0-127)

33 4 Numero record casuale

Come avrete notato ho contrassegnato due campi con (*x*) per

potergli aggiungere qualche nota.

A riguardo del numero del drive (*1*) e' necessaria fare una

precisazione.

Il numero di riferimento al drive prima dell'apertura e'

0 - Drive di default

1 - Drive A

2 - Drive B

ecc.

Dopo l'apertura i riferimenti possono essere

1 - Drive A

2 - Drive B

ecc.

Parlando della data (*2*) bisogna specificare il formato di

questa all'interno dei due bytes.

+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

:15 :14 :13 :12 :11 :10 : 9 : 8 : 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

: : : : : : : : : : : : : : : :

a a a a a a a m m m m g g g g g

dove a = anno (1980-2099)

m = mese (1-12)

g = giorno (1-31)

Un discorso si puo' fare anche a riguardo degli attributi di un

file. 00H - File normale

01H - Indica che il file e' marcato in sola lettura

02H - Indica un file hidden ovvero nascosto

04H - Indica un file di sistema

08H - Etichetta di volume

10H - E' una sub-directory

20H - Archivio. E' usato da BACKUP e RESTORE.

In linguaggio C il FCB potrebbe essere dichiarato come

char fcb[44];

Sull'argomento ritorneremo in seguito portando alcuni esempio

d'uso.

Ritornando al discorso relativo alla call 0FH possiamo dire che i

registri DS:DX devono puntare a un FCB non aperto.

In che modo possiamo settare questi registri ?

Una soluzione ci e' offerta dalle macro FP_OFF e FP_SEG.

La seconad soluzione e' invece quella di utilizzare la funzione

del compilatore Microsoft bdos().

Se vi ricordate questa utilizzava come argomenti il numero della

call, il valore del registro DX e quello del registro AL.

In questo caso AL, in ingresso, non ci serve a nulla e quindi

potremo sfruttare la funzione nel seguente modo.

char fcb[44];

funz()

{ .......

.......

bdos(call,fcb);

}

Il registro AL conterra' di ritorno 00H se il file e' stato

aperto con successo mentre FFH se il tentativo di apertura e'

fallito.

10H - Close File

Mentre la funzione precedente apriva un file questa funzione lo

chiude.

DS:DX in questo caso puntano a un FCB aperto.

Il registro AL ritorna dopo la chiamata della funzione 00H se il

file e' stato trovato mentre FFH in caso contrario.

11H - Search for First Entry

La funzione cerca nella directory la prima corrispondenza al file

specificato.

La funzione se trova il file corrispondente restituisce in AL 00H

e piazza i dati relativi al file all'interno del FCB.

L'esempio che ora riportero' utilizza anche la funzione 12H

(Search for Next Entry) che vedremo tra poco.

Il programma e' una combinazione di varie funzioni che abbiamo

visto fino ad ora nei capitoli precedenti.

BYTEDIR2.C e' un piccolo file che provvede a mostrare in una

finestra il contenuto delle dir specificate.

Oltre a cio' calcola il numero dei files presenti e la loro

dimensione in bytes.

E' possibile quindi sapere sempre le dimensioni di ogni

directory presente sul disco di deafult.

Sintassi : bytearea \path [opzione]

Opzioni : nessuna, A

Nessuna > Il risultato e' mostrato solo a video.

Viene segnalato errore se il path specificato non

esiste.

Se si desidera operare sulla dir presente non e'

necessario specificare il percorso.

A > Il risultato oltre che su video viene scritto su un

file denominato AREA.STA.

Nel file AREA.STA verra' aggiunto alla fine il

risultato.

Esempio : Path \bin : 1 files per un totale di 2 bytes

#include <stdio.h>

#include <dos.h>

#include <types.h>

#include <stat.h>

#include <direct.h>

#include <time.h>

#define SCREEN 0x10

#define BELL putchar('\007')

#define ??? /* ??? = CGA o HER */

#ifdef HER

char far *screen = 0xB0000000;

#endif

#ifdef CGA

char far *screen = 0xB8000000;

#endif

char matrix[25][80];

char attrib[25][80];

union REGS inregs, outregs;

struct stat files;

FILE *stream;

long total, k_byte;

int tot;

char dir_c[80];

char dir[300][14];

char bfr[135];

char fcb[44];

char buffer[64],buf_ret[64];

main(argc,argv)

int argc;

char *argv[];

{ int i, ch_d;

int risp;

tot=1;

total=0;

set_cur();

getcwd(buf_ret,64);

if(argc>1)

ch_d=chdir(argv[1]);

wind();

cur_mv(1,0);

printf("BYTEDIR by Maxpirata \n");

printf("(C) Copyright 1987 Opus AL\n\n\n");

if(ch_d== -1)

{ BELL;

printf("\n\n\n\n\n\n\n ERRORE : Path errato\33[1A");

goto end_pr;

}

getcwd(buffer,64);

printf("Path : %s",buffer);

cur_mv(9,0);

puts("------[DIRECTORY]------");

cur_mv(24,0);

puts("-----------------------\33[1A");

while(fi(dir[tot++],0,"????????","???",16) != NULL);

--tot;

for(i=1;i!=tot;i++)

{ clear(11,1,22,25,0,1);

cur_mv(6,0);

stat(dir[i],&files);

total += files.st_size;

printf("Files : %3d \nBytes : %ld", i, total);

cur_mv(22,0);

printf(" %s >%8ld",dir[i],files.st_size);

}

if(argc>2)

{ switch(*argv[2])

{

case 'A':

case 'a':

chdir(buf_ret);

sta();

stream=fopen("area.sta","a");

fprintf(stream,"\nPath %-48s : %03d files %08ld

bytes",buffer,tot,total);

break;

default:

BELL;

break;

}

}

end_pr:

cur_mv(24,8);

puts("[PREMI]\33[1A");

chdir(buf_ret);

risp=getc(stdin);

scrivi();

}

fi(b,drive,name,ext,tipo)

char *b;

char drive;

char *name;

char *ext;

char tipo;

{ int y;

register unsigned int regax;

register int i;

static int first_entry = 0;

y=0;

fcb[0] = 255;

strncpy(&fcb[1], "\0\0\0\0\0", 5);

fcb[6] = tipo;

fcb[7] = drive;

strncpy(&fcb[8], name, 8);

strncpy(&fcb[16], ext, 3);

if (first_entry == 0)

{ first_entry = 1;

bdos(0x1A, bfr);

regax = bdos(0x11, fcb);

}

else

regax = bdos(0x12, fcb);

if ((regax & 0x00FF) == 0xff)

{ first_entry = 0;

return(NULL);

}

if(bfr[19] == 16)

{ b[13] = 1;

y=1;

}

else

b[13] = 0;

for(i = 0; i < 12;++i) b[i] = ' ';

for(i = 0; i < 8;++i) b[i] = bfr[i+8];

b[8] = '.';

if(y==0)for(i = 0;i < 3;++i) b[i+9] = bfr[i+16];

else

{ strcpy(&b[9],"<D>",3);

y=0;

}

}

clear(y_u,x_u,y_d,x_d,att,n_r)

int y_u,x_u,y_d,x_d,att,n_r;

{ inregs.h.ah=0x06;

inregs.h.ch=y_u;

inregs.h.cl=x_u;

inregs.h.dh=y_d;

inregs.h.dl=x_d;

inregs.h.al=n_r;

inregs.h.bh=att;

int86(SCREEN,&inregs,&outregs);

}

sta()

{ long ltime;

time(&ltime);

if((stream=fopen("area.sta","r")) == NULL)

{ fcloseall();

stream=fopen("area.sta","w");

fprintf(stream,"\nFile(s) e bytes nelle dir al %s\n\n",

ctime(&ltime));

fclose(stream);

}

}

set_cur()

{ inregs.h.ah=0x01;

inregs.h.cl=0x13;

inregs.h.ch=0x13;

int86(SCREEN,&inregs,&outregs);

}

cur_mv(x,y)

int x,y;

{ inregs.h.dh=x;

inregs.h.dl=y;

inregs.h.ah=0x02;

int86(SCREEN,&inregs,&outregs);

}

wind()

{ register riga, colonna;

for(riga=0;riga != 25;riga++)

for(colonna=0;colonna != 80;colonna++) {

matrix[riga][colonna] = *screen;

++screen;

attrib[riga][colonna] = *screen;

++screen;

}

clear(0,0,25,26,0,0);

}

scrivi()

{ register riga, colonna;

screen -= 4000;

for(riga=0;riga != 25;riga++)

for(colonna=0;colonna != 80;colonna++) {

*screen = matrix[riga][colonna];

++screen;

*screen = attrib[riga][colonna];

++screen;

}

cur_mv(23,1);

}

Come avrete notato al programma vengono applicate alcune

funzioni, ad esempio per gestire la finestra video, che avevamo

visto precedentemente.

La funzione con maggior importanza al fine delle call 11H e 12H

e' in ogni caso fi().

Vediamo, a discapito dello spazio di questo file, di commentarlo.

Come compariva dal remark del programma precedente la chiamata

alla funzione che cerca la corrispondenza del file nella

directory era :

while(fi(dir[tot++],0,"????????","???",16);

dove dir[tot] e' l'elemento della matrice che conterra' i dati di

un certo file.

0 indica il drive (0 = default).

"????????","???" sono un utilizzo dei caratteri jolly per dire

che il file cercato corrisponde al primo, al secondo ecc. senza

fare riferimento ad un nome di file specifico.

16 corrisponde all'attributo.

fi(b,drive,name,ext,tipo)

char *b; /* Indirizzo di DIR[TOT] */

char drive; /* Drive */

char *name; /* "????????" */

char *ext; /* "???" */

char tipo; /* Tipo */

{ int y;

register unsigned int regax;

register int i;

static int first_entry = 0;

y=0;

Tutte le seguenti operazioni di assegnazione settano le

specifiche del file che deve essere cercato nella directory.

fcb[0] = 255; /* FFH 1 byte Extended FCB */

strncpy(&fcb[1], "\0\0\0\0\0", 5); /* 5 zeri */

fcb[6] = tipo;

fcb[7] = drive;

strncpy(&fcb[8], name, 8);

strncpy(&fcb[16], ext, 3);

La prima ricerca di file deve essere fatta mediante la call 11H

(Search For First Entry) e quindi la condizione if testa se

effettivamente e' la prima volta che la funzione e' stata

chiamata.

Nel caso che first_entry sia uguale a 0 potrete notare l'utilizzo

di una call di cui non abbiamo ancora accennato nulla.

Si tratta della 1AH (Set Disk Transfer Address).

Per ora datela per scontata.

Ne parleremo tra un po'.

if (first_entry == 0)

{

first_entry = 1;

bdos(0x1A, bfr);

regax = bdos(0x11, fcb);

}

else

Nel caso che non sia la prima chiamata alla funzione allora la

call da utilizzare e' la 12H (Search for Next Entry).

regax = bdos(0x12, fcb);

Se la funzione restituisce FFH allora significa che la ricerca ha

avuto un esito negativo.

Restituendo NULL il ciclo while che chiamava fi() si interrompe.

if ((regax & 0x00FF) == 0xff)

{

first_entry = 0;

return(NULL);

}

Se il valore e' 16 allora significa che il file trovato e' una

directory.

In questo caso setta y=1 che viene utilizzato come flag per delle

funzioni puramente estetiche che seguono.

if(bfr[19] == 16)

{

b[13] = 1;

y=1;

}

else

b[13] = 0;

Il nome del file viene copiato all' indirizzo di dir[tot].

for(i = 0; i < 12;++i) b[i] = ' ';

for(i = 0; i < 8;++i) b[i] = bfr[i+8];

Se il flag e' uguale a uno allora e' una directory.

Nel caso che si tratti di un file nella posizione tra il nome e

l'estensione viene messo un punto.

if(y==0) b[8] = '.';

else

Nel caso invece che sia una directory viene messo il segno '>'.

b[8] = '>';

if(y==0)for(i = 0;i < 3;++i) b[i+9] = bfr[i+16];

else

Anche in questo caso, se si tratta di una dir, al posto

dell'estensione viene copiata la stringa "DIR".

{ strcpy(&b[9],"DIR",3);

y=0;

}

}

Il metodo applicato per la memorizzazione del contenuto del video

allo scopo di gestire una finestra sul video e' quello relativo

alla lettura diretta della memoria screen.

Questo porta all'incompatibilita' del programma compilato per

scheda Hercules con quello per scheda CGA.

La compilazione in funzione di una o dell'altra si esegue

mediante la sostituzione nella direttiva #define ??? dei ??? con

le iniziali della scheda voluta.

Come avrete potuto notare negli esempi compaiono funzioni gia'

viste molte volte precedentemente.

Questo conferma una delle notevoli potenzialita' del linguaggio C

e cioe' quella di poter creare e gestire librerie contenenti le

routine di cui facciamo sovente uso senza bisogno di doverle

riscrivere continuamente.

12H - Search for Next Entry

DS:DX, come nel caso del servizio precedente, puntano al FCB.

L'uso della call e' stato gia' discusso in parte durante

l'esempio precedente.

AL ritorna 00H se il file cercato e' stato reperito mentre FFH in

caso contrario.

La funzione 12H puo' essere utilizzata dopo che una chiamata al

11H ha dato frutti positivi.

La modalita' d'uso e' in pratica la stessa del 11H.

13H - Delete File

I registri DS:DX puntano al FCB del file che si vuole cancellare.

AL restituisce 00H se il file e' stato cancellato mentre FFH se

non e' stato reperito.

14H - Sequential Read

Il servizio legge un record indicato dal FCB nella memoria del

DTA (Disk Transfer Area).

DS:DX puntano al FCB mentre AL restituisce il risultato

dell'operazione e precisamente :

00H - Lettura eseguita positivamente

01H - Nessun data letto (EOF)

02H - DTA troppo piccolo

03H - EOF con lettura parziale

Del DTA parleremo con la funzione 1AH (Set Disk Transfer

Address).

15H - Sequential Write

DS:DX puntano come al solito al FCB.

In questo caso la funzione si interessa della scrittura di un

record sequenziale su disco.

I valori riportati da AL sono :

00H - Scrittura eseguita positivamente

01H - Dischetto pieno

02H - DTA troppo piccolo

16H - Create File

La funzione crea un file sul disco.

DS:DX puntano al FCB.

I valori restituiti in AL sono :

00H - Operazione eseguita correttamente

01H - Dischetto pieno

02H - DTA troppo piccolo

17H - Rename File

In questa funzione il FCB e' modificato in un modo strano.

Il nome del file occupa la solita posizione all'interno di questo

mentre il nome del nuovo file e' all' indirizzo 16 del FCB (DS:DX

+ 11).

Come avrete capito DS:DX puntano al FCB.

L'utilizzo di caratteri jolly all'interno del nuovo nome indica

che quella parte indicata e' uguale alla precedente situata nel

nome da convertire.

Es. Originale Nuovo nome Risultato

--------------------------------------------------------

OPUSCTL.EXE ????VCR.EXE OPUSVCR.EXE

Noterete in seguito (fino ad ora non e' capitato) che ci saranno

degli intervalli numerici nei servizi del DOS.

Questi mancanti sono contrassegnati sul Technical Reference come

"Used Internally by DOS".

Di molte sono riuscito a trovare, faticosamente, la

documentazione non messa in circolazione dalla Microsoft.

Sinceramente non capisco come mai possano capitare queste cose.

Si dice che alcune funzioni non sono state documentate in quanto

non viene garantito il mantenimento di queste nelle varie relase

del Dos.

Sara' anche cosi ma sinceramente ho notato che alcune sono

presenti in tutte le edizioni apparse fino ad ora.

19H - Current Disk

AL ritorna il numero del lettore in uso e cioe' 0 = A:, 1 = B:

ecc.

1AH - Set Disk Transfer Address

Eccoci finalmente giunti alla funzione che setta l'indirizzo di

trasferimento del disco.

Di default questo si trova al offset 80H del prefisso del

segmento del programma ed e' lungo 128 byte.

Questa area di memoria viene utilizzata dal DOS per l' I/O dei

file.

DS:DX si interessano di puntare a quest'area nel caso di

settaggio mediante la call 1AH.

Questa funzione e' stata utilizzata precedentemente nella routine

che leggeva la directory del disco mediante le funzioni Search

for First Entry e Search for Next Entry.

1BH - Allocation Table Information

Con questo servizio diventa necessaria un' estensione del

discorso relativo a quella definita FAT (File Allocation Table).

Il servizio riporta infatti informazioni riguardanti questa

ultima.

Che cosa e' la FAT ?

La FAT viene utilizzata dal Dos per allocare lo spazio su disco

per un file, un cluster alla volta.

Trattando dischetti floppy e hard disk possiamo ritrovarci di

fronte a due formati diversi di questa.

Nel caso dei floppy ogni voce relativa ad un cluster e' di 12 bit

ovvero 1.5 byte.

I clusters possono essere, in questo caso, 4086.

Nel caso di un disco fisso con piu' di 10 Mbytes ogni voce della

FAT e' di 16 bit (2 bytes).

Vediamo i significati delle voci di questa.

La prima voce indica il tipo di disco ed esattamente :

Valore HEX Significato

-----------------------------------------

FF Doppia faccia, 8 settori

FE Singola faccia, 8 settori

FD Doppia faccia, 9 settori

FC Singola faccia, 9 settori

F9 Doppia faccia, 15 settori

F8 Disco fisso

Il secondo, il terzo e il quarto bytes contiene FFFFH.

Ogni successiva voce della FAT contiene il successivo cluster del

file con due particolari significati.

Un valore pari a 000H indica che il cluster e' libero e

disponibile.

Nel caso che si esegua la cancellazione di un file si puo'

notare che le voci della FAT relative a questo vengono messe a

zero.

Nel caso di occupazione si puo' ricostruire la catena del file.

Es. Voce FAT Valore Descrizione

-----------------------------------------

. ... ................

5 7 Next cluster = 7

6 FF7 Avariato

7 8 Next cluster = 8

8 FFF Fine file

ecc.

Nell'esempio precedente avrete notato la presenza di due valori

particolari.

Nel caso che una voce della FAT contenga FF7H significa che il

cluster e' stato marcato come avariato in fase di formattazione.

Questo chiaramente non sara' disponibile.

FFFH indica la fine della catena relativa a un file.

In altre parole significa che il file e' terminato.

Il Dos gestisce due copie della FAT sul dischetto nel caso che

una venga rovinata.

La prima occupa il settore immediatamente successivo al settore

di boot.

Il discorso fatto fino ad ora sulla FAT risulta estremamente

semplice.

Purtroppo questa e' solo la teoria in quanto il metodo utilizzato

fisicamente dal Dos per gestire la FAT e' un po' piu' complicato.

L'organizzazione fisica della FAT viene fatta con un metodo di

memorizzazione a rovescio.

Nel caso di una FAT a 12 bit ogni valore relativo a una voce

della stessa e' organizzato in modo che questo risulti essere un

valore di tre cifre (000H-FFFH) e cioe' rappresentabile appunto

con 12 bit.

L' organizzazione avviene a coppie di tre bytes ciascuna.

Se questa coppia di voci risulta essere

ABC DEF (in bytes AB CD EF)

ribaltando il tutto i valori relativi a ogni voce saranno

DAB EFC

Per calcolare ogni successivo cluster di un file partendo dal

primo segnato nei campi della directory, che vedremo, sono

necessari i seguenti calcoli (relativi a una FAT da 12 bit).

Si moltiplica il numero del cluster appena usato per 1.5.

La parte intera del risultato del prodotto e' un offset

all'interno della FAT che contiene il numero del successivo

cluster del file.

Se il numero del cluster era un numero pari allora si valutano i

12 bits di ordine piu' basso del valore a 16 bit ottenuto.

In caso contrario i bits da considerare sono i 12 di ordine piu'

alto.

Come e' gia' stato detto se il contenuto di un cluster e' FF8H o

FFFH allora si e' giunti alla fine del file.

Per convertire il cluster nel settore logico sono necessarie le

seguenti operazioni.

Si sottrae 2 dal numero del cluster e si moltiplica questo per il

numero di settori per cluster.

Successivamente si addiziona il numero del settore in cui

incomincia l'area data del disco.

Una FAT a 16 bits mantiene gli stessi riferimenti solo che, ad

esempio, la fine del file viene segnata da FFFFH anziche' FFFH

mentre un cluster errato e' FFF7H invece di FF7H.

Nel caso di una FAT a 16 bit i calcoli da fare sono piu'

semplici.

Si moltiplica il numero del cluster contenuto in uno dei campi

della directory per due.

Se il contenuto e' FFFFH significa che il file e' terminato

altrimenti il valore corrisponde al successivo cluster del file.

Sembrera' fuori luogo la descrizione dell'organizzazione della

directory sul disco ma penso che sia utile trattarla a questo

punto visto che parlando della FAT ho accennato a un campo che

conteneva il primo cluster di un file.

Ogni ingresso della directory e' composto da 32 bytes cosi

suddivisi :

Bytes 0-7

Il primo bytes potrebbe essere la prima lettera del nome del file

oppure uno dei seguenti valori :

00H - File non usato. Viene inserito per limitare la

ricerca sulla directory.

E5H - Indica che il file e' stato cancellato.

2EH - Indica che si tratta di una subdirectory.

Nel caso che il nome di un file non sia lungo 8 caratteri il

campo viene completato mediante degli spazi (ASCII 32).

Bytes 8-10

Indica l'estensione del file.

Bytes 11

Contiene l'attributo del file (1 byte).

I codici relativi ai vari tipi possibili sono gia' stati

riportati parzialmente nel programma d'esempio che leggeva la

directory e la mostrava in una finestra video.

L'elenco completo e' il seguente :

01H - File marcato come read-only.

02H - Hidden file (file nascosto)

04H - System file

08H - Volume label

10H - Sub directory

20H - Bit archivio (Usato da BACKUP e RESTORE)

Per semplificare si puo' immaginare il byte di attributo nel

seguente modo : +---+---+---+---+---+---+---+---+

: 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+

: : : : : : : :

Non usati : : : : : 1 = read only

: : : : 1 = Hidden

: : : 1 = System

: : 1 = Volume label

: 1 = Subdirectory

1 = Archivio di bit

Bytes 12-21

Riservati al Dos.

Bytes 22-23

Questi bytes contengono l' ora di quando il file e' stato creato

o quella relativa all'ultima modifica.

L'interpretazione dei due bytes e' la seguente :

:---------- Byte 23 ------------:---------- Byte 22 ------------:

+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

:15 :14 :13 :12 :11 :10 : 9 : 8 : 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

: : : : : : : : : : : : : : : :

h h h h h m m m m m m x x x x x

dove h e' il numero delle ore (0-23)

m e' il numero dei minuti (0-59)

x e' il numero di incrementi di due secondi

Bytes 24-25

Mentre nei precedenti due bytes veniva conservata l'ora in questi

ritroviamo la data della creazione del file o dell'ultima

modifica.

:---------- Byte 25 ------------:---------- Byte 24 ------------:

+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

:15 :14 :13 :12 :11 :10 : 9 : 8 : 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

: : : : : : : : : : : : : : : :

y y y y y y y m m m m d d d d d

dove y e' l'anno (0-119)

d e' il giorno (1-31)

m e' il mese (1-12)

Bytes 26-27

Ecco finalmente il valore che indica dove inizia il file.

Parlando precedentemente della FAT avevamo accennato a questo

campo della directory dicendo che da questo iniziava la catena di

assegnazione dello spazio della FAT.

Il technical reference riporta che il primo cluster per i dati,

sia nel caso del disco fisso che in quello dei dischetti, e'

sempre il cluster 002.

Bytes 28-31

In questi due bytes viene conservata la dimensione del file.

La prima word (2 bytes) contiene la parte bassa del valore mentre

la seconda la parte alta.

Il discorso partito dal servizio 1BH e' un po' degenerato ma,

come avevo detto prima, era necessario per chiarire alcuni

concetti fondamentali che il servizio stesso tratta.

Sarebbe possibile leggere direttamente su disco il settore in cui

sono conservati questi dati relativi alla FAT e alla directory

mediante il gia' citato int 25H.

Mediante la call 1BH e' possibile avere comodamente i dati

basilari della FAT senza dover eseguire operazione di lettura

diretta.

I valori riportati sono i seguenti.

DS:BX puntano al byte di identificazione della FAT (ID) del drive

di default.

DX contiene il numero totale di cluster su disco mentre AL indica

quanti settori sono riservati per ogni unita' di allocazione.

In genere questo valore e' uno nel caso di dischetti singola

faccia mentre e' due nel caso di doppia faccia.

In ultimo CX contiene il numero di bytes per ogni settore.

1CH - Allocation Table Information for Specific Device

Il servizio e' in pratica uguale al 1BH solo che in questo caso

non e' piu' relativo al disco di default ma mediante il registro

DL, in ingresso, e' possibile specificare il numero del drive

interessato (0 = default, 1 = A:, ecc.).

21H - Random Read

Il servizio legge un blocco casuale da un file.

Il numero del blocco deve essere specificato nel FCB che e'

puntato da DS:DX alla chiamata della funzione.

I valori restituiti sono contenuti in AL e precisamente si

possono avere le seguenti condizioni.

00H - Lettura eseguita con successo

01H - Lettura fallita (EOF)

02H - DTA troppo piccolo

03H - Lettura parziale (EOF)

22H - Random Write

All'opposto del servizio 21H che legge questo scrive un record in

un file casuale.

Anche in questo caso, come per la funzione precedente, il numero

del blocco deve essere settato all'interno dell'apposito campo

del FCB che viene puntato dai registri DS:DX.

I valori riportati in AL dal servizio sono :

00H - Scrittura eseguita con successo

01H - Disco pieno

02H - DTA troppo piccolo

23H - File Size

Il servizio 23H riporta le dimensioni di un determinato file.

Il registro AL puo' restituire due diversi valori

00H - il file e' stato trovato

FFH - il file non e' stato trovato

La dimensione del file non viene riportata direttamente

all'interno di qualche registro ma bensi' piazzata nell'apposito

campo del FCB (Bytes 16-19).

24H - Set Realtive Record Field

Setta il campo del record casuale in modo che corrisponda

agli attuali campi dei record del FCB.

Si deve, in genere, chiamare questa funzione prima di eseguire

una lettura o una scrittura random.

25H - Set Interrupt Vector

Questa call non risultera' nuova in quanto l'ho gia' utilizzata

in un esempio precedente accennando soltanto al suo scopo.

Mediante questa funzione e' possibile settare l'indirizzo di una

routine di servizio di un interrupt.

Piu' avanti vedremo la funzione opposta ovvero quella che

riporta il segmento e l'offset di un interrupt specificato (anche

questa e' gia' stata utilizzata precedentemente in alcuni

esempi).

In quali casi ritorna utile scriversi delle particolari funzioni

di servizio degli interrupts o modificare quelle esistenti ?

Rispondere in modo preciso alla domanda non e' possibile in

quanto esistono un infinita' di situazioni in cui potrebbe

rivelarsi utile modificare o addirittura scrivere le routine di

servizio.

Un esempio potrebbe essere portato da un programma residente in

memoria.

Le vie da seguire, in questo caso, potrebbero essere svariate.

Uno dei metodi potrebbe essere la scrittura di una routine da

collegare all'interrupt di timer (INT 1CH) che esegua un

controllo periodico sul buffer di tastiera per individuare

eventuali tasti premuti.

Un'altro metodo, gia' visto precedentemente, e' quello di

modificare il servizio di interrupt che serve la tastiera allo

scopo di fare eseguire un controllo sui codici dei caratteri.

Un altro esempio di scrittura integrale potrebbe essere quello

relativo alla gestione delle porte di comunicazione.

In alcuni programmi di questo tipo si potrebbe sostituire una

routine di polling con una funzione di interrupt.

In altre parole al posto di testare mediante cicli ripetitivi lo

stato del modem e della linea si potrebbe fare in modo che sia

una chiamata di interrupt, eseguita in modo automatico, ad

assolvere al compito desiderato.

La scrittura in linguaggio C di una routine di interrupt presenta

alcuni problemi eliminabili mediante alcune funzioni, da abbinare

al modulo principale, scritte in assembler.

In ogni caso preferisco rimandare l'argomentazione all'apposito

capitolo riguardante la scrittura di programmi residenti in

memoria.

Proprio ultimamente sono riuscito a reperire la documentazione

riguardante gli interrupts non documentati dalla Microsoft e

cioe' quelli definiti "strategici".

Il discorso penso che sara' abbastanza lungo ed e' proprio per

questo motivo che ho optato per una trattazione separata

dell'argomento.

Per ora limitiamoci a ripetere la sintassi di utilizzo di questo

servizio.

AH contiene quindi 25H che e' il numero della call.

DS:DX puntano al segmento e all'offset della routine

dell'interrupt.

AL, infine, contiene il numero dell'interrupt di cui si vuole

settare l'indirizzo della funzione di servizio.

La call 25H non restituisce alcun valore dopo la chiamata

dell'interrupt 21H.

26H - Create New Program Segment

Lo scopo della call e' quello di creare un nuovo segmento di

programma per l'esecuzione di un processo caricato separatamente.

Un esempio e' relativo al caricamento di un file di overlay.

Il registro DX viene utilizzato per passare alla funzione il

valore del paragrafo di segmento per il nuovo processo.

Il servizio copia nel nuovo segmento creato il PSP relativo al

programma chiamante ed esegue su questo alcune modifiche.

E' inutile dire che anche in questo caso il valore relativo alla

call e' settato in AH.

Non viene restituito alcun valore.

Andando anche in questo caso fuori dall'argomento e' necessario

aprire una parentesi riguardante il significato del PSP (Program

Segment Prefix).

Quando il Dos carica per l'esecuzione un programma crea quello

definito PSP.

Si tratta di un area di memoria collocata in testa al programma

dove vengono conservati molti dati utilizzati dallo stesso Dos

per l'esecuzione di questo.

Il PSP viene sempre piazzato all'offset 0 nel segmento di codice.

Il programma , se .COM, viene collocato all'offset 100H.

In caso di un programma .EXE questo valore potrebbe essere

diverso.

Infatti nel caso di un programma .EXE, dopo il caricamento, i

regsitri DS e ES puntano al PSP mentre i registri CS, IP, SP e SS

sono settati al valore passato dal linker.

A differenza, nei .COM, tutti i registri di segmento puntano

inizialmente al PSP.

Il registro IP, in quest' ultimo caso, punta sempre a 100H nel

segmento di codice.

Come vedremo nei seguenti capitoli esistono alcune funzioni che

permettono di avere di ritorno il segmento in cui e' piazzato il

PSP o di settarlo.

Un discorso di questo tipo diventa importante nel momento in cui

si vuole scrivere un programma residente in memoria.

Un idea piu' chiara a riguardo del PSP e' possibile farsela

guardando la sua struttura.

0 +---------+--------+--------+-------------------+

: INT 20H :Dim.Mem.: Riserv.: Dispacciatore DOS:

8 +---------+--------+--------+-------+-----------+

: :Terminate address:(IP,CS): CTRLC IP :

10 +---------+-----------------+---+---------------+

:CTRLC CS :Critical Error IP,CS : :

+---------+---------------------+ :

: Used by Dos 2C :

: 5C +------------------------+

: : Formatted Parameter :

+----------------------+ Area 1 formatted as :

: standard unopened FCB :

: 6C +------------------------+

: : Formatted Parameter :

+----------------------+ Area 2 formatted as :

: unopened FCB (overlaid if FCB at 5C is opened):

80 +-----------------------------------------------+

: Unformatted parameter area (DTA) :

100 +-----------------------------------------------+

Vediamo ora singolarmente ogni campo del PSP.

Il primo campo inizia all'offset 00H ed e' lungo 2 bytes.

Contiene l'istruzione INT 20H che come vedremo e' uno dei metodi

per terminare un programma.

Come e' facile capire se il programma deve terminare basta che

punti a questo campo.

Il secondo campo inizia all'offset 02H ed e' lungo 2 bytes.

In questo viene conservata la dimensione della memoria in

paragrafi.

Come avevamo gia' detto, parlando dei registri di segmento, un

paragrafo e' lungo 10H bytes (16 in decimale).

Da questo risulta facile calcolare il numero di bytes

disponibili. (NUMERO PARAGRAFI x 16 = MEMORIA)

In alcuni casi si potrebbe verificare che il numero di bytes

riportati non corrispondano effettivamente all'ammontare della

memoria rimasta libera.

Alcuni programmi eseguono delle particolari scritture nelle

tabelle del Dos al fine di modificare l'indicatore della fine

della memoria disponibile.

Saltando il terzo campo che e' riservato al Dos arriviamo al

quarto ovvero a quello definito come dispacciatore delle funzioni

del Dos.

Il campo si trova all'offset 05H ed e' lungo 5 bytes.

Il suo scopo e' quello di contenere l'indirizzo della routine di

controllo delle funzioni Dos (INT 21H).

Il sesto campo, offset 0AH, e' lungo 4 byte e contiene una copia

dell'indirizzo della routine di terminazione del programma.

Questo infatti punta alla routine che deve essere eseguita quando

il programma termina.

Un altro campo inizia all'offset 0EH ed e' lungo 4 byte come il

precedente.

Viene contenuta in questa locazione una copia dell'indirizzo di

ctrl-break.

A partire dall'offset 12H per 4 byte ritroviamo il vettore

d'errore (Critical error exit).

All'offset 16H troviamo un'area di lavoro del Dos lunga 22 bytes.

Il puntatore alle variabili d'ambiente sono all'offset 2CH.

Nei due bytes da questo offset sono scritti segmento e offset

dell'area in cui sono conservate le variabili di sistema.

Per variabili di sistema intendo dire, ad esempio, il PATH, il

PROMPT e variabili come quelle che avevamo settato per l'utilizzo

del compilatore.

Il successivo campo all'offset 2EH, lungo 34 bytes, e' anche

questo destinato come area di lavoro del Dos.

All'offset 50H ritroviamo 3 bytes contenenti le istruzioni

assembler int 21H

retf

Costituisce un metodo per chiamare un int 21H senza farlo in modo

diretto.

Si setta nel registro AH il numero della call che si vuole

eseguire e si esegue un salto di programma a questo offset.

All'offset 55H ritroviamo 43 bytes occupati dall'area destinata

al FCB.

Avevo detto precedentemente parlando del passaggio di argomenti

al main() di un programma in linguaggio C che potevano essere

utilizzate argc e argv per indicare rispettivamente il numero di

argomenti passati e la matrice contenente la stringa con i vari

argomenti.

Questi valori sono conservati all'offset 80H per quanto riguarda

il numero di argomenti e al 81H (lungo 127 byte) per la stringa

con i vari parametri.

Questi ultimi 128 bytes vengono anche utilizzati come area di

trasferimento del disco (DTA).

Il programma lanciato processa le informazioni che sono

conservate nell'area degli argomenti passati e utilizza questa

come DTA.

Se si desidera conservarli e' possibile salvarli in altre

zone di memoria oppure settare, mediante una delle call dell'int

21H appena viste, una differente area di trasferimento del disco.

27H - Random Block Read

Questo servizio legge uno o piu' record da una posizione casuale

all'interno del file.

DS:DX puntano al FCB aperto mentre CX contiene il numero dei

record da leggere.

AL restituisce l'esito dell'operazione (vedi servizio 21H) e CX

il numero di blocchi effettivamente letti.

28H - Random Block Write

Mentre il servizio precedente leggeva un certo numero di blocchi

questo li scrive.

DS:DX puntano a un FCB aperto e CX contiene il numero di records

che devono essere scritti.

Per i valori di ritorno restituiti in AL vedi il servizio 22H che

e' simile a questo con l'unica differenza che permette di

scrivere un solo record.

CX anche in questo caso conterra' il numero di record

effettivamente scritti.

29H - Parse Filename

Il servizio analizza una riga di comando alla ricerca di un file

espresso nella forma d:filename.ext

e se trovato crea un FCB non aperto nella memoria puntata da

ES:DI.

DS:SI puntano alla stringa di comando da analizzare.

Mediante appositi settaggi dei bit del registro AL e' possibile

specificare il modo in cui deve essere analizzata la linea di

comando.

I bit da 4 a 7 sono ignorati mentre quelli da 0 a 3 hanno i

seguenti significati :

Bit 0 = 1 La specifica del file viene ricercata al di la' dei

separatori.

Bit 1 = 1 L' identificatore del drive viene cambiato nel FCB

soltanto se questo viene specificato nella linea di

comando che viene analizzata.

Bit 2 = 1 Il nome del file nel FCB viene cambiato solo se

nella linea di comando viene specificato un nome di

file valido.

In questo modo e' possibile nei programmi

specificare un nome di file di default ed

eventualmente modificarlo mediante un comando

diretto.

Bit 3 = 1 L'estensione del file viene cambiata nel FCB solo se

nella linea di comando viene specificata.

Le informazioni necessarie sono salvate, come abbiamo visto

prima, all' offset 80H del PSP (128 Bytes).

Un esempio di utilizzo in assembler e' il seguente :

;Controlla la linea di comando e setta un FCB

;all'offset 5CH nel PSP

mov ah,29H

mov si,81H

mov di,5CH

mov al,1111b ; bit value controls parsing

int 21H

Il servizio 29H restituisce i seguenti valori.

AL = 00H Non ci sono caratteri jolly all'interno della linea di

comando.

01H Ci sono caratteri jolly (* o ?) nella linea.

FFH Operazione fallita.

DS:SI Puntano al primo carattere dopo il nome di file

analizzato.

ES:DI Puntano al primo byte del FCB.

Questa call non e' utilizzata con linee di comando che contengono

il path completo del file.

2AH - Get Date

Il servizio acquisisce la data.

In ingresso solo il registro AH conterra' il numero dell call

(2AH).

In uscita si avranno i seguenti valori :

AL = Giorno della settimana (0 = Domenica, ...., 6 = Sabato)

CX = Anno (1980-2099)

DH = Mese (1 - 12)

DL = Giorno (1 - 31)

Un esempio in linguaggio C.

#include <stdio.h>

#include <dos.h>

union REGS inregs, outregs;

static char *date[] = {

"Domenica",

"Lunedi",

"Martedi",

"Mercoledi",

"Giovedi",

"Venerdi",

"Sabato"

} ;

main()

{ int var3, var4, var5, var6;

inregs.h.ah = 0x2A;

intdos ( &inregs, &outregs );

var3 = outregs.h.al;

var4 = outregs.x.cx;

var5 = outregs.h.dh;

var6 = outregs.h.dl;

printf("di %s %ld:%ld:%ld\n", date[var3], var6, var5, var4);

}

2BH = Set Date

In questo caso la funzione serve a settare la data del Dos.

I registri in ingresso devono contenere :

AH = Numero call (2BH)

CX = Anno (1980 - 2099)

DH = Mese (1 - 12)

DL = Giorno (1 - 31)

AL viene utilizzato per restituire l'esito dell'operazione di

settaggio e precisamente :

00H = Data settata

FFH = Data non settata.

Un altro esempio :

#include <stdio.h>

#include <dos.h>

union REGS inregs, outregs;

main()

{ int var3, var4, var5;

inregs.h.ah = 0x2B;

printf("Giorno : ");

scanf("%d", &var3);

inregs.h.dl = var3;

printf("Mese : ");

scanf("%d", &var4);

inregs.h.dh = var4;

printf("Anno 1980-2000 : ");

scanf("%d", &var5);

inregs.x.cx = var5;

intdos( &inregs, &outregs);

if( outregs.h.al == 0xFF) {

puts("\nData non valida");

exit();

}

}

2CH - Get Time

Riporta l'ora, minuti, secondi e centesimi di secondo.

I registri interessati in uscita sono :

CH = Ora (0 - 23)

CL = Minuti (0 - 59)

DH = Secondi (0 - 59)

DL = Centesimi (0 - 99)

2DH - Set Time

Setta ora, minuti, secondi e centesimi.

In questo caso i registri interessati sono uguali a quelli del

servizio precedente solo che devono essere settati prima di

chiamare la funzione.

2EH - Set/Reset Verify Switch

Il servizio se messo a stato attivo (ON) fa' in modo che ad ogni

operazione di scrittura su disco corrisponda un controllo del

CRC.

AL conterra' i valori relativi all'attivazione e alla

disattivazione del servizio di verifica ovvero :

00H = Verify OFF

01H = Verify ON

2FH - Get Disk Transfer Address (DTA)

Il servizio 1AH serviva a settare l'area di trasferimento del

disco.

Mediante il servizio 2FH questa viene restituita mediante ES:BX

che puntano alla zona di memoria utilizzata per questo scopo.

30H - Get DOS Version Number

Il servizio 30H riporta il numero della versione Dos su cui si

lavora.

Possono verificarsi dei casi in cui un nostro particolare

programma faccia riferimento a delle chiamate presenti solo nelle

versioni superiori a un certo numero di relase del Dos.

In questo caso e' necessario eseguire una verifica relativa al

numero di questa.

Il numero di versione viene restituito suddiviso in due parti.

Il registro AL contiene la parte maggiore mentre AH quella

minore.

Esempio : Vers. 3.20

- --

+-----------+ +-----------+

: :

Parte maggiore Parte minore

Nel seguente esempio viene controllata la versione del Dos.

Se questa e' minore alla 3.00 il programma abortisce.

#include <stdio.h>

#include <dos.h>

#define FROMVERS 3

union REGS inregs,outregs;

main()

{ inregs.h.ah = 0x30;

intdos(&inregs,&outregs);

printf("Dos Version %d.%d", outregs.h.al, outregs.h.ah);

if(outregs.h.al < FROMVERS) {

puts("Versione Dos non corretta");

puts("Deve essere dalla 3.00 in poi");

abort();

}

}

31H - Terminate Process and Remain Resident

Il servizio permette ad un programma di terminare ma di rimanere

allo stesso tempo residente in memoria.

Esistono altri servizi che eseguono lo stesso compito come ad

esempio l' int 27H.

Vedremo questi parlando dei programmi residenti in memoria.

Il servizio 31H permette di restituire in uscita un codice

testabile mediante il sottocomando dos ERRORLEVEL.

Questo numero di codice viene passato come argomento in ingresso

nel servizio mediante il registro AL.

Sempre in ingresso deve essere specificato il numero di paragrafi

che devono essere conservati residenti.

Il registro che conservera' questo valore e' DX.

32H - Get Disk Information

Questo servizio non e' stato documentato dalla Microsoft.

Guardando il Technical Reference si nota che il servzio viene

definito come "Used Internally by DOS".

In ogni caso il servizio restituisce informazioni riguardanti il

lettore specificato nel registro DL. (0 = Default, 1 = A:, ecc.)

In uscita vengono riportate le seguenti informazioni.

AL contiene 00H se il drive specificato esiste.

In caso contrario AL contiene FFH.

DS:BX puntano al blocco di informazioni del disco che contiene le

notizie viste parlando dell'interrupt relativo ai floppy.

Fate attenzione ad utilizzare questa funzione in quanto il

registro DS viene cambiato.

Questo significa che prima di eseguire la call e' necessario

salvare il contenuto di questo registro allo scopo di

ripristinarlo dopo la chiamata stessa.

33H - Ctrl-Break Check

Il servizio testa o setta lo stato del Ctrl-Break.

AL contiene il codice dell'operazione da eseguire e precisamente

00H - Restituisce lo stato

01H - Setta lo stato

Lo stato viene specificato nel registro DL

00H - OFF

01H - ON

Nel caso che venga richiesta la funzione per avere di ritorno lo

stato del Ctrl-Break DL contiene in uscita

00H - Se in stato OFF

01H - Se in stato ON

34H - Find Active Byte

Ed ecco un altra funzione non documentata.

Questa assume una notevole importanza per la scrittura di

programmi residenti in memoria in quanto uno dei peggiori

ostacoli per la scrittura di questi ultimi e' costituita dalle

funzioni non rientranti.

Tutte le call del Dos non sono rientranti.

Il "byte attivo" conta il numero delle chiamate al Dos che sono

processate al momento della call.

Il registro ES restituisce il segmento del byte attivo mentre

l'offset e' contenuto in BX.

Anche in questo caso bisogna prestare attenzione al fatto che la

call cambia il registro ES.

35H - Get Vector

Di questa call e' gia' stato fatto uso in alcuni esempi

precedenti.

Il servizio restituisce in ES:BX segmento e offset della routine

di servizio di un determinato interrupt specificato in ingresso

mediante il registro AL.

Nel momento in cui si modifica un vettore di interrupt mediante

il servizio 25H e' conveniente salvare il vecchio indirizzo

mediante questa call allo scopo di poterlo ripristinare prima

della fine del nostro programma.

36H - Get Disk Free Space

Il servizio restituisce un certo numero di informazioni

riguardanti il disco specificato in ingresso nel registro DL

(0 = default, 1 = A:, ecc.).

I registri in uscita specificano :

BX = Cluster disponibili

DX = Numero totale di cluster

CX = Bytes per settore

AX = Restituisce FFFFH se si e' verificato un errore.

E' possibile mediante alcuni calcoli risalire alle seguenti

informazioni :

CX*AX*DX = Spazio totale disco

CX*AX*BX = Spazio libero

CX*AX = Bytes per cluster

(BX*100)/DX = Percentuale libera su disco

Un esempio in linguaggio C.

#include <stdio.h>

#include <dos.h>

union REGS inregs, outregs;

long val_bx, val_dx, val_cx, val_ax;

main()

{ inregs.h.ah = 0x36;

inregs.h.dl = 0x00;

intdos(&inregs, &outregs);

val_bx = outregs.x.bx;

val_dx = outregs.x.dx;

val_cx = outregs.x.cx;

val_ax = outregs.x.ax;

printf("\n%8ld bytes per cluster",val_cx*val_ax);

printf("\n%8ld bytes totali disco",val_cx*val_ax*val_dx);

printf("\n%8ld bytes liberi", val_cx*val_ax*val_bx);

printf("\n%8d %% libero", (val_bx*100) / val_dx);

}

37H - Determine or set DOS switch character

Si tratta anche in questo caso di una funzione normalmente non

documentata dal Technical Reference.

Il carattere di switch normalmente utilizzato dal Dos e' /.

Si tratta del carattere utilizzato per separare gli argomenti

sulla linea di comando.

Esempio: FORMAT A: /S oppure DIR /W

E' possibile mediante un opportuno settaggio di valore

all'interno del registro AL settare o avere restituito il

carattere di switch.

AL = 0 Restituisce il carattere in DL

AL = 1 Setta il carattere inserito in DL

Anche in questo caso un esempio in linguaggio C.

#include <stdio.h>

#include <dos.h>

union REGS inregs, outregs;

main()

{ int c;

puts("\n\na .. Set switch character");

puts("b .. Determine switch character");

c = getch();

if(c == 'a' ||c == 'A') set();

inregs.h.ah = 0x37;

inregs.h.al = 0x00;

intdos(&inregs, &outregs);

printf("\n\nActual switch character is %c", outregs.h.dl);

}

set()

{ int c;

printf("\n\nEnter new switch character : ");

c = getche();

inregs.h.ah = 0x37;

inregs.h.al = 0x01;

inregs.h.dl = c;

intdos(&inregs, &outregs);

}

Dopo aver compilato il programma usate la funzione set e

passategli come argomento, ad esempio, il carattere '%'.

Provate a questo punto a fare DIR %W invece di DIR /W.

38H - Get or Set Country Dependent

Il servizio permette ai programmi di adattarsi automaticamente ai

formati relativi alla zona specificata.

Nel caso che si vogliano restituiti i dati relativi a un certo

paese DS:DX puntano a un area di memoria in cui devono essere

salvate le informazioni.

I campi di questa zona di memoria sono i seguenti :

Campo Lunghezza Descrizione

-----------------------------------------------------------------

1 2 Bytes Formato data

2 5 Bytes Simbolo valuta (Esempio USA $)

3 2 Bytes Separatore migliaia (. , spazio)

4 2 Bytes Separatore decimali (. ,)

5 2 Bytes Separatore data

6 2 Bytes Separatore ora

7 1 Byte Collocazione simbolo valuta 1 = prima,0 = dopo

8 1 Byte Posizioni decimali valuta

9 1 Byte Formato ora 0 = clock 12 ore 1 = clock 24 ore

10 4 Bytes Indirizzo subroutine per determinazione uso

lettere minuscole e maiuscole

11 2 Bytes Carattere separazione liste (Es. A, B, ecc.)

Il primo campo, quello relativo al formato della data, puo'

assumere uno dei seguenti valori:

0 = USA mese giorno anno

1 = Europa giorno mese anno

2 = Giappone anno mese giorno

In ingresso e' possibile specificare in AL 255 codici di paese.

Se il numero e' maggiore di 255 in AL si settera' FFH e il codice

del paese verra' messo nel registro BX.

In caso che si passi un codice di paese errato allora il servizio

setta il flag di carry e restituisce in AL il codice d'errore.

Se si vogliono invece settare i parametri il registro DX contiene

FFFFH e AL il codice del paese.

Anche in questo caso vale il discorso fatto prima per la funzione

che restituisce le informazioni e cioe' che se il numero del

paese e' superiore a 255 allora AL contiene FFH e il codice va'

messo in BX.

39H - Create Subdirectory (MKDIR)

Il servizio crea una subdirectory.

DS:DX puntano a una stringa ASCII in cui deve essere specificato

il path.

In caso di errore il flag di carry viene settato e in AX viene

restituito il codice d'errore.

3AH - Remove Subdirectory (RMDIR)

Anche in questo caso DS:DX puntano a una stringa ASCII contenente

il path della subdirectory da rimuovere.

In caso di errore il comportamento del flag di carry e del

registro AX e' lo stesso di quello della funzione precedente.

3BH - Change the Currebt Directory (CHDIR)

Vale per questo servizio lo stesso discorso fatto per le due

funzioni precedenti.

3CH - Create a File

Il servizio crea un nuovo file oppure tronca uno gia' esistente a

lunghezza 0.

In ingresso DS:DX puntano alla stringa ASCII contenente il path

del file da creare.

CX contiene l'attributo.

Dopo la chiamata AX potrebbe contenere il codice d'errore, nel

caso che il flag di carry sia settato, oppure l'handle di 16 bit

del file creato, nel caso che non sia settato il flag di carry.

Se l'operazione ha successo il file viene aperto come

read/write.

3DH - Open a File

DS:DX puntano a una stringa ASCII contenente il path del file che

deve essere aperto mentre AL il codice relativo al modo.

Il registro AX, come per il servizio precedente, potrebbe

ritornare o il codice d'errore oppure l'handle del file.

In caso d'errore il flag di carry viene settato.

Il codice relativo al modo contenuto in AL e' costituito da campi

bit oriented e precisamente i seguenti.

I - Flag di eredita' (Inheritance flag)

R - Campi riservati

A - Campi d'accesso (Access field)

S - Campi di condivisione (Sharing mode field)

La mappatura e' la seguente

+---+---+---+---+---+---+---+---+

AL : 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+

: : : : : : : :

I S S S R A A A

Il flag I specifica' se il file aperto puo' essere lasciato in

"eredita'" a un processo secondario lanciato dal programma

principale.

Se questo viene messo a 1 significa che il file aperto e' privato

e quindi non puo' essere utilizzato senza opportuna operazione di

riapertura da parte di un processo minore.

Chiaramente se questo flag e' a 0 il significato e' quello

contrario.

Le combinazioni dei bit del campo S, quelli riservati alle

specifiche relative alla condivisione, sono :

0 0 0 : Modo di compatibilita'

0 0 1 : Modo inibizione lettura/scrittura

0 1 0 : Modo inibizione scrittura

0 1 1 : Modo inibizione lettura

1 0 0 : Modo nessuna inibizione

Il bit indicato come riservato deve essere messo a 0.

Gli ultimi bit indicano il modo di accesso al file e precisamente

0 0 0 : Read

0 0 1 : Write

0 1 0 : Read/Write

Il concetto di multitasking e' introdotto dalla versione 3.00 del

Dos e quindi se la funzione viene richiamata da un programma che

gira sotto una versione precedente gli accessi permessi sono solo

quelli relativi ai bit del settaggio del modo.

Nel caso in cui il vostro programma pretenda il settaggio anche

degli altri campi potete utilizzare una delle funzioni appena

viste (Get DOS Version Number) per accertarvi che la versione Dos

sia corretta.

3EH - Close a File Handle

Il servizio chiude un file legato all'handle precedentemente

ritornato da una funzione creat o open.

Il numero dell'handle e' specificato nel registro BX.

Di ritorno, se il flag di carry e' settato, AX contiene il codice

d'errore.

3FH - Read from a File or Device

Il servizio e' molto simile alla funzione read del linguaggio C.

BX contiene il numero relativo all' handle del file o del device

su cui si vuole leggere.

DS:DX puntano al buffer che dovra' contenere i dati letti.

CX contiene infine il numero di byte che devono essere letti.

Di ritorno AX conterra', se il flag di carry e' settato, il

codice d'errore oppure, in caso contrario, il numero di bytes

effettivamente letti.

Se i byte ritornati da AX sono maggiori di 0 ma minori di quelli

richiesti in CX significa che e' stata incontrata la fine del

file.

40H - Write to a File or Device

Simile al servizio precedente con la differenza che in questo

caso la funzione svolta e' di scrittura.

BX si riferisce all'handle del file su cui si vuole eseguire

l'operazione.

DS:DX puntano ai dati che devono essere scritti.

CX contiene il numero di bytes.

AX, come nella call precedente, puo' contenere il codice

d'errore, se il flag di carry e' settato, oppure il numero di

bytes effettivamente scritti.

41H - Delete a File from a Specified Directory

La call 41H cancella un file dalla directory specificata.

DS:DX puntano alla stringa ASCII contenente il path con il nome

del file da eliminare.

Se il flag di carry e' settato AX contiene in uscita il codice

d'errore.

42H - Move File Read/Write Pointer

In pratica il servizio svolge un operazione di SEEK

(posizionamento) su di un file.

In tutto simile alle funzioni del linguaggio C lseek e fseek i

parametri passati mediante i registri sono i seguenti.

CX:DX contengono il nuovo offset ovvero la distanza da muovere in

bytes considerando che CX contiene la parte piu' significativa.

AL contiene il riferimento da cui contare l'offset ossia

0 Il puntatore e' mosso di CX:DX bytes

dall'inizio del file.

1 Il puntatore e' mosso di CX:DX bytes

dalla posizione attuale.

2 Il puntatore e' mosso alla fine del file.

Di ritorno, se il flag di carry e' settato, AX contiene il codice

d'errore.

In caso negativo DX:AX contengono la nuova locazione del

puntatore.

43H - Change File Mode (CHMOD)

A riguardo degli attributi di un file abbiamo gia' parlato in

molti casi.

Riguardate ad esempio il paragrafo in cui erano descritti i campi

della directory.

Il servzio serve appunto a cambiare questi attributi.

DS:DX puntano alla stringa ASCII con il path del file di cui si

vuole cambiare il modo.

CX contiene l'attributo.

AL contiene il codice della funzione.

Se in AL si setta 00H allora il servizio restituisce in CX

l'attributo del file specificato.

Nel caso che AL contenga 01H la funzione da eseguire e' quella di

settaggio dell'attributo.

Nel seguente esempio il file specificato alla richiesta di input

viene settato come "file nascosto".

/*

* HIDDEN.C

*/

#include <stdio.h>

#include <dos.h>

union REGS inregs, outregs;

struct SREGS segregs;

char path[50];

char far *var = path;

main()

{ puts("Hidden File");

printf("\nFile : ");

gets(path);

segregs.ds = FP_SEG(var);

inregs.x.dx = FP_OFF(var);

inregs.h.ah = 0x43;

inregs.h.al = 0x00;

intdosx(&inregs,&outregs,&segregs);

inregs.h.cl = outregs.h.cl | 2;

inregs.h.ah = 0x43;

inregs.h.al = 0x01;

intdosx(&inregs,&outregs,&segregs);

}

Vengono utilizzate tutte e due le funzioni del servizio 43H (Get

e Set).

La prima (Get) riporta gli attributi attuali del file.

A questi, mediante un operazione di OR, viene forzato il bit

relativo all' attributo HIDDEN.

Successivamente viene riscritto mediante la funzione set.

44H - I/O Control for Devices (IOCTL)

Il servizio esegue alcune funzioni per il controllo dell' I/O sui

devices.

DS:DX puntano, a seconda dell'operazione che si intende svolgere,

a un area data oppure a un buffer.

In caso di operazioni di lettura o di scrittura CX contiene il

numero di bytes che devono essere,per l'appunto, letti o

scritti.

Il servizio viene selezionato mediante il registro AL.

AL = 00H Riporta informazioni su un device (DX)

AL = 01H Setta informazione su un device. DH deve essere 0 per

questo servizio.

AL = 02H Legge il numero di byte specificato in CX all' interno

del buffer puntato da DS:DX.

AL = 03H Scrive il numero di byte specificato in CX. L'area di

memoria con i dati per l'operazione e' puntata da DS:DX

AL = 04H Uguale ad AL = 02H ma solo che qui e' possibile settare

il numero del drive (0 = default, 1 = A: ecc.)

AL = 05H Uguale ad AL = 03H ma anche in questo caso e' possibile

specificare il drive.

AL = 06H Riporta lo stato dell'input.

AL = 07H Riporta lo stato dell'output.

AL = 08H Nelle versioni successive alla relase 3.00 del Dos

questo riporta se il device e' fisso o rimovibile.

AL = 0BH Anche questo servizio e' disponibile dalla relase 3.00

del Dos. Specifica il numero di tentativi che il Dos

deve effettuare per tentare di accedere a un file.

Riguarda problemi di condivisione di un file.

Di ritorno dal servizio AX riporta, nel caso che il flag di carry

non sia settato, il numero di bytes trasferiti (in riferimento ai

servizi AL = 2, 3, 4 e 5) oppure FFH = ready, 00H = Not Ready

(funzioni 6 e 7).

In caso contrario AX contiene un codice d'errore.

I bits del registro DX nel caso delle calls 00H e 01H valgono :

+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

:15 :14 :13 :12 :11 :10 : 9 : 8 : 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

: : : : : : : : : : : : : : : :

R C +---- RISERVATI ----+ I E B R I I I I

I T S O I I S S S S

S R D F N S C N C C

* L * E A L U O I

V R K L T N

Se il bit ISDEV e' a 1 significa che ci si riferisce a un device.

Se ISDEV, in caso contrario, e' a 0 allora si ha a che fare con

un file su disco.

In questo ultimo caso tutti i bit contrassegnati da 8 a 15 devono

essere a 0.

I bit, nel caso di un device (ISDEV=1), hanno i seguenti

significati. EOF = 0 Fine del file.

BINARY = 1 Se si opera in modo binario

Il carattere CTRL Z e' ignorato.

0 Si opera in modo ASCII.

CTRL Z ha significato di fine file.

ISCLK = 1 Indica che il device e' il clock.

ISNUL = 1 Indica il device NUL

ISCOT = 1 Il device di output e' la console.

ISCIN = 1 Il device di input e' la console.

CTRL = 0 Il device non elabora stringhe di

controllo durante lettura/scrittura.

Riporto ora alcuni servizi legati a questa call non documentati

dal Technical Reference del Dos.

Il numero del servizio, come per i precedenti, sono selezionati

mediante l'opportuno settaggio del registro AL.

AL = 09H Testa se il device e' locale o remoto.

BL contiene il numero del drive (0 = default, 1 = A:

ecc.).

Il bit 12 del registro DX e' settato in uscita nel caso

che il device sia remoto.

AL = 0AH Testa se l'handle e' remoto o locale.

BX contiene l'handle del file.

Il bit 15 del registro DX viene settato nel caso che si

tratti di un handle remoto.

AL = 0EH Testa il numero di lettere assegnate a un certo device.

Mediante un operazione di ASSIGN da Dos sarebbe

possibile indicare con piu' lettere lo stesso drive.

BL contiene il numero del drive a cui ci si vuole

riferire.

In uscita AL riportera' 0 se solo una lettera e'

riferita al drive.

Se il numero di lettere e' maggiore questo sara'

contenuto sempre in AL.

Nel caso che si verifichi un errore il flag di carry

viene settato e il codice di errore viene restituito in

AX.

AL = 0DH Questo servizio riporta molte informazioni suplementari

sul device specificato.

AL contiene 13 (0DH).

BL il numero del drive a cui ci si vuole riferire.

CL viene settato con 96 (60H) e CH con 8.

Di ritorno DS:DX puntano al blocco di parametri.

ATTENZIONE: DS viene cambiato dal servizio.

Il blocco di parametri a cui puntano di ritorno DS:DX hanno i

seguenti significati.

BYTE DESCRIZIONE

0 0 se e' un BPB (BIOS parameter Block) costruito e'

stato riportato.

1 se e' un BPB di default che e' stato restituito.

1 A seconda del valore contenuto in questo byte e'

possibile risalire alle seguenti informazioni.

0 5.25'' double density disk

1 5.25'' high density disk

2 3.5'' disk

3 8'' single density disk

4 8'' double density disk

5 Fixed disk

6 Tape driver

7 Other

2 Contiene gli attributi del device

0 Se il device e' un disco fisso

1 Se il device e' rimovibile

3-4 Numero di tracce

5 Tipo nel drive. 0 = default

BYTE DESCRIZIONE

6-7 Bytes per settore

8 Settori per cluster

9-10 Settori riservati (boot)

11 Numero delle FAT'S

12-13 Numero di ingressi nella root directory

14-15 Numero di settori

16 Disk type byte

17-18 Settori per FAT

19-20 Settori per traccia

21-22 Facce

23-26 Settori nascosti (HIDDEN)

Con questo ho concluso la descrizione di questo servizio che

tutto sommato offre molte caratteristiche utilizzabili nei nostri

programmi.

45H - Duplicate a File Handle (DUP)

BX contiene il numero dell'handle del file che deve essere

duplicato.

Di ritorno, se il flag di carry non e' settato, AX contiene un

nuovo handle del file.

In caso contrario AX contiene il codice d'errore.

Fate attenzione che ogni operazione eseguita con il nuovo handle

sono riflesse su quello vecchio.

46H - Force a Duplicate of a Handle (CDUP)

Il servizio e' simile a quello precedente solo che in questo caso

viene fornito il numero del secondo handle.

Questo deve essere gia' esistente.

In altre parole nel servizio 45H il secondo handle viene creato

automaticamente dal Dos mentre in questo viene passato in

ingresso mediante il registro CX.

BX contiene l'handle originale.

Il secondo handle potrebbe essere uno non in uso per cui si vuole

ridefinire lo scopo.

AX potrebbe ritornare, sempre nel caso che il flag di carry sia

settato, il codice d'errore.

47H - Get Current Directory

Il servizio restituisce nella zona di memoria di 64 byte puntata

da DS:SI il path attuale relativo al drive specificato nel

registro DL (0 = default, 1 = A: ecc.)

AX restituisce, nel caso che il flag di carry sia settato, il

codice d'errore.

Vediamo un esempio.

/*

* GETDIR.C

*/

#include <stdio.h>

#include <dos.h>

union REGS inregs,outregs;

struct SREGS segregs;

unsigned char directory[64];

char far *var = directory;

main(argc,argv)

int argc;

char *argv[];

{ int drive = 0;

if(argc > 1) {

switch(*argv[1]) {

case 'a':

case 'A':

drive = 1;

break;

case 'b':

case 'B':

drive = 2;

break;

case 'c':

case 'C':

drive = 3;

break;

case 'D':

case 'd':

drive = 4;

break;

default:

break;

}

}

inregs.h.ah = 0x47;

inregs.h.dl = drive;

inregs.x.si = FP_OFF(var);

segregs.ds = FP_SEG(var);

intdosx(&inregs,&outregs,&segregs);

if(outregs.x.cflag == 1) {

puts("Fatal error");

abort();

}

printf("\n\nActual directory is \\%s\n\n", directory);

}

48H - Allocate memory

Il servizio alloca il numero di paragrafi specificati in ingresso

nel registro BX.

Di ritorno AX:0 (segmento in AX, offset 0) punta al blocco di

memoria allocato.

In caso di errore, indicato sempre dal flag di carry settato, AX

restituisce il codice d'errore.

Un errore possibile e' dovuto al fatto di richiedere piu' memoria

di quanta ne sia disponibile.

In questo caso BX ritorna la grandezza massima che sarebbe

possibile allocare per un blocco.

49H - Free Allocated Memory

La call libera la memoria allocata dal servizio precedente.

ES punta al segmento del blocco che deve essere liberato.

AX restituisce, eventualmente, il codice d'errore.

4AH - Modify Allocated Memory Blocks (SETBLOCK)

Nel caso che la memoria allocata dalla call 48H debba essere

modificata, aumentata o diminuita, e' possibile utilizzare questo

servizio per farlo.

ES punta al blocco di memoria.

BX contiene la dimensione del nuovo blocco in paragrafi.

AX ritorna il codice d'errore nel caso che il flag di carry sia

settato.

BX, come per il servizio 48H, riporta la dimensione massima che

potrebbe essere allocata.

4BH - Load or Execute a Program (EXEC)

Questa funzione permette a un programma di caricare in memoria un

altro programma e di eseguirlo.

DS:DX puntano a una stringa ASCII contenente il drive, il path e

il nome del file che deve essere eseguito.

AL contiene il codice relativo alla funzione che deve essere

eseguita.

AL = 00H Il programma e' letto e viene per questo creato un

nuovo PSP.

L'esecuzione del programma e' automatico.

AL = 01H Il programma e'letto ma non viene creato nessun PSP.

L'esecuzione in questo caso non e' automatica ma

deve avvenire mediante una chiamata dal programma

principale.

Questo servizio viene utilizzato per la lettura

degli overlay.

Gli oggetti puntati da ES:BX variano a seconda della funzione

selezionata in AL.

Nel caso di AL = 00H ES:BX puntano a un blocco di 14 bytes con le

seguenti informazioni.

BYTE LUNGHEZZA DESCRIZIONE

0 2 Indirizzo segmento con la variabili di

environment che devono essere passate.

2 4 Puntatore alla stringa di comando che deve

essere passata al PSP.

6 4 Puntatore al FCB di default da passare al PSP.

10 4 Puntatore al secondo FCB di default.

La funzione carica/esegui intaccano il contenuto dei registri SS

e SP che devono essere quindi salvati e ripristinati prima di

terminare.

Nel caso di AL = 01H allora ES:BX puntano a un blocco dati con le

seguenti caratteristiche.

BYTE LUNGHEZZA DESCRIZIONE

0 2 Segmento dove deve essere caricato il file.

2 2 Fattore di riallocazione per i programmi .EXE

AX ritorna il codice d'errore oppure nulla se la funzione ha

avuto esito positivo.

4CH - Terminate a Process (EXIT)

Il servizio termina un programma restituendo al Dos un codice di

uscita specificabile nel registro AL e testabile mediante la la

pseudo istruzione ERRORLEVEL.

Un esempio e' il seguente.

Il programma viene richiamato dal seguente file .BAT .

echo off

prog

if ERRORLEVEL 1 goto scelta1

if ERRORLEVEL 2 goto scelta2

if ERRORLEVEL 3 goto scelta3

goto fine

:scelta1

echo carico scelta 1

goto fine

:scelta2

echo carico scelta 2

goto fine

:scelta3

echo carico scelta 3

:fine

Il listato del programma (prog.c)

#include <stdio.h>

#include <dos.h>

union REGS inregs;

main()

{ int c;

puts("a .. Scelta A");

puts("b .. Scelta B");

puts("c .. Scelta C");

c = getch();

switch(c) {

case 'a':

case 'A':

inregs.h.al = 1;

break;

case 'b':

case 'B':

inregs.h.al = 2;

break;

case 'c':

case 'C':

inregs.h.al = 3;

break;

default:

exit(0);

break;

}

inregs.h.ah = 0x4C;

intdos(&inregs);

}

4DH - Get Return Code of a Sub-process (WAIT)

Il servizio acquisisce il codice di ritorno di un sottoprogramma

terminato mediante la call 4CH.

Viene riportato in AL il codice riportato dal programma.

AH restituisce il modo con cui e' terminato il processo ed

esattamente :

00H Terminato normalmente

01H Terminato a seguito di un CTRL BREAK

02H Terminato a seguito di un errore di un device

03H Terminato a seguito di una call 31H

4EH - Find First Matching File (FIND FIRST)

Questa funzione e' simile alla call 11H in quanto ricerca il

primo ingresso nella directory corrispondente alle specifiche

passate.

DS:DX puntano a una stringa ASCII contenente il path e il nome

del file che si vuole ricercare.

E' possibile utilizzare i caratteri jolly ? e *.

CX contiene gli attributi del file.

Guardate la descrizione fatta per il servizio 11H.

Se in uscita il flag di carry e' settato allora AX contiene il

codice d'errore.

Se il file viene reperito il DTA e' riempito con i seguenti

parametri.

1 byte - attributo trovato

2 bytes - ora del file

2 bytes - data del file

2 bytes - valore meno significativo delle dimensioni del file

2 bytes - valore piu' significativo delle dimensioni del file

13 bytes - nome ed estensione del file trovato

4FH - Find Next Matching File

La call e' la continuazione della precedente in quanto ricerca,

dopo che il primo ingresso in directory e' stato trovato, ogni

successivo.

DS:DX puntano alle informazioni di una precedente chiamata al

servizio 4EH.

50H - Set address of PSP

Si tratta di un' altro servizio non documentato o meglio

specificato come "Used Internally by Dos".

Mediante questa call e' possibile settare l'indirizzo del PSP.

L'uso principale e' per quanto riguarda i programmi residenti in

memoria.

BX contiene l'indirizzo del segmento del PSP da settare.

Nel caso che si esegua un operazione del tipo, ricordatevi prima

di salvare il vecchio indirizzo del PSP mediante la successiva

call.

51H - Get Address of PSP.

Esiste una similitudine di questo servizio con il 62H.

Viene restituito in BX il segmento dell'attuale PSP.

Anche questo viene documentato dal Technical Reference come "Used

Internally by Dos".

Il seguente esempio riporta il segmento relativo al PSP.

#include <stdio.h>

#include <dos.h>

union REGS inregs, outregs;

main()

{ inregs.h.ah = 0x51;

intdos(&inregs,&outregs);

printf("\nSegment of PSP : %04x", outregs.x.bx);

}

54H - Get Verify Setting

Il servizio riporta mediante il registro AL lo stato della

verifica.

AL = 00H La verifica e' disattiva

AL = 01H La verifica e' attiva

56H - Rename a File

La call serve a rinominare un file.

DS:DX puntano a una stringa ASCII contenente il vecchio path e

nome del file.

ES:DI puntano al nuovo path e nome del file.

Sempre indicato dal flag di carry settato un eventuale errore

potrebbe essere restituito dal registro AX.

57H - Get/Set a File's Date and Time

Il servizio a seconda del valore del registro AL setta o riporta

la data relativa alla creazione o all'ultima modifica di un file.

Se AL e' a 0 l'operazione eseguita e' quella di riportare la data

mentre con AL a 1 e' l'operazione inversa ad essere eseguita.

BX contiene l'handle del file aperto su cui si vuole eseguire

l'operazione.

In caso di settaggio la data viene specificata all'interno del

registro DX.

Il valore da settare viene dato da

DX = (ANNO - 1980) * 512 + MESE * 32 + GIORNO

Sempre in caso di un operazione di settaggio il tempo viene

specificato nel registro CX.

Il valore viene dato da

CX = ORA * 2048 + MINUTI * 32 + SECONDI / 2

Nel seguente esempio viene cambiata la data relativa al file

specificato alla richiesta.

#include <stdio.h>

#include <dos.h>

#include <fcntl.h>

#include <types.h>

#include <stat.h>

union REGS inregs, outregs;

int infile;

main()

{ char n_file[13];

int ora, minuti, secondi, giorno, mese, anno;

printf("\n\nNome del file : ");

gets(n_file);

if(n_file[0] == '\0')

exit(0);

puts("\n");

do {

printf("Ora : ");

scanf("%d", &ora);

} while(ora < 0 || ora > 23);

do {

printf("Minuti : ");

scanf("%d", &minuti);

} while(minuti < 0 || minuti > 60);

do {

printf("Secondi : ");

scanf("%d", &secondi);

} while(secondi < 0 || secondi > 60);

puts(" ");

do {

printf("Anno : ");

scanf("%d", &anno);

} while(anno < 1980 || anno > 2029);

do {

printf("Mese : ");

scanf("%d", &mese);

} while(mese < 1 || mese > 12);

do {

printf("Giorno : ");

scanf("%d", &giorno);

} while(giorno < 1 || giorno > 31);

if((infile = open(n_file,O_RDWR)) == NULL) {

puts("ERRORE : File non trovato");

abort();

}

inregs.h.ah = 0x57;

inregs.h.al = 0x01;

inregs.x.bx = infile;

inregs.x.cx = ora*2048+minuti*32+secondi/2;

inregs.x.dx = (anno-1980)*512+mese*32+giorno;

intdos(&inregs,&outregs);

close(infile);

}

59H - Get Extended Error

Il servizio ritorna informazioni estese sul tipi di errore che

possono verificarsi durante l'esecuzione di una procedura.

Vengono restituite quattro distinte informazioni riguardanti il

tipo di errore.

BX contiene in ingresso 00H.

In uscita AX contiene il codice d'errore, BL l'azione suggerita,

BH ls classe d'errore e CH la causa.

L'elenco dettagliato di tutto e' il seguente.

Codice d'errore Descrizione

1 Invalid function number

2 File not found

3 Path not found

4 No available handles

5 Access denied

6 Invalid handle

7 Memory control blocks destroyed

8 Not enough memory

9 Invalid memory block

10 Invalid environment

11 Invalid format

12 Invalid access code

13 Invalid data

14 Reserved

15 Invalid drive

16 Can't remove current directory

17 Not same device

18 No more files found

19 Write protect

20 Unknow unit

21 Drive not ready

22 Invalid command

23 CRC error

24 Bad request

25 Seek error

26 Unknown media type

27 Sector not found

28 Out of paper

29 Write error

30 Read error

31 General error

32 Sharing violation

33 Lock violation

34 Invalid disk change

35 File control block unavailable

36 Sharing buffer overflow

37-49 Reserved

50 Network request not supported

51 Remote computer not listening

52 Duplicate name of network

53 Network name not found

54 Network busy

55 Network device no longer exist

56 BIOS command limit exceeded

57 Network adaptor hardware error

58 Incorrect network response

59 Unexpected network error

60 Incompatible remote adaptor

61 Full print queue

62 Not enough space for print

63 Print file deleted

64 Network name deleted

65 Access denied

66 Incorrect network device type

67 Network name not found

68 Too many network names

69 BIOS session limit exceeded

70 Temporany pause

71 Network request not accepted

72 Redirection paused

73-79 Reserved

80 File exists

81 Reserved

82 Cannot make directory entry

83 Critical error handler failure

84 Too many redirections

85 Duplicate redirection

86 Invalid password

87 Invalid parameter

88 Network device fault

Classe d'errore Descrizione

1 Out of resource

2 Temporary problem

3 Permission problem

4 Internal software error

5 Hardware failure

6 System software error

7 Application software error

8 Item not found

9 Invalid format

10 Item locked

11 Media failure or wrong disk

12 Item already exists

13 Unknown

Azione raccomandata Descrizione

1 Retry

2 Retry in a while

3 Ask user to reenter request

4 Abort and cleanup

5 Abort immediately, don't cleanup

6 Ignore

7 Ask user to perform remedial action, then

retry

Causa Descrizione

1 Unknown or not specific

2 Block device

3 Network device

4 Serial device

5 Memory

Fate attenzione che il servizio distrugge il contenuto dei

registri CL, DX, DI, SI, DS ed ES.

5AH - Create Temporary File

Il servizio crea un file temporaneo.

L'attributo con cui deve essere creato il file va' settato in CX.

DS:DX puntano al path dove deve essere creato il file terminante

con \.

Questa zona di memoria deve essere sempre 12 bytes piu' lunga in

quanto la call aggiunge in questo spazio il nome del file che

viene creato.

La stringa cosi' composta viene puntata in uscita dai registri

DS:DX.

AX potrebbe restituire un codice d'errore.

Il file creato con questa funzione non e' cancellato

automaticamente al termine del programma.

Il seguente esempio, mediante la funzione getcwd(), legge la

directory attuale e crea un file temporaneo all'interno di

questa.

#include <stdio.h>

#include <direct.h>

#include <stdlib.h>

#include <dos.h>

char var1[64];

char far *var = var1;

union REGS inregs,outregs;

struct SREGS segregs;

main()

{ getcwd(var1,51);

inregs.h.ah = 0x5A;

inregs.x.dx = FP_OFF(var);

segregs.ds = FP_SEG(var);

inregs.x.cx = 0x00;

intdosx(&inregs,&outregs,&segregs);

printf("\n\nHo creato : %s\n\n", var1);

}

5BH - Create New File

Il servizio crea un nuovo file.

DS:DX puntano a una stringa ASCII contenente il path e il nome

del file da creare.

CX contiene l'attributo di questo.

AX contiene al ritorno il codice d'errore, se il flag di carry e'

settato, oppure l'handle del file appena creato.

5CH - Lock/Unlock File Access

Dalla versione 3.00 del Dos si possono trovare alcuni sevizi

progettati alla luce della multiutenza.

La call 5CH serve appunto a bloccare e a liberare una parte

specificata di un file.

AL contiene il codice della funzione ovvero

00H per chiudere

01H per liberare

BX contiene l'handle del file a cui ci si vuole riferire.

CX e DX contengono rispettivamente la parte alta e la parte bassa

dell'offset dal quale si vuole bloccare o liberare il file.

SI e DI a sua volta contengono la parte alta e la parte bassa del

numero di bytes, a partire dall'offset specificato, che devono

essere interessati nell'operazione.

Anche in questo servizio AX restituisce, nel caso che il flag di

carry si settato, il codice d'errore.

Nel caso che una parte del file sia bloccato e un altro processo

tenti di accedere a questa saranno eseguiti 3 tentativi.

Il Technical Reference consiglia, nel caso di insuccesso

dell'operazione, di richiamare la call 59H per avere il codice

esteso d'errore.

62H - Get Program Segment Prefix Address (PSP)

Il servizio e' simile a quello non documentato di cui abbiamo

parlato prima in quanto restituisce in BX l'indirizzo del

segmento del PSP.

Con questa call abbiamo terminato i servizi rigiuardanti l'int

21H del Dos.

Come avrete potuto notare molte funzioni incluse nella libreria

standard del compilatore Microsoft C fanno esattamente quello che

abbiamo visto parlando di tutte queste calls.

Questo chiaramente e' un punto a nostro favore che non dovremo

metterci li a riscrivere i vari servizi.

Penso che con la nuova versione del Dos 3.3 siano stati incluse

nuove calls.

Purtroppo penso anche che per un po' di tempo il Technical

Reference non sara' disponibile in circolazione.

Sinceramente quando avevo annunciato questo fascicolo non

supponevo che avrebbe preso tutto questo tempo.

L'idea che mi ero fatto inizialmente era che al massimo saremmo

arrivati sulle 100-150 pagine.

A questo punto siamo al doppio e oltre tutto senza aver terminato

le argomentazioni.

La parte finale sara' dedicata all'utilizzo di tutto quello di

cui abbiamo parlato per la scrittura di programmi residenti in

memoria.

Durante la descrizione di quanto abbiamo visto ho introdotto

alcune argomentazioni nel momento in cui comparivano nei servizi

trattati.

Come avevo detto precedentemente si sarebbe potuto dedicargli dei

capitoli a parte.

Facendo in questo modo probabilmente si sarebbe andati a favore

dell'ordine ma a discapito della comprensione.

In ogni caso un indice facilitera' la ricerca dei vari argomenti.

Vediamo ora alcuni accenni sulla mappatura di memoria.

Mappatura della memoria

Durante questo testo abbiamo accennato all'uso di varie locazioni

di memoria.

Una mappatura generica per quanto riguarda le aree di memoria

utilizzate dal Bios e dal Dos e' la seguente.

Segmento Offset Utilizzo

---------------------------------------------

0000 0000 Tavola vettori di interrupts

0040 0000 Area parametri BIOS

0050 0000 Area parametri DOS

Per fare alcuni esempi possiamo riportare gli indirizzi che

potrebbero rivelarsi utili.

0040:0010 Byte contenente lo stato delle periferiche collegate.

Ad esempio se si desidera sapere lo stato dell'adattatore video

si potrebbero testare il bit 4 e 5 di questa locazione.

Se i due bits sono 10 = 80x25 CGA

01 = 40x25 CGA

11 = Monocromatico

Se i due bits sono 00 e' possibile testare la locazione

0040:0087 per vedere se la scheda EGA e' quella attiva (!=0 con

bit 3 = 0).

L'elenco completo delle periferiche testabili a partire da

0040:0010 e' :

bit descrizione

0 1 se sono collegati dei drive a floppy

1 (XT,AT) 1 se e' installato un coprocessore matematico

2-3 11 se sono installati 64K di RAM base

4-5 stato adattatore video

6-7 se bit 0 = 1, numero di floppy

8 non usato su PC, XT e AT

9-11 Numero di seriali

12 1 se e' installato un adattatore games

13 non usato su PC, XT e AT

14-15 numero porte parallele

Un altro esempio e' portato dalla locazione 0040:0013 in cui il

BIOS salva il numero di Kbytes disponibili nel sistema.

Le locazioni 0040:0017-18 vengono utilizzate dal BIOS per

l'interpretazione della tastiera.

Abbiamo gia' visto queste locazioni per la rilevazione del tasto

Insert.

Alcune locazioni riguardano il buffer di tastiera.

Questo e' lungo 32 Bytes che iniziano alla locazione 0040:001E.

Esistono anche le locazioni 0040:001A e 0040:001C che contengono

i puntatori alla testa e alla coda del buffer.

Il buffer di tastiera e' di tipo FIFO (first-in first-out).

Guardate il seguente schema.

ecc. 36 34 32 30 2E 2C 2A 28 26 24 22 20 1E 1C 1A

+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

: : : : : C : I : A : O : : : : : : :28 :30 :

+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

: :

+----------------------------------+ :

: +--------------+

: :

Tail Pointer Head Pointer

La locazione 0040:003E contiene un insieme di bit che indicano,

se a 0, che le unita' abbinate hanno bisogno di una

ricalibrazione.

Il bit 0 corrisponde al drive A:, il bit 1 a B;, il bit 2 a C: e

il bit 3 a d: .

La locazione 0040:003F sempre mediante alcuni bit ci indica lo

stato del motore di ogni drive.

Se un bit e' a 0 significa che il motore corrispondente sta'

girando.

Il bit 7 indica uno stato di scrittura su un drive.

I bit relativi allo stato del motore per ogni drive hanno gli

stessi abbinamenti di quelli della locazione appena vista.

Alla locazione 0040:0041 e' contenuto il byte indicante lo stato

del disco.

Se il flag di carry al ritorno di un int 13H e' settato allora il

registro AH riportato dallo stesso contiene questo byte.

La modalita' del video e' invece contenuta a 0040:0049.

I valori sono relativi a quelli che vengono settati con il

servizio 00H dell'int 10H.

Un valore uguale a 28H nella locazione 0040:004A specifica che le

colonne del video sono 40 mentre un valore pari a 50H ne indica

80.

Le dimensioni del buffer video, corrispondenti alla modalita',

sono conservate a 0040:004C.

0040:004E contiene l'offset di partenza del buffer del video.

I due byte a partire da 0040:0050 indicano ls posizione del

cursore relativa ad ogni pagina video possibile.

Mentre le locazioni precedenti mantenevano la posizione del

cursore, i due bytes a partire da 0040:0060 contengono la

modalita' del cursore ovvero linea di partenza e finale.

Anche questo contenuto e' funzione del settaggio effettuato

mediante l'apposita call dell'int 10H.

Alla cella 0040:0062 e' contenuto il numero della pagina attiva.

Il valore 3B4H contenuto a 0040:0063 specifica che la scheda

video attiva e' la monocromatica.

3D4H e' relativa alla scheda CGA.

Per l'identificazione della EGA vedi la pagina precedente.

La locazione 0040:0065 contiene l'impostazione della modalita'

video attiva relativa al chip del controllore 6845.

La tavolozza del colore impostata mediante la porta 3D9H, vista

parlando della gestione video, e' memorizzata a 0040:0066.

La locazione 0040:0069 viene utilizzato per il calcolo del CRC su

blocchi di dati da 256 byte.

Un valore di quattro byte contenuti dalla locazione 0040:006C e'

incrementato automaticamente dall' INT 08H.

Quando il valore non viene letto per piu' di 24 ore si ottiene un

overflow che viene indicato dalla locazione 0040:0070.

Fate attenzione che la significativita' del valore contenuto a

0040:006C va da sinistra a destra.

Se il tasto di break viene premuto il bit 7 della locazione

0040:0071 e' settato a 1.

I valori di timeout relativi alla stampante e alla porta RS_232

sono contenuti rispettivamente alle locazioni 0040:0078 e

0040:007C.

I valori sono da 0 a 255 secondi.

Come avrete notato le locazioni di cui abbiamo parlato erano

relative all' area di memoria utilizzata dal BIOS.

Molte di queste le avevamo gia' viste negli esempi riportati

precedentemente parlando in modo specifico delle periferiche.

In ogni caso ho pensato che fosse meglio raggruppare il tutto al

fine di riportare un po' d'ordine per facilitare un eventuale

consultazione.

Concludiamo l'argomento e iniziamo a vedere altri interrupts

alcuni dei quali saranno utilizzati all'interno dei programmi

memory resident. Altri Interrupts

Alcuni di questi interrupts li ho gia' utilizzati negli esempi

riportati in questo testo accennando solo al loro utilizzo.

Altri risulteranno nuovi e di fatto solo alcuni saranno utili nei

nostri programmi in linguaggio C.

20H - Program Terminate

Parlando del PSP abbiamo visto che uno dei campi in questo

contenuti era relativo all'istruzione INT 20H.

Avevamo detto che costituiva uno dei metodi utilizzati da un

programma per mettere fine a se stesso.

In altre parole a un processo e' sufficente puntare a questa

istruzione per terminare.

L'interrupt svuota tutti i buffers ma non salva i file che, dopo

esser stati modificati, non sono stati chiusi prima della

chiamata.

22H - Terminate Address

L'interrupt punta alla locazione che contiene l'indirizzo al

quale deve essere passato il controllo quando un programma

termina.

Questo indirizzo e' copiato all'interno del PSP.

23H - Ctrl Break Exit Address

L'interrupt viene eseguito quando l'utente, durante l'esecuzione

di un programma, batte Ctrl Break.

Come abbiamo visto precedentemente anche questo indirizzo viene

copiato all'interno del PSP.

24H - Critical Error Handler Vector

Quando capita un errore durante l'esecuzione di una funzione Dos

il controllo viene trasferito all' int 24H.

25H - Absolute Disk Read

Di questo interrupt come del 26H, il prossimo, abbiamo parlato

nei capitoli precedenti.

Senza ripetere ricordo solo che l'interrupt legge uno o piu'

settori dal disco specificato.

26H - Absolute Disk Write

Vale lo stesso discorso dell'interrupt precedente solo che in

questo caso il servizio svolto e' di scrittura.

27H - Terminate but Stay Resident

Questo interrupt e' importante per i programmi residenti in

memoria.

Infatti l'interrupt permette a un programma di rimanere residente

nell'istante in cui il COMMAND del dos riprende il controllo.

L'interrupt puo' essere utilizzato, ad esempio, nell'istante in

cui si vuole modificare una routine di interrupt.

Come sicuramente ricorderete nel programma esempio che

intercettava un tasto e settava in ON/OFF il dtr era stato

utilizzato questo interrupt.

Non dovrebbe essere utilizzato con un programma .EXE che viene

letto nella parte alta della memoria.

Un altra precauzione da prendere e' quella relativa alla

dimensione massima del programma.

Questo non deve superare i 64 Kbytes.

Vediamo ora gli esempi relativi al settaggio di due vettori di

interrupt eseguiti con un programma .COM e uno .EXE.

_text segment

assume cs:_text, ds:_text

org 100H ;offset .COM

jmp init ;salta alla routine di

;installazione

inter proc far

push ds ;salvataggio registri che

push di ;verranno cambiati

.... ..

(routine interrupt)

.... ..

pop di

pop ds

iret

fine equ $

inter endp

;

init: mov dx,offset init

mov al,NUMERO_INTERRUPT

mov ah,25H

int 21H

; lea dx,fine

int 27H

_text ends

end

Come potete vedere prima di richiamare l'interrupt 27H il

registro viene caricato con l'ultimo indirizzo della routine

residente.

Questo ha lo scopo di indicare da quale offset potrebbe essere

caricato un successivo programma.

Se vi ricordate avevamo detto che nel caso di un programma .COM

tutti i registri CS,ES,IP ecc. puntano inizialmente all'inizio

del PSP.

Nel caso di un programma .EXE allora i registri CS,IP ecc.

puntano all'inizio effettivo del codice ovvero 100H posizioni

oltre l'inizio del PSP.

Il programma, nel caso di un formato .EXE, sara' :

_text segment

jmp init

;

inter proc far

push ds

push di

.... ..

(routine interrupt)

.... ..

pop di

pop ds

iret

fine equ $

inter endp

;

init: mov dx offset inter

mov ax seg inter

mov ds,ax

mov al,NUMERO_INTERRUPT

mov ah,25H

int 21H

; mov dx,fine + 100H

mov byte ptr es:1,27H

ret

_text ends

end

In questo caso l'istruzione int 27H viene settata all'indirizzo

del PSP dove normalmente si trova l'istruzione int 20H.

E' stato aggiunto 100H in quanto, come avevamo detto prima, nel

caso di un .EXE CS e IP puntano effettivamente alla prima

istruzione del programma e non al suo PSP.

Nel caso di utilizzo dell'interrupt 27H per il settaggio di

routine di interrupts e' conveniente scrivere in assembler il

tutto in quanto in linguaggio C andremmo incontro a inutili

difficolta' come ad esempio il salvataggio dei registri.

Questo non significa che in C un lavoro simile non sia fattibile.

Dato che la modifica dei vettori di interrupt e' una delle

funzioni principali per la scrittura di programmi residenti

vedremo in modo accurato il tutto nei capitoli successivi.

28H - Call when DOS is free

Il nome all'interrupt non e' riportato sul Technical Reference in

quanto compare su questo come "Used Internally by Dos".

L'interrupt viene richiamato ripetutamente quando il Dos non ha

funzioni in esecuzione.

Si tratta di un altro interrupt importante per la scrittura di

programmi residenti in memoria che fanno uso di alcune call del

Dos.

Ne riparleremo tra poco.

2FH - Printer

Questo interrupt costituisce un mezzo per comunicare con lo

spooler di stampa.

A seconda del valore settato in AL il servizio svolge varie

funzioni e precisamente i seguenti:

0 = Riporta se lo spooler di stampa e' installato o meno.

I valori di ritorno sono restituiti sempre da AL e possono

essere

00 - Non installato. E' possibile eseguire installazione.

01 - Non installato. Non e' possibile eseguire

installazione.

FF - Installato.

1 = Presenta allo spooler un file per farlo stampare.

La forma di presentazione e' costituita da un blocco di 5

bytes definito dal Technical Reference come submit packet.

Questo viene puntato da DS:DX.

Il primo byte costituisce il livello che penso debba

trattarsi di un qualche cosa che stabilisce il tempo

macchina dedicato alla stampa in background dei file.

I rimanenti 4 byte costituiscono l'indirizzo

(segmento/offset) in cui e' conservato il drive, path e il

nome del file presentato allo spooler.

Il nome del file non puo' contenere dei caratteri jolly

del tipo ? o *.

2 = Cancella un file dallo spooler di stampa.

DS:DX puntano alla stringa ASCII con il path e il nome del

file che deve essere rimosso.

In questo caso sono ammessi nel nome del file i caratteri

jolly.

3 = Cancella tutti i file dallo spooler di stampa.

4 = Mediante questa chiamata la coda di stampa viene bloccata

e permette di farsi esaminare in quanto di ritorno

restituisce in DX il numero d'errore e DS:SI puntano alla

coda di stampa che contiene la serie di nomi di file che

sono in attesa.

Il primo nome di file corrisponde a quello che e' in fase

di stampa.

Qualsiasi richiesta di servizio tramite int 2FH sblocca la

coda.

5 = Teoricamente il servizio servirebbe a sbloccare la coda

dopo una chiamata al servizio AL = 4.

I codici d'errore ritornati dai servizi sono i seguenti.

Codice d'errore Descrizione

1 Funzione non valida

2 File non trovato

3 Path non esistente

4 Troppi files aperti

5 Accesso negato

8 Coda piena

9 Occupato (busy)

12 Nome troppo lungo

15 Drive non valido

E con questo abbiamo terminato anche il discorso relativo agli

altri interrupt non visti prima d'ora.

Purtroppo la Microsoft per alcuni servizi non e' stata prodiga di

informazioni.

Come avevo gia' accennato prima la scusante a questa carenza in

genere e' data dal fatto che per alcuni servizi non si garantisce

un futuro nelle successive versioni del Dos.

Questa per me e' una stupidata in quanto a questo punto si

potrebbe benissimo non documentare nulla e dire che non si

garantiscono neppure altre versioni del Dos.

Intanto che ci sono perche' non dire nulla ?

Prima di addentrarci sulle problematiche dei programmi memory

resident vediamo ancora il metodo per la creazione dei device

driver.

D'altra parte in questo testo abbiamo parlato un po' di tutte le

argomentazioni inerenti al sistema operativo Ms Dos e quindi al

fine di completare la panoramica riportata mi sembra doveroso

trattare anche questi. Device Driver

Un device driver e' uno speciale programma atto al controllo del

I/O con una determinata periferica.

Ho utilizzato il termine speciale in quanto per la scrittura di

un driver bisogna attenersi ad alcune specifiche particolari.

Il programma deve avere un' immagine di memoria non utilizzante

ORG 100H.

Il device deve essere scritto in assembler con un ORG 0H o al

limite senza nessuna istruzione che stabilisca l'origine dello

stesso in quanto il programma non utilizza il PSP ma viene

semplicemente letto.

Esistono due tipi di devices.

I devices di tipo carattere sono creati per l' I/O seriale di

caratteri singoli.

Per portare un esempio sono definiti devices di questo tipo CON

(console) e AUX (porta ausiliaria).

Il secondo tipo e' costituito dai devices a blocchi.

Sono esempi di questi i dischi che trattano l' I/O appunto a

blocchi.

Come difatto saprete un disco tratta come unita' minima per il

trasferimento di dati un settore.

Abbiamo parlato prima della configurazione fisica di un programma

di un device.

Oltre a questo uno di questi programmi necessita in testa di un

header lungo diciotto bytes.

Il primo campo lungo 4 bytes e' lo spazio riservato a un

puntatore al successivo driver.

Questo contiene sempre FFFFFFFFH in quanto il Dos al momento

della lettura del devices piazzera' l'indirizzo di partenza del

prossimo header.

Il secondo campo e' costituito da 2 bytes che servono a contenere

l'attributo del device.

Di 16 bits solo 7 hanno un significato e precisamente :

+---+---+---+---+---+---+---+

:15 :14 :13 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+

: : : : : : :

: : : : : : 1 = standard input device

: : : : : : 0 = not standard input

: : : : : 1 = standard output device

: : : : : 0 = not standard output

: : : : 1 = current NUL device

: : : : 0 = not NUL device

: : : 1 = clock device

: : : 0 = not a clock device

: : 1 = IBM block format

: : 0 = other block format

: 1 = IOCTL supported

: 0 = IOCTL not supported

1 = character device

0 = block device

Generalmente vengono settati solo i bit 15 e 14.

Il bit 13 chiaramente e' in funzione del bit 15 in quanto ha

esclusivamente significato con i device a blocchi.

I rimanenti vengono utilizzati solo per rimpiazzare i devices di

default del Dos.

Il terzo campo di due bytes serve a contenere quella definita

strategy routine.

Quando il Dos legge il device crea e setta un blocco data

chiamato request header.

Questo possiede fondamentalmente due funzioni.

La prima e' quella di servire come area di lavoro del Dos.

La funzione piu' importante e' quella di puntare alle

informazioni che devono essere passate al device dal programma

chiamante e di consevare alcune informazioni.

Il formato del request header e' variante come lunghezza a

seconda della natura del servizio richiesto anche se in ogni caso

i primi tredici byte sono sempre uguali e sono :

campo lunghezza scopo

1 1 byte Lunghezza del request header

2 1 byte Codice unita'. Usato solo per device a

blocchi.

3 1 byte Codice comando. Viene salvato l'ultimo

codice di comando impartito al device.

Vedremo tra poco i comandi validi.

4 2 bytes Stato. Viene settato tutte le volte che il

driver viene chiamato. Se il bit 15 e'

settato significa che si e' verificato un

errore. In questo caso gli otto bit meno

significativi contengono il codice

d'errore e precisamente

0 tentativo di scrittura su disco protetto

1 unita' sconosciuta

2 device non pronto

3 comando sconosciuto

4 errore di CRC

5 errata lunghezza struttura drive

richiesto

6 errore posizionamento

8 media sconosciuto

9 errore di fine carta su stampante

A errore scrittura

B errore lettura

C errore generale

F cambio disco non valido

5 8 bytes Area riservata al Dos.

6 variabile Area riservata operazioni del driver

I byte che si differenziano li vedremo con i singoli comandi

Il quarto campo di due byte e' un puntatore alla routine di

interrupt del device.

Ambedue i campi precedenti devono essere residenti nello stesso

segmento del device header.

L'ultimo campo, il quinto, del device header contiene il nome del

device stesso nel caso che si tratti di un device di tipo

carattere.

Nel caso di un device a blocchi lo spazio sara' riservato al

numero dell'unita'.

Questi sono lunghi al massimo 8 bytes e vengono giustificati, nel

caso che il nome o il numero unita'non siano tanto lungo, a

sinistra.

I bytes liberi vengono riempiti di spazi.

Il Dos quando richiama il servizio di un driver punta alla

strategy routine e setta nel request header le informazioni

relative a quello che desidera che venga eseguito.

Appena il Dos ritorna dalla richiesta fatta a quella definita

strategy routine richiama la routine d'interrupt senza passargli

nessun parametro in quanto questa utilizza le informazioni

restituite dalla chiamata precedente.

Il Dos passa il puntatore al request header mediante i registri

ES:BX.

Il codice di comando specificato nel request header e' uno dei

seguenti.

Codice Funzione

-------------------------------------------------------------

0 INIT

1 MEDIA CHECK (Solo per device a blocchi)

2 BUILD BPB (Solo per device a blocchi)

3 IOCTL input

4 INPUT

5 NO DESTRUCTIVE INPUT NO WAIT (Solo device

a carattere)

6 INPUT STATUS (Solo device a carattere)

7 INPUT FLUSH (Solo device a carattere)

8 OUTPUT

9 OUTPUT WITH VERIFY

10 OUTPUT STATUS (Solo device a carattere)

11 OUTPUT FLUSH (Solo device a carattere)

12 IOCTL output

13 DEVICE OPEN

14 DEVICE CLOSE

15 REMOVABLE MEDIA

Benche' esistano vari metodi per installare un device il piu'

utilizzato e' quello di utilizzare in fase di bootstrap del Dos

il CONFIG.SYS.

Supponiamo di aver creato un device chiamato PIPPO.

C>type config.sys

device = pippo.sys

device = ansi.sys

ecc.

Prima di proseguire con la descrizione particolareggiata dei

comandi precedentemente elencati vediamo lo schema rappresentante

il percorso eseguito per effettuare un operazione, ad esempio, di

scrittura.

programma chiamante

char buffer[512];

char far *var = buffer;

...........

...........

int c; Command code 13

c = open("pippo",O_RDWR);------------------------------+

inregs.h.ah = 0x40; /* Write to a device */ :

inregs.x.bx = c; /* handle */ :

segregs.ds = FP_SEG(buffer); /* seg data */ :

inregs.x.dx = FP_OFF(buffer); /* offset data */ :

inregs.x.cx = 512; /* bytes da scrivere */ :

intdosx(&inregs,&outregs,&segregs);------------------+-+

Command code 08 :

:

Device +-----------------+ <--------------------------------+

Header : 1 ............. : Cerca device

: . ............. :

: 5 Nome device :------+ Ritorna indirizzo

+-----------------+ : request header ret ^

: :

Device +-----------------+ <----+ :

Strategy : Puntatore al : Request Header :

: request header : --------------> +------------------+

: ES:BX : : 1 .............. :

+-----------------+ codice comando : 2 .............. :

Device +-------------------------+ +---- 3 Codice comando :

Interrupt: Tavola funzioni :Cerca: +-> 4 Status Word :

: +-----------------+ <-------+ : : 5 Ecc. :

: : Check Media : : : +------------------+

: : Input or output : : :

: : Ecc. : Trovato ? : Se non trovato segna

: +-----------------+ -----+----+ errore nella status

: : : no ^ word ed esce

: Routines di servizio : :si :

: del device : : : Se trovato codice

: +-----------------+ : : : comando cerca la sua

: +-----------------+ <----+ : routine di servizio

: : : la esegue e setta la

: +-----------------+ : : status word prima di

: +-----------------+ : : uscire per tornare

: : : : al programma

+------------:------------+ :

: :

+--------------------+

Spero che sia stata chiara la meccanica di funzionamento.

Ancora una puntualizzazione.

State attenti che nel caso che scriviate una device dovrete

salvare TUTTI i registri e ripristinarli prima di terminare.

Es: push ax

push bx

push cx

push ds

.... ..

(routine)

.... ..

pop ds

pop cx

pop bx

pop ax

ret

Vediamo ora singolarmente i bytes che si differenziano dalla

struttura di 13 bytes vista prima a seconda del codice di comando

diretto al device.

Codice comando = 0 INIT

Struttura request header

13 bytes - Request Header

1 byte - Numero unita' (Non per device a carattere)

4 bytes - Indirizzo finale del codice del programma residente

4 bytes - Puntatore al BPB (Non per device a carattere)

1 byte - Contiene la lettera relativa alla prima unita a

blocchi

Codice comando = 1 MEDIA CHECK

Struttura request header

13 bytes - Request Header

1 byte - Descrittore media dal Dos

1 byte - Per ritorno valore e precisamente

-1 Media has been changed

0 Don't know if media has been changed

1 Media has not been changed

4 bytes - Puntatore alla precedende ID di Volume se il driver ha

settato i bit 11 a 1 della word contenente l'attributo

del device e se il byte precedente indica un

cambiamento (-1 = Media changed)

Codice comando = 2 BUILD BPB (Bios Parameter Block)

Struttura request header

13 bytes - Request Header

1 byte - Descrittore media dal Dos

4 bytes - Indirizzo trasferimento (buffer)

4 bytes - Puntatore al BPB

Il BPB contiene informazioni pertinenti il device quali

dimensione dei settori, settori per unita' allocate, numero

unita' allocate, ingressi in directory ecc.

Le informazioni relative al BPB sono prese dal settore di boot

dell'unita'.

Il formato di questo settore e' il seguente.

3 bytes - JUMP near oppure 2 bytes per un istruzione di JMP short

seguito da un istruzione NOP.

8 bytes - Nome OEM e versione

2 bytes - Bytes per settore <----+

1 byte - Settore per unita' allocata (potenza di 2) :

2 bytes - Settori riservati :

1 byte - Numero di FAT'S B

2 bytes - Numero di massimo di ingressi nella root directory P

2 bytes - Numero totale di settori B

1 byte - Descrittore disco :

2 bytes - Numero di settori per una FAT <----+

1 byte - Settori per traccia

1 byte - Numero di facciate

1 byte - Numero di settori nascosti

Il byte contenente il descrittore del disco ha il seguente

formato. +---+---+---+---+---+---+---+---+

: 7 : 6 : 5 : 4 : 3 : 2 : 1 : 0 :

+---+---+---+---+---+---+---+---+

: : : : : : : :

1 1 1 1 1 : : 1 = 2 sided

: : 0 = 1 sided

: 1 = 8 sectors

: 0 = not 8 sectors

1 = removable

0 = fixed

Codice comando = 3,4,8,9,12 INPUT or OUTPUT

Struttura request header

13 bytes - Request Header

1 byte - Descrittore media

4 bytes - Indirizzo area trasferimento (buffer)

2 bytes - Byte/settore

2 bytes - Numero settore iniziale (non usato da device a

carattere)

4 bytes - Se viene restituito il codice d'errore 0FH contiene un

puntatore all'identificatore di volume.

Codice comando = 5 Non Destructive Input No Wait

Struttura request header

13 bytes - Request Header

1 byte - Letto dal device

Codice comando = 6,10 STATUS

Struttura request header

13 bytes - Request Header

Codice comando = 7,11 FLUSH

Struttura request header

13 bytes - Request Header

Codice comando = 13,14 OPEN or CLOSE

Struttura request header

13 bytes - Static Request Header

Codice comando = 15 REMOVABLE MEDIA

Struttura request header

13 bytes - Static Request Header

Nel dischetto contenente i programmi complementari del Dos esiste

un file, VDISK.LST, che puo' essere listato contenente un esempio

di scrittura relativo a un disco virtuale.

Con questo concludo anche il discorso relativo ai devices.

Ora non ci resta che concludere il tutto trattando alcuni punti

chiave per la scrittura di programmi residenti in memoria.

Programmi residenti in memoria

Ogni programma residente in memoria si basa sulla ridirezione di

uno o piu' vettori di interruzione.

Fondamentalmente possiamo suddividere questo genere di programmi

in due tipi differenti.

Il primo tipo e' costituito da quei programmi che modificano una

routine di interrupt per svolgere particolari compiti.

Supponiamo, ad esempio, di voler bloccare il settaggio video in

una determinata modalita'.

Potremmo modificare la routine dell'interrupt 10H, quello che si

interessa della gestione screen, in modo che ad ogni chiamata a

questo venga vagliato il servizio richiesto.

Nel caso che si tratti di un servizio estraneo al settaggio del

video ci sarebbe una chiamata al vecchio vettore di interrupt e

quindi l'esecuzione della funzione richiesta.

Se la call e' relativa a un settaggio video allora si potrebbe

eseguire una forzatura del valore relativo al modo e il

successivo salto alla routine di interrupt originale.

In questo testo abbiamo gia' visto alcuni esempi relativi al tipo

di programma appena accennato.

L'altro tipo di programmi residenti sono quelli del tipo di

SideKick (Tm).

In quest'ultimo caso la ridirezione dei vettori d' interrupts

avviene allo scopo di testare se in input sono stati battuti

certi tasti corrispondenti a quelli per l'attivazione.

Come vedremo successivamente potrebbe verificarsi la necessita'

di ridirigere o addirittura di scrivere altre routine.

Parlando dell'interrupt 27H avevo accennato alla difficolta' di

scrivere in linguaggio C simili programmi.

In alcuni casi i problemi sono minimi ma in altri possono essere

talmente complicati da far preferire una scrittura in linguaggio

assembler.

In ogni caso non voglio lasciare ad intendere che scrivere un

programma residente in memoria mediante quest' ultimo risulti una

cosa semplice.

Le strade percorse da coloro che hanno scritto programmi di

questo tipo sono svariate e non si puo' in nessun caso parlare di

tecniche uniche.

In un esempio riportato nel capitolo riguardante la gestione

della tastiera avevamo visto un programma che modificava il

vettore dell' interrupt 16H allo scopo di eseguire

l'intercettazione del tasto CTRL B.

Essenzialmente un programma residente si puo' suddividere in due

parti ovvero la parte che rimarra' in memoria e la parte

d'installazione non residente.

Questo e' a livello semplicistico in quanto come vedremo esistono

ben altre problematiche.

Quando esiste la possibilita' conviene scrivere un programma in

modo che risulti un .COM.

In ogni caso, per tornare al discorso di prima, possiamo fare la

seguente schematizzazione.

segmento codice

origine 100H

salta all'installazione

procedura residente interrupt

abilita interrupt

e' una richiesta input tastiera ?

se si chiama vecchio interrupt (CALL)

altrimenti salta vecchio interrupt (JMP)

controlla il tasto

..................

nostro programma

..................

ritorna interrupt (IRET)

procedura residente fine

installazione parte NON residente

controllo se la procedura e' gia' stata installata

disattivazione CTRL BREAK

salvataggio vecchio vettore interrupt

settaggio nuovo vettore interrupt indirizzo procedura

residente

termina ma rimani residente

fine segmento codice

Come avrete notato ho segnato tra parentesi due diversi tipi di

richiami alle vecchie routine d'interrupt.

Come sapra' chi conosce l'assembler il salto (JMP) non e'

condizionato al ritorno mentre la chiamata (CALL) mediante

l'istruzione RET ripristina l'indirizzo successivo alla CALL.

Questo significa che il controllo all'interno della modifica

della routine d'interrupt avverra' prima sul registro AH, che

come sapete contiene il numero del servizio, allo scopo di

controllare se si tratta di una richiesta d'input da tastiera.

Nel caso che AH contenga il codice relativo ad un altro servizio

e' inutile proseguire e quindi mediante un salto senza ritorno si

ripassa il controllo alla vecchia routine d'interrupt.

In caso affermativo allora ci sara' una chiamata con ritorno in

quanto dopo che la vecchia routine d'interrupt ha inputato il

tasto ci dovra' essere il controllo sul suo codice al fine di

accertare che questo sia quello da noi aspettato.

Chiaramente se questo equivale allora prima dell' IRET ci sara'

il richiamo al nostro programma residente.

In altre parole quando ci serve una chiamata con ritorno al

vecchio interrupt potremo simulare questa con

pushf

cli

call DWORD PTR cs:vecchio_vettore

Nel caso che il salto debba essere incondizionato allora

pop registri salvati

cli

jmp DWORD PTR cs:vecchio_vettore

In questo ultimo caso non avremo piu' il controllo del programma.

Voglio ripetere il discorso relativo alla chiamata dell'interrupt

a costo di sembrare pedante in quanto penso che sia necessario

capire il suo meccanismo.

Normalmente quando viene chiamato un interrupt viene letto il

segmento e l'offset della routine di servizio dalla tavola dei

vettori.

Supponendo di aver ridiretto l' int 16H e' chiaro che l'indirizzo

contenuto in questa tabella e' quello relativo alla nostra

routine residente in quanto l'operazione e' stata eseguita

mediante la funzione d'installazione.

Come sapete un' interrupt potrebbe svolgere piu' servizi

selezionabili, mediante opportuno settaggio prima della chiamata

stessa, all'interno del registro AH.

Tutto questo significa che quando ci sara' una chiamata

all'interrupt 16H il controllo passera' alla nostra routine.

Ora si dovra' vedere se si tratta di una richiesta d' input.

_key_i proc near

sti

cmp ah,0 ;e' una richiesta d'input

jne kio ;se no salta alla vecchia

;routine.

pushf ;se si simula una chiamata

cli ;al vecchio interrupt e dopo

call dword ptr cs:o_kio ;che ha eseguito la sua

;funzione

sti

cmp ax,4900H ;controlla se e' PgUp

jne fine ;se no ritorna dall'int

NOSTRE FUNZIONI........ ;se si richiama funzioni

fine: iret

kio: jmp dword ptr cs:o_kio

_key_i endp

L'esempio precedente non e' completo in quanto entrando nella

routine d'interrupt si dovrebbero salvare tutti i registri che

pensiamo vengano cambiati per poi ripristinarli prima di uscire.

Il discorso della memorizzazione e del ripristino vale anche per

quanto riguarda l'ambiente precedente alla possibile esecuzione

delle nostre funzioni.

Supponiamo che si stia lavorando con un programma di WP e che la

nostra utility residente sia una finestra in cui appare un

calcolatore tipo quello di SideKich.

Prima di eseguire il richiamo alle funzioni di questo dovremo

salvare il modo video utilizzato dal programma di WP, la

schermata presente ecc. in modo che terminando il nostro lavoro

con l' utility sia possibile ripristinare l'ambiente originale

del WP.

Sempre nell'esempio precedente avrete notato l'uso delle

istruzioni CLI e STI.

Quando una routine d'interrupt e' chiamata il flag

d'interruzione e' settato in modo che non possa essere richiesto

nessun altro interrupt mascherabile.

Lasciare disabilitati questi interrupts per un tempo abbastanza

lungo puo' creare dei problemi in quanto potrebbero esserci

alcune periferiche i cui programmi agiscono in background che,

nel frattempo che la nostra utility e' attiva, eseguono chiamate.

Appena si presenta la possibilita' conviene riabilitare il tutto

mediante l'istruzione STI.

Il problema maggiore per la stesura di programmi residenti in

memoria e' dato dagli interrupts non rientranti.

Il termine "rientrante" mi lascia un po' perplesso in quanto non

so' se e' la traduzione esatta della parola inglese "reentrancy".

Tra questo tipo di interrupts troviamo quelli del Dos.

Mi spiego meglio.

Il Bios quando esegue una routine d'interrupt utilizza i registri

e lo stack per salvare alcuni valori usati durante

l'elaborazione.

Il Dos, al contrario, utilizza delle zone di memoria per eseguire

lo stesso compito.

Supponendo che venga effettuata una chiamata ad un servizio del

Dos questa verrebbe eseguita e alcuni valori verrebbero salvati

all'interno di variabili in memoria.

Se per puro caso venisse eseguita una seconda call al Dos mentre

la prima non e' ancora terminata si potrebbe verificare che i

dati salvati in una determinata area dal primo servizio vengano

ricoperti da quelli scritti dal secondo.

Questo ci spinge a pensare che nei nostri programmi residenti

dobbiamo vagliare l'istante in cui eseguire una call al Dos.

Un eventuale chiamata ad un servizio potrebbe avvenire solo

nell'istante in cui il Dos risulta essere libero.

Allo scopo di girare intorno a questo tipo di problema ci si

presenta una soluzione.

Tra gli interrupts e le calls non documentate visti

precedentemente c'era il servizio 34H dell'interrupt 21H (Find

active byte).

Il metodo per accertarsi se e' in corso o meno una call del Dos

e' quello di utilizzare questa funzione.

Questo servizio riporta il numero di chiamate al Dos che sono in

esecuzione.

Il valore riportato dal servizio puo' essere 0, nel caso che il

Dos sia libero, oppure maggiore di 0, nel caso che ci sia una

call attiva.

Questo detto in altre parole significa che durante la stesura di

un programma residente non potremo chiamare direttamente un

servizio del Dos ma potremo farlo chiamando una nostra funzione

che prima di eseguire la call dovra' accertarsi che il Dos non

sia attivo.

Un esempio come quello che segue potrebbe sembrare essere esatto:

........... ; ERRATO !!!!!!!

attivo dw ?,?

...........

jmp init

new_interrupt proc far

...........

mov ah,00H ;prima di richiedere la call 00H

call active ;dell'int 21H guarda se il DOS e'

int 21H ;attivo.

...........

new_interrupt endp

active proc near

push di

push bp

mov di,attivo

mov ax,attivo[2]

mov bp,ax

a_1: cmp BYTE PTR [bp][di],0

jne a_1

pop bp

pop di

ret

active endp

init: ...........

push es

mov ah,34H

int 21H

mov attivo,bx

mov attivo[2],es

pop es

...........

Purtroppo si rivela inefficace in quanto se nella routine active

ci si entra nell' istante in cui il Dos non e' attivo allora si

esce normalmente.

Nel caso in cui ci si entra quando l'active byte e' attivo allora

il programma si blocca miseramente in quanto ci si entra in un

loop infinito.

Il nostro programma occuperebbe per eseguire il loop tutto il

tempo macchina non dando la possibilita' al Dos di finire i

servizi che erano attivi e quindi di settare l'active byte a off.

La nostra funzione destinata a controllare lo stato del Dos

dovra' essere abbinata ad un interrupt come l'int 8.

Questo come saprete viene richiamato varie volte al secondo.

In altre parole potremmo scrivere una piccola routine collegata a

questo interrupt che testi tutte le volte che viene richiamato,

in genere 18.2 volte al secondo, l'active byte e che setti un

flag ad indicare che il Dos e' attivo o meno.

Dal nostro programma dovremo successivamente testare questo flag

per sapere se esiste o meno la possibilita' di richiamare una

call del Dos.

C'e' anche la possibilita' di utilizzare l'int 28H che come

abbiamo visto viene richiamato quando il Dos attende un input.

Un altro esempio legato all'active byte e' il seguente.

Si tratta di una routine collegata all'interrupt 1CH (Timer Tick)

che testa lo stato dell'active byte e lo stampa 18 volte al

secondo nell'angolo in alto a sinistra.

;

; Show Active Byte - ACTIVE.ASM

;

code segment para 'code'

assume cs:code, ds:code

org 100H

;

active dw ?,?

;

jmp init

;

new proc far

push ax

push ss

push bp

push ds

push di

push cx

push dx

push bx

push es

sti

;

mov ax,0B800H

push ds

mov ds,ax

mov cx,active[2]

mov bp,cx

mov di,active

;

mov al,BYTE PTR [bp][di]

add al,10

mov ds:[0],al

pop ds

;

pop es

pop bx

pop dx

pop cx

pop di

pop ds

pop bp

pop ss

pop ax

iret

new endp

;

init: ;

mov ah,25H

mov al,1CH

mov bx,SEG new

mov ds,bx

mov dx,OFFSET new

int 21H

;

push es

mov ah,34H

int 21H

mov active,bx

mov active+2,es

pop es

;

mov dx,OFFSET init+100H

int 27H

code ends

end

Potrebbe essere adottata per piccole routine il seguente caso.

L'interrupt 16H (o al limite il 9H) viene ridiretto in modo che

testi i caratteri in input alla ricerca del tasto di attivazione.

Quando questo viene intercettato la routine collegata

all'interrupt di input da tastiera setta un flag che indichera'

all'interrupt 8H lo stato di attivo.

Questo con il flag non settato tutte le volte che viene chiamato

ritorna mediante un IRET.

Nel caso che l'int 8H testi il flag e verifichi la condizione di

attivazione eseguira' le altre funzioni legate alla nostra

utility.

In questo caso la routine collegata all'int 8H non ha solo piu'

lo scopo di controllare lo stato del Dos ma bensi' quello di

eseguire il nostro programma in quanto questo difatti diventa

parte della routine di servizio dell'interrupt.

Nel seguente esempio viene utilizzato solo l'interrupt 16H

che intercetta CTRL F1 o CTRL F2 e attiva l'applicazione.

Il programma serve al monitoraggio delle porte seriali COM1 e

COM2.

;

;----------------------------------------------------------

; TSR.ASM

;----------------------------------------------------------

; NOTA: Il programma e' in grado di stabilire il tipo di

; scheda video installata. I tipi validi sono CGA ed

; Hercules.

;----------------------------------------------------------

;

attivable equ 0

noattiva equ 1

;

;

code segment para public 'code'

assume cs:code

;

;----------------------------------------------------------

; Salta a inizializzazione vettore interrupt 16H

;----------------------------------------------------------

; jmp init

;

;----------------------------------------------------------

; Vecchi vettori d'interrupts e flag indicanti attivazione

;----------------------------------------------------------

;

dtr_on db '1'

db '$'

dtr_off db '0'

db '$'

error db 10,10,13

db 7

db 'Non ci sono porte seriali'

db 10,13

db 'Impossibile installare Monitor'

db 10,10,13

db '$'

buffer dw 2000 dup(0)

m_1 db '+---[COM Monitor 1.0]---+'

db 10,13

db ': :'

db 10,13

db ': DCD RNG DSR CTS DTR :'

db 10,13

db ': +-+ +-+ +-+ +-+ +-+ :'

db 10,13

db ': : : : : : : : : : : :'

db 10,13

db ': +-+ +-+ +-+ +-+ +-+ :'

db 10,13

db ': :'

db 10,13

db '+-------------------------+'

db '$'

attivo db '1'

db '$'

non_att db '0'

db '$'

;

;

cur_type_ch db 0

cur_type_cl db 0

cur_pos_x db 0

cur_pos_y db 0

;

old_int_16 dw ?,?

utile db 0

flag db 0

status db 0

video_type dw ?

port_attive db 0


PAGINE

288

PESO

869.31 KB

AUTORE

Sara F

PUBBLICATO

+1 anno fa


DETTAGLI
Corso di laurea: Corso di laurea in ingegneria informatica
SSD:
Università: Firenze - Unifi
A.A.: 2013-2014

I contenuti di questa pagina costituiscono rielaborazioni personali del Publisher Sara F di informazioni apprese con la frequenza delle lezioni di Fondamenti di Informatica I e studio autonomo di eventuali libri di riferimento in preparazione dell'esame finale o della tesi. Non devono intendersi come materiale ufficiale dell'università Firenze - Unifi o del prof Vicario Enrico.

Acquista con carta o conto PayPal

Scarica il file tutte le volte che vuoi

Paga con un conto PayPal per usufruire della garanzia Soddisfatto o rimborsato

Recensioni
Ti è piaciuto questo appunto? Valutalo!

Altri appunti di Fondamenti di informatica i

Fondamenti di Informatica I – Linguaggio C
Dispensa
Fondamenti di Informatica I - Tutorial linguaggio c
Appunto
Fondamamenti di Informatica I - Programma
Dispensa
Appunti di Geometria e Algebra Lineare
Appunto