Assemblaggio manuale
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,65535
            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                    RET
Possiamo 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.
            31FFFF                LD SP,65535
            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                    RET
A 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.

Il 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,65535
            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                    RET
Ora mancano i salti assoluti, che richiedono sia specificata esattamente la locazione di memoria (indirizzo) a cui saltare (JP) o da richiamare (CALL).

Per 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,65535
     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
I seguenti 71 byte sono il nostro programma tradotto in codice macchina eseguibile ed espresso in esadecimale:
31 FF FF AF D3 00 DB 00 CB 47 20 FA 01 98 3A CD 34 00 3E 02 D3 00
11 14 1E 3E 03 D3 00 06 C8 10 FE CB 87 D3 00 06 C8 10 FE 1B 7A B3
20 EB AF D3 00 C3 06 00 C5 CD 3F 00 C1 0B 78 B1 20 F6 C9 06 BE 00
00 10 FC 00 C9
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).


Pagina realizzata da Claudio Fin
Ultimo aggiornamento 23-9-2000