Assembly NumaBoa - Capítulo 1

REPRESENTAÇÃO DE DADOS III

Deslocamentos e Rotações

Outro conjunto de operações lógicas que podem ser aplicadas em strings de bits são o deslocamento (shift) e a rotação (rotate). As duas categorias ainda podem ser subdivididas em deslocamento para a esquerda (left shift), deslocamento para a direita (right shift), rotação para a esquerda (left rotate) e rotação para a direita (right rotate). Estas operações se revelaram extremamente úteis para os programadores da linguagem Assembly.

Shift left

Shift para a esquerda

A operação de deslocamento para a esquerda move uma posição para a esquerda cada um dos bits de uma string de bits (veja ao lado). O bit zero é deslocado para a posição 1, o da posição 1 para a posição 2, etc. Surgem naturalmente duas perguntas: "O que vai para o bit zero?" e "Para onde vai o bit 7?" Bem, isto depende do contexto. Nós colocaremos um bit zero na posição zero e o bit sete "cai fora" nesta operação.

Observe que, deslocar o valor para a esquerda é o mesmo que multiplicá-lo pela sua base (ou radix). Por exemplo, deslocar um número decimal para a esquerda em uma posição (adicionando um zero à direita do número) o multiplica por 10 (a sua base):

	1234 SHL 1 = 12340       (SHL 1 = shift esquerda 1 posição)

Como a base de um número binário é dois, o deslocamento em uma posição para a esquerda multiplica-o por 2. Se deslocarmos um valor binário duas vezes para a esquerda, ele é multiplicado duas vezes por 2, ou seja, é multiplicado por 4. Se o deslocarmos três vezes, será multiplicado por 8 (2*2*2). Como regra, se deslocarmos um valor binário para a esquerda n vezes, isto o multiplicará por 2^n (2n ou 2 elevado a n).

Shift right

Shift para a direita

Uma operação de shift para a direita funciona do mesmo modo que a anterior, exceto que os dados são deslocados na direção oposta. O bit sete é movido para a posição seis, o bit seis para a cinco e assim sucessivamente. Numa operação de deslocamento para a direita, introduzimos um zero no bit sete e o bit zero será descartado.

Como o deslocamento para a esquerda equivale a uma multiplicação pela base, não é de se admirar que um deslocamento para a direita equivale a uma divisão pela base. No sistema binário, se fizermos n deslocamentos para a direita, o valor será dividido por 2n.

Existe um problema relacionado com a divisão efetuada por um shift para a direita: um shift para a direita equivale a uma divisão de um número sem sinal por 2. Por exemplo, se deslocarmos uma posição para a direita a representação sem sinal de 254 (0FEh), obtemos 127 (07Fh), exatamente o esperado. Entretanto, se deslocarmos uma posição para a direita a representação binária de -2 (0FEh), obtemos 127 (07Fh), o que não está correto. Este problema ocorre porque estamos introduzindo zero no bit sete. Se o bit sete contém 1 antes do deslocamento, indicativo de número negativo nos inteiros com sinal, e depois recebe zero, estamos alterando o sinal deste número (que passa de negativo para positivo). Como este não é o propósito da divisão... dá erro.

Arithmetic shift right

Shift artimético para a direita

Para usar um shift para a direita como um operador de divisão, antes é preciso definir uma terceira operação de deslocamento: o deslocamento aritmético para a direita (arithmetic shift right). Um shift aritmético para a direita funciona como o shift para a direita normal, com uma exceção: ao invés de deslocar o bit sete para a posição seis, este bit é deixado intacto, ou seja, o bit sete não é zerado.

Isto geralmente produz o resultado esperado. Por exemplo, fazendo um shift aritmético para a direita com -2 (0FEh), obtemos -1 (0FFh). Uma coisa, no entanto, não pode ser esquecida: esta operação sempre arredonda os números para o inteiro que seja menor ou igual ao resultado, ou seja, arredonda para baixo. Um shift artimético para a direita com 5 dá como resultado 2. Mas preste atenção. Um shift aritmético para a direita com -1 dá como resultado -1, e não zero! O arredondamento se faz na direção do menor valor e -1 é menor do que 0. Este não é um "bug" no shift aritmético para a direita, é apenas como a divisão de inteiros foi definida.

Outro para muito útil é a rotação para a esquerda e para a direita. Estas operações se comportam como os deslocamentos, com uma diferença importante: o bit que sai numa extremidade entra na extremidade oposta.

Rotate left Rotate right
Rotação para a esquerdaRotação para a direita

Campos Bit e Dados Compactados

Campos bit

Campos bit especiais

