picoZ80 Guida Tecnica
picoZ80 Guida Tecnica
Questa guida documenta l'architettura hardware del picoZ80, l'interfaccia bus PIO dell'RP2350, il modello di memoria, il riferimento per la configurazione JSON, il framework dei dispositivi virtuali e le procedure di debug. E' destinata agli sviluppatori che desiderano comprendere i meccanismi interni, scrivere nuovi driver, portare il firmware su una nuova macchina host o risolvere problemi a livello firmware.
Per la configurazione utente e l'utilizzo dell'interfaccia web, consultare il
picoZ80 User Manual. Per una panoramica del progetto e le istruzioni di compilazione, consultare la
pagina del progetto picoZ80.
Architettura Hardware
Il picoZ80 integra cinque sottosistemi su un singolo PCB compatto, progettato per rientrare nell'ingombro di un package DIP-40. Tutta la logica opera a 3,3V; l'interfaccia bus Z80 gestisce la traslazione dei livelli e il pilotaggio di corrente per il bus host a 5V.
Schema a Blocchi del Sistema
┌─────────────────────────────────────────────────────────────────────────┐
│ picoZ80 PCB │
│ │
│ ┌────────────────────────────┐ ┌──────────────────────────────┐ │
│ │ RP2350B │ │ ESP32-S3 │ │
│ │ (Cortex-M33, dual core) │ │ │ │
│ │ │ │ ┌──────┐ ┌──────────────┐ │ │
│ │ Core 0: USB, file I/O, │◄────►│ │ SD │ │ Web Server │ │ │
│ │ ESP32 relay │ FSPI │ │ Card │ │ (Bootstrap) │ │ │
│ │ Core 1: Z80 bus hot loop │ UART │ └──────┘ └──────────────┘ │ │
│ │ │ │ │ │
│ │ PIO 0,1,2: bus interface │ │ WiFi ─── 802.11 b/g/n AP │ │
│ │ │ │ or Client mode │ │
│ │ 16MB SPI Flash │ └──────────────────────────────┘ │
│ │ 8MB PSRAM (SPI) │ │
│ └────────────────────────────┘ │
│ │ │
│ ┌────────┴────────┐ │
│ │ Z80 Bus Interface│ │
│ │ (40-pin DIP out) │ │
│ └────────┬────────┘ │
│ │ 5V bus (A0–A15, D0–D7, MREQ, IORQ, RD, WR...) │
└────────────────┼────────────────────────────────────────────────────────┘
│
┌───────┴───────┐
│ Host Z80 │
│ DIP-40 socket│
│ (legacy │
│ computer) │
└───────────────┘
Componenti Principali
| Componente |
Dispositivo |
Funzione |
| MCU Primario | RP2350B (QFN-80) | Dual Cortex-M33, 150MHz (fino a 300MHz OC), 512KB SRAM, 12 macchine a stati PIO, 48 pin GPIO |
| Flash | W25Q128 (16MB SPI) | Bootloader, doppio slot firmware, partizioni di configurazione |
| PSRAM | 8MB SPI PSRAM | 64 × 64KB banchi RAM/ROM per lo spazio di indirizzamento Z80 |
| Coprocessore | ESP32-S3-PICO-1 | WiFi, scheda SD, web server, OTA |
| Hub USB | CH334F | Hub USB, bridging per aggiornamento firmware |
| Alimentazione | TLV62590BV | Convertitore buck sincrono 5V → 3,3V |
Assegnazione GPIO RP2350B
Il package RP2350B QFN-80 fornisce 48 pin GPIO. Il picoZ80 utilizza praticamente tutti i pin. L'assegnazione e' fissata nel design della scheda e riflessa nei programmi PIO:
| Intervallo GPIO |
Segnali |
Direzione |
| GPIO 0–15 |
A0–A15 (Bus Indirizzi Z80) |
Uscita (pilotato da PIO) |
| GPIO 16–23 |
D0–D7 (Bus Dati Z80) |
Bidirezionale (PIO tri-state) |
| GPIO 24 |
MREQ |
Uscita |
| GPIO 25 |
IORQ |
Uscita |
| GPIO 26 |
RD |
Uscita |
| GPIO 27 |
WR |
Uscita |
| GPIO 28 |
M1 |
Uscita |
| GPIO 29 |
RFSH |
Uscita |
| GPIO 30 |
BUSREQ |
Ingresso |
| GPIO 31 |
BUSACK |
Uscita |
| GPIO 32 |
HALT |
Uscita |
| GPIO 33 |
INT |
Ingresso |
| GPIO 34 |
NMI |
Ingresso |
| GPIO 35 |
WAIT |
Uscita |
| GPIO 36 |
CLK |
Ingresso (clock host) |
| GPIO 37 |
RESET |
Ingresso |
| GPIO 38–41 |
ESP32 FSPI (CS, CLK, MOSI, MISO) |
SPI |
| GPIO 42–43 |
ESP32 UART (TX, RX) |
UART |
| GPIO 44–45 |
PSRAM SPI |
SPI |
| GPIO 46–47 |
USB (D+, D–) |
USB |
Architettura Firmware
Il firmware dell'RP2350 e' costruito con il Raspberry Pi Pico SDK 2.x per la piattaforma RP2350-arm-s. Il firmware e' suddiviso in due eseguibili indipendenti: il Bootloader e l'Applicazione.
Layout della Memoria Flash
| Partizione |
Intervallo Indirizzi |
Dimensione |
Contenuti |
| Bootloader |
0x10000000 – 0x1001FFFF |
128KB |
Bridge USB, aggiornamento firmware, selettore partizione |
| App Slot 1 |
0x10020000 – 0x1051FFFF |
5MB |
Firmware Z80 — applicazione attiva (slot 1) |
| App Slot 2 |
0x10520000 – 0x10A1FFFF |
5MB |
Firmware Z80 — applicazione attiva (slot 2) |
| App Config 1 |
0x10A20000 – 0x10C9FFFF |
2,5MB |
Immagini ROM + JSON di configurazione minificato (slot 1) |
| App Config 2 |
0x10CA0000 – 0x10F1FFFF |
2,5MB |
Immagini ROM + JSON di configurazione minificato (slot 2) |
| General Config |
0x10F20000 – 0x10FFEFFF |
892KB |
Impostazioni principali, spazio di lavoro |
| Partition Table |
0x10FFF000 – 0x11000000 |
4KB |
Numero slot attivo, checksum, metadati |
Responsabilita' Dual-Core
I due core Cortex-M33 hanno responsabilita' completamente separate e comunicano tramite una coda di messaggi inter-core (queue_t). Questa separazione garantisce che il lavoro non in tempo reale sul Core 0 non introduca mai jitter nelle transazioni bus Z80 sul Core 1.
| Core |
Responsabilita' |
| Core 0 |
Bridge USB CDC-seriale; coordinamento aggiornamento firmware; I/O file (inoltrato all'ESP32 via UART); invio comandi ESP32 (cambi immagine disco, ricaricamenti configurazione, query versione); gestione partizioni; invio messaggi inter-core. |
| Core 1 |
Hot loop di emulazione del bus Z80 — esegue in modo esclusivo. Gestisce i FIFO PIO, risolve ogni transazione bus rispetto alla mappa di memoria e inoltra a: hardware host fisico (PHYSICAL), PSRAM (RAM/ROM) o handler di dispositivo virtuale (FUNC). Il loop interno e' posizionato in SRAM. |
Interfaccia Bus PIO
L'interfaccia bus Z80 e' implementata interamente in assembly PIO dell'RP2350 (
z80.pio). L'RP2350 fornisce tre blocchi PIO (PIO 0, PIO 1, PIO 2) ciascuno con quattro macchine a stati — dodici macchine a stati in totale, delle quali il firmware Z80 le utilizza tutte e dodici.
I programmi PIO vengono eseguiti indipendentemente dai core Cortex-M33. L'interfaccia bus continua a rispondere in modo deterministico anche quando il Core 1 e' impegnato con accessi alla PSRAM o chiamate a funzioni di dispositivi virtuali. Le macchine a stati comunicano tramite flag IRQ PIO anziche' tramite polling, eliminando la latenza inter-macchina.
Tabella Programmi PIO
| PIO |
Macchina a Stati |
Programma |
Funzione |
| 0 |
SM 0 |
z80_addr |
Emette l’indirizzo a 16 bit (A0–A15) sul bus e segnala l’inizio del ciclo a SM 2. |
| 0 |
SM 1 |
z80_data |
Pilota o campiona D0–D7 con controllo tri-state; rilasciato durante BUSRQ. |
| 0 |
SM 2 |
z80_cycle |
Sequenziatore del ciclo bus di livello superiore — orchestra i cicli di fetch, lettura, scrittura, I/O e refresh DRAM. |
| 0 |
SM 3 |
z80_fetch |
Ciclo di fetch opcode (M1 + MREQ + RD). |
| 1 |
SM 0 |
z80_mem_read |
Ciclo di lettura memoria (MREQ + RD). |
| 1 |
SM 1 |
z80_mem_write |
Ciclo di scrittura memoria (MREQ + WR). |
| 1 |
SM 2 |
z80_io_read |
Ciclo di lettura I/O (IORQ + RD). |
| 1 |
SM 3 |
z80_io_write |
Ciclo di scrittura I/O (IORQ + WR). |
| 2 |
SM 0 |
z80_busrq |
Gestisce BUSREQ/BUSACK; rilascia /IORQ, /MREQ, /RFSH, /M1, /HALT, /WR, /RD. |
| 2 |
SM 1 |
z80_nmi |
Rileva l’asserzione NMI e segnala il Core 1. |
| 2 |
SM 2 |
z80_clk_sync |
Sincronizza le macchine a stati PIO con il segnale CLK dello Z80 host. |
| 2 |
SM 3 |
z80_int_ack |
Gestisce i cicli di acknowledge degli interrupt (M1 + IORQ). |
Convenzioni Segnali IRQ PIO
La comunicazione inter-macchina a stati utilizza i flag IRQ PIO. Il Core 1 monitora questi flag nell'hot loop per intraprendere azioni su ogni evento del bus:
| IRQ |
Evento |
| IRQ 0 |
Indirizzo valido / inizio ciclo — e’ iniziato un nuovo ciclo bus e A0–A15 sono stabili. |
| IRQ 1 |
Fase dati — la direzione del bus dati e’ stata risolta; D0–D7 devono essere pilotati o campionati. |
| IRQ 2 |
T1 rilevato — il fronte di salita di T1 nel ciclo corrente. Utilizzato per sincronizzare le operazioni interne con il clock host. |
| IRQ 3 |
Evento RESET — la linea RESET dell’host e’ stata asserita. Il Core 1 deve reinizializzare lo stato di emulazione. |
| IRQ 4 |
NMI rilevato — linea NMI dell’host asserita. |
| IRQ 6 |
BUSRQ attivo — l’host ha asserito BUSREQ; il PIO sta rilasciando il bus. |
Generazione Wait State
Il programma PIO
z80_wait in PIO 2 SM 0 inserisce wait state T-cycle configurabili sul bus host asserendo
/WAIT. Il numero di wait state aggiuntivi e' controllato per ogni blocco di memoria o I/O tramite il parametro
tcycwait in
config.json.
I wait state sono necessari quando l'RP2350 necessita di tempo aggiuntivo per completare un accesso alla PSRAM o una chiamata a funzione di dispositivo virtuale prima di presentare i dati al bus host. Il parametro
tcycsync abilita la sincronizzazione T1 (
z80_sync in PIO 2 SM 1), che blocca la finestra di accesso alla PSRAM sul fronte di salita T1 di ogni ciclo bus, prevenendo derive temporali nelle applicazioni che dipendono dal clock host per temporizzazioni precise (cassetta, bit-banging seriale).
Architettura PIO — Come Vengono Ricreati i Cicli Bus
Il sottosistema Programmable I/O (PIO) dell'RP2350 e' la tecnologia chiave che permette al picoZ80 di ricreare la temporizzazione del bus Z80 con precisione al ciclo. Comprendere come le macchine a stati PIO lavorano insieme e' essenziale per chiunque modifichi l'interfaccia bus o esegua il debug di problemi di temporizzazione.
Fondamenti PIO dell'RP2350
Ogni blocco PIO contiene quattro macchine a stati (SM) indipendenti che eseguono piccoli programmi da una memoria di istruzioni condivisa di 32 posizioni. Le macchine a stati funzionano indipendentemente dai core Cortex-M33 alla frequenza del clock di sistema (fino a 300 MHz). Risorse PIO chiave utilizzate dal picoZ80:
- TX FIFO — una coda a 4 elementi dalla CPU alla macchina a stati. Il codice C sul Core 1 inserisce dati (indirizzi, parole di controllo, istruzioni iniettate) nel FIFO; il programma PIO li preleva usando
out o pull.
- RX FIFO — una coda a 4 elementi dalla macchina a stati alla CPU. Il PIO inserisce i campioni del bus dati nel FIFO usando
in; il Core 1 li legge dopo il completamento di ogni ciclo bus.
- Flag IRQ — 8 flag (IRQ 0–7) condivisi tra tutte le macchine a stati all'interno di un blocco PIO. I flag possono anche essere visti tra blocchi PIO diversi (IRQ 0–3 in un blocco corrispondono a IRQ 4–7 nei blocchi adiacenti). Le macchine a stati usano
irq set / irq wait / irq clear per sincronizzarsi tra loro e con il codice C.
- Registri scratch X e Y — due registri a 32 bit per SM utilizzati per contatori di loop e valori temporanei.
out exec — un'istruzione speciale che preleva un valore dal TX FIFO e lo esegue come istruzione PIO. Questo e' il meccanismo con cui il codice C controlla dinamicamente le sequenze dei cicli bus (vedi sotto).
set pins / out pins — pilotano direttamente i pin GPIO. set usa un valore immediato a 5 bit; out trasferisce dati dall'output shift register (OSR) ai pin.
in pins — campiona i pin GPIO nell'input shift register (ISR), poi esegue auto-push nell'RX FIFO.
wait gpio — blocca la SM finche' un pin GPIO specifico raggiunge un livello specificato. Utilizzato estensivamente per sincronizzarsi con il segnale clock dello Z80 host.
- Side-set — permette di pilotare uno o due pin GPIO come effetto collaterale di qualsiasi istruzione, senza utilizzare un ciclo di istruzione. Il picoZ80 usa side-set a 2 bit per controllare
/RD e /WR simultaneamente con altre operazioni.
- JMP PIN — salto condizionale basato sul livello di un pin GPIO designato. Utilizzato per verificare
/WAIT, BUSREQ, /NMI e /RESET.
Il Meccanismo out exec — Iniezione Dinamica di Istruzioni
La caratteristica piu' distintiva del design PIO del picoZ80 e' l'uso di
out exec, 16 nella macchina a stati orchestratrice
z80_cycle. Questa istruzione preleva un valore a 16 bit dal TX FIFO e lo esegue immediatamente come istruzione PIO — il valore non e' un dato,
e' la prossima istruzione che la macchina a stati eseguira'.
Questo meccanismo permette al codice C sul Core 1 di controllare la sequenza dei cicli bus in tempo reale. Anziche' caricare un programma PIO fisso per ogni tipo di ciclo, il Core 1 inserisce una sequenza di istruzioni PIO pre-codificate nel TX FIFO, e la SM del ciclo le esegue una per una:
// z80_cycle SM (PIO 0 SM 2) — the orchestrator
//
// .program z80_cycle
// .side_set 2 opt
// public start_cycle:
// wait 0 irq 6 ; Pause if BUSACK is active (bus relinquished).
// irq set 0 ; Signal "ready for new cycle".
// wait 0 irq 0 ; Wait until C code clears IRQ 0 (address loaded).
// wait 1 gpio Z80_PIN_CLK ; Sync to T1 rising edge of host clock.
// cycle_exec:
// out exec, 16 ; ← Pull next instruction from TX FIFO and execute it.
// jmp cycle_exec ; Loop: keep executing injected instructions.
//
// The C code pushes a sequence of encoded PIO instructions into the FIFO.
// Each instruction controls one step of the bus cycle (assert /MREQ, wait for
// clock edge, read data bus, etc.). The sequence ends with a JMP back to
// start_cycle, which restarts the orchestrator for the next bus transaction.
Il codice C pre-calcola queste sequenze di istruzioni all'avvio per ogni tipo di ciclo (fetch, lettura memoria, scrittura memoria, lettura I/O, scrittura I/O, refresh, acknowledge interrupt). Durante l'esecuzione, il Core 1 seleziona la sequenza pre-costruita appropriata e la inserisce nel FIFO. Questo approccio ha due vantaggi critici:
- Efficienza dello spazio programma — ogni blocco PIO ha solo 32 slot di istruzioni. Iniettando le istruzioni dinamicamente, la SM del ciclo necessita di soli 7 istruzioni di memoria programma per orchestrare tutti i tipi di ciclo. I programmi effettivi dei tipi di ciclo (fetch, lettura, scrittura, ecc.) esistono come array C di istruzioni codificate, non come programmi PIO residenti.
- Flessibilita' — il codice C puo' modificare la sequenza di istruzioni iniettate a runtime per gestire casi speciali (ad es. inserimento di wait state aggiuntivi, salto della fase di refresh, o generazione di un ciclo non standard per il debug).
Coordinamento delle Macchine a Stati
Le 12 macchine a stati lavorano come una pipeline coordinata. Il diagramma seguente mostra il flusso di un tipico ciclo di lettura memoria:
Core 1 (C code) PIO State Machines
───────────── ──────────────────
1. Resolve address z80_cycle: IRQ 0 set
from memory map (waiting for work)
│
2. Push addr → TX FIFO ──────────────────→ z80_addr: receives addr
Clear IRQ 0 outputs A0–A15 on pins
│
3. Push cycle instructions ──────────────→ z80_cycle: out exec, 16
(e.g. mem_read sequence) executes: set /MREQ low
into cycle SM TX FIFO executes: set /RD low
│ executes: wait CLK edges
4. Wait for RX FIFO ←──────────────────── z80_data: samples D0–D7
(data byte from bus) pushes to RX FIFO
│
5. Read data from z80_cycle: JMP start_cycle
RX FIFO (ready for next cycle)
│
6. Dispatch to PSRAM
or driver handler
L'handshake basato su IRQ garantisce che il bus indirizzi sia stabile prima che i segnali di controllo vengano asseriti, e che i dati vengano campionati nel punto corretto del ciclo bus. Le macchine a stati non effettuano mai polling — utilizzano
wait 0 irq N per sospendersi finche' l'evento rilevante si verifica, consumando zero cicli CPU durante l'attesa.
Ciclo di Fetch Z80 (Ciclo M1) — Passo per Passo
Il fetch dell'opcode e' il ciclo bus Z80 piu' complesso — combina una lettura memoria con un ciclo di refresh. Il programma z80_fetch viene eseguito su 4 T-cycle del clock host:
Host CLK: ──┐ ┌──┐ ┌──┐ ┌──┐ ┌──
│ │ │ │ │ │ │ │
└──┘ └──┘ └──┘ └──┘
T1 T2 T3 T4
A0–A15: ══╤═══ PC address ══════╤═══ Refresh addr ══╗
│ │ ║
/M1: ──┘ └────────────────────╜── (low during T1–T2, high T3–T4)
/MREQ: ────┘ ┌────┘ ┌──── (low T1↓–T3↑, then T3↓–T4↓ for refresh)
/RD: ────┘ ┌─────────────────────── (low T1↓–T3↑)
/RFSH: ────────────────────┘ ┌── (low T3↑–T4↓)
D0–D7: ═══════════════╤═══╗ (sampled at T3↑)
│ ║
opcode read
Il programma PIO implementa questo come segue:
- Fronte di salita T1 — la SM
z80_addr emette il valore del PC su A0–A15. z80_fetch asserisce /M1 a livello basso tramite set pins.
- Fronte di discesa T1 —
/MREQ e /RD vengono asseriti a livello basso (tramite set pins e side-set). L'indirizzo e' ora valido e il sistema di memoria puo' iniziare a rispondere.
- T2 — la SM entra in un loop di wait-state: attende il fronte di salita e poi di discesa del CLK, quindi controlla il pin
/WAIT tramite jmp pin. Se /WAIT e' basso, la SM ripete il loop (aggiungendo cicli Tw). Se e' alto, procede a T3.
- Fronte di salita T3 —
in pins, 8 campiona D0–D7 (il byte dell'opcode) e lo inserisce nell'RX FIFO. L'IRQ 1 viene attivato per segnalare a z80_data/z80_addr che l'indirizzo di refresh deve ora essere emesso. /M1, /MREQ e /RD vengono deasseriti; /RFSH viene asserito a livello basso.
- Fronte di discesa T3 —
/MREQ viene asserito nuovamente (per lo strobe di riga del refresh).
- T4 — il refresh continua. Alla fine di T4,
/MREQ e /RFSH vengono deasseriti. La SM del ciclo ritorna a start_cycle pronta per la transazione bus successiva.
Il Core 1 legge l'opcode dall'RX FIFO e lo utilizza per decodificare l'istruzione, determinare quanti cicli di memoria o I/O successivi sono necessari e inserire le sequenze di istruzioni appropriate.
Cicli di Lettura e Scrittura Memoria
I cicli di lettura e scrittura memoria sono piu' semplici del fetch — si estendono su 3 T-cycle senza fase di refresh.
Memory Read:
Host CLK: ──┐ ┌──┐ ┌──┐ ┌──
│ │ │ │ │ │
└──┘ └──┘ └──┘
T1 T2 T3
A0–A15: ══╤═══ address ═══════╗
/MREQ: ────┘ ┌──── (low T1↓–T3↓)
/RD: ────┘ ┌──── (low T1↓–T3↓)
D0–D7: ═══════════════╤═══╗ (sampled at T3↓)
Memory Write:
Host CLK: ──┐ ┌──┐ ┌──┐ ┌──
│ │ │ │ │ │
└──┘ └──┘ └──┘
T1 T2 T3
A0–A15: ══╤═══ address ═══════╗
D0–D7: ══════╤═══ data ═════╗ (driven from T2 onwards)
/MREQ: ────┘ ┌──── (low T1↓–T3↓)
/WR: ──────────┘ ┌──── (low T2↓–T3↓)
Per le letture, la SM
z80_mem_read asserisce
/MREQ e
/RD al fronte di discesa T1, attende durante T2 (controllando
/WAIT per i wait state), quindi campiona il bus dati al fronte di discesa T3 usando
in pins, 8. Per le scritture,
z80_mem_write asserisce
/MREQ al fronte di discesa T1, quindi asserisce
/WR al fronte di discesa T2 dopo che il bus dati e' stato pilotato da
z80_data. Entrambi deasseriscono tutti i segnali di controllo alla fine di T3.
Cicli di Lettura e Scrittura I/O
I cicli I/O dello Z80 utilizzano /IORQ invece di /MREQ e includono sempre un wait state automatico (Tw) tra T2 e T3. Questa e' una caratteristica architetturale dello Z80 — il ciclo aggiuntivo da' ai dispositivi I/O piu' lenti il tempo di rispondere:
I/O Read:
Host CLK: ──┐ ┌──┐ ┌──┐ ┌──┐ ┌──
│ │ │ │ │ │ │ │
└──┘ └──┘ └──┘ └──┘
T1 T2 Tw T3
A0–A15: ══╤═══ port address ══════════╗
/IORQ: ──────┘ ┌──── (low T2↑–T3↓)
/RD: ──────┘ ┌──── (low T2↑–T3↓)
D0–D7: ═══════════════════════╤═══╗ (sampled at T3↓)
La SM
z80_io_read asserisce
/IORQ e
/RD al fronte di salita T2 (non T1 come per i cicli di memoria — questa e' la specifica dello Z80). Il wait state automatico Tw e' implementato dallo stesso pattern di loop
jmp pin /
wait utilizzato per i cicli di memoria. Le scritture I/O seguono lo stesso schema con
/WR al posto di
/RD.
Gestione BUSREQ / BUSACK
La SM
z80_busrq (PIO 2 SM 0) monitora il pin di ingresso
/BUSREQ dell'host. Quando
/BUSREQ diventa attivo (basso), la SM:
- Attiva l'IRQ 6 per segnalare alla SM del ciclo che e' in sospeso una richiesta del bus.
- Attende il completamento del ciclo bus corrente (
wait 1 irq 0).
- Preleva una parola di controllo a 32 bit dal TX FIFO che specifica le direzioni e i valori dei pin per lo stato di rilascio del bus — questo mette in tri-state i bus indirizzi e dati e asserisce
/BUSACK a livello basso.
- Resta in loop su
jmp pin finche' /BUSREQ diventa inattivo (alto).
- Preleva una seconda parola a 32 bit per ripristinare le direzioni normali dei pin e deasserire
/BUSACK.
- Cancella l'IRQ 6, permettendo alla SM del ciclo di riprendere.
La SM del ciclo controlla l'IRQ 6 all'inizio di ogni ciclo tramite
wait 0 irq 6 — se il flag e' attivato, la SM si blocca fino al completamento della richiesta del bus. Questo garantisce che il rilascio del bus avvenga in modo pulito tra i cicli, mai a meta' ciclo.
Sincronizzazione del Clock
Tutte le SM dei tipi di ciclo si sincronizzano con il clock dello Z80 host usando
wait 1 gpio Z80_PIN_CLK (attesa fronte di salita) e
wait 0 gpio Z80_PIN_CLK (attesa fronte di discesa). Questo significa:
- I programmi PIO sono indipendenti dalla frequenza del clock — funzionano a qualsiasi velocita' di clock host, da DC alla velocita' massima che l'RP2350 puo' seguire (limitata dal clock di sistema PIO e dalla velocita' di campionamento GPIO).
- Il clock PIO a 300 MHz dell'RP2350 fornisce circa 85 cicli PIO per T-state dello Z80 a 3,5 MHz, dando tempo piu' che sufficiente per eseguire istruzioni PIO, inserire/prelevare dai FIFO e controllare i flag IRQ tra i fronti del clock.
- La SM
z80_sync (PIO 2 SM 1) fornisce un IRQ di sincronizzazione T1 che il codice C utilizza per allineare gli accessi alla PSRAM con il clock host, prevenendo derive temporali nel software host sensibile al clock.
- La SM
z80_clk_sync (PIO 2 SM 2) rigenera il clock host su un GPIO separato, fornendo un'uscita clock pulita per il monitoraggio esterno o il trigger dell'analizzatore logico.
Ciclo di Acknowledge dell'Interrupt
La SM
z80_int_ack (PIO 2 SM 3) implementa la sequenza di acknowledge dell'interrupt dello Z80. Quando il codice C rileva una condizione di interrupt, carica il programma int_ack. Questo ciclo e' simile a un fetch ma con differenze chiave:
/M1 viene asserito a T1 (come un fetch), ma /IORQ viene asserito invece di /MREQ al wait state (Tw1).
- Vengono inseriti due wait state automatici (Tw1, Tw2) per dare al dispositivo che ha generato l'interrupt il tempo di posizionare un vettore sul bus dati.
- Il byte del vettore viene letto da D0–D7 e inserito nell'RX FIFO.
- Segue un ciclo di refresh, identico alla fase di refresh del fetch.
Modello di Memoria
Gli accessi alla memoria vengono risolti attraverso tre livelli a latenza crescente. Il design a tre livelli garantisce che il caso comune (RAM/ROM supportata da PSRAM) sia veloce, consentendo al contempo la massima flessibilita' per i dispositivi virtuali e il pass-through verso l'host fisico.
Livello 1 — Tabella di Dispatch SRAM dell'RP2350
Un array di 128 valori
membankPtr a 32 bit, residente nella SRAM on-chip da 512KB dell'RP2350, fornisce un lookup O(1) del tipo di blocco per ogni transazione bus. Ogni voce copre ciascun blocco da 512 byte dello spazio di indirizzamento Z80 a 64KB (128 × 512 = 65.536 byte). Ogni voce codifica:
- Il tipo di blocco (PHYSICAL, RAM, ROM, FUNC, ecc.).
- Per i blocchi supportati da PSRAM: il numero del banco PSRAM e l'offset.
- Per i blocchi FUNC: un indice nella tabella dei puntatori a funzione dei dispositivi virtuali.
Questo e' il percorso piu' veloce — il Core 1 legge la voce della tabella di dispatch per l'indirizzo corrente in un singolo accesso SRAM (zero wait state a 300MHz) prima di decidere cosa fare dopo.
Livello 2 — PSRAM Esterna
La PSRAM da 8MB e' organizzata come:
- 64 banchi × 64KB — dati di immagini RAM o ROM per lo spazio di indirizzamento Z80.
- 64KB
memPtr — array di puntatori di redirect per byte per i blocchi di tipo PTR.
- 64KB
memioPtr — array di puntatori a funzione per dispositivi FUNC mappati in memoria.
- 64KB
ioPtr — array di puntatori a funzione per dispositivi FUNC su porte I/O.
La PSRAM viene acceduta tramite la periferica SPI dedicata dell'RP2350 con DMA. La latenza di accesso e' deterministica e gestita dal generatore di wait-state per evitare violazioni del bus.
Livello 3 — Flash SPI da 16MB
Le immagini ROM vengono caricate dalla Flash (o dalla scheda SD, tramite l'ESP32) nella PSRAM all'avvio. A runtime la Flash non viene acceduta per le transazioni bus — tutti i dati ROM vengono serviti dalla PSRAM. La Flash viene utilizzata per:
- Bootloader e firmware applicativo.
config.json minificato (memorizzato nella cache dalla scheda SD ad ogni avvio).
- Immagini ROM nelle partizioni App Config (utilizzate quando non e' presente la scheda SD).
Tipi di Blocchi Memoria
| Tipo |
Descrizione |
PHYSICAL |
Pass-through — l’RP2350 rilascia il bus e la memoria fisica dell’host risponde. Utilizzato per la ROM e la RAM native dell’host. |
PHYSICAL_VRAM |
Come PHYSICAL ma con wait state aggiuntivi per la temporizzazione della video RAM dell’host. Adatto per le regioni VRAM di MZ-700/MZ-80A. |
PHYSICAL_HW |
Pass-through per i registri hardware dell’host (dispositivi mappati in I/O nello spazio di memoria). |
RAM |
Lettura/scrittura — supportato da un banco PSRAM. L’RP2350 gestisce letture e scritture da/verso la PSRAM. |
ROM |
Sola lettura — supportato da un banco PSRAM. I cicli di scrittura vengono ignorati silenziosamente (l’host vede una temporizzazione bus normale ma nessun dato viene memorizzato). |
VRAM |
Video RAM supportata da PSRAM. I cicli di scrittura vengono replicati contemporaneamente sia nella PSRAM che nella VRAM fisica dell’host. |
FUNC |
Dispositivo virtuale — ogni accesso attiva una chiamata a funzione C tramite la tabella dei puntatori a funzione memioPtr o ioPtr, abilitando l’emulazione arbitraria di I/O. |
PTR |
Redirect per byte — ogni byte del blocco da 512 byte puo’ puntare indipendentemente a qualsiasi altro tipo di blocco o posizione PSRAM. |
Riferimento Configurazione
Tutto il comportamento del picoZ80 e' controllato da
config.json sulla scheda SD. L'RP2350 legge e minifica questo file all'avvio, memorizzando il risultato nella Flash. Gli avvii successivi utilizzano la copia nella Flash se non e' presente la scheda SD.
La struttura JSON di livello superiore e':
{
"esp32": {
"core": { ... },
"wifi": { ... }
},
"rp2350": {
"core": { ... },
"z80": [ { "memory": [...], "io": [...], "drivers": [...] } ]
}
}
esp32.core
| Chiave |
Tipo |
Descrizione |
device |
string |
Personalita’ CPU — "Z80" per picoZ80, "6502" per pico6502, "6512" per pico6512. |
mode |
integer |
Modalita’ WiFi di avvio predefinita: 0 = client (station), 1 = Access Point. |
esp32.wifi
| Chiave |
Tipo |
Descrizione |
override |
0/1 |
Interruttore principale: 1 = applica tutte le impostazioni seguenti; 0 = usa le impostazioni NVS persistenti. |
wifimode |
string |
"ap" = modalita’ Access Point; "client" = modalita’ Station/client. |
ssid |
string |
Nome della rete WiFi da creare (AP) o a cui connettersi (client). |
password |
string |
Passphrase WiFi. |
ip |
string |
Indirizzo IP fisso (ad es. "192.168.1.192"). |
netmask |
string |
Maschera di sottorete (ad es. "255.255.255.0"). |
gateway |
string |
Gateway predefinito (ad es. "192.168.1.1"). |
dhcp |
0/1 |
Modalita’ client: 1 = DHCP; 0 = usa le impostazioni IP fisse. |
webfs |
string |
Directory root del filesystem web sulla scheda SD (predefinito "webfs"). |
persist |
0/1 |
1 = scrive le impostazioni risolte nel NVS per la persistenza tra i riavvii. |
rp2350.core
| Chiave |
Tipo |
Descrizione |
cpufreq |
integer |
Clock di sistema dell’RP2350 in Hz (ad es. 300000000). La frequenza massima stabile dipende dalla frequenza PSRAM e dalla tensione del core. |
psramfreq |
integer |
Clock SPI della PSRAM in Hz (ad es. 133000000). |
voltage |
float |
Tensione del core dell’RP2350 in volt (ad es. 1.10). Velocita’ di clock piu’ elevate richiedono tensioni piu’ alte. |
addrDrive |
integer (0–3) |
Capacita’ di pilotaggio GPIO del bus indirizzi: 0=2mA, 1=4mA, 2=8mA, 3=12mA. Predefinito 0. |
addrSlew |
integer (0–1) |
Slew rate GPIO del bus indirizzi: 0=lento (predefinito), 1=veloce. |
dataDrive |
integer (0–3) |
Capacita’ di pilotaggio GPIO del bus dati. Stessa codifica di addrDrive. Predefinito 0. |
dataSlew |
integer (0–1) |
Slew rate GPIO del bus dati. Stessa codifica di addrSlew. Predefinito 0. |
ctrlDrive |
integer (0–3) |
Capacita’ di pilotaggio GPIO dei segnali di controllo. Stessa codifica di addrDrive. Predefinito 0. |
ctrlSlew |
integer (0–1) |
Slew rate GPIO dei segnali di controllo. Stessa codifica di addrSlew. Predefinito 0. |
addrSchmitt / dataSchmitt / ctrlSchmitt |
0/1 |
Abilita l’ingresso Schmitt-trigger per il gruppo di bus indirizzo / dati / controllo. 1 = abilitato (predefinito), 0 = disabilitato. Si applica ai pin configurati come ingressi. |
addrPull / dataPull / ctrlPull |
integer (0–2) |
Resistenza di pull per il gruppo di bus indirizzo / dati / controllo: 0 = nessuna, 1 = pull-down, 2 = pull-up. |
refresh |
integer |
Generazione del refresh DRAM Z80: 0 = off (nessun ciclo di refresh), 1 = un ciclo di refresh a ogni fetch di opcode, N = un ciclo di refresh ogni N fetch. Riduce l’overhead del bus sugli host la cui DRAM non richiede un refresh per fetch. |
pinOverrides |
array |
Override GPIO per singolo pin. Ogni elemento: { "pin": N, "drive": D, "slew": S, "schmitt": 0/1, "pull": P }. Sovrascrive i valori predefiniti a livello di bus per i singoli pin GPIO quando un hardware specifico richiede caratteristiche elettriche diverse. |
z80[].memory — Voci della Mappa di Memoria
L'array memory definisce la mappa di memoria dello Z80. Le voci devono essere ordinate per indirizzo. Le regioni devono essere allineate a e dimensionate come multipli di 512 byte. I vuoti tra le voci vengono trattati come pass-through PHYSICAL.
| Chiave |
Tipo |
Descrizione |
enable |
0/1 |
Se questa voce e’ attiva. Le voci disabilitate vengono ignorate all’avvio. |
addr |
hex string |
Indirizzo di partenza nello spazio di indirizzamento Z80 (ad es. "0x0000"). Deve essere allineato a 512 byte. |
size |
hex string |
Dimensione della regione in byte (ad es. "0x2000" per 8KB). Deve essere un multiplo di 512. |
type |
string |
Tipo di blocco — vedi Tipi di Blocchi Memoria. |
bank |
integer |
Numero del banco PSRAM (0–63) per i tipi RAM/ROM/VRAM/FUNC. |
tcycwait |
integer |
Wait state T-cycle aggiuntivi da inserire su ogni accesso a questa regione. |
tcycsync |
0/1 |
Abilita la sincronizzazione T1 per questa regione. Necessario per le regioni sensibili alla temporizzazione. |
task |
string |
Identificatore di task opzionale per i blocchi di tipo FUNC (stringa di binding del driver). |
file |
string |
Percorso sulla scheda SD di un’immagine ROM da precaricare nel banco PSRAM all’avvio (ad es. "/ROM/mz700.rom"). |
fileofs |
integer |
Offset in byte nel file dell’immagine ROM da cui iniziare la lettura. |
z80[].io — Voci della Mappa Porte I/O
L'array io mappa gli intervalli di porte I/O dello Z80 ai tipi di blocco. Solo i tipi PHYSICAL e FUNC sono significativi per le voci I/O.
| Chiave |
Tipo |
Descrizione |
enable |
0/1 |
Se questa voce I/O e’ attiva. |
addr |
hex string |
Indirizzo di partenza della porta I/O (ad es. "0xE0"). |
size |
hex string |
Numero di porte consecutive (ad es. "0x04" per le porte E0–E3). |
type |
string |
PHYSICAL = passa all’host; FUNC = chiama la funzione handler C. |
task |
string |
Stringa di binding del driver per le voci di tipo FUNC. |
z80[].drivers — Istanze dei Driver
L'array drivers istanzia i driver dei dispositivi virtuali e li lega alle regioni di memoria o I/O. Ogni driver ha un tipo (il modulo driver C), un nome (identificatore dell'istanza) e uno o piu' oggetti interfaccia che definiscono le immagini ROM, le mappe degli indirizzi, le mappe I/O e i parametri per quell'istanza del driver.
"drivers": [
{
"enable": 1,
"name": "MZ700",
"type": "PHYSICAL",
"if": [
{
"enable": 1,
"name": "main",
"type": "PHYSICAL",
"rom": [
{
"enable": 1,
"file": "/MZ700/mz700.rom",
"loadaddr": [
{
"enable": 1,
"position": 0,
"addr": "0x0000",
"bank": 0,
"size": "0x1000",
"tcycwait": 0,
"tcycsync": 0
}
]
}
],
"addrmap": [
{ "enable":1, "srcaddr":"0x0000", "size":"0x1000",
"dstaddr":"0x0000" }
],
"iomap": [
{ "enable":1, "srcaddr":"0xE0", "size":"0x08",
"dstaddr":"0xE0", "16bit":0 }
],
"param": [
{ "enable":1, "file":"/config/mz700.cfg" }
]
}
]
},
{
"enable": 1,
"name": "MZ-1E05",
"type": "PHYSICAL",
"if": [
{
"enable": 1,
"name": "fdc0",
"type": "PHYSICAL",
"rom": [],
"addrmap": [],
"iomap": [
{ "enable":1, "srcaddr":"0xD8", "size":"0x04",
"dstaddr":"0xD8", "16bit":0 }
],
"param": [
{ "enable":1, "file":"/DSK/MZ700/disk0.dsk" }
]
}
]
}
]
Ogni voce driver ha un "name" di livello superiore (il modulo driver C da istanziare), "type" (PHYSICAL o VIRTUAL) e un array "if" (interfaccia) contenente uno o piu' oggetti interfaccia. Ogni oggetto interfaccia descrive un sotto-driver o una scheda periferica e contiene questi campi:
| Chiave |
Tipo |
Descrizione |
enable |
0/1 |
Se questa interfaccia e’ attiva (0 = saltata durante l’inizializzazione). |
name |
string |
Identificatore dell’istanza — deve corrispondere a un nome nell’interfaceFuncMap[] della persona. |
type |
string |
PHYSICAL o VIRTUAL. |
rom |
array |
Immagini ROM da caricare nella PSRAM (vedi sotto). |
addrmap |
array |
Voci di rimappatura indirizzi di memoria (srcaddr/dstaddr/size). |
iomap |
array |
Voci di rimappatura porte I/O (srcaddr/dstaddr/size/16bit). |
param |
array |
Parametri specifici del driver — tipicamente { "enable":1, "file":"/path/to/image" } per immagini disco, { "name":"key", "value":"val" } per parametri nominati, o { "ip":"a.b.c.d:port", "enable":1 } per l’indirizzo del file server di rete Celestite. |
Porte base delle interfacce riposizionabili. Affinche’ le schede indipendenti dalla macchina possano essere utilizzate su schede personalizzate / sperimentali (vedi OpenZ80), diversi driver di interfaccia leggono la loro porta I/O base dal dstaddr della relativa voce iomap (con srcaddr impostato sulla porta autentica della scheda), utilizzando come predefinita la loro porta originale quando non e’ presente alcuna voce iomap. Le schede riposizionabili e le porte base predefinite sono MZ-1R12 (0xF8/3), MZ-1R18 (0xEA/2), MZ-1R23 (0xB8/2), MZ-1R37 (0xAC/2), PIO-3034 (0x00/4), MZ-8BIO3 / MZ-1E24 (0xB0/4), MZ-1E05 (0xD8/7) e Celestite (0x60/16). Le chiavi JSON sono in minuscolo (srcaddr / dstaddr) e i loro valori vengono interpretati come numeri. Il campo Base I/O Port nella pagina di configurazione GUI scrive automaticamente la voce iomap corretta.
rom[].loadaddr[] — Indirizzi di Caricamento ROM
Ogni voce file ROM contiene un array loadaddr che specifica dove i dati ROM vengono caricati nella PSRAM. Piu' voci loadaddr permettono a un singolo file ROM di essere suddiviso su intervalli di indirizzi non contigui o banchi PSRAM diversi.
| Chiave |
Tipo |
Descrizione |
enable |
0/1 |
Se questo indirizzo di caricamento e’ attivo. |
position |
integer |
Offset in byte all’interno del file ROM da cui iniziare la lettura. |
addr |
hex string |
Indirizzo Z80 di destinazione nella mappa di memoria (ad es. "0x0000"). |
bank |
integer |
Numero del banco PSRAM in cui caricare. |
size |
hex string |
Numero di byte da caricare (ad es. "0x1000" per 4KB). |
tcycwait |
integer |
Wait state T-cycle aggiuntivi per questa regione. |
tcycsync |
integer |
Valore di sincronizzazione T-cycle per questa regione. |
Compatibilita' Persona–Interfaccia
Non tutti i driver di interfaccia sono disponibili per ogni persona. La tabella seguente mostra la compatibilita' attuale delle interfacce per le serie Sharp MZ / X1. La persona Amstrad PCW-9512 e' un driver autonomo con un FDC uPD765 integrato (nessun driver di interfaccia separato). La persona Tatung Einstein TC-01 e' un driver autonomo con un FDC WD1770 integrato e una sotto-interfaccia EinsteinFDC. La colonna
Open mostra le schede indipendenti dalla macchina accettate dalla persona sperimentale
OpenZ80.
La tabella mostra quali interfacce possono essere utilizzate con ogni persona macchina di livello superiore tramite l'array "if". Il valore "name" dell'interfaccia nel JSON deve corrispondere a una delle voci supportate per la persona scelta.
| Interfaccia |
MZ-700 |
MZ-1500 |
MZ-80K |
MZ-800 |
MZ-80A |
MZ-2000 |
MZ-2200 |
MZ-80B |
MZ-2500 |
Open |
| RFS |
Si’ |
Si’ |
Si’ |
Si’ |
Si’ |
Si’ |
— |
— |
— |
— |
| TZFS |
Si’ |
— |
— |
— |
— |
— |
— |
— |
— |
— |
| MZ-1E05 |
Si’ |
Si’ |
— |
Si’ |
— |
— |
— |
— |
— |
Si’ |
| MZ80FIO |
— |
— |
Si’ |
Si’ |
— |
— |
— |
— |
— |
— |
| MZ80AFI |
— |
— |
Si’ |
Si’ |
Si’ |
— |
— |
— |
— |
— |
| MZ-8BFI / E0054PA |
— |
— |
— |
Si’ |
— |
Si’ |
Si’ |
Si’ |
Si’ |
— |
| MZ-1E14 |
Si’ |
Si’ |
Si’ |
Si’ |
— |
— |
— |
Si’ |
Si’ |
— |
| MZ-1E19 |
Si’ |
Si’ |
Si’ |
Si’ |
— |
Si’ |
Si’ |
Si’ |
Si’ |
— |
| MZ-1R12 |
Si’ |
Si’ |
Si’ |
Si’ |
Si’ |
Si’ |
Si’ |
Si’ |
Si’ |
Si’ |
| MZ-1R18 |
Si’ |
Si’ |
Si’ |
Si’ |
Si’ |
Si’ |
Si’ |
Si’ |
Si’ |
Si’ |
| MZ-1R23 |
— |
Si’ |
— |
Si’ |
— |
— |
— |
Si’ |
Si’ |
Si’ |
| MZ-1R37 |
— |
Si’ |
Si’ |
Si’ |
— |
— |
— |
Si’ |
Si’ |
Si’ |
| PIO-3034 |
— |
Si’ |
Si’ |
Si’ |
— |
— |
— |
Si’ |
Si’ |
Si’ |
| Celestite |
— |
Si’ |
— |
Si’ |
— |
— |
— |
Si’ |
Si’ |
Si’ |
| MZ-8BIO3 |
Si’ |
Si’ |
— |
Si’ |
— |
— |
— |
Si’ |
— |
Si’ |
| MZ-1E24 |
Si’ |
Si’ |
— |
Si’ |
— |
— |
— |
Si’ |
— |
Si’ |
| MZ-1E30 |
— |
— |
— |
— |
— |
— |
— |
Si’ |
Si’ |
— |
Driver Integrati
Il firmware supporta un sistema di compilazione mirato con cinque target di modello: BaseZ80 (tutti i driver), SharpZ80 (solo Sharp), AmstradZ80 (solo Amstrad), TatungZ80 (solo Tatung) e OpenZ80 (la persona sperimentale indipendente dalla macchina). Le define a tempo di compilazione INCLUDE_SHARP_DRIVERS, INCLUDE_AMSTRAD_DRIVERS, INCLUDE_TATUNG_DRIVERS e INCLUDE_OPEN_DRIVERS controllano quali moduli driver vengono compilati. Le tabelle seguenti elencano tutti i driver disponibili.
Serie Sharp MZ (INCLUDE_SHARP_DRIVERS)
Quando il firmware viene compilato con
INCLUDE_SHARP_DRIVERS, i seguenti moduli driver vengono compilati e possono essere istanziati tramite l'array
drivers:
| Driver |
Stringa Tipo |
Descrizione |
MZ700.c |
MZ700 |
Sharp MZ-700 commutazione banchi, video, I/O tastiera |
MZ80A.c |
MZ80A |
Sharp MZ-80A — ROM monitor (SA-1510), VRAM, emulazione Intel 8253 PIT, 8255 PPI, scambio memoria MEMSW/MEMSWR (inclusa commutazione banchi CP/M), supporto modalita’ mista fisica+virtuale e supporto per le sotto-interfacce RFS, MZ80AFI, MZ-1E14, MZ-1E19, MZ-1R12, MZ-1R18 |
MZ2000.c |
MZ2000 |
Sharp MZ-2000 — commutazione modalita’ memoria BST/NST, overlay VRAM caratteri + grafica, 8253 PIT, 8255 PPI, Z80 PIO, MB8866 FDC. Supporta sia la modalita’ fisica (sostituzione drop-in dello Z80 con rilevamento automatico modalita’ boot/normale) sia la modalita’ virtuale (emulazione completa basata su PSRAM con mirroring della ROM IPL) |
MZ2200.c |
MZ2200 |
Sharp MZ-2200 — commutazione modalita’ memoria BST/NST, overlay VRAM, 8253 PIT, 8255 PPI, Z80 PIO, MB8866 FDC, CRT a colori |
MZ80B.c |
MZ80B |
Sharp MZ-80B — ROM IPL 2K, BST/NST, display monocromatico, doppia pagina GRPH, 8253 PIT, 8255 PPI, Z80 PIO |
MZ2500.c |
MZ2500 |
Sharp MZ-2500 (SuperMZ) — MMU a 8 pagine (64 blocchi), modalita’ compatibilita’ MZ-2000/MZ-80B, YM2203, G-CRTC, MB8876 FDC, palette, controllore interrupt. La modalita’ virtuale supporta software guidato da interrupt tramite handler personalizzati fetchByte/RETI (cicli bus M1 fisici per il clocking del gate array), formato disco nativo D88 con auto-rilevamento sparse/contiguo, soppressione interrupt one-shot durante l’inizializzazione |
MZ1500.c |
MZ1500 |
Sharp MZ-1500 — superset del MZ-700 con unita’ Quick Disk integrata, PCG, suono stereo PSG (SN76489AN), interfaccia stampante Z80 PIO, 8253 PIT, selezione modalita’ MZ-700/MZ-1500 tramite DIP switch. Sotto-interfacce: RFS, MZ-1E05, MZ-1E14, MZ-1E19, MZ-1R12, MZ-1R18, MZ-1R23, MZ-1R37, PIO-3034, Celestite |
MZ80K.c |
MZ80K |
Sharp MZ-80K — ROM monitor SP-1002 (0x0000–0x0FFF), VRAM 2KB (0xD000–0xD7FF), tastiera 8255 PPI / 8253 PIT / LS367 (0xE000–0xE7FF), boot ROM MZ-80FD nativa (0xF000–0xF3FF), scambio memoria MEMSW/MEMSWR per CP/M. Modalita’ fisica + virtuale (il CP/M in modalita’ fisica usa il remap degli accessi Z80 per driver; la modalita’ virtuale e’ richiesta per il CP/M standard). Due percorsi floppy: MZ80FIO nativo (T3444M) — interfaccia MZ-80FD originale, fa il boot e legge tutti i dischi MZ-80K; e MZ80AFI (FDC MZ-80A) — usato per CP/M, fa il boot del CP/M MZ-80K e legge i dischi CP/M MZ-80K all’interno del CP/M (C:/D:). Solo un’interfaccia floppy e’ attiva alla volta; MZ80FIO ha la precedenza se entrambe sono presenti. Sotto-interfacce: RFS, MZ80FIO, MZ80AFI, MZ-1E14, MZ-1E19, MZ-1R12, MZ-1R18, MZ-1R37, PIO-3034 |
MZ800.c |
MZ800 |
Sharp MZ-800 — driver a doppia modalita’ (compatibilita’ MZ-700 + MZ-800 nativa). Intercetta il registro Display-Mode del GDG (porta 0xCE) per commutare modalita’ al volo: la modalita’ nativa aggiunge grafica 320×200 / 640×200 (piani VRAM 0x8000–0xBFFF), palette a 4/16 colori, I/O GDG mappato su porte (0xCC–0xCF, palette 0xF0), interrupt vettorizzati IM2 tramite la daisy-chain dello Z80-PIO; la modalita’ MZ-700 usa l’8255/8253 mappati in memoria (0xE000–0xE7FF) e la VRAM testo (0xD000). PSG SN76489 (0xF2), FDC WD1773 (0xD8–0xDF), QuickDisk (0xF4–0xF7), porte di banking memoria 0xE0–0xE6. La modalita’ virtuale riproduce un RETI fisico sul bus reale per servire il latch in-service del PIO. |
MZ80AFI.c |
MZ80AFI |
Interfaccia floppy Sharp MZ-80A — emula il controller floppy disk AFI del MZ-80A |
MZ80FIO.c |
MZ80FIO |
Interfaccia floppy Sharp MZ-80FD/MZ-80FIO (MZ-80K) — boot ROM FDIF a 0xF000–0xF3FF, porte T3444M 0xF8–0xFB, fino a 4 unita’, cambio disco a runtime |
T3444M.c |
(usato da MZ80FIO) |
FDC Toshiba T3444M/T3444A — floppy nativo MZ-80K. DSK esteso CPC, 35 tracce, 2 testine, 16 settori/traccia, settori FM da 128 byte; parser DSK Track-Info robusto; 4 immagini di unita’ simultanee |
WD1773.c |
WD1773 |
FDC WD1773 — 80 tracce, 2 testine, 8 settori, immagini DSK/RAW/D88 |
QDDrive.c |
QDDRIVE |
Unita’ Sharp QuickDisk — emulazione completa Z80 SIO/2 con dati a traccia spirale, controllo motore e I/O asincrono da scheda SD |
RFS.c |
RFS |
ROM Filing System — caricamento MZF, CP/M, BASIC da scheda SD |
TZFS.c |
TZFS |
TranZPUter Filing System — monitor multi-bank + file system CP/M funzionante, selezionabile sulla persona MZ-700. Modalita’ di memoria tranZPUter via porta 0x60, processore di servizio virtuale K64F via OUT (0x68), I/O settori CP/M tramite l’ESP32. ROM roms/tzfs.bin da TZFS/asm/tzfs.asm |
MZ-1E05.c |
MZ1E05 |
Unita’ interfaccia floppy disk Sharp MZ-1E05 (basata su WD1773) |
MZ8BFI.c |
MZ8BFI / E0054PA |
Interfaccia floppy disk MZ-2000 — FDC MB8866 senza ROM driver (codice nell’IPL). Supporto formato D88. |
MZ-1E14.c |
MZ1E14 |
Controller QuickDisk MZ-1E14 con BIOS ROM (MZ-700/MZ-800) |
MZ-1E19.c |
MZ1E19 |
Controller QuickDisk MZ-1E19 senza BIOS ROM |
MZ-1R12.c |
MZ1R12 |
Scheda RAM 32KB con batteria tampone (persistente su scheda SD) |
MZ-1R18.c |
MZ1R18 |
Scheda di espansione RAM 64KB |
MZ-1R23.c |
MZ1R23 |
ROM Kanji MZ-1R23 128KB (pattern JIS 16×16) e ROM Dizionario MZ-1R24 256KB. File ROM caricati dalla scheda SD. Porte I/O B8h–B9h con lettura auto-incrementante |
MZ-1R37.c |
MZ1R37 |
EMM (Expanded Memory Manager) MZ-1R37 640KB — spazio di indirizzamento a 20 bit con latching indirizzo tramite porta I/O |
PIO-3034.c |
PIO3034 |
EMM IO DATA PIO-3034 320KB — contatore di indirizzi a 19 bit con porta dati auto-incrementante |
Celestite.c |
Celestite |
Scheda composita Celestite — controller Ethernet Wiznet W5100 (emulazione registri), controllore interrupt, UFM, RAM CMOS MZ-1R12 32KB integrata (espandibile a 64KB), EMM MZ-1R37 640KB opzionale. Porte I/O 60h–6Fh. Il parametro ip nell’array JSON param configura l’indirizzo del file server netfs.py (ad es. "192.168.1.210:6800"). Fase 2: networking TCP/IP reale tramite bridge ESP32 — comandi socket W5100 inoltrati all’ESP32 per operazioni BSD socket effettive. IPC inter-core per NET_CFG, NET_SOCK, NET_SEND, NET_RECV, NET_PING |
MZ8BIO3.c |
MZ-8BIO3 |
Scheda seriale RS-232C (connettore BI), Z80 SIO emulato alle porte 0xB0–0xB3 (base configurabile). Canali A/B collegati alle porte seriali USB CDC 2 e 3. Nessuna ROM. |
MZ1E24.c |
MZ-1E24 |
Scheda seriale RS-232C (connettore Sharp ST); come MZ-8BIO3 ma con cablaggio del connettore diverso. |
Z80SIO.c |
(usato da MZ-8BIO3 / MZ-1E24) |
Zilog Z80 SIO/2 register-accurate: WR0–WR7, RR0–RR2, interrupt vettorizzati Z80 mode-2, daisy-chain in-service a 4 livelli, status-affects-vector. Ring SPSC lock-free collegano il core Z80 (core 1) e il servizio USB CDC (core 0). |
SASI.c + MZ1E30.c |
MZ-1E30 |
Controller hard disk SASI MZ-1E30 — emula il controller hard disk SASI (Shugart Associates System Interface) Sharp MZ-1E30 per MZ-2500/MZ-80B. Fino a 4 target disco (~21,4 MB ciascuno, blocchi da 256 byte), ROM IPL 32KB (acceduta via I/O tramite porte 0xA8–0xA9), I/O settori on-demand da immagini disco su scheda SD. Comandi SASI: TEST_UNIT_READY, REQUEST_SENSE, READ(6), WRITE(6), SEEK(6), INQUIRY. Porte I/O 0xA4–0xA5 (dati/controllo SASI) |
PIT8253.c |
PIT8253 |
Emulazione standalone Intel 8253 PIT — tutte e sei le modalita’ contatore, BCD/binario, latch, lettura/caricamento LSB/MSB |
PPI8255.c |
PPI8255 |
Emulazione standalone Intel 8255 PPI — I/O Modo 0, set/reset bit, callback di output, iniezione di input |
Serie Amstrad PCW (INCLUDE_AMSTRAD_DRIVERS)
Quando il firmware viene compilato con
INCLUDE_AMSTRAD_DRIVERS, i seguenti moduli driver vengono compilati:
| Driver |
Stringa Tipo |
Descrizione |
PCW9512.c |
PCW9512 |
Amstrad PCW-9512 — Z80A @ 4MHz, 512KB RAM con commutazione a 4 banchi di pagine da 16KB (porte F0–F3), gate array (ASIC) per routing video/clock di sistema/FDC/controllo motore (porta F8), controller stampante daisy wheel 8041 (porte FC–FD), emulazione sequenza bootstrap. Modalita’ virtuale e fisica supportate |
uPD765.c |
— |
Emulazione FDC NEC uPD765 — supporto formato CPC DSK (porte 00–01). Comandi gate array: fine bootstrap, riavvio, routing INT FDC (NMI/INT/ignora), conteggio terminale, motore on/off. Imaging disco fisico tramite codice Z80 iniettato. Modulo FDC riutilizzabile (analogo a WD1773.c per Sharp) |
Serie Tatung Einstein (INCLUDE_TATUNG_DRIVERS)
Quando il firmware viene compilato con
INCLUDE_TATUNG_DRIVERS, i seguenti moduli driver vengono compilati:
| Driver |
Stringa Tipo |
Descrizione |
EinsteinTC01.c |
EinsteinTC01 |
Tatung Einstein TC-01 — Z80A @ 4MHz, 64KB RAM + 8KB ROM commutabile (X-TAL MOS), commutazione ROM/RAM tramite porta 0x24, VDP TMS9129 con applicazione temporizzazione inter-accesso (~2us di intervallo), PSG AY-3-8910 (porte 0x02-0x03), Z80 CTC (porte 0x28-0x2B), Z80 PIO (porte 0x30-0x33), interfaccia tastiera (porta 0x20). Modalita’ virtuale e fisica supportate |
EinsteinFDC.c |
— |
Sotto-interfaccia FDC Einstein — 2 unita’, supporto formato DSK/D88. Imaging disco fisico: lettura floppy fisico verso DSK, scrittura DSK verso floppy fisico |
WD1770.c |
— |
Emulazione FDC WD1770 — supporto formato Extended CPC DSK, D88 e DSK standard. 40 tracce, 1 testina, 10 settori, 512 byte (dischi da 200KB). Modulo FDC riutilizzabile per macchine basate su WD1770 (separato da WD1773.c utilizzato da Sharp) |
TZFS — Modello Monitor + CP/M (persona MZ-700)
TZFS.c implementa un monitor a basso livello multi-bank e file system funzionante — un insieme di miglioramenti rispetto al MONITOR 1Z-013A originale (accesso SD, ROM banking, un assembler / disassembler e strumenti) — modellato sul TZFS del tranZPUter SW e sul suo processore I/O virtuale K64F. E' offerto come
interfaccia selezionabile solo sulla persona MZ-700 (registrato in
MZ700.c insieme a RFS, e in pratica mutuamente esclusivo con esso), non come persona di primo livello. Il CP/M viene eseguito sotto il monitor.
Emulazione delle modalita' di memoria (porta 0x60)
La scrittura di una modalita' di memoria tranZPUter sulla porta I/O
0x60 ri-punta i puntatori di bank. Le modalita' sono
TZMM_ORIG,
TZMM_BOOT,
TZMM_TZFS,
TZMM_TZFS2,
TZMM_TZFS3,
TZMM_TZFS4,
TZMM_CPM,
TZMM_CPM2 e
TZMM_COMPAT. Il layout CP/M
CPM2 usa un paging del blocco 0 a granularita' di byte —
0x0000–
0x003F mappano il blocco dei vettori e
0x0040–
0x01FF l'inizio della TPA.
Processore di servizio virtuale K64F (porta 0x68)
Lo Z80 richiede i servizi eseguendo
OUT (0x68), che accoda un messaggio
MSG_TZFS_SVCREQ al Core 0; il Core 0 esegue
TZFS_processServiceRequest. I servizi di file system sono READDIR / NEXTDIR (blocchi directory a 16 voci in cache), READFILE / NEXTREADFILE, LOADFILE (consapevole dell'header MZF e degli oggetti bank), CHANGEDIR e CLOSE. I servizi CP/M sono LOADBDOS (ricaricamento CCP + BDOS al warm-boot), ADDSDDRIVE, READSDDRIVE e WRITESDDRIVE, piu' i servizi di frequenza CPU. Poiche' picoZ80 non ha accesso diretto alla SD, i settori CP/M da 512 byte vengono letti e scritti attraverso l'
ESP32 (
ESP_readSector /
ESP_writeSector) su file immagine interi; i percorsi immagine per drive provengono dalle voci
param[].file del JSON dell'interfaccia, con template di fallback
CPM/SDC16M/RAW/CPMDSK<nn>.RAW. La ROM TZFS e'
roms/tzfs.bin sulla scheda SD, assemblata da
TZFS/asm/tzfs.asm (il suo BIOS CP/M da
TZFS/asm/cbios.asm /
cpm22.asm). Viene fornita in
config_MZ-700_MZ-700.json con
"enable": 0 ed e' attivata nel JSON o dalla pagina Configurazione della GUI web.
OpenZ80 — Persona Sperimentale (INCLUDE_OPEN_DRIVERS)
Il target
OpenZ80 (
TARGET_MODEL_OPEN →
INCLUDE_OPEN_DRIVERS) compila una persona Z80 "vanilla" deliberatamente essenziale per gli sperimentatori che inseriscono il picoZ80 in una scheda di propria progettazione o in una macchina priva di un driver dedicato. In modalita'
PHYSICAL l'intero spazio di memoria e I/O da 64K passa attraverso alla scheda reale (le schede di interfaccia sovrappongono le loro porte I/O); in modalita'
VIRTUAL presenta una RAM piatta da 64K in cui le immagini ROM a livello di driver vengono caricate in sequenza a partire da 0x0000. Non contiene alcun hardware macchina e riutilizza i moduli delle schede di interfaccia Sharp verbatim (non contengono stato specifico della persona), esponendo solo le schede indipendenti dalla macchina elencate di seguito — ognuna delle quali supporta la
riposizionamento della porta I/O base.
| Driver |
Stringa Tipo |
Descrizione |
Open.c |
Open |
Persona Z80 vanilla / sperimentale — nessun hardware macchina. Passthrough fisico o RAM virtuale piatta da 64K con caricamento ROM a livello di driver da 0x0000 |
MZ-1R12.c |
MZ1R12 |
Scheda RAM-file MZ-1R12 da 32K (base predefinita 0xF8, riposizionabile) |
MZ-1R18.c |
MZ1R18 |
Scheda RAM MZ-1R18 da 64K (base predefinita 0xEA, riposizionabile) |
MZ-1R23.c |
MZ1R23 |
Scheda ROM Kanji MZ-1R23 / dizionario MZ-1R24 (base predefinita 0xB8, riposizionabile) |
MZ-1R37.c |
MZ1R37 |
EMM MZ-1R37 da 640K (base predefinita 0xAC, riposizionabile) |
PIO-3034.c |
PIO3034 |
EMM parallelo / I/O parallelo PIO-3034 (base predefinita 0x00, riposizionabile) |
MZ8BIO3.c / MZ1E24.c |
MZ8BIO3 / MZ1E24 |
Doppie schede seriali RS-232C (Z80 SIO, base predefinita 0xB0, riposizionabile; canali A/B su USB CDC 2/3) |
MZ-1E05.c |
MZ1E05 |
Interfaccia floppy WD1773 (base predefinita 0xD8, riposizionabile; richiede una boot ROM FDC) |
Celestite.c |
Celestite |
Scheda LAN ESP32 Celestite (base predefinita 0x60, riposizionabile) |
Compilare con
build_tzpuPico.sh open. Per adattare il picoZ80 a una nuova macchina, prendere come base uno dei driver di persona in
src/drivers/{Sharp,Amstrad,Tatung,Other}/ e abbinarlo al sorgente della ROM monitor / IPL / BIOS CP/M / boot floppy corrispondente nelle directory
asm/ dei progetti RFS e TZFS (ad es.
RFS/asm/sa1510.asm,
RFS/asm/cbios.asm,
TZFS/asm/mz2000_ipl.asm,
RFS/asm/mz80afi.asm). Consultare la
Guida dello Sviluppatore per la procedura passo per passo.
Framework Dispositivi Virtuali
Il tipo di blocco FUNC abilita l'emulazione arbitraria di I/O chiamando funzioni handler C su ogni accesso al bus. Qualsiasi blocco di 512 byte di memoria o intervallo di porte I/O puo' essere supportato da una funzione.
Firme delle Funzioni Handler
Gli handler FUNC di memoria sono memorizzati nella tabella memioPtr nella PSRAM. Gli handler FUNC I/O sono memorizzati nella tabella ioPtr. Le firme delle funzioni sono:
/* Memory read handler */
uint8_t mem_read_handler(uint16_t addr, void *ctx);
/* Memory write handler */
void mem_write_handler(uint16_t addr, uint8_t data, void *ctx);
/* I/O read handler */
uint8_t io_read_handler(uint8_t port, void *ctx);
/* I/O write handler */
void io_write_handler(uint8_t port, uint8_t data, void *ctx);
Le funzioni handler vengono chiamate direttamente dall'hot loop del Core 1. Devono completarsi prima che i wait state del ciclo bus corrente scadano — mantenere gli handler brevi ed evitare qualsiasi operazione bloccante (I/O file, UART, ecc.). Se un handler deve attivare un'operazione piu' lunga (ad es. caricare un settore del disco), deve inviare un messaggio al Core 0 tramite la coda inter-core e ritornare immediatamente con un byte di stato, delegando l'I/O effettivo al Core 0.
Scrivere un Nuovo Driver
Per aggiungere il supporto per una nuova periferica o macchina host:
- Creare un nuovo file
.c / .h nella directory src/drivers/.
- Implementare le funzioni handler di lettura e scrittura corrispondenti alle firme sopra indicate.
- Registrare i puntatori alle funzioni handler nelle tabelle
memioPtr o ioPtr durante l'inizializzazione del driver.
- Aggiungere il driver al target di compilazione CMakeLists.txt.
- Aggiungere una voce di stringa tipo in modo che il parser della configurazione JSON possa istanziare il driver per nome.
- Documentare le chiavi
param del driver nel file header del driver.
La funzione di inizializzazione del driver viene chiamata una volta all'avvio, dopo che
config.json e' stato analizzato. Il driver riceve un puntatore al suo blocco di configurazione dell'interfaccia e deve configurare qualsiasi stato interno e registrare i suoi handler in questo momento.
ICE (Shell di Debug)
Il picoZ80 include una shell di debug ICE (In-Circuit Emulator) integrata sul Canale 1 USB CDC (la seconda porta seriale enumerata quando la scheda e' collegata via USB). La shell viene eseguita sul Core 0 e comunica con il loop di emulazione del Core 1 tramite flag condivisi nella struttura di contesto Z80CPU. La shell di debug e' disponibile solo nella variante firmware DBGSH, compilata con la define INCLUDE_DBGSH.
Canali Seriali USB CDC
Il picoZ80 enumera diverse porte seriali USB CDC quando e' collegato a un host. CDC 0 e CDC 1 servono le UART fisiche / il bridge ESP32 e la shell di debug ICE (CDC 1, varianti DBGSH). CDC 2 e CDC 3 sono i due canali (A e B) di una scheda seriale RS-232C virtuale — il driver MZ-8BIO3 o MZ-1E24 — quando ne e' configurata una nel config.json. Queste due porte non hanno alcuna UART fisica dietro di se': sono bridge a ring buffer verso lo Z80 SIO emulato, cosi' che i dati scritti su CDC 2/3 sull'host appaiano sul canale A/B dello Z80 SIO visto dal guest, e viceversa. Se nessuna scheda seriale e' configurata, CDC 2 e CDC 3 non vengono enumerate.
Architettura
- Input/Output: Canale 1 USB CDC a 115200 baud. Il prompt della shell e'
dbg> . Sono supportati la cronologia dei comandi (16 voci) e l'echo dei caratteri.
- Breakpoint: Fino a 8 breakpoint simultanei memorizzati in
cpu->dbgBpAddr[]. Il Core 1 controlla l'array dei breakpoint prima di ogni fetch dell'opcode; su una corrispondenza, imposta cpu->hold = true e segnala il Core 0 tramite dbgBpHit.
- Single-step: Il comando
step imposta cpu->dbgStepCount. Il Core 1 decrementa questo contatore dopo ogni istruzione, fermandosi automaticamente quando raggiunge lo zero. Lo stato dei registri prima/dopo e l'istruzione disassemblata vengono visualizzati per ogni passo.
- Trace di esecuzione: Un buffer circolare a 512 voci (
cpu->dbgTrace[]) registra PC, opcode e registro flag per ogni istruzione eseguita quando il tracing e' abilitato. Ogni voce a 32 bit impacchetta [31:16]=PC, [15:8]=opcode, [7:0]=registro F.
- Accesso alla memoria: L'accesso alla memoria fisica (
dm p, wm p) esegue cicli bus Z80 reali tramite le macchine a stati PIO. L'accesso virtuale (dm v, wm v) legge/scrive direttamente nella PSRAM. La modalita' auto (wm senza qualificatore) segue la mappa di memoria. L'accesso RP2350 (dm r) legge lo spazio di indirizzamento del microcontrollore host con validazione dell'intervallo.
- Hold/Release: Il comando
hold imposta cpu->hold = true. Il Core 1 conferma tramite cpu->holdAck, garantendo che la CPU sia quiescente prima che la shell acceda allo stato condiviso.
- Break: Il comando
break mette in hold il Core 1 e poi riporta il PC corrente, l'istruzione in procinto di essere eseguita (disassemblata) e l'intero set di registri — il modo piu' rapido per vedere dove si trova un programma in esecuzione, prima di eseguire lo step o l'ispezione. go/cont riprende.
- Tasti rapidi e abbreviazioni: Per ridurre la digitazione durante il debug, i comandi piu' comuni hanno tasti rapidi a lettera singola —
c=cont, s=step, g=go, b=break, r=regs, h=hold — che vengono risolti prima della corrispondenza per prefisso, quindi non sono mai trattati come ambigui. Qualsiasi altro comando puo' essere inserito come il suo prefisso univoco piu' corto (ad es. ste=step, sta=status, dis=disassemble); un prefisso ambiguo elenca i candidati, e un nome digitato per intero ha sempre la precedenza.
Riferimento Comandi
| Comando |
Sintassi |
Descrizione |
help |
help |
Elenca tutti i comandi |
regs |
regs |
Dump di tutti i registri Z80, flag e conteggio cicli |
dm |
dm <p|f|v|r> <addr> [len] |
Dump memoria (fisica / fetch / virtuale / RP2350) |
search |
search [p|v] <start> <end> <hex..>|"text" |
Cerca nella memoria un pattern di byte o una stringa di testo ASCII. p = bus fisico, v = PSRAM virtuale, omettere per mappato. Le corrispondenze vengono visualizzate con 8 byte di contesto. Arresta automaticamente la CPU per l’accesso fisico/mappato. Pattern fino a 32 byte |
cmp |
cmp [f] <phys> <virt> <len> |
Confronta la memoria del bus fisico con la PSRAM virtuale |
dis |
dis [p|v] [addr] [count] |
Disassembla codice Z80 |
asm |
asm [addr] |
Assemblatore Z80 interattivo |
memmap |
memmap [block] |
Mostra la tabella dei puntatori dei banchi di memoria |
memptr |
memptr [addr] |
Mostra la tabella memPtr della PSRAM |
iomap |
iomap [port] |
Mostra la tabella degli handler delle porte I/O |
status |
status |
Stato del sistema (frequenza CPU, PSRAM, uptime) |
ver |
ver |
Versione firmware e informazioni sulla partizione |
drivers |
drivers |
Elenca i driver e le interfacce attive |
hold |
hold |
Mette in pausa l’emulazione della CPU |
release |
release |
Riprende l’emulazione della CPU |
break |
break |
Arresta lo Z80 in esecuzione e riporta dove si e’ fermato — PC, l’istruzione (byte + disassemblaggio) in procinto di essere eseguita e un dump completo dei registri. A differenza di hold (che mette in pausa silenziosamente), break mostra lo stato in modo da poter vedere esattamente dove si trova l’esecuzione (ad es. quando bloccato in un loop). Riemettere break rimostra lo stato. Riprendere con go/cont o eseguire il single-step con step. Va in timeout (~3 s) se lo Z80 e’ tenuto in reset o il suo clock e’ disattivato |
go |
go |
Continua (rilascia hold, breakpoint attivi) |
cont |
cont |
Alias per go. Continua l’esecuzione (rilascia hold, breakpoint attivi) |
step |
step [n] |
Single-step di n istruzioni |
bp |
bp <addr> |
Imposta un breakpoint (max 8) |
bc |
bc <n|*> |
Cancella il breakpoint n o tutti |
bl |
bl |
Elenca i breakpoint |
wm |
wm [p|v] <addr> <byte>... |
Scrive in memoria (fisica/virtuale/auto) |
fill |
fill [p|v] <addr> <len> [w|d] <val> |
Riempie la memoria con un valore costante |
copy |
copy <pv|fp|vp> <src> <len> <dst> |
Copia memoria tra fisica e virtuale |
memtest |
memtest <addr> <len> [pattern] |
Testa la memoria fisica (scrittura+lettura, scrittura+fetch, interlacciato) |
in |
in <port> |
Legge una porta I/O Z80 |
out |
out <port> <byte> |
Scrive su una porta I/O Z80 |
trace |
trace <on|off|dump [n]|clear|rt|byte ...> |
Controllo trace di esecuzione; rt abilita l’output trace in tempo reale; byte abilita il tracing a livello di byte |
verify |
verify <on|off> |
Attiva/disattiva la verifica completa del fetch degli opcode |
fwait |
fwait <0-4> |
Forza wait state aggiuntivi per M1 (fetch opcode); 0 = disattivato (predefinito) |
iowait |
iowait <0-8> |
Forza wait state aggiuntivi per cicli I/O; 0 = disattivato (predefinito) |
corrupt |
corrupt [clear] |
Visualizza o cancella le corruzioni di fetch rilevate |
fdctrace |
fdctrace <on|off|dump> |
Abilita/disabilita il trace I/O FDC; dump mostra le ultime 64 operazioni |
qdtrace |
qdtrace <on|off|dump> |
Abilita/disabilita il trace I/O Quick Disk; dump mostra le ultime 64 operazioni |
piodbg |
piodbg [clear] |
Visualizza la diagnostica hardware PIO dell’RP2350 (FDEBUG, FSTAT, FIFO, PC, GPIO); clear resetta i flag persistenti |
load |
load <p|v> <file> <addr> [len] [ofs] |
Carica un file dalla scheda SD dell’ESP32 nella memoria Z80. p = bus fisico, v = banco 0 PSRAM virtuale. file relativo a /sdcard/. Se len e’ omesso viene caricato l’intero file (fino a 64KB); se specificato, max 1MB. ofs opzionale per offset nel file. Arresta automaticamente la CPU per le scritture fisiche. Utilizza il banco PSRAM 63 come buffer temporaneo |
save |
save <p|pf|v> <file> <addr> <len> |
Salva la memoria Z80 in un file sulla scheda SD dell’ESP32. p = bus fisico, pf = fetch fisico (M1), v = banco 0 PSRAM virtuale. file relativo a /sdcard/. Max 64KB. Arresta automaticamente la CPU per le letture fisiche. Refresh DRAM periodico durante le letture fisiche |
dir |
dir [path] |
Elenca i file sulla scheda SD dell’ESP32. Il percorso opzionale e’ relativo a /sdcard/. Mostra nomi file e dimensioni |
echo |
echo [on|off] |
Attiva/disattiva l’echo del terminale |
reset |
reset |
Forza il reset dello Z80 |
set |
set <reg|flags|memmap|memptr|iomap> <idx> <val> |
Modifica registro Z80, flag, mappa memoria, memPtr o voce mappa I/O a runtime |
hist |
hist [n] |
Visualizza la cronologia dei comandi (preservata tra le sessioni tramite NVS dell’ESP32) |
savehst |
savehst |
Forza il salvataggio della cronologia dei comandi nel NVS dell’ESP32 |
ipl |
ipl |
Esegue un reset IPL (modalita’ BST) commutando il bit 3 della Porta C del PPI 8255. Resetta alla modalita’ boot senza un reset Z80 completo |
mmutrace |
mmutrace |
Dump delle informazioni di trace specifiche della macchina (stato MMU, snapshot dei registri I/O). L’output varia per persona tramite l’handler di trace registrato |
intcount |
intcount |
Mostra il conteggio degli acknowledge degli interrupt e lo stato corrente degli interrupt |
psync |
psync [start end] |
Sincronizza la memoria fisica con la PSRAM. Intervallo di indirizzi opzionale; predefinito sull’intero spazio di indirizzamento |
dskimage |
dskimage read <filename> [cylinders] [heads] / dskimage write <filename> |
Crea un’immagine di un floppy fisico in un file DSK sulla scheda SD (read), o scrive un file DSK dalla scheda SD su un floppy fisico (write). dskimage <filename> predefinito a lettura (compatibilita’ all’indietro). Rileva automaticamente la geometria se omessa |
busdiag |
busdiag |
Visualizza la diagnostica del bus (stato PIO, livelli dei segnali, contesa del bus) |
fdcimage |
fdcimage |
Visualizza lo stato e il progresso dell’imaging FDC |
fdcdiag |
fdcdiag |
Visualizza le informazioni diagnostiche del FDC (stato del controller, dump dei registri) |
gadiag |
gadiag |
Visualizza le informazioni diagnostiche del gate array (stato dei comandi, routing degli interrupt) |
Tasti rapidi e abbreviazioni. I comandi non devono essere digitati per intero. I sei comandi piu' usati hanno tasti rapidi a lettera singola — c (cont), s (step), g (go), b (break), r (regs) e h (hold) — e ogni altro comando puo' essere inserito come il suo prefisso univoco piu' corto (ad esempio ste per step, sta per status, dis per disassemble). Un nome di comando digitato per intero ha sempre la precedenza, e un prefisso ambiguo stampa l'elenco dei comandi corrispondenti.
Coprocessore ESP32
Il modulo ESP32-S3-PICO-1 funge da coprocessore gestendo tutte le funzioni di rete e archiviazione. Comunica con l'RP2350 tramite due interfacce:
- FSPI (50MHz, SPI a 4 fili) — Protocollo IPC Binario v1.1 — trasferimento dati bulk ad alta velocita' (immagini ROM, letture/scritture settori disco, download file di configurazione). Il protocollo utilizza un header di frame binario fisso da 64 byte con verifica di integrita' CRC32 (in sostituzione del precedente checksum XOR). I canali DMA sono pre-allocati all'inizializzazione e mai rilasciati, eliminando l'overhead di claim/unclaim per trasferimento e le condizioni di gara. I trasferimenti burst di settori permettono fino a 16 × 512 byte settori (8KB) in una singola transazione SPI, migliorando significativamente i tempi di caricamento delle immagini floppy e QuickDisk. Il canale DMA RX e' elevato a PRIORITA' ALTA per prevenire l'overflow del FIFO causato dalla contesa del bus QMI PSRAM del Core 1.
- UART (460,8kbaud) — protocollo comando/risposta per messaggi di controllo, query di stato e brevi scambi di dati.
Modalita' di Rete
Il firmware dell'ESP32 supporta tre modalita' di rete, selezionate a tempo di compilazione tramite file sdkconfig pre-costruiti:
| Modalita’ |
File di Configurazione |
WiFi |
USB NCM |
Console |
FCC/RED Richiesto |
| Solo WiFi |
sdkconfig.mode_wifi_only |
Si’ |
No |
USB Serial/JTAG |
Si’ |
| WiFi + NCM |
sdkconfig.mode_wifi_and_ncm |
Si’ |
Si’ |
TinyUSB CDC-ACM |
Si’ |
| Solo NCM |
sdkconfig.mode_ncm_only |
No |
Si’ |
TinyUSB CDC-ACM |
No |
USB NCM (Network Control Model) presenta un adattatore Ethernet CDC-NCM sulla porta USB OTG dell'ESP32-S3 (GPIO 19/20). Un dispositivo USB composito espone sia una porta seriale CDC-ACM (per il logging di debug) che l'interfaccia di rete NCM. Il server DHCP integrato assegna all'host un indirizzo IP dalla sottorete
192.168.7.0/24, con il picoZ80 accessibile a
192.168.7.1. Il tempo di lease e' di 120 minuti.
In modalita' WiFi+NCM, il server HTTP si collega a
INADDR_ANY:80 e serve entrambe le interfacce simultaneamente. Il WiFi si connette in modo asincrono cosi' l'interfaccia USB NCM e' disponibile immediatamente all'accensione.
Quando e' abilitato solo NCM, la radio WiFi e' completamente disabilitata, la pagina WiFi Manager viene rimossa dall'interfaccia web e il titolo del pannello di stato della Dashboard cambia da "WiFi Configuration" a "Network Configuration" (mostrando lo stato USB NCM invece dei dettagli SSID/WiFi). La rete di adattamento dell'antenna dell'ESP32-S3 non deve essere popolata sulla PCB.
Interfaccia Scheda SD
L'ESP32 gestisce la scheda SD tramite la sua interfaccia SPI. La scheda SD viene montata come FAT32 e tutto l'accesso ai file dall'RP2350 e' mediato dall'ESP32 — l'RP2350 invia comandi di I/O file tramite il collegamento FSPI/UART e l'ESP32 esegue le operazioni effettive di lettura/scrittura FAT32.
La scheda SD e' anche direttamente accessibile al web server dell'ESP32, che serve i file dalla directory
webfs/ e permette al File Manager di navigare e modificare i contenuti della scheda tramite HTTP.
Web Server
L'ESP32 esegue un server HTTP sulla porta 80 (nessun TLS — solo per uso su rete locale). Tutte le risorse web (HTML, CSS, JavaScript) vengono servite dalla directory
webfs/ sulla scheda SD, permettendo di aggiornare l'interfaccia web senza riflashare il firmware dell'ESP32. Il web server gestisce:
- Servizio di risorse web statiche dalla directory
webfs/ della scheda SD.
- Endpoint API REST per dati JSON (stato del sistema, lettura/scrittura configurazione, operazioni sui file).
- Endpoint di upload firmware OTA sia per l'RP2350 che per l'ESP32.
- Connessione WebSocket per aggiornamenti di stato della Dashboard in tempo reale.
Protocollo di Comando RP2350 ↔ ESP32
L'RP2350 (Core 0) comunica con l'ESP32 utilizzando un semplice protocollo comando/risposta sul collegamento UART. I comandi sono opcode a singolo byte con byte di payload opzionali. L'ESP32 conferma ogni comando con un byte di stato seguito da eventuali dati di risposta.
Categorie di comandi comuni:
- I/O File — apertura, lettura, scrittura, chiusura, elenco directory, stat file.
- Configurazione — richiesta contenuto config.json, scrittura configurazione aggiornata, richiesta di ricaricamento.
- Disco — mount/unmount immagine disco, lettura/scrittura settore (inoltrato dall'emulazione WD1773 e QDDrive). I nomi file delle immagini floppy e QuickDisk attualmente montate vengono tracciati dall'ESP32 e visualizzati nel menu Actions dell'interfaccia web.
- Sistema — query versione, richiesta di riavvio, lettura/scrittura NVS.
L'interfaccia FSPI viene utilizzata per i trasferimenti bulk dove il payload e' troppo grande per la UART (upload di immagini ROM, dati di settori disco), mentre la UART gestisce tutti i comandi di controllo.
Comandi IPC di Rete
L'implementazione del networking Celestite Fase 2 aggiunge cinque comandi IPC inter-core che l'RP2350 utilizza per richiedere operazioni di rete dall'ESP32. Questi comandi vengono inoltrati tramite il collegamento FSPI/UART e l'ESP32 esegue le corrispondenti operazioni BSD socket. Le connessioni non bloccanti utilizzano select() con un timeout, e flag pending per socket tracciano le operazioni in corso.
| Comando |
Opcode |
Descrizione |
IPCF_CMD_NET_CFG |
0x10 |
Ottieni la configurazione di rete dell’ESP32 — restituisce indirizzo IP, gateway, maschera di sottorete e indirizzo MAC |
IPCF_CMD_NET_SOCK |
0x11 |
Operazione ciclo di vita socket — apertura, connessione, ascolto, chiusura o disconnessione di un socket |
IPCF_CMD_NET_SEND |
0x12 |
Invia dati a un socket aperto |
IPCF_CMD_NET_RECV |
0x13 |
Ricevi dati da un socket aperto |
IPCF_CMD_NET_PING |
0x14 |
Richiesta ICMP echo (ping) |
I comandi socket W5100 supportati tramite questo livello IPC sono: OPEN, CONNECT, LISTEN, SEND, RECV, CLOSE e DISCON. L'ESP32 li traduce in chiamate API BSD socket standard (
socket(),
connect(),
listen(),
send(),
recv(),
close(),
shutdown()), permettendo alla scheda Celestite di comunicare con servizi di rete come il file server
netfs.py.
Watchdog e Diagnostica di Avvio
Il firmware dell'RP2350 utilizza un timer watchdog hardware per rilevare e recuperare da blocchi durante l'avvio e stalli del loop principale. Il watchdog viene abilitato nella fase iniziale della sequenza di avvio con un timeout di 30 secondi e viene rinnovato (watchdog_update()) ad ogni milestone importante. Se qualsiasi fase di avvio o iterazione del loop principale impiega piu' tempo del timeout, il watchdog resetta l'RP2350 automaticamente.
Tracciamento del Progresso di Avvio
Il progresso dell'avvio viene tracciato utilizzando i registri scratch del watchdog dell'RP2350, che sopravvivono ai reset del watchdog (ma non ai reset all'accensione). Questo permette al firmware di determinare, dopo un reset del watchdog, esattamente quale fase di avvio era stata raggiunta prima del blocco.
| Registro Scratch |
Nome |
Contenuti |
scratch[0–3] |
Cronologia avvio |
Ultimi quattro tentativi di reset — ogni voce codifica (attempt_count << 24) | (stage << 16) | (resetCause & 0xFFFF). Le voci si spostano ad ogni reset del watchdog: [0]←[1]←[2]←[3]←corrente. |
scratch[4] |
Diagnostica SPI |
Contatori diagnostici impacchettati per il collegamento FSPI: breadcrumb, tipo di messaggio, contatori timeout T1/T3, conteggio frame errati e conteggio OK. |
scratch[5] |
Marcatore magico |
Impostato a 0xB00710BE per indicare che i registri scratch contengono dati validi di progresso dell’avvio. |
scratch[6] |
Fase corrente |
Il codice della fase di avvio piu’ recente (vedi tabella sotto). |
scratch[7] |
Causa del reset |
Il codice della causa del reset dal controller hardware di reset. |
I codici delle fasi di avvio progrediscono da
0x01 (avvio) fino a
0x10 (ingresso nel loop principale). Le sotto-fasi all'interno del loop principale (
0x11–0x17) e l'elaborazione dei comandi inter-core (
0x20–0x27) forniscono un tracciamento a grana fine:
| Codice |
Fase |
Descrizione |
0x01 |
BOOTP_START |
Punto di ingresso raggiunto |
0x02 |
BOOTP_CLK_SET |
Clock di sistema configurato |
0x03 |
BOOTP_PSRAM_INIT |
Inizializzazione PSRAM avviata |
0x04 |
BOOTP_PSRAM_OK |
PSRAM inizializzata con successo |
0x05 |
BOOTP_STDIO_INIT |
USB stdio inizializzato |
0x06 |
BOOTP_PIO_INIT |
Macchine a stati PIO caricate |
0x07 |
BOOTP_Z80_INIT |
Contesto CPU Z80 inizializzato |
0x08 |
BOOTP_USB_INIT |
Bridge USB inizializzato |
0x0A |
BOOTP_ESP_HS_SYNC |
Sincronizzazione handshake SPI ESP32 |
0x0B |
BOOTP_CORE1_LAUNCH |
Core 1 avviato |
0x0D |
BOOTP_FSPI_INIT |
IPC binario FSPI inizializzato |
0x0E |
BOOTP_ESP_INIT |
Comunicazione ESP32 pronta |
0x10 |
BOOTP_MAIN_LOOP |
Ingresso nel loop principale |
0x11–0x17 |
Sotto-fasi loop principale |
Poll USB, inter-core, SPI NOP/CMD, task |
0x20–0x27 |
Comandi inter-core |
Caricamento floppy, caricamento QD, caricamento RAMFILE, I/O file |
Log Persistente PSRAM (plogf)
Gli ultimi 4KB della PSRAM da 8MB (indirizzo 0x117FF000) sono riservati per un log di debug persistente che sopravvive ai reset del watchdog. La macro plogf() scrive messaggi in stile printf in questo buffer durante l'avvio, prima che l'USB sia disponibile per il normale output debugf(). Al successivo avvio riuscito, la funzione dump_plog() emette i messaggi catturati sulla console di debug e poi cancella il buffer. Il log utilizza una struttura semplice: un marcatore magico di 4 byte (0x504C4F47 = "PLOG"), un contatore di lunghezza di 4 byte e un buffer di testo circolare di 3840 byte.
Diagnostica dei Fault
Il firmware installa handler di fault Cortex-M33 per hard fault, fault di gestione memoria, bus fault e usage fault. Quando si verifica un fault, l'handler salva uno snapshot diagnostico completo negli ultimi 256 byte della PSRAM (indirizzo 0x117FFF00) con un marcatore magico (0xFA017000), il tipo di fault, tutti i registri rilevanti (PC, LR, SP, R0–R3, R12, PSR), il Configurable Fault Status Register (CFSR), l'Hard Fault Status Register (HFSR), il Bus Fault Address Register (BFAR), il Memory Management Fault Address Register (MMFAR) e l'ID del core. L'handler entra quindi in un loop infinito, permettendo al watchdog di attivare un reset. All'avvio successivo, il firmware verifica la presenza di una diagnostica di fault valida e emette le informazioni catturate tramite debugf(), abilitando l'analisi post-mortem senza richiedere una sessione di debugger dal vivo.
Cancellazione della Configurazione Flash
Il meccanismo di aggiornamento OTA dell'RP2350 supporta due operazioni aggiuntive oltre al caricamento del firmware:
- Cancella App Config (
FW_CFGCLEAR_ID = 0xB1D7E5FA) — cancella la partizione App Config (immagini ROM e JSON minificato) associata allo slot firmware di destinazione. Questo forza il firmware a rileggere config.json dalla scheda SD al prossimo avvio, che e' necessario quando lo schema di configurazione e' cambiato tra le versioni firmware.
- Cancella Header Flash (
FW_HDRCLEAR_ID = 0xC2E8F6AB) — resetta l'header della partizione flash ai valori di fabbrica. La configurazione del bootloader (partizione 0) viene preservata, ma tutti i metadati delle partizioni applicative vengono ricostruiti da zero. Utilizzare questo quando la tabella delle partizioni e' diventata corrotta o quando si esegue il downgrade a una versione firmware precedente che si aspetta un layout diverso delle partizioni.
Entrambe le operazioni vengono attivate dalle checkbox della pagina web OTA dell'RP2350 e vengono eseguite dal bootloader durante il processo di aggiornamento firmware.
Debug SWD — RP2350
L'RP2350 supporta il debug completo a livello sorgente tramite ARM Serial Wire Debug (SWD). Collegare una sonda compatibile CMSIS-DAP (Raspberry Pi Debug Probe, Black Magic Probe o simile) ai Pin 1 (SWCLK), 2 (SWDIO) e 5 (GND) dell'header di debug.
Configurazione OpenOCD
Il picoZ80 richiede una piccola modifica allo script target OpenOCD standard dell'RP2350 per abilitare il debug SMP con porte GDB separate per core:
sudo cp /usr/local/share/openocd/scripts/target/rp2350.cfg \
/usr/local/share/openocd/scripts/target/rp2350_tzpu.cfg
Modificare rp2350_tzpu.cfg — trovare la riga target smp all'interno del blocco if {[string compare $_USE_CORE SMP] == 0} e rimuovere il # iniziale:
# Before:
#target smp $_TARGETNAME_0 $_TARGETNAME_1
# After:
target smp $_TARGETNAME_0 $_TARGETNAME_1
Questa singola modifica fa si' che OpenOCD registri il Core 0 sulla porta GDB 3333 e il Core 1 sulla porta GDB 3334, permettendo sessioni GDB indipendenti per core. Avviare OpenOCD prima di avviare GDB:
openocd -f interface/cmsis-dap.cfg -f target/rp2350_tzpu.cfg -c "adapter speed 5000"
Configurazione GDB
Aggiungere quanto segue a ~/.gdbinit (con percorsi assoluti corrispondenti alla posizione del proprio progetto) per permettere il caricamento automatico dei file .gdbinit per directory:
set history save on
set history filename ~/.gdb_history
set history size 65536
add-auto-load-safe-path /path/to/project/build/bin/model/BaseZ80/.gdbinit
add-auto-load-safe-path /path/to/project/build/bin/model/Bootloader/.gdbinit
Debug del Bootloader
# Terminal 1 — Core 0 (port 3333)
cd build/bin/model/Bootloader
cp ../../../../.gdbinit.bootloader.3333 .gdbinit
gdb-multiarch Bootloader.elf
# Terminal 2 — Core 1 (port 3334)
cd build/bin/model/Bootloader
cp ../../../../.gdbinit.bootloader.3334 .gdbinit
gdb-multiarch Bootloader.elf
Debug del Firmware Principale
# Terminal 1 — Core 0 (port 3333)
cd build/bin/model/BaseZ80
cp ../../../../.gdbinit.3333 .gdbinit
gdb-multiarch BaseZ80_0x10020000.elf
# Terminal 2 — Core 1 (port 3334)
cd build/bin/model/BaseZ80
cp ../../../../.gdbinit.3334 .gdbinit
gdb-multiarch BaseZ80_0x10020000.elf
# Memory dump (from GDB prompt) — hex + ASCII:
(gdb) xac 0x20000000 64
Il comando GDB xac <address> <count> e' definito nei file .gdbinit.3333 / .gdbinit.3334. Esegue un dump della memoria come output esadecimale e ASCII combinato ed e' utile per ispezionare i contenuti dei banchi PSRAM e lo stato dei dispositivi mappati in memoria.
Debug USB dell'ESP32
Il coprocessore ESP32-S3 dispone di un'interfaccia USB-JTAG integrata — non e' richiesta una sonda di debug esterna. Collegare un cavo USB dal PC host direttamente alla porta USB dell'ESP32 sulla scheda picoZ80.
# Start OpenOCD for ESP32-S3
openocd -f board/esp32s3-builtin.cfg
# In a second terminal — launch Xtensa GDB
xtensa-esp32s3-elf-gdb esp32/build/main.elf
(gdb) target extended-remote :3333
Assicurarsi che l'ELF sia stato compilato dalla stessa revisione sorgente del firmware in esecuzione sul dispositivo, in modo che i simboli e gli indirizzi corrispondano correttamente.
Sistema di Compilazione
Il firmware del picoZ80 utilizza CMake con il Raspberry Pi Pico SDK 2.x. Il sistema di compilazione produce il firmware
Bootloader e il firmware
Applicazione. L'applicazione viene compilata in quattro varianti — due partizioni (Partizione 1 a 0x10020000, Partizione 2 a 0x10520000) ciascuna in configurazione standard e DBGSH. Le varianti DBGSH aggiungono
INCLUDE_DBGSH ai flag di compilazione, abilitando la shell di debug completa sul Canale 1 USB CDC. Il firmware dell'ESP32 viene compilato separatamente utilizzando ESP-IDF v5.4, gestito tramite Docker.
Il modo piu rapido per ottenere un ambiente funzionante e lo script automatizzato
setup_picoZ80 (macOS/Linux e Windows), che installa l'SDK e tutte le dipendenze e crea script di build pronti all'uso — consultare il
README e la
Guida dello Sviluppatore. I target CMake, i flag e i comandi qui sotto documentano la build sottostante che lo script automatizza, per chi preferisce configurarla manualmente.
Target di Compilazione CMake
| Target |
Output |
Indirizzo Flash |
Note |
Bootloader |
Bootloader.elf, Bootloader.uf2 |
0x10000000 |
|
BaseZ80_0x10020000 |
BaseZ80_0x10020000.elf, .bin |
0x10020000 (Slot 1) |
Standard (senza shell di debug) |
BaseZ80_0x10520000 |
BaseZ80_0x10520000.elf, .bin |
0x10520000 (Slot 2) |
Standard (senza shell di debug) |
BaseZ80_DBGSH_0x10020000 |
BaseZ80_DBGSH_0x10020000.elf, .bin |
0x10020000 (Slot 1) |
DBGSH — include la shell di debug ICE |
BaseZ80_DBGSH_0x10520000 |
BaseZ80_DBGSH_0x10520000.elf, .bin |
0x10520000 (Slot 2) |
DBGSH — include la shell di debug ICE |
I modelli
SharpZ80,
AmstradZ80,
TatungZ80 e
OpenZ80 seguono lo stesso schema slot / DBGSH, ad es.
OpenZ80_0x10020000,
OpenZ80_0x10020000_DBGSH,
OpenZ80_0x10520000 e
OpenZ80_0x10520000_DBGSH (ciascuno emette
.elf,
.bin,
.hex e
.map; gli slot applicativi usano
.bin semplice, non UF2). Compilare un singolo modello con il filtro
build_tzpuPico.sh, ad es.
build_tzpuPico.sh open.
Flag di Compilazione CMake Principali
| Flag |
Effetto |
INCLUDE_SHARP_DRIVERS |
Compila tutti i driver periferici Sharp MZ (MZ700, MZ80K, MZ800, MZ80A, MZ80B, MZ2000, MZ2200, MZ2500, MZ1500, WD1773, T3444M, QDDrive, RFS, TZFS, MZ-1E05, MZ80AFI, MZ80FIO, MZ8BFI, MZ8BIO3, MZ1E24, Z80SIO, MZ-1E14, MZ-1E19, MZ-1R12, MZ-1R18, MZ-1R23, MZ-1R37, PIO-3034, Celestite, MZ-1E30). |
INCLUDE_AMSTRAD_DRIVERS |
Compila i driver periferici Amstrad PCW (PCW9512, uPD765). |
INCLUDE_TATUNG_DRIVERS |
Compila i driver periferici Tatung Einstein (EinsteinTC01, EinsteinFDC, WD1770). |
INCLUDE_OPEN_DRIVERS |
Compila la persona sperimentale OpenZ80 (Open.c) e le schede di interfaccia indipendenti dalla macchina (MZ-1R12/1R18/1R23/1R37, PIO-3034, MZ8BIO3, MZ1E24, Z80SIO, MZ-1E05, WD1773, Celestite). |
TARGET_MODEL_TATUNG |
Imposta Tatung Einstein come modello target esclusivo (utilizzato nella build TatungZ80). |
TARGET_MODEL_OPEN |
Imposta la persona sperimentale OpenZ80 come modello target esclusivo (utilizzato nella build OpenZ80; abilita INCLUDE_OPEN_DRIVERS). |
INCLUDE_DBGSH |
Compila la shell di debug ICE sul Canale 1 USB CDC. Presente solo nelle varianti di build DBGSH. |
CMAKE_BUILD_TYPE=Debug |
Abilita i simboli di debug e disabilita l’ottimizzazione. Necessario per il debug a livello sorgente con GDB. |
CMAKE_BUILD_TYPE=Release |
Ottimizzazione completa (-O3). Utilizzato per il firmware di produzione. |
Comandi di Compilazione
# First time: clone and build the SDK
./get_and_build_sdk.sh
# Standard release build (RP2350 only)
./build_tzpuPico.sh
# Debug build
./build_tzpuPico.sh DEBUG
# Full build: RP2350 + ESP32 (ESP32 built via Docker)
./build_tzpuPico.sh ALL
# ESP32 only, using the Docker idf54 alias
cd projects/tzpuPico/esp32
idf54 build
Lo script build_tzpuPico.sh incrementa automaticamente il numero di versione dopo una compilazione riuscita e copia i file di output versionati in fw/uf2/ (UF2 del bootloader) e fw/bin/ (binario applicativo per OTA). L'UF2 del Bootloader viene utilizzato solo per il flashing iniziale tramite mass-storage USB. I binari degli slot applicativi utilizzano il formato binario semplice (non UF2) perche' risiedono a indirizzi flash non standard.
Selezione della Modalita' di Rete ESP32
Per cambiare modalita' di rete, copiare il file di configurazione pre-costruito appropriato come sdkconfig prima della compilazione:
cd esp32/
cp sdkconfig.mode_ncm_only sdkconfig # NCM only (FCC/RED safe)
# or: cp sdkconfig.mode_wifi_only sdkconfig
# or: cp sdkconfig.mode_wifi_and_ncm sdkconfig
idf.py build
idf.py flash
Siti di Riferimento
Avviso Normativo sulle Telecomunicazioni Wireless
Questo dispositivo incorpora un modulo wireless ESP32-S3-PICO-1 in grado di trasmettere nella banda ISM a 2,4 GHz, rendendolo un radiatore intenzionale ai sensi delle normative sulle radiofrequenze a livello mondiale (incluso FCC Part 15 Subpart C negli Stati Uniti e la Direttiva sulle Apparecchiature Radio 2014/53/UE nell'Unione Europea) quando e' installato il firmware WiFi.
Configurazione di Fabbrica
Come fornito, la scheda picoZ80 e' flashata con il firmware
solo NCM (
sdkconfig.mode_ncm_only). In questa configurazione la radio WiFi e' completamente disabilitata e i componenti della rete di adattamento dell'antenna dell'ESP32-S3 non sono popolati sulla PCB. Poiche' non avviene alcuna trasmissione RF, il dispositivo
non e' un radiatore intenzionale e non richiede la certificazione FCC, CE/RED o equivalente. Puo' essere venduto, distribuito o regalato senza autorizzazione normativa.
Aggiunta del WiFi
Gli utenti finali possono popolare la rete di adattamento dell'antenna e flashare il firmware abilitato WiFi (
sdkconfig.mode_wifi_only o
sdkconfig.mode_wifi_and_ncm) per uso personale, sperimentale o educativo. Una volta abilitato il WiFi, il dispositivo diventa un radiatore intenzionale e si applicano le seguenti regole:
Sebbene il modulo ESP32-S3-PICO-1 stesso possieda certificazioni normative preesistenti (FCC, CE e altre), tali certificazioni a livello di modulo
non si estendono automaticamente a un prodotto finito che incorpora il modulo. L'esenzione per moduli pre-certificati consente ai
singoli hobbisti di costruire un numero limitato di dispositivi per
uso personale, sperimentale o educativo senza ottenere un'autorizzazione separata per l'apparecchiatura.
Limitazioni Importanti
- I dispositivi con firmware WiFi non devono essere venduti, offerti in vendita, regalati o altrimenti distribuiti a terzi a meno che il prodotto finito non sia stato testato in modo indipendente e abbia ottenuto la propria autorizzazione per l'apparecchiatura (ad es. FCC ID, marcatura CE con valutazione di un Organismo Notificato) nella giurisdizione pertinente.
- La costruzione di questo progetto con WiFi abilitato per uso personale in quantita' limitate e' generalmente consentita ai sensi delle disposizioni per hobbisti e uso sperimentale (ad es. FCC § 15.23), a condizione che il dispositivo non causi interferenze dannose.
- La vendita commerciale con WiFi abilitato richiede la certificazione completa FCC/RED (o equivalente) a livello di prodotto.
- I requisiti normativi variano per paese. I costruttori al di fuori degli Stati Uniti dovrebbero consultare la propria autorita' nazionale per le radiofrequenze per le regole applicabili.
Responsabilita' del Costruttore
E' esclusiva responsabilita' del costruttore assicurarsi che qualsiasi dispositivo realizzato a partire da questi design sia conforme a tutte le normative sulle radiofrequenze applicabili nella propria giurisdizione. L'autore fornisce questi design per uso personale, educativo e hobbistico e non garantisce che un dispositivo costruito a partire da essi soddisfi i requisiti normativi per la distribuzione commerciale.