Assembly Avanzato con NASM

Capitolo 13: Il controller PS/2 per tastiera e mouse


Tra le varie inizializzazioni effettuate dal BIOS durante la fase di boot, troviamo anche quelle relative ai controller per l'interfacciamento con tastiera e mouse; dopo il boot, tali periferiche vengono ulteriormente impostate anche dal SO. Tra le varie impostazioni relative alla tastiera si possono citare, ad esempio, la velocità di ripetizione quando si tiene premuto un tasto (typematic rate); analogamente, nel caso del mouse, viene impostata la velocità di campionamento degli eventi, la sensibilità di movimento del cursore, etc.
Una volta effettuate queste impostazioni iniziali, il SO provvede ad installare appositi driver per la gestione di tastiera e mouse; nel caso del DOS, ad esempio, editando il file C:\AUTOEXEC.BAT possiamo notare la riga:
KEYB IT,,C:\DOS\KEYBOARD.SYS
Tale riga carica in memoria il driver per la tastiera (KEYBOARD.SYS) e imposta la nazionalità italiana.
Analogamente, nel caso di FreeDOS, editando il file C:\AUTOEXEC.BAT si nota, oltre alla riga per il caricamento del driver della tastiera, anche l'altra riga:
MOUSE
Tale riga carica in memoria il driver del mouse, il quale provvede a gestire tutti gli eventi relativi alla periferica. Nel Capitolo 3 dedicato al PIC 8259 - Programmable Interrupt Controller, è stato presentato un esempio relativo alla programmazione diretta della tastiera attraverso l'installazione di una ISR personalizzata; in tale esempio è stato spiegato che l'interfacciamento tra tastiera e PC avviene tramite un apposito dispositivo denominato 8042 controller, che con l'evolversi dell'hardware è stato integrato nel PS/2 controller.
Un aspetto interessante è che il controller PS/2 dispone di due connettori di cui il primo è destinato alla tastiera, mentre il secondo permette il collegamento di una ulteriore periferica che, nel caso più frequente è un mouse, ma può essere anche un touchpad o una trackball; in questo capitolo ci occuperemo principalmente dei dettagli relativi all'interfacciamento tra mouse e controller PS/2.

La tastiera e il mouse PS/2 sono pienamente supportati da VirtualBox.

13.1 Evoluzione dell'hardware per il controllo di tastiera e mouse

Nei vecchi PC IBM di classe XT, per il collegamento della tastiera è presente un tipico connettore standard a 5 pin di tipo DIN (acronimo tedesco per Deutsches Institut für Normung); la Figura 13.1 mostra il connettore femmina sul computer. La Figura 13.2 illustra il significato (pinout) dei 5 pin. Per quanto riguarda gli aspetti legati all'interfaccia, sul computer XT è presente un controller 8042 denominato on-board controller o OBC, mentre sulla tastiera è presente un chip 8048.
Il chip 8048 non è programmabile, per cui le comunicazioni tra 8048 e OBC sono di tipo unidirezionale, dalla tastiera al computer. Ogni volta che si preme o si rilascia un tasto, l'8048 determina il relativo scan code e lo invia all'OBC; l'OBC a sua volta invia una IRQ alla linea 1 del PIC Master e rende disponibile lo scan code nella porta B del chip 8255, che sugli XT serve per interfacciare la CPU alle varie periferiche (tra le quali la tastiera).
Come sappiamo, alla IRQ1 viene associata una INT 09h (Figura 3.10 del Capitolo 3); la relativa ISR può così prelevare lo scan code dalla porta B del chip 8255 per sottoporlo alle varie elaborazioni.

Sui PC XT non è presente alcuna porta specifica per il mouse; all'epoca, risultano diffusi i mouse seriali da collegare alla porta seriale del computer, nota anche come RS-232.

Con l'avvento dei PC IBM di classe AT, anche sulla tastiera viene montato un controller programmabile 8042 (o superiore), denominato keyboard controller o KBC; ciò rende possibile la programmazione della tastiera e le comunicazioni bidirezionali tra OBC e KBC.
Nei primi modelli, il connettore per la tastiera AT è lo stesso a 5 pin di Figura 13.1.
Ogni volta che l'utente preme o rilascia un tasto, il KBC determina il relativo scan code e lo invia all'OBC; l'OBC a sua volta invia una IRQ alla linea 1 del PIC Master, a cui è associata una INT 09h. La relativa ISR, una volta chiamata, può prelevare il precedente scan code dalla porta 60h dell'OBC per sottoporlo alle varie elaborazioni.

Anche sui PC AT non risulta presente alcuna porta specifica per il mouse; pure in questo caso, quindi, si può sopperire a tale mancanza con un mouse seriale.

La vera svolta arriva quando IBM presenta la nuova architettura PS/2; una delle principali novità è l'integrazione di numerose funzioni in un sofisticato controller denominato PS/2 controller. Tra le varie funzioni, il nuovo controller fornisce anche il supporto per la tastiera e per un secondo dispositivo che può essere un mouse, un touchpad o una trackball; i due connettori per il collegamento di tali periferiche sono di tipo Mini-DIN a 6 pin, come illustrato in Figura 13.3. La Figura 13.4 illustra il significato (pinout) dei 6 pin. Uno dei due connettori è dedicato espressamente alla tastiera, mentre all'altro, come già detto, si può collegare un mouse, un touchpad o una trackball.
I due connettori non sono interscambiabili; collegando la tastiera al connettore sbagliato, può anche verificarsi un blocco del computer durante il boot!
Per evitare tale problema, si è deciso per convenzione di assegnare il colore viola al connettore della tastiera e il colore verde all'altro connettore; la Figura 13.5 illustra questo aspetto. In termini di interfaccia, la tastiera PS/2 risulta compatibile con la tastiera AT (ma non con quella XT); valgono quindi tutte le considerazioni esposte in precedenza (IRQ1, INT 09h, porta 60h, etc).

Per quanto riguarda il mouse PS/2, la sua gestione è del tutto simile a quella della tastiera; in effetti, i mouse vengono assimilati a delle mini-tastiere.
Ogni evento del mouse PS/2 (movimento, pressione/rilascio dei pulsanti), viene elaborato dalla logica di controllo della periferica, la quale provvede ad inviare un pacchetto di dati al controller PS/2; il controller PS/2 invia una IRQ alla linea 4 del PIC Slave (IRQ12 - Mouse PS/2). Alla IRQ12 risulta associata una INT 74h (Figura 3.11 del Capitolo 3); la relativa ISR può prelevare il precedente pacchetto di dati dalla porta 60h del controller PS/2 per sottoporlo alle varie elaborazioni.

