picoZ80 テクニカルガイド

picoZ80 テクニカルガイド

このガイドでは、picoZ80 のハードウェアアーキテクチャ、RP2350 PIO バスインターフェース、メモリモデル、JSON 設定リファレンス、仮想ハードウェアフレームワーク、デバッグ手順について説明します。内部構造を理解したい、新しいドライバーを書きたい、新しいホストマシンにファームウェアを移植したい、またはファームウェアレベルの問題をデバッグしたいデベロッパーを対象としています。
エンドユーザー向けのセットアップとウェブインターフェースの使用方法については、picoZ80 ユーザーマニュアルを参照してください。プロジェクトの概要とビルド手順については、picoZ80 プロジェクトページを参照してください。

ハードウェアアーキテクチャ

picoZ80 は、DIP-40 パッケージのフットプリント内に収まるよう設計されたコンパクトな単一の PCB 上に 5 つのサブシステムを統合しています。すべてのロジックは 3.3V で動作し、Z80 バスインターフェースが 5V ホストバスのレベル変換と電流駆動を処理します。

システムブロック図

┌─────────────────────────────────────────────────────────────────────────┐
│                         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)   │
         └───────────────┘

主要コンポーネント

コンポーネント デバイス 役割
プライマリ MCURP2350B (QFN-80)デュアル Cortex-M33、150MHz(OC で最大 300MHz)、512KB SRAM、12 個の PIO ステートマシン、48 本の GPIO ピン
FlashW25Q128 (16MB SPI)ブートローダー、デュアルファームウェアスロット、設定パーティション
PSRAM8MB SPI PSRAMZ80 アドレス空間用の 64 × 64KB RAM/ROM バンク
コプロセッサESP32-S3-PICO-1WiFi、SDカード、ウェブサーバー、OTA
USB ハブCH334FUSB ハブ、ファームウェアアップデートのブリッジング
電源TLV62590BV5V → 3.3V 同期整流降圧コンバーター

RP2350B GPIO 割り当て

RP2350B QFN-80 パッケージは 48 本の GPIO ピンを提供します。picoZ80 はほぼすべてのピンを使用します。割り当てはボード設計で固定されており、PIO プログラムに反映されています。
GPIO 範囲 信号 方向
GPIO 0–15 A0–A15(Z80 アドレスバス) 出力(PIO が駆動)
GPIO 16–23 D0–D7(Z80 データバス) 双方向(PIO トライステート)
GPIO 24 MREQ 出力
GPIO 25 IORQ 出力
GPIO 26 RD 出力
GPIO 27 WR 出力
GPIO 28 M1 出力
GPIO 29 RFSH 出力
GPIO 30 BUSREQ 入力
GPIO 31 BUSACK 出力
GPIO 32 HALT 出力
GPIO 33 INT 入力
GPIO 34 NMI 入力
GPIO 35 WAIT 出力
GPIO 36 CLK 入力(ホストクロック)
GPIO 37 RESET 入力
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

ファームウェアアーキテクチャ

RP2350 ファームウェアは、RP2350-arm-s プラットフォームを対象とする Raspberry Pi Pico SDK 2.x でビルドされます。ファームウェアは ブートローダーアプリケーション という 2 つの独立した実行ファイルに分かれています。

Flash メモリレイアウト

パーティション アドレス範囲 サイズ 内容
ブートローダー 0x10000000 – 0x1001FFFF 128KB USB ブリッジ、ファームウェアアップデート、パーティションセレクター
App Slot 1 0x10020000 – 0x1051FFFF 5MB Z80 ファームウェア — アクティブなアプリケーション(スロット 1)
App Slot 2 0x10520000 – 0x10A1FFFF 5MB Z80 ファームウェア — アクティブなアプリケーション(スロット 2)
App Config 1 0x10A20000 – 0x10C9FFFF 2.5MB ROM イメージ+ミニファイされた設定 JSON(スロット 1)
App Config 2 0x10CA0000 – 0x10F1FFFF 2.5MB ROM イメージ+ミニファイされた設定 JSON(スロット 2)
General Config 0x10F20000 – 0x10FFEFFF 892KB コア設定、スクラッチ領域
Partition Table 0x10FFF000 – 0x11000000 4KB アクティブなスロット番号、チェックサム、メタデータ

デュアルコアの役割分担

2 つの Cortex-M33 コアにはまったく別個の役割が割り当てられており、コア間メッセージキュー(queue_t)を介して通信します。この分離により、コア 0 のリアルタイム性を要しない処理が、コア 1 上の Z80 バスサイクルにジッターを持ち込むことが決してないようにしています。
コア 役割
コア 0 USB CDC シリアルブリッジ、ファームウェアアップデートの調整、ファイル I/O(UART 経由で ESP32 にリレー)、ESP32 コマンドのディスパッチ(ディスクイメージの変更、設定の再読み込み、バージョン照会)、パーティション管理、コア間メッセージのディスパッチ。
コア 1 Z80 バスエミュレーションのホットループ — 専有で実行されます。PIO FIFO をサービスし、各バスサイクルをメモリマップに対して解決し、物理ホストハードウェア(PHYSICAL)、PSRAM(RAM/ROM)、または仮想ハードウェアハンドラー(FUNC)にディスパッチします。内部ループは SRAM に配置されます。

PIO バスインターフェース

Z80 バスインターフェースは、RP2350 PIO アセンブリ(z80.pio)で完全に実装されています。RP2350 は 3 つの PIO ブロック(PIO 0、PIO 1、PIO 2)を提供し、それぞれに 4 つのステートマシンがあります(合計 12 のステートマシン)。Z80 ファームウェアはその 12 個すべてを使用します。
PIO プログラムは Cortex-M33 コアとは独立して実行されます。コア 1 が PSRAM アクセスや仮想ハードウェアの関数呼び出しで占有されているときでも、バスインターフェースは決定論的に応答し続けます。ステートマシンはポーリングではなく PIO IRQ フラグを介して通信するため、マシン間のレイテンシが排除されます。

PIO プログラム一覧

PIO ステートマシン プログラム 機能
0 SM 0 z80_addr 16 ビットアドレス(A0–A15)をバスに出力し、SM 2 にサイクル開始を通知します。
0 SM 1 z80_data トライステート制御で D0–D7 を駆動またはサンプリングします。BUSRQ 中は解放されます。
0 SM 2 z80_cycle 最上位のバスサイクルシーケンサー — フェッチ、リード、ライト、I/O、DRAM リフレッシュの各サイクルを統括します。
0 SM 3 z80_fetch オペコードフェッチサイクル(M1 + MREQ + RD)。
1 SM 0 z80_mem_read メモリリードサイクル(MREQ + RD)。
1 SM 1 z80_mem_write メモリライトサイクル(MREQ + WR)。
1 SM 2 z80_io_read I/O リードサイクル(IORQ + RD)。
1 SM 3 z80_io_write I/O ライトサイクル(IORQ + WR)。
2 SM 0 z80_busrq BUSREQ/BUSACK を管理します。/IORQ、/MREQ、/RFSH、/M1、/HALT、/WR、/RD を解放します。
2 SM 1 z80_nmi NMI のアサートを検出し、コア 1 に通知します。
2 SM 2 z80_clk_sync PIO ステートマシンをホスト Z80 CLK 信号に同期します。
2 SM 3 z80_int_ack 割り込みアクノリッジサイクル(M1 + IORQ)を処理します。

PIO IRQ 信号の規約

ステートマシン間の通信には PIO IRQ フラグを使用します。コア 1 はホットループでこれらのフラグを監視し、各バスイベントに応じた処理を行います。
IRQ イベント
IRQ 0 アドレス確定/サイクル開始 — 新しいバスサイクルが始まり、A0–A15 が安定しています。
IRQ 1 データフェーズ — データバスの方向が解決されました。D0–D7 を駆動またはサンプリングする必要があります。
IRQ 2 T1 検出 — 現在のサイクルにおける T1 の立ち上がりエッジ。内部動作をホストクロックに同期するために使用します。
IRQ 3 RESET イベント — ホストの RESET ラインがアサートされました。コア 1 はエミュレーション状態を再初期化する必要があります。
IRQ 4 NMI 検出 — ホストの NMI ラインがアサートされました。
IRQ 6 BUSRQ アクティブ — ホストが BUSREQ をアサートしました。PIO がバスを解放中です。

ウェイトステートの生成

PIO 2 SM 0 の z80_wait PIO プログラムは、/WAIT をアサートすることでホストバスに設定可能な T サイクルウェイトステートを挿入します。追加するウェイトステートの数は、config.jsontcycwait パラメータによってメモリブロックまたは I/O ブロックごとに制御されます。
ウェイトステートは、RP2350 がデータをホストバスに提示する前に PSRAM アクセスや仮想ハードウェアの関数呼び出しを完了させるための追加時間を必要とする場合に必要となります。tcycsync パラメータは T1 同期(PIO 2 SM 1 の z80_sync)を有効にします。これにより PSRAM アクセスウィンドウが各バスサイクルの T1 立ち上がりエッジにロックされ、ホストクロックに精密なタイミングを依存するアプリケーション(カセット、シリアルビットバンギング)でのタイミングドリフトを防ぎます。

PIO アーキテクチャ — バスサイクルの再現方法

RP2350 のプログラマブル I/O(PIO)サブシステムは、picoZ80 がサイクル精度の Z80 バスタイミングを再現できる鍵となる技術です。PIO ステートマシンがどのように連携して動作するかを理解することは、バスインターフェースを変更したり、タイミングの問題をデバッグしたりする人にとって不可欠です。

RP2350 PIO の基礎
各 PIO ブロックには、共有された 32 命令のメモリから小さなプログラムを実行する 4 つの独立したステートマシン(SM)が含まれています。ステートマシンは Cortex-M33 コアとは独立して、システムクロック周波数(最大 300 MHz)で動作します。picoZ80 が使用する主要な PIO リソース:
  • TX FIFO — CPU からステートマシンへの 4 エントリのキュー。コア 1 上の C コードがデータ(アドレス、制御ワード、注入される命令)を FIFO にプッシュし、PIO プログラムが out または pull でそれをプルします。
  • RX FIFO — ステートマシンから CPU への 4 エントリのキュー。PIO が in でデータバスのサンプルを FIFO にプッシュし、コア 1 が各バスサイクル完了後にそれを読み取ります。
  • IRQ フラグ — PIO ブロック内のすべてのステートマシンで共有される 8 個のフラグ(IRQ 0–7)。フラグは PIO ブロックをまたいでも参照できます(あるブロックの IRQ 0–3 は隣接ブロックの IRQ 4–7 にマップされます)。ステートマシンは irq set / irq wait / irq clear を使用して、互いに、また C コードと同期します。
  • スクラッチレジスタ X と Y — SM ごとの 2 つの 32 ビットレジスタで、ループカウンターや一時的な値に使用します。
  • out exec — TX FIFO から値をプルし、それを PIO 命令として実行する特殊な命令。これは C コードがバスサイクルシーケンスを動的に制御する仕組みです(後述)。
  • set pins / out pins — GPIO ピンを直接駆動します。set は即値の 5 ビット値を使用し、out は出力シフトレジスタ(OSR)からピンへデータをシフトします。
  • in pins — GPIO ピンを入力シフトレジスタ(ISR)にサンプリングし、自動的に RX FIFO へプッシュします。
  • wait gpio — 指定した GPIO ピンが指定したレベルに達するまで SM をストールさせます。ホスト Z80 クロック信号との同期に多用されます。
  • サイドセット(Side-set) — 命令サイクルを消費せずに、任意の命令の副作用として 1 本または 2 本の GPIO ピンを駆動できます。picoZ80 は 2 ビットのサイドセットを使用して、他の操作と同時に /RD/WR を制御します。
  • JMP PIN — 指定した GPIO ピンのレベルに基づく条件分岐。/WAITBUSREQ/NMI/RESET のテストに使用します。

out exec の仕組み — 動的な命令注入
picoZ80 PIO 設計の最も特徴的な機能は、z80_cycle オーケストレーターステートマシンでの out exec, 16 の使用です。この命令は TX FIFO から 16 ビット値をプルし、それを PIO 命令として即座に実行します — その値はデータではなく、ステートマシンが次に実行する命令そのものです。
この仕組みにより、コア 1 上の C コードはバスサイクルシーケンスをリアルタイムで制御できます。各サイクルタイプに対して固定の PIO プログラムをロードするのではなく、コア 1 が事前エンコードされた PIO 命令のシーケンスを TX FIFO にプッシュし、サイクル SM がそれを 1 つずつ実行します。
// 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.
C コードは起動時に、各サイクルタイプ(フェッチ、メモリリード、メモリライト、I/O リード、I/O ライト、リフレッシュ、割り込みアクノリッジ)に対してこれらの命令シーケンスを事前計算します。実行中、コア 1 は適切な事前構築済みシーケンスを選択し、それを FIFO にプッシュします。このアプローチには 2 つの重要な利点があります。
  • プログラム空間の効率性 — 各 PIO ブロックには 32 命令スロットしかありません。命令を動的に注入することで、サイクル SM はすべてのサイクルタイプを統括するのにわずか 7 命令のプログラムメモリしか必要としません。実際のサイクルタイプのプログラム(フェッチ、リード、ライトなど)は、常駐する PIO プログラムとしてではなく、エンコードされた命令の C 配列として存在します。
  • 柔軟性 — C コードは、特殊なケース(追加のウェイトステートの挿入、リフレッシュフェーズのスキップ、デバッグ用の非標準サイクルの生成など)を処理するために、注入する命令シーケンスを実行時に変更できます。

ステートマシンの協調
12 個のステートマシンは協調したパイプラインとして動作します。次の図は、典型的なメモリリードサイクルの流れを示しています。
                  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
IRQ ベースのハンドシェイクにより、制御信号がアサートされる前にアドレスバスが安定していること、そしてデータがバスサイクルの正しいポイントでサンプリングされることが保証されます。ステートマシンは決してポーリングを行いません — wait 0 irq N を使用して該当するイベントが発生するまでスリープし、待機中は CPU サイクルをまったく消費しません。

Z80 フェッチサイクル(M1 サイクル) — ステップごとの解説
オペコードフェッチは最も複雑な Z80 バスサイクルです — メモリリードとリフレッシュサイクルを組み合わせます。z80_fetch プログラムはホストクロックの 4 T サイクルにわたって実行されます。
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
PIO プログラムはこれを次のように実装します。
  1. T1 立ち上がりエッジz80_addr SM が PC 値を A0–A15 に出力します。z80_fetchset pins を介して /M1 を Low にアサートします。
  2. T1 立ち下がりエッジ/MREQ/RD が(set pins とサイドセットを介して)Low にアサートされます。これでアドレスが有効になり、メモリシステムが応答を開始できます。
  3. T2 — SM はウェイトステートループに入ります。CLK の立ち上がりエッジ、続いて立ち下がりエッジを待ち、jmp pin を介して /WAIT ピンをチェックします。/WAIT が Low であれば SM はループします(Tw サイクルを追加)。High であれば T3 に進みます。
  4. T3 立ち上がりエッジin pins, 8 が D0–D7(オペコードバイト)をサンプリングし、RX FIFO にプッシュします。IRQ 1 がセットされ、リフレッシュアドレスを出力すべきことを z80_data/z80_addr に通知します。/M1/MREQ/RD がデアサートされ、/RFSH が Low にアサートされます。
  5. T3 立ち下がりエッジ — (リフレッシュの行ストローブのために)/MREQ が再びアサートされます。
  6. T4 — リフレッシュが続きます。T4 の終わりに /MREQ/RFSH がデアサートされます。サイクル SM は start_cycle に戻り、次のバスサイクルに備えます。
