Curso de Assembly
Conhecendo o Processador Z80


Você está em: MarMSX >> Cursos >> Assembly Z80   O principal componente de um computador é a CPU. No MSX, a CPU é o processador Z80-A, da Zilog, com um clock de 3,58 MHz. O Z80-A é um processador de 8 bits, ou seja, é capaz de processar 8 bits de cada vez. O endereçamento de memória, por sua vez, é feito com 16 bits, resultando em 65536 posições de memória diferentes [1].

  A imagem a seguir, apresenta a arquitetura do processador Z80 do MSX.


  Repare no lado direito do esquema do processador acima, que há três setas grandes: a de dados (DATA BUS), de endereço (ADR BUS) e de controle (CON BUS). Elas representam as vias de comunicação entre o processador, memória principal e periféricos.


  Registradores

  Os registradores são as memórias internas do processador Z80, que são extremamente rápidas e armazenam pequena quantidade de informações. Elas são utilizadas pelo processador para auxiliar na execução de programas e cálculos. Eles podem ser de 8 ou 16 bits.
  A memória principal é uma memória localizada fora do processador, conectada através de um barramento de dados de 8 bits e outro de endereçamento de 16 bits. Ela possui no MSX o tamanho de 64 Kbytes (65536 bytes), variando de 0 a 65535 (0 a FFFF, em hexadecimal). O objetivo da memoria principal é armazenar os programas e os dados que serão processados pelo Z80. Por exemplo, no endereço de memória &H3CD2 do esquema abaixo, encontramos o valor &H2C.



  O registrador possui velocidade de escrita/leitura maior que a memória principal. Dessa forma, os registradores deverão ser os preferidos para a realização de cálculos, sempre que possível. Para uma troca de valores de registrador para outro registrador são necessários 4 ciclos de clock, enquanto que de um registrador para a memória são necessários de 7 a 13 ciclos. Quanto menos ciclos de clock necessitar uma instrução, mais rápida será sua execução.

  Os registradores do Z80:

  A - O acumulador é o registrador responsável pelos resultados aritméticos, lógicos e comunicação externa através de INs e OUTs.
  B - Registrador comum. Utilizado também como contador de laços de repetição, como na instrução DJNZ.
  C - Registrador comum. Pode ser usado em par com o registrador B, se tornando um registrador de 16 bits.
  D - Registrador comum.
  E - Registrador comum. Pode ser utilizado em par com o registrador D, se tornando um registrador de 16 bits.
  HL - Registrador de 16 bits.
  IX - Registrador de índice. Permite a utilização de deslocamentos fixos para uma variável, como o valor "1" na expressão "A + 1". Ex: IX+1.
  IY - Registrador de índice. Permite a utilização de deslocamentos fixos para uma variável, como o valor "1" na expressão "A + 1". Ex: IY+2.
  F - Flag. São bits que indicam certas propriedades resultantes após uma operação aritmética.
  PC - Esse registrador é um ponteiro para a memória principal que contém o local da próxima instrução a ser executada.
  SP - Esse registrador é um ponteiro para a memória principal que contém o endereço do topo da pilha.


  Registradores alternativos: A', B', C', D', E', F', H' e L'.

  São cópias dos registradores A, B, C, D, E, F, H e L que ficam em segundo plano e não podem ser usados. Entretanto, utilizando as instruções EX e EXX, trocamos o conteúdo de pares dos registradores AF, BC, DE e HL pelos seus correspondentes AF', BC', DE' e HL'.
  Através dessa troca nós:
  Trocas possíveis:
 EX AF,AF'  faz  AF ⇄ AF'

           ┌ BC ⇄ BC'
 EXX  faz ─┤ DE ⇄ DE'
           └ HL ⇄ HL'

  Exemplo:
 Instante 0: 
 A  00
 A' 00

 Instante 1: LD A,&H20  ; Faz A = &H20
 A  20
 A' 00

 Instante 2: EX AF,AF'
 A  00
 A' 20

 Instante 3: LD A,&H45  ; Faz A = &H45
 A  45
 A' 20
 
 Instante 4: EX AF,AF'
 A  20
 A' 45

  O objetivo desses registradores é aumentar a memória interna do processador e também armazenar temporariamente dados de um processamento, para posterior uso.
  A troca entre esses registradores é feita aos pares. Dessa forma, sempre que desejarmos utilizar esse recurso para um registrador, como por exemplo o registrador A, a troca de conteúdo EX/EXX afetará também o seu par correspondente, que nesse caso é o registrador F.
  No caso da instrução EXX, ela afeta um grupo maior: BC, DE e HL. Assim, quando é feita a troca de conteúdo com EXX, troca-se o conteúdo desses 6 registradores com seus correspondentes linha.


  Flags

  Os flags indicam algumas propriedades resultantes de operações aritméticas. Esses resultados são representados pelos 8 bits do registrador F.
  A configuração de bits desse registrador, bem como a descrição de cada propriedade é mostrada a seguir.
 Bit   |  7   6   5   4   3   2   1   0
 ------+---------------------------------   ← Registrador F
 Flag  |  S   Z   -   H   -   P   N   C
  S - Sign - O resultado da operação é negativo (1) ou positivo (0).
  Z - Zero - O resultado da operação é zero (1) ou não (0).
  H - Half-Carry - Se 1, houve estouro no bit 3 (ou ficou devendo).
  P - Parity/Overflow - Indica se número de bits 1 é par ou ímpar.
  N - Subtraction - A última operação foi subtração (1) ou adição (0).
  C - Carry - Se 1, houve estouro no bit 7 (ou ficou devendo).

  Exemplos de operações que afetam o flag:

  Primeiro caso:
  00000000		Teste em Assembly:
