Teclado
Estado do i8042, scancodes set 1, command byte e leitura por polling ou interrupções.
Premir a seta direita produz E0 4D; largá-la produz E0 CD, no set 1 usado aqui. O i8042 recebe e disponibiliza estes bytes um de cada vez. O programa só deve produzir um evento depois de reunir o scancode completo.
A chegada segue sempre esta ordem, do teclado para o programa:
- O controlador do teclado envia o byte pela linha série para o KBC.
- O KBC guarda-o no output buffer e assinala OBF a 1 no registo de estado.
- Se as interrupções do teclado estiverem ativas no command byte, o KBC levanta IRQ1.
- O programa lê o estado em
0x64e depois retira o byte de0x60.
Em polling, o passo 3 não produz efeito no programa: é a leitura repetida do estado que encontra OBF. Em interrupções, o handler executa o passo 4. Trocar esta ordem, por exemplo assumir que a interrupção cria o byte em vez de apenas o anunciar, leva a ler o buffer antes de ele ter dados.
Estado do i8042
Consulta o registo de estado em 0x64. Os bits usados nas leituras e escritas são:
| Bit | Nome | Significado quando vale 1 |
|---|---|---|
| 7 | PARITY | O byte recebido tem erro de paridade |
| 6 | TIMEOUT | Houve erro de timeout na comunicação |
| 5 | AUX | O byte do output buffer veio do rato |
| 1 | IBF | O input buffer está cheio, não escrevas |
| 0 | OBF | Há um byte para ler no output buffer |
Em polling, espera OBF e distingue a origem pelo AUX. Numa subscrição exclusiva da IRQ1, sabes que a notificação é do teclado, mas ainda precisas de tratar falhas de leitura e erros de comunicação. Teclado e rato partilham a porta de dados: o teu desenho deve impedir que uma rotina consuma um byte destinado à outra.
Se OBF estiver ativo, lê 0x60 mesmo quando o estado assinala erro, para retirar o byte inválido, porque deixar esse byte no output buffer pode impedir novas entregas. Descarta-o depois.
Make, break e prefixos
Nos exemplos usamos os scancodes set 1 usados pelo ambiente do laboratório. Para muitas teclas, o make code indica que a tecla foi premida e o break code é o make com o bit 7 ativo. Esc produz make 0x01 e break 0x81.
Um scancode não é um carácter ASCII, porque a tecla que produz 0x1E no set 1 não entrega o byte ASCII de a. Para obter texto, tens de interpretar layout, Shift, Caps Lock e outras teclas. Para controlar uma aplicação, podes trabalhar diretamente com eventos de tecla.
Algumas teclas usam o prefixo 0xE0. Por exemplo, a seta para a direita produz make E0 4D e break E0 CD. No modelo do laboratório, dois atendimentos recolhem os dois bytes, sem esperar pelo segundo dentro do primeiro handler. O prefixo não é um evento completo e não deve provocar uma ação sozinho.
O set 1 tem também sequências especiais mais longas, como Pause. O descodificador simples seguinte trata códigos de um byte e códigos com um prefixo E0. Uma aplicação que precise das teclas especiais deve reconhecer a sequência completa; este modelo não é um parser universal de todos os teclados.
Descodificador por estados
Este programa completo é C portátil. Simula a chegada dos bytes E0 4D E0 CD 81, com a mesma lógica que pode ficar acima do handler de hardware, e imprime make e break da seta direita seguidos do break de Esc:
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
typedef struct { bool extended; } decoder;
static bool feed(decoder *d, uint8_t byte,
bool *extended, bool *released, uint8_t *key) {
if (!d->extended && byte == 0xE0) {
d->extended = true;
return false;
}
*extended = d->extended;
*released = (byte & 0x80u) != 0;
*key = (uint8_t)(byte & 0x7Fu);
d->extended = false;
return true;
}
int main(void) {
decoder d = {false};
const uint8_t bytes[] = {0xE0, 0x4D, 0xE0, 0xCD, 0x81};
for (size_t i = 0; i < sizeof bytes; ++i) {
bool extended, released;
uint8_t key;
if (feed(&d, bytes[i], &extended, &released, &key))
printf("%s key=0x%02X extended=%u\n",
released ? "break" : "make", (unsigned)key,
(unsigned)extended);
}
return 0;
}
O programa imprime:
make key=0x4D extended=1
break key=0x4D extended=1
break key=0x01 extended=0
O estado persiste entre os bytes:
| Byte | E0 antes | Evento | E0 depois |
|---|---|---|---|
E0 | Não | Nenhum | Sim |
4D | Sim | Make da seta direita | Não |
E0 | Não | Nenhum | Sim |
CD | Sim | Break da seta direita | Não |
81 | Não | Break de Esc | Não |
O bit 7 de CD assinala break depois de reconhecer o prefixo. O mesmo bit em E0 faz parte do prefixo e não indica uma tecla largada.
O estado do prefixo persiste entre chamadas, por isso se perderes ou descartares um byte por erro, define também uma política de recuperação do parser. Não uses um prefixo antigo para classificar um byte de uma nova sequência.
Handler e aplicação
O handler de teclado lê o estado, retira um byte e assinala se esse byte é válido. A função de montagem conserva o prefixo. O código da aplicação escolhe o que um evento completo faz.
Subscreve IRQ1 com IRQ_REENABLE | IRQ_EXCLUSIVE no contexto do laboratório, para impedir o driver normal de consumir o mesmo buffer. Se timer e teclado forem subscritos, testa as duas máscaras de forma independente. Quando esperas a libertação de Esc, termina ao receber um scancode completo 0x81, não ao ver qualquer byte cujo bit 7 seja 1. 0xE0 também tem o bit 7 ativo e é um prefixo.
Para uma tecla mantida premida, podem chegar vários make codes de repetição. Uma ação que deve ocorrer só uma vez por pressão precisa de distinguir a transição de solta para premida dos make codes seguintes.
Command byte do KBC
O command byte configura o controlador. Não é o registo de estado, embora a porta 0x64 participe no acesso aos dois.
| Bit do command byte | Significado quando vale 1 |
|---|---|
| 0 | Ativa interrupções do teclado |
| 1 | Ativa interrupções do rato |
| 4 | Desativa a interface do teclado |
| 5 | Desativa a interface do rato |
Para o ler, espera IBF a 0, escreve comando 0x20 em 0x64, espera a resposta no output buffer e lê 0x60. Para escrever um novo valor, espera IBF a 0, escreve comando 0x60 em 0x64, espera novamente IBF a 0 e escreve o novo byte em 0x60.
Uma resposta a um comando e um scancode chegam pelo mesmo output buffer. A sequência de comandos deve controlar quem o lê e impedir que um scancode seja confundido com a resposta. Para comandos do controlador, uma opção do ambiente é desativar temporariamente a interface de teclado com 0xAD, tratar os bytes já pendentes e repor a interface no fim, se estava ativa. Desativar apenas a IRQ não impede a chegada de novos scancodes.
Aqui 0x60 pode ser tanto um valor de comando como um endereço de porta. Distingue-os pela posição na sequência. Não escrevas o valor do command byte diretamente em 0x64.
Se queres apenas ativar interrupções do teclado, lê o valor anterior e faz old | 0x01, porque substituir o byte por 0x01 pode alterar a configuração do rato e outros bits. Em experiências de polling que desativam interrupções, guarda e repõe a configuração anterior no fim.
Inatividade com timer e teclado
Um timeout de inatividade mede o tempo desde a última entrada válida. Quando um byte ou evento válido chega, repõe o contador conforme a definição do exercício. Em cada tick, incrementa-o. Para um limite de 3 s a 60 Hz, termina quando chega a 180 ticks.
O critério importa: “sem bytes válidos” e “sem teclas completas” não são idênticos para scancodes com prefixo. Explicita qual conta como atividade. Processar a tecla antes do timer numa notificação com ambos pode levar a um resultado diferente de processar primeiro o timeout no instante limite.
Para comparar polling e interrupções, conta as chamadas de I/O em ambas as soluções para a mesma sequência e duração. Polling lê estado repetidamente sem dado; o handler lê estado e dados quando recebe uma notificação. A comparação mede trabalho efetivamente feito, não prova que todas as cargas tenham a mesma relação de custos.
Exercícios
Descodificar make e break
No modelo set 1 de um ou dois bytes, com prefixo E0 opcional, interpreta E0 4B E0 CB 81. Quantos eventos completos há e quais são?
Pista
Solução
Há três eventos: make da tecla estendida 0x4B, break da mesma tecla e break simples da tecla Esc, 0x01. E0 CB é um único código, não dois break codes.
Confere a tua resposta:
- Agrupei E0 com o byte seguinte.
- Identifiquei make e break da tecla 0x4B estendida.
- Identifiquei o break simples de Esc.
Erros frequentes
Tratar todo o byte com bit 7 a 1 como break ou esquecer o prefixo entre notificações.
Consumir um byte inválido
Numa leitura de teclado, o registo de estado vale 0x81. Deves deixar de ler os dados porque há erro? Explica a sequência e o efeito no parser.
Pista
Solução
OBF e PARITY estão ativos. Lê a porta 0x60 para consumir o byte e depois descarta-o. Não o passes como scancode válido. Invalida também um prefixo parcial se a política de recuperação o exigir. Deixá-lo no buffer pode impedir a entrega seguinte.
Confere a tua resposta:
- Reconheci OBF e PARITY ativos.
- Li 0x60 para retirar o byte.
- Descartei o byte sem gerar evento.
Erros frequentes
Confundir descartar com não ler ou interpretar 0x81 de estado como o scancode de Esc.
Alteração do command byte
Já leste o command byte do KBC e guardaste 0x22. Queres ativar interrupções
do teclado, mantendo os restantes bits. A fase de comandos controla o buffer,
sem leituras concorrentes.
Calcula o novo byte e indica as duas escritas, com porta, valor e condição
de estado. Explica por que escrever o novo byte diretamente em 0x64 falha.
Pista
Solução
Ligar o bit 0 dá 0x22 | 0x01 = 0x23. Espera IBF a 0 e escreve o comando
0x60 em 0x64. Espera novamente IBF a 0 e escreve o argumento 0x23
em 0x60. Usa um limite de espera e trata falhas de I/O em ambos os passos.
A porta 0x64 interpreta escritas como comandos do controlador. Escrever
ali 0x23 não fornece o argumento ao comando de alteração do command byte.
Confere a tua resposta:
- Obtive 0x23 sem alterar os restantes bits.
- Escrevi o comando 0x60 na porta 0x64 e o valor na porta 0x60.
- Esperei IBF a 0 antes de cada escrita.
Recuperar depois de um byte perdido
O descodificador guardou 0xE0 e o byte seguinte foi
descartado por erro de paridade. A política descarta o prefixo parcial e
recomeça à espera de um novo código. Chega depois 0x1E,
primeiro byte de uma nova sequência. Que evento produzes?
Pista
Solução
Produz make simples da tecla 0x1E, sem estendido. O erro
invalida a sequência parcial e limpa o prefixo guardado. Reutilizar o
0xE0 antigo classificaria mal uma tecla nova.
Confere a tua resposta:
- Guardei E0 sem emitir evento.
- Descartei o prefixo ao perder o byte.
- Não classifiquei a sequência nova com o prefixo antigo.
Erros frequentes
Emitir um evento estendido com bytes de sequências diferentes.