Curso de Assembly
Lógica Digital


Você está em: MarMSX >> Cursos >> Assembly Z80   É necessário entender um pouco de lógica digital para programarmos em Assembly. O objetivo é compreendermos os sistemas de numeração, de forma a diferenciar um número binário, decimal ou hexadecimal, além de conhecermos operações lógicas como E, OU, NÃO e OU Exclusivo.


  1- Sistemas de Numeração

  Sistema decimal

  O sistema de numeração que usamos diariamente em nosso cotidiano é o sistema decimal. Ele é formado por dez algarismos distintos a saber: 0, 1, 2, 3, 4, 5, 6, 7, 8 e 9. É a partir desses algarismos que formamos todos os outros números.
  Um número decimal é composto por casas decimais, começando pela casa das unidades, passando pela dezenas, centenas, milhares e por aí vai. Por exemplo, o número 125 irá possuir:
 Centena Dezena Unidade
    1       2      5
  O valor "1" está na casa das centenas, "2" está na casa das dezenas e "5" está na casa das unidades.
  Quando aumentamos em uma unidade um número, se uma casa decimal possuir o algarismo máximo (9), o valor da casa decimal imediatamente à esquerda é incrementado (aumentado em uma unidade), e o valor da casa em questão é retornado ao algarismo mínimo (0). Quando há estouro do valor máximo de uma casa decimal, dizemos que "vai um".

 

  Para a soma de um número com um valor qualquer, o resultado da soma para aquela casa decimal quando "estoura" é o valor da casa da unidade da soma, e o "1" que sobra vai para a próxima casa decimal. Exemplo:
   1
   1 8
 +   4   8 + 4 = 12
 ----- 
   2 2
  De forma análoga, quando diminuímos em uma unidade um determinada casa decimal contendo o valor mínimo (0), diminuímos em uma unidade a casa decimal imediatamente à sua esquerda, e a casa atual vai para o valor máximo (9). A essa operação dizemos que "pegamos emprestado" da próxima casa.
 Passo 0   Passo 1   Passo 2
            0
  1 0       1 10       0 10
 -  1      -   1     -   1
  ---      -----     -----
               9       0 9

           10-9=1    0-0=0
  Sempre quando somamos ou subtraímos um número, fazemos a operação com as casas decimais equivalentes e aplicando as regras mencionadas, começando pela casa das unidades e partindo para as casas maiores. Exemplo:
  <----

   D U
   2 5
 + 1 4
 -----
   3 9
  Primeiros somamos 5 com 4, e depois 2 com 1.

  A propriedade do "vai um" é transmissível às casas seguintes, quando há sucessivos "estouros". Exemplos:

  Soma:
  <-----

   1 1
   1 9 9
 +   1 5
 -------
   2 1 4

  Subtração:
  <-----

   1 1
   2 1 4     Unidades: 4-6 = pede emprestado = 14-6 = 8
 -   2 6     Dezenas:  0-2 = pagou, mas também pede emprestado = 10-2 = 8
 -------     Centenas: 1-0 = pagou e quitou empréstimo = 1-0 = 1
   1 8 8

  Esses conceitos que aprendemos no Ensino Fundamental são importantes para a compreensão do flag "Carry" do Z80, que indica que houve estouro em uma operação aritmética.

  Além do sistema decimal, existem ainda mais três sistemas de numeração utilizados em computação: binário, octal e hexadecimal.


  Sistema binário

  O sistema binário (no MSX é representado pelo prefixo &B) possui apenas 2 algarismos distintos: 0 e 1. A ordem crescente dos números é:
 0, 1, ?
  Não temos o algarismo 2 para representar o próximo número. E agora?
  Simples! Seguindo a regra de formação dos números decimais, quando "estoura" uma casa, incrementamos a casa seguinte e retornamos a casa atual para zero. Assim, o próximo número binário seria "10". Logo, a seqüência fica:
 0, 1, 10, 11, 100, 101, 110, 111, 1000, 1001, ...
