Assembly Z-80


  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 aritimética. Assim, digita no terminal: PRINT 4+2 e tecla enter, obtendo o resultado desejado.
  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 Assembly, constatamos que não há qualquer uma que some dois números diretamente. Somente a partir do uso de registradores.
  Vamos entender como funciona a tabela de instruções do Z80. Por exemplo, a instrução ADD 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 pode ser: qualquer registrador r, valor n, [HL] ou [xx+d], onde r é um dos seguintes registradores: A, B, C, D, E, H ou L. O valor n é qualquer número entre 0 e 255, [HL] é o conteúdo de memória apontado pelo registrador HL e [xx+d] é o conteúdo de memória apontado por um registrador de índice (IX ou IY), mais o deslocamento.
  Legal, agora posso fazer a soma. Mas antes, preciso alterar o valor do registrador A para fazer a conta. A instrução LD dst,src faz a transferência de dados. Consultando o item 2, "Símbolos para registradores ", constatamos que dst pode ser o registrador A e src pode ser um número. Obs: não é permitido fazer a carga de um registrador para um número (o contrário), nem trocar valores diretos ou conteúdos de memória. Sempre deve passar por um registrador.
  Assim, descobrimos que para somar dois números (por exemplo 4+2), devemos fazer o seguinte programa:
  LD A,4
  ADD A,2

  As colunas das tabelas de instruções do Z80 são descritas a seguir:   Cada instrução pode ter uma expressão matemática equivalente. Por exemplo, ADD A,B equivale a A=A+B.

  Antes de começarmos, aqui vai uma dica importantíssima: cuidado onde você está mexendo na memória!! Dado que o programa principal fica na mesma memória física que os 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: transferência de dados, desvio, aritiméticas, pilhas e lógicas. 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 tranferê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ó podemos transferir dados de registrador<=>registrador, registrador<=>memória e registrador<=valor. Jamais memória<=>memória ou valor<=>valor.
  São elas: LD, LDI, LDIR, LDD, LDDR, EX e EXX.

  Vamos analisar o que faz a instrução LD:

  LD destino,origem

  Copia o conteúdo de origem para destino. Se houver parêntesis ( ), indica o valor contido no endereço de memória.

  Exemplos:

  LD A,B
  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   F
 Antes:  00  01  02  00  00  00  00  00 0 00
 Depois: 01  01  02  00  00  00  00  00 0 00

  LD B,A
  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   F
 Antes:  0F  01  CA  00  00  00  00  00 0 00
 Depois: 0F  0F  CA  00  00  00  00  00 0 00

  LD (1000H),A
  Copia o conteúdo de um registrador de 8 bits, o 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   F          0FFF  1000  1001
 Antes:  0F  01  CA  00  00  00  00  00 0 00    45    CD    00
 Depois: 0F  01  CA  00  00  00  00  00 0 00    45    0F    00

  LD (1000H),HL
  Copia o conteúdo de um registrador de 16 bits, o 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   F          0FFF  1000  1001
 Antes:  0F  01  CA  00  00  BB  DD  00 0 00    45    CD    00
 Depois: 0F  01  CA  00  00  BB  DD  00 0 00    45    DD    BB
  Nota: Observe que o número de 16 bits é invertido na memória (little endian).


  LD HL,(1000H)
  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   F          0FFF  1000  1001
 Antes:  0F  01  CA  00  00  BB  DD  00 0 00    45    CD    00
 Depois: 0F  01  CA  00  00  00  CD  00 0 00    45    CD    00

  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   F          0FFF  1000  1001
 Antes:  0F  01  CA  00  00  BB  DD  00 0 00    45    CD    00
 Depois: 0F  01  CA  00  00  12  34  00 0 00    45    CD    00
  Nota: Repare que, diferente da trasnferência de registrador de 16 bits para a memória, onde há inversão, a tranferência do valor imediato de 16 bits para o registrador NÃO HÁ INVERSÃO.

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

  EX DE,HL
         A   B   C   D   E   H   L   F
 Antes:  0F  01  CA  12  34  AB  CD  00 0 00
 Depois: 0F  01  CA  AB  CD  12  34  00 0 00

  Para fazermos o que desejamos, temos que procurar na tabela, na parte de descrição ou instrução, a instrução correspondente. Caso não exista, teremos que fazer um caminho diferente do que planejamos.
  Por exemplo: desejamos passar o conteúdo de memória de 1000H para 2000H. Vamos procurar uma instrução do tipo LD (end),(end), no conjunto de instruções do Z80. Não existe nenhuma instrução deste tipo! Mas existem milhares de instruções que passam memória para registrador e vice-versa.
  Escolha qualquer um registrador para ajudar na operação. Escolha B por exemplo. A solução é:
  LD B,(1000H)
  LD (2000H),B



  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.
  Há desvios condicionados, como por exemplo, desvios NC e NZ querem dizer not carry (C=0) e not zero (não é zero). Por exemplo, um desvio condicionado do tipo NZ irá funcionar se e somente se o flag de zero Z for igual a zero. Caso Z=1, o programa segue em frente.
  O Basic é um linguagem sequencial, onde as instruções são dispostas e executadas linha a linha (salvo quando uma linha contém mais de uma instrução separadas por dois pontos) e de forma sequencial. 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, salvo seja encontrado desvios.
  O programa em Basic a seguir ilustra a forma sequencial de execução das instruções, bem como a presença de um desvio condicional.
  10 screen 0
  20 input"Qual o nome";n$
  30 input"profissao";p$
  40 if profissao = "estudante" then gosub 1000
  50 print"nome:";n$
  60 print"Profissão:";p$
  70 end

  1000 input"faculdade ou escola";fe$
  1010 if fe$="faculdade" then input"Carreira";c$ else c$=" Escola"
  1020 p$=P$ + " de " + c$
  1030 return 

  Repare que há um desvio condicinado na linha 40. Se a profissão informada for "estudante", a execução do programa desviará para a linha 1000. Caso a profissão seja outra, a execução segue para a próxima linha (linha 50) e o programa termina após executar a linha 70.
  Na linha 40, poderia haver um desvio direto para a linha 1000, sem atender qualquer condição. Também poderemos voltar ou não do desvio. No caso do exemplo acima, a instrução return retorna para a linha imadiata à linha de onde a instrução gosub foi acionada.
  Em Basic, as instruções gosub e goto são os modos de desvio. Quando usamos gosub, o endereço da próxima linha Basic é armazenada em uma pilha. Assim que ele encontra o comando return, o programa pula para a linha armazenada na pilha e o valor na pilha é desfeito.
  O comando goto desvia também, mas não tem a possibilidade de retorno à proxima instrução, como o gosub.
  Em linguahem de máquina, a instrução CALL equivale ao gosub e as instruções JP, JR e o restante equivalem ao goto. O comando RET é geralmente utilizado para retornar uma desvio do tipo CALL e equivale ao return do Basic.
  Podemos agora compreender como funcionam as sub-rotinas!
  Em um dado programa escrito em Assembly, uma instrução call é realizada, direcionando a execução do programa para uma outra rotina localizada em outra parte da memória. Este programa então é executado e ao final dele, um ret retorna ao programa chamador.



   No esquema acima, a seta verde indica onde o programa principal começa. A instrução call desvia a execução do programa para outra rotina localizada mais acima no esquema. Ao encontrar a instrução ret, o retorno para o programa principal é feito para a instrução imediatamente após o call, assim como no basic.

  Localize no material e execute o programa desvio.asm no RSCII ou o simulador Z80dt para PC, utilizando os recursos de passo a passo e visualização de registradores. O objetivo é observar como os desvios são feitos. Não se importe com as intruções desconhecidas agora.
  Observe que o endereço de execução do CALL é 1802H e da próxima instrução 1805H. Assim que a linha for executada, o valor vai para a pilha como 05 18. Como o valor no stack do simulador aparece errado, pressione F2 e, usando as setas cima/baxo ou pgup/pgdown, vá até o endereço indicado em SP. Agora sim o valor aparece 05 18, como realmente fica na memória!
  PC aponta para o endereço do desvio, que é 180BH. Aqui está a instrução add a,5.
  Portanto, podemos concluir que depois de um call:
  PC aponta para a próxima instrução.*
  (sp-1) recebe PChigh e (sp) recebe PClow;   Armazena
  high => CD00 <=low
  sp=sp-2;   Passa para próximo elemento da pilha
  pc = endereço apontado por call.
  * Em quaisquer microinstruções (procedimentos de uma instrução), a primeira coisa a ser feita é apontar PC para a próxima instrução. Se houverem desvios, como em call, PC recebe o valor do desvio.

  Qual a diferença entre os desvios JP e JR? O JP é um salto absoluto, enquanto que o JR é um salto relativo. Assim, se a instrução for JP 0500h, o PC irá apontar diretamente para o endereço de memória 0500h. Por outro lado, se for JR 0500h, o PC irá saltar 0500h posições de memória, isto é, PC = PC + 0500h. Resumindo:

 JP endereço    ; PC = endereço
 JR salto       ; PC = PC + salto

  A instrução JP <end> é um salto chamado de incondicional, uma vez que não depende de qualquer condição para ocorrer. Já a instrução JP <cond>,<end> é um salto do tipo condicional e irá ocorrer se somente se a condição <cond> for verdadeira. A condição é testada no registrador A e equivale ao seguinte programa em Basic.
  10 a=a-1
  20 if a=<cond> then goto 10

  Existem vários tipos de condições para o desvio:

  C - Estouro. Flag C=1.
  NC - Não houve estouro. Flag C=0.
  Z - O resultado é zero. Flag Z=1.
  NZ - O resultado é diferente de zero. Flag Z=0.
  PO - A paridade é negativa.
  PE - A paridade é positiva.
  P - Número positivo.
  M - Número negativo.

  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 for usando saltos (RSCII):
  10 ORG &HC000
  20 LD A,4
  30 LACO: INC B
  40 DEC A
  50 JP NZ,LACO
  60 RET
  O programa acima utiliza etiquetas, que são como variáveis que servem para armazenar valores. No caso acima, a etiqueta "LACO" armazena o endereço da instrução INC B, que após a conversão para o código de máquina, ficará armazenada na posição &HC002. Assim, "LACO" substitui o endereço &HC002.
  Dessa forma, enquanto A não for zero (flag alterado pela instrução DEC A), o programa volta para a instrução INC B. Assim que A=0, o programa segue normalmente e a instrução RET é executada.

  Abra o programa produto.asm. Ele utiliza os saltos e condições.

  Usando-se o registrador B como contador, podemos utilizar uma instrução muito útil de desvio, chamada DJNZ. A sintaxe é:
 DJNZ <end>
  Esta instrução decrementa o resgitrador B, ou seja, faz B=B-1, e desvia para o endereço indicado, enquanto B for diferente de 0. Dessa forma, podemos simular o comando FOR do Basic, caso desviemos para um endereço anterior à essa instrução. Programa exemplo:
 Endereço  Assembly  Linha  Mnemônico
 C000                10     ORG &HC000
 C000      060A      20     LD B,10
 C002      AF        30     XOR A
 C003      3C        40     INC A
 C004      10FD      50     DJNZ,&HFD
  A instrução DJNZ faz um salto relativo. Como ela ocupa 2 bytes, teremos que voltar 3 posições (&HFD, ou -3) para a instrução XOR A.
  É evidente que o uso de etiquetas facilita muito, pois a conta do salto relativo é feita automaticamente pelo assemblador. Assim, temos:
 Endereço  Assembly  Linha  Mnemônico
 C000                10     ORG &HC000
 C000      060A      20     LD B,10
 C002      AF        30     XOR A
 C003      3C        40     LOOP: INC A
 C004      10FD      50     DJNZ, LOOP
  Carregue o programa prod2.asm e compare com produto.asm.
  A instrução DJNZ leva 8 clocks. Já a instrução JP em conjunto com a instrução DEC leva 12 clocks. Assim, em um loop de tamanho n, são n x 4 clocks a menos de economia.
  Idem para LD A,0 e XOR A. LD leva 7 clocks e XOR 4!!

  Não se esqueça de monitorar o desempenho dos programas testados.

