Curso de Assembly
Assembly Z80


  Assim como o programador da linguagem Basic necessita consultar o manual de instruções da linguagem para descobrir quais as instruções mais apropriadas para resolver um determinado problema, no Assembly é a mesma coisa.
  Por exemplo, suponhamos que um programador Basic deseje somar dois números e imprimir o valor na tela. Ao consultar o manual, constata que a instrução "PRINT" imprime o resultado de uma expressão aritmética. Assim, digita na tela: PRINT 4+2, tecla enter e obtém o resultado.
  Através do conjunto de instruções do Z80, o programador de Assembly também pode localizar as instruções que permitam realizar uma soma entre dois números.
  Ao consultarmos as instruções em Assembly, constatamos que não há qualquer uma que some dois números diretamente. Somente a partir do uso de registradores.
  Para realizar uma soma, encontramos a instrução:
 ADD A,s
  Essa instrução realiza uma soma entre o registrador A com qualquer registrador, um valor direto ou um conteúdo de memória apontado pelo registrador HL, IX ou IY.

  O conjunto de instruções Z80

  Vamos entender como funciona a tabela de instruções do Z80. Por exemplo, a instrução anterior ADD A,s:
 Mnemônico | SZHPNC | Tipo        | Descrição      | Notas
 ----------+--------+-------------+----------------+---------
 ADD A,s   | ***V0* | Aritmética  | Add            | A=A+s   
  Ao observarmos a coluna "Notas", relativa a essa instrução, há uma expressão que indica o que acontece quando ela é executada:
 A = A + s
  O registrador A (acumulador), recebe a soma entre o próprio registrador A e uma expressão s, que significa:
 s - Qualquer registrador r, valor n, [HL] ou [xx+d]
 xx - Registrador de índice: IX ou IY 
  Onde:   Exemplos de instruções válidas para ADD A,s:
 ADD A,B
 ADD A,15
 ADD A,(HL)
 ADD A,(IX+4)
  A coluna "SZHPNC" indica quais são os flags afetados pela instrução. Símbolos:   Agora sabemos como fazer uma soma em Assembly. Mas antes de realizá-la, precisamos alterar o valor do registrador A para fazer uma conta.
  A instrução LD dst,src faz transferência de dados.
 Mnemônico  | SZHPNC | Tipo          | Descrição      | Notas
 -----------+--------+---------------+----------------+---------
 LD dst,src | ------ | Transferência | Load           | dst=src 
  Onde:
 src - Fonte: s, ss, [BC], [DE], [HL], nn, [nn]
 dst - Destino: s, ss, [BC], [DE], [HL], [nn]
 s - Qualquer registrador r, valor n, [HL] ou [xx+d]  
 ss - Par de registradores: BC, DE, HL ou SP
 nn - Expressão de dois bytes (0 a 65535)

  Finalmente podemos criar um programa em Assembly que soma dois números:
  LD A,4
  ADD A,2

  Antes de começarmos, aqui vai uma dica importantíssima: cuidado com o uso da memória !!
  Uma vez que o programa principal compartilha a mesma memória física que dados, pilhas, etc, é necessário definir bem o local de cada um desses. Assim, reserve uma área da memória para o programa e outra para os dados e pilhas. Se você acidentalmente alterar o código do programa, seja pela pilha ou alteração de dados, o programa perderá o controle.

  A seguir, as instruções do Z80 serão divididas em 5 tipos de operações:   Assim o objetivo deste capítulo é apresentar as principais instruções do Z80, descrevendo-as e exemplificando, bem como ilustrar o que acontece após a execução de cada instrução apresentada.



  1- Instruções de transferência de dados

  São instruções que tem por objetivo realizar transferência de dados entre registradores, memória e dado direto (valor fornecido).

  São elas: LD, LDI, LDIR, LDD, LDDR, EX e EXX.

  Só podemos transferir dados:   Jamais podemos transferir:
  1.1. LD destino,origem

  Copia o conteúdo de origem para destino. Se houver parêntesis ( ), indica o conteúdo da memória apontado pelo endereço entre parêntesis.

  Exemplos:

  LD A,B
  Faz A=B.
  A é alterado e B continua com o mesmo valor.
  Vejamos o estado dos registradores antes e depois da instrução:

         A   B   C   D   E   H   L   SZ H PNC
 Antes:  00  01  02  00  00  00  00  00 0 000
 Depois: 01  01  02  00  00  00  00  00 0 000

  LD B,A
  Faz B=A.
  B é alterado e A continua com o mesmo valor.
  Vejamos o estado dos registradores antes e depois da instrução:
         A   B   C   D   E   H   L   SZ H PNC
 Antes:  0F  01  CA  00  00  00  00  00 0 000
 Depois: 0F  0F  CA  00  00  00  00  00 0 000

  LD (1000H),A
  Copia o conteúdo de um registrador de 8 bits (registrador A) para o endereço de memória indicado nos parêntesis.
  Vejamos o estado dos registradores e memória antes e depois da instrução:
                Registradores                     End. Memória
         A   B   C   D   E   H   L   SZ H PNC     0FFF  1000  1001
 Antes:  0F  01  CA  00  00  00  00  00 0 000     45    CD    00
 Depois: 0F  01  CA  00  00  00  00  00 0 000     45    0F    00

  LD (1000H),HL
  Copia o conteúdo de um registrador de 16 bits (registrador HL) para o endereço de memória indicado nos parêntesis.
  Vejamos o estado dos registradores e memória antes e depois da instrução:
                Registradores                     End. Memória
         A   B   C   D   E   H   L   SZ H PNC     0FFF  1000  1001
 Antes:  0F  01  CA  00  00  BB  DD  00 0 000     45    CD    00
 Depois: 0F  01  CA  00  00  BB  DD  00 0 000     45    DD    BB
  Nota: Observe que o número de 16 bits é invertido na memória. Assim, concluímos que:
 (end)   ← L
 (end+1) ← H

  LD HL,(1000H)
  Copia o conteúdo de memória indicado nos parêntesis para um registrador de 16 bits (registrador HL).
  Vejamos o estado dos registradores e memória antes e depois da instrução:
                Registradores                     End. Memória
         A   B   C   D   E   H   L   SZ H PNC     0FFF  1000  1001
 Antes:  0F  01  CA  00  00  BB  DD  00 0 000     45    CD    00
 Depois: 0F  01  CA  00  00  00  CD  00 0 000     45    CD    00
  Igualmente:
 L ← (end)
 H ← (end+1)

  LD HL,1234H
  Carrega numero direto de 16 bits para o registrador HL.
  Vejamos o estado dos registradores e memória antes e depois da instrução:
                Registradores                     End. Memória
         A   B   C   D   E   H   L   SZ H PNC     0FFF  1000  1001
 Antes:  0F  01  CA  00  00  BB  DD  00 0 000     45    CD    00
 Depois: 0F  01  CA  00  00  12  34  00 0 000     45    CD    00
  Nota: Diferentemente da transferência de registrador de 16 bits para a memória, onde há inversão, a transferência de um valor imediato de 16 bits para o registrador NÃO causa inversão.


  1.2. LDD, LDI, LDIR, LDD e LDDR

  Instruções para cópia de bloco de dados de uma parte da memória para outra.

  As instruções utilizam os registradores DE, HL e BC, onde:
 

  As instruções LDD e LDI somente transferem um byte:
 LDD (decrementa):
   (DE)=(HL)
   HL=HL-1
   DE=DE-1
   BC=BC-1

 LDI (incrementa):
   (DE)=(HL)
   HL=HL+1
   DE=DE+1
   BC=BC-1
  LDD vai no sentido negativo da memória, enquanto que LDI vai no sentido positivo da memória. Ambas instruções decrementam o registador BC.

  Exemplo:
 LD BC,0
 LD DE,&H1000
 LD HL,&H2000
 LDD
  Vejamos o estado dos registradores e memória antes e depois da instrução "LDD":
                Registradores                   End. Memória
         A   B   C   D   E   H   L   SZ H PNC   1000    2000
 Antes:  00  00  00  10  00  20  00  00 0 000   45      CD
 Depois: 00  FF  FF  0F  FF  1F  FF  00 0 000   CD      CD

  As instruções LDDR e LDIR transferem um bloco inteiro de memória, de tamanho BC.
  LDDR (decrementa em loop):
    Faz LDD até que BC=0

  LDDI (incrementa em loop):
    Faz LDI até que BC=0
  LDDR vai no sentido negativo da memória, enquanto que LDIR vai no sentido positivo da memória.

  Exemplo:
 LD BC,10
 LD DE,&H1000
 LD HL,&H2000
 LDDR
  Transfere o conteúdo de memória de 2000H-1FF7H para 1000H-0FF7H. Se fosse LDDI, seria de 2000H-2009H para 1000H-1009H.


  1.3. EX R1,R1 e EXX

  A instrução EX R1,R2 troca o conteúdo entre os registadores R1 e R2. Exemplos:

  EX DE,HL

  Realiza uma troca entre o conteúdo dos registradores DE e HL.
         A   B   C   D   E   H   L   SZ H PNC
 Antes:  0F  01  CA  12  34  AB  CD  00 0 000
 Depois: 0F  01  CA  AB  CD  12  34  00 0 000

  EX AF,AF'

  Realiza uma troca entre o conteúdo dos registradores A com A' e F com F'.
         A   B   C   D   E   H   L   SZ H PNC
 Antes:  A4  00  00  00  00  00  00  00 0 000
         15  00  00  00  00  00  00  00 0 000
 Depois: 15  00  00  00  00  00  00  00 0 000
         A4  00  00  00  00  00  00  00 0 000

  EXX

  Realiza uma troca entre o conteúdo dos registradores BC com BC', DE com DE' e HL com HL'.
         A   B   C   D   E   H   L   SZ H PNC
 Antes:  00  11  11  22  22  33  33  00 0 000
         00  AA  AA  BB  BB  CC  CC  00 0 000
 Depois: 00  AA  AA  BB  BB  CC  CC  00 0 000
         00  11  11  22  22  33  33  00 0 000

  Obs: repetindo o comando EX ou EXX, os registradores retomam seus valores iniciais. Exemplo:
 EX AF,AF'
 EX AF,AF'