コア 1 は RX FIFO からオペコードを読み取り、それを使用して命令をデコードし、後続でいくつのメモリサイクルや I/O サイクルが必要かを判断し、適切な命令シーケンスをプッシュします。

メモリリードおよびライトサイクル
メモリリードおよびライトサイクルはフェッチよりも単純です — リフレッシュフェーズがなく、3 T サイクルにわたります。
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↓)
リードでは、z80_mem_read SM が T1 立ち下がりエッジで /MREQ/RD をアサートし、T2 を通過し(ウェイトステートのために /WAIT をチェック)、T3 立ち下がりエッジで in pins, 8 を使用してデータバスをサンプリングします。ライトでは、z80_mem_write が T1 立ち下がりエッジで /MREQ をアサートし、z80_data によってデータバスが駆動された後、T2 立ち下がりエッジで /WR をアサートします。どちらも T3 の終わりにすべての制御信号をデアサートします。

I/O リードおよびライトサイクル
Z80 I/O サイクルは /MREQ の代わりに /IORQ を使用し、常に T2 と T3 の間に自動的なウェイトステート(Tw)を含みます。これは Z80 のアーキテクチャ上の特徴です — この追加サイクルにより低速な I/O デバイスに応答する時間が与えられます。
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↓)
z80_io_read SM は T2 立ち上がりエッジで /IORQ/RD をアサートします(メモリサイクルのような T1 ではありません — これは Z80 仕様です)。自動的な Tw ウェイトステートは、メモリサイクルで使用されるのと同じ jmp pin / wait ループパターンで実装されます。I/O ライトは同じパターンに従い、/RD/WR に置き換えます。

BUSREQ / BUSACK の処理
z80_busrq SM(PIO 2 SM 0)はホストの /BUSREQ 入力ピンを監視します。/BUSREQ がアクティブ(Low)になると、SM は次の処理を行います。
  1. バス要求が保留中であることをサイクル SM に通知するために IRQ 6 をセットします。
  2. 現在のバスサイクルが完了するのを待ちます(wait 1 irq 0)。
  3. バス解放状態のピンの方向と値を指定する 32 ビットの制御ワードを TX FIFO からプルします — これによりアドレスバスとデータバスがトライステートになり、/BUSACK が Low にアサートされます。
  4. /BUSREQ が非アクティブ(High)になるまで jmp pin でスピンします。
  5. 通常のピン方向を復元し /BUSACK をデアサートするために、2 つ目の 32 ビットワードをプルします。
  6. IRQ 6 をクリアし、サイクル SM が再開できるようにします。
サイクル SM は、すべてのサイクルの開始時に wait 0 irq 6 を介して IRQ 6 をチェックします — フラグがセットされている場合、SM はバス要求が完了するまでストールします。これにより、バス解放はサイクルの途中ではなく、サイクル間でクリーンに行われることが保証されます。

クロック同期
すべてのサイクルタイプの SM は、wait 1 gpio Z80_PIN_CLK(立ち上がりエッジを待つ)と wait 0 gpio Z80_PIN_CLK(立ち下がりエッジを待つ)を使用してホスト Z80 クロックに同期します。つまり、
  • PIO プログラムは クロック周波数に依存しません — DC から RP2350 が追従できる最大レート(PIO システムクロックと GPIO サンプリングレートによって制限される)まで、あらゆるホストクロック速度で動作します。
  • RP2350 の 300 MHz PIO クロックは、3.5 MHz における Z80 の 1 T ステートあたり約 85 PIO サイクルを提供します。これは、クロックエッジ間で PIO 命令を実行し、FIFO のプッシュ/プルを行い、IRQ フラグをチェックするのに十分すぎる時間です。
  • z80_sync SM(PIO 2 SM 1)は、C コードが PSRAM アクセスをホストクロックに揃えるために使用する T1 同期 IRQ を提供し、クロック感応性のあるホストソフトウェアでのタイミングドリフトを防ぎます。
  • z80_clk_sync SM(PIO 2 SM 2)は、ホストクロックを別の GPIO 上に再生成し、外部モニタリングやロジックアナライザのトリガリング用にクリーンなクロック出力を提供します。

割り込みアクノリッジサイクル
z80_int_ack SM(PIO 2 SM 3)は Z80 割り込みアクノリッジシーケンスを実装します。C コードが割り込み条件を検出すると、int_ack プログラムをロードします。このサイクルはフェッチに似ていますが、主な違いがあります。
  • /M1 は(フェッチと同様に)T1 でアサートされますが、ウェイトステート(Tw1)では /MREQ の代わりに /IORQ がアサートされます。
  • 割り込みを発生させたデバイスがデータバスにベクターを配置する時間を与えるために、2 つの自動ウェイトステート(Tw1、Tw2)が挿入されます。
  • ベクターバイトが D0–D7 から読み取られ、RX FIFO にプッシュされます。
  • フェッチのリフレッシュフェーズと同一のリフレッシュサイクルが続きます。

メモリモデル

メモリアクセスは、レイテンシが段階的に増加する 3 つの階層を通じて解決されます。この 3 階層設計により、一般的なケース(PSRAM 上の RAM/ROM)が高速である一方で、仮想ハードウェアや物理ホストのパススルーに対して最大限の柔軟性が確保されます。

階層 1 — RP2350 SRAM ディスパッチテーブル

RP2350 の 512KB オンチップ SRAM に常駐する 32 ビット membankPtr 値の 128 エントリ配列が、すべてのバスサイクルに対して O(1) のブロックタイプ検索を提供します。1 つのエントリが 64KB の Z80 アドレス空間の各 512 バイトブロックをカバーします(128 × 512 = 65,536 バイト)。各エントリは次の情報をエンコードします。
  • ブロックタイプ(PHYSICAL、RAM、ROM、FUNC など)。
  • PSRAM 上のブロックの場合:PSRAM バンク番号とオフセット。
  • FUNC ブロックの場合:仮想ハードウェア関数ポインタテーブルへのインデックス。
これは最速のパスです — コア 1 は、次に何をするかを決定する前に、現在のアドレスに対するディスパッチテーブルエントリを単一の SRAM アクセス(300MHz でゼロウェイトステート)で読み取ります。

階層 2 — 外部 PSRAM

8MB PSRAM は次のように構成されています。
  • 64 バンク × 64KB — Z80 アドレス空間用の RAM または ROM イメージデータ。
  • 64KB memPtr — PTR タイプブロック用のバイト単位リダイレクトポインタ配列。
  • 64KB memioPtr — メモリマップされた FUNC デバイス用の関数ポインタ配列。
  • 64KB ioPtr — I/O ポート FUNC デバイス用の関数ポインタ配列。
PSRAM は DMA を備えた RP2350 専用 SPI ペリフェラルを介してアクセスされます。アクセスレイテンシは決定論的であり、バス違反を回避するためにウェイトステートジェネレーターによって管理されます。

階層 3 — 16MB SPI Flash

ROM イメージは、起動時に Flash(または ESP32 経由で SDカード)から PSRAM にロードされます。実行時にはバスサイクルのために Flash がアクセスされることはなく、すべての ROM データは PSRAM から提供されます。Flash は次の用途に使用されます。
  • ブートローダーおよびアプリケーションファームウェア。
  • ミニファイされた config.json(起動ごとに SDカードからキャッシュ)。
  • App Config パーティション内の ROM イメージ(SDカードがない場合に使用)。

メモリブロックタイプ

タイプ 説明
PHYSICAL パススルー — RP2350 がバスを解放し、物理ホストメモリが応答します。ホストのネイティブ ROM および RAM に使用します。
PHYSICAL_VRAM PHYSICAL と同様ですが、ホストビデオ RAM のタイミングのために追加のウェイトステートを含みます。MZ-700/MZ-80A の VRAM 領域に適しています。
PHYSICAL_HW ホストハードウェアレジスタ(メモリ空間内の I/O マップデバイス)用のパススルー。
RAM リード/ライト — PSRAM バンクで裏付けられます。RP2350 が PSRAM への/からのリードとライトをサービスします。
ROM リードオンリー — PSRAM バンクで裏付けられます。ライトサイクルは黙って無視されます(ホストは通常のバスタイミングを見ますが、データは格納されません)。
VRAM PSRAM 上のビデオ RAM。ライトサイクルは PSRAM と物理ホスト VRAM の両方に同時にミラーされます。
FUNC 仮想ハードウェア — 各アクセスが memioPtr または ioPtr 関数ポインタテーブルを介して C 関数呼び出しをトリガーし、任意の I/O エミュレーションを可能にします。
PTR バイト単位リダイレクト — 512 バイトブロックの各バイトが、他の任意のブロックタイプまたは PSRAM ロケーションを独立して指すことができます。

設定リファレンス

picoZ80 の動作はすべて、SDカード上の config.json によって制御されます。RP2350 は起動時にこのファイルを読み取ってミニファイし、その結果を Flash に格納します。以降の起動では、SDカードがない場合に Flash のコピーが使用されます。
最上位の JSON 構造は次のとおりです。
{
  "esp32": {
    "core":  { ... },
    "wifi":  { ... }
  },
  "rp2350": {
    "core":  { ... },
    "z80":   [ { "memory": [...], "io": [...], "drivers": [...] } ]
  }
}

esp32.core

キー タイプ 説明
device string CPU ペルソナ — picoZ80 は "Z80"、pico6502 は "6502"、pico6512 は "6512"
mode integer デフォルトの WiFi 起動モード:0 = クライアント(ステーション)、1 = アクセスポイント。

esp32.wifi

キー タイプ 説明
override 0/1 マスタースイッチ:1 = 以下のすべての設定を適用、0 = 永続化された NVS 設定を使用。
wifimode string "ap" = アクセスポイントモード、"client" = ステーション/クライアントモード。
ssid string 作成する(AP)または参加する(クライアント)WiFi ネットワーク名。
password string WiFi パスフレーズ。
ip string 固定 IP アドレス(例:"192.168.1.192")。
netmask string サブネットマスク(例:"255.255.255.0")。
gateway string デフォルトゲートウェイ(例:"192.168.1.1")。
dhcp 0/1 クライアントモード:1 = DHCP、0 = 固定 IP 設定を使用。
webfs string SDカード上のウェブファイルシステムのルートディレクトリ(デフォルト "webfs")。
persist 0/1 1 = 再起動をまたいで永続化するために解決された設定を NVS に書き込む。

rp2350.core

キー タイプ 説明
cpufreq integer RP2350 システムクロック(Hz 単位、例:300000000)。最大安定周波数は PSRAM 周波数とコア電圧に依存します。
psramfreq integer PSRAM SPI クロック(Hz 単位、例:133000000)。
voltage float RP2350 コア電圧(ボルト単位、例:1.10)。クロック速度が高いほど高い電圧が必要です。
addrDrive integer (0–3) アドレスバス GPIO ドライブ強度:0=2mA、1=4mA、2=8mA、3=12mA。デフォルト 0
addrSlew integer (0–1) アドレスバス GPIO スルーレート:0=低速(デフォルト)、1=高速。
dataDrive integer (0–3) データバス GPIO ドライブ強度。addrDrive と同じエンコーディング。デフォルト 0
dataSlew integer (0–1) データバス GPIO スルーレート。addrSlew と同じエンコーディング。デフォルト 0
ctrlDrive integer (0–3) 制御信号 GPIO ドライブ強度。addrDrive と同じエンコーディング。デフォルト 0
ctrlSlew integer (0–1) 制御信号 GPIO スルーレート。addrSlew と同じエンコーディング。デフォルト 0
addrSchmitt / dataSchmitt / ctrlSchmitt 0/1 アドレス / データ / 制御バスグループの入力シュミットトリガーを有効にします。1 = 有効(デフォルト)、0 = 無効。入力として設定されたピンに適用されます。
addrPull / dataPull / ctrlPull integer (0–2) アドレス / データ / 制御バスグループのプル抵抗:0 = なし、1 = プルダウン、2 = プルアップ。
refresh integer Z80 DRAM リフレッシュ生成:0 = オフ(リフレッシュサイクルなし)、1 = 各オペコードフェッチごとに 1 リフレッシュサイクル、N = N フェッチごとに 1 リフレッシュサイクル。フェッチごとのリフレッシュを DRAM が必要としないホストでのバスオーバーヘッドを削減します。
pinOverrides array ピン単位の GPIO オーバーライド。各要素:{ "pin": N, "drive": D, "slew": S, "schmitt": 0/1, "pull": P }。特定のハードウェアが異なる電気的特性を必要とする場合に、個々の GPIO ピンに対してバスレベルのデフォルトをオーバーライドします。

z80[].memory — メモリマップエントリ

memory 配列は Z80 メモリマップを定義します。エントリはアドレス順に並べる必要があります。領域は 512 バイトの倍数に整列し、512 バイトの倍数のサイズである必要があります。エントリ間のギャップは PHYSICAL パススルーとして扱われます。
キー タイプ 説明
enable 0/1 このエントリがアクティブかどうか。無効なエントリは起動時に無視されます。
addr hex string Z80 アドレス空間内の開始アドレス(例:"0x0000")。512 バイトに整列する必要があります。
size hex string 領域サイズ(バイト単位、例:8KB の場合は "0x2000")。512 の倍数である必要があります。
type string ブロックタイプ — メモリブロックタイプ を参照。
bank integer RAM/ROM/VRAM/FUNC タイプ用の PSRAM バンク番号(0–63)。
tcycwait integer この領域への各アクセスに挿入する追加の T サイクルウェイトステート。
tcycsync 0/1 この領域に対して T1 同期を有効にします。タイミング感応性のある領域に必要です。
task string FUNC タイプブロック用のオプションのタスク識別子(ドライバーバインディング文字列)。
file string 起動時に PSRAM バンクへプリロードする ROM イメージへの SDカードパス(例:"/ROM/mz700.rom")。
fileofs integer 読み取りを開始する ROM イメージファイル内のバイトオフセット。

z80[].io — I/O ポートマップエントリ

io 配列は Z80 I/O ポート範囲をブロックタイプにマッピングします。I/O エントリに対して意味があるのは PHYSICAL と FUNC タイプのみです。
キー タイプ 説明
enable 0/1 この I/O エントリがアクティブかどうか。
addr hex string 開始 I/O ポートアドレス(例:"0xE0")。
size hex string 連続するポートの数(例:ポート E0–E3 の場合は "0x04")。
type string PHYSICAL = ホストに渡す、FUNC = C ハンドラー関数を呼び出す。
task string FUNC タイプエントリ用のドライバーバインディング文字列。

z80[].drivers — ドライバーインスタンス

drivers 配列は仮想ハードウェアドライバーをインスタンス化し、それらをメモリまたは I/O 領域にバインドします。各ドライバーには、タイプ(C ドライバーモジュール)、名前(インスタンス識別子)、およびそのドライバーインスタンスの ROM イメージ、アドレスマップ、I/O マップ、パラメータを定義する 1 つ以上のインターフェースオブジェクトがあります。
"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" }
        ]
      }
    ]
  }
]
各ドライバーエントリには、最上位の "name"(インスタンス化する C ドライバーモジュール)、"type"PHYSICAL または VIRTUAL)、および 1 つ以上のインターフェースオブジェクトを含む "if"(インターフェース)配列があります。各インターフェースオブジェクトはサブドライバーまたはペリフェラルカードを記述し、次のフィールドを持ちます。
キー タイプ 説明
enable 0/1 このインターフェースがアクティブかどうか(0 = 初期化時にスキップ)。
name string インスタンス識別子 — ペルソナの interfaceFuncMap[] 内の名前と一致する必要があります。
type string PHYSICAL または VIRTUAL
rom array PSRAM にロードする ROM イメージ(後述)。
addrmap array メモリアドレスの再マッピングエントリ(srcaddr/dstaddr/size)。
iomap array I/O ポートの再マッピングエントリ(srcaddr/dstaddr/size/16bit)。
param array ドライバー固有のパラメータ — 通常、ディスクイメージには { "enable":1, "file":"/path/to/image" }、名前付きパラメータには { "name":"key", "value":"val" }、Celestite ネットワークファイルサーバーアドレスには { "ip":"a.b.c.d:port", "enable":1 }