Veja as operações a seguir utilizando números binários.

  0 + 1:
   0
 + 1
 ---
   1

  1 + 1:
  1
   1
 + 1  <---- Aqui houve estouro, pois "1" é o máximo valor de um número binário
 ---
  10

  10 + 1:
   10
 +  1
 ---
   11

  11 + 1:
  1
   11
 +  1  <---- Aqui houve estouro, pois "1" é o máximo valor de um número binário
  ---
  100

  Observe que essa regra se aplica para incrementos e decrementos. Para soma ou subtração entre números, siga a mesma regra utilizada pelo sistema decimal para somas ou subtrações desse tipo.

  Exemplo de adição:
 Decimal:			Binário
   1                             111
   15				  011
 + 17                           + 101
 ----                           ----- 
   32                            1000

 Ordem das somas:		Ordem das somas:
 1o.: 5 + 7 = 12 -> Vai um      1o.: 1 + 1 = 10 -> Vai um
 2o.: 1 + 1 (+ 1) = 3           2o.: 1 + 0 (+ 1) = 10 -> Vai um
                                3o.: 0 + 1 (+ 1) = 10 -> Vai um
                                4o.: 0 + 0 (+ 1) = 1

  Exemplo de subtração:
  <--- sentido das subtrações

   1
   101
 - 010
 -----
   011

 Ordem das subtrações:
 1o.: 1 - 0 = 1
 2o.: 0 - 1 = pede emprestado = 10 - 1 = 1
 3o.: 1 - 0 = paga = 0 - 0 = 1

  A equivalência entre valores do sistema decimal e binário pode ser vista a seguir:
 Decimal  Binário
    0        0
    1        1
    2        10
    3        11
    4        100
    5        101
    6        110
    7        111
   ...       ...
  A conversão entre os sistemas binário e decimal podem ser feitas usando calculadoras ou o próprio Basic do MSX. Veja como:
' Binario para decimal
PRINT &B100 
 4
Ok

' Decimal para binario
PRINT BIN$(4) 
100
Ok
  Observa-se que as operações lógicas do processador Z80 sobre números é feita sempre baseada em números binários.


  Sistema octal

  O sistema octal (no MSX é representado pelo prefixo &O) possui 8 algarismos distintos: 0, 1, 2, 3, 4, 5, 6 e 7.
  A seqüência crescente desse sistema seria: 0, 1, 2, 3, 4,5, 6, 7, 10, 11, 12, 13, 14, 15, 16, 17, 20, 21, ...

  A conversão entre os sistemas octal e decimal podem ser feitas usando calculadoras ou o próprio Basic do MSX. Veja como:
' Octal para decimal
PRINT &O12 
 10
Ok

' Decimal para octal
PRINT OCT$(10) 
12
Ok
  Esse sistema não é utilizado na programação Assembly.


  Sistema hexadecimal

  O sistema hexadecimal (no MSX é representado pelo prefixo &H) possui 16 algarismos. Devido ao fato dos caracteres numéricos serem baseados no sistema decimal, ficam faltando 6 algarismos, que serão representados pelas 6 primeiras letras do alfabeto. Assim, temos os seguintes algarismos: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F.
  A seqüencia crescente em hexadecimal fica: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1A, 1B, 1C, 1D, 1E, 1F, 20, 21, ...


  Quem já digitou programas em Assembly de revistas sobre MSX, percebeu-se de códigos como CD, C9, AB, etc. Agora você já sabe o que eles significam. São códigos de máquina representados no sistema de numeração hexadecimal.

  A conversão entre os sistemas hexadecimal e decimal podem ser feitas usando calculadoras ou o próprio Basic do MSX. Veja como:
' Hexadecimal para decimal
PRINT &HF 
 15
Ok

' Decimal para hexadecimal
PRINT HEX$(15) 
F
Ok
  Em suma, temos:
  Vejamos uma tabela de equivalência numérica entre os sistemas:
 