L'evoluzione successiva per tastiera e mouse è consistita nel passaggio all'interfaccia Universal Serial Bus (o USB); in ogni caso, le periferiche PS/2 hanno mantenuto una notevole diffusione, grazie all'efficienza di tale interfaccia e alla possibilità di lasciare libere le porte USB, presenti sempre in numero insufficiente sui PC.

13.2 Protocollo di comunicazione con tastiera e mouse PS/2

Le comunicazioni bidirezionali tra tastiera/mouse e controller PS/2 sono di tipo seriale sincrono; in sostanza, i dati transitano in serie attraverso la linea connessa al pin DATA e risultano sincronizzati grazie al segnale di clock presente sulla linea connessa al pin CLK. Il segnale di clock viene sempre generato dalla periferica PS/2.

Il dato unitario (Serial Data Unit o SDU) che transita lungo la linea DATA, in un verso o nell'altro, è un blocco da 11 bit che assume le caratteristiche illustrate in Figura 13.6. Ogni SDU contiene quindi 1 byte di dati (gli 8 bit da DB0 a DB7); si può notare che il bit meno significativo (DB0) arriva per primo.

Parità dispari significa che gli 8 bit di dati più il bit di parità devono contenere un numero dispari di 1; in caso contrario, la logica di controllo ne deduce che si è verificato un errore di trasmissione e può così chiedere la ritrasmissione del dato.

Ciascuna delle linee CLK e DATA può assumere i due possibili stati LOW (basso) o HIGH (alto), assimilabili ai livelli logici 0 e 1. Quando entrambe le linee assumono lo stato HIGH, si dice che il BUS (canale di comunicazione) è IDLE (inattivo); solamente in questo stato la periferica può inviare dati al controller PS/2.

13.2.1 Comunicazioni dalla periferica al controller PS/2

Nel seguito, la periferica PS/2 viene indicata con il termine "device", mentre il controller PS/2 viene indicato con il termine "sistema".

Le comunicazioni dal device al sistema si svolgono secondo la seguente sequenza: In ogni momento, nel corso delle fasi appena descritte, il sistema può bloccare la trasmissione; a tale proposito, il sistema stesso pone la linea DATA a LOW quando essa è HIGH o pone la linea CLK a LOW quando essa è HIGH. A questo punto il sistema può, ad esempio, inviare un comando al device; se ciò accade, il device stesso in seguito deve ritrasmettere il dato precedentemente interrotto.

La Figura 13.7 illustra le considerazioni appena svolte; il colore rosso indica le operazioni effettuate dal device. Come si può notare, inizialmente entrambe le linee CLK e DATA sono HIGH (stato IDLE), per cui il device può iniziare la trasmissione. Prima di tutto la linea DATA viene posta a LOW per rappresentare lo start bit (STRT); a questo punto iniziano i cicli di clock, con lo start bit che viene trasferito al sistema durante il fronte di discesa del segnale CLK. I successivi 9 cicli di clock trasferiscono, sempre durante il fronte di discesa, gli 8 data bit (da DB0 a DB7) e il bit di parità (PAR).
Durante l'undicesimo ciclo di clock, la linea DATA viene posta a HIGH (livello logico 1) per indicare la fine della trasmissione (STOP); a questo punto, il sistema può bloccare la linea DATA (o CLK) tenendola su LOW per inibire una ulteriore trasmissione ed avere quindi il tempo di elaborare il dato appena ricevuto.

I dati generati dalla tastiera rappresentano codici di scansione dei tasti premuti o rilasciati e risposte (acknowledgments) ai comandi inviati dal sistema; come sappiamo, ogni codice di scansione è costituito da 1 o più byte. Ciascuno dei byte di un codice di scansione arriva al sistema sotto forma di SDU e produce una IRQ1 (se le IRQ sono abilitate); di conseguenza, se da una nostra ISR vogliamo leggere un codice di scansione formato, ad esempio, da 3 byte, dobbiamo elaborare 3 IRQ1 consecutive (come è stato illustrato nel Capitolo 3).

I dati generati dal mouse rappresentano lo stato ON/OFF dei pulsanti, la posizione del cursore sullo schermo e risposte (acknowledgments) ai comandi inviati dal sistema. Lo stato del mouse (pulsanti e posizione del cursore) viene inviato al sistema attraverso 3 SDU consecutive, che producono quindi una sola IRQ12 (se le IRQ sono abilitate); si ottiene così un pacchetto di dati (Mouse Data Packet) da 3 byte, la cui struttura è illustrata in Figura 13.8. La logica di controllo del mouse PS/2 legge continuamente lo stato dei pulsanti e il movimento del cursore; il numero di letture al secondo rappresenta la frequenza di campionamento, la cui impostazione predefinita è di 100 letture al secondo.
Per gestire il movimento del cursore vengono usati due contatori, uno per gli spostamenti in orizzontale e l'altro per quelli in verticale; tali due spostamenti, da inserire nel prossimo pacchetto di dati da inviare al sistema, vengono calcolati rispetto alla posizione assunta dal cursore durante il precedente pacchetto di dati inviato al sistema (si tratta quindi di spostamenti relativi e non di coordinate X, Y).
I due contatori vengono aggiornati in base al parametro risoluzione, la cui impostazione predefinita è di 4 conteggi per ogni millimetro. In sostanza, se spostiamo il cursore di 1 mm in orizzontale, il relativo contatore conterrà il valore 4; analogamente, se spostiamo il cursore di 2 mm in verticale, il relativo contatore conterrà il valore 4*2=8.
Gli spostamenti che il mouse invia al sistema, possono essere alterati attraverso il parametro fattore di scala, la cui impostazione predefinita è 1:1, Se il fattore di scala è 1:1, il mouse invia al sistema spostamenti identici a quelli calcolati; se, invece, si imposta il fattore di scala a 2:1, il mouse invia al sistema spostamenti doppi rispetto a quelli calcolati.
Come si può notare in Figura 13.8, lo spostamento relativo in orizzontale (Delta X) viene inviato nel secondo BYTE, mentre lo spostamento relativo in verticale (Delta Y) viene inviato nel terzo BYTE del Mouse Data Packet. Si tenga presente che tali spostamenti sono rappresentati come interi relativi a 8 bit in complemento a 2.