再配置可能なインターフェースのベースポート。 マシン非依存カードをカスタム / 実験者ボード上で使用できるように(OpenZ80 を参照)、いくつかのインターフェースドライバーは、その iomap エントリの dstaddr からベース I/O ポートを読み取り(srcaddr にはカード本来のポートを設定)、iomap エントリが存在しない場合は元のポートをデフォルトとします。再配置可能なカードとデフォルトベースは、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)、Celestite(0x60/16)です。JSON キーは小文字(srcaddr / dstaddr)で、その値は数値として解析されます。GUI 設定ページの Base I/O Port フィールドが、正しい iomap エントリを自動的に書き込みます。

rom[].loadaddr[] — ROM ロードアドレス

各 ROM ファイルエントリには、ROM データを PSRAM のどこにロードするかを指定する loadaddr 配列が含まれます。複数の loadaddr エントリにより、単一の ROM ファイルを非連続のアドレス範囲または PSRAM バンクにまたがって分割できます。
キー タイプ 説明
enable 0/1 このロードアドレスがアクティブかどうか。
position integer 読み取りを開始する ROM ファイル内のバイトオフセット。
addr hex string メモリマップ内のターゲット Z80 アドレス(例:"0x0000")。
bank integer ロード先の PSRAM バンク番号。
size hex string ロードするバイト数(例:4KB の場合は "0x1000")。
tcycwait integer この領域用の追加 T サイクルウェイトステート。
tcycsync integer この領域用の T サイクル同期値。

ペルソナ–インターフェース互換性

すべてのインターフェースドライバーがすべてのペルソナで利用できるわけではありません。以下の表は、現在の Sharp MZ / X1 シリーズのインターフェース互換性を示しています。Amstrad PCW-9512 ペルソナは、統合された uPD765 FDC を備えた自己完結型ドライバーです(個別のインターフェースドライバーはありません)。Tatung Einstein TC-01 ペルソナは、統合された WD1770 FDC と EinsteinFDC サブインターフェースを備えた自己完結型ドライバーです。Open 列は、OpenZ80 実験者向けペルソナが受け入れるマシン非依存カードを示します。
この表は、"if" 配列を介して各最上位マシンペルソナで使用できるインターフェースを示しています。JSON 内のインターフェースの "name" 値は、選択したペルソナでサポートされるエントリのいずれかと一致する必要があります。
インターフェース MZ-700 MZ-1500 MZ-80K MZ-800 MZ-80A MZ-2000 MZ-2200 MZ-80B MZ-2500 Open
RFS Yes Yes Yes Yes Yes Yes
TZFS Yes
MZ-1E05 Yes Yes Yes Yes
MZ80FIO Yes Yes
MZ80AFI Yes Yes Yes
MZ-8BFI / E0054PA Yes Yes Yes Yes Yes
MZ-1E14 Yes Yes Yes Yes Yes Yes
MZ-1E19 Yes Yes Yes Yes Yes Yes Yes Yes
MZ-1R12 Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes
MZ-1R18 Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes
MZ-1R23 Yes Yes Yes Yes Yes
MZ-1R37 Yes Yes Yes Yes Yes Yes
PIO-3034 Yes Yes Yes Yes Yes Yes
Celestite Yes Yes Yes Yes Yes
MZ-8BIO3 Yes Yes Yes Yes Yes
MZ-1E24 Yes Yes Yes Yes Yes
MZ-1E30 Yes Yes

組み込みドライバー

ファームウェアは、5 つのモデルターゲットを持つ的を絞ったビルドシステムをサポートします。BaseZ80(全ドライバー)、SharpZ80(Sharp のみ)、AmstradZ80(Amstrad のみ)、TatungZ80(Tatung のみ)、OpenZ80(マシン非依存の実験者向けペルソナ)です。コンパイル時定義 INCLUDE_SHARP_DRIVERSINCLUDE_AMSTRAD_DRIVERSINCLUDE_TATUNG_DRIVERSINCLUDE_OPEN_DRIVERS が、どのドライバーモジュールをコンパイルに含めるかを制御します。以下の表は、利用可能なすべてのドライバーを列挙します。
Sharp MZ シリーズ(INCLUDE_SHARP_DRIVERS)
ファームウェアが INCLUDE_SHARP_DRIVERS でビルドされると、以下のドライバーモジュールがコンパイルされ、drivers 配列を介してインスタンス化できます。
ドライバー タイプ文字列 説明
MZ700.c MZ700 Sharp MZ-700 のバンク切り替え、ビデオ、キーボード I/O
MZ80A.c MZ80A Sharp MZ-80A — モニター ROM(SA-1510)、VRAM、Intel 8253 PIT エミュレーション、8255 PPI、MEMSW/MEMSWR メモリスワップ(CP/M バンク切り替えを含む)、物理+仮想ミックスドモード対応、および RFS、MZ80AFI、MZ-1E14、MZ-1E19、MZ-1R12、MZ-1R18 サブインターフェース対応
MZ2000.c MZ2000 Sharp MZ-2000 — BST/NST メモリモード切り替え、キャラクター+グラフィックス VRAM オーバーレイ、8253 PIT、8255 PPI、Z80 PIO、MB8866 FDC。物理モード(ブート/通常モードの自動検出を備えたドロップイン Z80 置換)と仮想モード(IPL ROM ミラーリングを備えた PSRAM ベースの完全エミュレーション)の両方をサポート
MZ2200.c MZ2200 Sharp MZ-2200 — BST/NST メモリモード切り替え、VRAM オーバーレイ、8253 PIT、8255 PPI、Z80 PIO、MB8866 FDC、カラー CRT
MZ80B.c MZ80B Sharp MZ-80B — 2K IPL ROM、BST/NST、モノクロディスプレイ、デュアル GRPH ページ、8253 PIT、8255 PPI、Z80 PIO
MZ2500.c MZ2500 Sharp MZ-2500(SuperMZ)— 8 ページ MMU(64 ブロック)、MZ-2000/MZ-80B 互換モード、YM2203、G-CRTC、MB8876 FDC、パレット、割り込みコントローラー。仮想モードはカスタム fetchByte/RETI ハンドラー(ゲートアレイクロッキング用の物理 M1 バスサイクル)を介した割り込み駆動ソフトウェア、スパース/連続自動検出付き D88 ネイティブディスクフォーマット、初期化中のワンショット割り込み抑制をサポート
MZ1500.c MZ1500 Sharp MZ-1500 — 内蔵 Quick Disk ドライブ、PCG、ステレオ PSG サウンド(SN76489AN)、Z80 PIO プリンターインターフェース、8253 PIT、DIP スイッチによる MZ-700/MZ-1500 モード選択を備えた MZ-700 のスーパーセット。サブインターフェース:RFS、MZ-1E05、MZ-1E14、MZ-1E19、MZ-1R12、MZ-1R18、MZ-1R23、MZ-1R37、PIO-3034、Celestite
MZ80K.c MZ80K Sharp MZ-80K — SP-1002 モニター ROM(0x0000–0x0FFF)、2KB VRAM(0xD000–0xD7FF)、8255 PPI / 8253 PIT / LS367 キーボード(0xE000–0xE7FF)、ネイティブ MZ-80FD ブート ROM(0xF000–0xF3FF)、CP/M 用の MEMSW/MEMSWR メモリスワップ。物理モード+仮想モード(物理モード CP/M はドライバーごとの Z80 アクセスリマップを使用、純正 CP/M には仮想モードが必要)。2 つのフロッピーパス:ネイティブ MZ80FIO(T3444M)— オリジナルの MZ-80FD インターフェースで、すべての MZ-80K ディスクをブート/読み取り可能。および MZ80AFI(MZ-80A FDC)— CP/M 用で、MZ-80K CP/M をブートし、CP/M 内(C:/D:)で MZ-80K CP/M ディスクを読み取り可能。一度にアクティブになるフロッピーインターフェースは 1 つのみで、両方が存在する場合は MZ80FIO が優先されます。サブインターフェース:RFS、MZ80FIO、MZ80AFI、MZ-1E14、MZ-1E19、MZ-1R12、MZ-1R18、MZ-1R37、PIO-3034
MZ800.c MZ800 Sharp MZ-800 — デュアルモードドライバー(MZ-700 互換 + ネイティブ MZ-800)。GDG ディスプレイモードレジスタ(ポート 0xCE)をスヌープして動的にモードを切り替えます。ネイティブモードでは 320×200 / 640×200 グラフィックス(VRAM プレーン 0x8000–0xBFFF)、4/16 色パレット、ポートマップされた GDG I/O(0xCC–0xCF、パレット 0xF0)、Z80-PIO デイジーチェーンを介した IM2 ベクター割り込みを追加します。MZ-700 モードではメモリマップされた 8255/8253(0xE000–0xE7FF)とテキスト VRAM(0xD000)を使用します。SN76489 PSG(0xF2)、WD1773 FDC(0xD8–0xDF)、QuickDisk(0xF4–0xF7)、メモリバンキングポート 0xE0–0xE6。仮想モードでは、実バス上で物理 RETI を再生して PIO の in-service ラッチをサービスします。
MZ80AFI.c MZ80AFI Sharp MZ-80A フロッピーインターフェース — MZ-80A AFI フロッピーディスクコントローラーをエミュレートします
MZ80FIO.c MZ80FIO Sharp MZ-80FD/MZ-80FIO フロッピーインターフェース(MZ-80K)— 0xF000–0xF3FF の FDIF ブート ROM、ポート 0xF8–0xFB の T3444M、最大 4 ドライブ、実行時ディスク交換
T3444M.c (MZ80FIO が使用) Toshiba T3444M/T3444A FDC — MZ-80K ネイティブフロッピー。CPC 拡張 DSK、35 トラック、2 ヘッド、16 セクター/トラック、128 バイト FM セクター。堅牢な Track-Info DSK パーサー、4 ドライブイメージ同時対応
WD1773.c WD1773 WD1773 FDC — 80 トラック、2 ヘッド、8 セクター DSK/RAW/D88 イメージ
QDDrive.c QDDRIVE Sharp QuickDisk ドライブ — スパイラルトラックデータ、モーター制御、非同期 SDカード I/O を備えたフル Z80 SIO/2 エミュレーション
RFS.c RFS ROM ファイリングシステム — SDカードからの MZF ロード、CP/M、BASIC
TZFS.c TZFS TranZPUter ファイリングシステム — 動作するマルチバンクモニター + CP/M ファイリングシステム、MZ-700 ペルソナで選択可能。tranZPUter メモリモードはポート 0x60 経由、仮想 K64F サービスプロセッサーは OUT (0x68) 経由、CP/M セクター I/O は ESP32 経由。ROM roms/tzfs.binTZFS/asm/tzfs.asm から
MZ-1E05.c MZ1E05 Sharp MZ-1E05 フロッピーディスクインターフェースユニット(WD1773 ベース)
MZ8BFI.c MZ8BFI / E0054PA MZ-2000 フロッピーディスクインターフェース — ドライバー ROM なしの MB8866 FDC(コードは IPL 内)。D88 フォーマット対応。
MZ-1E14.c MZ1E14 BIOS ROM 付き MZ-1E14 QuickDisk コントローラー(MZ-700/MZ-800)
MZ-1E19.c MZ1E19 BIOS ROM なしの MZ-1E19 QuickDisk コントローラー
MZ-1R12.c MZ1R12 32KB バッテリーバックアップ RAM ボード(SDカードに永続化)
MZ-1R18.c MZ1R18 64KB RAM 拡張ボード
MZ-1R23.c MZ1R23 MZ-1R23 128KB 漢字 ROM(16×16 JIS パターン)および MZ-1R24 256KB 辞書 ROM。ROM ファイルは SDカードからロードされます。I/O ポート B8h–B9h、自動インクリメント読み取り
MZ-1R37.c MZ1R37 MZ-1R37 640KB EMM(拡張メモリマネージャー)— I/O ポートアドレスラッチング付きの 20 ビットアドレス空間
PIO-3034.c PIO3034 IO DATA PIO-3034 320KB EMM — 自動インクリメントデータポート付きの 19 ビットアドレスカウンター
Celestite.c Celestite Celestite 複合ボード — Wiznet W5100 Ethernet コントローラー(レジスターエミュレーション)、割り込みコントローラー、UFM、統合された MZ-1R12 32KB CMOS RAM(64KB に拡張可能)、オプションの MZ-1R37 640KB EMM。I/O ポート 60h–6Fh。JSON param 配列内の ip パラメータが netfs.py ファイルサーバーアドレスを設定します(例:"192.168.1.210:6800")。Phase 2:ESP32 ブリッジを介した実際の TCP/IP ネットワーキング — W5100 ソケットコマンドが実際の BSD ソケット操作のために ESP32 に転送されます。NET_CFG、NET_SOCK、NET_SEND、NET_RECV、NET_PING のコア間 IPC
MZ8BIO3.c MZ-8BIO3 RS-232C シリアルカード(BI コネクター)、ポート 0xB0–0xB3(ベースは設定可能)でエミュレートされた Z80 SIO。チャンネル A/B は USB CDC シリアルポート 2 および 3 にブリッジされます。ROM なし。
MZ1E24.c MZ-1E24 RS-232C シリアルカード(Sharp ST コネクター)。コネクター配線が異なる点を除き MZ-8BIO3 と同じ。
Z80SIO.c (MZ-8BIO3 / MZ-1E24 が使用) レジスターレベルで正確な Zilog Z80 SIO/2:WR0–WR7、RR0–RR2、Z80 モード 2 ベクター割り込み、4 レベルの in-service デイジーチェーン、status-affects-vector。ロックフリー SPSC リングが Z80 コア(コア 1)と USB CDC サービス(コア 0)をブリッジします。
SASI.c + MZ1E30.c MZ-1E30 MZ-1E30 SASI ハードディスクコントローラー — MZ-2500/MZ-80B 用の Sharp MZ-1E30 SASI(Shugart Associates System Interface)ハードディスクコントローラーをエミュレートします。最大 4 ディスクターゲット(各約 21.4 MB、256 バイトブロック)、32KB IPL ROM(ポート 0xA8–0xA9 を介して I/O アクセス)、SDカードのディスクイメージからのオンデマンドセクター I/O。SASI コマンド:TEST_UNIT_READY、REQUEST_SENSE、READ(6)、WRITE(6)、SEEK(6)、INQUIRY。I/O ポート 0xA4–0xA5(SASI データ/制御)
PIT8253.c PIT8253 スタンドアロン Intel 8253 PIT エミュレーション — 全 6 カウンターモード、BCD/バイナリ、ラッチ、LSB/MSB 読み取り/ロード
PPI8255.c PPI8255 スタンドアロン Intel 8255 PPI エミュレーション — モード 0 I/O、ビットセット/リセット、出力コールバック、入力インジェクション
Amstrad PCW シリーズ(INCLUDE_AMSTRAD_DRIVERS)
ファームウェアが INCLUDE_AMSTRAD_DRIVERS でビルドされると、以下のドライバーモジュールがコンパイルされます。
ドライバー タイプ文字列 説明
PCW9512.c PCW9512 Amstrad PCW-9512 — Z80A @ 4MHz、4 バンク 16KB ページ切り替え(ポート F0–F3)付き 512KB RAM、ビデオ/システムクロック/FDC ルーティング/モーター制御用のゲートアレイ(ASIC)(ポート F8)、8041 デイジーホイールプリンターコントローラー(ポート FC–FD)、ブートストラップシーケンスエミュレーション。仮想モードと物理モードに対応
uPD765.c NEC uPD765 FDC エミュレーション — CPC DSK フォーマット対応(ポート 00–01)。ゲートアレイコマンド:ブートストラップ終了、再起動、FDC INT ルーティング(NMI/INT/無視)、ターミナルカウント、モーターのオン/オフ。注入された Z80 コードによる物理ディスクイメージング。再利用可能な FDC モジュール(Sharp 用の WD1773.c に相当)
Tatung Einstein シリーズ(INCLUDE_TATUNG_DRIVERS)
ファームウェアが INCLUDE_TATUNG_DRIVERS でビルドされると、以下のドライバーモジュールがコンパイルされます。
ドライバー タイプ文字列 説明
EinsteinTC01.c EinsteinTC01 Tatung Einstein TC-01 — Z80A @ 4MHz、64KB RAM + 8KB 切り替え可能 ROM(X-TAL MOS)、ポート 0x24 経由の ROM/RAM トグル、アクセス間タイミング強制(約 2us のギャップ)付き TMS9129 VDP、AY-3-8910 PSG(ポート 0x02-0x03)、Z80 CTC(ポート 0x28-0x2B)、Z80 PIO(ポート 0x30-0x33)、キーボードインターフェース(ポート 0x20)。仮想モードと物理モードに対応
EinsteinFDC.c Einstein FDC サブインターフェース — 2 ドライブ、DSK/D88 フォーマット対応。物理ディスクイメージング:物理フロッピーを DSK へ読み取り、DSK を物理フロッピーへ書き込み
WD1770.c WD1770 FDC エミュレーション — 拡張 CPC DSK、D88、標準 DSK フォーマット対応。40 トラック、1 ヘッド、10 セクター、512 バイト(200KB ディスク)。WD1770 ベースのマシン用の再利用可能な FDC モジュール(Sharp が使用する WD1773.c とは別)