Decimal
Binário
Octal
Hexadecimal
0 0 0 0
1 1 1 1
2 10 2 2
3 11 3 3
4 100 4 4
5 101 5 5
6 110 6 6
7 111 7 7
8 1000 10 8
9 1001 11 9
10 1010 12 A
11 1011 13 B
12 1100 14 C
13 1101 15 D
14 1110 16 E
15 1111 17 F
16 10000 20 10
17 10001 21 11
18 10010 22 12
19 10011 23 13

  O computador somente entende valores binários. Nós humanos, compreendemos melhor o sistema decimal. Dessa forma, os sistemas de numeração apenas mudam a representação de um mesmo valor, tornando-o mais compreensível tanto para um, como para o outro. Por exemplo, enquanto o computador entende o valor binário 1101, nós o convertemos para o seu correspondente em decimal e obtemos o valor 13, que é mais compreensível para nós. Entretanto, o valor é o mesmo em ambos os sistemas de numeração.

  A nível de programação Assembly, não é necessário saber como converter entre os sistemas de numeração. Apenas, entender o que eles significam e como funcionam.


  2- Operações lógicas

  As operações lógicas tem como por objetivo testar uma ou mais entradas e produzir uma única saída. As operações lógicas mais usuais utilizam-se de duas entradas apenas.
  Os principais operadores lógicos são os seguintes:
  O operador AND

  O operador AND (E, em português) tem como objetivo emitir uma saída verdadeira (valor 1), se e somente se todas as entradas forem verdadeiras.

  Ex: Um jogo de futebol só pode ter início, quando o juiz e os dois assistentes estiverem presentes. Assim, temos:

  SE (juiz_presente E assistente_1_presente E assistente_2_presente) ENTÃO jogo_começa

  Se o valor de pelo menos um deles for falso (valor 0), o jogo não pode começar.


  O operador OR

  O operador OR (OU, em português) tem como objetivo emitir uma saída verdadeira (valor 1), se pelo menos uma das entradas for verdadeira.

  Ex: Uma escola possui 3 técnicos em informática. Se pelo menos um deles estiver presente, o computador poderá ser consertado:

  SE (tecnico_1_presente OU tecnico_2_presente OU tecnico_3_presente) ENTÃO conserta_computador

  Basta a presença de um dos três técnicos para consertar o computador.


  O operador NOT

  O operador NOT (NÃO, em português) tem como objetivo inverter o valor de uma entrada. Se era falsa, torna-se verdadeira. Se era verdadeira, torna-se falsa.

  Exs:
  Belo → NÃO belo
  Alto → NÃO alto.


  O operador XOR

  O operador XOR (OU Exclusivo, em português) tem como objetivo emitir uma saída verdadeira (valor 1), quando duas entradas são diferentes.

  Ex: Diálogo da mãe com o filho: "escolhe, ou o carrinho ou a bicicleta. Escolhe só um!"
  No caso, não é permitido deixar de escolher algo, bem como escolher as duas coisas.


  Tabelas-verdade para os operadores

  Uma tabela verdade é valoração das saídas para todas as combinações possíveis de entrada. Assim, temos:
NOT
Entrada Saída
A S
0 1
1 0
AND
Entradas Saída
A B S
0 0 0
0 1 0
1 0 0
1 1 1
OR
Entradas Saída
A B S
0 0 0
0 1 1
1 0 1
1 1 1
XOR
Entradas Saída
A B S
0 0 0
0 1 1
1 0 1
1 1 0
  Obs: O valor 0 corresponde ao falso, enquanto que o valor 1 corresponde ao verdadeiro.

  Quando a operação lógica possui mais de duas entradas, aplica-se a operação em cascata. Por exemplo:
 A AND B AND C = S
  Faz-se primeiro o par A and B, obtendo-se a saída parcial S1. Depois, combina-se essa saída com a próxima entrada C, fazendo-se S1 and C, finalmente obtendo-se S.
 Passo 0: A AND B AND C = S
 Passo 1: A AND B = S1
 Passo 2: S1 AND C = S
  Como testar as operações lógicas em Basic:
PRINT 0 AND 1 
 0