- 00000001		
----------		10 ORG &HC000
  11111111		20 LD A,0
			30 LD B,1
 7 6 5 4 3 2 1 0	40 SUB B
 S Z - H - P N C	50 RET
 1 0 1 1 1 0 1 1 	

  Segundo caso:
  00001111		Teste em Assembly:
+ 00000001		
----------		10 ORG &HC000
  00010000		20 LD A,&HF
			30 LD B,1
 7 6 5 4 3 2 1 0	40 ADD A,B
 S Z - H - P N C	50 RET
 0 0 0 1 0 0 0 0 	

  Terceiro caso:
  11111111		Teste em Assembly:
+ 00000001		
----------		10 ORG &HC000
  00000000		20 LD A,&HFF
			30 LD B,1
 7 6 5 4 3 2 1 0	40 ADD A,B
 S Z - H - P N C	50 RET
 0 1 0 1 0 0 0 1 	


  Program Counter - PC e Stack Pointer - SP

  Para a execução de uma instrução, o processador precisa saber em que lugar da memória principal está localizada a próxima instrução. O registrador PC contém esse endereço.
  Cada instrução do Z80 possui um comprimento em bytes que varia de 1 a 4. A instrução de tamanho "n" executada consome todos bytes pertencentes à ela. Dessa forma, toda vez que uma instrução de comprimento de n bytes é executada, o registrador PC avança n bytes, em busca da próxima instrução.
  Por exemplo, se uma dada instrução do Z80 possuir 2 bytes e o registrador PC apontar para o endereço de memória &HC000, após a execução desta instrução, o registrador PC irá apontar para a posição de memória &HC002.