>> Porquê foi usada a instrução NOP em desvio.asm?
Esta instrução não faz nada, semelhante ao REM do basic. Foi utilizada para preencher um espaço entre o programa principal e a sub-rotina. Mas cuidado, que não é assim que se faz em montadores comuns!!
>> Como é que pula então?
Existe uma macroinstrução (instrução de montador, onde veremos mais tarde), a ORG end, que direciona o inicio do programa. Ela não é uma instrução do processador, como ld, call, jp e sim um a instrução do montador, que indica ao montador aonde carregar o programa.



  3- Intruções aritméticas

  Realizam operações matemáticas, do tipo soma, subtração, decrementos e incrementos. No caso de multiplicação e divisão, deve-se aplicar as propriedades matemáticas de soma e subtração para atingir tais fins. Veja a seção de exercícios.
  Vejamos duas palavras novas: incrementar e decrementar. Incrementar é acrecentar uma unidade e decrementar, subtrair uma unidade. Logo:
  incrementar A ==> A=A+1
  decrementar A ==> A=A-1

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

  Em uma operação aritmética, podemos levar em conta o flag carry. é o que diferencia uma soma ADD e ADC ou SUB e SBC.

  ADD A,valor
  Soma o valor de A com o valor depois da vírgula e coloca em a.

  Exemplos:

  ADD A,2
  Equivale a A=A+2

  ADD A,B
  Equivale a A=A+B

  ADD A,(HL)
  Equivale a A=A+conteúdo da memória apontada por HL.

  Cuidado com operações entre números de 8 bits e 16 bits. Dá erro.

  Ex: ADD A,HL ==> ERRO!!

  SUB valor
  Subtrai A do valor.

  Exemplos:

  SUB 10h
  Equivale a A=A-10h

  SUB B
  Equivale a A=A-B

  ADC
  Add with carry, soma levando em conta o flag de carry. Vejamos mais detalhadamente:

  ADC A,B

         A   B   C   D   E   H   L   F
 Antes:  02  01  00  00  00  BB  DD  00 0 01
 Depois: 04  01  00  00  00  BB  DD  00 0 00
  Ele fez: A=A+B+Carry. Como carry=1, ao invés de dar 3 o resultado, deu 4.
  Para a subtração, SBC, é a mesma coisa: A=A-C-Carry.

  SBC
  Subtração com o carry. Subtrai levando em conta o flag de carry. Exemplo:

  SBC A,B
  Equivale a A=A-B-Carry.

         A   B   C   D   E   H   L   F
 Antes:  04  02  00  00  00  BB  DD  00 0 01
 Depois: 01  02  00  00  00  BB  DD  00 0 00

  INC e DEC
  INC reg e DEC reg fazem reg=reg+1 e reg=reg-1, respectivamente.

  Exemplos:
  INC A
  DEC HL

  DAA
  Ajuste decimal. Ajusta a conta resultante no acumulador A para um valor decimal. Suponha uma soma &H08 + &H04 = &H0C. Após o DAA, o valor será ajustado para &H12. Em outras palavras, esse ajuste assume que a operação possuia valores decimais. Código exemplo (para o RSCII):
  10 ORG &HC000
  20 LD A,8
  30 ADD A,4
  40 DAA
  Após a execução da linha 30, o registrador A receberá o valor &H0C. Após o ajuste da linha 40, o registrador A receberá o valor &H12.

  Nota: as instruções LD, ADD, ADC, SUB, etc podem utilizar o regitrador SP, de pilhas. Com isso, podemos redirecionar o endereço da pilha. Porém PC não pode ser manipulado diretamente, somente com o JP e o CALL.



  4- Pilhas e E/S e outros.

  E/S são as instruções que realizam a comunicação entre o processador Z80 e outros dispositivos, como o processador de sons, vídeo e PPI.
  Pilha é a disposição dos dados de forma empilhada, assim como uma pilha de livros. O primeiro elemento inserido nela será o último a ser retirado, assim como o último elemento inserido será o primeiro a ser retirado.

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

  Muitas das instruções acima lidam com interrupção. Vamos ver as que não lidam primeiro, depois veremos o que é interrupção e as instruções.

  PUSH rr
  Coloca na pilha o valor contido no registrador rr, sem alterar o valor de rr.

  Ex: PUSH BC

                Registradores                           End. Memória
         A   B   C   D   E   H   L   F        SP      0FFF  1000  1001
 Antes:  0F  01  CA  00  00  BB  DD  00 0 00  1000    45    CD    00
 Depois: 0F  01  CA  00  00  00  CD  00 0 00  0FFE    CA    01    00

  POP rr
  Retira da pilha um dado e o coloca em um registrador, sem alterar a memória.

  Ex: POP AF
                Registradores                           End. Memória
         A   B   C   D   E   H   L   F        SP      0FFF  1000  1001
 Antes:  0F  01  CA  00  00  BB  DD  00 0 00  0FFE    45    CD    00
 Depois: CD  01  CA  00  00  00  CD  45       1000    45    CD    00
  Consultando-se a tabela dos flags na seção Conhecendo o Processador, temos: s=0, z=1, h=0, p=1, n=0, c=1.


  NOP
  É o equivalente do comando em Basic rem. Não faz nada!

  HALT
  Pára a CPU.

  IN A,(porta)
  Recebe um dado de uma determinada porta e armazena no registrador A. A porta retorna um valor de 8 bits.
  Ex: IN A,(FFH)

  IN r,(c)
  Recebe um dado pelo número da porta apontada pelo registrador C e armazena no registrador r.

  OUT (porta),A
  Exatamente ao contrário do IN. Envia dados do computador para a porta especificada.

  OUTIR
  Outras instruções utilizam-se de laços (loops), como por exemplo: OTIR.
  OTIR lê uma porta apontada por C e coloca na porta o conteudo de memória apontada por HL, de forma crescente (HL=HL+1). A instrução é de loop e termina quando B=0. Repare a riqueza de passos contidos numa só instrução!!
  Para entendermos a instrução, vejamos ela sendo executada (a instrução é escrita sem nenhum dado ao lado, como OUT A,20):

  OUTIR
                Registradores                           End. Memória
         A   B   C   D   E   H   L   F        SP      1000  1001  1002
 Antes:  00  03  10  00  00  10  00  00 0 00  1000    45    CD    00
 Depois: 00  02  10  00  00  10  01  00 0 00  1000    45    CD    00
 Depois: 00  01  10  00  00  10  02  00 0 00  1000    45    CD    00
 Depois: 00  00  10  00  00  10  03  00 0 00  1000    45    CD    00
  Ordem dos dados enviados para a porta 10H: 45H, CDH, 00H

  Interrupções:

  Quando um periférico necessita comunicar-se com o computador, ativa a interrupção do computador, utiliza-se do mesmo para fazer a transferência de dados e depois libera o processador. Quando o processador é interrompido, o endereço da instrução do programa que está sendo executado é guardado em uma pilha, faz-se o que tem que ser feito, e depois o programa continua de onde parou.
  Imagine que façamos um programa para desenhar linhas na tela. Se o drive for acionado antes do programa terminar, o CPU pára de desenhar as linhas e atende ao drive. Depois continua a desenhar as linhas. Provavelmente você já viu o Windows fazer algo semelhante...
  Quais os modos de entrada e saída?