PRINT 1 OR 0 
 1

PRINT 1 XOR 0 
 1

  No arquivo do curso, há dois programas em Basic para brincar com as portas lógicas: PORTAE.BAS e PORTAOU.BAS.


  2.1- Operações lógicas com números

  As operações lógicas envolvendo números em qualquer sistema de numeração será feita bit a bit, ou seja, no sistema binário. Assim, devemos:
  1. Converter os números de um sistema qualquer para o sistema binário.
  2. Realizar a operação lógica.
  3. Converter o resultado final para o sistema original.
  Exemplo:
 14 AND 4

  Passo 1: converter os números para binário.
 14 = &B1110
 4  = &B100

  Passo 2: realizar a operação lógica, bit a bit, ou seja, casa decimal a casa decimal, assim como nas operações de soma e subtração.
    1 1 1 0
 E  0 1 0 0   0 E 0 = 0
 ----------
          0

    1 1 1 0
 E  0 1 0 0   1 E 0 = 0
 ----------
        0 0

    1 1 1 0
 E  0 1 0 0   1 E 1 = 1
 ----------
      1 0 0

    1 1 1 0
 E  0 1 0 0   1 E 0 = 0
 ----------
    0 1 0 0

  Passo 3: converter de volta para o sistema original.
 &B0100 = 4

  Conferindo no Basic:
PRINT 14 AND 4 
 4
Ok 


  3- Testes lógicos

  No Basic, os operadores de teste lógicos são os seguintes:   Um teste lógico envolve um ou mais operandos, que resultam em uma única saída, valorada em verdadeiro ou falso. Exemplos:
 5 > 6   -> falso
 4 <= 5  -> verdadeiro
 3 = 3   -> verdadeiro

  Realizamos um teste em Basic através da cláusula IF, que pode tomar dois caminhos dependendo da valoração verdadeira ou falsa. Exemplo:
IF 5>6 THEN PRINT"Verdadeiro" ELSE PRINT"Falso" 
Falso

  Em Assembly, não há a clausula IF e tampouco os operadores apresentados. O teste lógico é feito através de uma comparação entre o registrador A e outro registrador de 8 bits, através da instrução CP. Essa operação faz a subtração de A pelo registrador sem acumular o resultado, mas setando os flags.
  Assim, teremos as seguintes situações para CP:   Exemplo:
 LD A,5	   ; A=5
 LD B,6	   ; B=6
 CP B      ; Compara A com B, fazendo A-B
  Nesse caso, o resultado da conta é negativo e constata-se que A é menor que B.


  3.1 - Equivalências lógicas

  Equivalências envolvendo maior igual e menor e igual:
 Entrada  Saída
 A >= B   1 ELSE 0
 fica:
 A < B    0 ELSE 1

 Entrada  Saída
 A <= B   1 ELSE 0
 fica:
 A > B    0 ELSE 1

  Exemplo:
 IF IDADE >= 18 THEN PRINT"Pode dirigir" ELSE PRINT"Não pode dirigir"
  Equivale a:
 IF IDADE < 18 THEN PRINT"Não pode dirigir" ELSE PRINT"Pode dirigir"

  Quando realizamos a negação da entrada, negamos a saída também, que resulta na inversão das situações.

  Equivalências envolvendo os operadores E e OU:
 a) NOT (A AND B) = NOT(A) OR NOT(B)
 b) NOT (A OR B) = NOT(A) AND NOT(B)

  Exemplo:
 IF IDADE >= 20 AND IDADE <= 30 THEN PRINT"Apto" ELSE PRINT"Não apto"
  Equivale a:
 IF IDADE < 20 OR IDADE > 30 THEN PRINT"Não apto" ELSE PRINT"Apto"

  Graficamente:
 0        20        30
 +---------+---------+-----------+
  Não apto |  Apto   | Não apto


  4 - Álgebra com números de 8 bits

  Antes de começarmos, vejamos alguns conceitos envolvendo números binários, a respeito do comprimento deles.

  Conceitos:
  Os números são lidos da direita para a esquerda. Sendo assim, para um número de 4 bits, tem-se:
 bit 3 | bit 2 | bit 1 | bit 0
 ------+-------+-------+------
   1   |   0   |   1   |   0
  O bit 3 é a casa de maior valor, enquanto o bit 0 é a casa de menor valor.

  Para um número de 8 bits, tem-se:
 bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 |
 ------+-------+-------+-------+-------+-------+-------+-------+
   1   |   0   |   0   |   1   |   0   |   0   |   0   |   0   |

  O bit mais significativo ou MSB é a casa mais alta do número binário. Para um número de 4 bits, é o bit 3. Para um número de 8 bits, é o bit 7.
  O bit menos significativo ou LSB é a casa mais baixa e sempre será o bit 0.

  Operações aritméticas

  O processador Z80 sempre realiza operações aritméticas em cima de números de 8 ou 16 bits. Elas são de 8 bits, se o registrador envolvido for um registrador de 8 bits, como por exemplo, os registradores A, B, C, D, E. Elas são de 16 bits, se envolverem registradores de 16 bits como BC, DE, HL etc.

  Um registrador de 8 bits será sempre representado por 8 bits, independente do valor dele. O valor dele varia de 0 a 255.
  Exemplos:
Valor em decimal: 1
Valor 8 bits em binário: 00000001

Valor em decimal: 254
Valor 8 bits em binário: 11111110

  Conforme já visto, as operações aritméticas começam pela casa mais à direita e caminham para as casas mais à esquerda.

  Em Assembly, o registrador F possui dois flags (sinais, semáforos) que sinalizam um "estouro": um chamado de Half-Carry, que sinaliza um estouro localizado no bit 3 (nibble), e outro chamado de Carry, que sinaliza estouro localizado no bit 7 (byte).

  Exemplo de estouro do "nibble":
                           1
 bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 |
 ------+-------+-------+-------+-------+-------+-------+-------+
   0   |   0   |   0   |   0   |   1   |   1   |   1   |   1   |
+  0   |   0   |   0   |   0   |   0   |   0   |   0   |   1   |
------------------------------------------------------------------
   0       0       0       1       0       0       0       0
  No exemplo acima, o bit 3 foi estourado. Assim, o flag de Half-Carry (denominado pela letra H) seria setado (valor igual a 1).

  Exemplo de estouro do "byte":
1
 bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 |
 ------+-------+-------+-------+-------+-------+-------+-------+
   1   |   1   |   1   |   1   |   1   |   1   |   1   |   1   |
+  0   |   0   |   0   |   0   |   0   |   0   |   0   |   1   |
------------------------------------------------------------------
   0       0       0       0       0       0       0       0
  Agora o bit 7 (além do 3) também foi "estourado". Assim, o flag de Carry (C) é setado. Observe que nesse caso, o número binário voltou a zero, pois o tamanho máximo do número binário é de 8 bits.

  Na subtração, os flags H e C são setados quando há empréstimos e a última casa não consegue "pagar" tudo.

  Exemplo de estouro do "nibble" para subtração:
                           1
 bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 |
 ------+-------+-------+-------+-------+-------+-------+-------+
   0   |   0   |   0   |   1   |   0   |   0   |   0   |   0   |
-  0   |   0   |   0   |   0   |   0   |   0   |   0   |   1   |
------------------------------------------------------------------
   0       0       0       0       1       1       1       1
  O bit 3 não conseguiu "zerar" o empréstimo.

  Exemplo de estouro do "byte" para subtração:
                           1
 bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 |
 ------+-------+-------+-------+-------+-------+-------+-------+
   0   |   0   |   0   |   0   |   0   |   0   |   0   |   0   |