Instrução: LD A,4 
Código de máquina equivalente: &H3E &H04

 End	Código de máquina   
 C000	3E
 C001	04
 C002	XX  ← Código da próxima instrução

  Uma pilha de dados na memória principal é semelhante a uma pilha de livros sobre uma mesa. No caso dessa, um primeiro livro é colocado diretamente sobre a mesa, sendo esse o topo da pilha. O segundo é posto acima do primeiro livro e esse passa a ser o topo da pilha. O terceiro é posto sobre o segundo e esse passa a ser o topo da pilha. Quando retiramos um livro da pilha, retiramos o livro do topo, que no caso é o terceiro livro. Assim, o segundo livro passa a ser o topo da pilha. Por aí vai, até retirarmos o último livro da pilha, que no caso é o primeiro que foi colocado na mesa, tornando-se assim uma pilha vazia.
  A seqüência de eventos de empilhamento e desempilhamento, narrada acima, é ilustrada a seguir.
                                L3
                        L2      L2      L2
                L1      L1      L1      L1      L1
 Mesa  ----    ----    ----    ----    ----    ----    ----
 Legenda:
 L1: primeiro livro colocado
 L2: segundo livro colocado
 L3: terceiro livro colocado
   : Topo da pilha

  A convenção de listagem de memória em papel ou em tela é feita de forma crescente, de cima para baixo. No caso do Z80, que é capaz de endereçar 65536 posições, tem-se:
 Endereço
 --------
 0000      |
 0001      |
 0002      |
 0003      |
 ...       |
 FFFE      |
 FFFF      V
  O empilhamento é feito no sentido decrescente da memória. A partir da convenção apresentada, temos que o empilhamento na memória é feito no sentido de baixo para cima. Entretanto, quando o valor for "estourado", isto é, uma posição de memória menor que &H0000, retorna-se ao valor &HFFFF.
  De forma a auxiliar o programador a armazenar dados na memória, o registrador SP contém um ponteiro para a posição de memória que indica o topo de uma pilha.
SP=&HF000

...
EFFD       ^
EFFE       | Sentido da pilha
EFFF       |
F000  <--------- topo
F001
...

  A instrução PUSH reg, empilha dados contidos no registrador reg na próxima posição de memória acima do topo. Já a instrução POP reg, recupera o dado contido no topo da pilha para o registrador reg.

  Obs: reg é necessariamente um registrador de 16 bits (AF, BC, DE, HL).

  Exemplo prático: suponha que temos a seqüência de bytes 01 02 03 04 05 06. Se o SP estiver apontando para a posição &H1000, ao empilharmos byte a byte, os dados ficariam assim:
 End.	Dado
 -----------
 0FFA	06
 0FFB	05
 0FFC	04
 0FFD	03
 0FFE	02
 0FFF	01
 1000	xx
  Obs: xx significa "lixo" da memória.
  Ao final da operação, o registrador SP irá apontar para a posição de memória &HFFA, que é o valor do topo da pilha. Assim, quando houver desempilhamento, o valor 06 é retornado.

  Exemplo com programa Assembly:
 BC = 0102  (B=01 e C=02)
 SP = 1000

 PUSH BC  faz: (SP-2)=C, (SP-1)=B e SP=SP-2