Il primo BYTE del Mouse Data Packet contiene altre importanti informazioni.
I bit in posizione 0, 1 e 2 rappresentano, rispettivamente, lo stato del pulsante sinistro, di quello destro e di quello centrale; chiaramente, per ogni pulsante è sufficiente un solo bit in quanto lo stato può essere 1 (premuto) o 0 (rilasciato).
Il bit in posizione 3 vale sempre 1.
I bit in posizione 4 e 5 rappresentano, rispettivamente, il segno di Delta X e quello di Delta Y; il valore 0 indica segno positivo, mentre il valore 1 indica segno negativo. Lo spostamento in orizzontale viene considerato positivo se avviene verso destra e negativo se avviene verso sinistra; analogamente, lo spostamento in verticale viene considerato positivo se avviene verso l'alto e negativo se avviene verso il basso. Considerando che Delta X e Delta Y sono interi relativi a 8 bit in complemento a 2, con questa tecnica possiamo rappresentare spostamenti compresi tra -128 e +127.
Nel caso di spostamenti molto rapidi del cursore, Delta X e/o Delta Y possono superare gli estremi -128 e +127, andando quindi in overflow; in tal caso, viene posto a 1 il corrispondente bit in posizione 6 e/o 7.

13.2.2 Comunicazioni dal controller PS/2 alla periferica

Le comunicazioni dal sistema al device si svolgono secondo la seguente sequenza: La Figura 13.9 illustra le considerazioni appena svolte; il colore rosso indica le operazioni effettuate dal device, mentre il colore verde indica le operazioni effettuate dal sistema. Come si può notare, prima di tutto il sistema pone la linea CLK a LOW; dopo un certo intervallo di tempo, lo stesso sistema pone la linea DATA a LOW per rappresentare lo start bit (STRT). Questa sequenza indica al device che il sistema sta per inviare un comando.
Il device risponde attivando il segnale di clock, con lo start bit che viene trasferito al device stesso durante il fronte di salita. I successivi 9 cicli di clock trasferiscono, sempre durante il fronte di salita, gli 8 data bit (da DB0 a DB7) e il bit di parità (PAR).
Durante l'undicesimo ciclo di clock, la linea DATA viene posta a HIGH (livello logico 1) per indicare la fine della trasmissione (STOP); durante il dodicesimo ciclo di clock, il device invia il bit di risposta o acknowledgment (ACK) ponendo la linea DATA prima a LOW e poi a HIGH.

I dati generati dal sistema rappresentano, in genere, comandi da inviare ai device.

13.3 Programmazione del controller PS/2

La Figura 13.10 mostra uno schema a blocchi con le varie connessioni tra il controller PS/2, le periferiche PS/2 e il resto del sistema (CPU, PIC, etc); con il termine MOUSE viene indicata genericamente una qualunque periferica PS/2 secondaria, che può essere un mouse, un touchpad o una trackball. Possiamo suddividere questo schema a blocchi in due parti principali. Sulla destra vediamo l'interfaccia di comunicazione che permette al controller PS/2 di comunicare con le periferiche attraverso il protocollo analizzato in precedenza; sulla sinistra vediamo come il controller PS/2 risulti interfacciato al resto del sistema (in particolare, la CPU e i due PIC).
Tutta la parte relativa alle comunicazioni tra controller PS/2 e periferiche viene gestita dall'hardware; ovviamente, tale aspetto ci può interessare solo se siamo progettisti di hardware. Nel nostro caso, ci concentreremo sugli aspetti legati alla programmazione del controller PS/2; a tale proposito, dobbiamo analizzare in dettaglio la parte sinistra dello schema di Figura 13.10.

Il controller PS/2 ci mette a disposizione un input buffer, un output buffer, un control register e uno status register; queste quattro aree di memoria interna del controller, sono associate alle porte hardware illustrate in Figura 13.11. Ogni volta che una periferica invia una informazione, sotto forma di SDU, il controller PS/2 estrae dalla SDU stessa il dato associato da 1 byte e lo posiziona nell'area da 8 bit dell'output buffer; come già sappiamo, tale dato può essere un codice di scansione generato dalla tastiera, lo stato dei pulsanti del mouse, la posizione del cursore del mouse, una risposta generata da una periferica PS/2 in seguito ad un comando inviato dal controller, etc.
Il metodo più efficiente per l'accesso a queste informazioni è quello delle IRQ; supponiamo quindi che tale metodo sia stato abilitato secondo le procedure che verranno illustrate più avanti. Se il dato è arrivato dalla tastiera, il controller invia un segnale IRQ all'ingresso IR1 del PIC Master; si tratta quindi di una IRQ1. Se il dato è arrivato dal mouse, il controller invia un segnale IRQ all'ingresso IR4 del PIC Slave; si tratta quindi di una IRQ12.
La richiesta di interruzione giunge alla CPU la quale associa la INT 09h alla IRQ1 e la INT 74h alla IRQ12; le relative ISR, una volta chiamate, possono leggere il dato in oggetto accedendo in lettura alla porta 60h (output buffer).

Il controller può inviare comandi alle periferiche PS/2; a tale proposito, il comando da inviare deve essere inserito nel control register accedendo in scrittura alla porta 64h. Questo compito può essere svolto anche da noi se, ad esempio, vogliamo inviare comandi di configurazione ad una periferica; in ogni caso, il procedimento da seguire consiste ugualmente nello scrivere il comando nel control register attraverso la porta 64h.
Alcuni comandi richiedono informazioni supplementari; tali informazioni devono essere inserite nell'input buffer accedendo in scrittura alla porta 60h.
Se i comandi e le informazioni supplementari vengono inviate da noi, dobbiamo svolgere tale compito accedendo al control register e all'input buffer attraverso la CPU; non è previsto, infatti, l'invio di dati verso le periferiche con il metodo delle IRQ.

Tutte le operazioni in corso, relative alla trasmissione o alla ricezione dei dati, possono essere monitorate attraverso lo status register; a tale proposito, bisogna accedere in lettura alla porta 64h. La Figura 13.12 mostra la struttura dello status register. Ogni volta che una periferica invia un dato, il controller salva tale dato nell'output buffer e pone a 1 il campo output buffer status; come già sappiamo, il dato può essere una risposta ad un comando, un codice di scansione della tastiera, la posizione del cursore del mouse, etc.
Se da una nostra ISR vogliamo leggere il dato dall'output buffer attraverso la porta 60h, dobbiamo prima testare il bit in posizione 0 di Figura 13.12 per assicurarci che esso valga 1; se tale bit vale 0, significa che l'output buffer è vuoto.

Se vogliamo inviare un comando ad una periferica, dobbiamo scrivere il relativo codice nel control register attraverso la porta 64h e un eventuale parametro nell'input buffer attraverso la porta 60h; il controller provvede a leggere il comando e ad inviarlo alla periferica.
Durante la fase di trasmissione del comando, il controller pone a 1 il bit in posizione 1 di Figura 13.12; ne consegue che, prima di scrivere un comando nell'input buffer, dobbiamo assicurarci che tale bit valga 0 (buffer di input vuoto).

