Sistema di archiviazione ROM (RFS) — Guida per sviluppatori
Guida per sviluppatori RFS
Questa guida e' un percorso dettagliato del codice sorgente RFS e dell'ambiente di sviluppo. Spiega i concetti del linguaggio assembly Z80 per sviluppatori che potrebbero non avere familiarita' con il linguaggio, percorre ogni modulo sorgente, documenta l'architettura di commutazione banchi e mostra come aggiungere nuovi comandi, modificare moduli esistenti e portare RFS su nuovo hardware.
Per i dettagli dell'architettura hardware e del sistema di build, vedere la Guida tecnica. Per l'operativita' rivolta all'utente, vedere il Manuale utente.
Introduzione all'assembly Z80 per programmatori non-assembly
L'intero firmware RFS e' scritto in linguaggio assembly Z80 — il linguaggio nativo di istruzioni del processore Zilog Z80 usato nella serie Sharp MZ. A differenza dei linguaggi di alto livello, l'assembly mappa quasi direttamente sull'hardware fisico: ogni istruzione si traduce in uno o pochi byte che la CPU esegue direttamente.
Registri
Lo Z80 non ha "variabili" — dispone invece di un piccolo insieme di registri (posizioni di memorizzazione veloci all'interno della CPU). Quelli usati piu' comunemente in RFS:
| Registro | Dimensione | Ruolo |
|---|---|---|
| A | 8 bit | Accumulatore — il registro principale per operazioni aritmetiche, logiche e di I/O. Quasi ogni istruzione coinvolge A. |
| B, C | 8 bit | Uso generale. BC insieme forma una coppia a 16 bit, comunemente usata come contatore di ciclo o conteggio di byte. |
| D, E | 8 bit | Uso generale. DE insieme e’ una coppia a 16 bit, comunemente usata come puntatore di origine o destinazione. |
| H, L | 8 bit | Uso generale. HL insieme e’ il puntatore di memoria principale a 16 bit — la maggior parte delle istruzioni di lettura/scrittura della memoria usa HL. |
| IX, IY | 16 bit | Registri indice — usati per l’indirizzamento di memoria base+offset. Piu’ lenti di HL ma comodi per dati strutturati. |
| SP | 16 bit | Stack Pointer — punta alla cima dello stack delle chiamate. PUSH e POP usano SP automaticamente. |
| PC | 16 bit | Program Counter — l’indirizzo dell’istruzione corrente. Incrementato automaticamente; modificato da salti e chiamate. |
| F | 8 bit | Registro dei flag — singoli bit impostati dalle operazioni aritmetiche: Z (zero), C (carry), S (segno), P/V (parita’/overflow). |
LD dest, src— Carica (copia) dati.LD A, Bcopia B in A.LD A, (HL)legge in A il byte all'indirizzo di memoria contenuto in HL.LD (0x1200), Ascrive A all'indirizzo di memoria 0x1200.CALL addr— Chiama un sottoprogramma. Mette l'indirizzo di ritorno (istruzione successiva) sullo stack e salta aaddr. Equivale a una chiamata di funzione.RET— Ritorna dal sottoprogramma. Preleva l'indirizzo di ritorno dallo stack e vi salta.JP addr— Salto incondizionato aaddr.JP Z, addrsalta solo se il flag Zero e' impostato (cioe' l'ultima operazione ha prodotto zero).JR offset— Salto relativo breve (da -128 a +127 byte). Piu' veloce e compatto di JP per le diramazioni vicine.DJNZ offset— Decrementa B e salta se diverso da zero (Not Zero). L'istruzione di ciclo canonica dello Z80:LD B, 10 / LOOP: ... / DJNZ LOOPripete 10 volte.ADD A, n— Aggiunge n ad A.SUB nsottrae.AND n,OR n,XOR n— logica bit a bit su A.IN A, (port)— Legge da una porta di I/O in A.OUT (port), A— scrive A in una porta di I/O. E' cosi' che lo Z80 comunica con l'hardware (il WD1773, il controller SPI, il latch dei banchi, ecc.).PUSH rr / POP rr— Salva/ripristina una coppia di registri a 16 bit verso/dallo stack.EI / DI— Abilita / Disabilita gli interrupt. Il codice che non deve essere interrotto (ad esempio le operazioni su nastro critiche per il timing) e' racchiuso tra DI ed EI.
Lo Z80 offre vari modi per specificare da dove provengono o dove vanno i dati:
Sintassi dell'assemblatore GLASS
- Immediato:
LD A, 42— il valore e' incorporato nei byte dell'istruzione stessa. - Registro:
LD A, B— i dati provengono da o vanno a un registro. - Indiretto (tramite HL):
LD A, (HL)— HL contiene un indirizzo di memoria; i dati vengono letti da quell'indirizzo. - Esteso (indirizzo diretto):
LD A, (0x1200)— l'indirizzo e' una costante letterale a 16 bit nell'istruzione. - Indicizzato:
LD A, (IX+5)— IX contiene un indirizzo base; 5 viene aggiunto per ottenere l'indirizzo effettivo. Usato in RFS per accedere ai campi all'interno di strutture dati a formato fisso.
RFS utilizza l'assemblatore GLASS Z80. Caratteristiche sintattiche principali:
- I commenti iniziano con
;— tutto cio' che si trova a destra di un punto e virgola viene ignorato. - Le label sono identificatori seguiti da
:. Una label all'inizio di una riga nomina l'indirizzo dell'istruzione successiva. EQUdefinisce una costante:BELL EQU 007H— l'assemblatore sostituisce ogni occorrenza di BELL con 0x07.DB(Define Byte) inserisce byte grezzi:DB 0x41, 0x42emette due byte. Usato per stringhe e tabelle di lookup.DW(Define Word) inserisce valori a 16 bit in little-endian:DW HANDLERemette l'indirizzo della label HANDLER.ORG addrimposta l'origine di assemblaggio — il codice successivo viene assemblato come se risiedesse all'indirizzoaddr.INCLUDE "file.asm"include testualmente un altro file nella posizione corrente.IF / ENDIFassemblaggio condizionale:IF BUILD_SFD700 = 1 ... ENDIF— le istruzioni racchiuse vengono assemblate solo quando la condizione e' vera. E' cosi' che RFS costruisce quattro diverse varianti di firmware da un unico albero sorgente.
Albero dei sorgenti
| Percorso | Contenuto |
|---|---|
asm/ |
Tutti i file sorgente assembly Z80 |
asm/include/ |
Definizioni condivise e file di configurazione |
asm/dis/ |
File di riferimento disassemblati per SA-5510 e XPATCH |
tools/ |
Script di build, assemblatore GLASS, binari utility |
MZF/ |
File applicazione in formato MZF, organizzati per tipo di macchina |
MZB/ |
Applicazioni binarie con padding di settore (generate dalla build) |
roms/ |
Output di build — immagini ROM e immagini scheda SD |
releases/ |
Binari di release precostruiti |
config/ |
Definizioni del formato disco CP/M (diskdefs) |
cpmtools/ |
Sorgenti cpmtools (submodule) |
src/ |
Sorgenti per gli strumenti di supporto |
Configurazione: rfs_definitions.asm
Questo e' il file di configurazione centrale, incluso da ogni altro file sorgente tramite
Flag dei target di build
INCLUDE "rfs_definitions.asm". Ogni opzione in fase di assemblaggio e' controllata qui. Le sezioni principali:
HW_SPI_ENA EQU 1 ; 1 = hardware SPI on RomDisk v2+ PCB SW_SPI_ENA EQU 0 ; 1 = software bit-bang SPI (RomDisk v1) PP_SPI_ENA EQU 0 ; 1 = SPI via parallel port (RomDisk v1 alternative) FUSIONX_ENA EQU 0 ; 1 = running on tranZPUter FusionX KUMA80_ENA EQU 0 ; 1 = Kuma 40/80 upgrade present VIDEOMODULE_ENA EQU 0 ; 1 = 40/80 colour video module present BUILD_ROMDISK EQU 0 ; 1 = build for RomDisk card BUILD_SFD700 EQU 0 ; 1 = build for SFD-700 floppy interface BUILD_PICOZ80 EQU 1 ; 1 = build for picoZ80 board ENADEBUG EQU 0 ; 1 = enable debug output during assembly
Esattamente un flag
Costanti di indirizzo
BUILD_* deve essere impostato a 1 alla volta. Tutti i blocchi di assemblaggio condizionale nell'intero sorgente testano questi flag per includere o escludere codice specifico della piattaforma.
UROMADDR EQU 0E800H ; Base address of the User ROM window UROMBSTBL EQU UROMADDR + 020H ; Bank-switch table entry point (fixed offset) RFSJMPTABLE EQU UROMADDR + 0B0H ; RFS jump table start FDCROMADDR EQU 0F000H ; FDC ROM address (SFD-700 MROM location) ; SFD-700 specific bank defaults (only assembled when BUILD_SFD700 = 1): BNKDEFMROM_MZ80A EQU 0 ; Default MROM bank for MZ-80A (AFI ROM) BNKDEFMROM_MZ700 EQU 1 ; Default MROM bank for MZ-700 (AFI ROM) BNKDEFUROM EQU 2 ; Default UROM bank for RFS (starts at 8KB in Flash)
Queste costanti definiscono dove, nello spazio di indirizzamento dello Z80, si trova ciascuna finestra ROM. Il codice compilato per la finestra User ROM viene sempre assemblato con
Definizioni di caratteri e controllo
ORG 0xE800; il codice per la finestra Monitor ROM viene assemblato a ORG 0x0000.
I caratteri di controllo ASCII standard sono definiti come costanti con nome per rendere il sorgente auto-documentante:
BELL EQU 007H ; Terminal bell CR EQU 00DH ; Carriage return LF EQU 00AH ; Line feed CS EQU 00CH ; Clear screen SPACE EQU 020H ; ASCII space DELETE EQU 07FH ; Delete key
La commutazione banchi in dettaglio
La commutazione banchi e' il cuore dell'architettura RFS. Comprenderla e' essenziale prima di modificare qualsiasi file sorgente.
Perche' serve la commutazione banchi
Il Sharp MZ-80A assegna alla User ROM solo 2 KB di spazio di indirizzamento (0xE800-0xEFFF). 2 KB possono contenere solo qualche centinaio di istruzioni — ben lontano da quanto necessario per un filing system, un assemblatore, un disassemblatore, un controller per nastro, un driver per scheda SD e un CBIOS CP/M. La soluzione e' commutare fisicamente quale blocco di 2 KB di un chip Flash da 512 KB e' visibile in quell'intervallo di indirizzi. Memorizzando 12 diversi banchi RFS da 2 KB (banchi 0-11) nel chip Flash e commutandoli su richiesta, RFS ottiene di fatto 24 KB di codice ROM — con ulteriori 4 banchi (12-15) riservati al CBIOS CP/M. Inoltre, tre delle 16 pagine della Monitor ROM (banchi 6, 7 e 9) contengono le tabelle degli opcode dell'assemblatore/disassemblatore Z80 e le stringhe di messaggi RFS, estendendo lo spazio ROM disponibile senza consumare alcuna capacita' della User ROM.
Lo stub di commutazione banchi
Ogni banco inizia con una copia identica dello stub di commutazione banchi che occupa i primi 32 byte del banco (0xE800-0xE81F). Questo stub fornisce:
Formato della tabella comandi (rfs.asm)
- Un gateway di chiamata standard: Qualsiasi banco puo' chiamare qualsiasi routine in qualsiasi altro banco chiamando lo stub con il numero del banco di destinazione e l'indirizzo di destinazione. Lo stub scrive il numero del banco nel latch hardware (tipicamente una scrittura su porta di I/O), poi chiama l'indirizzo richiesto. Il chiamato viene eseguito nel nuovo banco e, al ritorno, lo stub ricommuta al banco originale.
- Un punto di ingresso coerente: Poiche' lo stub si trova a un offset fisso (0xE800 + 0x20 per la tabella di commutazione banchi), il codice nel banco 0 puo' trovare in modo affidabile lo stub nel banco 3 anche se non ha mai visto gli indirizzi interni del banco 3.
Il dispatcher dei comandi del monitor in
rfs.asm usa una tabella comandi compatta. Ogni voce descrive un comando ed e' strutturata come segue:
; One command table entry:
;
; DB FLAGS ; 1 byte: END|MATCH|BANK[5:3]|SIZE[2:0]
; DB "COMMAND" ; SIZE bytes: the command string (no null terminator)
; DW HANDLER_ADDR ; 2 bytes: address of the handler routine in the named bank
;
; Flags byte:
; Bit 7 = 1: End of table marker (last entry).
; Bit 6 = 1: Exact match required (command must be entire input, no trailing chars).
; Bits 5:3 Bank number where HANDLER_ADDR lives (0-11 for RFS, 12-15 for CBIOS).
; Bits 2:0 Length of the command string in bytes.
;
; Example - the ASM command (all builds, lives in bank 6, 3-char string):
CMDTABLE:
DB 000H | 000H | 030H | 003H ; FLAGS: not-end, not-exact, bank 6, length 3
DB "ASM" ; Command string
DW ASM_MAIN ; Handler address in bank 6
Il dispatcher legge la riga di input del monitor, percorre la tabella e, per ogni voce:
- Confronta l'input con la stringa del comando (senza distinzione tra maiuscole e minuscole in alcune build).
- Se c'e' corrispondenza, estrae il numero del banco e l'indirizzo dell'handler dalla voce della tabella.
- Esegue una commutazione al banco di destinazione.
- Chiama l'handler con eventuale input rimanente (parametri) disponibile nel buffer di input del monitor.
CMDTABLE2 per la build SFD-700 e CMDTABLE per la build RomDisk/picoZ80. Entrambe hanno una struttura identica ma contengono insiemi di comandi diversi — in particolare la tabella SFD-700 esclude i comandi della scheda SD (IC, LC, SC, EC, DUC, T2SD, SD2T) poiche' quell'hardware non ha una scheda SD. I comandi ASM e DASM sono presenti in entrambe le tabelle.
Analisi dei moduli
rfs.asm — Dispatcher dei comandi (User ROM Banco 0)
Ruolo: Il punto di ingresso per tutte le funzionalita' RFS. Quando il monitor SA-1510 non riconosce un comando, passa il controllo al punto di ingresso della User ROM a 0xE800. Questo e' sempre il banco 0.
Sezioni principali:
rfs_bank1.asm — Controller del disco floppy (User ROM Banco 1)
- Stub di commutazione banchi (0xE800-0xE81F): Il gateway di chiamata cross-bank descritto sopra. Ogni banco ne ha una copia identica.
- Tabella di commutazione banchi (0xE800 + 0xB0): Una tabella di salto che mappa i numeri dei banchi ai loro indirizzi fisici nella ROM Flash. Modificata all'avvio se l'hardware richiede un indirizzamento dei banchi non sequenziale.
- Tabella comandi (CMDTABLE / CMDTABLE2): L'elenco di tutti i comandi RFS con il loro banco e l'indirizzo dell'handler.
- Ciclo principale del dispatcher: Legge il buffer di input del monitor, percorre la tabella comandi, esegue la commutazione di banco e chiama l'handler. Se nessun comando corrisponde, ritorna al monitor SA-1510 perche' possa stampare l'errore "?".
- Inizializzazione RFS: Al primo ingresso dopo il reset, RFS rileva la piattaforma hardware (dal registro MODE su SFD-700, o dai flag su RomDisk), inizializza la SPI e la scheda SD e imposta l'unita' iniziale a 0.
BUILD_SFD700 = 1, rfs.asm viene assemblato con ORG 0xE000 / ALIGN 0xE300 anziche' ORG 0xE800, perche' l'SFD-700 mappa la sua finestra User ROM a 0xE300-0xEFFF (0xE000-0xE2FF e' riservato all'I/O mappato in memoria dell'MZ-700).
Ruolo: Implementa i comandi del disco floppy — boot da floppy (F / FL), directory floppy (FD) e il salto diretto AFI (f). L'intero insieme di comandi FDC e' assemblato in tutte le build.
Funzioni principali:
rfs_bank2.asm — Controller della scheda SD (User ROM Banco 2)
- FLOPPY (FL): Richiede un numero di unita' (se non fornito sulla riga di comando), inizializza il disco, legge il settore di boot, verifica la firma del disco, estrae le informazioni del programma (nome, indirizzo di caricamento, dimensione, indirizzo di esecuzione), carica il programma in memoria e lo esegue.
- FDDIR (FD): Elenca la directory dei file su un disco floppy. Accetta un numero di unita' opzionale (1-4, default 1). Legge il settore di boot, verifica il formato disco MZ-700, poi scandisce i settori della directory mostrando i nomi dei file, gli indirizzi di caricamento, gli indirizzi di esecuzione e le dimensioni dei file.
- FDCK: Legge il byte a 0xF000 per verificare che la ROM AFI sia presente e non nulla, poi chiama 0xF000 direttamente. E' il comando f (minuscolo) in tutte le build.
- Alla fine del banco, una direttiva
ALIGN 0xF000assicura che l'immagine ROM dell'SFD-700 posizioni la ROM di boot AFI esattamente a 0xF000 nel layout Flash.
Ruolo: Il sottosistema completo della scheda SD — inizializzazione SPI, protocollo dei comandi della scheda SD e le routine di directory e I/O su file SDCFS. Nella build SFD-700, il codice del controller della scheda SD non viene assemblato (l'hardware SFD-700 non ha un'interfaccia per scheda SD); lo slot del banco e' presente nell'immagine ROM ma contiene solo lo stub di commutazione banchi.
Funzioni principali:
rfs_bank3.asm — Utility di memoria (User ROM Banco 3)
- Driver SPI (hardware o software): L'assemblaggio condizionale seleziona tra SPI hardware (usando i registri del controller SPI di RomDisk v2) e SPI software bit-bang (commutando singoli bit di porte di I/O per generare il clock del bus SPI). Il percorso SPI hardware e' sostanzialmente piu' veloce ed e' usato su tutte le schede attuali (
HW_SPI_ENA = 1). - Inizializzazione della scheda SD (SDINIT): Implementa la sequenza di inizializzazione della scheda SD — invia CMD0 (GO_IDLE), CMD8 (SEND_IF_COND), ACMD41 (SD_SEND_OP_COND) per commutare la scheda dalla modalita' SPI allo stato attivo. Gestisce sia le schede SD sia quelle SDHC/SDXC controllando la risposta OCR.
- Lettura settore (SDREAD): Invia CMD17 (READ_SINGLE_BLOCK) con un indirizzo di settore a 32 bit, attende il token di inizio dati (0xFE), poi legge 512 byte in un buffer RAM dello Z80. Usa la modalita' burst dell'SPI hardware dove disponibile.
- Scrittura settore (SDWRITE): Invia CMD24 (WRITE_BLOCK), il token di inizio dati, 512 byte di dati e il CRC. Attende la risposta di scrittura e l'azzeramento del segnale di busy.
- Lettura directory SDCFS (SDDIR): Legge la directory dai primi 8 KB dell'immagine dell'unita' attiva e costruisce una cache di directory residente in RAM usata dai comandi IC, LC, SC ed EC.
- Caricamento file SDCFS (SDLOAD): Dato un numero di file dalla directory, calcola l'indirizzo di settore del blocco da 64 KB del file, legge i byte della dimensione effettiva del file e li carica all'indirizzo Z80 specificato nel campo LOAD ADDR della voce di directory.
- Salvataggio file SDCFS (SDSAVE): Alloca un nuovo slot di directory (o trova una voce esistente con lo stesso nome da sovrascrivere), imposta i campi START SECTOR, SIZE, LOAD ADDR ed EXEC ADDR, poi scrive i dati del file nel blocco da 64 KB appropriato.
Ruolo: Implementa i comandi D (dump esadecimale), M (modifica memoria), CP (copia memoria), IN (lettura porta di I/O) e OUT (scrittura porta di I/O), disponibili in tutte le build. Anche i comandi DUC (dump file scheda SD), T2SD (nastro verso SD) e SD2T (SD verso nastro) sono implementati qui ma vengono assemblati solo per le build RomDisk / picoZ80 — la build SFD-700 li esclude poiche' non e' presente una scheda SD.
Dump esadecimale (D): Legge fino a 20 righe di 16 byte ciascuna dall'intervallo di indirizzi di destinazione. Per ogni riga stampa l'indirizzo esadecimale a 4 cifre, 16 valori di byte esadecimali (con uno spazio ogni 4 byte) e i 16 caratteri ASCII (usando un punto per i byte non stampabili). Il Sharp MZ usa una codifica di caratteri insolita — il banco 5 fornisce la tabella di conversione da Sharp ad ASCII usata qui.
Editor di memoria (M): Presenta ogni byte a turno, mostrando l'indirizzo e il valore corrente. L'utente puo' digitare un nuovo valore esadecimale (1 o 2 cifre) e premere Invio per scriverlo, oppure premere Invio da solo per lasciarlo invariato. Premendo Ctrl+C o una specifica sequenza di escape si esce.
Comandi porta di I/O (IN / OUT): IN legge una o piu' porte di I/O Z80 (indirizzi a 2 o 4 cifre esadecimali, separati da virgola) usando
rfs_bank4.asm — Controller CMT (User ROM Banco 4)
IN A,(C) e stampa ogni valore come esadecimale a 2 cifre. OUT scrive su una o piu' porte — ogni voce e' un indirizzo di porta seguito da due punti e un valore esadecimale a 2 cifre (es. OUTD0:01,D1:80), eseguito con OUT (C),A. Entrambi i comandi supportano l'intero spazio di indirizzi delle porte di I/O Z80 a 16 bit.
T2SD e SD2T: Questi comandi eseguono una copia bidirezionale trasparente tra nastro (CMT) e scheda SD. T2SD chiama la routine di caricamento da CMT del banco 4 per leggere un file da nastro in RAM, poi chiama la routine di salvataggio su SD del banco 2 per scriverlo sull'unita' attiva. SD2T chiama la routine di caricamento da SD del banco 2 per portare il file in RAM, poi chiama la routine di salvataggio su CMT del banco 4 per scriverlo su nastro. Entrambe le direzioni usano la directory SDCFS per mantenere nomi file, dimensioni e indirizzi.
Ruolo: Implementa i comandi per nastro (CMT) L/LT, LTNX, S/ST e V.
Il Sharp MZ-80A usa un'interfaccia per cassette Kansas City Standard a 1200 baud. I byte sono codificati come raffiche di tono a 1200 Hz (bit 0) o 2400 Hz (bit 1). Le routine per nastro sono critiche per il timing — devono leggere o scrivere ogni bit entro una finestra temporale stretta. Usano il chip timer 8253 (o cicli con conteggio dei cicli di CPU su piattaforme prive del timer) per misurare la frequenza del tono in ingresso e generare la forma d'onda in uscita. Gli interrupt vengono disabilitati (
rfs_bank5.asm — Funzioni di utility (User ROM Banco 5)
DI) per tutta la durata delle operazioni su nastro per evitare disturbi al timing.
Il formato nastro MZF antepone a ogni programma un'intestazione di 128 byte contenente il tipo di file, il nome file, la lunghezza dei dati, l'indirizzo di caricamento e l'indirizzo di esecuzione — gli stessi campi memorizzati nella voce di directory SDCFS. E' per questo che la copia SD<->nastro e' trasparente: il formato dell'intestazione e' identico.
Ruolo: Una libreria di routine condivise chiamate dagli altri banchi. Poiche' la commutazione banchi e' costosa (richiede la sequenza di sblocco sulle schede v2+), le routine usate di frequente sono centralizzate qui per ridurre al minimo l'overhead di commutazione.
Routine principali:
rfs_bank6.asm — Tabella opcode ASM/DASM 1 (User ROM Banco 6)
- PRTHEX: Stampa il registro A come due cifre esadecimali sullo schermo.
- PRTHL: Stampa il registro HL come quattro cifre esadecimali.
- PRTSTR: Stampa una stringa terminata da null da (HL) sullo schermo, gestendo la conversione della codifica di caratteri Sharp.
- INPHEX: Legge un numero esadecimale (fino a 4 cifre) dalla tastiera, restituendo il valore in HL.
- STRCMP: Confronta due stringhe terminate da null.
- SUBSTR: Estrae una sottostringa, usata dal dispatcher dei comandi per separare i nomi dei comandi dai parametri.
- WAITKEY: Attende la pressione di un tasto e restituisce il codice del tasto in A. Usata per le pause "premere un tasto per continuare" negli elenchi di directory IC e IR.
Ruolo: Memorizza la prima meta' delle tabelle di lookup degli opcode Z80 usate sia dal comando assemblatore (ASM) sia da quello disassemblatore (DASM), oltre alla funzione PRINTMSG e all'infrastruttura delle stringhe di messaggi. Le tabelle degli opcode mappano le stringhe mnemoniche Z80 ai byte di opcode e viceversa. L'assemblatore e il disassemblatore sono disponibili in tutte le build (RomDisk, picoZ80, SFD-700 e FusionX).
La funzione PRINTMSG legge le stringhe di messaggi dal banco MROM 9 in un buffer RAM prima di stamparle, poiche' le funzioni della Monitor ROM (PRNT, ?DSP, ecc.) richiedono che il banco MROM del monitor sia attivo durante l'output dei caratteri. Anche il corrispondente banco MROM 6 contiene dati delle tabelle degli opcode nello spazio Monitor ROM da 4 KB, fornendo spazio aggiuntivo per l'intero set di istruzioni Z80, comprese tutte le varianti con byte di prefisso (CB, DD, ED, FD).
rfs_bank7.asm — Tabella opcode ASM/DASM 2, DASM, Test (User ROM Banco 7)
Ruolo: Memorizza la seconda meta' delle tabelle di lookup degli opcode Z80, la routine principale del disassemblatore DASM, il test DRAM R e il test del timer T.
Disassemblatore (DASM): Legge i byte di codice macchina dall'indirizzo di destinazione, decodifica ogni istruzione (usando le tabelle degli opcode nei banchi 6 e 7) e stampa l'indirizzo, i byte esadecimali e il mnemonico di ogni istruzione. Gestisce tutti i byte di prefisso Z80 (CB, DD, ED, FD) e le istruzioni estese.
Test DRAM (R): Esegue un pattern di scrittura/verifica a bit mobile su tutto lo spazio di RAM utente (0x1200-0xCFFF). Segnala gli eventuali indirizzi falliti. Utile per diagnosticare chip di RAM difettosi — un modo di guasto comune nelle macchine d'epoca.
rfs_bank8.asm — Assemblatore Z80 (User ROM Banco 8)
Ruolo: Implementa il comando dell'assemblatore interattivo ASM.
Assemblatore interattivo (ASM): Presenta un prompt di input di riga all'indirizzo di destinazione. L'utente digita mnemonici Z80 (es.
rfs_bank9.asm — Funzioni di directory e file ROM (User ROM Banco 9)
LD A, 42) che vengono analizzati, assemblati in byte di codice macchina e scritti direttamente all'indirizzo di destinazione in RAM. L'indirizzo avanza della dimensione di ogni istruzione assemblata. Questo consente di assemblare piccole routine direttamente sull'hardware senza un PC esterno. Le tabelle di lookup degli opcode nei banchi 6 e 7 (e nei banchi MROM 6 e 7) vengono usate per la traduzione da mnemonico a opcode.
Ruolo: Contiene le funzioni di enumerazione della directory ROM, ricerca file, caricamento file e stampa, che sono state spostate dal banco 0 (il banco del dispatcher dei comandi) per liberare spazio nel banco 0 per ulteriori voci della tabella comandi e infrastruttura. Le funzioni principali includono DIRROM9 (elenco della directory ROM), FINDSDX9 (ricerca file), ISMZF9 (validazione dell'intestazione MZF) e _PRTMZF9 (visualizzazione voce MZF).
rfs_bank11.asm — Schermata di aiuto (User ROM Banco 11)
Ruolo: Memorizza il testo della schermata di aiuto paginata (spostato dal banco 6 per fare spazio alle tabelle degli opcode). La schermata di aiuto e' memorizzata come una sequenza di stringhe terminate da null (una per riga). Il comando H le stampa con paginazione automatica, chiamando la routine WAITKEY del banco 5 a ogni confine di schermata.
rfs_mrom.asm — Utility Monitor ROM (Monitor ROM Banco 3)
Ruolo: Fornisce le routine di scansione ROM e caricamento di file MZF che devono essere eseguite dallo spazio Monitor ROM anziche' dallo spazio User ROM.
Perche' un banco Monitor ROM separato? I comandi IR e LR devono enumerare i file MZF memorizzati nei chip Flash della User ROM (i banchi User ROM oltre il 15 contengono programmi MZF impacchettati). Per scandire un banco User ROM, il CPLD deve commutare la finestra User ROM in modo che punti a quel banco. Ma il codice di scansione stesso risiede nella User ROM — se commuta il banco User ROM, sostituisce istantaneamente se' stesso con un altro banco e va in crash.
La soluzione e' collocare il ciclo di scansione nel banco Monitor ROM 3. La commutazione dei banchi Monitor ROM e' indipendente da quella dei banchi User ROM. La routine di scansione MROM puo' commutare liberamente i banchi User ROM (per enumerare l'elenco delle intestazioni MZF di ciascun banco) senza disturbare il proprio contesto di esecuzione.
Funzioni principali:
Moduli CBIOS CP/M
- ROMDIR: Scandisce tutti i banchi User ROM oltre il 15, legge ogni intestazione MZF e costruisce una directory ROM residente in RAM usata dal comando IR.
- ROMLOAD: Dato un numero di file dalla directory ROM, legge i dati MZF dal banco User ROM appropriato all'indirizzo LOAD ADDR specificato nell'intestazione, poi opzionalmente salta a EXEC ADDR.
I quattro banchi User ROM del CBIOS (12-15) e il banco Monitor ROM del CBIOS (2) implementano insieme il CBIOS CP/M 2.2 completo. Ogni banco fornisce un sottosistema:
cbios.asm (Monitor ROM banco 2): La tabella dei punti di ingresso del CBIOS — tutti i 17 vettori dell'API CP/M (da BOOT a SECTRN) sono indirizzi di salto all'interno di questo modulo. Contiene anche le tabelle dei parametri del disco (strutture DPH, DPB che indicano a CP/M la geometria di ogni unita' disco), le sequenze di cold/warm boot e il controller del disco ROM (legge i settori dalla Flash User ROM).
cbios_bank1.asm (User ROM banco 12): Uscita audio (tono campanello e melodia usando l'8255 PPI e il buzzer dell'MZ-80A), routine dell'orologio in tempo reale che usano il timer 8253, e input da tastiera con auto-ripetizione (un tasto tenuto premuto per > 500 ms si ripete a ~10 Hz, in linea con il comportamento che gli utenti si aspettano da una tastiera moderna).
cbios_bank2.asm (User ROM banco 13): Driver dello schermo — output di caratteri alla posizione corrente del cursore, scorrimento, pulizia schermo, posizionamento del cursore. Anche l'emulatore di terminale ANSI: una macchina a stati che riconosce le sequenze di escape VT52/VT100 (codici CSI) e le traduce nelle equivalenti operazioni di schermo dell'MZ-80A. Questo fa funzionare correttamente le applicazioni CP/M che presuppongono un terminale intelligente (WordStar, Turbo Pascal, ecc.) senza alcuna modifica a tali applicazioni.
cbios_bank3.asm (User ROM banco 14): Driver del disco su scheda SD per CP/M. Traduce le richieste di lettura/scrittura di settori da 128 byte di CP/M in operazioni SDCFS sulle immagini disco CP/M memorizzate dopo il confine dei 256 MB sulla scheda SD. Include la tabella di skew dei settori usata da SECTRN per migliorare le prestazioni di accesso al disco.
cbios_bank4.asm (User ROM banco 15): Controller del disco floppy per CP/M. Usa il WD1773 (tramite SFD-700) o un controller floppy equivalente per servire le richieste di lettura/scrittura disco CP/M per le unita' floppy fisiche. I dati vengono letti non invertiti (a differenza della MZ-80A AFI ROM che usa dati invertiti) perche' il CBIOS scrive il proprio formato re-invertito. Quando pilotato tramite la scheda MZ80AFI, interroga il report sulla dimensione del settore del controller (porta 0xDF) per distinguere i dischi MZ-80A da 256 byte dai dischi MZ-80K da 128 byte, e puo' avviare/leggere dischi CP/M MZ-80K originali (non convertiti) direttamente — identificati dal marcatore di boot non invertito
01h + IPLPRO — mappandoli sulle unita' CP/M C:/D: (gli helper ?SETDRVMAP / ?SELDRIVE assegnano le unita' floppy fisiche alle lettere di unita' CP/M appropriate al cold boot).
Aggiungere un nuovo comando del monitor
Per aggiungere un nuovo comando FOO che risiede nel banco User ROM 3 (utility di memoria):
- Scrivere l'handler in rfs_bank3.asm: Aggiungere una routine con label
FOO_CMD:che implementa il comando. I parametri sono disponibili nel buffer di input del monitor (HL punta al primo carattere dopo il nome del comando). Terminare conRETquando finito. - Aggiungere una voce alla tabella comandi in rfs.asm: Nella
CMDTABLEappropriata (oCMDTABLE2per SFD-700), aggiungere:
; FLAGS: not-end (bit7=0), not-exact (bit6=0), bank 3 (bits5:3 = 011 = 0x18), length 3 (bits2:0 = 011)
DB 000H | 000H | 018H | 003H
DB "FOO"
DW FOO_CMD
- Aggiornare il testo di aiuto in rfs_bank6.asm: Aggiungere una riga che descrive il nuovo comando alle stringhe di testo della schermata di aiuto.
- Ricompilare: Eseguire
./build.sh. L'assemblatore segnalera' eventuali overflow di dimensione se il banco 3 e' ora piu' grande di 2 KB — in tal caso, rimuovere o comprimere altro codice in quel banco.
Aggiungere una nuova piattaforma hardware
Per portare RFS su un nuovo target hardware:
- Aggiungere un flag di build in
rfs_definitions.asm:BUILD_NEWBOARD EQU 0(impostare a 1 quando si compila per il nuovo target). - Aggiungere costanti di indirizzo se il nuovo hardware mappa le finestre ROM a indirizzi diversi.
- Aggiungere blocchi di assemblaggio condizionale nell'intero sorgente dove il comportamento specifico dell'hardware differisce — indirizzi delle porte di I/O per il latch dei banchi, layout dei registri del controller SPI, rilevamento della presenza della scheda SD, ecc. Seguire lo schema dei blocchi
IF BUILD_ROMDISK = 1 ... ENDIFesistenti. - Aggiungere un nuovo script di build (o estendere
make_roms.sh) per impacchettare l'immagine ROM secondo il layout del chip Flash del nuovo target. - Aggiornare il controllo dei prerequisiti in
build.shse il nuovo target necessita di strumenti aggiuntivi.
Suggerimenti per il debug
Abilitare l'output di debug: Impostare
ENADEBUG EQU 1 in rfs_definitions.asm prima di compilare. Questo include output diagnostico aggiuntivo in punti strategici dell'inizializzazione della scheda SD e delle routine SDCFS.
Usare il disassemblatore integrato: Sulle build SFD-700, digitare DASM addr per disassemblare il codice assemblato in RAM. E' prezioso per verificare che una routine appena assemblata sia stata codificata correttamente dal comando ASM.
Usare il dump esadecimale: D E800 mostra i primi 320 byte del banco User ROM corrente — utile per verificare che il latch dei banchi stia selezionando il banco previsto.
Test DRAM dopo ogni modifica della RAM: Digitare R per eseguire un test DRAM completo ogni volta che si aggiungono nuove strutture dati residenti in RAM. Questo intercetta i bug di indirizzamento per tempo, prima che si manifestino come crash misteriosi.
Protezione della commutazione banchi: Sulle schede RomDisk v2+, non collocare mai un DJNZ o qualsiasi altra istruzione di ciclo che attraversi 0xEFF8-0xEFFF. Il latch codificato interpreta le letture da quell'intervallo come la sequenza di sblocco e potrebbe commutare i banchi in modo imprevisto.
Ambiente di build
La toolchain di build di RFS e' semplice — per i componenti assembly Z80 sono richiesti solo un runtime Java e l'assemblatore GLASS Z80. Lo script di build globale FusionX
Prerequisiti
build.sh automatizza l'intero processo.
# Install Java runtime (required for the GLASS assembler) sudo apt install -y default-jre git # Clone the repository git clone https://git.eaw.app/eaw/tzpuFusionX.git cd tzpuFusionX # Initialise git submodules git submodule update --init --recursive
Il file JAR dell'assemblatore GLASS Z80 e' incluso nel repository in
Configurazione e compilazione automatizzate (consigliato)
software/tools/glass-0.5.1.jar — non e' necessario alcun download separato. Funziona qualsiasi runtime Java 8 o successivo.
Il modo consigliato per compilare RFS e' lo script di configurazione autonomo per la propria piattaforma. Installa i prerequisiti (un JRE Java per l'assemblatore GLASS Z80, una toolchain C +
make per compilare il sottomodulo cpmtools incluso, oltre a perl/git/coreutils; su Windows, Git Bash + Java), clona il repository, scarica il bundle di contenuti da ~110 MB (RFS_Files.zip), scrive il file d'ambiente e puo' eseguire la prima compilazione — il tutto in modo interattivo, con valori predefiniti sensati che si possono accettare premendo Enter. E' sufficiente copiare il singolo file per la propria piattaforma ed eseguirlo.
macOS / Linux / WSL — setup_RFS.sh
chmod +x setup_RFS.sh
./setup_RFS.sh
Windows 10 / 11 — setup_RFS_windows_native.ps1 (consigliato — nativo, senza WSL). Da un prompt PowerShell:
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
.\setup_RFS_windows_native.ps1
Lo script nativo usa winget per installare Git for Windows (che fornisce bash, coreutils, perl e curl) e un JRE Temurin 17 (Java), clona il repository, scarica il bundle di contenuti ed esegue
./build.sh tramite Git Bash — senza WSL, Docker o riavvii. Non e' richiesto alcun compilatore C poiche' due strumenti host precompilati (tools\cpmcp.exe e tools\sdtool.exe) sono inclusi.
Windows 10 / 11 — setup_RFS_windows.ps1 (alternativa — compila all’interno di WSL2 / Ubuntu). Da una PowerShell come Amministratore:
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
.\setup_RFS_windows.ps1
Questa variante WSL installa WSL2 + Ubuntu se non sono gia' presenti (e' necessario un riavvio una tantum — riavviare, avviare Ubuntu una volta per creare il proprio utente Linux, quindi rieseguire lo script), poi esegue
setup_RFS.sh all'interno di Ubuntu.
Impostare la variabile d'ambiente
RFS_REPO_URL per sovrascrivere l'URL del repository predefinito (https://git.eaw.app/eaw/RFS.git) senza modificare lo script, e RFS_FILES_URL per puntare a un bundle di contenuti alternativo.
Output e ricompilazione. Le immagini ROM e le immagini della scheda SD vengono scritte in
Compilazione
roms/. Per ricompilare in seguito, entrare nel checkout; su macOS eseguire prima source ./rfs_env.sh per aggiungere GNU coreutils e il JRE al PATH; quindi eseguire ./build.sh -m per la prima compilazione (elabora i sorgenti MZF in MZB/) e ./build.sh per le compilazioni successive. Su Windows (nativo), eseguire gli stessi comandi ./build.sh da Git Bash all'interno del checkout.
# Build all Z80 assembly ROMs and MZF files (includes RFS) ./build.sh --asm # Build output appears in software/roms/ ls software/roms/*.rom software/roms/*.mzf
Lo script di build assembla ogni target ROM e MZF usando l'assemblatore GLASS, passando i percorsi di include e i flag di build appropriati. Le varianti di MS BASIC (MZ-80A, MZ-700, TZ40, TZ80) sono costruite dallo stesso file sorgente con diversi flag
BUILD_VERSION impostati tramite file di include generati.
Output di build:
| Output | Descrizione |
|---|---|
monitor_sa1510.rom |
ROM del monitor SA-1510 |
monitor_80c_sa1510.rom |
Monitor SA-1510 con supporto 80 colonne |
monitor_1z-013a.rom |
ROM del monitor 1Z-013A |
mz80afi.rom |
ROM MZ-80A AFI (interfaccia floppy) |
mz2000_ipl_*.rom |
ROM IPL MZ-2000 (originale, TZPU, FusionX) |
mz800_*.rom |
ROM di sistema MZ-800 |
msbasic_*.mzf |
MS BASIC per ogni target |
sa-5510_tzfs.mzf |
SA-5510 BASIC con supporto TZFS |
sharpmz-test.mzf |
MZF di test hardware |
Cos'e' il CI/CD? L'integrazione continua (CI) e' una pratica in cui un server dedicato compila automaticamente il progetto ogni volta che si inviano modifiche al codice. Invece di eseguire manualmente l'assemblatore sulla propria macchina di sviluppo, impacchettare i file ROM e caricarli su una pagina di download, un server CI fa tutto questo automaticamente. Se la build si interrompe — ad esempio a causa di un errore di sintassi o di un file di include mancante — si riceve immediatamente una notifica via email. Questo intercetta i problemi per tempo e garantisce che ogni release pubblicata sia stata costruita da un punto di partenza pulito e riproducibile.
Le ROM RFS sono costruite come parte della pipeline Jenkins CI di FusionX. Jenkins e' un popolare server di automazione open source che gira su un VPS (Virtual Private Server) o su qualsiasi macchina Linux. Sorveglia il repository Gitea per gli invii al ramo
Come funziona
master e attiva automaticamente una build completa di tutti i componenti del progetto.
Il processo di build automatizzato per RFS segue questi passi:
- Si invia il codice al ramo
masterdel repository Gitea. - Gitea invia un webhook (una notifica HTTP) al server Jenkins.
- Jenkins clona il repository in un workspace nuovo e pulito.
- Jenkins esegue
./build.sh --asmche assembla tutte le ROM del monitor Z80 e i file MZF usando l'assemblatore GLASS. - Jenkins impacchetta i file ROM e MZF assemblati in un tarball versionato (
FusionX-ROMs-v1.08.tar.gz). - Jenkins crea una Release Gitea con il tarball allegato come asset scaricabile.
- Jenkins invia un'email che riporta successo o fallimento.
L'intero processo richiede circa un minuto e non necessita di alcun intervento manuale dopo l'invio iniziale.
Configurazione di Jenkins
Jenkins gira all'interno di un container Docker per facilitarne l'installazione e la portabilita'. I requisiti minimi sono un server Linux con 2 GB di RAM, Docker installato e accesso di rete al repository Gitea. Sul server:
# Install Docker (Debian/Ubuntu) sudo apt update && sudo apt install -y docker.io docker-compose sudo systemctl enable docker && sudo systemctl start docker # Create the Jenkins directory sudo mkdir -p /srv/jenkins/data cd /srv/jenkins
Creare un file
docker-compose.yml:
# /srv/jenkins/docker-compose.yml
version: '3.8'
services:
jenkins:
image: jenkins/jenkins:lts
ports:
- "8080:8080"
volumes:
- /srv/jenkins/data:/var/jenkins_home
environment:
- JAVA_OPTS=-Djenkins.install.runSetupWizard=false
restart: unless-stopped
# Start Jenkins docker-compose up -d # Get the initial admin password (first run only) docker-compose logs jenkins | grep "initial admin password" -A 2 # Open http://your-server:8080 in a browser
Al primo avvio, Jenkins richiede la password di amministratore mostrata nei log. Dopo l'accesso, installare i "plugin suggeriti" e poi aggiungere il plugin Generic Webhook Trigger tramite Manage Jenkins -> Plugins -> Available.
Creazione della pipeline
Un job "Pipeline" di Jenkins e' definito da uno script Groovy che indica a Jenkins esattamente quali comandi eseguire. Per creare una pipeline di build RFS:
- Fare clic su New Item nella dashboard di Jenkins.
- Inserire un nome (es.
RFS-Build), selezionare Pipeline, fare clic su OK. - Nella sezione Pipeline, impostare Definition su "Pipeline script" e incollare questo:
pipeline {
agent any
environment {
GITEA_URL = "https://git.eaw.app"
REPO_URL = "https://git.eaw.app/eaw/tzpuFusionX.git"
GITEA_TOKEN = credentials('gitea-api-token')
GITEA_OWNER = "eaw"
GITEA_REPO = "tzpuFusionX"
}
triggers {
GenericTrigger(
genericVariables: [[key: 'ref', value: '$.ref']],
causeString: 'Triggered by Gitea push to $ref',
token: 'rfs-build-trigger',
regexpFilterText: '$ref',
regexpFilterExpression: '^refs/heads/(main|master)$'
)
}
stages {
stage('Checkout') {
steps {
cleanWs()
git url: "${REPO_URL}", branch: 'master'
sh 'git submodule update --init --recursive'
}
}
stage('Build Assembly ROMs') {
steps {
sh 'chmod +x build.sh && mkdir -p software/tmp software/roms'
sh './build.sh --asm'
}
}
stage('Package') {
steps {
script {
def ver = readFile('VERSION').trim()
sh "cd software/roms && tar czf ../../FusionX-ROMs-v${ver}.tar.gz *.rom *.mzf"
archiveArtifacts artifacts: "FusionX-ROMs-v${ver}.tar.gz"
}
}
}
}
post {
success { mail to: 'your-email@example.com', subject: "RFS Build - SUCCESS", body: "Build completed." }
failure { mail to: 'your-email@example.com', subject: "RFS Build - FAILED", body: "Check console output." }
always { cleanWs() }
}
}
Questa e' una pipeline semplificata che costruisce solo le ROM assembly Z80. Per la pipeline FusionX completa — che costruisce anche TZFS, CP/M, moduli del kernel, bitstream CPLD e crea le release Gitea — vedere la sezione Guida per sviluppatori FusionX — Integrazione continua. Quella guida copre anche la configurazione del cross-compilatore ARM, l'installazione di Quartus tramite Docker, la traduzione dei percorsi tra container fratelli e lo script completo della pipeline.
Webhook Gitea
Un webhook indica a Gitea di notificare Jenkins ogni volta che viene inviato del codice. Nel repository Gitea, andare su Settings -> Webhooks -> Add Webhook -> Gitea e impostare:
- Target URL:
http://your-server:8080/generic-webhook-trigger/invoke?token=rfs-build-trigger - Content Type:
application/json - Trigger On: Push Events
Dopo aver salvato, inviare un commit a
master e controllare Jenkins — una nuova build dovrebbe comparire automaticamente. Fare clic sul numero della build e poi su Console Output per seguire l'avanzamento in tempo reale.
Link di riferimento
| Risorsa | Link |
|---|---|
| Pagina del progetto RFS | /it/sharpmz-upgrades-rfs/ |
| Manuale utente RFS | /it/sharpmz-upgrades-rfs-usermanual/ |
| Guida tecnica RFS | /it/sharpmz-upgrades-rfs-technicalguide/ |
| Galleria RFS | /it/sharpmz-upgrades-rfs-gallery/ |
| Guida per sviluppatori SFD-700 mkII | /it/sfd700-developersguide/ |
| Guida per sviluppatori picoZ80 | /it/picoz80-developersguide/ |
| Guida per sviluppatori FusionX | /it/tranzputer-fusionx-developersguide/ |
| Assemblatore GLASS Z80 | Incluso in tools/glass-0.5.1.jar |
| Manuale CPU Zilog Z80 | Datasheet standard — timing del bus, set di istruzioni, riferimento dei registri |
| Guida di adattamento CP/M 2.2 | Digital Research — riferimento di progettazione CBIOS |