Z80 Assembler

>>> LINK A PAGINA AGGIORNATA <<<

  • .
  • .
  • .
  • .
  • .
  • .
  • .
  • .
  • .
  • .
  • .
  • .
  • .
  • .
  • .
  • .
  • .
  • .
  • .
  • .
  • .
  • .

Un assemblatore personale per CPU Z80

Logo AssemblatoreHo scritto questo assemblatore sia come esercizio per l’apprendimento di Python, sia per riprendere in mano alcuni vecchi progetti elettronici (e poter ricompilare i sorgenti assembly scritti nei primi anni 90). Lo sviluppo con Python è stato più veloce e semplice rispetto alla precedente versione in Pascal/Delphi. In Python non c’è bisogno di occuparsi esplicitamente di puntatori e allocazioni/deallocazioni di memoria. Le strutture dati dinamiche di Python hanno permesso una migliore astrazione sui dati di lavoro (dizionari, liste, iterazioni automatiche, funzioni di appartenenza ecc). Il programma è stato successivamente portato in Python3 con funzionalità e codice migliorati, e successivamente reso indipendente sia dalla versione di Python (>2.5 e >3.1) che dalla piattaforma (Windows e Linux).

 

UTILIZZO ASSEMBLATORE

Si può realizzare un assemblaggio semplicemente chiamando la funzione ‘assemble’ presente nel modulo Z80asm.py:

from z80asm import assemble
risultato = assemble(nome_sorgente)
  • ‘nome_sorgente’ è una stringa, ‘risultato’ è una tupla di tre oggetti, il primo è un booleano True o False a seconda che l’assemblaggio sia andato a buon fine o meno.
  • In caso di errore il secondo oggetto è una stringa con la riga del sorgente dove si è verificato un errore e il terzo una stringa con il frammento di codice specifico e descrizione dell’errore.
  • In caso di assemblaggio ok il secondo oggetto è una stringa che indica la lunghezza in byte del codice oggetto prodotto.
  • Ad ogni assemblaggio vengono prodotti i seguenti file:
    • Un file binario .bin da caricare in memoria così com’è (è consentita una sola direttiva ORG).
    • Un file di testo .hex con la codifica delle istruzioni e il corrispondente indirizzo di caricamento (permette direttive ORG multiple).
    • Un file di testo .lst crossreference con la tavola delle label.

 

pyzasm.py

Il modulo pyzasm.py è una comoda interfaccia che permette sia l’avvio di un’apposita GUI (se non si specifica un nome di file sorgente), sia l’assemblaggio da riga di comando (se si specifica il nome del file sorgente):

python pyzasm.py nomefile.asm

In entrambi i casi (GUI o riga di comando) il nome del file oggetto viene per default impostato uguale a quello del sorgente, ma con estensione .bin

 

Z80 Assemblerasm-pyzasm291.zip
(Versione 2.91 per Python >2.5 e >3.1, Windows e Ubuntu/Linux)

 

FORMATO ASSEMBLY

Può essere elaborato un numero virtualmente illimitato di righe di codice sorgente assembly, tuttavia il codice oggetto prodotto non può eccedere 64 kiB, che rappresentano il massimo spazio di memoria indirizzabile dallo Z80.

L’assemblatore è case insensitive, il testo sorgente quindi può essere scritto indifferentemente in maiuscolo o minuscolo, utilizzando un qualsiasi editor che produca file in formato testo ASCII. Gli eventuali caratteri di tabulazione sono automaticamente convertiti in spazi, i caratteri non ASCII vengono convertiti in ‘?’.

Il formato del programma segue il consueto schema del linguaggio assembly Z80:

label       opcode       operando       ;commento
label       direttiva    argomenti      ;commento

A differenza di assembler sintatticamente più restrittivi, ogni elemento può iniziare a partire da qualsiasi colonna.

 

Label

Una label (etichetta) può iniziare con una lettera o con il simbolo di sottolineatura ‘_’ e proseguire con lettere, cifre e simboli di sottolineatura. Facoltativamente può terminare con il simbolo ‘:’ o iniziare con il simbolo ‘.’

