Rato
Pacotes PS/2, deltas de nove bits, comandos e deteção de gestos.
O pacote 28 05 FB move o rato cinco unidades para a direita e cinco para baixo. Para chegar a estes deltas, é preciso juntar três bytes: o primeiro contém sinais e botões, os seguintes contêm os oito bits inferiores de X e Y. O rato PS/2 usa o i8042 e IRQ12; cada atendimento do laboratório recolhe um byte.
Anatomia do pacote
Os dois contadores de deslocamento do rato têm nove bits em complemento para dois. O primeiro byte contém o bit mais significativo de cada delta; os bytes seguintes contêm os oito bits inferiores.
| Byte | Conteúdo |
|---|---|
| 0, bit 7 | Overflow de Y |
| 0, bit 6 | Overflow de X |
| 0, bit 5 | Bit de sinal de Y |
| 0, bit 4 | Bit de sinal de X |
| 0, bit 3 | Sempre 1 no primeiro byte |
| 0, bits 2, 1 e 0 | Botões do meio, direito e esquerdo |
| 1 | Oito bits inferiores do delta X |
| 2 | Oito bits inferiores do delta Y |
Os botões indicam o estado no instante do pacote, não apenas uma mudança, por isso um botão a 1 em vários pacotes pode significar que continua premido; a aplicação deve por isso detetar a transição de solto para premido em vez de reagir a cada pacote com o botão ativo.
Reconstruir nove bits
Se o bit de sinal de X estiver a 0, é o valor sem sinal do segundo byte. Se estiver a 1, subtrai 256:
Aqui e estão entre 0 e 255; e são 0 ou 1 obtidos do primeiro byte. Isto representa o intervalo de nove bits, de -256 a 255, na ausência de overflow.
No pacote 28 05 FB, o primeiro byte 0x28 tem o bit 5 a 1 e o bit 4 a 0, por isso o sinal X é 0 e o sinal Y é 1. Obtemos e .
No protocolo, Y positivo significa movimento para cima. Num ecrã onde Y cresce para baixo, atualiza screen_y -= delta_y, ou seja, partindo de (100, 100), o pacote do exemplo leva a (105, 105).
O programa seguinte imprime dx=5 dy=-5 x=105 y=105, porque aplica a fórmula de nove bits e a conversão para coordenadas de ecrã:
#include <stdint.h>
#include <stdio.h>
int main(void) {
uint8_t bytes[] = {0x28, 0x05, 0xFB};
int dx = (int)bytes[1] - ((bytes[0] & 0x10u) ? 256 : 0);
int dy = (int)bytes[2] - ((bytes[0] & 0x20u) ? 256 : 0);
int x = 100 + dx;
int y = 100 - dy;
printf("dx=%d dy=%d x=%d y=%d\n", dx, dy, x, y);
return 0;
}Executa o código para ver a saída e os gráficos.
Este exemplo assume ausência de overflow e não aplica limites do ecrã. Na aplicação, mantém a soma num tipo com sinal, limita às coordenadas permitidas e só depois converte para o tipo da posição.
Montagem e sincronização
Mantém um array de três bytes e um índice. Se o índice for 0, aceita apenas um byte com bit 3 a 1. Se não tiver esse bit, descarta-o. Nos índices 1 e 2, guarda o byte sem repetir o teste, porque os dados podem ter qualquer valor.
Quando o índice chega a 3, entrega o pacote completo e volta a 0, porque o bit 3 também pode estar ativo nos bytes de dados. É uma ajuda de sincronização, não uma prova de que nunca juntaste pedaços de dois pacotes.
Se uma leitura falhar ou houver erro de comunicação durante a montagem, invalida o pacote parcial e volta a procurar um primeiro byte. Um erro não deve produzir um movimento com bytes antigos. Se os bits de overflow estiverem ativos, a magnitude correspondente não é fiável. Define se ignoras esse eixo ou todo o movimento; ainda podes interpretar os botões se o pacote foi recebido validamente.
Stream, remote e reporting
Em stream mode, o rato envia pacotes quando há movimento ou mudança de botões, até à taxa de amostragem configurada. Ativar o modo não é o mesmo que ativar o envio: data reporting pode estar desligado.
Em remote mode, o programa pede um pacote com 0xEB. Isso é útil para uma experiência de polling. Cada byte do pacote continua a passar pelo output buffer do i8042; não chega um array de três bytes numa única leitura.
| Comando do rato | Função |
|---|---|
0xF4 | Ativar data reporting em stream mode |
0xF5 | Desativar data reporting |
0xEA | Selecionar stream mode |
0xF0 | Selecionar remote mode |
0xEB | Pedir dados em remote mode |
0xF3 | Definir taxa de amostragem, com um argumento |
0xF6 | Repor parâmetros por defeito |
Subscreve IRQ12 com a política exclusiva apropriada ao laboratório, para o driver normal do Minix não disputar os bytes. Guarda a configuração relevante e repõe o modo e reporting ao sair.
Enviar um comando
Os comandos do rato não se escrevem diretamente na porta de comandos do KBC. Para enviar cada byte:
- Espera IBF a 0 e escreve
0xD4em0x64, a ordem ao KBC para encaminhar o byte seguinte ao rato. - Espera IBF a 0 e escreve o byte em
0x60. - Espera a resposta e lê-a de
0x60, tratando timeout e erros.
O rato confirma com 0xFA cada comando e cada argumento que aceita. A exceção documentada nos slides é o comando de reenvio: quando o byte recebido é o próprio 0xFE enviado pelo anfitrião, o rato não devolve ACK. No sentido contrário, 0xFE como resposta do rato pede reenvio e 0xFC assinala erro. Limita as tentativas. O ACK não é o conteúdo da resposta de um comando que devolve dados: essa resposta vem depois.
Para definir taxa de amostragem, há uma sequência para 0xF3 e outra para o argumento. Cada uma usa 0xD4 e recebe o seu ACK. Se o argumento provocar 0xFE, a regra apresentada nos slides pede repetir o comando completo, não apenas o argumento. Os dois bytes do rato precisam novamente de encaminhamento por 0xD4 e de confirmação; o próprio 0xD4 é uma ordem ao KBC e não recebe ACK do rato.
Em stream mode com reporting ativo, desativa reporting antes de enviar outros comandos para não misturar pacotes de movimento com respostas. A fase de comandos e a fase de receção de pacotes devem ter um dono claro do output buffer.
De pacotes a gestos
Um gesto depende de uma sequência, por isso precisa de estado. Num exemplo próprio, queremos um arrasto horizontal para a direita com botão esquerdo premido, comprimento de pelo menos 100 unidades e desvio vertical absoluto no máximo 5.
Começa em WAIT_PRESS. Na transição de botão solto para premido, passa a DRAWING e põe os acumulados a zero. Enquanto o botão continuar premido, soma os deltas. Se o movimento voltar para a esquerda ou o desvio ultrapassar a tolerância, passa a INVALID e ignora os movimentos até o botão ser largado. Só uma nova pressão inicia outro candidato. Se e , reconhece o gesto uma vez e passa a COMPLETE, onde espera pela libertação. Largar o botão antes disso cancela-o. Neste modelo, o pacote da pressão inicia o candidato, mas não acrescenta movimento; só os seguintes, ainda com o botão premido, entram na soma.
Premir leva a DRAWING, com x = y = 0. O pacote da pressão não soma deltas.
Δx ≥ 0, |y| ≤ 5 e x < 100: continuar aqui.Se o movimento deixar de cumprir estas condições:
Δx < 0 ou |y| > 5INVALIDIgnorar movimento até largar.Δx ≥ 0, |y| ≤ 5 e x ≥ 100COMPLETEReconhecer uma vez. Esperar libertação do botão.DRAWING cancela o candidato; INVALID eCOMPLETE deixam de esperar. Os três regressam aWAIT_PRESS. Só uma nova pressão inicia outro candidato.A decisão de usar desvio vertical acumulado é parte do enunciado. Aceitar cada pacote com permitiria dez pacotes de +5 e um desvio final de 50. O parser de bytes e a máquina do gesto resolvem problemas distintos: a primeira produz pacotes completos; a segunda interpreta o seu significado ao longo do tempo.
Exercícios
Usar o nono bit
Um pacote válido sem overflow é 08 C8 00. Quanto vale delta X em decimal?
Pista
Solução
O sinal X é 0, por isso delta X é o byte baixo 0xC8 sem sinal, ou +200. Não
há overflow. Um cast para int8_t produziria um resultado
diferente e incorreto para o protocolo.
Erros frequentes
Usar o bit 7 de C8 como sinal de um delta que tem nove bits.
Converter para o ecrã
Um pacote válido é 28 00 05. O cursor tem screen_y = 100 e
o ecrã cresce para baixo. Sem aplicar limites, qual é o novo
screen_y?
Pista
Solução
delta Y = -251 nas coordenadas físicas do rato. screen_y =
100 - (-251) = 351. O movimento físico foi para baixo, por isso a coordenada
do ecrã aumenta.
Erros frequentes
Interpretar 0x05 sempre como +5 ou somar delta Y numa coordenada de ecrã que cresce para baixo.
Não reiniciar no byte de dados
A montagem começa com índice 0 e recebe 08 18 00. Um programa reinicia o pacote sempre que vê bit 3 a 1, mesmo a meio. Explica por que falha e interpreta o pacote corretamente.
Pista
Solução
Depois de aceitar 0x08, o índice é 1. 0x18 deve ocupar o byte X e 0x00 o byte Y. O pacote tem delta X +24, delta Y 0, sem botões. O teste de sincronização só deve escolher o primeiro byte quando o índice é 0; reiniciar em 0x18 perde o pacote.
Confere a tua resposta:
- Aceitei 0x08 como primeiro byte.
- Guardei 0x18 como dado X.
- Completei o pacote com 0x00 sem reiniciar.
Erros frequentes
Assumir que bit 3 a 1 identifica exclusivamente um cabeçalho em qualquer posição.
Acumular o desvio de um gesto
Num arrasto horizontal para a direita, exige-se comprimento de pelo menos 100 unidades e desvio vertical acumulado absoluto no máximo 5 em todos os passos. Movimento para a esquerda ou desvio excessivo invalida o candidato até largar o botão. O botão esquerdo acabou de ser premido e os acumulados são zero. Chegam movimentos (40, 3), (40, 3) e (30, -2), mantendo o botão premido. O gesto deve ser reconhecido? Indica em que passo se decide.
Pista
A tolerância vale para o desvio acumulado em todo o candidato.
Solução
Não. O primeiro movimento deixa (40, 3), dentro da tolerância. O segundo deixa (80, 6), ultrapassa 5 e invalida o candidato. O terceiro não recupera esse gesto, apesar de a soma dos três dar (110, 4). Um novo candidato exige largar e voltar a premir o botão.
Confere a tua resposta:
- Somei y até 6 no segundo movimento.
- Invalidei antes de atingir comprimento 100.
- Não reativei o candidato no terceiro movimento.
Erros frequentes
Testar apenas cada delta ou só a soma final aceita uma sequência que saiu da tolerância a meio.
Repetir o comando após reenvio
Enviaste 0xF3 com ACK e o argumento recebe
0xFE. Segue a regra dos slides de rato para este erro: indica
que bytes reenvias e quais recebem ACK. O KBC também confirma
0xD4 com ACK do rato?
Pista
Mais uma pista
Solução
Repete o comando completo. Espera IBF vazio e envia 0xD4 ao
KBC, depois 0xF3 ao rato, que deve responder com ACK. Para o
argumento, envia outro 0xD4 e depois o argumento, com outro ACK
do rato. O KBC não devolve um ACK de rato por receber 0xD4;
essa ordem só encaminha o byte seguinte. Limita as tentativas e trata
0xFC
como erro. Repetir apenas o argumento não segue a regra de recuperação dada.
Confere a tua resposta:
- Identifiquei 0xFE como pedido de reenvio.
- Repeti comando e argumento completos.
- Limitei as tentativas.
Erros frequentes
Tratar o ACK do comando como resposta ou repetir bytes sem limite.