Apesar do 80x86 operar eficientemente com tipos de dado byte, word e double word, ocasionalmente teremos que trabalhar com tipos de dado que usam um número bits diferente do habitual (8, 16 ou 32). Por exemplo, imagine uma data na forma "2/4/98". São três valores numéricos que representam uma data: valores para o dia, o mês e o ano. Os dias variam de 1 a 31, o que consome 5 bits (valor máximo de 32) para representar a entrada dos dias. Os meses variam de 1 a 12. Portanto, 4 bits são suficientes (valor máximo de 16). O valor para o ano, imaginando que trabalhemos com valores variando de 0 a 99, precisam de 7 bits (que podem representar até o valor 128). Somando os bits necessários, chega-se a 16 bits, o mesmo que 2 bytes. Em outras palavras, podemos acomodar nossos dados de data em dois bytes ao invés de três se usássemos um byte para cada um dos campos dia, mês e ano. Isto economiza um byte de memória para cada data armazenada, o que pode representar uma economia substancial se tivermos que armazenar milhões de datas. O arranjo dos bits pode ser visto na figura acima.

DDDDD representam os 5 bits reservados para o valor do dia, MMMM representam os quatro bits para armazenar o mês e YYYYYYY são os sete bits reservados para o valor do ano. Cada coleção de bits representando um item de data é denominado de campo bit. 2 de Abril de 1998 seria representado respectivamente pelos bits 00010 0100 1100010, ou seja, 1262h.

	DDDDD   MMMM   AAAAAAA
	00010   0100   1100010	ou 1262h
	  2       4       98

Apesar dos valores compactados serem muito eficientes para economizar espaço (isto é, eficientes em termos de uso de memória), computacionalmente são muito ineficientes. A razão é que dependem de instruções extras para descompactar os dados compactados nos diversos campos bit. Estas operações adicionais consomem tempo e bytes adicionais para armazenar as instruções. Portanto, o planejamento prévio e o custo/benefício precisa ser bem analisado.

Existem inúmeros exemplos de tipos de dados compactados. Pode-se compactar oito valores booleanos num único byte, pode-se acomodar dois dígitos BCD num byte, etc.

O conjunto de caracteres ASCII

O conjunto de caracteres ASCII (excluindo-se os caracteres expandidos definidos pela IBM) é dividido em quatro grupos de 32 caracteres. O primeiro grupo, códigos ASCII de 0 a 1Fh (31), formam um conjunto especial de caracteres não imprimíveis chamados caracteres de controle. São chamados de caracteres de controle porque realizam várias operações de controle de impressão/display ao invés de mostrarem símbolos. Exemplos incluem o retorno de carro (carriage return), que posiciona o cursor no lado esquerdo da linha atual, avanço de linha (line feed), que move o cursor uma linha para baixo no dispositivo de saída e o retorno (back space), que move o cursor uma posição para a esquerda. Infelizmente os caracteres de controle realizam operações diferentes dependendo do dispositivo de saída. A padronização entre os dispositivos de saída é precária. Para saber exatamente como se comporta determinado caracter de controle em relação a um determinado dispositivo de saída, é preciso consultar o manual do dispositivo.

O segundo grupo de 32 códigos de caracteres ASCII inclui vários símbolos de pontuação, caracteres especiais e dígitos numéricos. Os caracteres mais conhecidos deste grupo são o espaço (código ASCII 20h) e os dígitos numéricos (códigos ASCII de 30h a 39h). Lembre-se de que os dígitos numéricos diferem dos seus valores numéricos apenas no nibble mais significativo. Se subtrairmos 30h do código ASCII de qualquer um dos dígitos, obtemos o equivalente numérico deste dígito.

O terceiro grupo de caracteres ASCII é reservado para os caracteres alfabéticos maiúsculos. Os códigos ASCII para os caracteres de A a Z ficam no intervalo 41h a 5Ah (65 a 90). Como só existem 26 caracteres alfabéticos diferentes, os seis códigos restantes são de vários símbolos especiais.

Caracteres ASCII

Caracteres maiúsculos e minúsculos
diferem em apenas um bit

Finalmente, o quarto grupo de 32 códigos de caracteres ASCII são reservdos para os símbolos alfabéticos minúsculos, cinco símbolos adicionais especiais e outro caracter de controle (delete). Observe que os símbolos dos caracteres minúsculos usam os códigos ASCII de 61h a 7Ah. Se convertermos os códigos dos caracteres maiúsculos e minúsculos para binário, é possível verificar que os símbolos maiúsculos diferem dos seus correspondentes minúsculos em apenas um bit. Por exemplo, veja ao lado os códigos para os caracteres "E" e "e".