TZFS — モニター + CP/M モデル(MZ-700 ペルソナ)
TZFS.c は、動作するマルチバンクの低レベルモニター兼ファイリングシステムを実装します — オリジナルの MONITOR 1Z-013A に対する一連の拡張(SD アクセス、ROM バンキング、アセンブラー / ディスアセンブラー、およびツール)で — tranZPUter SW の TZFS とその K64F 仮想 I/O プロセッサーをモデルとしています。これはトップレベルのペルソナではなく、MZ-700 ペルソナ上でのみ選択可能なインターフェースとして提供されます(MZ700.c 内で RFS と並んで登録され、実際には RFS と排他的です)。CP/M はモニターの下で動作します。
メモリモードエミュレーション(ポート 0x60)
tranZPUter メモリモードを I/O ポート 0x60 に書き込むと、バンクポインターが再設定されます。モードは TZMM_ORIGTZMM_BOOTTZMM_TZFSTZMM_TZFS2TZMM_TZFS3TZMM_TZFS4TZMM_CPMTZMM_CPM2TZMM_COMPAT です。CP/M の CPM2 レイアウトはバイト単位のブロック 0 ページングを使用します — 0x00000x003F はベクターブロックに、0x00400x01FF は TPA の先頭にマップされます。
仮想 K64F サービスプロセッサー(ポート 0x68)
Z80 は OUT (0x68) を実行してサービスを要求し、これが MSG_TZFS_SVCREQ メッセージを Core 0 にキューイングします。Core 0 は TZFS_processServiceRequest を実行します。ファイリングシステムサービスは READDIR / NEXTDIR(キャッシュされた 16 エントリのディレクトリブロック)、READFILE / NEXTREADFILE、LOADFILE(MZF ヘッダーおよびバンクオブジェクト対応)、CHANGEDIR、CLOSE です。CP/M サービスは LOADBDOS(ウォームブート CCP + BDOS リロード)、ADDSDDRIVE、READSDDRIVE、WRITESDDRIVE、および CPU 周波数サービスです。picoZ80 には直接の SD アクセスがないため、CP/M の 512 バイトセクターは ESP32ESP_readSector / ESP_writeSector)を介してイメージ全体のファイルに対して読み書きされます。ドライブごとのイメージパスはインターフェース JSON の param[].file エントリから取得され、フォールバックテンプレートは CPM/SDC16M/RAW/CPMDSK<nn>.RAW です。TZFS ROM は SD カード上の roms/tzfs.bin で、TZFS/asm/tzfs.asm からアセンブルされます(その CP/M BIOS は TZFS/asm/cbios.asm / cpm22.asm から)。config_MZ-700_MZ-700.json"enable": 0 で同梱されており、JSON または Web GUI の設定ページからオンに切り替えられます。

OpenZ80 — 実験者向けペルソナ(INCLUDE_OPEN_DRIVERS)
OpenZ80 ターゲット(TARGET_MODEL_OPENINCLUDE_OPEN_DRIVERS)は、picoZ80 を自作ボードや専用ドライバーのないマシンに搭載する実験者向けに、意図的に素の「バニラ」な Z80 ペルソナをコンパイルします。PHYSICAL モードでは 64K のメモリと I/O 空間全体が実際のボードへ通過します(インターフェースカードはその I/O ポートに重ねられます)。VIRTUAL モードではフラットな 64K RAM を提示し、そこへドライバーレベルの ROM イメージが 0x0000 から順に読み込まれます。マシンハードウェアを一切持たず、Sharp のインターフェースカードモジュールをそのまま再利用し(それらはペルソナ固有の状態を持ちません)、以下のマシン非依存カードのみを公開します — そのいずれもベース I/O ポートの再配置をサポートします。
Driver Type String Description
Open.c Open バニラ / 実験者向け Z80 ペルソナ — マシンハードウェアなし。物理パススルー、または 0x0000 からのドライバーレベル ROM ロードを伴うフラットな 64K 仮想 RAM
MZ-1R12.c MZ1R12 MZ-1R12 32K RAM ファイルカード(デフォルトベース 0xF8、再配置可能)
MZ-1R18.c MZ1R18 MZ-1R18 64K RAM ボード(デフォルトベース 0xEA、再配置可能)
MZ-1R23.c MZ1R23 MZ-1R23 漢字 / MZ-1R24 辞書 ROM ボード(デフォルトベース 0xB8、再配置可能)
MZ-1R37.c MZ1R37 MZ-1R37 640K EMM(デフォルトベース 0xAC、再配置可能)
PIO-3034.c PIO3034 PIO-3034 パラレル EMM / パラレル I/O(デフォルトベース 0x00、再配置可能)
MZ8BIO3.c / MZ1E24.c MZ8BIO3 / MZ1E24 デュアル RS-232C シリアルカード(Z80 SIO、デフォルトベース 0xB0、再配置可能。チャンネル A/B は USB CDC 2/3)
MZ-1E05.c MZ1E05 WD1773 フロッピーインターフェース(デフォルトベース 0xD8、再配置可能。FDC ブート ROM が必要)
Celestite.c Celestite Celestite ESP32 LAN ボード(デフォルトベース 0x60、再配置可能)
build_tzpuPico.sh open でビルドします。picoZ80 を新しいマシンに適応させるには、src/drivers/{Sharp,Amstrad,Tatung,Other}/ 配下のペルソナドライバーの 1 つをベースにし、RFS および TZFS プロジェクトの asm/ ディレクトリにある対応するモニター / IPL / CP/M BIOS / フロッピーブート ROM ソース(例: RFS/asm/sa1510.asmRFS/asm/cbios.asmTZFS/asm/mz2000_ipl.asmRFS/asm/mz80afi.asm)と組み合わせます。手順を追った詳細についてはデベロッパーズガイドを参照してください。

仮想 CMT(カセット)ユニット

仮想 CMT は、Sharp MZ シリーズ向けの波形レベルの仮想カセットデッキです。ローダーを傍受するのではなくカセットの波形そのものをエミュレートするため、Sharp 側から見ると実機のデッキと区別がつきません。モニターの LOAD、BASIC、カスタムローダーやターボローダー、そして SAVE がすべてそのまま動作します。SDカードからのロードは引き続き高速なパスであり、仮想 CMT は本物志向のパスです。.mzf を取り込んで「実機のカセットのように」ロードしたり、実行中のプログラムを新しい .mzf に録音して書き出したりできます。
ドライバーは単一のエンジン — src/drivers/Sharp/CMT.csrc/include/drivers/Sharp/CMT.hCMT_Init / CMT_Reset / CMT_PollCB / CMT_TaskProcessor)— であり、各マシンの interfaceFuncMap[]{"CMT", false, CMT_Init, CMT_Reset, CMT_PollCB, CMT_TaskProcessor} として登録され、マシンにカセットポートがあればどこでも選択可能なインターフェースとして提供されます。すべてのタイミングはウォールクロックtime_us_64() — RFS CMT が使用するのと同じワンショットのタイミングドメイン)に対して動作し、エミュレートされた Z80 T ステートに対しては決して動作しません。設計の全容は picoZ80 リポジトリの docs/VIRTUAL_CMT_DESIGN.md に記載されています。

2 つのハードウェアファミリー、1 つのエンジン

MZ のカセットハードウェアは 2 つのファミリーに分かれており、どちらも同じコーデックエンジンで駆動されます。ファミリーはアクティブなペルソナから推定されます。
ファミリー マシン バス / ポート ボーレート 半波(0 / 1)
SIMPLE MZ-80K / MZ-80A / MZ-700 / MZ-800 8255 のモーター オン/オフ、リニアテープ。E000–E003 のメモリマップド 8255(MZ-800 は代わりに I/O ポート D0–D3 を使用) 1200 ボー 0 ≈ 504 µs、1 ≈ 958 µs(MZ-800 は ≈ 379 / 964 µs に短縮)
CONTROLLED MZ-80B / MZ-2000 / MZ-2200 / MZ-2500 コンピューター制御のトランスポート(PLAY / STOP / FF / REW / EJECT + APSS プログラムサーチ)。E0–E3 で ioPtr[] をフック 2000 ボー ≈ 333 / 667 µs(≈ 1800 ボーのバリアント)
共有コーデック。両ファミリーは同じバイト、ブロック、ファイルのフレーミングを共有します。バイトは長いスタートビットに続く 8 データビット(MSB ファースト)です。ブロックはリーダー、テープマーク、データバイト、そして 16 ビットチェックサム(1 ビットの個数)で構成されます。ファイルはヘッダー(情報)レコードに続くデータレコードで、それぞれ 2 回書き込まれるため、1 回の物理読み取りでフォールバック用の 2 つ目のコピーが得られます。

仮想テープ — 順序付きキュー

仮想テープは、GUI で設定される最大 16 個の MZF ファイルの順序付きキューであり、ウェブ GUI からライブで編集できます(追加 / 削除 / 並べ替え)。各プログラムの終わりでキューは自動的に進み(連続したテープの挙動)、次の LOAD が次のプログラムを見つけます。制御ファミリーでは、FF / REW がプログラム単位でスキップし、APSS が次または前のプログラム境界までシークして自動停止します。キューは、インターフェースの param 配列を通じて { "enable", "file" } エントリの順序付きリストとしてドライバーに供給されます。新しいウェブ GUI パネル — esp32/webserver/js/cmt.js — がキューを管理します。

実機 ↔ 仮想 — パススルー、重畳、抑制

ウェブ GUI のアクションメニューにある 「CMT: Virtual / Real」項目 — CMT インターフェースが有効な場合にのみ表示されます — は、ESP32 → RP2350 の逆コマンドキューを介してライブのドライバーフラグを切り替えます(再起動不要)。2 つのモードは、物理的に接続されたデッキをドライバーがどう扱うかで異なります。
  • VIRTUAL — ドライバーは読み取り側で仮想カセットのビットを重畳し、物理デッキへのモーターおよびトランスポートの書き込みを抑制します。Sharp は仮想テープからロードし、実機のデッキはアイドル状態のままです。
  • REAL — 純粋なパススルー。CMT は読み取り専用でスヌープし、モーターが実機のデッキを駆動します。物理カセットは picoZ80 が存在しないかのように正確に動作します。
トグルはライブであるため、仮想テープからロードしてから物理デッキに SAVE したり、物理デッキからロードして仮想テープに録音したりを、再起動なしで行えます。

録音(SAVE)— 波形デコーダー

モーターが録音モードで動作しているとき、ドライバーは書き込みデータ線をサンプリングし、それをファイルへとデコードして戻します。各半波の間隔はまたはに分類され、短 / 長のペアがビットになり、ビットがバイトになり、16 ビットチェックサムが検証されます。復元されたレコードから MZF が合成され、次の名前で SDカードに保存されます。
CMT/taperecord_<name>_<YYYYMMDD_HHMMSS>.mzf
ここで <name> は、Sharp ディスプレイコードから ASCII へトランスコードされ、さらに FAT 用にサニタイズされた(不正な文字は _ になる)Sharp ヘッダーのファイル名です。ESP32 のクロックが設定されていない場合、タイムスタンプはシーケンス番号(taperecord_<name>_0001.mzf)にフォールバックし、FAT の「1980」年の日付衝突を回避します。実際の Sharp 名は MZF ヘッダー内にそのまま格納されるため、再ロード時にプログラムは元の名前を保持します。

再生エンジンとドライバーの内部構造

再生はストリーミングエッジカーソルを使用し、ウォールクロックに対して次の読み取りデータの遷移をその場で計算します。ならし O(1) であり、巨大な事前展開済みエッジバッファを保持しないため、テープイメージ全体は長さに関係なくほとんど RAM を消費しません。2 つのファミリーはバスへのフックの仕方が異なります。
  • シンプルファミリー — ドライバーはカセットの 8255 ブロック(512 バイト、E000–E1FF = ブロック 112)を MEMBANK_TYPE_RAM に再タイプし、ブロック全体のハンドラーをインストールします。そのブロック内のカセット以外のレジスタ — キーボード(E000 / E001)、8253(E004–E007)、ジョイスティック(E008)およびそれらのミラー — は Z80CPU_readPhysicalMem / Z80CPU_writePhysicalMem を介してそのまま通過し、Port C のカセットビットのみが読み取り時に重畳され、書き込み時にスヌープされます。チューニングされた PHYSICAL_HW のホットパスは手つかずのまま残されます。
  • 制御ファミリー — ドライバーはポート E0–E3 で ioPtr[] をフックし、そこでコンピューター制御のトランスポートを駆動します。
SD の読み取りと書き込みは、コア間の requestQueue / responseQueue を介してコア 0 に引き渡されるため、コア 1 のホットループがストレージでブロックすることは決してありません。前述のとおり、テープキューはインターフェースの param 配列を通じて届き、ファミリーはペルソナから推定されます。

TZFS からの MZ-800 フロッピーブート

TZFS はモニター内から直接、MZ-80A、MZ-700、MZ-800 のフロッピーディスク — MZ-800 CP/M を含む — をブートできるようになり、フロッピーからブートした OS が制御を引き継いだ後でも、ハードウェアの RESET スイッチで TZFS に戻れるようになりました。これは TZFS ROM と picoZ80 ファームウェアの両方にまたがる変更です。TZFS のバージョンは v1.8.3 になりました。