0FFE  02
0FFF  01
1000  xx


  Código em linguagem de máquina e Mnemônicos

  As instruções passadas ao processador são códigos numéricos, no qual é extremamente difiícil para um programador entendê-los ou memorizá-los. Por exemplo, o seguinte trecho é um código em linguagem de máquina (valores em hexadecimal):

  3E FF 06 01 80 C9

  De modo a facilitar a vida do programador de Assembly, foram criadas certas palavras que representassem os códigos em linguagem de máquina, tornando-se mais compreensíveis para o Homem. A essas palavras que representam os códigos de máquina, chamamos de Mnemônicos.
  O programa anterior, ao ser reescrito utilizando os Mnemônicos, torna-se agora legível para nós:
 LD A,&HFF
 LD B,1
 ADD A,B
 RET
  Observe que fica muito mais fácil de entender o que o programa faz:   É necessário converter o código em Mnemônicos para o Assembly, pois o processador só entende os códigos numéricos. O utilitário que realiza essa conversão é chamado de Assembler.


  Assembler / Disassembler

  Assembler (ou Assemblador) é um programa que converte os mnemônicos para código de máquina. Por exemplo, o programa fonte (Mnemônicos) a seguir será convertido para linguagem de máquina no Macro-Assemblador RSCII, que será visto em detalhes no próximo capítulo.
 10 ORG &HC000
 20 LD A,B
  Obs: o comando ORG define o endereço inicial do programa.

  Foi digitado EN e teclado "Enter", para assemblar o código:
 EN 
 Número de Opción: 3 
 Modo (H/D): H 

  Resultado:
 End    Cód.Maq.	Linha	Mnemônico
 -------------------------------------------
 C000			10	ORG &HC000
 C000	78		20	LD A,B
  Assim, o código de máquina para a instrução LD A,B é 78 (hexadecimal).

  O sentido inverso do Assembler, ou seja, converter código de máquina para mnemônicos, é chamado de Disassembler.
  No RSCII, o comando DE seguido do endereço inicial (ou endereço inicial mais endereço final) desassembla o código.
 DE &HC000 
 Modo (H/D): H 
 DEFB: 

 C000  78       LD A,B

  Ao adicionar a opção "G" no final do comando DE, o código mnemônico é salvo em disco. Para isso, o programa pedirá um nome.
 DE &HC000,G 
 Modo (H/D): H 
 Nombre: "prog1.asm" 
  O arquivo será salvo no formato de listagem do RSCII e poderá ser carregado através do comando CT.

  O Disassembler para uma faixa de endereços é feito da seguinte maneira: suponha que nosso programa em Assembly esteja locado da posição de memória &HC000 até &HC00A. O comando DE para salvar nosso código será:
 DE &HC000-&HC00A 
  ou
 DE &HC000-&HC00A,G 
  Obs: a listagem numérica de um programa com mnemônicos similar ao Basic, é convenção do Macro-Assemblador RSCII.


  Fluxo de execução dos programas

  Conforme dito anteriormente, o registrador PC contém o endereço da próxima instrução a ser executada. Uma instrução é executada por vez, onde cada instrução pode ter de um a quatro bytes.
  O fluxo de execução é sempre progressivo na memória, executando instruções em seqüência, salvo quando encontrados desvios.
  Quando uma instrução é executada, o registrador PC avança N bytes, de acordo com o tamanho de cada instrução.
  A seguir, será apresentado um programa onde o fluxo de leitura de instruções é ilustrado.
 End    Cód.Maq.	Linha	Mnemônico
 -------------------------------------------
 C000			10	ORG &HC000
 C000	78		20	LD A,B
 C001	3E04		30	LD A,4
 C003	DD340A		40	INC (IX+10)
 C006	DDCB0A1E	50	RR (IX+10)
  O fluxo de execução, instrução a instrução, é o seguinte:
 Ordem	Antes		Instrução Executada	Depois
 1	PC=C000		LD A,B			PC=C001
 2	PC=C001		LD A,4			PC=C003
 3	PC=C003		INC (IX+10)		PC=C006
 4	PC=C006		RR (IX+10)		PC=C00A
  O fluxo segue sempre adiante, salvo quando encontrar desvios. Os desvios serão abordados no capítulo 6.

  Para simular esse programa no RSCII, use o comando SI (simular).
 SI &HC000 
 Modo (H/D): H 
  Principais comandos:
 Teclar E para executar cada instrução.

 Teclar M, caso deseje alterar um dos registradores:
 A, B, C, D, E, F, H, L, IX ou IY.

 Tecle "enter" para validar as modificações nos registradores, em cada linha
 (a primeira linha são os registradores normais, a segunda os alternativos).



  A linguagem Assembly é bem mais complexa do que as linguagens de alto nível. Portanto, para começar a entendê-la bem, é necessário:
  1. Se ambientar bem com o simulador RSC II.
  2. Começar pelos programas de exemplo fornecidos nesse curso.
  3. Executar o passo a passo e observar o comportamento dos registradores, flags e memória.
  4. Testar pequenas mudanças, sejam elas pela alteração do valor dos registradores ou memória, bem como pela alteração do código fonte.
  5. Procurar por entender as instruções do Z80, através de pequenos programas de teste.
  No capítulo 6 serão apresentadas as principais instruções do Z80, divididas por tipo de operação.
  Conforme dito anteriormente, pequenos programas podem ser feitos para testar e comparar os efeitos das instruções do Z80.



  Referências:

  [1] - Aprofundando-se no MSX; Piazzi, Maldonado, Oliveira; Ed. Aleph, 1987.


<< Anterior Assembly Próxima >>