Disponendo di un programma assemblatore si può convertire molto rapidamente il programma della pagina esempio in codice macchina.E'però possibile procedere anche manualmente, e questo chiarisce molto il modo in cui lo Z80 lavora.
Per prima cosa scriviamo il codice mnemonico. Poi accanto ad ogni istruzione scriviamo la relativa codifica esadecimale come visibile nella pagina del set di istruzioni completo.
Al posto delle XXXX lasciamo segli spazi, delle sottolineature, delle punteggiature o quello che preferiamo, facendo attenzione a ricordare che XX significa un byte e XXXX 2 byte.
Questi spazi andranno poi sostituiti con delle costanti, in pratica con dei valori da 0 a 255 (o 0H..FFH in esa).
31.... LD SP,65535Possiamo già sostituire le costanti dichiarate esplicitamente nel programma, ad esempio LD B,200 che è codificato con 06.. lo possiamo già scrivere come 06C8 (C8 è 200 in esadecimale). Il valore 65535 da assegnare allo stack pointer va scomposto nei due byte alto e basso, in questo caso sono entrambi 255 (cioè FF esadecimale). E' da ricordare che anche qui va scritto prima il byte basso e poi quello alto, il valore 15000 da caricare in BC si codifica come 983A.
AF XOR A
D3.. OUT (0),A
DB.. ATTESA1 IN A,(0)
CB47 BIT 0,A
20.. JR NZ,ATTESA1
01.... LD BC,15000
CD.... CALL DELAY
3E.. LD A,00000010B
D3.. OUT (0),A
11.... LD DE,7700
3E.. TONO LD A,00000011B
D3.. OUT (0),A
06.. LD B,200
10.. RIT1 DJNZ RIT1
CB87 RES 0,A
D3.. OUT (0),A
06.. LD B,200
10.. RIT2 DJNZ RIT2
1B DEC DE
7A LD A,D
B3 OR E
20.. JR NZ,TONO
AF XOR A
D3.. OUT (0),A
C3.... JP ATTESA1
C5 DELAY PUSH BC
CD.... CALL MS1
C1 POP BC
0B DEC BC
78 LD A,B
B1 OR C
20.. JR NZ,DELAY
C9 RET
06.. MS1 LD B,190
00 MSR1 NOP
00 NOP
10.. DJNZ MSR1
00 NOP
C9 RET31FFFF LD SP,65535A questo punto ci mancano solo i valori da assegnare ai salti e alle chiamate di subroutine. Per i salti corti (JR), detti anche relativi, possiamo già procedere, però bisogna conoscere alcune cose.
AF XOR A
D300 OUT (0),A
DB00 ATTESA1 IN A,(0)
CB47 BIT 0,A
20.. JR NZ,ATTESA1
01983A LD BC,15000
CD.... CALL DELAY
3E02 LD A,00000010B
D300 OUT (0),A
11141E LD DE,7700
3E03 TONO LD A,00000011B
D300 OUT (0),A
06C8 LD B,200
10.. RIT1 DJNZ RIT1
CB87 RES 0,A
D300 OUT (0),A
06C8 LD B,200
10.. RIT2 DJNZ RIT2
1B DEC DE
7A LD A,D
B3 OR E
20.. JR NZ,TONO
AF XOR A
D300 OUT (0),A
C3.... JP ATTESA1
C5 DELAY PUSH BC
CD.... CALL MS1
C1 POP BC
0B DEC BC
78 LD A,B
B1 OR C
20.. JR NZ,DELAY
C9 RET
06BE MS1 LD B,190
00 MSR1 NOP
00 NOP
10.. DJNZ MSR1
00 NOP
C9 RETIl valore da scrivere dopo un'istruzione di salto relativo corto indica di quanti byte il program counter deve incrementare o decrementare. Ad esempio scrivere JR 12 significa che la CPU cercherà la prossima istruzione 12 byte più avanti nella memoria rispetto a dove la cercherebbe normalmente.
Si deve tener conto inoltre che il PC (program counter) punta sempre alla successiva istruzione da eseguire. Questo vuol dire che se noi guardiamo il primo JR che incontriamo nel programma (JR NZ,ATTESA1), per calcolare il salto da fare dobbiamo considerare che PC punta al primo byte dell'istruzione LD BC,15000.
Quindi per saltare all'istruzione IN A,(0) (etichetta ATTESA1) dobbiamo decrementare il PC di 6, si deve in sostanza fare JR NZ,-6.
Visto che -6 è un valore negativo, dobbiamo calcolare il corrispondente numero positivo che lo rappresenta in complemento a 2. Per fare questo basta calcolare 256-6=250. JR NZ,250 è identico a JR NZ,-6. In esadecimale 250 è FA, la nostra istruzione verrà perciò codificata come 20FA.
Per le istruzioni DJNZ vale lo stesso identico discroso, l'istruzione RIT1 DJNZ RIT1, è equivalente a RIT1 DJNZ -2. Dato che -2 = 254 = FEH, la codifica sarà 10FE.
31FFFF LD SP,65535Ora mancano i salti assoluti, che richiedono sia specificata esattamente la locazione di memoria (indirizzo) a cui saltare (JP) o da richiamare (CALL).
AF XOR A
D300 OUT (0),A
DB00 ATTESA1 IN A,(0)
CB47 BIT 0,A
20FA JR NZ,ATTESA1 (-6)
01983A LD BC,15000
CD.... CALL DELAY
3E02 LD A,00000010B
D300 OUT (0),A
11141E LD DE,7700
3E03 TONO LD A,00000011B
D300 OUT (0),A
06C8 LD B,200
10FE RIT1 DJNZ RIT1 (-2)
CB87 RES 0,A
D300 OUT (0),A
06C8 LD B,200
10FE RIT2 DJNZ RIT2 (-2)
1B DEC DE
7A LD A,D
B3 OR E
20EB JR NZ,TONO (-21)
AF XOR A
D300 OUT (0),A
C3.... JP ATTESA1
C5 DELAY PUSH BC
CD.... CALL MS1
C1 POP BC
0B DEC BC
78 LD A,B
B1 OR C
20F6 JR NZ,DELAY (-10)
C9 RET
06BE MS1 LD B,190
00 MSR1 NOP
00 NOP
10FC DJNZ MSR1 (-4)
00 NOP
C9 RETPer poterli codificare è necessario conoscere esattamente l'indirizzo di partenza di ogni singola istruzione. Sappiamo che il programma va caricato a partire dall'indirizzo 0, e quindi possiamo contare i byte di ogni istruzione per trovare l'indirizzo della successiva.
A questo punto sostituiamo alle CALL e JP esattamente l'indirizzo dei punti dove ci interessa arrivare,
ricordando che anche gli indirizzi sono valori a 16 bit da scomporre in due byte e da scrivere nel formato basso/alto:00000 31FFFF LD SP,65535I seguenti 71 byte sono il nostro programma tradotto in codice macchina eseguibile ed espresso in esadecimale:
00003 AF XOR A
00004 D300 OUT (0),A
00006 DB00 ATTESA1 IN A,(0)
00008 CB47 BIT 0,A
00010 20FA JR NZ,ATTESA1
00012 01983A LD BC,15000
00015 CD3400 CALL DELAY (52)
00018 3E02 LD A,00000010B
00020 D300 OUT (0),A
00022 11141E LD DE,7700
00025 3E03 TONO LD A,00000011B
00027 D300 OUT (0),A
00029 06C8 LD B,200
00031 10FE RIT1 DJNZ RIT1
00033 CB87 RES 0,A
00035 D300 OUT (0),A
00037 06C8 LD B,200
00039 10FE RIT2 DJNZ RIT2
00041 1B DEC DE
00042 7A LD A,D
00043 B3 OR E
00044 20EB JR NZ,TONO
00046 AF XOR A
00047 D300 OUT (0),A
00049 C30600 JP ATTESA1 (6)
00052 C5 DELAY PUSH BC
00053 CD3F00 CALL MS1 (63)
00056 C1 POP BC
00057 0B DEC BC
00058 78 LD A,B
00059 B1 OR C
00060 20F6 JR NZ,DELAY
00062 C9 RET
00063 06BE MS1 LD B,190
00065 00 MSR1 NOP
00066 00 NOP
00067 10FC DJNZ MSR1
00069 00 NOP
00070 C9 RET
31 FF FF AF D3 00 DB 00 CB 47 20 FA 01 98 3A CD 34 00 3E 02 D3 00 |
Se un programma contiene solo salti relativi (JR) e non salti assoluti (JP) o chiamate a subroutine (CALL) allora è detto "rilocabile", in quanto funziona indipendentemente dalla sua posizione in memoria.Se invece contiene JP o CALL allora può essere caricato solo all'indirizzo previsto al momento del suo assemblaggio. Per caricarlo altrove è necessario ricalcolare i valori dei salti assoluti. L'indirizzo di partenza di un programma di solito viene segnalato all'assemblatore con la direttiva di compilazione ORG (origine).