TZFS ブートローダー — GETBOOTDSK と 8253 の制御

GETBOOTDSK は、"IPLPRO" シグネチャに加えて、任意の Sharp マシン ID(01 / 02 / 03)を受け付けるようになりました。1000H より下にロードする OS(例: MZ-800 CP/M)のために、ブロック 7 の DRAM を 0000–0FFF に事前ページングし、ネイティブの 9Z-504M IPL とまったく同じように、ロードしたプログラムに BC = 0200H を渡します。これにより、セカンドステージローダーが独自のディレクトリ読み取りを実行できます。
JP (HL) を実行する前に、TZFS は 8253 クロックを制御します。カウンター 2 を約 1 Hz に再プログラムし、8255 Port C ビット 2 をマスクして、IPL の SORES を再現します。これにより、フリーランのカウンターが RST 38H 割り込みで Z80 を氾濫させ、割り込みを早期に再有効化するローダーを中断させること — 「P-CP/M80 → No system file」を引き起こした障害 — を防ぎます。

MZ-1E05 A10 トグルフェッチハンドラー

picoZ80 の MZ-1E05 ドライバーは MZ1E05_IO_A10Toggle を追加します。これは、アドレス 0xF3FE / 0xF7FEDRQA10 を OR する FDC ハードウェアアクセラレーションのフェッチハンドラーです。これにより、TZFS の DSKREAD ルーチンは、MZ-80A ですでに行っているのとまったく同じように、MZ-800 でブートセクターをストリーミングできます。

TZMM_DSKLOAD / TZMM_DSKRUN メモリモード

picoZ80 の 2 つの TZFS メモリモード、TZMM_DSKLOADTZMM_DSKRUN は、マシン自身の IPL とまったく同じように、フロッピーからブートした OS をネイティブのブロック 0 DRAM でロードして実行します。これにより、ブートした OS は TZFS のバンクレイアウトではなく、期待どおりのメモリマップを見ることになります。

cgWindow PCG フォントトラッキング(MZ-800)

picoZ80 の MZ800 ドライバーは cgWindow を追跡するようになり、PCG フォントを(IN 0xE0 を介して)ロードする MZ-800 モードの OS が、TZFS 上で実行中に実際の CG-ROM / CG-RAM に到達できるようになりました。これにより、ディスク BASIC 下での文字化けが修正されます。一方、PCG ウィンドウに触れない Flappy などのネイティブゲームには影響しません。

TZFS への復帰リストア

フロッピーからブートした OS の後にハードウェアの RESET スイッチが押されると、ファームウェアは無傷のブロック 0 モニターと TZFS UROM を再ロードし — ブロック 0 の ROM と RAM は同じ PSRAM をエイリアスするため、OS は TZMM_DSKRUN での実行中にこれらを破壊していました — その後 TZFS をコールドブートします。したがって、RESET スイッチは素の 1Z-013A モニターに落ちるのではなく、TZFS に戻ります。

関連するバグ修正

  • Flappy の PSRAM バンク不一致 — TZFS 下で 0x0000–0x0FFF がバンク 7 からマップされるようになりました。これにより、コードを低位 RAM に LDIR してからビデオモードを切り替えるネイティブプログラムが、両方のパスで同じメモリを見るようになります(Flappy 固有ではなく、一般的な修正です)。
  • ハードウェアリセット時の GDG リカバリー/RESET が解放されると initVideoText が再実行されるため、GDG が再スキャンし、モニターの HBLK 同期が完了します。
  • 物理リセットの安定化ディレイ — 10 ms のディレイにより、ファームウェアがビデオを再初期化する前に、オンボードの GDG / 8255 がハードウェアリセットを完了できます。

仮想ハードウェアフレームワーク

FUNC ブロックタイプは、各バスアクセスで C ハンドラー関数を呼び出すことにより、任意の I/O エミュレーションを可能にします。メモリの任意の 512 バイトブロック、または I/O ポートの任意の範囲を関数で裏付けることができます。

ハンドラー関数のシグネチャ

メモリ FUNC ハンドラーは PSRAM 内の memioPtr テーブルに格納されます。I/O FUNC ハンドラーは ioPtr テーブルに格納されます。関数シグネチャは次のとおりです。
/* 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);
ハンドラー関数はコア 1 のホットループから直接呼び出されます。現在のバスサイクルのウェイトステートが切れる前に完了する必要があります — ハンドラーは短く保ち、ブロッキング操作(ファイル I/O、UART など)を避けてください。ハンドラーがより長い操作(ディスクセクターのロードなど)をトリガーする必要がある場合は、コア間キューを介してコア 0 にメッセージをポストし、ステータスバイトを返して即座にリターンし、実際の I/O をコア 0 に委ねるべきです。

新しいドライバーの作成

新しいペリフェラルまたはホストマシンのサポートを追加するには:
  1. src/drivers/ ディレクトリに新しい .c / .h ファイルを作成します。
  2. 上記のシグネチャに一致するリードおよびライトハンドラー関数を実装します。
  3. ドライバー初期化時に、ハンドラー関数ポインタを memioPtr または ioPtr テーブルに登録します。
  4. ドライバーを CMakeLists.txt のビルドターゲットに追加します。
  5. JSON 設定パーサーがドライバーを名前でインスタンス化できるように、タイプ文字列エントリを追加します。
  6. ドライバーの param キーをドライバーのヘッダーファイルに文書化します。
ドライバー初期化関数は、config.json が解析された後、起動時に一度呼び出されます。ドライバーはそのインターフェース設定ブロックへのポインタを受け取り、この時点で内部状態をセットアップし、ハンドラーを登録する必要があります。

仮想ペリフェラルデバイス

仮想ペリフェラルデバイスとは、config.json に宣言するだけでマシンに追加できる完全なサポートチップです — Z80 DMA、Z80 CTC、8255 などがこれにあたります。ファームウェアは実際の集積回路をレジスター単位でモデル化しており、チップは基板にはんだ付けされた実部品とまったく同じように Z80 のアドレス空間に現れます。さらにデバイス同士はネットリストによって相互に配線できるため、あるチップの出力ピンで別のチップの入力ピンを駆動することができます — プリント基板上のパターンとまったく同じです。
これは、前述の仮想デバイスフレームワークとは別の仕組みです。FUNC ブロックはハンドラーであり、バスアクセスのたびにファームウェアが呼び出す C コードを開発者が記述し、ファームウェアにコンパイルして組み込みます。一方、仮想ペリフェラルデバイスは宣言であり、ファームウェアが既にモデル化しているチップのインスタンスを、コードを一切書かずに設定データから生成するものです。また、drivers[].if[] の下に宣言されるインターフェースカードとも異なります。インターフェースカードはマシン固有の複合体(ROM イメージ、メディア、アドレスマッピング)です。この 3 つは階層関係ではなく対等な存在であり、インターフェースカードがデバイスとして宣言できるものと同じチップを内部に含んでいることもよくあります。
その目的は、ファームウェアを書かずに誰でも picoZ80 上で自分だけの Z80 システムを構築できるようにすることです。マシンペルソナが CPU、メモリ、ビデオを供給し、デバイスがペリフェラルを供給し、ネットリストがそれらの間の配線を供給します。

デバイスカタログ

7 種類のデバイスが利用できます。name 列は config.json で使用する正確な文字列です。サービスコア列はタイミングに関係します。コア 0 でサービスされるデバイスは Z80 とは独立したフリーランタイマーで進行し、コア 1 のデバイスはバスアクティビティによって駆動されます。
name モデル化されたチップ ピン数 デコードサイズ サービスコア
Z80DMA Zilog Z8410 ダイレクトメモリアクセスコントローラー 10 1 コア 1
Z80CTC Zilog Z8430 カウンター / タイマー回路 10 4 コア 0
Z80PIO Zilog Z8420 パラレル入出力コントローラー 23 4 コア 1
PIT8253 Intel 8253 / 8254 プログラマブルインターバルタイマー 6 4 コア 0
PPI8255 Intel 8255 プログラマブルペリフェラルインターフェース 24 4 コア 1
WD1773 Western Digital WD1773 フロッピーディスクコントローラー 5 8 コア 1
SignalPort 実在のチップではありません — テストおよびブリッジ用ポート 16 1 コア 1
宣言されたデバイスに加えて、cpu という名前の常設の擬似デバイスが常に存在します。これはプロセッサー自身の制御ピンを公開するため、デバイスをマシン本体に配線することができます。

デバイスの宣言

デバイスは z80[] パーティションオブジェクト内の device 配列で、既存の memoryiodrivers 配列と並べて宣言します。各パーティションは独自のデバイスと独自のネットリストを持つため、2 つのパーティションをまったく異なる構成にすることもできます。nameどのチップかを選択するのに対し、typeどのアドレス空間かを選択する点に十分注意してください — ここは間違えやすい箇所です。
キー 必須 デフォルト 説明
name string はい 上記カタログのチップタイプ。大文字小文字は区別されません。認識されない名前はログメッセージを出してスキップされます。
addr number はい デコードのベースアドレス。
id string いいえ dev<i>N</i> インスタンスラベル。配線時にこのチップを参照するために使用します。最大 11 文字で、それより長い名前は切り詰められます。
type string いいえ "IO" アドレス空間 — "MEMORY" はチップをメモリ空間に配置し、それ以外の値は I/O 空間に配置します。
size number いいえ 1 デコードされる連続アドレスの個数。
enable number いいえ 有効 0 でデバイスを無効化します。false ではなく数値の 0 を使用してください。
busack number いいえ 1 このデバイスがバスを保持している間、ホストのバスアクノリッジをアサートします。Z80 DMA 専用。
maxburst number いいえ 4096 プロセッサーにバスを返すまでに転送する最大バイト数。Z80 DMA 専用で、0 にするとこのガードが外れます。
デバイスはペルソナの memory および io 領域のに適用されるため、デバイスのデコードが優先されます — 同じアドレスを覆う包括的な領域指定よりも強く効きます。デコード範囲は 64K 空間内に完全に収まっている必要があり、そうでなければデバイスはスキップされます。
"device": [ {
              "name":     "Z80DMA",
              "id":       "dma0",
              "enable":   1,
              "type":     "IO",
              "addr":     0x0F,
              "size":     1,
              "busack":   1,
              "maxburst": 4096
            },
            {
              "name":     "Z80CTC",
              "id":       "ctc0",
              "enable":   1,
              "type":     "IO",
              "addr":     0x10,
              "size":     4
            } ]

デバイス同士の配線 — インターリンク

interlink 配列がネットリストです。各エントリーが 1 本の配線を表し、1 つの接続先ピンと、それを駆動する 1 つ以上のソースを記述します。ピンは <id>.<PIN> の形式で参照します(例:dma0.RDYcpu.INT)。どちらの部分も大文字小文字を区別しません。
キー 必須 説明
dst string はい この配線が駆動する単一の入力ピン。各入力を駆動できる配線はちょうど 1 本だけです。
src array はい 1 個から 8 個のソース項。項はピン参照、またはリテラル文字列 "0" / "1" です。先頭に ! を付けるとその項が反転します。
op string いいえ 複数のソースを結合する方法 — BUFNOTANDORNANDNORXORXNOR。ソースが 1 個なら BUF、2 個以上なら OR がデフォルトです。
enable number いいえ 0 でこの配線を無効化します。
name string いいえ 自分用のラベル。ファームウェアは無視します。
重要:ネットが運ぶのはピンの電気的レベルであり、抽象的な「アサートされた」状態ではありません。1 が High、0 が Low で、電圧計で読み取るのとまったく同じです。/INT/BUSREQ のような負論理ピンはアイドル時に High で、Low になることでアサートされます。これがネットリストを回路図のように振る舞わせている理由です — 2 つの正論理タイマー出力の NOR は、picoZ80 上でも紙の上とまったく同じ動作をします。

ピンを駆動する 3 つの方法

直接、1 対 1op を省略してソースを 1 つだけ指定します。これはあるチップの出力から別のチップの入力へ引いた単純な 1 本の配線です。
{ "name": "フロッピーのデータリクエストが DMA を駆動する",
  "dst":  "dma0.RDY",
  "src":  [ "fdc0.DRQ" ] }
固定レベルへのハード結線 — 唯一のソースとしてリテラルの "0" または "1" を指定します。これはピンをグランドまたは電源ラインに直結するのと同等で、設計で使用しないすべての入力に対して行う処置です。未配線の入力はエラーではありませんが、起動時に報告され、その静止レベルを読み出すことになります。明示的に結線しておけば意図を文書化できます。
{ "dst": "dma0.CE",   "src": [ "0" ] }      /* Low に固定  - 常時選択される     */
{ "dst": "dma0.WAIT", "src": [ "1" ] }      /* High に固定 - 決してアサートされない */
論理条件を介して — 2 個から 8 個のソースと op を指定します。項は左から右へ順に結合され、任意の項の先頭に ! を付けて個別に反転できます。これはチップの間に小さなゲートをはんだ付けするのと同等です。
/* どちらのタイマー出力も High でない間だけ DMA が動作する。                  */
{ "name": "タイマーが DMA をゲートする",
  "dst":  "dma0.RDY",
  "op":   "NOR",
  "src":  [ "ctc0.ZCTO0", "ctc0.ZCTO1" ] }

/* ポート A のビット 1 を反転してポート B のビット 0 を駆動する。             */
{ "dst": "pio0.B0", "src": [ "!pio0.A1" ] }
割り込みラインは理解しておく価値のある特別なケースです。実際のハードウェアでは、すべての /INT ピンはオープンドレイン出力です。どのチップも共有ラインを Low に引くことができ、ラインが High に戻るのはすべてのチップが解放したときだけです。ネットリストはこれを、すべての割り込み出力の AND によって再現します。"op":"AND" は明示的に書く必要があります — 複数ソースのデフォルトは OR であり、それでは意味が逆転して、すべてのチップが同時にアサートしたときにしか割り込みが発生しなくなります。
{ "name": "割り込みチェーン",
  "dst":  "cpu.INT",
  "op":   "AND",
  "src":  [ "dma0.INT", "ctc0.INT", "pio0.INT" ] }

割り込み優先度 — デイジーチェーン

割り込みを発生させるチップ間の優先度は、設定内の数値ではなくどのように配線したかで決まります — これは実際の Z80 システムとまったく同じです。各チップは IEI(割り込みイネーブル入力)と IEO(割り込みイネーブル出力)を持ちます。あるチップの IEO を次のチップの IEI に配線すると、後者が前者より下位の優先度になります。チェーンの先頭のチップは IEI1 に結線します。
チップが IEO を High に駆動するのは、自身の IEI が High で、かつ自身が割り込み要求中でもサービス中でもない場合だけです。このひとつのルールだけで全体の動作が決まります。サービス中のチップは処理が終わるまで自分より下位のすべてを抑止し、IEI が Low になったチップはより高い優先度のデバイスがプロセッサーを占有していることを知って静かにしています。
/* ctc0 が最上位、次に dma0、その次に pio0。                                  */
{ "dst": "ctc0.IEI", "src": [ "1" ] },
{ "dst": "dma0.IEI", "src": [ "ctc0.IEO" ] },
{ "dst": "pio0.IEI", "src": [ "dma0.IEO" ] }
割り込みモード 2 は完全にサポートされています。アクノリッジ時には、要求中かつブロックされていないデバイスのうち最も優先度の高いものが、プログラムされたベクターを供給し、自身をサービス中としてマークし、ペンディングフラグをクリアします。RETI でサービス中の状態が再びクリアされ、下位のチップが解放されます。
物理的に装着されたカードは常に、宣言されたデバイスより優先されます。ホストバス上の実際のペリフェラルは独自の IEI / IEO ピンを持ちますが、picoZ80 はそれらを見ることも調停することもできません — 割り当てられる予備のプロセッサーピンがないためです。そこでファームウェアは、まず既存のカードハンドラーにアクノリッジを提示し、どのカードも応答しなかった場合にのみ自分で受け取ります。これにより、例えば MZ-8BIO3 や MZ-1E24 シリアルカードが既に装着されているマシンに対しても、それらを乱すことなくデバイスを追加できます。

