Curso de Assembly
Assembly Z80
Você está em: MarMSX >> Cursos >> 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:
- 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.
- [xx+d] é o conteúdo de memória apontado por um registrador de índice (IX ou IY), mais um deslocamento "d".
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:
- - → Flag não afetado.
- * → Flag afetado.
- 0 → Flag ressetado.
- 1 → Flag setado.
- ? → Desconhecido.
- P/V → flag usado como paridade ou estouro.
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:
- Transferência de dados
- Desvio
- Aritméticas
- Pilhas
- 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 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:
- registrador ⇄ registrador
- registrador ⇄ memória
- registrador ← valor
- memória ⇄ memória (exceto LD)
Jamais podemos transferir:
- valor ⇄ valor
- valor ← registrador
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:
- DE - contém o endereço de memória de destino.
- HL - contém o endereço de memória de origem.
- BC - o tamanho do bloco de memória.
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:
- Salto - salta para um endereço e não retorna, como a instrução GOTO do Basic.
- Salto com retorno - salta para um endereço e retorna, como a instrução GOSUB-RETURN do Basic.
- Incondicional - salta para um endereço sem necessitar qualquer condição para isso.
- Condicional - salta para um endereço somente se uma condição for atendida, com em Basic: IF condição THEN GOTO/GOSUB.
Um salto condicional será baseado no estado de um flag do registrador F. As condições são:
- C - Carry setado. Flag C=1.
- NC - Carry não setado. Flag C=0.
- Z - O resultado no registador A é zero. Flag Z=1.
- NZ - O resultado no registador A é diferente de zero. Flag Z=0.
- PO - O bit de paridade/overflow é 0. Flag P=0.
- PE - O bit de paridade/overflow é 1. Flag P=1.
- P - O resultado no registador A é um número positivo. Flag S=0.
- M - O resultado no registador A é um número negativo. Flag S=1.
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:
- O "JP nn" faz um salto utilizando um endereçamento direto para a memória, onde "nn" é um valor de 16 bits não sinalizado (0 a 65535).
- O "JR e" faz um salto utilizando um endereçamento relativo para a memória, onde "e" é um valor de 8 bits sinalizado (-128 a 127).
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:
- O resultado da conta é positivo, se A >= s.
- O resultado da conta é zero, se A = s.
- O resultado da conta é negativo, se A < s.
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.