A única diferença entre estes dois códigos reside no bit 5. Caracteres maiúsculos sempre possuem 0 no bit cinco, os minúsculos sempre possuem 1 no bit cinco. Podemos usar esta característica para converter rapidamente maiúsculas em minúsculas e vice versa. Se o caracter for maiúsculo, para forçá-lo para minúsculo basta setar o bit cinco para 1. Se o caracter for minúsculo, para forçá-lo para maiúsculo basta setar o bit cinco para 0.

Na realidade, os bits cinco e seis determinam o grupo ao qual o caracter ASCII pertence:

Bit 6Bit 5Grupo
00Caracteres de controle
01Dígitos e caracteres de pontuação
10Maiúsculos e especiais
11Minúsculos e especiais

Podemos, por exemplo, transformar qualquer caracter maiúsculo ou minúsculo (ou especial) no seu caracter de controle equivalente apenas zerando os bits cinco e seis.

CaractDecimalHexa
04830h
14931h
25032h
35133h
45234h
55335h
65436h
75537h
85638h
95739h

Agora observe ao lado os códigos ASCII para os caracteres dos dígitos numéricos. A representação decimal destes códigos ASCII não esclarece grande coisa mas, a representação hexadecimal revela algo muito importante - o nibble menos significativo do código ASCII é o equivalente binário do número representado. Zerando o nibble mais significativo, converte-se o código do caracter para a sua representação binária. Inversamente, é possível converter um valor binário do intervalo de 0 a 9 para a sua representação ASCII simplesmente setando o nibble mais significativo em três. Note que é possível usar a operação lógica AND para forçar os bits mais significativos para zero e usar a operação lógica OR para forçar os bits mais significativos para 0011 (três).

Lembre-se de que não é possível transformar uma string de caracteres numéricos na sua representação binária correspondente simplesmente zerando o nibble mais significativo de cada dígito da string. Transformando 123 (31h 32h 33h) desta maneira resulta em três bytes: 010203h, e não no valor correto que é 7Bh. Transformar uma string de dígitos requer um pouco mais de sofisticação. A transformação explicada acima só serve para dígitos únicos.

O bit sete no ASCII padrão é sempre zero. Isto significa que o conjunto de caracteres ASCII utiliza apenas a metade dos códigos possíveis num byte de oito bits. A IBM usa os 128 códigos restantes para vários caracteres especiais (com acento, etc), símbolos matemáticos e caracteres de desenho de linhas. Deve ficar claro que estes caracteres extras são uma extensão não padronizada do conjunto de caracteres ASCII. É claro que o nome IBM tem peso e a maioria dos computadores baseados no 80x86 e as impressoras acabaram incorporando os caracteres adicionais da IBM.

Apesar do fato de que é um padrão, codificar seus dados usando simplesmente os caracteres padrão ASCII não garante a compatibilidade entre os sistemas se bem que, hoje em dia, dificilmente encontraremos problemas. Como usaremos com frequência os caracteres ASCII em Assembly, seria interessante guardar de cabeça alguns códigos ASCII importantes, como o do "A", do "a" e do "0".

Comentários

Brinque com os bits até não poder mais e parabéns por entrar no clube dos chamados "escovadores de bits". Dou-lhe as boas vindas, mas prepare-se... no capítulo 2 vem chumbo grosso. Se você não estiver firme nestes assuntos, hmmmmmm...

Para este tópico do capítulo 1 também existe um aplicativo escrito em Object Pascal (Delphi) pelo Randall Hyde que traduzi para o Português, adaptei para o exemplo acima e recompilei. É um compactador de dados para datas no formato dia/mês/ano. Você pode fazer o download do executável com código fonte aqui na Aldeia.

De qualquer modo, é melhor encarar o laboratório do capítulo 1, assunto do próximo módulo, antes de querer continuar. As ferramentas indicadas para download devem ajudar.



| AAAA | Página Inicial | Mapa do Site | Novidades | Busca | Indique esta página | Mestre da Teia | Voltar |
| Localizador || @ Info NumaBoa > Assembly NumaBoa > Numeração hexadecimal > Deslocamentos e rotações > Laboratório cap.1
Autoria: Randall Hyde - Art of Assembly Language Programming. Tradução: vovó Vicki

webdesign sobMedida by vickiSoft - /informatica/assembly/cap1_3.php (12.01.04) versão 1.0 de 12.02.04
Licença Creative Commons 1998-2006 Aldeia NumaBoa
Exceto onde especificamente declarado, todo material deste site é disponibilizado de acordo com a Licença Creative Commons.