デバイスのピン配置

以下の表は、各デバイスがネットリストに公開するすべてのピンを示しています。方向はチップ側から見た向きです。アイドルは、何も駆動していないときに入力が読み取るレベルです。宣言のみと記されたピンは存在し配線もできますが、まだファームウェアが実際に処理していません — 今日書いた設定がそれらが有効になったときにもそのまま正しくあり続けるよう、記載しています。
cpu — プロセッサー擬似デバイス
ピン 方向 アイドル 機能
INT 入力 1 マスカブル割り込み要求。CPU 上で唯一機能する接続先です。Low に駆動すると割り込みが発生します。
BUSACK 出力 1 バスアクノリッジ。宣言のみ — まだ駆動されないため常に High を読み取ります。
BUSRQ, NMI, RESET, WAIT 入力 1 宣言のみ — これらを配線してもまだ効果はありません。
M1, CLK, HALT 出力 宣言のみ — まだ駆動されません。
Z80DMA — ダイレクトメモリアクセス(Z8410)
ピン 方向 アイドル 機能
RDY 入力 1 ペリフェラルからの転送要求。唯一機能する入力です。そのアクティブ極性は設定ではなくチップの WR5 レジスターで決まります。アクティブ状態への遷移で転送が始まります。
BUSREQ 出力 1 バスリクエスト、負論理、オープンドレイン。DMA がバスを保持している間 Low に駆動されます。
INT 出力 1 割り込み要求、負論理、オープンドレイン。
IEI 入力 1 割り込み優先度入力。前段チップの IEO から配線するか、チェーンの先頭なら 1 に結線します。
IEO 出力 1 割り込み優先度出力。次段チップの IEI へ配線します。
BAI, CE, WAIT, M1 入力 1 宣言のみ — モデルはこれらを参照しません。
BAO 出力 1 宣言のみ — まだ駆動されません。
Z80CTC — カウンター / タイマー(Z8430)
ピン 方向 アイドル 機能
CLKTRG0CLKTRG3 入力 0 チャンネル 0 から 3 のクロックまたはトリガー入力。有効エッジはチャンネル制御ワードで選択します。
ZCTO0ZCTO2 出力 0 チャンネル 0 から 2 のゼロカウント / タイムアウト出力。短いパルスとして出力されます。チャンネル 3 には出力ピンがありません — 実デバイスとまったく同じです。
INT 出力 1 割り込み要求、負論理、オープンドレイン。
IEI 入力 1 割り込み優先度入力 — デイジーチェーンを参照。
IEO 出力 1 割り込み優先度出力。
Z80PIO — パラレル入出力(Z8420)
ピン 方向 アイドル 機能
A0A7 双方向 1 ポート A のデータビット。方向は実行時に Z80 がプログラムしたモードに従います。
B0B7 双方向 1 ポート B のデータビット。
ASTB, BSTB 入力 1 ポートストローブ入力、負論理。有効なのは立ち上がりエッジです。
ARDY, BRDY 出力 0 ポートハンドシェイクのレディ出力、正論理。
INT 出力 1 割り込み要求、負論理、オープンドレイン。
IEI 入力 1 割り込み優先度入力 — デイジーチェーンを参照。
IEO 出力 1 割り込み優先度出力。
デコードされる 4 つのアドレスは順に、ポート A データ、ポート A 制御、ポート B データ、ポート B 制御です。リセットすると両ポートともモード 1(入力)が選択されます。
PIT8253 — インターバルタイマー(8253 / 8254)
ピン 方向 アイドル 機能
GATE0GATE2 入力 1 カウンターのゲート入力。モード 0、2、3、4 はレベルに応答し、モード 1 と 5 は立ち上がりエッジでトリガーされます。
OUT0OUT2 出力 1 カウンター出力。
デコードされる 4 つのアドレスはカウンター 0、1、2 に続いて制御ワードです。8253 に割り込みピンはありません。このデバイスには設定キーがもう 1 つ適用されます — "clockhz" がカウンターの入力周波数をヘルツで指定し、デフォルトは 1000000 です。Sharp MZ シリーズはタイマーを 895000 で駆動しています。
PPI8255 — パラレルインターフェース(8255)
ピン 方向 アイドル 機能
PA0PA7 双方向 1 ポート A のデータビット。
PB0PB7 双方向 1 ポート B のデータビット。
PC0PC7 双方向 1 ポート C のデータビット。上位半分と下位半分は独立した方向を持ちます。
デコードされる 4 つのアドレスはポート A、B、C に続いて制御ワードです。モード 1 およびモード 2 のハンドシェイクラインはネットリストには公開されていません。
既知の制限
現状、8255 のあるポートの入力ピンを 1 本でも駆動すると、同じポートの他の入力ビットがクリアされてしまいます。これが修正されるまでは、8255 の入力ピンは一度に 1 ビットずつ使用するか、代わりに Z80 PIO を使用してください — PIO は各ビットを独立して扱うため、この問題の影響を受けません。
WD1773 — フロッピーディスクコントローラー
ピン 方向 アイドル 機能
DDEN 入力 1 倍密度選択、負論理。Low レベルで MFM が選択されます。
WPRT 入力 1 ライトプロテクト、負論理。
INTRQ 出力 0 割り込み要求。Zilog のチップとは異なり、このデバイスでは正論理です。
DRQ 出力 0 データリクエスト。DMA の RDY ピンを駆動することを想定しています。
MOTOR 出力 0 モーターオン。
デコードされる 8 つのアドレスは Sharp MZ-1E05 の配置に従います — ステータスおよびコマンド、トラック、セクター、データ、ドライブ選択およびモーター、サイド選択、密度、そして 2 つ目のステータス読み出しです。追加の設定キーが 3 つ適用されます — "machine" がイメージのジオメトリを選択し("MZ-80B""MZ-2500"、省略時は MZ-700)、"drives" がドライブ数を 1 から 4 で指定し、"file" がマウントするディスクイメージを指定します。WD1773 は 1 つしか宣言できません。
SignalPort — テストおよびブリッジ用ポート
ピン 方向 アイドル 機能
IN0IN7 入力 0 Z80 がこのポートを読み取ると、これら 8 本のレベルがビット 0 から 7 として返ります。
OUT0OUT7 出力 0 Z80 が書き込むと、これら 8 本のレベルが駆動されます。
これは実在するチップのモデルではありません。ネットリストにおける計測器のようなもので、8 本の配線を 1 つの I/O アドレスに割り当て、Z80 上で動作するプログラムから設計に信号を送り込み、返ってきたものをサンプリングできるようにします。配線のアイデアを確定させる前に試すには、これが最も直接的な方法です。

実例 — Z80 DMA を追加する

以下は完全に動作する設定例です。I/O ポート 0x0F に Z80 DMA を、0x10 に Z80 CTC を配置し、タイマーが転送をゲートするよう両者を配線しています。DMA の未使用入力はすべて固定レベルに結線し、2 つの割り込み出力はワイヤード AND でプロセッサーの割り込みピンに接続しています。
"device": [ { "name": "Z80DMA", "id": "dma0", "enable": 1,
              "type": "IO", "addr": 0x0F, "size": 1,
              "busack": 1, "maxburst": 4096 },

            { "name": "Z80CTC", "id": "ctc0", "enable": 1,
              "type": "IO", "addr": 0x10, "size": 4 } ],

"interlink": [
    /* DMA の未使用入力。状態を明示するため固定レベルに結線する。              */
    { "name": "dma バスアクノリッジ",   "dst": "dma0.BAI",  "src": [ "cpu.BUSACK" ] },
    { "name": "dma チップイネーブル",   "dst": "dma0.CE",   "src": [ "0" ] },
    {                                "dst": "dma0.WAIT", "src": [ "1" ] },
    {                                "dst": "dma0.IEI",  "src": [ "1" ] },
    {                                "dst": "dma0.M1",   "src": [ "1" ] },

    /* CTC の未使用トリガー入力。                                             */
    { "dst": "ctc0.CLKTRG2", "src": [ "0" ] },
    { "dst": "ctc0.CLKTRG3", "src": [ "0" ] },
    { "dst": "ctc0.IEI",     "src": [ "1" ] },

    /* 2 つのタイマー出力が NOR ゲートを介して転送要求をゲートする。          */
    { "name": "タイマーが dma をゲートする",
      "dst":  "dma0.RDY", "op": "NOR",
      "src":  [ "ctc0.ZCTO0", "ctc0.ZCTO1" ] },

    /* どちらの割り込み出力もオープンドレインで、CPU へワイヤード AND する。  */
    { "name": "割り込みチェーン",
      "dst":  "cpu.INT", "op": "AND",
      "src":  [ "dma0.INT", "ctc0.INT" ] } ]
DMA に関するそれ以外のすべて — バイトを転送するのか特定の値を検索するのか、どの方向に転送するのか、ブロック長、アドレス、バイト / バースト / コンティニュアスのいずれのモードか — は、実際のハードウェアとまったく同じように、実行時に Z80 がチップ自身のレジスターを通してプログラムします。設定が決めるのはチップがどこに置かれるか何に配線されているかだけです。
メモリ間転送については一言添えておく価値があります。多くの人が最初に試すものだからです。この種の転送には要求を出すペリフェラルが存在しないため、駆動すべき RDY ラインがありません。実際のハードウェアでは Z80 DMA は Force Ready コマンド(B3)でこれを解決し、レディ条件を内部で供給します。picoZ80 のモデルも同じことを行います。ENABLE DMA87)は最後に書き込むバイトでなければならない点に注意してください — 他のほとんどすべての制御バイトはチップを無効化するためです。Force Ready はバス解放とブロック終了で取り消されるため、自分自身で再起動し続ける必要のある転送では、Force Ready コマンドではなくタイマー出力などの配線されたソースからレディ信号を取る必要があります。

上限と現在の制約

このフレームワークは固定サイズのテーブルによって制限されています。いずれかを超えた場合は起動ログに報告されますが、それ以外では設定が正常に読み込まれたように見えます。
上限 備考
デバイス数 8 それ以降のエントリーは無視されます。
シグナル総数 96 すべてのデバイスにわたる合計に、CPU の 9 本のピンを加えたもの。
インターリンクの配線数 48 それ以降のエントリーは無視されます。
配線あたりのソース数 8 それ以降の項は無視されます。
収束の反復回数 8 相互結合ラッチには十分な回数です。真の発振は報告されます。
デバイス識別子 11 文字 それより長い識別子は切り詰められます。
CPU の 9 本のピンは最後に登録されるため、シグナルテーブルを使い切った設計では最初に cpu.INT が失われ、すべての割り込み配線が黙って解決に失敗します。大規模な設計で割り込みが動かなくなったら、ピンの数を数えてみてください。
重要な制限事項
  • カウンター系デバイスは BaseZ80 モデルでしかカウントを進めません。Z80 CTC と 8253 をクロックするコア 0 のフリーランティックは、BaseZ80 ファームウェアでのみ起動されます。Sharp、Amstrad、Tatung、OpenZ80 の各ビルドでは、これら 2 つのデバイスは宣言できてアドレス指定もできますが、カウンターは動きません。
  • デバイスはプロセッサーのリセットではリセットされません。デバイスは設定が適用されるときに一度だけ初期化されます。
  • 仮想 DMA を実在のペリフェラルからトリガーすることはできません。picoZ80 が届くのは Z80 ソケットだけで、RP2350 の汎用ピンはすべて割り当て済みのため、物理カードのデータリクエストラインをネットリストに引き込む手段がありません。仮想 DMA は定数、Force Ready、あるいは別の仮想デバイスから要求を受け取ります — フロッピーコントローラーの DRQ 出力がその代表例です。これは欠陥ではなくハードウェアの性質です。
  • mirrorrefreshtrace の各キーは受け付けられますが、現在は効果がありません。
Web インターフェースを使った手順については、picoZ80 ユーザーマニュアルを参照してください。内部構造、デバッグシェルのツール群、新しいデバイスモデルの追加方法については、開発者ガイドを参照してください。

ICE(デバッグシェル)

picoZ80 には、USB CDC チャンネル 1(ボードを USB 経由で接続したときに列挙される 2 番目のシリアルポート)上に組み込みの ICE(インサーキットエミュレーター)デバッグシェルが含まれています。シェルはコア 0 上で実行され、Z80CPU コンテキスト構造体内の共有フラグを介してコア 1 のエミュレーションループと通信します。デバッグシェルは、INCLUDE_DBGSH 定義でコンパイルされる DBGSH ファームウェアバリアントでのみ利用可能です。

USB CDC シリアルチャンネル

picoZ80 は、ホストに接続されると複数の USB CDC シリアルポートを列挙します。CDC 0CDC 1 は、物理 UART / ESP32 ブリッジと ICE デバッグシェル(CDC 1、DBGSH バリアント)に使用されます。CDC 2CDC 3 は、config.json でいずれかが設定されている場合、仮想 RS-232C シリアルカード — MZ-8BIO3 または MZ-1E24 ドライバー — の 2 つのチャンネル(A および B)です。これら 2 つのポートの背後に物理 UART はありません。これらはエミュレートされた Z80 SIO へのリングバッファブリッジであり、ホスト側で CDC 2/3 に書き込まれたデータはゲストから見える Z80 SIO のチャンネル A/B に現れ、その逆も同様です。シリアルカードが設定されていない場合、CDC 2 と CDC 3 は列挙されません。

アーキテクチャ

  • 入出力: USB CDC チャンネル 1、115200 ボー。シェルのプロンプトは dbg> です。コマンド履歴(16 エントリ)と文字エコーがサポートされています。
  • ブレークポイント: cpu->dbgBpAddr[] に格納される最大 8 個の同時ブレークポイント。コア 1 は各オペコードフェッチの前にブレークポイント配列をチェックします。ヒット時には cpu->hold = true を設定し、dbgBpHit を介してコア 0 に通知します。
  • シングルステップ: step コマンドは cpu->dbgStepCount を設定します。コア 1 は各命令の後にこのカウンターをデクリメントし、ゼロに達すると自動的にホールドします。各ステップごとに、前後のレジスタ状態と逆アセンブルされた命令が表示されます。
  • 実行トレース: トレースが有効な場合、512 エントリのリングバッファ(cpu->dbgTrace[])が、実行された各命令の PC、オペコード、フラグレジスタを記録します。各 32 ビットエントリは [31:16]=PC, [15:8]=opcode, [7:0]=F register をパックします。
  • メモリアクセス: 物理メモリアクセス(dm pwm p)は PIO ステートマシンを介して実際の Z80 バスサイクルを駆動します。仮想アクセス(dm vwm v)は PSRAM を直接リード/ライトします。自動モード(修飾子なしの wm)はメモリマップに従います。RP2350 アクセス(dm r)は、範囲検証を伴ってホストマイクロコントローラーのアドレス空間を読み取ります。
  • ホールド/リリース: hold コマンドは cpu->hold = true を設定します。コア 1 は cpu->holdAck を介して確認応答し、シェルが共有状態にアクセスする前に CPU が静止していることを保証します。
  • ブレーク: break コマンドはコア 1 をホールドし、現在の PC、実行しようとしている命令(逆アセンブル)、および全レジスタセットを報告します — 実行中のプログラムがどこにあるかを、ステップ実行や検査の前に確認する最速の方法です。go/cont で再開します。
  • クイックキーと省略形: デバッグ中のタイピングを減らすため、最も一般的なコマンドには単一文字のクイックキーがあります — c=cont、s=step、g=go、b=break、r=regs、h=hold — これらは接頭辞マッチングの前に解決されるため、決して曖昧として扱われることはありません。その他のコマンドは、一意となる最短の接頭辞として入力できます(例:ste=step、sta=status、dis=disassemble)。曖昧な接頭辞は候補を列挙し、完全に入力された名前が常に優先されます。