Il bit in posizione 2 (System flag) di Figura 13.12 viene posto a 1 per indicare che una periferica ha superato l'autodiagnosi in seguito ad un comando di reset.

Il bit in posizione 3 (Command/Data) di Figura 13.12 vale 0 se un dato è stato scritto nell'input buffer attraverso la porta 60h; tale bit vale invece 1 se un comando è stato scritto nel control register attraverso la porta 64h.

Il bit in posizione 4 (Keyboard lock) di Figura 13.12 viene posto a 0 quando è attiva una password che blocca la tastiera (vedere più avanti); tale bit viene invece posto a 1 quando non è attiva alcuna password e la tastiera è quindi libera.

Il bit in posizione 5 (Auxiliary device) di Figura 13.12 viene posto a 1 per indicare che l'output buffer ha ricevuto un dato dal mouse; tale bit viene invece posto a 0 per indicare che l'output buffer ha ricevuto un dato dalla tastiera.
Ovviamente, questo bit è significativo quando il bit 0 dello status register vale 1 (buffer di output pieno).

Il bit in posizione 6 (General Timeout) di Figura 13.12 viene posto a 1 per indicare che la tastiera non ha risposto al controller entro i limiti di tempo stabiliti; in caso contrario, tale bit viene posto a 0.

Il bit in posizione 7 (Parity Error) di Figura 13.12 viene posto a 1 per indicare che un dato inviato dalla tastiera o dal mouse contiene un errore di parità; in caso contrario, tale bit viene posto a 0.

13.3.1 Comandi destinati al controller PS/2

Analizziamo ora i principali comandi che il programmatore può inviare al controller. Questo comando legge 1 byte dalla memoria interna del controller; l'indirizzo (offset) da cui leggere viene specificato attraverso i bit nelle posizioni da 0 a 5 del comando stesso. Il byte letto viene salvato nell'output buffer.
Il caso più importante è rappresentato dal comando 20h (00100000b) che consiste nel leggere 1 byte all'offset 0 (00000b) nella RAM interna del controller; il dato restituito nell'output buffer prende il nome di Controller Command Byte ed assume la struttura mostrata in Figura 13.13. Se il bit in posizione 0 vale 1, il controller invia una IRQ alla linea 1 del PIC Master (IRQ1) ogni volta che un nuovo dato giunto dalla tastiera è stato salvato nell'output buffer.

Se il bit in posizione 1 vale 1, il controller invia una IRQ alla linea 4 del PIC Slave (IRQ12) ogni volta che un nuovo dato giunto da un device secondario (mouse, touchpad o trackball) è stato salvato nell'output buffer.

Il valore del bit in posizione 2 rispecchia quello del bit nella stessa posizione dello status register (Figura 13.12).

Se il bit in posizione 4 vale 1, la tastiera viene disabilitata tenendo su LOW la relativa linea CLK; finché permane questo stato, la tastiera non può inviare dati al controller.

Se il bit in posizione 5 vale 1, il device secondario viene disabilitato tenendo su LOW la relativa linea CLK; finché permane questo stato, il device secondario non può inviare dati al controller.

Se il bit in posizione 6 vale 1, i codici di scansione interni in arrivo dalla tastiera vengono convertiti dal controller nei corrispondenti codici di scansione appartenenti al Set 1 (vecchie tastiere XT) prima di essere salvati nell'output buffer; se il bit vale 0, i codici di scansione interni in arrivo dalla tastiera vengono salvati direttamente nell'output buffer (tastiere AT e PS/2 che supportano i codici di scansione appartenenti al Set 2).

Vediamo un esempio che ha validità generale per l'invio di comandi al controller; in questo caso, inviamo al controller il comando 20h (Read from Controller RAM) per avere come risposta nell'output buffer il Controller Command Byte. Questo comando scrive 1 byte nella memoria interna del controller; l'indirizzo (offset) in cui scrivere viene specificato attraverso i bit nelle posizioni da 0 a 5 del comando stesso. Dopo aver ricevuto questo comando, il controller attende che il byte da scrivere venga posizionato nell'input buffer.
Il caso più importante è rappresentato dal comando 60h (01100000b) che consiste nello scrivere 1 byte di dati all'offset 0 (00000b) nella RAM interna del controller; il dato da scrivere è il Controller Command Byte di Figura 13.13 nel caso in cui il programmatore intenda modificare le impostazioni predefinite. Nell'esempio presentato più avanti, viene utilizzato proprio il comando 60h per abilitare la linea CLK del mouse e gestire tale periferica tramite la IRQ12; si tratta quindi di porre a 1 il bit 1 e a 0 il bit 5 del Controller Command Byte (Figura 13.13). Questo comando verifica se è installata una password per la tastiera. Il controller salva la risposta nell'output buffer e genera una IRQ1; il valore FAh indica che la password è installata, mentre il valore F1h indica che la password non è installata. Questo comando permette di inizializzare una nuova password per la tastiera; la vecchia password viene persa. Una volta ricevuto questo comando, il controller legge l'input proveniente dalla tastiera e tratta i codici di scansione interni ricevuti, come caratteri che formeranno la nuova password; il codice 00h (NULL) segna la fine della nuova password.
La password può occupare sino a 7 byte; è consentito solo l'uso di tasti i cui codici di scansione non superano il valore 7Fh. Questo comando rende attiva la password memorizzata nel controller; se la password non esiste, il comando viene ignorato.
Quando la password è attiva, il controller si limita a comparare continuamente il flusso di codici di scansione proveniente dalla tastiera, con i codici di scansione che formano la password stessa; finché il flusso non coincide con la password, tutte le informazioni provenienti dalla tastiera vengono rese inaccessibili e non è neppure consentito inviare comandi alla periferica. Nel momento in cui il controller riceve un flusso che coincide con la password, il normale funzionamento della tastiera (ricezione dati e invio comandi) viene ripristinato. Questo comando pone a 1 il bit 5 del Controller Command Byte (Figura 13.13); di conseguenza, il controller disabilita la periferica PS/2 secondaria tenendo su LOW la relativa linea CLK. Questo comando pone a 0 il bit 5 del Controller Command Byte (Figura 13.13); di conseguenza, il controller abilita la periferica PS/2 secondaria rilasciando la relativa linea CLK. Questo comando pone a 1 il bit 4 del Controller Command Byte (Figura 13.13); di conseguenza, il controller disabilita la tastiera tenendo su LOW la relativa linea CLK. Questo comando pone a 0 il bit 4 del Controller Command Byte (Figura 13.13); di conseguenza, il controller abilita la tastiera rilasciando la relativa linea CLK. Quando il controller riceve questo comando, legge il contenuto della sua porta di input e lo salva nell'output buffer.
La porta di input del controller è rappresentata dai pin del microchip (8042 o superiore) elencati in Figura 13.14. Quando il controller della tastiera riceve questo comando, legge il contenuto dei bit da 0 a 3 della sua porta di input e lo salva nei bit da 4 a 7 dello Status Register (Figura 13.12). Questo comando non è supportato dal controller del mouse. Quando il controller della tastiera riceve questo comando, legge il contenuto dei bit da 4 a 7 della sua porta di input e lo salva nei bit da 4 a 7 dello Status Register (Figura 13.12). Questo comando non è supportato dal controller del mouse. Quando il controller riceve questo comando, legge il contenuto della sua porta di output e lo salva nell'output buffer. Prima di inviare questo comando, è necessario accertarsi che il bit 0 dello Status Register valga 0 (output buffer vuoto).
La porta di output del controller è rappresentata dai pin del microchip (8042 o superiore) elencati in Figura 13.15. (OBF = Output Buffer Full) Quando il controller riceve questo comando, attende che un dato da 1 byte venga scritto nell'input buffer; tale dato viene poi inviato alla porta di output. Quando il controller riceve questo comando, attende che un dato da 1 byte venga scritto nell'input buffer; tale dato viene poi salvato nell'output buffer facendolo apparire come se provenisse dalla tastiera. Successivamente, lo stesso controller genera una IRQ1 (se le interruzioni sono abilitate). Quando il controller riceve questo comando, attende che un dato da 1 byte venga scritto nell'input buffer; tale dato viene poi salvato nell'output buffer facendolo apparire come se provenisse dal device secondario. Successivamente, lo stesso controller genera una IRQ12 (se le interruzioni sono abilitate). Quando il controller riceve questo comando, attende che un dato da 1 byte venga scritto nell'input buffer; tale dato viene poi trasmesso al device secondario.
Il codice D4h è molto importante in quanto ci permette di specificare al controller che il contenuto dell'input buffer è un comando destinato al device secondario e non alla tastiera. Quando il controller riceve questo comando, legge il contenuto della sua porta di test e salva il risultato nell'output buffer; il bit 0 del risultato è relativo al test T0 (stato della linea CLK della tastiera), mentre il bit 1 del risultato è relativo al test T1 (stato della linea CLK del device secondario).
La porta di test del controller è rappresentata dai pin del microchip (8042 o superiore) elencati in Figura 13.16. Quando il controller riceve questo comando, invia un impulso per circa 6 microsecondi a specifici bit nelle posizioni da 0 a 3 della sua porta di output. I bit a cui inviare gli impulsi devono essere specificati ponendo a 0 i corrispondenti bit nelle posizioni da 0 a 3 del comando stesso; ad esempio, il comando FEh (11111110b) indica che vogliamo inviare un impulso al bit 0 (reset CPU) della porta di output del controller.