0 - Programada, onde nós colocamos a interrupção dentro do programa.
1 - interrupção, como vimos.
2 - DMA, onde o dispositivo interrompe e não utiliza o processador para transferências.



  5- Instruções lógicas

  Realizam operações de lógica digital, tais como testes lógicos (AND, OR, XOR e NOT), alteração de bits, deslocamentos de bits etc.
  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.

  AND reg
  Faz um E lógico entre os bits correspondentes de A e reg e retorna o valor em A.

  Ex: AND B
 A=00010100
 B=10101100
 ----------
 A=00000100

  AND valor
  Faz um E lógico entre os bits correspondentes de A e o valor e retorna o valor em A.

  Ex: AND 40h
 A=01111111
   01000000
 ----------
 A=01000000

  OR reg
  Faz um OU lógico entre os bits correspondentes de A e o registrador e retorna o valor em A.

  Ex: OR B
 A=00010100
 B=10101100
 ----------
 A=10111100

  OR valor
  Faz um OU lógico entre os bits correspondentes de A e o valor e retorna o valor em A.

 Ex: AND 40h
 A=01111111
   01000000
 ----------
 A=01111111

  XOR reg
  XOR é uma operação lógica equivale à seguinte expressão lógica: [(A'.B) + (A.B')].

  Tabela verdade:
 A | B | Saída
 --+---+-------
 0 | 0 |   0
 0 | 1 |   1
 1 | 0 |   1
 1 | 1 |   0
  XOR detecta quando as duas entradas são diferentes.

  Ex: XOR B
 A=01101111
 B=01010000
 ----------
 A=00111111
  Se fizermos um XOR de dois números iguais, a resposta é zero, pois logicamente existirá coincidência bit a bit.

  Ex: XOR A
 A=10101010
 A=10101010
 ----------
 A=00000000
  Esse truque é utilizado para zerar o acumulador, pois é mais rápido que a instrução LD A,0 e economiza tempo.

  CP reg
  Compara o valor do registrador reg com A, fazendo a oporação A-reg. Ele não coloca a resposta em lugar algum, somente setando os flags, de acordo com o resultado. Por exemplo, para CP B, se A=5 e B=5, A-B=0. O flag Z é setado (Z=1).

  Como saber se o resultado da comparação do registrador é maior ou menor que A? Se reg for maior que A, A-reg<0 e o flag S, que indica se o número é positivo ou negativo, é setado (S=1). Se A for maior, S=0.
  Infelizmente ele não compara números de 16 bits. Portanto, imagine que estejamos percorrendo a memória e queremos que o programa pare no endereço CD45h. Assim, devemos um programa semelhante ao seguinte programa, que testa quando HL chega à posição CD45H:
  inicio: ld hl,C000 ; Define endereço inicial
  loop:   ld a,(hl)
          out (ff),a ; Operação qualquer
          inc hl     ; Passa para a próxima posição de memória
          ld a,cdh   ; Carrega A com o valor CD
          cp h       ; e compara com h
          jp nz,loop ; Se h<>a, Z=0 e pula para loop, senão continua
          ld a,45h
          cp l       ; Agora compara l com 45
          jp nz,loop
  fim:    ret
  Onde vemos inicio, loop e fim chamamos de etiquetas. Elas substituem o endereço aonde está a instrução ao lado delas.
  No próximo capítulo iremos ver o montador RSC II que utiliza as etiquetas.
  Fazemos o primeiro teste com h. Se for diferente, não interessa o que tem em l, pois AB45 <> CD45, por exemplo, o programa voltará para a instrução out ff,(hl).
  Se h=CD, aí sim vamos verificar se l=45. Se não, mais uma vez volta para out ff,(hl). Se for, o programa acaba.
  Por quê usar RET ao final do programa? Porque quando chamamos um programa nosso, o compuador é desviado para o nosso programa. O RET é o retorno do nosso programa ao sistema operacional.

  Assim como o registrador de flags (F), existem os registradores de outros dispositivos que também são analisados bit a bit. Em um regitrador de dispositivo (registrador próprio do dispositivo, como regs de vídeo), cada bit indica uma coisa. Por exemplo, no registrador de Status S#0 os bits valem:

  b0-b4 => Número do sprite excedente;
  b5 => Indicador de colisão de sprites;
  b6 => Indica se há sprites excedentes;
  b7 => Indicador de interrupção.

  E cada um varia de 0 a 1, ou seja, para os casos b5, b6 e b7 é sim ou não.
  Então, se quiséssemos saber se houve uma colisão entre os sprites (desenhos que se movimentam na tela), precisaríamos verificar se o bit 5 é 0 ou 1.

  BIT b,r
  Transfere o valor do bit b do registrador r para o flag Z.
  Ex:
  BIT 5,C
  Testa o bit 5 do registrador C. Se o registrador C tiver o valor binário igual a 00110111, temos:

 Bit | 76543210
 ----+---------
  C  | 00110111
  Z=0, isto é, o bit 5 testado é 1 (não zero).
  Para o mesmo valor do registrador C, a instrução "BIT 6,C" retornaria Z=1.

  CPL
  Inverte todos os bits correspondentes do reg. A. Complemento a 1.
  Ex: CPL
  Se A=11110110, A passa a 00001001

  NEG
  Faz o complemento a dois de A.
  Ex: NEG
  Se A=11110110, A=A'+1, A=00001001+1, A=00001010

  CCF
  Inverte o valor do bit de Carry. Se 1 passa para 0 e se 0 passa para 1.

  SCF
  Faz Carry=1.

  CPIv
  Compara A com (HL), somente setando os flags. HL=HL+1 e BC=BC-1.

  CPD
  Compara igual ao CPI, mas HL=HL-1 e BC=BC-1.

  Preste bastante atenção a estas instruções mais completas!!

  SET b,r
  Faz o bit b do registrador r ficar 1.
  Ex: set 4,a
  Se A=00000000, logo:
  A=00010000

  RES b,r
  Faz o bit b do registrador r ficar 0.

  As próximas instruções lógicas se referem a rotação e deslocamentos de bits.

  OBS: Todo deslocamento é feito passando-se o valor do bit n para seu vizinho (bit n+1 ou bit n-1) e este bit n recebe o valor de seu outro vizinho (bit n-1 ou bit n+1), para todos os 8 bits do registrador. Quando o bit é de extremidade(b7 ou b0), cada caso tem uma regra especial.
 Ex: A=01100010
Deslocamento 1: A=00110001 (>>>>>>)
Deslocamento 2: A=11000100 (<<<<<<)
  Quando o deslocamento é LEFT (esquerda), vai de 0 a 7. Quando é RIGHT (direita), vai de 7 a 0.
  Sempre que a instrução vier sem um complemento, como reg, ELA ESTá USANDO O ACUMULADOR A.

  RLCA
  Desloca no sentido de 0 para 7, copiando o valor do bit 7 para o carry e para o bit 0. Usa o acumulador (reg. A).
  Ex: RLCA
  A=10001000 e Carry=0, logo:
  A=00010001 e Carry=1

  RR reg
  Desloca no sentido de 7 para 0, copiando o valor do carry para o bit 7 e copiando o valor do bit 0 para carry.

  --------<--------
  |               |

  7 6 5 4 3 2 1 0 CY
  -------->--------
  Ex: RR b

  SLA reg
  Desloca no sentido de 0 a 7, colocando o valor 0 no bit 0 e copia o bit 7 no Carry.
  Esta instrução é importante, porque todo deslocamento deste tipo, equivale a reg=reg x 2!!
  Ex: A=00000001 = 1
  SLA a
  A=00000010 = 2
  SLA a
  A=00000100 = 4
  SLA a
  A=00001000 = 8
  SLA a
  A=00010000 = 16 decimal

  SRA reg
  Desloca do bit 7 ao 0, copiando o bit 0 em Carry e copiando o bit 7 no próprio bit 7.
  Ex: B=11000001
  SRA b
  B=11100000 e CY=1
  Se B=10100000
  SRA b
  B=11010000 e CY=0

  SRL reg
  Desloca no sentido de 7 a 0, copiando o valor 0 no bit 7 e o valor do bit 0 em Carry.

  RLD
  Deslocamento completamente diferente do que já vimos. Trabalha com o registrador A e a posição de memória apontada por HL. Ela pega os bits de 0 a 3 de A e copia para a memória, também nos bits de 0 a 3. Os bits de 0 a 3 da memória são passados para os bits de 4 a 7 da memória e os bits 4 a 7 da memória são passados para os bits de 0 a 3 de A. é uma espécie de rodízio de bits.
 A    = XXXX MMMM
 (HL) = NNNN OOOO

 RLD

 A    = XXXX NNNN
 (HL) = OOOO MMMM

  Ex: HL = 1000h. Logo:
 A      = 0000 1100
 (1000) = 0100 0001

 RLD

 A      = 0000 0100
 (1000) = 0001 1100 


  RRD
  Deslocamento do mesmo tipo anterior, só que para a direita.
  Ex: HL = 1000h. Logo:
 A      = 0000 1100
 (1000) = 0100 0001

 RLD

 A      = 0000 0001
 (1000) = 1100 0100


  RLA
  Desloca do sentido de 0 a 7, copiando o o valor de CY(carry) em b0 e o valor de b7 em CY.
  Ex: A=11000000 e CY=1
  RLA
  A=10000001 e CY=1

  RRCA
  Deslocamento semelhante ao RLCA. Do sentido de 7 a 0, copiando b0 em CY e b0 em b7.

  RRA
  Desloca do sentido de 7 a 0, copiando CY em b7 e b0 em CY. é o inverso de RLA.

  RLC reg
  Idem RLCA, só que para todos os registradores de 8 bits, IX e HL.

  RL reg
  Idem RLA, só que para todos os registradores de 8 bits, IX e HL.

  RRC reg
  Idem RRCA, só que para todos os registradores de 8 bits, IX e HL.



  Pronto! Agora que conhecemos boa parte das instruções do Z80, podemos começar a programar. Aqui vai uma sugestão para começar:
  1. Rodar programas com apenas uma instrução do Z80 em um simulador (o RSCII tem um), e observar seus efeitos.
  2. Aumentar o número de instruções e observar seus efeitos, principalmente a seqüência da execução das instruções.
  3. Antes de criar programas mais elaborados, programar usando as sub-rotinas da ROM do MSX. Irão trazer efeitos práticos, como escrever uma letra na tela, e a programação delas é bem simples.
  4. Elaborar programas mais complexos. Ver a seção de exercícios.
  Dica: este curso dispõe de diversos exemplos de programas em Asembly Z80. Procure executar e entender eles, para cada passo sugerido. Depois, você provavelvente estará apto a criar seus próprios programas.


CURSOS/ASSEMBLY/AULA6