I nomi delle label sono riconosciuti tali in base al contesto. Nonostante l’assemblatore lo permetta, per evitare interpretazioni ambigue è consigliabile dare alle label nomi diversi rispetto a direttive di compilazione, opcode, e nomi di registri.

FOO              ; <---- FOO e` una label
LD    EQU  12    ; <---- LD e` una label, ambigua con opcode LD
XOR:             ; <---- XOR e` una label, ambigua con opcode XOR
LD    A,B        ; <---- LD e` un opcode
NOP              ; <---- NOP e` un opcode
.EQU  EQU  15    ; <---- EQU e` una label, ambigua con direttiva EQU
END              ; <---- END e` una direttiva

NOTA: Una label si può trovare da sola su una riga, ma non va mai scritta sulla riga dove è presente una direttiva INCLUDE in quanto verrebbe ignorata.

 

Commenti

Il punto e virgola indica l’inizio di un commento per il programmatore, tutto ciò che si trova dopo questo simbolo viene ignorato.

NOTA: Un ‘;’ può comunque essere scritto all’interno di una stringa o di una costante carattere, in questi casi viene considerato come un carattere e non come l’inizio di un commento.

 

Costanti numeriche

Nel testo sorgente valori numerici possono essere scritti in decimale, esadecimale, binario e carattere ASCII. Le costanti decimali negative vengono codificate in complemento a due. I diversi modi per scrivere le costanti binarie ed esadecimali servono per accettare testi sorgenti di diverse “epoche”. In passato era comune scrivere una costante esadecimale con il simbolo $. Si possono usare indifferentemente lettere maiuscole o minuscole.

1200       ; Costante decimale
36D        ; Costante decimale

$0AF4      ; Costante esadecimale
&0AF4      ; Costante esadecimale
#0AF4      ; Costante esadecimale
0FAH       ; Costante esadecimale (deve iniziare con una cifra)
0xFA       ; Costante esadecimale

10010b     ; Costante binaria
%10010     ; Costante binaria
0b1001     ; Costante binaria

'Z'        ; Valore ASCII del carattere Z
' '        ; Valore ASCII del carattere spazio
'\''       ; Valore ASCII del carattere '
"'"        ; Valore ASCII del carattere '
'\\'       ; Valore ASCII del carattere \

Davanti ad ogni costante può essere scritto il più o il meno unario.

 

Stringhe

Le stringhe di caratteri vanno sempre racchiuse tra apici o virgolette

'stringa'              ;caratteri: stringa
"altra stringa"        ;caratteri: altra stringa

In una stringa si può liberamente scrivere il delimitatore che non è stato usato per racchiuderla.

'un "certo" qualcosa'    ;caratteri: un "certo" qualcosa
"un po' di piu`"         ;caratteri: un po' di piu`

In una stringa si possono scrivere le seguenti sequenze di escape che vengono considerate un singolo carattere:

\\  →  \
\'  →  '
\"  →  "
\0  →  byte 0
\t  →  byte 9   (TAB)
\r  →  byte 13  (CR)
\n  →  byte 10  (LF)

Con le sequenze di escape è possibile scrivere anche il carattere corrispondente al delimitatore della stringa:

"un \"certo\" qualcosa"   ;caratteri: un "certo" qualcosa
'un po\' di piu`'         ;caratteri: un po' di piu`

Siccome il simbolo \ si usa per indicare l’inizio di una sequenza di escape, la sequenza di escape \\ serve per indicare il simbolo stesso:

'\\'              ;carattere: \
"\\\"\\"          ;caratteri: \"\

Ogni stringa deve essere lunga almeno un carattere, in assembly non hanno alcun senso le stringhe nulle.

 

Espressioni

Dove il formato di un’istruzione o di una direttiva di compilazione lo consente, è possibile scrivere un’ espressione composta da costanti e label usando gli operatori + – * / % che viene calcolata al momento dell’assemblaggio. Gli operatori matematici / e % indicano rispettivamente la divisione intera e il modulo (resto della divisione).