13.3.2 Comandi destinati alla tastiera PS/2

Durante il Power On Self Test del BIOS (o quando viene inviato un comando reset), la tastiera viene inizializzata con le seguenti impostazioni: Se teniamo premuto un tasto (ad esempio, il tasto 'A') in un editor di testo o in un terminale, possiamo notare che la prima 'A' compare immediatamente; dopo un certo ritardo di tempo compaiono poi una serie di 'A' ad una determinata frequenza. Il ritardo di tempo iniziale prende il nome di Typematic Delay, mentre la frequenza con cui compaiono le 'A' successive alla prima prende il nome di Typematic Rate; il Typematic Delay è espresso in millisecondi, mentre il Typematic Rate è espresso in cps o "caratteri per secondo".
Dopo il reset, tutti i tasti risultano typematic (cioè, assumono il comportamento appena descritto) e producono il codice di scansione, sia quando vengono premuti (make), sia quando vengono rilasciati (break).

Analizziamo ora i principali comandi che il programmatore può inviare alla tastiera. Questo comando permette di modificare lo stato dei LED della tastiera relativi ai tasti "Num Lock", "Caps Lock" e "Scroll Lock"; il nuovo stato deve essere specificato in seguito nell'input buffer e assume la struttura mostrata in Figura 13.17. Vediamo un esempio che ha validità generale per l'invio di comandi alla tastiera; in questo caso, inviamo alla tastiera il comando EDh (Set/Reset mode indicators) per impostare i LED a 00000110b (Scroll Lock spento, Num Lock acceso, Caps Lock acceso). Questo esempio funziona su un vero ambiente DOS; nel caso di un emulatore o di una macchina virtuale, è probabile che non succeda nulla in quanto i LED vengono gestiti dal sistema host.
Innanzi tutto, definiamo una procedura wait4empty il cui scopo è attendere che l'input buffer sia vuoto. A questo punto, possiamo scrivere il seguente codice: Quando la tastiera riceve questo comando, risponde semplicemente ponendo lo stesso codice EEh nell'output buffer; si tratta in sostanza di un test diagnostico. Questo comando permette di impostare il set di codici di scansione o di avere informazioni sul set attualmente in uso; la tastiera risponde al comando inviando all'output buffer il codice FAh (ACK) e poi attende che venga scritto un parametro nell'input buffer.
Se il parametro è 00h, la tastiera risponde inviando all'output buffer il codice FAh (ACK); successivamente, scrive nell'output buffer il set (1, 2 o 3) attualmente in uso.
Se il parametro è 01h, 02h o 03h, la tastiera risponde inviando all'output buffer il codice FAh (ACK); successivamente, imposta, rispettivamente, il set 1 (standard XT), il set 2 (standard AT e PS/2) o il set 3 (opzionale PS/2). Quando la tastiera riceve questo comando, risponde inviando all'output buffer un proprio codice identificativo da 2 byte formato dai due valori ABh e 83h; tali due valori devono essere letti in sequenza dalla porta 60h. Questo comando permette di impostare il Typematic Rate e il Typematic Delay; il byte successivo che scriviamo nella porta 60h contiene le nuove impostazioni secondo lo schema illustrato in Figura 13.18. Quindi, i primi 5 bit contengono la codifica del Typematic Rate; si va da 00000b (30 cps) a 11111b (2 cps).
I bit in posizione 5 e 6 contengono la codifica del Typematic Delay; si va da 00b (0.25 secondi) a 11b (1 secondo). Questo comando riabilita la tastiera, precedentemente disabilitata con il comando F5h. Questo comando disabilita la tastiera, carica le impostazioni predefinite (illustrate in precedenza) e pone la periferica in attesa di un comando F4h (Enable Keyboard). Questo comando carica le impostazioni predefinite per la tastiera secondo i valori che abbiamo visto all'inizio di questa sezione 13.3.2. Questo comando fa in modo che tutti i tasti della tastiera siano typematic; la tastiera risponde al comando inviando all'output buffer il codice FAh (ACK). Questo comando fa in modo che tutti i tasti della tastiera generino i codici di scansione sia quando vengono premuti (make), sia quando vengono rilasciati (break); la tastiera risponde al comando inviando all'output buffer il codice FAh (ACK). Questo comando fa in modo che tutti i tasti della tastiera generino i codici di scansione solo quando vengono premuti (make); la tastiera risponde al comando inviando all'output buffer il codice FAh (ACK). Questo comando fa in modo che tutti i tasti della tastiera siano typematic e generino i codici di scansione sia quando vengono premuti (make), sia quando vengono rilasciati (break); la tastiera risponde al comando inviando all'output buffer il codice FAh (ACK). Questo comando permette di rendere typematic un tasto specifico; tutti gli altri tasti mantengono le vecchie impostazioni. La tastiera risponde al comando inviando all'output buffer il codice FAh (ACK); il byte successivo che scriviamo nella porta 60h specifica il codice di scansione del tasto che ci interessa. Questo comando fa in modo che un tasto specifico generi il codice di scansione sia quando viene premuto (make), sia quando viene rilasciato (break), ma che non sia typematic; tutti gli altri tasti mantengono le vecchie impostazioni. La tastiera risponde al comando inviando all'output buffer il codice FAh (ACK); il byte successivo che scriviamo nella porta 60h specifica il codice di scansione del tasto che ci interessa. Questo comando fa in modo che un tasto specifico generi il codice di scansione solo quando viene premuto (make) e che non sia typematic; tutti gli altri tasti mantengono le vecchie impostazioni. La tastiera risponde al comando inviando all'output buffer il codice FAh (ACK); il byte successivo che scriviamo nella porta 60h specifica il codice di scansione del tasto che ci interessa. Questo comando chiede alla tastiera di inviare nuovamente il precedente dato appena ricevuto dal sistema; è una situazione che si presenta quando, ad esempio, il controller ha ricevuto dalla tastiera un dato che contiene un errore di parità. In presenza di questo comando, la tastiera viene nuovamente sottoposta al test diagnostico già svolto durante il POST del BIOS; se il test ha esito positivo, vengono attivate le impostazioni predefinite illustrate all'inizio di questa sezione 13.3.2 e, inoltre, viene inviato all'output buffer il codice FAh (ACK).

