* * * Z80 SIMULATION v3.00 (Freeware) * * *
Linguaggio ASSEMBLER

3.1 IL LINGUAGGIO DI Z80 SIMULATION

Per scrivere programmi che possano essere compilati ed eseguiti da Z80 Simulation, è necessario conoscere la sintassi e la semantica di ciascuna istruzione. Tali istruzioni sono identiche a quelle utilizzate dal microprocessore Z80 o al più possono differire da queste solo per alcuni particolari che comunque sono evidenziati nei prossimi paragrafi. Proprio in virtù di questa similitudine, spesso nel resto del testo, si utilizzerà il termine "microprocessore" al posto del termine "esecutore di Z80 Simulation".

Prima di descrivere una per una le istruzioni, vediamo quali sono gli oggetti su cui operano (registri, flag, memoria, stack, e porte di Input/Output) e il formato generale di una linea di programma.

3.2 I REGISTRI

I registri servono per memorizzare in modo temporaneo i dati da elaborare dal microprocessore. Questi si presentano perciò, come fossero delle locazioni di memoria di 8 o 16 bit a seconda che il registro sia semplice o doppio.

I registri semplici di Z80 Simulation sono:

A, B, C, D, E, H, L, F, I, R

mentre quelli doppi sono:

AF, BC, DE, HL, IX, IY, SP, PC.

Si noti comunque, che i registri doppi AF, BC, DE e HL non sono altro che i registri A, F, B, C, D, E, H, L combinati a due a due. Ciò vuol dire che un'operazione del tipo:

LD HL,#12FE ;carica #12FE in HL

equivale a due istruzioni del tipo:

Inoltre questi ultimi registri, sono presenti in due blocchi: quello formato dai registri AF, BC, DE e HL, e quello degli omologhi che indicheremo con AF', BC', DE' e HL'. Si passa da un blocco all'altro mediante le istruzioni:

Con ciò si mette in evidenza il fatto che i registri normali non possono essere utilizzati contemporaneamente ai rispettivi omologhi e che quindi Z80 Simulation, nella sintassi delle istruzioni non distingue tra normali e omologhi. Sarebbe infatti scorretta l'istruzione:

LD HL',#12FE

Diamo adesso una breve descrizione dei ruoli dei registri:

  1. Il registro A (8 bit). E' il più importante dei registri del microprocessore, in quanto partecipa al funzionamento di un grandissimo numero di operazioni e accetta ogni forma di indirizzamento. Molto spesso ci riferiremo ad esso con il nome di accumulatore.
  2. I registri IX e IY (16 bit). Possono essere utilizzati in tutti quei casi in cui sia lecito usare il registro HL. In più, questi possono realizzare il cosiddetto indirizzamento indicizzato, di cui ci occuperemo nella descrizione della memoria.
  3. I registri B, C, D, E, H, L (8 bit). Sono semplici registri a 8 bit che vengono sfruttati sia per contenere temporaneamente i dati dell'elaborazione, sia, uniti a coppie, per puntare alla memoria.
  4. Il registro SP (16 bit). Si tratta dello Stack Pointer, ovvero del puntatore alla cima dello stack. Approfondiremo il discorso su questo registro quando parleremo dello stack.
  5. Il registro PC (16 bit). Questo è un registro essenziale per il funzionamento del microprocessore, poiché, il suo compito è quello di puntare all'indirizzo della prossima istruzione che verrà eseguita. Proprio agendo su questo registro, le istruzioni di salto riescono a cambiare il flusso del programma. In realtà, in Z80 Simulation, il Program Counter ha una funzione leggermente diversa da quella che ha nel microprocessore vero e proprio: in esso, non punta a un indirizzo di memoria, bensì a una linea di programma contenuto nell’EDITOR.
  6. Il registro I (8 bit). E' utilizzato per la gestione del modo 2 d'interruzione. Tuttavia in Z80 Simulation non ha alcun significato, dato che questo non gestisce le interruzioni.
  7. Il registro R (8 bit). Come il precedente ha un utilizzo molto particolare: contiene un indirizzo in continua evoluzione, utilizzato per il rinfresco della memoria. Anche in questo caso questo registro non ha alcun significato in Z80 Simulation.
  8. Il registro F (8 bit). E' il registro di stato del microprocessore. I suoi bit sono denominati flag e lo stato di questi è determinato in base al risultato di alcune operazioni.

3.3 I FLAG

Vediamo adesso il significato di ciascun bit del registro F:

Il bit 0 (C). E' il flag del carry che in genere è settato a 1 quando si verifica un riporto sull'ultimo bit. Inoltre è alterato dalle operazioni di shift e dalle operazioni logiche.

Il bit 1 (N). E' il flag N che viene settato quando viene effettuata una sottrazione o un decremento. Non può essere testato mediante i salti condizionati, ma è sfruttato dalla operazione DAA.

Il bit 2 (P/V). E' il flag di parità o di overflow. Quando lavora come flag di parità (in genere nelle operazioni logiche e negli shift) viene posto a uno quando il numero dei bit posti a 1 nel risultato è pari. Se invece lavora come flag di overflow (in genere nelle operazioni aritmetiche) viene settato a 1 quando il segno del risultato è errato. Supponiamo ad esempio di voler sommare -1 a -128. Le istruzioni sono:

Il risultato corretto sarebbe -129 ma invece nel registro A troviamo il valore 127. In questo caso il flag P/V sarà posto a 1.

Il bit 3. Questo bit non è un flag e non ha alcun significato. Il suo valore è casuale.

Il bit 4 (H). E' il flag dell'half-carry e indica il riporto che avviene al bit 3 dell'accumulatore. Non può essere testato con le istruzioni di salto condizionato, ma è sfruttato dall'operazione DAA.

Il bit 5. Questo bit non è un flag e non ha alcun significato. Il suo valore è casuale.

Il bit 6 (Z). E' il flag dello zero: viene posto a 1 nel momento in cui il risultato di un'operazione è 0.

Il bit 7 (S). E' il flag del segno. Quando viene posto a 1 significa che il risultato dell'operazione è negativo (in altre parole il bit più significativo del risultato è uguale a 1).

3.4 LA MEMORIA

Il microprocessore per poter memorizzare i dati dell'elaborazione, qualora i registri non siano più sufficienti, utilizza la memoria. Z80 Simulation ne ha una di 16 Kbyte, i cui indirizzi sono contigui, ma variabili dall'utente mediante il comando dell'ambiente integrato [Options]/[Memoria]/[Posiziona] il cui funzionamento è stato già descritto.

Per accedere ad un byte di memoria ci sono più modi di indirizzamento:

Indirizzamento diretto. In questo modo di indirizzamento, viene specificato direttamente, mediante una costante, l'indirizzo della locazione a cui si vuole accedere. Se si vuole ad esempio caricare il contenuto del registro A nella locazione di indirizzo #2FCB si scrive:

LD (#2FCB),A

Indirizzamento indiretto. In questo caso l'indirizzo della locazione a cui si vuole accedere, viene specificato mediante un doppio registro (registro puntatore). L'equivalente dell'esempio precedente è:

Indirizzamento indicizzato. E' una tecnica di indirizzamento molto simile alla precedente: l'indirizzo è ottenuto come somma (algebrica) tra uno dei registri IX o IY ed una costante a 8 bit che chiameremo spiazzamento. Si noti comunque, che la costante viene rappresentata in complemento a 2 e che quindi lo spiazzamento deve essere compreso tra -128 e 127. Le seguenti istruzioni caricano il valore di A nel byte di indirizzo pari a quello contenuto in IX diminuito di uno:

LD (IX+255),A o anche LD (IX-1),A

mentre queste altre caricano il valore #A5 nella locazione di indirizzo 300:

Per concludere il discorso sulla memoria, si noti che un qualsiasi tipo di accesso ad una locazione non presente, implica l'arresto del programma con la visualizzazione di un messaggio d'errore.

3.5 LO STACK

Una delle difficoltà della programmazione dei microprocessori, consiste nella gestione dello spazio della memoria. Bisogna infatti tener conto degli indirizzi in cui si collocano i dati. Con lo stack è invece tutto più semplice. Questo è una struttura LIFO (comunque residente in memoria), in cui i dati vengono immessi (istruzione PUSH) e poi prelevati (istruzione POP) in ordine inverso da quello con cui sono stati inseriti, senza dover tenere conto dell'indirizzo di memoria in cui saranno destinati.

Lo stack gioca un ruolo importante, anche nella gestione delle chiamate dei sottoprogrammi. Infatti: quando si esegue una chiamata a sottoprogramma (istruzione CALL), il microprocessore salva il contenuto del registro PC nello stack; quando bisogna ritornare all'istruzione successiva alla chiamata (istruzione RET), il microprocessore carica nel PC il dato prelevato dalla cima dello stack. La cima dello stack viene puntata dal registro SP. Tenendo conto che lo stack cresce da indirizzi alti a indirizzi bassi, possiamo dire che: ad ogni deposito nello stack, corrisponde il decremento di SP; ad ogni prelievo corrisponde l'incremento di SP.

All'avviamento di Z80 Simulation, la cima dello stack viene automaticamente posta alla fine della memoria, ma può essere riposizionata all'indirizzo nnnn mediante l'istruzione LD SP,nnnn.

3.6 LE PORTE DI INPUT/OUTPUT

Nel microprocessore Z80, le porte di INPUT/OUTPUT sono 256 con indirizzi che vanno da 0 a 255. Tramite queste, il microprocessore comunica con i dispositivi periferici, ed in particolare scrive su di un dispositivo con un'istruzione di output, e legge da esso con un'istruzione di input.

Tuttavia, Z80 Simulation non può comunicare con l'esterno. In esso le istruzioni di input provocano (qualora il debugger lo permetta), la visualizzazione nella finestra di INPUT/OUTPUT del messaggio:

INP (porta #pp): 00

L'utente può quindi digitare un valore che sarà considerato come input dalla porta pp. Le istruzioni di output causano invece la visualizzazione del messaggio:

OUT (porta #pp): nn

dove nn è il valore scritto sulla porta pp.

3.7 IL FORMATO DELLE COSTANTI

Abbiamo avuto già modo di vedere, specialmente negli esempi, che le costanti numeriche possono essere scritte in 2 formati:

Formato decimale. Si specifica il numero con cifre decimali. Per esempio:

Formato esadecimale. Si specifica il numero con cifre esadecimali precedute dal simbolo #. Per esempio:

Si possono anche definire delle costanti simboliche tramite la pseudo-istruzione EQU il cui funzionamento è descritto più avanti.

3.8 I COMMENTI

Spesso, all'interno del programma, si ha la necessità di inserire dei commenti. In Z80 Simulation questi iniziano con il simbolo ";" e possono occupare un'intera linea o condividerla con un'istruzione. Sono esempi di commento i seguenti:

; SALVATAGGIO NELLO STACK

PUSH HL ;SALVA HL

PUSH AF ;SALVA AF

; FINE SALVATAGGIO

LD IX,0 ;AZZERAMENTO DI IX

CALL STAMPA ;CHIAMA STAMPA

3.9 LE LABEL

Si è già osservato, che mentre nel microprocessore Z80 il registro PC contiene l'indirizzo di memoria della prossima istruzione da eseguire, in Z80 Simulation, questo registro contiene il numero della prossima riga di programma da eseguire. Si deduce perciò, che l'operando delle istruzioni di salto non è un indirizzo, ma un numero di riga: se per esempio si vuole saltare alla linea 315, basta scrivere:

JP 315

Tuttavia, questo metodo è abbastanza scomodo, poiché non solo è necessario tener conto della posizione in cui si collocano le istruzioni, ma, anche un semplice inserimento di una riga, può comportare la modifica di molte istruzioni di salto.

Per evitare questi problemi, il compilatore di Z80 Simulation gestisce le cosiddette label. Queste, altro non sono, che delle costanti, che assumono il valore del numero della linea in cui vengono specificate. Una label deve essere composta da lettere e cifre, non può superare 6 caratteri di lunghezza e non deve iniziare con una cifra.

Vediamo adesso l'uso delle label nel seguente programma che inizializza la memoria dall'indirizzo #0000 all'indirizzo #00FF, con il valore 32:

;INIZIALIZZA MEMORIA (0000-00FF)

;CON IL VALORE 32

ORG #B000

VALUE EQU 32 ; VALUE ¬ 32

LD HL,0 ; HL ¬ 0

LOOP LD (HL),VALUE ; (HL) ¬ value

INC L ; incrementa L e quindi HL

JP NZ,LOOP ; se L<>0 torna a LOOP

HALT

Si noti infine, che le label non possono essere usate in luogo delle costanti nelle istruzioni che non siano di salto, ovvero sarebbe scorretta l'istruzione

LD A,LOOP

dove LOOP sia una label.

3.10 LE CONDIZIONI

Il microprocessore può eseguire dei salti condizionati basandosi sullo stato dei flag. Vediamo adesso quali sono le condizioni:

  1. Condizione NZ (Z=0). Questa condizione è vera quando il flag dello zero è 0.
  2. Condizione Z (Z=1). Questa condizione è vera quando il flag dello zero è 1.
  3. Condizione NC (C=0). Questa condizione è vera quando il flag del carry è 0.
  4. Condizione C (C=1). Questa condizione è vera quando il flag del carry è 1.
  5. Condizione PO (P/V=0). Questa condizione è vera quando il flag della parità/overflow è 0.
  6. Condizione PE (P/V=1). Questa condizione è vera quando il flag della parità/overflow è 1.
  7. Condizione P (S=0). Questa condizione è vera quando il flag del segno è 0.
  8. Condizione M (S=1). Questa condizione è vera quando il flag del segno è 1.

Ecco alcuni esempi di salto condizionato:

3.11 I SALTI ASSOLUTI E RELATIVI

Si è detto in precedenza, che quando il microprocessore esegue un'istruzione di salto, in realtà altro non fa se non caricare l'indirizzo di salto nel registro PC.

Questo modo di saltare lo diciamo assoluto, per distinguerlo da quello relativo, in cui l'operando dell'istruzione di salto, non è l'indirizzo a cui saltare, bensì un valore compreso fra -128 e 127, che verrà sommato al valore del PC. In particolare poi, in Z80 Simulation, dove non si parla di indirizzi, ma di numeri di linea, un'istruzione del tipo JR 20, fa saltare l'esecutore 20 linee più avanti della linea successiva a quella che contiene la JR. Un'istruzione del tipo JR 253 fa tornare indietro l'esecutore di 3 linee (-3 in complemento a 2 è 253) rispetto alla linea successiva a quella contenente la JR. Si noti comunque, che se l'operando di un'istruzione di salto relativo, è una label, questo non vuol dire che l'esecutore salterà avanti o indietro per un numero di linee pari al valore della label, bensì salterà (come per il salto assoluto) sulla linea in cui è stata definita la label.

3.12 LE PSEUDO-ISTRUZIONI

Le pseudo-istruzioni sono delle istruzioni che non producono codice eseguibile, ma hanno particolari funzioni durante la compilazione del programma. Z80 Simulation ne ha soltanto due, vediamole di seguito:

  1. ORG nnnn. E' una pseudo-istruzione che influenza solo l'assemblatore: essa fissa l'indirizzo a partire dal quale verrà posto il codice macchina. Se per esempio si specifica nel programma ORG #A200, il codice macchina inizia dall'indirizzo #A200. La pseudo-istruzione ORG può essere ripetuta più volte nel programma.
  2. cost EQU nnnn. Questa pseudo-istruzione serve per definire una costante simbolica. Vediamo alcuni esempi:

Anche le costanti simboliche, come le label, devono essere composte da lettere e cifre, non possono superare 6 caratteri di lunghezza e non devono iniziare con una cifra.

3.13 LE ISTRUZIONI

Nei prossimi paragrafi sono descritte tutte le istruzioni del microprocessore Z80. Il titolo di ciascun paragrafo è l'istruzione stessa, mentre all'interno di questo viene descritta la sintassi, lo scopo, l'influenza sui flag. Spesso sono presenti alcune note sul comportamento dell'istruzione e sulle eventuali differenze tra microprocessore vero e proprio e Z80 Simulation.

3.14 I SIMBOLI UTILIZZATI

Nel descrivere le varie istruzioni utilizzeremo i seguenti simboli:

"REG8" (registri a 8 bit). Con questo simbolo indicheremo i registri a 8 bit.

"REG16" (registri a 16 bit). Con questo simbolo indicheremo i registri a 16 bit.

"PMEM" (puntatore alla memoria). Con questo simbolo indicheremo i puntatori alla memoria del tipo HL, IX+DD e IY+DD, ove "DD" sia un valore compreso tra -128 e 127.

"VAL8" (costante a 8 bit). Con questo simbolo indichiamo una costante a 8 bit.

"VAL16" (costante a 16 bit). Con questo simbolo indichiamo una costante a 16 bit.

"·" (flag non modificato). Il simbolo "· " indica, che il flag sotto cui è posto, non viene modificato dalla istruzione.

"» " (flag modificato). Il simbolo "» " indica, che il flag sotto cui è posto, viene modificato dall’istruzione.

"X" (flag modificato in modo casuale). Il simbolo "X" indica, che il flag sotto cui è posto, viene modificato dall’istruzione, ma in modo casuale.

"nb" (numero di bit). Con "nb" indicheremo un bit all'interno di un byte. Il bit meno significativo viene indicato con 0.

(condizione). Il Simbolo CC indica le condizioni NZ, Z, NC, C, PO, PE, P, M.