Como manipular bits usando Golang
Estou brincando com um projeto de um interpretador BASIC escrito em Go, e a saída de video em modo texto, segue o padrão de cores do CGA, ou seja, um byte contém nos quatro bits mais altos o código para a cor do fundo e nos quatro bits mais baixos o código para a cor da fonte. Ou seja, 16 cores para as letras e 16 cores para o fundo. Se o flag de blink estiver ativo, o bit mais alto indicará que o sistema deve “piscar” a letra. Com isso, acontece uma coisa interessante, só as cores de fundo com esse bit ligado podem piscar. Por exemplo, o azul cor 0x09, que em binário é 1001, pisca, mas o vermelho 0x4 não pisca porque em binário é 0100.
Essa brincadeira parece complicada, mas na verdade é muito simples, precisamos apenas aprender algumas poucas operações para poder manipular bits.
Antes de começarmos, vamos combinar o seguinte: quando eu falar sobre a parte superior ou alta de um conjunto de bits, estou me referindo aos bits mais à esquerda, e quando estiver falando da parte inferior ou parte baixa, estou falando dos bits mais à direita. Nem sempre é assim, porém, para efeito desse texto, é assim que funciona. É o que chamamos de little-endian.
Shift (<< e >>, desloca bits)
Shift é o operador para mover bits para direita << ou esquerda >>
Exemplo:
00000001 << 1 resulta em: 00000010 00000001 << 2 resulta em: 00000100 00000001 << 3 resulta em: 00001000 10000000 << 1 resulta em: 01000000 10000000 << 2 resulta em: 00100000 10000000 << 3 resulta em: 00010000
AND (&)
Usamos AND para definir uma máscara e usamos essa mascara para extrair a parte que queremos de um conjunto de bits.
Exemplo:
Digamos que queremos extrair apenas os quatro bits centrais de um byte, para isso definimos a seguinte mascara: 00111100. Note que os bits que queremos extrair estão com 1 e os que não queremos estão com 0. Daí, basta comparar o byte que contém o valor a ser extraído com a máscara, usando AND (&).
valor & mascara 10101010 & 00111100 resulta em: 00101000
O que AND faz é comparar cada um dos bits com o seu correspondente na máscara e apenas onde os dois bits forem 1, vai ser 1 no resultado. Com isso, os outros bits fora da máscara foram zerados.
E agora podemos usar shift para a direita , andar duas casas e obter o valor que queremos extrair em um byte.
00101000 >> 2 resulta em: 00001010
Ou seja, usamos AND junto com uma máscara de bits para separar as partes que queremos de um conjunto de bits.
OR (|)
OR nesse caso é o inverso, usaremos para combinar dois conjuntos de bits em um.
Exemplo:
Digamos que queremos juntar esses quatro bits 1010 na parte superior de um byte, seguido desses 4 bits 0101 na parte inferior.
Primeiro movemos os 4 bits para a parte superior do byte:
00001010 << 4 resulta em: 10100000
Daí, usamos OR para juntar a parte superior e a inferior:
10100000 | 00000101 resulta em: 10100101
Vamos ver um exemplo em Go:
package main import "fmt" func main() { /* Pega os códigos da cor da fonte e do fundo e combina os dois em um único byte */ var b, f byte b = 0x9 // cor do fundo (azul claro) f = 0x4 // cor da fonte (vermelho) // combina os dois códigos para compor o código da cor c := (f & 0x0f) | (b << 4) /* Pega o código da cor e separa a cor da fonte e do fundo para poder usar separadamente */ h := (c & 0xf0) >> 4 // High Nibble l := c & 0x0f // Low Nibble blink := (c & 0x80) >> 7 /* Mostra os resultados */ fmt.Printf("cor do fundo.: %04b\n", h) fmt.Printf("cor da letra.: %04b\n", l) fmt.Printf("codigo da cor: %08b\n", c) fmt.Printf("blink........: %b", blink) }