13.3.3 Comandi destinati al mouse PS/2

Durante il Power On Self Test del BIOS (o quando viene inviato un comando reset), il mouse viene inizializzato con le seguenti impostazioni: La modalità operativa predefinita per il mouse viene chiamata streaming mode; in tale modalità, ogni volta che si verifica un evento (pressione/rilascio dei pulsanti, spostamento del cursore) il mouse invia al sistema il Mouse Data Packet da 3 byte illustrato in Figura 13.8. Il sistema provvede ad inviare una IRQ12 alla CPU, la quale genera una INT 74h; la relativa ISR a questo punto può leggere in sequenza i 3 byte del Mouse Data Packet dalla porta 60h dell'output buffer.
Bisogna tenere presente, però, che dopo la fase di inizializzazione del mouse, lo streaming (invio letture) risulta disabilitato; è nostro compito abilitarlo attraverso un apposito comando che viene illustrato più avanti.
Anche la generazione della IRQ12 da parte del sistema, risulta inizialmente disabilitata; è nostro compito provvedere ad abilitarla, nel caso in cui sia nostra intenzione gestire il mouse con il metodo molto efficiente delle IRQ.
In alternativa, esiste anche la possibilità di gestire il mouse con il metodo del polling; in tal caso, è nostro compito chiedere esplicitamente al mouse l'invio di un nuovo Mouse Data Packet. Come abbiamo visto anche nella sezione Assembly Base, il metodo del polling è piuttosto inefficiente in quanto impegna continuamente la CPU e rallenta quindi l'intero sistema.

Analizziamo ora i principali comandi che il programmatore può inviare al mouse. Questo comando imposta il fattore di scala a 1:1; come è stato spiegato in precedenza, in questo caso il mouse invia al sistema spostamenti del cursore identici a quelli calcolati.
Il mouse inoltre risponde inviando all'output buffer il codice FAh (ACK); bisogna ribadire che, solo dopo che abbiamo letto tale codice, la logica di controllo del mouse provvede ad eseguire il comando richiesto (e ciò vale ovviamente anche per gli altri comandi elencati nel seguito). Questo comando imposta il fattore di scala a 2:1; come è stato spiegato in precedenza, in questo caso il mouse invia al sistema spostamenti del cursore doppi rispetto a quelli calcolati.
Il mouse inoltre risponde inviando all'output buffer il codice FAh (ACK). In seguito a questo comando, il mouse invia all'output buffer il codice FAh (ACK) e attende che venga specificata la risoluzione da impostare attraverso un dato da 1 byte nell'input buffer; dopo aver letto tale dato, il mouse invia un ulteriore codice FAh (ACK) all'output buffer.
La risoluzione da impostare viene specificata attraverso la codifica illustrata in Figura 13.19. In seguito a questo comando, il mouse invia all'output buffer il codice FAh (ACK); successivamente (dopo che abbiamo letto il codice FAh), invia in sequenza all'output buffer 3 byte contenenti le varie impostazioni e lo stato corrente.
Il blocco da 3 byte assume la struttura illustrata in Figura 13.20. Partendo dal Byte 1, i primi 3 bit indicano lo stato corrente dei pulsanti; come al solito, per ogni bit, il valore 1 indica pulsante premuto, mentre il valore 0 indica pulsante rilasciato.
Il bit in posizione 4 indica lo stato corrente del fattore di scala; il valore 0 equivale a 1:1, mentre il valore 1 equivale a 2:1.
Il bit in posizione 5 indica lo stato corrente dell'invio delle letture da parte del mouse; il valore 0 indica che l'invio delle letture è disabilitato, mentre il valore 1 indica che è abilitato.
Il bit in posizione 6 indica la modalità operativa corrente del mouse; il valore 0 indica che è attivo lo streaming mode, mentre il valore 1 indica che è attivo il polling mode.

Il Byte 2 contiene la risoluzione corrente; il valore restituito è quello di Figura 13.19.