-  0   |   0   |   0   |   0   |   0   |   0   |   0   |   1   |
------------------------------------------------------------------
   1       1       1       1       1       1       1       1
  O bit 7 não conseguiu "zerar" o empréstimo (além do bit 3 - H e C setados).


  5- Números binários positivos e negativos


  5.1- Complemento

  O complemento de um número é a inversão de todos os bits desse número. Corresponde ao operador lógico bit a bit NOT aplicado a um número. Exemplo:
        NOT
 1100  ---->  0011

  Devemos lembrar que o MSX representa seus números em 8 ou 16 bits. Assim, a operação anterior ficaria em 8 bits:
            NOT
 00001100  ---->  11110011

  E em 16 bits:
                    NOT
 0000000000001100  ---->  1111111111110011


  5.2- Números não sinalizados

  Os números inteiros não sinalizados utilizam toda a faixa de valores correspondente ao tamanho do número (8, 16, 32 etc bits) para representar números inteiros positivos. Assim, temos:
  8 bits - 0 a 255
 16 bits - 0 a 65535
 32 bits - 0 a 4294967295
 ...
  Os valores variam de 0 a 2n-1, onde n é o número de bits utilizado para representar um número. Por exemplo, um nibble representa números não sinalizados de 0 a 24-1 = 15.


  5.3- Números sinalizados

  No sistema decimal, representamos um número negativo com o sinal "-". E como funciona no sistema binário?
  No sistema binário, utilizamos o bit mais significativo (o bit mais à esquerda) para indicar o sinal. Dessa forma:   Exemplos (número de 8 bits):
 01010101 - Positivo
 11111110 - Negativo
  Assim, um número "perde" sempre um bit para a sua representação. No caso de 8 bits, temos 7 bits para representar números positivos e 7 bits para representar números negativos. Dessa forma, representamos:
  E para 16 bits, temos:   Obs: para os números negativos, não existe o "-0". Por isso, ele começa do -1 e vai até -128 ou -32768.

  Conforme visto, o valor de um número sinalizado é representado pelos outros bits restantes ao bit de sinal. Por exemplo:
 01010101 - Positivo
 11111110 - Negativo
  O sinal está destacado em verde e o número em amarelo.

  Para números positivos, o valor é obtido diretamente do número binário. No exemplo anterior:
 1010101 = 85
  Assim, o número sinalizado &B01010101 corresponde ao valor decimal +85.

  Já o número negativo, devemos obter o complemento do número e depois somar mais um, pois o número negativo começa de -1 e não de 0.
  O exemplo a seguir parte de um número binário sinalizado, de forma a obter o correspondente decimal sinalizado:
 Número negativo:
 11111110

 Obtemos somente o número:
 1 1111110
 1111110

 Complemento:
 0000001

 Mais um:
 0000010

 Valor em decimal: 2
 Como é negativo, fica -2.

  Para obter o binário sinalizado, a partir do decimal sinalizado, fazemos:
  Valor decimal: -2

  Valor binário (sem sinal):
  0000010

  Complemento:
  1111101

  Soma mais um:
  1111110

  Adiciona sinal:
  11111110

  Faixa de valores de números sinalizados de 8 bits:
 Decimal | Binário
 --------+---------
 +127    | 01111111
 +126    | 01111110
  ...    | ...
    3    | 00000011
    2    | 00000010
    1    | 00000001
    0    | 00000000
   -1    | 11111111
   -2    | 11111110
   -3    | 11111101
  ...    | ...
 -127    | 10000001
 -128    | 10000000

  No capítulo 6, veremos que há duas instruções em Assembly que fazem essas conversões para nós. A instrução CPL calcula o complemento de um número, enquanto que a instrução NEG calcula o valor negativo correspondente a um número positivo ou o valor positivo correspondente a um número negativo.

  Por exemplo, para o CPL:
 LD A,1
 CPL
  A operação realizada foi:
  A = 00000001
  CPL
  A = 11111110

  Já para NEG:
 LD A,1
 NEG
  A operação realizada foi:
  A = 00000001 (+1)
  NEG
  A = 11111111 (-1)

  Mas vale também:
 LD A,&HFF
 NEG
  A operação realizada foi:
  A = 11111111 (-1)
  NEG
  A = 00000001 (+1)


<< Anterior Assembly Próxima >>