Teclado

Estado do i8042, scancodes set 1, command byte e leitura por polling ou interrupções.

TLDR

Perguntar sobre esta página

ChatGPTClaudePerplexityGrokGeminiCopiar e abrirDeepSeekCopiar e abrir

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:

  1. O controlador do teclado envia o byte pela linha série para o KBC.
  2. O KBC guarda-o no output buffer e assinala OBF a 1 no registo de estado.
  3. Se as interrupções do teclado estiverem ativas no command byte, o KBC levanta IRQ1.
  4. O programa lê o estado em 0x64 e depois retira o byte de 0x60.

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:

BitNomeSignificado quando vale 1
7PARITYO byte recebido tem erro de paridade
6TIMEOUTHouve erro de timeout na comunicação
5AUXO byte do output buffer veio do rato
1IBFO input buffer está cheio, não escrevas
0OBFHá 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:

ByteE0 antesEventoE0 depois
E0NãoNenhumSim
4DSimMake da seta direitaNão
E0NãoNenhumSim
CDSimBreak da seta direitaNão
81NãoBreak de EscNã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 byteSignificado quando vale 1
0Ativa interrupções do teclado
1Ativa interrupções do rato
4Desativa a interface do teclado
5Desativa 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
E0 é um prefixo e não emite um evento.
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.

Voltar à explicação
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
O bit 0 indica que existe um byte no output buffer.
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.

Voltar à explicação
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
O comando que anuncia a escrita e o seu argumento usam portas diferentes.
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.
Voltar à explicação
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
O prefixo pertence à sequência interrompida.
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.

Voltar à explicação

À tua maneira

Aparência

Modo de cor
Tema
Cor de destaque

Leitura

Fonte de leitura

Álgebra, lógica e uma ideia de cada vez.

Código

Fonte do código

Navegação

Cursor de leitura, sem edição. h, j, k, l movem; / pesquisa nesta página. v seleciona caracteres; V seleciona linhas; y copia. Tab permite destacar ou comentar. Espaço ou Enter abre blocos recolhíveis. Nos cartões, h, j, k, l mudam o foco.

CSS personalizado

Ativa uma sugestão ou guarda o teu CSS. Só muda este navegador.

Usa --page, --text e --accent para acompanhar o tema.

Seletores e recuperação

.site-header, .prose, .course-card, [data-reading-history], [data-playground] e .page-actions.

Abre a aparência com a tecla vírgula ou no rodapé. Se o CSS esconder os controlos, abre esta página sem CSS personalizado.

Pesquisar

Escreve para pesquisar em todo o site.

para escolher · Enter para abrir · Esc para fechar

Copiar fórmula

A cópia automática não está disponível. Copia o LaTeX abaixo.

Pesquisar nesta página

Enter procura · n / N repetem · Esc cancela. Ctrl / Cmd K pesquisa todo o site.

Atalhos de teclado

Clica numa tecla para a mudar. Esc cancela. Backspace desativa.

PesquisarCtrl / Cmd K

Os atalhos não interferem enquanto escreves. Tab e Enter funcionam sempre.