Il Byte 3 contiene la frequenza di campionamento corrente; il valore restituito è uno tra 10, 20, 40, 60, 80, 100, 200. In seguito a questo comando, il mouse invia all'output buffer il codice FAh (ACK); successivamente, entra in modalità streaming per l'invio dei dati. In seguito a questo comando, il mouse invia all'output buffer il codice FAh (ACK); successivamente (dopo che abbiamo letto il codice FAh), invia un Mouse Data Packet all'output buffer.
Questo comando deve essere necessariamente usato quando il mouse è in modalità polling.

Vediamo un esempio che ha validità generale per l'invio di comandi al mouse; in questo caso, inviamo al mouse il comando EBh (Read Data) per richiedere un nuovo Mouse Data Packet. Per memorizzare i 3 byte inviati dal mouse, supponiamo di aver definito le 3 variabili MDP_byte1, MDP_byte2 e MDP_byte3 di tipo BYTE. In seguito a questo comando, il mouse invia all'output buffer il codice FAh (ACK); successivamente, torna nella modalità (streaming o polling) che era attiva prima dell'attuale modalità wrap.
La modalità wrap ha uno scopo diagnostico; in tale modalità, ogni comando inviato al mouse dal sistema viene rimandato indietro tale e quale. In seguito a questo comando, il mouse invia all'output buffer il codice FAh (ACK); successivamente, entra nella modalità wrap. In seguito a questo comando, il mouse invia all'output buffer il codice FAh (ACK); successivamente, entra nella modalità polling. In seguito a questo comando, il mouse invia all'output buffer il codice FAh (ACK); successivamente, invia all'output buffer il codice standard 00h che identifica i mouse PS/2. In seguito a questo comando, il mouse invia all'output buffer il codice FAh (ACK); successivamente (dopo che abbiamo letto il codice FAh), attende che venga indicato nell'input buffer il nuovo valore per il parametro risoluzione. Dopo aver letto tale parametro, il mouse invia un ulteriore codice FAh (ACK) all'output buffer e imposta la nuova risoluzione.
I valori consentiti per la risoluzione sono uno tra 10, 20, 40, 60, 80, 100, 200 (letture al secondo). In seguito a questo comando, il mouse invia all'output buffer il codice FAh (ACK); successivamente, attiva l'invio delle letture al sistema. Quando il Data Reporting è attivo, ogni volta che premiamo/rilasciamo un pulsante e/o spostiamo il cursore, il mouse predispone un nuovo Mouse Data Packet e lo invia al sistema.
Come è stato spiegato in precedenza, questo comando è necessario in quanto, subito dopo l'inizializzazione, l'invio delle letture al sistema è disabilitato. In seguito a questo comando, il mouse invia all'output buffer il codice FAh (ACK); successivamente, disabilita l'invio delle letture al sistema.
Anche se l'invio delle letture viene disabilitato, è sempre possibile forzare il mouse ad inviare un Mouse Data Packet attraverso il comando EBh (Read Data). In seguito a questo comando, il mouse invia all'output buffer il codice FAh (ACK); successivamente, vengono attivate le impostazioni predefinite illustrate all'inizio di questa sezione 13.3.3. Questo comando chiede al mouse di inviare nuovamente il precedente dato appena ricevuto dal sistema; è una situazione che si presenta quando, ad esempio, il controller ha ricevuto dal mouse un dato che contiene un errore di parità. In presenza di questo comando, il mouse viene nuovamente sottoposto al test diagnostico già svolto durante il POST del BIOS; se il test ha esito positivo, vengono attivate le impostazioni predefinite illustrate all'inizio di questa sezione 13.3.3 e, inoltre, viene inviato all'output buffer il codice FAh (ACK).

13.4 Esempi pratici

13.4.1 Gestione del mouse in modalità testo

Vediamo un esempio pratico relativo alla gestione del mouse in modalità testo standard da 80x25 celle; come si può facilmente immaginare, in questo caso il cursore occupa una delle celle e risulta quindi molto semplice da riprodurre sullo schermo.

Come abbiamo già visto nei precedenti capitoli, è molto conveniente creare apposite librerie nelle quali "nascondere" tutte le procedure destinate a fornirci i vari servizi di cui abbiamo bisogno; in questo modo, il programma principale risulta molto più semplice e comprensibile, essendo costituito da una serie di chiamate alle procedure delle librerie stesse.
Nel nostro caso, nella sezione Librerie di supporto per il corso assembly dell’ Area Downloads di questo sito, è presente una libreria, denominata PS2TLIB, che può essere linkata ai programmi destinati alla generazione di eseguibili in formato EXE; all'interno del pacchetto ps2tlibexe.zip è presente la libreria vera e propria PS2TLIB.ASM, l'include file PS2SET1.INC (vedere più avanti), l'include file PS2TLIBN.INC per il NASM e l'include file PS2TLIBM.INC per il MASM.
Per la creazione dell'object file di PS2TLIB.ASM è necessario il NASM; il comando da impartire è:
nasm -f obj ps2tlib.asm
L'object file così ottenuto è perfettamente linkabile anche ai programmi scritti con MASM; eventualmente, è necessario ricordarsi di chiamare le varie procedure della libreria secondo le convenzioni C (passaggio dei parametri da destra verso sinistra e pulizia dello stack a carico del caller).

La libreria fornisce alcune procedure essenziali il cui scopo è inizializzare il mouse e installare o disinstallare le ISR per la INT 09h (tastiera) e la INT 74h (mouse); tutti i dettagli relativi alla visualizzazione del cursore del mouse sullo schermo e alla memorizzazione dei codici di scansione della tastiera in un apposito buffer, vengono gestiti dalla libreria stessa attraverso procedure private in quanto si tratta di aspetti che non devono coinvolgere il programma principale.

La procedura pubblica install_int09h installa la nuova ISR per la gestione della tastiera; analogamente, la procedura pubblica restore_int09h ripristina la vecchia ISR.
La nuova ISR per la tastiera si chiama new_int09h ed è una versione semplificata di quella mostrata nel Capitolo 3. Ogni volta che si preme o si rilascia un tasto, la ISR intercetta la INT 09h e memorizza il relativo codice di scansione in una variabile pubblica da 4 byte chiamata key_buffer; vengono gestiti solo i tasti normali con codice di scansione da 1 byte e quelli speciali con codice di scansione da 2 byte (E0h + XXh). Una volta salvato il codice di scansione, la ISR chiama una procedura esterna get_keybdata che deve essere definita nel programma principale; il compito di get_keybdata è quello di permetterci di leggere e visualizzare il nuovo codice di scansione e l'eventuale codice ASCII ad esso associato. Si può notare la presenza di due ulteriori variabili pubbliche denominate normal_key e special_key; si tratta di due tabelle di lookup che permettono di associare ad ogni codice di scansione, il relativo codice ASCII (vedere più avanti per i dettagli).

