Pular para o conteúdo

VGA e Framebuffers

Conseguir colocar saída na tela é um dos primeiros marcos no desenvolvimento de kernel, e o x86 oferece dois mecanismos bem diferentes para isso: o VGA text mode legado, um display fixo e orientado a caracteres exigindo quase nenhuma configuração, e um framebuffer linear, um bloco de memória representando pixels individuais diretamente, do qual todo modo gráfico moderno (e a saída gráfica real de todo sistema operacional) depende em última instância.

O text mode apresenta a tela como uma grade de 80 colunas por 25 linhas de caracteres, sustentada por memória começando no endereço físico fixo 0xB8000, que firmware baseado em BIOS convencionalmente deixa já ativo no boot; um kernel pode escrever nessa memória imediatamente, sem passo algum de configuração de modo, tornando-a a saída de display mais simples possível para desenvolvimento inicial. Cada célula de caractere ocupa dois bytes consecutivos: o código de caractere ASCII, seguido de um byte de atributo codificando cor de primeiro plano e de fundo.

volatile uint16_t *vga_buffer = (uint16_t *)0xB8000;
void vga_put_char(int col, int row, char c, uint8_t color) {
vga_buffer[row * 80 + col] = (uint16_t)c | ((uint16_t)color << 8);
}

O byte de atributo se divide em uma cor de primeiro plano de 4 bits (bits 0–3) e uma cor de fundo de 3 bits (bits 4–6), com o bit 7 historicamente sendo ou um bit adicional de cor de fundo ou uma flag de habilitação de blink, dependendo do modo específico de hardware VGA configurado (um detalhe raramente relevante para um kernel que faz nada além de imprimir texto comum). A posição do cursor de hardware é controlada separadamente através de um par de portas de índice/dado do CRTC do VGA (0x3D4/0x3D5), escrita como um offset row-major de 16 bits na mesma grade 80×25 que o próprio buffer usa.

O text mode não tem equivalente algum para desenhar gráficos arbitrários: qualquer coisa além de glifos de caractere fixos exige um framebuffer genuinamente endereçável por pixel, um bloco de memória, geralmente muito maior que o pequeno buffer do text mode, onde cada pixel corresponde a um grupo de bytes de tamanho fixo (comumente 32 bits por pixel, contendo vermelho, verde, azul, e às vezes um canal alfa não usado) em uma localização computável diretamente a partir de suas coordenadas x/y e do pitch configurado do framebuffer (o número de bytes por linha, que nem sempre é simplesmente largura vezes bytes por pixel, já que o hardware às vezes preenche cada linha até um alinhamento conveniente).

void put_pixel(struct framebuffer *fb, int x, int y, uint32_t color) {
uint8_t *pixel = fb->address + y * fb->pitch + x * (fb->bpp / 8);
*(uint32_t *)pixel = color;
}

Diferente do endereço fixo do text mode, a localização, resolução, e formato de pixel de um framebuffer não são valores arquiteturalmente fixos que um kernel possa simplesmente presumir; precisam ser descobertos a partir do firmware, ou explicitamente requisitados e configurados, antes de poderem ser usados.

Em sistemas baseados em BIOS, as VESA BIOS Extensions (VBE) fornecem uma interface de interrupção em real mode para consultar modos gráficos disponíveis e seu endereço de framebuffer, resolução, e layout de pixel, e para trocar para um modo escolhido, necessariamente realizado ainda em real mode, já que VBE é um serviço de BIOS, antes de qualquer transição para protected ou long mode levar a CPU além de onde chamadas de BIOS permanecem utilizáveis. Sistemas UEFI, em vez disso, usam o Graphics Output Protocol (GOP), um protocolo de Boot Services retornando informação equivalente (endereço de framebuffer, resolução, formato de pixel) através de chamadas comuns de protocolo UEFI, em vez de uma interrupção em real mode, consistente com a interface geralmente mais estruturada da UEFI descrita em BIOS vs. UEFI. Um bootloader compatível com Multiboot pode adicionalmente configurar um framebuffer e passar seus detalhes diretamente na estrutura de informação Multiboot, poupando o kernel de implementar consulta VBE ou GOP por conta própria.

Memória de framebuffer deve ser mapeada como write-combining, em vez de totalmente cacheável ou totalmente não cacheável, quando o hardware subjacente e o formato de tabela de página suportam: write-combining permite que a CPU agrupe escritas individuais de pixel em transações maiores de barramento, o que melhora perceptivelmente o desempenho para qualquer coisa que desenhe substancialmente por pixel, ainda evitando os riscos de correção que um mapeamento totalmente cacheado introduziria para memória que o hardware de display lê diretamente. Um erro comum ao trocar do VGA text mode para um framebuffer gráfico é esquecer que são caminhos de display inteiramente separados, sem estado compartilhado: código escrito para imprimir texto de depuração via 0xB8000 para de ter efeito visível algum assim que um modo de vídeo diferente foi definido, silenciosamente, em vez de produzir qualquer indicação de por que a saída parou de aparecer.

  1. ^ OSDev Wiki, “VGA Hardware”: uma referência hobbyist detalhada cobrindo programação de registrador VGA além do text mode.
  2. ^ VESA, VBE Core Function Specification: define a interface de consulta de modo e descoberta de framebuffer da era BIOS.
  • Serial Port: um caminho de saída mais simples comumente usado antes de um driver de display sequer existir.
  • PCI: como uma placa de vídeo discreta é descoberta antes que seu framebuffer possa ser configurado.
  • Console e Emulação de Terminal: o interpretador de sequência de escape que a maioria dos kernels constrói diretamente sobre esta camada de desenho.
  • Escrevendo um Bootloader UEFI: o fluxo de Boot Services do qual a descoberta de framebuffer via GOP é uma chamada.