コマンドリファレンス

コマンド 構文 説明
help help 全コマンドを列挙
regs regs すべての Z80 レジスタ、フラグ、サイクルカウントをダンプ
dm dm <p|f|v|r> <addr> [len] メモリをダンプ(physical / fetch / virtual / RP2350)
search search [p|v] <start> <end> <hex..>|"text" バイトパターンまたは ASCII テキスト文字列でメモリを検索。p = 物理バス、v = 仮想 PSRAM、省略でマップ済み。マッチは 8 バイトのコンテキスト付きで表示。物理/マップ済みアクセスでは CPU を自動ホールド。パターンは最大 32 バイト
cmp cmp [f] <phys> <virt> <len> 物理バスメモリを仮想 PSRAM と比較
dis dis [p|v] [addr] [count] Z80 コードを逆アセンブル
asm asm [addr] インタラクティブな Z80 アセンブラー
memmap memmap [block] メモリバンクポインタテーブルを表示
memptr memptr [addr] PSRAM の memPtr テーブルを表示
iomap iomap [port] I/O ポートハンドラーテーブルを表示
status status システムステータス(CPU 周波数、PSRAM、稼働時間)
ver ver ファームウェアバージョンとパーティション情報
drivers drivers アクティブなドライバーとインターフェースを列挙
hold hold CPU エミュレーションを一時停止(無音で)
release release CPU エミュレーションを再開
break break 実行中の Z80 を停止し、停止した場所を報告 — PC、実行しようとしている命令(バイト+逆アセンブル)、および全レジスタダンプ。(無音で一時停止する)hold とは異なり、break は状態を表示するため、実行がどこにあるか(例:ループにはまっているとき)を正確に確認できます。break を再発行すると状態を再表示します。go/cont で再開するか、step でシングルステップします。Z80 がリセットでホールドされているか、そのクロックがゲートされている場合はタイムアウト(約 3 秒)
go go 継続(ホールドを解除、ブレークポイントは有効)
cont cont go のエイリアス。実行を継続(ホールドを解除、ブレークポイントは有効)
step step [n] n 命令をシングルステップ
bp bp <addr> ブレークポイントを設定(最大 8)
bc bc <n|*> ブレークポイント n またはすべてをクリア
bl bl ブレークポイントを列挙
wm wm [p|v] <addr> <byte>... メモリに書き込み(物理/仮想/自動)
fill fill [p|v] <addr> <len> [w|d] <val> メモリを定数値で埋める
copy copy <pv|fp|vp> <src> <len> <dst> 物理と仮想の間でメモリをコピー
memtest memtest <addr> <len> [pattern] 物理メモリをテスト(write+read、write+fetch、インターリーブ)
in in <port> Z80 I/O ポートを読み取り
out out <port> <byte> Z80 I/O ポートに書き込み
trace trace <on|off|dump [n]|clear|rt|byte ...> 実行トレース制御。rt はリアルタイムトレース出力を有効化、byte はバイトレベルトレースを有効化
verify verify <on|off> 完全なオペコードフェッチ検証をトグル
fwait fwait <0-4> 追加の M1(オペコードフェッチ)ウェイトステートを強制。0 = オフ(デフォルト)
iowait iowait <0-8> 追加の I/O サイクルウェイトステートを強制。0 = オフ(デフォルト)
corrupt corrupt [clear] 検出されたフェッチ破損を表示またはクリア
fdctrace fdctrace <on|off|dump> FDC I/O トレースを有効化/無効化。dump は直近 64 操作を表示
qdtrace qdtrace <on|off|dump> Quick Disk I/O トレースを有効化/無効化。dump は直近 64 操作を表示
piodbg piodbg [clear] RP2350 PIO ハードウェア診断を表示(FDEBUG、FSTAT、FIFO、PC、GPIO)。clear はスティッキーフラグをリセット
load load <p|v> <file> <addr> [len] [ofs] ESP32 SDカードから Z80 メモリにファイルをロード。p = 物理バス、v = 仮想 PSRAM バンク 0。file/sdcard/ からの相対パス。len を省略するとファイル全体をロード(最大 64KB)、指定すると最大 1MB。オプションの ofs でファイルオフセット指定。物理書き込みでは CPU を自動ホールド。PSRAM バンク 63 をスクラッチバッファとして使用
save save <p|pf|v> <file> <addr> <len> Z80 メモリを ESP32 SDカード上のファイルに保存。p = 物理バス、pf = 物理フェッチ(M1)、v = 仮想 PSRAM バンク 0。file/sdcard/ からの相対パス。最大 64KB。物理読み取りでは CPU を自動ホールド。物理読み取り中は周期的な DRAM リフレッシュ
dir dir [path] ESP32 SDカード上のファイルを列挙。オプションのパスは /sdcard/ からの相対パス。ファイル名とサイズを表示
echo echo [on|off] 端末エコーをトグル
reset reset Z80 リセットを強制
set set <reg|flags|memmap|memptr|iomap> <idx> <val> Z80 レジスタ、フラグ、メモリマップ、memPtr、または I/O マップエントリを実行時に変更
hist hist [n] コマンド履歴を表示(ESP32 NVS を介してセッション間で保持)
savehst savehst コマンド履歴を ESP32 NVS に強制保存
ipl ipl 8255 PPI のポート C ビット 3 をトグルして IPL リセット(BST モード)を実行。完全な Z80 リセットなしでブートモードにリセット
mmutrace mmutrace マシン固有のトレース情報(MMU 状態、I/O レジスタスナップショット)をダンプ。出力は登録されたトレースハンドラーによってペルソナごとに異なる
intcount intcount 割り込みアクノリッジカウントと現在の割り込み状態を表示
psync psync [start end] 物理メモリを PSRAM に同期。オプションのアドレス範囲。デフォルトは全アドレス空間
dskimage dskimage read <filename> [cylinders] [heads] / dskimage write <filename> 物理フロッピーディスクを SDカード上の DSK ファイルにイメージ化(read)、または SDカードの DSK ファイルを物理フロッピーに書き込み(write)。dskimage <filename> はデフォルトで read(後方互換)。省略時はジオメトリを自動検出
busdiag busdiag バス診断を表示(PIO 状態、信号レベル、バス競合)
fdcimage fdcimage FDC イメージングのステータスと進捗を表示
fdcdiag fdcdiag FDC 診断情報を表示(コントローラー状態、レジスタダンプ)
gadiag gadiag ゲートアレイ診断情報を表示(コマンド状態、割り込みルーティング)
クイックキーと省略形。 コマンドは完全に入力する必要はありません。最も多用される 6 つのコマンドには単一文字の クイックキー があり — c(cont)、s(step)、g(go)、b(break)、r(regs)、h(hold)— その他のすべてのコマンドは一意となる最短の接頭辞として入力できます(例:step には stestatus には stadisassemble には dis)。完全に入力されたコマンド名が常に優先され、曖昧な接頭辞は一致するコマンドのリストを表示します。

ESP32 コプロセッサ

ESP32-S3-PICO-1 モジュールは、すべてのネットワークおよびストレージ機能を処理するコプロセッサとして動作します。次の 2 つのインターフェースを介して RP2350 と通信します。
  • FSPI(50MHz、4 線式 SPI) — バイナリ IPC プロトコル v1.1 — 高速バルクデータ転送(ROM イメージ、ディスクセクターのリード/ライト、設定ファイルのダウンロード)。プロトコルは、CRC32 整合性チェック付きの固定 64 バイトバイナリフレームヘッダーを使用します(以前の XOR チェックサムを置き換え)。DMA チャンネルは初期化時に事前割り当てされ、決して解放されないため、転送ごとの claim/unclaim のオーバーヘッドと競合状態が排除されます。バーストセクター転送により、単一の SPI トランザクションで最大 16 × 512 バイトセクター(8KB)が可能になり、フロッピーおよび QuickDisk のイメージロード時間が大幅に改善されます。RX DMA チャンネルは、コア 1 の PSRAM QMI バス競合によって引き起こされる FIFO オーバーフローを防ぐために HIGH PRIORITY に引き上げられます。
  • UART(460.8kbaud) — 制御メッセージ、ステータス照会、短いデータ交換のためのコマンド/レスポンスプロトコル。

ネットワーキングモード

ESP32 ファームウェアは、事前構築済みの sdkconfig ファイルを介してビルド時に選択される 3 つのネットワーキングモードをサポートします。
モード 設定ファイル WiFi USB NCM コンソール FCC/RED 要否
WiFi のみ sdkconfig.mode_wifi_only Yes No USB Serial/JTAG Yes
WiFi + NCM sdkconfig.mode_wifi_and_ncm Yes Yes TinyUSB CDC-ACM Yes
NCM のみ sdkconfig.mode_ncm_only No Yes TinyUSB CDC-ACM No
USB NCM(Network Control Model) は、ESP32-S3 USB OTG ポート(GPIO 19/20)上に CDC-NCM Ethernet アダプターを提示します。コンポジット USB デバイスが、CDC-ACM シリアルポート(デバッグログ用)と NCM ネットワークインターフェースの両方を公開します。組み込みの DHCP サーバーは、192.168.7.0/24 サブネットからホストに IP アドレスを割り当て、picoZ80 は 192.168.7.1 でアクセス可能です。リース時間は 120 分です。
WiFi+NCM モードでは、HTTP サーバーは INADDR_ANY:80 にバインドし、両方のインターフェースに同時にサービスします。WiFi は非同期に接続するため、USB NCM インターフェースは電源投入時に即座に利用可能です。
NCM のみが有効な場合、WiFi 無線は完全に無効化され、ウェブインターフェースから WiFi マネージャーページが削除され、ダッシュボードのステータスパネルのタイトルが「WiFi Configuration」から「Network Configuration」に変わります(SSID/WiFi 詳細の代わりに USB NCM ステータスを表示)。ESP32-S3 のアンテナ整合ネットワークを PCB 上に実装する必要はありません。

SDカードインターフェース

ESP32 は SPI インターフェースを介して SDカードを管理します。SDカードは FAT32 としてマウントされ、RP2350 からのすべてのファイルアクセスは ESP32 によって仲介されます — RP2350 は FSPI/UART リンク経由でファイル I/O コマンドを送信し、ESP32 が実際の FAT32 のリード/ライト操作を実行します。
SDカードは ESP32 ウェブサーバーからも直接アクセス可能で、ウェブサーバーは webfs/ ディレクトリからファイルを提供し、ファイルマネージャーが HTTP 経由でカードの内容を閲覧および変更できるようにします。

ウェブサーバー

ESP32 はポート 80 で HTTP サーバーを実行します(TLS なし — ローカルネットワーク使用のみ)。すべてのウェブアセット(HTML、CSS、JavaScript)は SDカード上の webfs/ ディレクトリから提供されるため、ESP32 ファームウェアを再フラッシュすることなくウェブインターフェースを更新できます。ウェブサーバーは次を処理します。
  • SDカードの webfs/ ディレクトリからの静的ウェブアセットの提供。
  • JSON データ用の REST API エンドポイント(システムステータス、設定の読み書き、ファイル操作)。
  • RP2350 と ESP32 の両方の OTA ファームウェアアップロードエンドポイント。
  • リアルタイムのダッシュボードステータス更新用の WebSocket 接続。

RP2350 ↔ ESP32 コマンドプロトコル

RP2350(コア 0)は、UART リンク上のシンプルなコマンド/レスポンスプロトコルを使用して ESP32 と通信します。コマンドはオプションのペイロードバイトを伴う単一バイトのオペコードです。ESP32 は各コマンドに対してステータスバイトに続けて任意のレスポンスデータで確認応答します。
一般的なコマンドのカテゴリ:
  • ファイル I/O — open、read、write、close、ディレクトリ列挙、ファイル stat。
  • 設定 — config.json の内容の要求、更新された設定の書き込み、再読み込み要求。
  • ディスク — ディスクイメージのマウント/アンマウント、セクターのリード/ライト(WD1773 および QDDrive エミュレーションからリレー)。現在マウントされているフロッピーおよび QuickDisk のイメージファイル名は ESP32 によって追跡され、ウェブインターフェースのアクションメニューに表示されます。
  • システム — バージョン照会、再起動要求、NVS の読み書き。
FSPI インターフェースは、ペイロードが UART には大きすぎるバルク転送(ROM イメージのアップロード、ディスクセクターデータ)に使用され、UART はすべての制御コマンドを処理します。

ネットワーク IPC コマンド

Celestite Phase 2 ネットワーキング実装は、RP2350 が ESP32 にネットワーク操作を要求するために使用する 5 つのコア間 IPC コマンドを追加します。これらのコマンドは FSPI/UART リンク上で転送され、ESP32 が対応する BSD ソケット操作を実行します。非ブロッキング接続は select() をタイムアウト付きで使用し、ソケットごとの保留フラグが処理中の操作を追跡します。
コマンド オペコード 説明
IPCF_CMD_NET_CFG 0x10 ESP32 ネットワーク設定を取得 — IP アドレス、ゲートウェイ、サブネットマスク、MAC アドレスを返します
IPCF_CMD_NET_SOCK 0x11 ソケットライフサイクル操作 — ソケットの open、connect、listen、close、または disconnect
IPCF_CMD_NET_SEND 0x12 open されたソケットにデータを送信
IPCF_CMD_NET_RECV 0x13 open されたソケットからデータを受信
IPCF_CMD_NET_PING 0x14 ICMP エコー要求(ping)
この IPC レイヤーを介してサポートされる W5100 ソケットコマンドは、OPEN、CONNECT、LISTEN、SEND、RECV、CLOSE、DISCON です。ESP32 はこれらを標準の BSD ソケット API 呼び出し(socket()connect()listen()send()recv()close()shutdown())に変換し、Celestite ボードが netfs.py ファイルサーバーなどのネットワークサービスと通信できるようにします。

ウォッチドッグとブート診断

RP2350 ファームウェアは、ブート時のハングやメインループのストールを検出して回復するためにハードウェアウォッチドッグタイマーを使用します。ウォッチドッグはブートシーケンスの早い段階で 30 秒のタイムアウトで有効化され、各主要マイルストーンでキック(watchdog_update())されます。いずれかのブートステージまたはメインループの反復がタイムアウトより長くかかった場合、ウォッチドッグは自動的に RP2350 をリセットします。

ブート進捗の追跡

