Newsgroup: it.comp.lang.c++
Titolo: Lettura della tastiera sotto DOS

Sto cercando un metodo decente per leggere la tastiera senza dover
passare attraverso il DOS e le paturnie del ritardo e ripetizione
automatica. Ho elaborato le rountine che allego, che sembrano funzinare, ma
mi sembrano tremendamente macchinose. Inoltre vorrei sapere se sono
completamente corrette. C'e' un metodo migliore? (E vabbe... legge di Yles:
c'e' sempre un modo migliore.) Vorrei pareri e consigli.
			 COME FUNZIONA LA COSA:
			 ----------------------
Ogni volta che la tastiera trasmette un dato, tramite l'interrupt $09 viene
richiamato l'interrupt $15 con la funzione $4F in AH e il codice di
scansione in AL. Questa routine si limita a tornare indietro con il flag di
riporto attivato (niente di più inutile). Sostituendo l'interrupt $15 con
un'altra routine si può elaborare quello che si vuole, facendo attenzione
che tutte le funzioni che non siano la $4F vengano elaborate dalla routine
originale. Con installa() si attiva il meccanismo, con disinstalla() lo si
disattiva. Un primo problema consiste nello svuotare il buffer della
tastiera, che altrimenti riempiendosi ben presto provoca il classico
odiosissimo beep. La funzione che ho usato e' corretta? O provoca qualche
problema? (Ricordo che da quello che ho potuto vedere funziona, ma non si
sa mai) Il secondo problema e' dato dal fatto che i tasti cursore, quando
vengono rilasciati, danno tutti lo stesso identico codice di scansione! (Il
codice di scansione e' normalmente compreso tra 0 e 127 per i tasti che
vengono premuti, lo stesso valore piu' 128 quando vengono rilasciati) Sara'
per questo che giochi come GP (1 e 2) usano le combinazioni piu' scomode,
anziche' quelle che sembrerebbero piu' logiche?.
(Fonte: Peter Norton - La Bibbia del Programmatore)
/*
    Routine di lettura della tastiera   -   by Marco Nava
    Target: Dos 16 bit            Modello di memoria: HUGE
    Testato con Borland C++ 4.52 sotto Windows '95
*/
#include <dos.h>
/* se necessario aggiungere #pragma inline (Il BASM sembra cavarsela
   comunque bene) */
void far interrupt ( *oldhandler)(); // Inserire ... tra parentesi se C++
void far interrupt tastiera()  // Inserire ... tra parentesi se C++
{
  unsigned char regah;
  unsigned char scancode;
  asm
  {
	 mov regah,ah      //  Prelevo AH
	 mov scancode,al   //  e AL con l'eventuale codice di scansione
  }
  if (regah != 0x4f)     // Se il servizio richiesto non è il $4F,
	 oldhandler();     // Procedo con l'elaborazione standard.
  else
	 {
		asm
		{
                  mov ax, 0x0c0b  /* Funzione di svuotamento del buffer,
                                     seguita da una fittizia...  */
                  int 0x21        // ...dell'Int $21 
                  cli             /* Spegne gli interrupt (se non erano
                                     gia' spenti, boh?) */
        	}
       		/*
			 Elaborazione del tasto il cui codice di scansione
			 è contenuto in scancode.
			 Durante l'elaborazione, gli interrupt sono disabilitati,
			 percio' si dovrebbero fare solo cose RAPIDE, come incrementare
			 la coordinata di uno sprite oppure attivare un flag
			 che il programma principale elaborerà POI una volta che
			 questa routine sarà terminata.
		*/
		asm
		{
		  stc    // Pone a 1 il flag di riporto, come richiesto dalla $4F
		  sti    // Riattiva gli interrupt
		}
	 }
}
void installa()
{
  oldhandler = getvect(0x15);
  setvect(0x15, tastiera);
}
void disinstalla()
{
  setvect(0x15, oldhandler);
}
-------------------------------------------------------
                  __    Marco "The MaN of Nark" Nava
   /\  /\    /\  / /
  /  \/  \/\/  \/ /             E-Mail:
 /_/\__/\_\_\/\__/          
the...@mbox.vol.it
-------------------------------------------------------

		}
la stc non serve a nulla perche' la chiamata all'interrupt effettua
una PUSHF, e quindi al ritorno la corrispondente POPF ripristina lo
stato del flags, per fare un bel lavoro dovresti modificare
direttamente il contenuto della locazione di memoria dove e' stata
fatta la PUSHF in modo che in uscita dalla funzione il carry assuma il
valore desiderato.
Gianpaolo Bottin
- Live Long and Prosper -