LABEL1 + LABEL2 - $3F
INIZIO * (4 + 100011B)
  • Le espressioni vengono calcolate da sinistra verso destra senza alcuna priorità, per definire una qualsiasi priorità si devono usare le parentesi tonde.
  • Se il valore di una costante o di un’ espressione supera i limiti ammessi nell’ambito dell’istruzione a cui si riferisce (3, 8 o 16 bit), l’ assemblatore lo segnala con un messaggio di errore.

In un’espressione si può usare il simbolo ‘$’, che identifica il valore del program counter all’inizio della riga/istruzione in esame:

JP     $   ; salta a se stessa, loop infinito
JR     $   ; salta a se stessa, loop infinito
DJNZ   $   ; salta a se stessa finche' B non diventa zero

Nel solo caso dei salti relativi (istruzioni ‘JR’ e ‘DJNZ’) viene calcolata la differenza tra il valore dell’espressione e il program counter attuale meno 2. Il meno 2 è dovuto al fatto che le istruzioni di salto relativo puntano già all’indirizzo dell’istruzione successiva due byte più avanti.

Per questo motivo l’espressione $ darà luogo ad un valore di salto relativo pari a -2. Siccome il range di salto a 8 bit è compreso tra -128 e 127, è possibile specificare un offset da $ compreso tra -126 e 129.

JR     $ - 126   ; salta 126 byte indietro dall'indirizzo della JR
JR     $ + 129   ; salta 129 byte più avanti dall'indirizzo della JR

 

Sintassi

Per quanto riguarda la sintassi delle 696 istruzioni ci si può riferire al file asm-z80-tab.txt che le contiene tutte in ordine alfabetico assieme alla relativa codifica esadecimale. Sono inoltre riconosciute 102 istruzioni aggiuntive “non documentate”, che non fanno parte del set standard ufficiale, ma di fatto funzionano, e in quasi tutte le operazioni permettono di usare i registri IX e IY anche come singoli registri a 8 bit chiamati IXH IXL IYH IYL.

I simboli ## e #### indicano i punti in cui vanno scritte delle costanti, o i valori delle espressioni che vengono calcolate dall’assemblatore. I valori a 16 bit vengono memorizzati in ordine little endian (il byte meno significativo all’indirizzo di memoria più basso).

LD A,##     ;al posto di ## va scritto un valore tra 0 e 255
LD HL,####  ;al posto di #### va scritto un valore tra 0 e 65535

 

 

DIRETTIVE DI COMPILAZIONE

Possono essere facoltativamente precedute dai simboli ‘.’ o ‘#’.

 

label EQU espressione

Questa direttiva assegna a una label il valore di “espressione”.

LABEL1	EQU    45000
LABEL2	EQU    $FFFF
LABEL3	EQU    LABEL1 + 80

 

DEFINE ricerca sostituto

Nel testo sorgente i caratteri corrispondenti a ‘ricerca’ vengono sostituiti con il contenuto di ‘sostituto’. Ad esempio è possibile usare un nome al posto del numero del bit nelle istruzioni BIT SET e RES.

DEFINE  TXBIT  7
BIT     TXBIT,(HL)

ATTENZIONE: le define effettuano una sostituzione “stupida” direttamente sulle righe di testo sorgente lette da file. Una define troppo generica o breve può risultare accidentalmente sostituibile anche in qualsiasi altra riga che contiene la parte da cercare come sottostringa.

 

DB espressione o stringa, espressione o stringa, …

La direttiva DB (alias DEFB, DM, DEFM, BYTE, TEXT, ASC) consente di specificare una sequenza di byte che verrà inserita nel codice sorgente nel punto in cui è scritta, ogni elemento è un singolo byte il cui valore può essere compreso tra 0 e 255.

DB  12, 44, -8, 01001B, $FA, VALORE+12, 'F', 1110B

La direttiva DB consente di specificare anche delle stringhe di caratteri, i cui codici ASCII verranno inseriti nel codice oggetto nel punto in cui è scritta.