ブート進捗は、ウォッチドッグリセットを生き延びる(ただし電源投入リセットは生き延びない)RP2350 のウォッチドッグスクラッチレジスタを使用して追跡されます。これにより、ウォッチドッグリセット後に、ハングの前にどのブートステージに到達していたかをファームウェアが正確に判断できます。
スクラッチレジスタ 名前 内容
scratch[0–3] ブート履歴 直近 4 回のリセット試行 — 各エントリは (attempt_count << 24) | (stage << 16) | (resetCause & 0xFFFF) をエンコード。各ウォッチドッグリセットでエントリがシフト:[0]←[1]←[2]←[3]←current
scratch[4] SPI 診断 FSPI リンク用のパックされた診断カウンター:ブレッドクラム、メッセージタイプ、T1/T3 タイムアウトカウンター、不正フレーム数、OK 数。
scratch[5] マジックマーカー スクラッチレジスタが有効なブート進捗データを含むことを示すために 0xB00710BE に設定。
scratch[6] 現在のステージ 最新のブートステージコード(下表参照)。
scratch[7] リセット原因 ハードウェアリセットコントローラーからのリセット原因コード。
ブートステージコードは 0x01(開始)から 0x10(メインループ進入)まで進行します。メインループ内のサブステージ(0x11–0x17)とコア間コマンド処理(0x20–0x27)はきめ細かな追跡を提供します。
コード ステージ 説明
0x01 BOOTP_START エントリポイントに到達
0x02 BOOTP_CLK_SET システムクロックを構成
0x03 BOOTP_PSRAM_INIT PSRAM 初期化開始
0x04 BOOTP_PSRAM_OK PSRAM 初期化成功
0x05 BOOTP_STDIO_INIT USB stdio 初期化
0x06 BOOTP_PIO_INIT PIO ステートマシンをロード
0x07 BOOTP_Z80_INIT Z80 CPU コンテキスト初期化
0x08 BOOTP_USB_INIT USB ブリッジ初期化
0x0A BOOTP_ESP_HS_SYNC ESP32 SPI ハンドシェイク同期
0x0B BOOTP_CORE1_LAUNCH コア 1 起動
0x0D BOOTP_FSPI_INIT FSPI バイナリ IPC 初期化
0x0E BOOTP_ESP_INIT ESP32 通信準備完了
0x10 BOOTP_MAIN_LOOP メインループ進入
0x11–0x17 メインループサブステージ USB ポーリング、コア間、SPI NOP/CMD、タスク
0x20–0x27 コア間コマンド フロッピーロード、QD ロード、RAMFILE ロード、ファイル I/O

PSRAM 永続ログ(plogf)

8MB PSRAM の最後の 4KB(アドレス 0x117FF000)は、ウォッチドッグリセットを生き延びる永続デバッグログ用に予約されています。plogf() マクロは、USB が通常の debugf() 出力に利用可能になる前に、ブート中にこのバッファへ printf スタイルのメッセージを書き込みます。次の起動が成功すると、dump_plog() 関数がキャプチャされたメッセージをデバッグコンソールに出力し、その後バッファをクリアします。ログはシンプルな構造を使用します:4 バイトのマジックマーカー(0x504C4F47 = "PLOG")、4 バイトの長さカウンター、3840 バイトの循環テキストバッファ。

フォールト診断

ファームウェアは、ハードフォールト、メモリ管理フォールト、バスフォールト、使用法フォールトに対する Cortex-M33 フォールトハンドラーをインストールします。フォールトが発生すると、ハンドラーは完全な診断スナップショットを PSRAM の最後の 256 バイト(アドレス 0x117FFF00)に、マジックマーカー(0xFA017000)、フォールトタイプ、関連するすべてのレジスタ(PC、LR、SP、R0–R3、R12、PSR)、Configurable Fault Status Register(CFSR)、Hard Fault Status Register(HFSR)、Bus Fault Address Register(BFAR)、Memory Management Fault Address Register(MMFAR)、コア ID とともに保存します。その後ハンドラーは無限ループに入り、ウォッチドッグがリセットをトリガーできるようにします。次の起動時、ファームウェアは有効なフォールト診断をチェックし、キャプチャされた情報を debugf() 経由で出力するため、ライブデバッガーセッションを必要とせずに事後分析が可能になります。

Flash 設定のクリア

RP2350 OTA アップデート機構は、ファームウェアアップロードに加えて 2 つの追加操作をサポートします。
  • App Config のクリアFW_CFGCLEAR_ID = 0xB1D7E5FA)— ターゲットファームウェアスロットに関連付けられた App Config パーティション(ROM イメージとミニファイされた JSON)を消去します。これにより、次の起動時にファームウェアが SDカードから config.json を再読み込みすることを強制します。これは、ファームウェアバージョン間で設定スキーマが変更された場合に必要です。
  • Flash ヘッダーのクリアFW_HDRCLEAR_ID = 0xC2E8F6AB)— Flash パーティションヘッダーを工場出荷時のデフォルトにリセットします。ブートローダー設定(パーティション 0)は保持されますが、すべてのアプリケーションパーティションメタデータがゼロから再構築されます。パーティションテーブルが破損した場合、または異なるパーティションレイアウトを期待する以前のファームウェアバージョンにダウングレードする場合に使用します。
両方の操作は RP2350 OTA ウェブページのチェックボックスからトリガーされ、ファームウェアアップデートプロセス中にブートローダーによって実行されます。

SWD デバッグ — RP2350

RP2350 は、ARM Serial Wire Debug(SWD)を介した完全なソースレベルデバッグをサポートします。CMSIS-DAP 互換プローブ(Raspberry Pi Debug Probe、Black Magic Probe など)を、デバッグヘッダーのピン 1(SWCLK)、2(SWDIO)、5(GND)に接続します。

OpenOCD のセットアップ

picoZ80 では、コアごとに別々の GDB ポートで SMP デバッグを有効にするために、標準の OpenOCD RP2350 ターゲットスクリプトに小さな変更が必要です。
sudo cp /usr/local/share/openocd/scripts/target/rp2350.cfg \
        /usr/local/share/openocd/scripts/target/rp2350_tzpu.cfg
rp2350_tzpu.cfg を編集します — if {[string compare $_USE_CORE SMP] == 0} ブロック内の target smp 行を見つけ、先頭の # を削除します。
# Before:
    #target smp $_TARGETNAME_0 $_TARGETNAME_1

# After:
    target smp $_TARGETNAME_0 $_TARGETNAME_1
このたった 1 つの変更により、OpenOCD はコア 0 を GDB ポート 3333 に、コア 1 を GDB ポート 3334 に登録し、コアごとに独立した GDB セッションを可能にします。GDB を開始する前に OpenOCD を起動します。
openocd -f interface/cmsis-dap.cfg -f target/rp2350_tzpu.cfg -c "adapter speed 5000"

GDB の設定

ディレクトリごとの .gdbinit ファイルの自動ロードを許可するために、次の内容を ~/.gdbinit に追加します(プロジェクトの場所に一致する絶対パスを使用)。
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
ブートローダーのデバッグ
# 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
メインファームウェアのデバッグ
# 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
xac <address> <count> GDB コマンドは .gdbinit.3333 / .gdbinit.3334 ファイルで定義されています。これはメモリを 16 進と ASCII の組み合わせ出力としてダンプし、PSRAM バンクの内容やメモリマップされたデバイスの状態を検査するのに便利です。

ESP32 USB デバッグ

ESP32-S3 コプロセッサには組み込みの USB-JTAG インターフェースがあります — 外部デバッグプローブは不要です。ホスト PC から picoZ80 ボード上の ESP32 USB ポートに直接 USB ケーブルを接続します。
# 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
シンボルとアドレスが正しく整合するように、ELF がデバイス上で実行されているファームウェアと同じソースリビジョンからビルドされていることを確認してください。

ビルドシステム

picoZ80 ファームウェアは、Raspberry Pi Pico SDK 2.x を備えた CMake を使用します。ビルドシステムは ブートローダーアプリケーション ファームウェアを生成します。アプリケーションは 4 つのバリアントでビルドされます — 2 つのパーティション(0x10020000 のパーティション 1、0x10520000 のパーティション 2)それぞれが標準構成と DBGSH 構成です。DBGSH バリアントはコンパイルフラグに INCLUDE_DBGSH を追加し、USB CDC チャンネル 1 上の完全なデバッグシェルを有効にします。ESP32 ファームウェアは、Docker で管理される ESP-IDF v5.4 を使用して別途ビルドされます。
動作する環境を最も手早く用意する方法は、自動化された setup_picoZ80 スクリプト(macOS/Linux および Windows)です。SDK とすべての依存関係をインストールし、すぐに使えるビルドスクリプトを作成します — README開発者ガイド を参照してください。以下の CMake ターゲット、フラグ、コマンドは、このスクリプトが自動化する基盤となるビルドを、手動で構成したい方のために説明するものです。

CMake ビルドターゲット

ターゲット 出力 Flash アドレス 備考
Bootloader Bootloader.elf, Bootloader.uf2 0x10000000  
BaseZ80_0x10020000 BaseZ80_0x10020000.elf, .bin 0x10020000(スロット 1) 標準(デバッグシェルなし)
BaseZ80_0x10520000 BaseZ80_0x10520000.elf, .bin 0x10520000(スロット 2) 標準(デバッグシェルなし)
BaseZ80_DBGSH_0x10020000 BaseZ80_DBGSH_0x10020000.elf, .bin 0x10020000(スロット 1) DBGSH — ICE デバッグシェルを含む
BaseZ80_DBGSH_0x10520000 BaseZ80_DBGSH_0x10520000.elf, .bin 0x10520000(スロット 2) DBGSH — ICE デバッグシェルを含む
SharpZ80AmstradZ80TatungZ80OpenZ80 の各モデルも同じスロット / DBGSH パターンに従います。例: OpenZ80_0x10020000OpenZ80_0x10020000_DBGSHOpenZ80_0x10520000OpenZ80_0x10520000_DBGSH(それぞれ .elf.bin.hex.map を出力します。アプリケーションスロットは UF2 ではなくプレーンな .bin を使用します)。単一のモデルは build_tzpuPico.sh のフィルターでビルドします。例: build_tzpuPico.sh open

主要な CMake ビルドフラグ

フラグ 効果
INCLUDE_SHARP_DRIVERS すべての 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 Amstrad PCW ペリフェラルドライバー(PCW9512、uPD765)をコンパイルに含めます。
INCLUDE_TATUNG_DRIVERS Tatung Einstein ペリフェラルドライバー(EinsteinTC01、EinsteinFDC、WD1770)をコンパイルに含めます。
TARGET_MODEL_TATUNG Tatung Einstein を排他的なターゲットモデルとして設定します(TatungZ80 ビルドで使用)。
INCLUDE_OPEN_DRIVERS OpenZ80 実験者向けペルソナ(Open.c)とマシン非依存のインターフェースカード(MZ-1R12/1R18/1R23/1R37、PIO-3034、MZ8BIO3、MZ1E24、Z80SIO、MZ-1E05、WD1773、Celestite)をコンパイルに含めます。
TARGET_MODEL_OPEN OpenZ80 実験者向けペルソナを排他的なターゲットモデルとして設定します(OpenZ80 ビルドで使用。INCLUDE_OPEN_DRIVERS を有効化します)。
INCLUDE_DBGSH USB CDC チャンネル 1 上の ICE デバッグシェルをコンパイルに含めます。DBGSH ビルドバリアントにのみ存在します。
CMAKE_BUILD_TYPE=Debug デバッグシンボルを有効化し、最適化を無効化します。ソースレベルの GDB デバッグに必要です。
CMAKE_BUILD_TYPE=Release 完全な最適化(-O3)。本番ファームウェアに使用します。

ビルドコマンド

# 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
build_tzpuPico.sh スクリプトは、ビルドが成功するとバージョン番号を自動的にインクリメントし、バージョン付きの出力ファイルを fw/uf2/(ブートローダー UF2)と fw/bin/(OTA 用アプリケーションバイナリ)にコピーします。ブートローダー UF2 は、初回の USB マスストレージフラッシュにのみ使用されます。アプリケーションスロットのバイナリは、非標準の Flash アドレスに常駐するため、UF2 ではなくプレーンなバイナリ形式を使用します。

ESP32 ネットワーキングモードの選択

ネットワーキングモードを切り替えるには、ビルド前に適切な事前構築済み設定ファイルを sdkconfig にコピーします。
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

参考サイト

リソース リンク
picoZ80 プロジェクトページ /ja/picoz80/
picoZ80 ユーザーマニュアル /ja/picoz80-usermanual/
pico6502 プロジェクトページ /ja/pico6502/
RP2350 データシート datasheets.raspberrypi.com
RP2350 PIO リファレンス datasheets.raspberrypi.com — Appendix B
Pico SDK ドキュメント raspberrypi.github.io/pico-sdk-doxygen
ESP32-S3 テクニカルリファレンス docs.espressif.com
ESP-IDF プログラミングガイド docs.espressif.com/esp-idf
Zilog Z80 CPU ユーザーマニュアル zilog.com
OpenOCD ドキュメント openocd.org
X(Twitter)プロジェクトプレビュー engineerswork1

無線規制に関する注意

本デバイスには、2.4 GHz ISM バンドで送信可能な ESP32-S3-PICO-1 無線モジュールが組み込まれており、WiFi ファームウェアがインストールされている場合、世界中の無線周波数規制(米国の FCC Part 15 Subpart C、および欧州連合の Radio Equipment Directive 2014/53/EU を含む)の下で意図的放射器(intentional radiator)となります。
出荷時構成
出荷時、picoZ80 ボードには NCM のみ のファームウェア(sdkconfig.mode_ncm_only)がフラッシュされています。この構成では WiFi 無線は完全に無効化され、ESP32-S3 のアンテナ整合ネットワーク部品は PCB 上に実装されていません。RF 送信が一切行われないため、本デバイスは 意図的放射器ではなく、FCC、CE/RED、またはそれに相当する認証を必要としません。規制当局の認可なしに販売、配布、または贈与することができます。
WiFi の追加
エンドユーザーは、個人的、実験的、または教育的な使用のために、アンテナ整合ネットワークを実装し、WiFi 対応ファームウェア(sdkconfig.mode_wifi_only または sdkconfig.mode_wifi_and_ncm)をフラッシュすることができます。WiFi を有効にすると、本デバイスは意図的放射器となり、以下の規則が適用されます。
ESP32-S3-PICO-1 モジュール自体は既存の規制認証(FCC、CE など)を保有していますが、それらのモジュールレベルの認証は、モジュールを組み込んだ完成品に 自動的には拡張されません。事前認証済みモジュールの免除は、個人の趣味者個人的、実験的、または教育的な使用 のために限られた数のデバイスを製作することを、別途の機器認可を取得することなく許可するものです。
重要な制限事項
  • WiFi ファームウェアを搭載したデバイスは、完成品が独立してテストされ、該当する管轄区域でそれ自身の機器認可(例:FCC ID、Notified Body の評価を伴う CE マーキング)を付与されていない限り、第三者に 販売、販売のための提供、贈与、その他の方法で配布してはなりません
  • WiFi を有効にして本プロジェクトを個人使用のために限られた数量で製作することは、デバイスが有害な干渉を引き起こさないことを条件として、趣味者および実験的使用の規定(例:FCC § 15.23)の下で一般的に許可されています。
  • WiFi を有効にした商業的販売には、完全な製品レベルの FCC/RED(または同等)認証が必要です。
  • 規制要件は国によって異なります。米国外の製作者は、適用される規則について自国の無線周波数当局に相談してください。
製作者の責任
これらの設計から製作されたデバイスが、その管轄区域で適用されるすべての無線周波数規制に準拠していることを保証するのは、製作者の単独の責任です。著者はこれらの設計を個人的、教育的、趣味的な使用のために提供しており、それらから製作されたデバイスが商業的配布のための規制要件を満たすことを一切表明しません。