<HTML><BODY>
Mercury Mats wrote:&nbsp;
<BLOCKQUOTE TYPE=CITE><I>&gt;&gt;Un primo problema consiste nello svuotare
il buffer della</I>
<BR><I>&gt;&gt;tastiera, che altrimenti riempiendosi ben presto provoca il
classico</I>
<BR><I>&gt;&gt;odiosissimo beep. La funzione che ho usato e' corretta? O provoca
qualche</I>
<BR><I>&gt;&gt;problema?</I>
<BR>
<BR><I>&gt;di problemi ne provoca di sicuro ! Non puoi chiamare l'INT 21h da
una</I>
<BR><I>&gt;routine di interrupt. Il DOS non e' rientrante, se nel momento in
cui</I>
<BR><I>&gt;chiami quella funzione c'e' una chiamata all'INT 21h in corso puo'</I>
<BR><I>&gt;succedere un disastro.</I>
<BR>
<BR>Come si puo' ovviare alla non rientranza? Anch'io ho un problema
<BR>simile. Volevo intercettare una chimata di interrupt e in alcuni casi
<BR>re-indirizzarla a una mia funzione.
<BR>
<BR>**************************************&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \_/
<BR>I'm Mercury Mats&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
/ \&nbsp;&nbsp; fan #1 di
<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
gluc...@mv.itline.it
&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp; |
<BR>**************************************&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \_/&nbsp;
Sailor Mercury
<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
-|-
</BLOCKQUOTE>
Se il tuo problema &egrave; di chiamare una _tua_ funzione dopo aver intercettato
un interrupt, allora il problema della rientranza non si pone. Si pone
- e tanto! - se, invece, dalla tua ISR richiami una funzione del DOS; penso
comunque che fosse questo ci&ograve; che intendevi.
<BR>
<BR>Il problema &egrave; risolvibile, anche se bisogna rimboccarsi un po' le
maniche.&nbsp;
<BR>Per prima cosa, le basi (mi scuso se appaio pedante, ma questo &egrave;
un messaggio mandato sia a Mercury Mats che a it.comp.lang.c++, e quindi
non mi posso aspettare che tutti sappiano di cosa sto parlando).
<BR>Quando il DOS intercetta un interrupt 21h ed inizia una sua funzione, incrementa
di uno un contatore chiamato flag InDOS, e lo decrementa quando ha finito.
Quindi, quando questo contatore &egrave; a 0, il DOS &egrave; libero, e
lo si pu&ograve; chiamare senza problemi.&nbsp;
<BR>Esiste anche un altro contatore, chiamato flag CritErr (di errore critico).
Viene fornito perch&eacute; quando il DOS sta gestendo un errore critico,
decrementa la flag InDOS e incrementa la flag CritErr, ed un eventuale
chiamata potrebbe corrompere uno dei suoi stack o un'area di memoria usata,
bloccando il processo in corso e (probabilmente) &quot;ingrippando&quot;
la macchina.
<BR>
<BR>Ci sono due chiamate per verificare lo stato di queste due flag:
<BR>
<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INT 21h, AH = 34h&nbsp;
<BR>Questa funzione riporta in ES:BX l'indirizzo della flag InDOS. Siccome
questa funzione esegue sullo stack del DOS, non si pu&ograve; chiamarla
quando un'altra funzione DOS &egrave; in uso. E' meglio quindi chiamarla
all'inizio del programma, e conservare l'indirizzo in qualche variabile
per uso futuro.
<BR>
<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INT 21h, AH = 5D06h
<BR>Questa funzione, pur riportando anche altre informazioni, mette in DS:SI
l'indirizzo della flag CritErr. Come sopra per quanto riguarda l'uso.
<BR>
<BR>Quindi, prima di chiamare uno funzione DOS da una propria funzione, bisogna
controllare sia l'InDOS flag che la CritErr flag, e se entrambe sono a
0, si pu&ograve; procedere.
<BR>
<BR>Ma se non sono a zero? Si lascia tutto cos&igrave;?
<BR>
<BR>Una soluzione c'&egrave;: quando la propria funzione controlla i due flag,
e li trova &quot;occupati&quot;, pu&ograve; settare un &quot;semaforo&quot;,
una qualsiasi variabile globale.
<BR>
<BR>Il presupposto &egrave; che si sia &quot;intercettato&quot; prima l'interrupt
di clock (1Ch, se non ricordo male), scrivendo una propria funzione che
controlli la variabile semaforo: se questa &egrave; attivata, allora l'ISR
controlla le flag InDOS e CritErr, e se le trova libere, chiama la funzione
in attesa, ricordandosi di &quot;disattivare&quot; il semaforo per impedire
ulteriori chiamate non volute alla medesima funzione. Se invece le due
flag sono occupate, passa il controllo al vettore di INT 1Ch (che abbiamo
precedentemente salvato... vero? ;-)) ), cosicch&eacute; la routine di
clock vada avanti tranquillamente. Al prossimo 1/18.2 di secondo, si ritenta.
<BR>
<BR>C'&egrave; per&ograve; un altro ma: quando viene chiamata una funzione
DOS per la lettura da tastiera, &egrave; perfettamente possibile che chi
dovrebbe premere un tasto stia in quel momento bevendosi un caff&eacute;,
e lo faccia per altre due ore. In questo caso, pur apparendo attivo, il
DOS &egrave; in realt&agrave; libero (o meglio, in loop). Disgrazia!
<BR>Per fortuna Babbo Gates ci &egrave; venuto in aiuto, fornendo il DOS di
un provvidenziale interrupt, il 28h, detto anche &quot;idle interrupt&quot;.
Quando una funzione DOS di lettura da tastiera entra in un loop, viene
continuamente chiamato questo interrupt, la cui ISR normale &egrave; semplicemente
un' &quot;iret&quot;. Noi possiamo agganciare anche questa funzione, scrivendo
un ISR che controlli se ci sono &quot;semafori&quot; pendenti e in tal
caso rispondendo (dall'INT 28h si pu&ograve; senza problemi chiamare tutte
le funzioni DOS dalla 0Ch in su).&nbsp;
<BR>Quest'ultimo interrupt &egrave; particolarmente utile nel caso di TSR,
che altrimenti si bloccherebbero se il sottostante programma aspettasse
un input dall'utente.
<BR>
<BR>
<BR>Spero di essere stato di aiuto. Prima di chiudere, permettetemi di ringraziare
due individui che (pur indirettamente) mi hanno aiutato nel capire (spero!)
quanto ho scritto sopra:
<BR>
<BR>Randall Hyde, con &quot;The Art of Assembly Language&quot;&nbsp;
<BR>&nbsp; e
<BR>Ralf Brown, con la sua mastodontica &quot;Interrupt List&quot;, giunta
oramai alla versione 5.3
<BR>
<BR>Se avete occasione, procuratevi una copia di quest'ultima in qualsiasi
mirror SIMTELNET, directory /pub/simtelnet/msdos/info (credo): i file sono
intlist53x.zip, dove x sta per a,b,c,d,etc,etc.
<BR>--&nbsp;
<BR>
<BR>&nbsp;Andrea Glorioso Pelliccioni&nbsp;
<BR>&nbsp;&nbsp; 
sa...@intercity.it
<BR>&
nbsp;glor...@intercity.it
&nbsp;
<BR>
</BODY>
</HTML>