Passo 1 (EX AF,AF'):
          A
 Antes:   A4
          15
 Depois:  15
          A4

Passo 2 (EX AF,AF'):
          A
 Antes:   15
          A4
 Depois:  A4
          15
  Entretanto, se houver mudança do registrador entre as trocas, temos:
 EX AF,AF'
 LD A,&H67
 EX AF,AF'

Passo 1 (EX AF,AF'):
          A
 Antes:   A4
          15
 Depois:  15
          A4

Passo 2 (LD A,&H67):
          A
 Antes:   15
          A4
 Depois:  67
          A4

Passo 3 (EX AF,AF'):
          A
 Antes:   67
          A4
 Depois:  A4
          67



  2 - Instruções de desvio

  Serve para desviar a execução de um programa para um dado endereço de memória (qualquer área endereçável).

  As instruções são: CALL, RET, RETI, RETN, RST, JP, JR e DJNZ.

  O Basic é um linguagem sequencial, onde as instruções são dispostas em seqüência e executadas uma a uma, seguindo essa seqüência. Entretanto, há instruções em Basic para desviar a execução do programa para outros trechos, quando necessário.
  O Assembly também é uma linguagem onde a execução é sequencial, no qual as instruções são buscadas percorrendo-se a memória no sentido crescente. Assim como o Basic, instruções de desvios desviam a execução para outros trechos de memória.
  O programa em Basic a seguir ilustra a forma sequencial de execução das instruções, bem como a presença de um desvio.
  10 PRINT"Imprimo essa linha"
  20 GOTO 40
  30 PRINT"Mas não imprimo essa"
  40 PRINT"Eu saltei para cá"

  Os tipos de desvios são:
  Um salto condicional será baseado no estado de um flag do registrador F. As condições são:   Obs: verificar se um número negativo é verificar se o bit 7 dele é igual a 1. Se esse bit for igual a 0, o número é positivo. Isto é a convenção para números sinalizados, que, em 8 bits, varia de -128 a 127. É uma interpretação diferente do mesmo dado. Quando interpretamos um número de 8 bits como não-sinalizado, os valores variam de 0 a 255.

  Essas condições somente serão atendidas corretamente, se o flag correspondente à condição for ativado por uma instrução anterior.
  Veja os exemplos a seguir:
 LD A,1
 JP NZ,&HC100
  Pode não funcionar corretamente, pois os flags não foram afetados pela instrução LD A,1.

 LD A,&HFF
 INC A
 JP Z,&HC100
  Agora irá funcionar corretamente, pois os flags foram afetados pela instrução INC A.


  2.1 - CALL, RET, RETI e RETN

  Faz um desvio (CALL) com retorno (RET, RETI e RETN), semelhante ao GOSUB-RETURN do Basic.

 

  No esquema acima, a seta verde indica onde o programa principal começa.
  A instrução CALL desvia a execução do programa para outro endereço da memória, onde está localizada uma sub-rotina.
  Ao encontrar a instrução RET na sub-rotina, a execução retorna para a instrução imediatamente após o CALL.

  Sintaxes para a instrução CALL:
 CALL nn     ; Chama endereço de memória nn
 CALL cc,nn  ; Chama endereço nn, dada uma condição cc

  Ações do CALL:
 (SP) ← PC (da próxima instrução)
 SP ← SP-2
 PC ← nn

  Exemplo:

  CALL &H4D

  Desvia o programa para o endereço 004DH. Antes, salva o endereço da próxima instrução na pilha.
         PC    SP      Pilha:
 Antes:  C000  FD50    0000
                       0000
 Depois: 004D  FD4E    C003
                       0000

  Exemplo com a instrução CALL cc,nn:

  CALL NZ,&HC100

  Desvia o programa para o endereço 004DH, se o flag Z=0.
         SZ H PNC    PC 
 Antes:  00 0 000    C000
 Depois  00 0 000    C100

  Sintaxes para a instrução RET:
 RET     ; Retorna
 RET cc  ; Retorna, dada uma condição
 RETI    ; Retorna de uma interrupção
 RETN    ; Retorna de uma interrupção não-mascarada

  Ações do RET:
 PC ← (SP)
 SP ← SP+2

  Exemplos:

  RET

  Retorna para o endereço contido na pilha.

  RET M

  Retorna para o endereço contido na pilha, se o flag S=1 (número negativo).

  Importante: visto que a instrução RET busca o endereço deixado pelo CALL na pilha, é importante não "bagunçar" a pilha, de modo a perder tanto o valor do endereço de retorno, como o apontamento de SP para o endereço que contém esse valor.


  2.2 - JP e JR

  JP e JR são desvios sem retorno para um endereço de memória.

  Diferenças entre os saltos:   Assim:
 JP nn    ; PC = endereço
 JR e     ; PC = PC + 2 + offset

  Exemplos:

  JP &HC100

  Salta direto para o endereço de memória C100H.
 C000   C3 00 C1   JP &HC100  x--+
 C003   C9         RET           |
 ...                             |
 C100   <------------------------+

  JR 0

  Primeiro, PC passa para a próxima instrução. Depois, desloca "e" posições para frente ou para trás, dependendo do sinal de "e". Nessa caso, o deslocamento é 0.
 C000   18 00      JR 0
 C002   C9         RET        x

  JR &H10

  Primeiro, PC passa para a próxima instrução. Depois, desloca "e" posições para frente ou para trás, dependendo do sinal de "e". Nessa caso, o deslocamento é +10H.
 C000   18 10      JR 0
 C002   C9         RET        x--+
 ...                             |
 C012  <-------------------------+

  JR -&H10

  Primeiro, PC passa para a próxima instrução. Depois, desloca "e" posições para frente ou para trás, dependendo do sinal de "e". Nessa caso, o deslocamento é -10H.
 BFF2  <-------------------------+
 ...                             |
 C000   18 F0      JR 0          |
 C002   C9         RET        x--+

  O salto JR é um salto curto, para distâncias de até 128 bytes. Em relação ao JP, ele possui um byte a menos no código em LM. Para um programa cheio de desvios, isso representa uma boa economia de memória.

  A instrução JR necessita do cálculo do offset "e". Para evitar de ter que fazer esse cálculo, podemos substituir o valor de "e" por uma etiqueta. Assim, o Assemblador calcula o valor de "e" para a gente. Exemplo:
 10 ORG &HC000
 20 LD A,0
 30 LOOP: ADD A,B
 40 JR LOOP

  Após assemblar:
C000  3E 00     LD A,0
C002  80        LOOP: ADD A,B
C003  18 FD     JR LOOP
  O valor FDH é igual a -3. Visto que a posição de referência para o offset é C005H, ao subtrairmos 3 de C005H chegamos a C002H.

  A instrução "JP nn" ou "JR nn" realiza um salto incondicional, uma vez que não depende de qualquer condição para ocorrer.
  Já a instrução "JP cc,nn" ou "JR cc,nn" realiza um salto condicional, e irá ocorrer se somente se a condição cc for atendida.

  Exemplo:

  JP C,0500H

  Irá pular para o endereço &H0500, se o flag "carry" estiver setado (valor igual a 1). Caso C=0 (não setado), o programa segue para a próxima instrução.
  Exemplo de como fazer um laço de repetição usando um salto:
  10 ORG &HC000
  20 LD A,4
  30 LACO: DEC A  ; A=A-1
  40 JR NZ,LACO
  50 RET

  Outros saltos possíveis (somente JP e incondicional):
 JP (HL)  ; PC ← HL
 JP (IX)  ; PC ← IX
 JP (IY)  ; PC ← IY

  Exemplo:

  LD HL,&HD000
  JP (HL)

  Salta para a posição D000H.


  2.3 - DJNZ

  A instrução DJNZ é um salto condicional, onde utiliza o registrador B como variável de controle para laços de repetição. Enquanto B for diferente de 0, a instrução salta para o endereço indicado e subtrai o valor do registrador B.

  Sintaxe:
 DJNZ e
  Onde "e" é um endereço de 8 bits sinalizado (como na instrução JR).

  O que a instrução DJNZ faz:
 B=B-1
 Se B<>0 então PC ← e

  O exemplo a seguir mostra como criar um laço de repetição utilizando a instrução DJNZ. Em vez de colocar o offset na instrução, foi utilizada a etiqueta "LOOP".
 Endereço  Assembly  Linha  Mnemônico
 C000                10           ORG &HC000
 C000      060A      20           LD B,10      ; Número de repetições
 C002      AF        30           XOR A        ; Faz A=0
 C003      3C        40     LOOP: INC A        ; Faz A=A+1
 C004      10FD      50           DJNZ LOOP    ; Se B<>0 então vá para LOOP
 C006      C9        60           RET          ; Retorna

  Rode o programa acima no simulador do RSC II e observe os registradores A e B durante o funcionamento do programa.
 SI &HC000
 Modo (H/D): H

  Nota importante para o DJNZ: quando B=1 e a instrução for executada, temos:
 Primeiro, faz B=B-1. Logo B=0.
 Testa se B=0. Como é 0, termina.
  Assim:
 10       ORG &HC000
 20       LD B,1
 30 LOOP: DJNZ LOOP
  A execução do programa irá passar apenas uma vez pela linha 30.

  Obs: para criar um loop com 256 repetições, coloque o valor 0 no registrador B. Exemplo:
 10 LD B,0
 20 LOOP: DJNZ LOOP ; Repete 256x


  2.4 - RST p

  Dá um Reset, fazendo PC=0 + p*8.

  O valor "p" é multiplo de 8 e soma com o endereço 0. Por exemplo, RST 2 faz PC=16 pu PC=0010H.




  3- Intruções aritméticas

  Realizam operações aritméticas básicas como soma, subtração, incremento e decremento.

  Instruções: ADD, ADC, SUB, SBC, INC, DEC e DAA.

  Obs: no caso de multiplicação e divisão, deve-se aplicar as propriedades matemáticas de soma e subtração para realizá-las. Veja a seção de exercícios.


  3.1 - ADD e SUB

  Realiza uma soma ou subtração entre registradores, ou entre um registrador e um valor direto ou apontado por um endereço de memória.

  Operações possíveis para a soma:

 ADD A,s    ; A=A+s
 ADD HL,ss  ; HL=HL+ss
 ADD IX,pp  ; IX=IX+pp
 ADD IY,rr  ; IY=IY+rr
  Onde:
 s - Qualquer registrador r, valor n, [HL] ou [xx+d]
 ss - Par de registradores: BC, DE, HL ou SP
 pp - Par de registradores: BC, DE, IX ou SP 
 rr - Par de registradores: BC, DE, IY ou SP

  Exemplos:

  ADD A,2
  Soma o valor do registrador A com o valor direto 2.
         A   B   C   D   E   H   L   SZ H PNC
 Antes:  10  00  00  00  00  00  00  00 0 000
 Depois: 12  00  00  00  00  00  00  00 0 000

  ADD A,B
  Equivale a A=A+B.
         A   B   C   D   E   H   L   SZ H PNC
 Antes:  05  04  00  00  00  00  00  00 0 000
 Depois: 09  00  00  00  00  00  00  00 0 000

  ADD A,(HL)
  Equivale a A=A + "o conteúdo de memória apontado por HL".
                                       Memória 
         A   B   C   D   E   H   L     1000   
 Antes:  05  00  00  00  00  10  00    10
 Depois: 15  00  00  00  10  00  00    10

  ADD HL,BC
  Faz HL=HL+BC.
         A   B   C   D   E   H   L   SZ H PNC
 Antes:  00  02  34  00  00  10  00  00 0 000
 Depois: 09  00  00  00  00  12  34  00 0 000

  ADD IY,SP
  Faz IY=IY+SP.
         IX    IY    SP
 Antes:  0000  1000  E000
 Depois: 0000  F000  E000

  Cuidado com as operações envolvendo números de 8 bits e 16 bits, pois não é possível isso. Por exemplo:
 ADD A,HL  ; ERRO!!

  Operação possível para a subtração:
 SUB s     ; A=A-s

  Exemplos:

  SUB 8
  Faz a A=A-8.
         A   B   C   D   E   H   L   SZ H PNC
 Antes:  18  00  00  00  00  00  00  00 0 000
 Depois: 10  00  00  00  00  00  00  00 0 000

  SUB D
  Equivale a A=A-D.
         A   B   C   D   E   H   L   SZ H PNC
 Antes:  05  00  00  03  00  00  00  00 0 000
 Depois: 02  00  00  00  00  00  00  00 0 000

  Nota: podemos simular uma subtração através da instrução de soma ADD. Para isso, devemos considerar o subtraendo como um número sinalizado e calcular seu valor negativo através do complemento a dois.

  Exemplo: a operação 5-3.

  Para o valor negativo "-3", devemos calcular o complemento a dois de "3".
 Hexa  Binário
 03    00000011

Complemento (inverter bits):
 Hexa  Binário
 FC    11111100

Complemento a dois:
 Hexa  Binário
 FC    11111100 
+01    00000001
--------------- 
 FD    11111101

  Assim,
 LD A,5
 LD B,3
 SUB B
  Equivale a:
 LD A,5
 LD B,&HFD
 ADD A,B

  3.2 - ADC e SBC

  Realiza uma soma ou subtração entre registradores, ou entre um registrador e um valor direto ou apontado por um endereço de memória, agora levando em conta o flag de "carry".

  Operações possíveis para a soma e subtração:
 ADC A,s    ; A=A+s+CY
 ADC HL,ss  ; HL=HL+ss+CY
 SBC A,s    ; A=A-s-CY
 SBC HL,ss  ; HL=HL-ss-CY
  Onde CY é o valor do flag de carry.

  Exemplos:

  ADC A,B
  Soma os registradores A e B, levando em conta o flag de "carry".

         A   B   C   D   E   H   L   SZ H PNC
 Antes:  02  01  00  00  00  BB  DD  00 0 001
 Depois: 04  01  00  00  00  BB  DD  00 0 000
  Ele fez: A=A+B+CY. Como o flag de carry é igual a 1, em vez de dar 3, o resultado, deu 4.


  SBC A,E
  Subtrai o registrador A de E, levando em conta o flag de "carry".
         A   B   C   D   E   H   L   SZ H PNC
 Antes:  04  00  00  00  02  BB  DD  00 0 001
 Depois: 01  02  00  00  00  BB  DD  00 0 000
  Ele fez: A=A-E-CY. Como o flag de carry é igual a 1, em vez de dar 2, o resultado, deu 1.


  SBC HL,DE
  Subtrai o par de registradores HL de DE, levando em conta o flag de "carry". Uma subtração de 16 bits.
         A   B   C   D   E   H   L   SZ H PNC
 Antes:  04  00  00  05  10  15  10  00 0 000
 Depois: 04  00  00  05  10  10  00  00 0 000

  3.3 - INC e DEC

  Realiza operação de incremento ou decremento de um registrador ou conteúdo de memória apontado por HL, IX+d ou IY+d.

  O incremento de um valor é o aumento em uma unidade desse valor.
  Operações possíveis para o incremento:
 INC r      ; r=r+1
 INC (HL)   ; (HL)=(HL)+1
 INC xx     ; XI=XI+1 ou YI=YI+1
 INC (xx+d) ; (XI+d)=(XI+d)+1 ou (YI+d)=(YI+d)+1
 INC ss     ; ss=ss+1

  Exemplos:

  INC H
  Incrementa o registrador H em uma unidade, ou seja, faz H=H+1.
         A   B   C   D   E   H   L   SZ H PNC
 Antes:  00  00  00  00  00  A0  00  00 0 000
 Depois: 00  00  00  00  00  A1  00  00 0 000

  INC (HL)
  Incrementa o conteúdo de memória apontado por HL.
                                                Memória
         A   B   C   D   E   H   L   SZ H PNC   1000H
 Antes:  00  00  00  00  00  10  00  00 0 000    04
 Depois: 00  00  00  00  00  10  00  00 0 000    05

  O decremento de um valor é a diminuição em uma unidade desse valor.
  Operações possíveis para o decremento:
 DEC s      ; s=s-1
 DEC xx     ; XI=XI-1 ou YI=YI-1
 DEC ss     ; ss=ss-1

  Exemplo:

  DEC HL
  Decrementa o par de registradores HL em uma unidade, ou seja, faz HL=HL-1.
         A   B   C   D   E   H   L   SZ H PNC
 Antes:  00  00  00  00  00  10  00  00 0 000
 Depois: 00  00  00  00  00  0F  FF  00 0 000


  3.4 - DAA

  Faz o ajuste decimal no registrador A.

  O termo BCD (binary-coded decimal) significa um número decimal codificado por um número binário. Assim, cada dígito decimal é representado por 4 bits.
 Decimal  Binário     Decimal  Binário
    0       0000         5       0101
    1       0001         6       0110
    2       0010         7       0111
    3       0011         8       1000
    4       0100         9       1001

  Por exemplo, o número decimal 10 é codificado em binário como 0001 0000.
  Algo semelhante ao BCD é a representação de minutos e segundos, que variam de 0 a 59, por um número decimal de dois dígitos, que varia de 0 a 99. Quando o valor do minuto ou segundo ultrapassa o valor 59, ele volta a zero e a divisão de tempo imediatamente acima é incrementada. Por exemplo, 70 minutos é representado por 1 hora e 10 minutos.
  A codificação BCD geralmente é utilizada em dispositivos eletrônicos para displays de números.

  Veja um exemplo, a partir da soma entre dois valores em hexadecimal:
   08H 
 + 04H
 -----
   0CH
  Se considerarmos 08H e 04H como dois números BCD, o resultado da conta está errado. Assim, o ajuste DAA corrige o valor de 0CH para 12H.
  O que o DAA faz é somar o valor 6 a cada nibble (4 bits) do registrador A, se o valor do nibble for maior que 9.

  Exemplos:
 A=B1H                     |  A=0FH       
 DAA  -> B+6+CY 1+0 = 1 1  |  DAA   -> 0+0+CY F+6 = 1 5
 A=11H                     |  A=15H

  Exemplo em Assembly da soma entre 08 e 04, considerando-se os valores como BCD.
  10 ORG &HC000
  20 LD A,8
  30 ADD A,4
  40 DAA




  4- Pilhas, E/S e outros.

  São instruções para pilhas, entrada e saída de dados e outras operações.

  Instruções: PUSH, POP, DI, EI, IM, NOP, HALT, IN, INI, INIR, IND, INDR, OUT, OUTI, OTIR, OUTD e OTDR.


  4.1 - Pilhas: PUSH e POP

  A pilha é a disposição de dados na memória semelhante a uma pilha de livros, onde o último elemento inserido na pilha será o primeiro a ser retirado (LIFO - Last In, First Out).
  O objetivo da pilha é armazenar dados temporariamente para que possam ser resgatados no futuro. Um exemplo visto anteriormente, é o armazenamento do endereço da próxima instrução pela instrução CALL durante a chamada a uma sub-rotina. A rotina principal pára, o endereço é salvo na pilha, o programa desvia para a sub-rotina e esta quando acaba, a instrução RET lê o endereço armazenado na pilha e o programa retorna à rotina principal do ponto que parou.
  A pilha pode ser utilizada para salvar qualquer tipo de informação. Entretanto, observa-se que o acesso à memória principal é mais lento que o acesso aos registradores.
  O comando PUSH empilha, enquanto que o comando POP desempilha.

  Sintaxe para o PUSH e o POP:
 PUSH xx   ; (SP) ← xx / SP=SP-2
 PUSH qq   ; (SP) ← qq / SP=SP-2
 POP xx    ; xx ← (SP) / SP=SP+2
 POP qq    ; qq ← (SP) / SP=SP+2
  Onde:
 qq - Par de registradores: AF, BC, DE ou HL
 xx - Registrador de índice: IX ou IY 

  Exemplos:

  PUSH BC
  Coloca na pilha o valor contido no par de registradores BC, sem alterar o valor de BC.
                Registradores                           End. Memória
         A   B   C   D   E   H   L   SZ H PNC   SP      0FFF  1000  1001
 Antes:  0F  01  CA  00  00  00  00  00 0 000   1000    45    CD    00
 Depois: 0F  01  CA  00  00  00  00  00 0 000   0FFE    CA    01    00

  POP AF
  Retira da pilha um dado e o coloca nos par de registradores AF, sem alterar o conteúdo da memória.

  Ex: POP AF
                Registradores                           End. Memória
         A   B   C   D   E   H   L   SZ H PNC   SP      0FFF  1000  1001
 Antes:  0F  01  CA  00  00  BB  DD  00 0 000   0FFE    45    CD    00
 Depois: CD  01  CA  00  00  00  CD  01 0 101   1000    45    CD    00
  Obs: o valor passado para o registrador F foi 45H, que tem sua representação binária igual a 01000101. Essa configuração de bits é passada para os flags, conforme visto no exemplo acima.


  4.2 - Interrupções: DI, EI e IM

  As interrupções são sinais que chegam ao processador Z80, permitindo ele reagir a eventos mais eficientemente do que se fosse verificado o estado de cada dispositivo pela CPU. As interrupções permitem que eventos como alarmes, falhas, passagem de tempo e periféricos prontos ganhem a atenção imediata do computador.
  A interrupção do Z80 pode ser habilitada ou desabilitada. Uma interrupção que não pode ser desabilitada é chamada de interrupção não-mascarada e pode ser útil para avisos de falha no sistema, evento esse que deverá ter precedência sobre as outras atividades.
  Adaptado de [4]

  Comandos de interrupção:
 EI    ; Habilita interrupções
 DI    ; Desabilita interrupções
 IM n  ; Modo de interrupção (0,1,2)

  Exemplo de uso das instruções de interrupção:
     DI           ; Desabilita interrupções
     LD B,&H100   ; ****************************
     XOR A        ; * Esse trecho não pode ser *
 P1: ADD A,5      ; * interrompido             *
     DJZN         ; ****************************
     EI           ; Habilita interrupções

  4.3 - NOP e HALT

  A instrução NOP (no operation ou sem operação) equivale ao REM do Basic e não realiza qualquer atividade.
  A instrução HALT pára a CPU.


  4.4 - E/S de dados: IN e OUT

  Instruções que realizam a entrada e saída de dados através de portas, permitindo a comunicação entre o processador Z80 e outros dispositivos.

  Comandos de entrada de dados:
 IN A,(n)  ; A recebe dado da porta "n"
 IN r,(C)  ; r recebe dado da porta indicada por C
 IND       ; (HL) ← (C), HL=HL-1, B=B-1
 INDR      ; IND até B=0
 INI       ; (HL) ← (C), HL=HL+1, B=B-1
 INIR      ; INI até B=0

  Exemplos:

  IN A,(&HFF)
  Recebe um dado de 8 bits da porta FFH e armazena no registrador A.

  IN D,(C)
  Recebe um dado de 8 bits da porta indicada pelo registrador C e armazena no registrador D.

  INI
  Recebe um dado de 8 bits da porta indicada pelo registrador C e armazena na memória apontada pelo registrador (HL). Depois, incrementa HL e decrementa B.
                Registradores                    End. Memória
         A   B   C   D   E   H   L   SZ H PNC    0FFF  1000  1001
 Antes:  00  10  20  00  00  10  00  00 0 000    FF    FF    FF
 Depois: 00  0F  20  00  00  10  01  00 0 000    FF    25    FF
  Obs: supor que a porta 20 enviou o dado 25H.

  As instruções IND, INDR, INI e INIR servem para transferir blocos de informação externa para a memória principal. As instruções INDR e INIR fazem a transferência de um bloco de dados do tamanho indicado pelo registrador B de forma automática.

  Exemplo:
 LD B,100
 LD HL,&H1000
 LD C,10
 INIR
  Transfere 100 bytes da porta 10 para a memória, a partir da posição 1000H e para frente.

  Obs: o comando INI incrementa a posição de memória, enquanto que o comando IND decrementa a posição de memória.

  Comandos de saída de dados:
 OUT (n),A  ; A envia dado à porta "n"
 OUT (C),r  ; r envia dado à porta indicada por C
 OUTD       ; (C) ← (HL), HL=HL-1, B=B-1
 OTDR       ; OUTD até B=0
 OUTI       ; (C) ← (HL), HL=HL+1, B=B-1
 OTIR       ; OUTI até B=0

  Exemplos:

  OUT (15),A
  Envia o dado contido no registrador A para a porta 15.

  OTIR
  OTIR envia dados do conteúdo de memória apontado por HL para a porta indicada pelo registrador C. Depois, incrementa HL e decrementa B, até que B=0.
                Registradores                     End. Memória
         A   B   C   D   E   H   L   SZ H PNC     1000  1001  1002
 Antes:  00  03  10  00  00  10  00  00 0 000     21    22    23
 Depois: 00  02  10  00  00  10  01  00 0 000     21    22    23
 Depois: 00  01  10  00  00  10  02  00 0 000     21    22    23
 Depois: 00  00  10  00  00  10  03  00 0 000     21    22    23
  Ordem dos dados enviados para a porta 10H:
  21H, 22H e 23H.




  5- Instruções lógicas

  Realizam operações lógicas, alteração e deslocamento de bits.

  Instruções: AND, OR, XOR, CP, RLCA, RR, SLA, SRA, SRL, RLD, RRD, CPL, NEG, CCF, SCF, CPI, CPIR, CPD, CPDR, BIT, RLA, RRA, RLC, RL, RRC, SET e RES.


  5.1 - Operações lógicas: AND, OR, XOR, CPL e NEG

  Realizam operação lógica entre dois valores, bit a bit. Utilizam sempre o registrador A.

  Operações:
 AND s   ; A ← A AND s
 OR s    ; A ← A OR s
 XOR s   ; A ← A XOR s
 CP s    ; A-s, setando os flags
 CPL     ; A ← NOT A
 NEG     ; A ← -A
  Onde:
 s - Qualquer registrador r, valor n, [HL] ou [xx+d]

  Exemplos:

  AND B
  Faz um E lógico entre os bits correspondentes dos registradores A e B, retornando o resultado em A.
 A=14H, B=ACH

     00010100
 AND 10101100
 --------------
     00000100
  Situação dos registradores:
         A   B   C   D   E   H   L   SZ H PNC
 Antes:  10  AC  00  00  00  00  00  00 0 000
 Depois: 04  AC  00  00  00  00  00  00 1 000

  AND &H40
  Faz um E lógico entre os bits correspondentes do registrador A e o valor 40H, retornando o resultado em A.
 A=7FH, valor=40H

     01111111
 AND 01000000
 ------------
     01000000
  Situação dos registradores:
         A   B   C   D   E   H   L   SZ H PNC
 Antes:  7F  00  00  00  00  00  00  00 0 000
 Depois: 40  00  00  00  00  00  00  00 1 000

  OR C
  Faz um OU lógico entre os bits correspondentes dos registradores A e C, retornando o resultado em A.
 A=14H, C=ACH

    00010100
 OR 10101100
 -----------
    10111100
  Situação dos registradores:
         A   B   C   D   E   H   L   SZ H PNC
 Antes:  10  00  AC  00  00  00  00  00 0 000
 Depois: BC  00  00  00  00  00  00  10 0 000

  XOR D
  Faz um OU exclusivo entre os bits correspondentes dos registradores A e D, retornando o resultado em A.
 A=6FH, D=50H

     01101111
 XOR 01010000
 ------------
     00111111
  Situação dos registradores:
         A   B   C   D   E   H   L   SZ H PNC
 Antes:  6F  00  00  50  00  00  00  00 0 000
 Depois: 3F  00  00  00  00  00  00  00 0 100

  Obs: se aplicarmos o XOR em dois números iguais, o valor retornado é zero, pois não haverá bits diferentes entre os números. Por exemplo:
     10101010
 XOR 10101010
 ------------
     00000000
  Como a instrução XOR em Assembly é mais rápida que a instrução LD, muitos programadores utilizam esse recurso para zerar o registrador A, através da instrução "XOR A".

  CPL
  Inverte todos os bits do registrador A. Se A=11110000 ficará A=00001111.

  NEG
  Faz o complemento a dois de A, convertendo um valor de 8 bits sinalizado para seu correspondente em negativo. Por exemplo, se A=10, ficará A=-10.


  5.2 - Comparação: CP, CPI, CPIR, CPD e CPDR

  A instrução CP compara o valor de um registrador, valor direto ou de memória apontado por HL, IX+d ou IY+d com o registrador A, fazendo a operação A-s. A operação NÃO retorna o resultado, entretanto seta os flags. Vide capítulo 3.

  Exemplo:

  CP 10
  Compara 10 com o valor do registrador A, fazendo a conta A-10. Os flags afetados são sinalizados em F.
         A   B   C   D   E   H   L   SZ H PNC
 Antes:  09  00  00  00  00  00  00  00 0 000
 Depois: 09  00  00  00  00  00  00  10 1 011
  Obs: o resultado da conta dá FFH, que é igual a -1.

  Relembrando o capítulo 3, temos as seguintes situações para o CP:
  Vejamos agora como criar em Assembly o equivalente para alguns IFs do Basic:

  Exemplo 1:
 IF A >= B THEN rot1 ELSE rot2
  Em Assembly:
 CP B
 JP NC rot1  ; Se A>=B rot1
 rot2        ; Else rot2

  Exemplo 2:
 IF A < B THEN rot1 ELSE rot2
  Em Assembly:
 CP B
 JP C rot1   ; Se A<B rot1
 rot2        ; Else rot2
  Em ambos os casos, o flag de "carry" foi utilizado para verificar se houve estouro na conta. Quando "A-s" dá negativo, C=1.

  Exemplo 3:
 IF A = B THEN rot1 ELSE rot2
  Em Assembly:
 CP B
 JP Z rot1   ; Se A=B rot1
 rot2        ; Else rot2

  Exemplo 4:
 IF A <> B THEN rot1 ELSE rot2
  Em Assembly:
 CP B
 JP NZ rot1  ; Se A<>B rot1
 rot2        ; Else rot2

  Para as situações "A>B" e "A<=B", utilize as equivalências apresentadas no capítulo 3.

  As comparações automáticas comparam o registrador A com o conteúdo de memória apontado por HL.
  Comparações possíveis:
 CPD   ; A-(HL) / HL=HL-1 / BC=BC-1
 CPDR  ; CPD até A=(HL) ou BC=0
 CPI   ; A-(HL) / HL=HL+1 / BC=BC-1
 CPIR  ; CPI até A=(HL) ou BC=0

  Programa exemplo para buscar um valor em um trecho de memória:
 LD HL,&HD000  ; A partir de D000H
 LD BC,10      ; Em 10 posições para frente
 LD A,4        ; Busca o valor 04H
 CPIR
  Caso não exista qualquer valor 04H na memória de D000H a D009, teremos:
         A   B   C   D   E   H   L   SZ H PNC
 Antes:  04  00  0A  00  00  D0  00  00 0 000
 Depois: 04  00  00  00  00  D0  0A  10 1 010
  Como o flag de "carry" é 0, a busca não encontrou o valor 4.

  Caso exista o valor 04H na posição D003H:
         A   B   C   D   E   H   L   SZ H PNC
 Antes:  04  00  0A  00  00  D0  00  00 0 000
 Depois: 04  00  06  00  00  D0  04  01 0 110
  Como o flag de "carry" é 1, a busca encontrou o valor 4. O endereço de memória do valor encontrado é igual a "HL-1".

  Obs: em ambos os casos, a situação "Antes" é imediatamente antes da execução da instrução CPIR.


  5.3 - Operações em bits: BIT, CCF, SCF, RES e SET

  Operam em bits isolados de um registrador de 8 bits ou posição de memória apontada por HL, IX+d ou IY+d.

  Instruções:
 BIT b,m   ; Testa o bit "b" de m. Retorna em Z.
 CCF       ; Complemento do "carry": CY= NOT CY
 SCF       ; Seta o flag de carry: CY=1
 RES b,m   ; Resseta o bit "b" de m.
 SET b,m   ; Seta o bit "b" de m.
  Onde:
 m - Qualquer registrador r, [HL] ou [xx+d]

  Exemplos:

  BIT 5,C
  Testa o valor do bit 5 do registrador C. O complemento do bit é retornado em Z, ou seja, se b=1, Z=0 e se b=0, Z=1.
 Bit | 76543210
 ----+---------
  C  | 00110111
  Registradores:
         A   B   C   D   E   H   L   SZ H PNC
 Antes:  00  00  37  00  00  00  00  00 0 000
 Depois: 00  00  37  00  00  00  00  00 1 000
  Se for "BIT 6,C", Z=1.

  CCF
  Inverte o valor flag de "carry". Se for 1 passa para 0 e se for 0 passa para 1.

  SCF
  Sera o "carry", ou seja, CY=1.

  SET 4,A
  Seta o bit 4 do registrador A.
  A=00H

 +-----+-+-+-+-+-+-+-+-+
 | bit |7|6|5|4|3|2|1|0|
 +-----+-+-+-+-+-+-+-+-+
 |     |0|0|0|0|0|0|0|0|  Antes
 +-----+-+-+-+-+-+-+-+-+
 |     |0|0|0|1|0|0|0|0|  Depois
 +-----+-+-+-+-+-+-+-+-+

  RES 2,C
  Resseta o bit 2 do registrador C.
  C=FFH

 +-----+-+-+-+-+-+-+-+-+
 | bit |7|6|5|4|3|2|1|0|
 +-----+-+-+-+-+-+-+-+-+
 |     |1|1|1|1|1|1|1|1|  Antes
 +-----+-+-+-+-+-+-+-+-+
 |     |1|1|1|1|1|0|1|1|  Depois
 +-----+-+-+-+-+-+-+-+-+


  5.4 - Deslocamentos: RLCA, RR, SLA, SRA, SRL, RLD, RRD, RLA, RRCA, RRA, RLC, RL e RRC

  Aplicam um deslocamento nos bits de um registrador ou posição de memória, ou seja, em "r", (HL), (IX+d) ou (IY+d).

  Para um número de 8 bits, representado pelas suas posições dos bits "76543210", os deslocamentos podem ser:

  Para a esquerda:
   76543210

   <-------

 7 6543210?
  Onde o bit 7 sobra e "?" é preenchido de acordo com algum critério.


  Para a direita:
   76543210

   ------->

  ?7654321 0
  Onde o bit 0 sobra e "?" é preenchido de acordo com algum critério.


  Mapa de deslocamentos:

Instrução Descrição Ilustração
RLCA Rotação do registrador A para a esquerda.
Bit 7 vai para CY e bit 0.
RR m Rotação para a direita com "carry", através do "vai um".
CY vai para o bit 7 e o o bit 0 vai para CY.
SLA m Deslocamento para a esquerda.
O bit 7 vai para CY e o bit 0 é preenchido com 0.
SRA m Deslocamento para a direita, preservando o bit 7.
O bit 0 vai para CY.
SRL m Deslocamento para a direita.
O bit 0 vai para CY e o bit 7 é preenchido com 0.
RLD Deslocamento para a esquerda em 4 bits.
Faz combinação de nibbles de A e (HL).
RRD Deslocamento para a direita em 4 bits.
Faz combinação de nibbles de A e (HL).
RLA Rotação para a esquerda com "carry" no registrador A, através do "vai um".
CY vai para o bit 0 e o o bit 7 vai para CY.
RRCA Rotação do registrador A para a direita.
Bit 0 vai para CY e bit 7.
RRA Rotação para a direita com "carry" no registrador A, através do "vai um".
CY vai para o bit 7 e o o bit 0 vai para CY.
RLC m Rotação para a esquerda.
Bit 7 vai para CY e bit 0.
RL m Rotação para a esquerda com "carry", através do "vai um".
CY vai para o bit 0 e o o bit 7 vai para CY.
RRC m Rotação para a direita.
Bit 0 vai para CY e bit 7.
  Fonte: [2]

  Exemplos:

  RLCA
 
 Antes:  A=10001000 e CY=0

 Depois: A=00010001 e CY=1

  RRA
 
 Antes:  A=10001000 e CY=0

 Depois: A=01000100 e CY=0

  RLD
 
 Antes:  A=10000010 e HL=10100101

 Depois: A=10001010 e HL=01010010



  Referências:

  [1]- Z80 Software, volume II, Luiz Cypriano, Ed. Érica, 1984.
  [2]- Microcomputadores - Arquitetura-Projeto-Programação, Paulo Bianchi e Milton Bezerra, Ed LTC, 1983.
  [3]- Z80 em http://www.z80.info/z80-op.txt
  [4]- Z80 Assembly Language Programming, Lance Leventhal, Ed McGraw-Hill, 1979.
  [5]- Programming the Z80, Rodnay Zaks,Ed. Sybex, 1982.


<< Anterior Assembly Próxima >>


/MARMSX/CURSOS/Assembly