DB   "stringa", 0
DB   'nuova stringa', 0
DB   'nuova stringa\0'
DB   "stringa con degli \"escape\"", 13, 10, 0
DB   "un po'piu` avanti\n\0"
DB   'e` "quasi" riuscito\r\n\0'

NOTA: Il carattere ‘;’ all’interno di una stringa viene considerato parte della stringa stessa e non l’inizio di un commento.

 

DW espressione, espressione, espressione, …

La direttiva DW (alias DEFW, WORD)consente di specificare una sequenza di words che verrà inserita nel codice sorgente nel punto in cui è scritta, ogni elemento è una coppia di byte il cui valore può essere compreso tra 0 e 65535 di cui viene memorizzato prima il byte basso e poi quello alto (little endian).

DW  4000, LABEL1+LABEL3, 0FF0CH, 'P', 0

NOTA: In una DW non si possono inserire stringhe ma solo singoli caratteri.

 

DS bytes, initializer

La direttiva DS (alias DEFS, BLOCK) consente di riservare un blocco di byte, eventualmente inizializzati con il valore specificato da ‘initializer’.

TEMP:  DS  128
TEMP2: DS  1024, 207

 

INCLUDE “nomefile”

Questa direttiva consente di includere nel testo sorgente un altro file sorgente nel punto in cui è scritta, si può arrivare fino a 7 livelli di inclusione.

NOTA: i file da includere devono trovarsi nella stessa directory del file principale.

 

Livelli di inclusione assemblatore

ORG indirizzo

Questa direttiva è facoltativa, indica l’indirizzo fisico reale a cui verranno caricate le istruzioni in memoria, se viene omessa l’indirizzo parte da 0.

Può essere indicata più di una ORG all’interno dello stesso programma, ma in questo caso si deve usare il file .hex che contiene anche l’indicazione degli indirizzi di caricamento, mentre il file .bin (immagine binaria della memoria) risulterebbe inutilizzabile.

 

END

Facoltativa, se presente tutto ciò che la segue (anche nei file inclusi con INCLUDE) viene ignorato. Si usa per aggiungere annotazioni alla fine del codice che verranno ignorate durante l’assemblaggio.

 

ESEMPIO DI SINTASSI

                 ORG    30000
   DESTINAZIONE  EQU    61000             ; Valorizza DESTINAZIONE
   DESTINAZ_2    EQU    DESTINAZIONE + 50 ; Valorizza DESTINAZ_2

TRASMETTI:       EQU    50000             ; Indirizzo subr.trasmissione
                 LD     HL,TABELLA_1      ; HL=indirizzo di TABELLA_1
                 LD     BC,30             ; BC=nr.byte da copiare
                 LD     DE,DESTINAZ_2     ; DE=destinazione
                 LDIR                     ; Copia blocco di 30 byte
                 LD     HL,MESSAGGIO      ; HL=indirizzo di MESSAGGIO
                 _CICLO
                 LD     A,(HL)            ; Legge carattere
                 CP     0
                 RET    Z                 ; Se zero binario fine
                 CALL   TRASMETTI         ; Chiama subroutine
                 INC    HL                ; Incrementa indice
                 JP     _CICLO            ; Prossimo carattere

                 ORG    40000
CRLF:            DB     "\r\n"
TABELLA_1:       DB     ';', 67, 0x80, 90, "'", %1010, 200, 0b100
                 DB     1, 111B, 22, 56, 180, &FF, #AF, 0, 0, 0, 219
                 DB     "zx\n\0", 0FFH, 0, '\\',  12,  -99, 245, 177
MESSAGGIO        DEFM   'Hello World!', 0

 

STRUTTURA MODULI ASSEMBLATORE

pyZasm.py  (interfaccia grafica)
  |
  +-- tkinter
  +-- tkfiledialog
  |
  +-- z80asm.py (assemblatore)
         |
         |
         +-- z80dict.py (dizionario codifica istruzioni)

 

 

(2008)

 

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *