Desenvolvimento

29 set, 2017

Go: brincando com bits

Publicidade

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)
}

Teste no The Go Playground