Conteúdos da cadeira

Falar com o hardware

Mapeamento em memória, registos e polling com máscaras de bits.

Markdown

Perguntar sobre esta página

ChatGPTClaudePerplexityGeminiCopiar e abrir

Envia o link e pede à IA para ler a página. No Gemini, cola a pergunta copiada.

Ver pergunta para copiar
Nesta página

Um periférico, visto do programa, é um conjunto de registos indentados por endereços: escreves num endereço para lhe dar ordens e lês de outro para saber o estado e receber dados. A técnica chama-se mapeamento em memória (memory-mapped I/O), e dominá-la é falar com o hardware. Tudo o resto da cadeira é variação sobre este tema com registos diferentes.

Registos e portas

Cada periférico expõe alguns endereços. Por convenção, uns são de estado (dizem o que se passa), outros de dados (transportam os bytes) e outros de controlo (configuram o comportamento). Lês o estado, decides, e só depois tocas nos dados ou no controlo. Nunca escrevas num registo sem saber que bits estás a mexer: muitos registos misturam campos independentes no mesmo byte, e escrever o byte inteiro pode mudar um campo que querias intacto.

A leitura e a escrita fazem-se com funções de acesso a portas, que recebem o endereço e o valor. O padrão de cada operação é sempre o mesmo: máscara para isolar o campo, comparação para decidir, escrita cirúrgica para atuar.

Máscaras de bits

Uma máscara é um valor com 1s exatamente nas posições que te interessam, combinada com o registo através de AND para leitura ou OR para escrita:

uint8_t status = ler_registo(REG_ESTADO);

if (status & BIT_PRONTO) {
  uint8_t dado = ler_registo(REG_DADOS);
  /* processar o byte recebido */
}

BIT_PRONTO vale 1 << 0 se o bit de pronto for o bit 0. O teste status & BIT_PRONTO é diferente de zero quando o bit está ligado, seja qual for o resto do byte. Para ligar um bit sem tocar nos outros, usa OR: controlo | BIT_ATIVAR. Para desligar, usa AND com o complemento: controlo & ~BIT_ATIVAR. Estes três gestos, AND para testar, OR para ligar, AND com NOT para desligar, resolvem quase todas as manipulações de registos da cadeira.

Um exemplo concreto: o registo de estado vale 0x21, ou seja 0b00100001. Se o bit de pronto for o bit 0, 0x21 & 0x010x01, diferente de zero, por isso há dado à espera. Se o bit de erro for o bit 5, 0x21 & 0x200x20, por isso há também um erro assinalado. Dois testes independentes no mesmo byte, cada um cego ao outro campo.

Polling: o ciclo de espera

O polling pergunta ao dispositivo em ciclo até ele estar pronto. Se já viste a comparação entre mecanismos em Entrada e saída, aqui está a implementação:

while (!(ler_registo(REG_ESTADO) & BIT_PRONTO)) {
  /* espera ativa: o processador não faz mais nada */
}
uint8_t dado = ler_registo(REG_DADOS);

Repara nas duas leituras separadas: o estado lê-se dentro do ciclo, os dados lêem-se uma vez fora dele. Ler os dados dentro do ciclo consumiria bytes antes de os processares. E repara que o ciclo não tem corpo: a espera ativa é propositada, mas é também o preço, porque o processador fica preso aqui até o dispositivo responder.

Para levar para a próxima página

O polling funciona e é simples, mas ata o processador ao ritmo do periférico mais lento. A página sobre Interrupções inverte a relação: em vez de perguntares sem parar, subscreves o evento e segues a tua vida até o hardware te chamar.

Ver o ficheiro no GitHub

À tua maneira

Escolhe como preferes ler.

Aparência
Ajustar cores e largura
Cor de destaque do tema FEUP
Tipo de letra

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

As tuas escolhas ficam guardadas neste navegador.

Pesquisar

Escreve para pesquisar em todo o site.

para escolher · Enter para abrir · Esc para fechar

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.