La procedura pubblica install_int74h installa la nuova ISR per la gestione del mouse; analogamente, la procedura pubblica restore_int74h ripristina la vecchia ISR.
La nuova ISR per il mouse si chiama new_int74h; ad ogni evento del mouse, la ISR intercetta la INT 74h, legge il nuovo Mouse Data Packet e lo salva in una variabile pubblica da 4 byte chiamata mouse_data. La stessa ISR salva anche lo stato (0 o 1) dei pulsanti in tre ulteriori variabili pubbliche denominate mouse_lb, mouse_rb e mouse_mb.
Successivamente, la ISR chiama una procedura privata draw_cursor il cui compito è quello di disegnare il cursore sullo schermo dopo aver salvato gli attributi video sottostanti; per effettuare tutti i calcoli necessari, questa procedura si serve proprio delle informazioni presenti nel Mouse Data Packet. Come sappiamo, la memoria video testo a colori parte dall'indirizzo logico B800h:0000h ed è costituita da una sequenza di coppie di BYTE del tipo CA, CA, CA, ...; la lettera C indica il codice ASCII del simbolo da visualizzare, mentre la lettera A indica l'attributo video, la cui struttura è mostrata in Figura 13.21. La posizione iniziale del cursore è il centro dello schermo e cioè: X_RESOL/2, Y_RESOL/2, dove X_RESOL=80 e Y_RESOL=25; le due coordinate del cursore (ascissa e ordinata) vengono memorizzate nelle due variabili pubbliche mouse_x e mouse_y. Prima di visualizzare il cursore, viene salvato l'attributo video sottostante; per determinare l'offset della cella dello schermo a cui accedere basta quindi calcolare:
(mouse_y * 160) + (mouse_x * 2) + 1
Il 160 è dato da 80*2 (righe da 80 celle da 2 byte ciascuna), mentre mouse_x deve essere moltiplicato per 2, appunto, perché ogni cella è formata da 2 byte; il +1 serve per accedere al BYTE degli attributi.
Si noti che il cursore viene visualizzato effettuando uno XOR tra il suo attributo e quello della cella sottostante; in questo modo si evita l'eventualità che il cursore stesso risulti invisibile in un'area dello schermo avente lo stesso colore. Supponiamo, ad esempio, che lo schermo abbia come attributi: primo piano (foreground) bianco intenso su sfondo (background) verde (00101111b), mentre il cursore sia verde su verde (00100010b); effettuando uno XOR tra questi due attributi si ottiene: Il risultato è un cursore nero (0000b), mentre eventuali caratteri sottostanti in bianco intenso appariranno in rosa intenso (1101b).

Le successive posizioni del cursore vengono calcolate sommando (o sottraendo) a mouse_x e mouse_y, rispettivamente, i valori delta_x e delta_y presenti nel Mouse Data Packet; si ricordi che tali due valori sono numeri interi con segno a 8 bit in complemento a 2.

Una volta disegnato il cursore, new_int74h chiama una procedura esterna get_mousedata che deve essere definita nel programma principale; il compito di get_mousedata è quello di permetterci di leggere e visualizzare tutti i dati relativi allo stato del mouse.

Un'ultima considerazione riguarda il fatto che la IRQ12 (mouse PS/2) viene elaborata dal PIC Slave; di conseguenza, come già sappiamo, la relativa ISR prima di terminare deve inviare due EOI, il primo al PIC Slave e il secondo al PIC Master.

La libreria PS2TLIB contiene anche la procedura pubblica init_mouse; tale procedura calcola la posizione iniziale del cursore ed effettua tutte le inizializzazioni del mouse (abilitazione porta 2 del controller PS/2, abilitazione linee CLK e IRQ12, impostazioni di default per il mouse e abilitazione invio letture).

Per testare la libreria PS2TLIB, scriviamo un programma denominato PS2TTEST.ASM; la Figura 13.22 illustra il relativo listato. Il programma PS2TTEST chiama innanzi tutto la procedura fill_screen, la quale riempie lo schermo con attributi bianco intenso su sfondo verde; come spiegato in precedenza, lo scopo è quello di far vedere che il cursore del mouse, pur essendo a sua volta verde, risulterà ugualmente visibile grazie all'istruzione XOR.
Dopo aver visualizzato il titolo del programma e altre stringhe, PS2TTEST provvede ad installare le ISR per mouse e tastiera; successivamente viene inizializzato il mouse.
Il nucleo di PS2TTEST è un semplicissimo loop dal quale si esce solo quando si preme il tasto [ESC] (codice di scansione 01h); tutta la complessità del programma risulta nascosta in quanto il necessario lavoro viene eseguito in "background" dalle due ISR.

Come abbiamo visto, la ISR per la tastiera, new_int09h, salva il codice di scansione ricevuto in una variabile pubblica key_buffer; in seguito, provvede a chiamare la procedura get_keybdata affinché il programma principale possa accedere alla stessa key_buffer.
Come si può notare nel listato, get_keybdata scandisce i vari byte di key_buffer in modo da visualizzare l'intero codice di scansione appena generato dalla tastiera; la fine del codice è rappresentata da un byte di valore 0.
Successivamente, get_keybdata visualizza il codice ASCII associato a quel codice di scansione; a tale proposito, come già anticipato, viene utilizzata una lookup table contenuta nell'include file PS2SET1.INC. Si tratta di una tabella contenente delle stringhe da 16 byte ciascuna; ogni stringa si viene a trovare quindi all'offset scan_code * 16 della tabella stessa. Lo scopo di una lookup table è proprio quello di indirizzare una informazione (dato, procedura, etc) evitando inutili calcoli.
L'include file PS2SET1.INC rappresenta la prosecuzione del segmento di dati PS2TDATA presente nel file PS2TLIB.ASM; al suo interno vengono definite le due variabili globali normal_key (tabella per i tasti semplici, con codice di scansione da 1 byte) e special_key (tabella per i tasti speciali con codice di scansione da 2 byte).

La ISR per il mouse, new_int74h, provvede a catturare ogni nuovo Mouse Data Packet e a metterlo a disposizione del programma principale attraverso la variabile pubblica mouse_data; inoltre, la stessa ISR elabora i dati grezzi per ricavare lo stato dei pulsanti (variabili pubbliche mouse_lb, mouse_rb e mouse_mb) e le coordinate del cursore (variabili pubbliche mouse_x e mouse_y).

Bibliografia

IBM Personal System/2 Hardware Interface - Technical Reference

NXP (Freescale Semiconductors) - Interfacing MC68HC05 Microcontrollers to the IBM AT Keyboard Interface