Assembly Avanzato con MASM
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.
- Attraverso il pin 1 la tastiera fornisce un proprio segnale di clock per
sincronizzare l'invio dei dati al computer
- Attraverso il pin 2 la tastiera invia gli
scan codes dei tasti premuti o
rilasciati dall'utente
- Attraverso i pin 4 e 5 il computer fornisce l'alimentazione elettrica
alla tastiera; il pin 5 rappresenta il positivo (+5 V), mentre il pin
4 è la tensione nulla di riferimento (terra)
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.
- Attraverso il pin 1 la periferica comunica con il computer
- Attraverso i pin 3 e 4 il computer fornisce l'alimentazione elettrica
alla periferica; il pin 3 è la tensione nulla di riferimento (terra), mentre
il pin 4 rappresenta il positivo (+5 V)
- Attraverso il pin 5 la periferica fornisce un proprio segnale di clock per
sincronizzare lo scambio dei dati con il computer
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.
- Il bit 0 è lo start bit e vale sempre 0
- I bit da 1 a 8 sono i data bit
- Il bit 9 è il parity bit (sempre parità dispari)
- Il bit 10 è lo stop bit e vale sempre 1
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:
- Il device si assicura che le linee CLK e DATA siano HIGH
(stato IDLE)
- Il device pone la linea DATA su LOW (start bit)
- In seguito, il device pone la linea CLK su LOW; lo start bit viene
trasferito al sistema durante tale fronte di discesa (falling edge)
- Nello stesso modo, il device usa i successivi 9 cicli di clock per
trasferire al sistema 8 bit di dati più il bit di parità
- Il ciclo di clock successivo viene usato dal device per inviare il bit di stop
(1) ponendo a HIGH la linea DATA
- Il sistema blocca la linea CLK su LOW per inibire un nuovo invio
di dati da parte del device, in attesa che il precedente dato venga
elaborato
- Terminata l'elaborazione, il sistema rilascia la linea CLK per indicare
che è pronto a ricevere ulteriori dati dal device
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:
- Il sistema pone la linea CLK su LOW
- Dopo un certo intervallo di tempo il sistema pone la linea DATA su LOW
per rappresentare lo start bit
- Il device risponde attivando il segnale di clock sulla linea CLK
- Il sistema usa il fronte di salita (rising edge) del segnale di clock per
trasferire lo start bit
- Nello stesso modo, il sistema usa i successivi 9 cicli di clock per
trasferire al device 8 bit di dati più il bit di parità
- Il ciclo di clock successivo viene usato dal sistema per inviare il bit di stop
(1) ponendo a HIGH la linea DATA
- Un ulteriore ciclo di clock viene usato dal device per inviare il bit di risposta
(ACK) ponendo la linea DATA prima a LOW e poi a HIGH
- Se il device rileva un errore di parità, invia il valore FEh al sistema per
chiedere la ritrasmissione del dato
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.
- Accedendo in scrittura alla porta 60h, si viene indirizzati verso
l'input buffer
- Accedendo in lettura alla porta 60h, si viene indirizzati verso
l'output buffer
- Accedendo in scrittura alla porta 64h, si viene indirizzati verso
il control register
- Accedendo in lettura alla porta 64h, si viene indirizzati verso
lo status register
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.
- 20h - 3Fh - Read from Controller RAM
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.
- 60h - 7Fh - Write to Controller RAM
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).
- A4h - Test Password Installed
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.
- A7h - Disable Auxiliary Device Interface
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.
- A8h - Enable Auxiliary Device Interface
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.
- ADh - Disable Keyboard Interface
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.
- AEh - Enable Keyboard Interface
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.
- C1h - Poll Input Port Low
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.
- C2h - Poll Input Port High
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.
- D2h - Write Keyboard Output Buffer
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).
- D3h - Write Auxiliary Device Output Buffer
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).
- D4h - Write to Auxiliary Device
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.
- F0h - FFh - Pulse Output Port
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:
- Typematic Delay = 500 ms
- Typematic Rate = 10.9 cps
- Scan Code Set = 2
- Set all keys typematic/make/break
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.
- EDh - Set/Reset mode indicators
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.
- F0h - Select Alternate Scan Code Set
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).
- F2h - Send Keyboard ID Code
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.
- F3h - Set Typematic Rate/Delay
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.
- F7h - Set All Keys Typematic
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).
- F8h - Set All Keys Make/Break
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).
- FAh - Set All Keys Typematic/Make/Break
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:
- Frequenza di campionamento = 100 letture/secondo
- Risoluzione = 4 conteggi/millimetro
- Fattore di scala = 1:1
- Invio letture = disabilitato
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).
- F4h - Enable Data Reporting
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.
- F5h - Disable Data Reporting
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