per quanto mi e' parso di capire e' tutto corretto, pero' vorrei
aggiungere una cosa: NON POSTARE IN HTML !! 
Non tutti i programmi sono in grado di visualizzarlo direttamente e in
piu' occupa un sacco di spazio inutile.
     _______ _____         
    / _____// ___ \     Gianpaolo Bottin  - 
bott...@tin.it
 
   / /____ / /__/ /     Bruino (TO) - Italy
  / //_  // ___  <      
 / /__/ // /__/ /       "Se il programma e' stato concepito in modo tale che i dati
/______//______/        incorretti siano rifiutati, ci sara' sempre un idiota 
                        abbastanza ingegnoso per trovare il modo di farli passare."

Gianpaolo Bottin wrote:
  per quanto mi e' parso di capire e' tutto corretto, pero' vorrei
  aggiungere una cosa: NON POSTARE IN HTML !!
  Non tutti i programmi sono in grado di visualizzarlo direttamente e
  in
  piu' occupa un sacco di spazio inutile.
       _______ _____
      / _____// ___ \     Gianpaolo Bottin  - 
bott...@tin.it
     / /____ / /__/ /     Bruino (TO) - Italy
    / //_  // ___  <
   / /__/ // /__/ /       "Se il programma e' stato concepito in modo
  tale che i dati
  /______//______/        incorretti siano rifiutati, ci sara' sempre
  un idiota
                          abbastanza ingegnoso per trovare il modo di
  farli passare."
Mi cospargo il capo di cenere!
Non era mia intenzione postare in HTML: solo che uso una beta del
Netscape Communicator 4.0, e mi ha postato in HTML senza che gli dicessi
niente (non mi ha neanche avvertito come fa di solito che il messaggo
non era in plain text).
Mi dispiace molto; non accadrà più.
--
 Andrea Glorioso Pelliccioni
 
sa...@intercity.shiny.it
 
glor...@intercity.shiny.it

