ROMファイリングシステム (RFS) — デベロッパーズガイド
RFS デベロッパーズガイド
このガイドは RFS のソースコードと開発環境の詳細なウォークスルーです。アセンブリ言語に馴染みのない開発者のために Z80 アセンブリ言語の概念を解説し、すべてのソースモジュールを詳述し、バンク切り替えアーキテクチャを文書化し、新しいコマンドの追加、既存モジュールの変更、および RFS の新しいハードウェアへの移植方法を示します。
ハードウェアアーキテクチャとビルドシステムの詳細については テクニカルガイド を参照してください。ユーザー向けの操作については ユーザーマニュアル を参照してください。
アセンブリ言語初心者のための Z80 アセンブリ入門
RFS ファームウェア全体は Z80 アセンブリ言語で書かれています — Sharp MZ シリーズで使用される Zilog Z80 プロセッサーのネイティブ命令言語です。高水準言語とは異なり、アセンブリは物理的なハードウェアにほぼ直接対応しています:すべての命令が CPU が直接実行する 1 つまたは数バイトに変換されます。
レジスター
Z80 には「変数」がありません — 代わりに小さな レジスターのセット(CPU 内部の高速ストレージ)があります。RFS で最もよく使用されるもの:
| レジスター | サイズ | 役割 |
|---|---|---|
| A | 8 ビット | アキュムレーター — 算術、論理、I/O 操作の主要レジスター。ほぼすべての命令が A を使用します。 |
| B, C | 8 ビット | 汎用。BC を合わせると 16 ビットペアになり、ループカウンターやバイトカウントによく使われます。 |
| D, E | 8 ビット | 汎用。DE を合わせると 16 ビットペアになり、ソースまたはデスティネーションポインターによく使われます。 |
| H, L | 8 ビット | 汎用。HL を合わせると主要な 16 ビットメモリポインターになります — ほとんどのメモリ読み書き命令が HL を使用します。 |
| IX, IY | 16 ビット | インデックスレジスター — ベース+オフセットメモリアドレッシングに使用。HL より低速ですが構造化データに便利。 |
| SP | 16 ビット | スタックポインター — コールスタックのトップを指します。PUSH と POP が SP を自動的に使用します。 |
| PC | 16 ビット | プログラムカウンター — 現在の命令のアドレス。自動的にインクリメントされ、ジャンプやコールで変更されます。 |
| F | 8 ビット | フラグレジスター — 算術操作によって設定される個別のビット:Z(ゼロ)、C(キャリー)、S(符号)、P/V(パリティ/オーバーフロー)。 |
LD dest, src— データをロード(コピー)します。LD A, Bは B を A にコピーします。LD A, (HL)は HL に保持されているメモリアドレスのバイトを A に読み込みます。LD (0x1200), Aは A をメモリアドレス 0x1200 に書き込みます。CALL addr— サブルーチンを呼び出します。戻りアドレス(次の命令)をスタックにプッシュし、addrにジャンプします。関数呼び出しに相当します。RET— サブルーチンから戻ります。スタックから戻りアドレスをポップし、そこにジャンプします。JP addr—addrへの無条件ジャンプ。JP Z, addrはゼロフラグが設定されている場合(つまり直前の操作がゼロを生成した場合)にのみジャンプします。JR offset— 短い相対ジャンプ(−128〜+127 バイト)。近くの分岐に対して JP より高速でコンパクトです。DJNZ offset— B をデクリメントしてゼロでなければジャンプ。Z80 の標準的なループ命令:LD B, 10 / LOOP: ... / DJNZ LOOPで 10 回繰り返します。ADD A, n— n を A に加算します。SUB nで減算。AND n、OR n、XOR n— A のビット演算。IN A, (port)— I/O ポートから A に読み込みます。OUT (port), A— A を I/O ポートに書き込みます。Z80 がハードウェア(WD1773、SPI コントローラー、バンクラッチなど)と通信する方法です。PUSH rr / POP rr— 16 ビットレジスターペアをスタックに保存/復元します。EI / DI— 割り込みを有効化/無効化します。割り込まれてはいけないコード(例:時間クリティカルなテープ操作)は DI と EI の間に挟みます。
Z80 はデータの出所や行き先を指定するいくつかの方法を提供します:
GLASS アセンブラー構文
- 即値:
LD A, 42— 値は命令バイト自体に埋め込まれています。 - レジスター:
LD A, B— データはレジスターから来るかレジスターに行きます。 - 間接(HL 経由):
LD A, (HL)— HL はメモリアドレスを保持し、そのアドレスからデータが読み取られます。 - 拡張(直接アドレス):
LD A, (0x1200)— アドレスは命令内のリテラル 16 ビット定数です。 - インデックス:
LD A, (IX+5)— IX がベースアドレスを保持し、5 が加算されて実効アドレスになります。RFS では固定フォーマットのデータ構造のフィールドにアクセスするために使用されます。
RFS は GLASS Z80 アセンブラーを使用しています。主な構文機能:
- コメントは
;で始まります — セミコロンの右にあるものはすべて無視されます。 - ラベルは
:が後に続く識別子です。行の先頭にあるラベルは次の命令のアドレスを名付けます。 EQUは定数を定義します:BELL EQU 007H— アセンブラーは BELL のすべての出現箇所を 0x07 に置き換えます。DB(Define Byte)は生バイトを挿入します:DB 0x41, 0x42で 2 バイトを出力します。文字列とルックアップテーブルに使用されます。DW(Define Word)は 16 ビットリトルエンディアン値を挿入します:DW HANDLERは HANDLER ラベルのアドレスを出力します。ORG addrはアセンブリ原点を設定します — 以降のコードはaddrに存在するかのようにアセンブルされます。INCLUDE "file.asm"は別のファイルを現在の位置にテキストとして取り込みます。IF / ENDIF条件付きアセンブリ:IF BUILD_SFD700 = 1 ... ENDIF— 条件が真のときのみ囲まれた命令がアセンブルされます。RFS が 1 つのソースツリーから 4 種類のファームウェアバリアントをビルドする仕組みです。
ソースツリー
| パス | 内容 |
|---|---|
asm/ |
すべての Z80 アセンブリソースファイル |
asm/include/ |
共有定義と設定ファイル |
asm/dis/ |
SA-5510 と XPATCH の逆アセンブルされたリファレンスファイル |
tools/ |
ビルドスクリプト、GLASS アセンブラー、ユーティリティバイナリ |
MZF/ |
MZF フォーマットのアプリケーションファイル(マシンタイプ別に整理) |
MZB/ |
セクターパディングされたバイナリアプリケーション(ビルドで生成) |
roms/ |
ビルド出力 — ROM イメージと SD カードイメージ |
releases/ |
ビルド済みリリースバイナリ |
config/ |
CP/M ディスクフォーマット定義(diskdefs) |
cpmtools/ |
cpmtools ソース(サブモジュール) |
src/ |
サポートツールのソース |
設定:rfs_definitions.asm
これは中央設定ファイルで、他のすべてのソースファイルが
ビルドターゲットフラグ
INCLUDE "rfs_definitions.asm" で取り込みます。すべてのアセンブリ時オプションはここで制御されます。主なセクション:
HW_SPI_ENA EQU 1 ; 1 = hardware SPI on RomDisk v2+ PCB SW_SPI_ENA EQU 0 ; 1 = software bit-bang SPI (RomDisk v1) PP_SPI_ENA EQU 0 ; 1 = SPI via parallel port (RomDisk v1 alternative) FUSIONX_ENA EQU 0 ; 1 = running on tranZPUter FusionX KUMA80_ENA EQU 0 ; 1 = Kuma 40/80 upgrade present VIDEOMODULE_ENA EQU 0 ; 1 = 40/80 colour video module present BUILD_ROMDISK EQU 0 ; 1 = build for RomDisk card BUILD_SFD700 EQU 0 ; 1 = build for SFD-700 floppy interface BUILD_PICOZ80 EQU 1 ; 1 = build for picoZ80 board ENADEBUG EQU 0 ; 1 = enable debug output during assembly
一度に必ず 1 つの
アドレス定数
BUILD_* フラグを 1 に設定します。ソース全体の条件付きアセンブリブロックはこれらのフラグをテストして、プラットフォーム固有のコードを含めるか除外するかを決定します。
UROMADDR EQU 0E800H ; Base address of the User ROM window UROMBSTBL EQU UROMADDR + 020H ; Bank-switch table entry point (fixed offset) RFSJMPTABLE EQU UROMADDR + 0B0H ; RFS jump table start FDCROMADDR EQU 0F000H ; FDC ROM address (SFD-700 MROM location) ; SFD-700 specific bank defaults (only assembled when BUILD_SFD700 = 1): BNKDEFMROM_MZ80A EQU 0 ; Default MROM bank for MZ-80A (AFI ROM) BNKDEFMROM_MZ700 EQU 1 ; Default MROM bank for MZ-700 (AFI ROM) BNKDEFUROM EQU 2 ; Default UROM bank for RFS (starts at 8KB in Flash)
これらの定数は各 ROM ウィンドウが Z80 アドレス空間のどこに配置されるかを定義します。ユーザー ROM ウィンドウ用にコンパイルされたコードは常に
文字および制御定義
ORG 0xE800 でアセンブルされます;モニター ROM ウィンドウ用のコードは ORG 0x0000 でアセンブルされます。
標準 ASCII 制御文字はソースを自己文書化するために名前付き定数として定義されています:
BELL EQU 007H ; Terminal bell CR EQU 00DH ; Carriage return LF EQU 00AH ; Line feed CS EQU 00CH ; Clear screen SPACE EQU 020H ; ASCII space DELETE EQU 07FH ; Delete key
バンク切り替えの詳細
バンク切り替えは RFS アーキテクチャの核心です。ソースファイルを変更する前にこれを理解することが不可欠です。
バンキングが必要な理由
Sharp MZ-80A はユーザー ROM にわずか 2 KB のアドレス空間(0xE800〜0xEFFF)しか与えません。2 KB には数百の命令しか入りません — ファイリングシステム、アセンブラー、ディスアセンブラー、テープコントローラー、SD カードドライバー、CP/M CBIOS には到底足りません。解決策は、512 KB Flash チップのどの 2 KB がそのアドレス範囲に見えるかを物理的に切り替えることです。Flash チップに 12 個の異なる 2 KB プログラム(バンク)を格納してオンデマンドで切り替えることで、RFS は実質的に 24 KB の ROM コードを実現します。
バンク切り替えスタブ
すべてのバンクはバンクの最初の 32 バイト(0xE800〜0xE81F)を占有するバンク切り替えスタブの同一コピーで始まります。このスタブは以下を提供します:
コマンドテーブルフォーマット(rfs.asm)
- 標準コールゲートウェイ: 任意のバンクがターゲットバンク番号とターゲットアドレスでスタブを呼び出すことで、他のバンクの任意のルーチンを呼び出せます。スタブはバンク番号をハードウェアラッチ(通常は I/O ポート書き込み)に書き込み、要求されたアドレスを呼び出します。呼び出し先は新しいバンクで実行され、戻ったときにスタブが元のバンクに切り替え戻します。
- 一貫したエントリポイント: スタブが固定オフセット(バンク切り替えテーブルの 0xE800 + 0x20)にあるため、バンク 0 のコードはバンク 3 の内部アドレスを見たことがなくてもバンク 3 のスタブを確実に見つけられます。
rfs.asm のモニターコマンドディスパッチャーはコンパクトなコマンドテーブルを使用します。各エントリは 1 つのコマンドを説明し、以下のようにレイアウトされています:
; One command table entry:
;
; DB FLAGS ; 1 byte: END|MATCH|BANK[5:3]|SIZE[2:0]
; DB "COMMAND" ; SIZE bytes: the command string (no null terminator)
; DW HANDLER_ADDR ; 2 bytes: address of the handler routine in the named bank
;
; Flags byte:
; Bit 7 = 1: End of table marker (last entry).
; Bit 6 = 1: Exact match required (command must be entire input, no trailing chars).
; Bits 5:3 Bank number where HANDLER_ADDR lives (0–7 for RFS, 8–11 for CBIOS).
; Bits 2:0 Length of the command string in bytes.
;
; Example — the ASM command (SFD700 build only, lives in bank 7, 3-char string):
CMDTABLE2:
DB 000H | 000H | 038H | 003H ; FLAGS: not-end, not-exact, bank 7, length 3
DB "ASM" ; Command string
DW ASM_MAIN ; Handler address in bank 7
ディスパッチャーはモニターの入力行を読み取り、テーブルを走査し、各エントリに対して:
- 入力をコマンド文字列と照合します(一部のビルドでは大文字小文字を区別しない)。
- 一致した場合、テーブルエントリからバンク番号とハンドラーアドレスを取り出します。
- ターゲットバンクへのバンク切り替えを実行します。
- モニター入力バッファーに残りの入力(パラメーター)を利用可能な状態でハンドラーを呼び出します。
CMDTABLE2 と RomDisk/picoZ80 ビルド用の CMDTABLE。どちらも同じ構造ですが異なるコマンドセットを含みます。
モジュールウォークスルー
rfs.asm — コマンドディスパッチャー(ユーザー ROM バンク 0)
役割: すべての RFS 機能のエントリポイント。SA-1510 モニターがコマンドを認識できない場合、0xE800 のユーザー ROM エントリポイントに制御を渡します。これは常にバンク 0 です。
主なセクション:
rfs_bank1.asm — フロッピーディスクコントローラー(ユーザー ROM バンク 1)
- バンク切り替えスタブ(0xE800〜0xE81F): 上述のクロスバンクコールゲートウェイ。すべてのバンクに同一コピーがあります。
- バンク切り替えテーブル(0xE800 + 0xB0): バンク番号を物理的な Flash ROM アドレスにマッピングするジャンプテーブル。ハードウェアが非連続バンクアドレッシングを必要とする場合はブート時に変更されます。
- コマンドテーブル(CMDTABLE / CMDTABLE2): すべての RFS コマンドのリスト、それぞれのバンクとハンドラーアドレスを含みます。
- メインディスパッチャーループ: モニターの入力バッファーを読み取り、コマンドテーブルを走査し、バンク切り替えを実行してハンドラーを呼び出します。コマンドが一致しない場合は SA-1510 モニターに戻り、"?" エラーを表示させます。
- RFS 初期化: リセット後の最初のエントリ時に、RFS はハードウェアプラットフォームを検出し(SFD-700 では MODE レジスターから、RomDisk ではフラグから)、SPI と SD カードを初期化し、初期ドライブを 0 に設定します。
BUILD_SFD700 = 1 の場合、rfs.asm は ORG 0xE800 ではなく ORG 0xE000 / ALIGN 0xE300 でアセンブルされます。SFD-700 はユーザー ROM ウィンドウを 0xE300〜0xEFFF にマッピングするためです(0xE000〜0xE2FF は MZ-700 メモリマップド I/O 用に予約されています)。
役割: フロッピーディスクコマンドを実装します — フロッピーブート(F / FL)、フロッピーディレクトリ(FD)、および直接 AFI ジャンプ(f)。FDC コマンドセットはすべてのビルドでアセンブルされます。
主な機能:
rfs_bank2.asm — SD カードコントローラー(ユーザー ROM バンク 2)
- FLOPPY (FL): ドライブ番号を求めるプロンプト(コマンドラインで指定されていない場合)を表示し、ディスクを初期化してブートセクタを読み取り、ディスクシグネチャーを確認し、プログラム情報(名前、ロードアドレス、サイズ、実行アドレス)を取得してプログラムをメモリにロードし実行します。
- FDDIR (FD): フロッピーディスクのディレクトリ一覧を表示します。オプションのドライブ番号(1〜4、デフォルト:1)を受け付けます。ブートセクタを読み取り、MZ-700 ディスクフォーマットを確認した後、ディレクトリセクターをスキャンしてファイル名、ロードアドレス、実行アドレス、ファイルサイズを表示します。
- FDCK: 0xF000 のバイトを読み取って AFI ROM が存在してゼロでないことを確認し、0xF000 を直接呼び出します。これはすべてのビルドで f(小文字)コマンドです。
- バンクの末尾にある
ALIGN 0xF000ディレクティブは、SFD-700 ROM イメージが Flash レイアウトの正確に 0xF000 に AFI ブート ROM を配置することを保証します。
役割: 完全な SD カードサブシステム — SPI 初期化、SD カードコマンドプロトコル、SDCFS ディレクトリおよびファイル I/O ルーチン。SFD-700 ビルドでは SD カードコントローラーコードはアセンブルされません(SFD-700 ハードウェアには SD カードインターフェースがありません);バンクスロットは ROM イメージに存在しますがバンク切り替えスタブのみを含みます。
主な機能:
rfs_bank3.asm — メモリユーティリティ(ユーザー ROM バンク 3)
- SPI ドライバー(ハードウェアまたはソフトウェア): 条件付きアセンブリによりハードウェア SPI(RomDisk v2 SPI コントローラーレジスターを使用)とソフトウェアビットバン SPI(個別の I/O ポートビットを切り替えて SPI バスをクロック)を選択します。ハードウェア SPI パスは大幅に高速で、現在のすべてのボードで使用されています(
HW_SPI_ENA = 1)。 - SD カード初期化(SDINIT): SD カード初期化シーケンスを実装します — CMD0(GO_IDLE)、CMD8(SEND_IF_COND)、ACMD41(SD_SEND_OP_COND)を送信してカードを SPI モードからアクティブ状態に切り替えます。OCR レスポンスを確認することで SD と SDHC/SDXC 両方のカードタイプを処理します。
- セクター読み取り(SDREAD): 32 ビットセクターアドレスで CMD17(READ_SINGLE_BLOCK)を送信し、データ開始トークン(0xFE)を待ち、512 バイトを Z80 RAM バッファーに読み込みます。利用可能な場合はハードウェア SPI バーストモードを使用します。
- セクター書き込み(SDWRITE): CMD24(WRITE_BLOCK)、データ開始トークン、512 バイトのデータ、CRC を送信します。書き込みレスポンスとビジー信号がクリアされるのを待ちます。
- SDCFS ディレクトリ読み取り(SDDIR): アクティブドライブイメージの最初の 8 KB からディレクトリを読み取り、IC、LC、SC、EC コマンドで使用される RAM 常駐ディレクトリキャッシュを構築します。
- SDCFS ファイルロード(SDLOAD): ディレクトリからファイル番号が与えられると、ファイルの 64 KB ブロックのセクターアドレスを計算し、実際のファイルサイズバイトを読み取り、ディレクトリエントリの LOAD ADDR フィールドで指定された Z80 アドレスにロードします。
- SDCFS ファイル保存(SDSAVE): 新しいディレクトリスロットを割り当て(または同じ名前の既存エントリを上書きするために見つけ)、START SECTOR、SIZE、LOAD ADDR、EXEC ADDR フィールドを設定し、適切な 64 KB ブロックにファイルデータを書き込みます。
役割: すべてのビルドで使用可能な D(16 進ダンプ)、M(メモリ編集)、CP(メモリコピー)コマンドを実装します。T2SD(テープから SD)と SD2T(SD からテープ)コマンドもここに実装されていますが、RomDisk / picoZ80 ビルドのみにアセンブルされます — SFD-700 ビルドでは SD カードがないため除外されます。
16 進ダンプ(D): ターゲットアドレス範囲から最大 20 行、各 16 バイトを読み取ります。各行に 4 桁の 16 進アドレス、16 個の 16 進バイト値(4 バイトごとにスペース)、16 個の ASCII 文字(印刷不可バイトにはドット)を表示します。Sharp MZ は通常とは異なる文字エンコーディングを使用します — バンク 6 がここで使用される Sharp から ASCII への変換テーブルを提供します。
メモリエディター(M): 各バイトを順番に表示し、アドレスと現在値を表示します。ユーザーは新しい 16 進値(1 桁または 2 桁)を入力して Enter を押して書き込むか、Enter のみで変更なしにスキップできます。Ctrl+C または特定のエスケープシーケンスで終了します。
T2SD と SD2T: これらのコマンドはテープ(CMT)と SD カードの間の透過的な双方向コピーを実行します。T2SD はバンク 4 の CMT ロードルーチンを呼び出してテープファイルを RAM に読み込み、次にバンク 2 の SD 保存ルーチンを呼び出してアクティブドライブに書き込みます。SD2T はバンク 2 の SD ロードルーチンを呼び出してファイルを RAM に配置し、次にバンク 4 の CMT 保存ルーチンを呼び出してテープに書き込みます。どちらの方向も SDCFS ディレクトリを使用してファイル名、サイズ、アドレスを維持します。
rfs_bank4.asm — CMT コントローラー(ユーザー ROM バンク 4)
役割: L/LT、LTNX、S/ST、V テープ(CMT)コマンドを実装します。
Sharp MZ-80A は 1200 ボードの Kansas City Standard カセットインターフェースを使用します。バイトは 1200 Hz(ビット 0)または 2400 Hz(ビット 1)のトーンのバーストとしてエンコードされます。テープルーチンは時間クリティカルです — 各ビットを厳密なタイミングウィンドウ内に読み書きする必要があります。入力トーン周波数を測定し出力波形を生成するために 8253 タイマーチップ(またはタイマーのないプラットフォームでは CPU サイクルカウントループ)を使用します。タイミングの乱れを防ぐため、テープ操作全体を通じて割り込みは無効(
rfs_bank5.asm — ユーティリティ関数(ユーザー ROM バンク 5)
DI)です。
MZF テープフォーマットはすべてのプログラムにファイルタイプ、ファイル名、データ長、ロードアドレス、実行アドレスを含む 128 バイトのヘッダーを前置します — SDCFS ディレクトリエントリに格納されているフィールドと同じです。SD↔テープコピーが透過的な理由はこれです:ヘッダーフォーマットが同一だからです。
役割: 他のバンクから呼び出される共有ルーチンのライブラリ。バンク切り替えは高コスト(v2+ ボードではアンロックシーケンスが必要)なため、頻繁に使用されるルーチンはここに集中させて切り替えのオーバーヘッドを最小化します。
主なルーチン:
rfs_bank6.asm — ヘルプと文字列(ユーザー ROM バンク 6)
- PRTHEX: A レジスターを 2 桁の 16 進数として画面に表示します。
- PRTHL: HL レジスターを 4 桁の 16 進数として表示します。
- PRTSTR: (HL) からの null 終端文字列を画面に表示し、Sharp 文字エンコーディング変換を処理します。
- INPHEX: キーボードから 16 進数(最大 4 桁)を読み取り、HL に値を返します。
- STRCMP: 2 つの null 終端文字列を比較します。
- SUBSTR: サブ文字列を抽出します。コマンドディスパッチャーがコマンド名をパラメーターから分離するために使用します。
- WAITKEY: キー押下を待ち、キーコードを A に返します。IC と IR ディレクトリ一覧の「任意のキーを押して続行」ポーズに使用されます。
役割: ページ形式のヘルプ画面テキスト、すべてのエラーメッセージ文字列、Sharp MZ 文字セットから ASCII への変換テーブルを格納します。
Sharp MZ シリーズは独自の 8 ビット文字エンコーディングを使用しており、多くの印刷可能文字が ASCII とは異なるコードポイントを持ちます。このバンクの変換テーブルは Sharp 文字コードを ASCII の同等物にマッピングし、ファイル名(Sharp エンコーディングで格納)を画面上に ASCII テキストとして表示する際に使用されます。
ヘルプ画面は null 終端文字列のシーケンス(1 行に 1 つ)として格納されています。H コマンドは自動的なページネーションでそれらを表示し、各画面分の境界でバンク 5 の WAITKEY ルーチンを呼び出します。
rfs_bank7.asm — アセンブラー / ディスアセンブラー(ユーザー ROM バンク 7)
役割: ASM と DASM コマンド(SFD-700 ビルド)、R DRAM テスト、T タイマーテストを実装します。
インタラクティブアセンブラー(ASM): ターゲットアドレスで行入力プロンプトを表示します。ユーザーが Z80 ニーモニック(例:
rfs_mrom.asm — モニター ROM ユーティリティ(モニター ROM バンク 3)
LD A, 42)を入力すると、解析され、マシンコードバイトにアセンブルされ、ターゲットアドレスの RAM に直接書き込まれます。アドレスはアセンブルされた各命令のサイズだけ進みます。これにより外部 PC なしで直接ハードウェア上でスモールルーチンをアセンブルできます。
ディスアセンブラー(DASM): ターゲットアドレスからマシンコードバイトを読み取り、各命令をデコードし(Z80 オペコードニーモニックのルックアップテーブルを使用)、各命令のアドレス、16 進バイト、ニーモニックを表示します。すべての Z80 プレフィックスバイト(CB、DD、ED、FD)と拡張命令を処理します。2 KB の ROM に収まる完全な Z80 ディスアセンブラー — コンパクトなコードの驚くべき偉業です。
DRAM テスト(R): 完全なユーザー RAM 空間(0x1200〜0xCFFF)全体でウォーキングビットパターンの書き込み/確認を実行します。失敗したアドレスを報告します。ビンテージマシンでよくある故障モードである欠陥 RAM チップの診断に役立ちます。
役割: ユーザー ROM 空間ではなくモニター ROM 空間から実行しなければならない ROM スキャンおよび MZF ファイルロードルーチンを提供します。
なぜ別のモニター ROM バンクが必要か? IR と LR コマンドはユーザー ROM Flash チップ(バンク 11 以降のユーザー ROM バンクはパックされた MZF プログラムを保持)に格納された MZF ファイルを列挙する必要があります。ユーザー ROM バンクをスキャンするには、CPLD がユーザー ROM ウィンドウをそのバンクに向けて切り替える必要があります。しかしスキャンコード自体がユーザー ROM に存在します — ユーザー ROM バンクを切り替えると即座に自分自身を別のバンクに置き換えてクラッシュします。
解決策はスキャンループをモニター ROM バンク 3 に配置することです。モニター ROM バンク切り替えはユーザー ROM バンク切り替えから独立しています。MROM スキャンルーチンは自分自身の実行コンテキストを乱すことなく、自由にユーザー ROM バンクを切り替え(各バンクの MZF ヘッダーリストを列挙するため)ることができます。
主な機能:
CP/M CBIOS モジュール
- ROMDIR: バンク 11 以上のすべてのユーザー ROM バンクをスキャンし、各 MZF ヘッダーを読み取り、IR コマンドで使用される RAM 常駐 ROM ディレクトリを構築します。
- ROMLOAD: ROM ディレクトリからファイル番号が与えられると、適切なユーザー ROM バンクから MZF データをヘッダーで指定された LOAD ADDR に読み込み、オプションで EXEC ADDR にジャンプします。
4 つの CBIOS ユーザー ROM バンク(8〜11)と CBIOS モニター ROM バンク(2)が合わさって完全な CP/M 2.2 CBIOS を実装します。各バンクが 1 つのサブシステムを提供します:
cbios.asm(モニター ROM バンク 2): CBIOS エントリポイントテーブル — すべての 17 の CP/M API ベクター(BOOT から SECTRN まで)がこのモジュール内のジャンプアドレスです。また、ディスクパラメーターテーブル(CP/M に各ディスクドライブのジオメトリを伝える DPH、DPB 構造体)、コールド/ウォームブートシーケンス、ROM ディスクコントローラー(ユーザー ROM Flash からセクターを読み取る)も含みます。
cbios_bank1.asm(ユーザー ROM バンク 8): オーディオ出力(MZ-80A の 8255 PPI とブザーを使用したベルトーンとメロディー)、8253 タイマーを使用したリアルタイムクロックルーチン、オートリピート付きキーボード入力(500 ms 以上キーを押し続けると約 10 Hz で繰り返し、現代のキーボードでユーザーが期待する動作に一致)。
cbios_bank2.asm(ユーザー ROM バンク 9): スクリーンドライバー — 現在のカーソル位置での文字出力、スクロール、画面クリア、カーソル位置指定。また ANSI ターミナルエミュレーター:VT52/VT100 エスケープシーケンス(CSI コード)を認識し、同等の MZ-80A スクリーン操作に変換するステートマシン。これにより、スマートターミナルを前提とする CP/M アプリケーション(WordStar、Turbo Pascal など)がそれらのアプリケーションを変更することなく正しく動作します。
cbios_bank3.asm(ユーザー ROM バンク 10): CP/M 用 SD カードディスクドライバー。CP/M 128 バイトセクター読み書き要求を、SD カードの 256 MB 境界以降に格納された CP/M ディスクイメージ上の SDCFS 操作に変換します。SECTRN によるディスクアクセスパフォーマンス向上に使用されるセクタースキューテーブルを含みます。
cbios_bank4.asm(ユーザー ROM バンク 11): CP/M 用フロッピーディスクコントローラー。物理フロッピードライブの CP/M ディスク読み書き要求を処理するために WD1773(SFD-700 経由)または同等のフロッピーコントローラーを使用します。データは反転なし(MZ-80A AFI ROM が反転データを使用するのとは異なり)で読み取られます。CBIOS が独自の再反転フォーマットを書き込むためです。
新しいモニターコマンドの追加
ユーザー ROM バンク 3(メモリユーティリティ)に存在する新しいコマンド FOO を追加するには:
- rfs_bank3.asm にハンドラーを記述します: コマンドを実装するラベル付きルーチン
FOO_CMD:を追加します。パラメーターはモニター入力バッファーで利用可能です(HL はコマンド名の後の最初の文字を指します)。完了したらRETで返します。 - rfs.asm のコマンドテーブルにエントリを追加します: 適切な
CMDTABLE(SFD-700 の場合はCMDTABLE2)に追加します:
; FLAGS: not-end (bit7=0), not-exact (bit6=0), bank 3 (bits5:3 = 011 = 0x18), length 3 (bits2:0 = 011)
DB 000H | 000H | 018H | 003H
DB "FOO"
DW FOO_CMD
- rfs_bank6.asm のヘルプテキストを更新します: ヘルプ画面テキスト文字列に新しいコマンドを説明する行を追加します。
- リビルドします:
./build.shを実行します。バンク 3 が 2 KB を超えた場合、アセンブラーはサイズオーバーフローを報告します — その場合、そのバンクの他のコードを削除または圧縮してください。
新しいハードウェアプラットフォームの追加
RFS を新しいハードウェアターゲットに移植するには:
rfs_definitions.asmに ビルドフラグを追加します:BUILD_NEWBOARD EQU 0(新しいターゲット用にビルドするときは 1 に設定)。- 新しいハードウェアが ROM ウィンドウを異なるアドレスにマッピングする場合は アドレス定数を追加します。
- ハードウェア固有の動作が異なる箇所(バンクラッチの I/O ポートアドレス、SPI コントローラーレジスターレイアウト、SD カード存在検出など)全体にわたって 条件付きアセンブリブロックを追加します。既存の
IF BUILD_ROMDISK = 1 ... ENDIFブロックのパターンに従ってください。 - 新しいターゲットの Flash チップレイアウト用に ROM イメージをパッケージ化するための 新しいビルドスクリプトを追加(または
make_roms.shを拡張)します。 - 新しいターゲットに追加ツールが必要な場合は
build.shの 前提条件チェックを更新します。
デバッグのヒント
デバッグ出力を有効にします: ビルド前に
rfs_definitions.asm で ENADEBUG EQU 1 を設定します。これにより SD カード初期化と SDCFS ルーチンの戦略的なポイントで追加の診断出力が含まれます。
内蔵ディスアセンブラーを使用します: SFD-700 ビルドでは、DASM addr と入力して RAM 内のアセンブルされたコードをディスアセンブルします。ASM コマンドで新しくアセンブルされたルーチンが正しくエンコードされたことを確認するのに役立ちます。
16 進ダンプを使用します: D E800 は現在のユーザー ROM バンクの最初の 320 バイトを表示します — バンクラッチが期待されるバンクを選択していることを確認するのに役立ちます。
RAM 変更後は DRAM テストを実行します: 新しい RAM 常駐データ構造を追加するたびに R と入力して包括的な DRAM テストを実行します。これにより、不思議なクラッシュとして現れる前にアドレッシングバグを早期に検出できます。
バンク切り替えガード: RomDisk v2+ ボードでは、0xEFF8〜0xEFFF にまたがる DJNZ やその他のループ命令を絶対に配置しないでください。コード化ラッチはその範囲からの読み取りをアンロックシーケンスとして解釈し、予期せずバンクを切り替える可能性があります。
ビルド
# Build all Z80 assembly ROMs and MZF files (includes RFS) ./build.sh --asm # Build output appears in software/roms/ ls software/roms/*.rom software/roms/*.mzf
ビルドスクリプトは GLASS アセンブラーを使用して各 ROM および MZF ターゲットをアセンブルし、適切なインクルードパスとビルドフラグを渡します。MS BASIC バリアント(MZ-80A、MZ-700、TZ40、TZ80)は、生成されたインクルードファイルを介して異なる
BUILD_VERSION フラグを設定し、同じソースファイルからビルドされます。
ビルド出力:
| 出力 | 説明 |
|---|---|
monitor_sa1510.rom |
SA-1510 モニター ROM |
monitor_80c_sa1510.rom |
80 カラム対応 SA-1510 モニター |
monitor_1z-013a.rom |
1Z-013A モニター ROM |
mz80afi.rom |
MZ-80A AFI(フロッピーインターフェース)ROM |
mz2000_ipl_*.rom |
MZ-2000 IPL ROM(オリジナル、TZPU、FusionX) |
mz800_*.rom |
MZ-800 システム ROM |
msbasic_*.mzf |
各ターゲット用 MS BASIC |
sa-5510_tzfs.mzf |
TZFS 対応 SA-5510 BASIC |
sharpmz-test.mzf |
ハードウェアテスト MZF |
CI/CD とは? 継続的インテグレーション(CI)とは、コード変更をプッシュするたびに専用サーバーがプロジェクトを自動的にビルドするプラクティスです。開発マシンで手動でアセンブラーを実行し、ROM ファイルをパッケージ化し、ダウンロードページにアップロードする代わりに、CI サーバーがこれらすべてを自動的に行います。ビルドが失敗した場合(たとえば構文エラーやインクルードファイルの欠落など)、すぐにメール通知を受け取ります。これにより問題を早期に発見でき、公開されるすべてのリリースがクリーンで再現可能な出発点からビルドされたことを保証します。
RFS ROM は FusionX Jenkins CI Pipeline の一部としてビルドされます。Jenkins は、VPS(Virtual Private Server)または任意の Linux マシン上で動作する人気のオープンソース自動化サーバーです。Gitea リポジトリの
仕組み
master ブランチへのプッシュを監視し、すべてのプロジェクトコンポーネントのフルビルドを自動的にトリガーします。
RFS の自動ビルドプロセスは以下の手順に従います:
- コードをプッシュします — Gitea リポジトリの
masterブランチへ。 - Gitea が Webhook を送信します(HTTP 通知)— Jenkins サーバーへ。
- Jenkins がリポジトリをクローンします — クリーンな新しいワークスペースに。
- Jenkins が
./build.sh --asmを実行します — GLASS アセンブラーを使用してすべての Z80 モニター ROM と MZF ファイルをアセンブルします。 - Jenkins がパッケージ化します — アセンブルされた ROM および MZF ファイルをバージョン付き tarball(
FusionX-ROMs-v1.08.tar.gz)にまとめます。 - Jenkins が Gitea Release を作成します — tarball をダウンロード可能なアセットとして添付します。
- Jenkins がメールを送信します — 成功または失敗を報告します。
プロセス全体は約 1 分で完了し、最初のプッシュ後は手動操作を必要としません。
Jenkins のセットアップ
Jenkins は Docker コンテナ内で動作し、簡単にインストールおよび移植できます。最低要件は、2 GB RAM の Linux サーバー、Docker がインストールされていること、および Gitea リポジトリへのネットワークアクセスです。サーバー上で以下を実行します:
# Install Docker (Debian/Ubuntu) sudo apt update && sudo apt install -y docker.io docker-compose sudo systemctl enable docker && sudo systemctl start docker # Create the Jenkins directory sudo mkdir -p /srv/jenkins/data cd /srv/jenkins
docker-compose.yml ファイルを作成します:
# /srv/jenkins/docker-compose.yml
version: '3.8'
services:
jenkins:
image: jenkins/jenkins:lts
ports:
- "8080:8080"
volumes:
- /srv/jenkins/data:/var/jenkins_home
environment:
- JAVA_OPTS=-Djenkins.install.runSetupWizard=false
restart: unless-stopped
# Start Jenkins docker-compose up -d # Get the initial admin password (first run only) docker-compose logs jenkins | grep "initial admin password" -A 2 # Open http://your-server:8080 in a browser
初回起動時、Jenkins はログに表示された管理者パスワードを要求します。ログイン後、「suggested plugins」をインストールし、Manage Jenkins → Plugins → Available から Generic Webhook Trigger プラグインを追加します。
Pipeline の作成
Jenkins の「Pipeline」ジョブは、Jenkins に実行させるコマンドを正確に指示する Groovy スクリプトで定義されます。RFS ビルド Pipeline を作成するには:
- Jenkins ダッシュボードで New Item をクリックします。
- 名前を入力し(例:
RFS-Build)、Pipeline を選択して OK をクリックします。 - Pipeline セクションで、Definition を「Pipeline script」に設定し、以下を貼り付けます:
pipeline {
agent any
environment {
GITEA_URL = "https://git.eaw.app"
REPO_URL = "https://git.eaw.app/eaw/tzpuFusionX.git"
GITEA_TOKEN = credentials('gitea-api-token')
GITEA_OWNER = "eaw"
GITEA_REPO = "tzpuFusionX"
}
triggers {
GenericTrigger(
genericVariables: [[key: 'ref', value: '$.ref']],
causeString: 'Triggered by Gitea push to $ref',
token: 'rfs-build-trigger',
regexpFilterText: '$ref',
regexpFilterExpression: '^refs/heads/(main|master)$'
)
}
stages {
stage('Checkout') {
steps {
cleanWs()
git url: "${REPO_URL}", branch: 'master'
sh 'git submodule update --init --recursive'
}
}
stage('Build Assembly ROMs') {
steps {
sh 'chmod +x build.sh && mkdir -p software/tmp software/roms'
sh './build.sh --asm'
}
}
stage('Package') {
steps {
script {
def ver = readFile('VERSION').trim()
sh "cd software/roms && tar czf ../../FusionX-ROMs-v${ver}.tar.gz *.rom *.mzf"
archiveArtifacts artifacts: "FusionX-ROMs-v${ver}.tar.gz"
}
}
}
}
post {
success { mail to: 'your-email@example.com', subject: "RFS Build - SUCCESS", body: "Build completed." }
failure { mail to: 'your-email@example.com', subject: "RFS Build - FAILED", body: "Check console output." }
always { cleanWs() }
}
}
これは Z80 アセンブリ ROM のみをビルドする簡略化された Pipeline です。TZFS、CP/M、カーネルモジュール、CPLD ビットストリームのビルドおよび Gitea Release の作成も含む完全な FusionX Pipeline については、FusionX デベロッパーズガイド — 継続的インテグレーションセクションを参照してください。そのガイドでは ARM クロスコンパイラーのセットアップ、Quartus Docker のインストール、兄弟コンテナのパス変換、および完全な Pipeline スクリプトについても説明しています。
Gitea Webhook
Webhook は、コードがプッシュされるたびに Gitea が Jenkins に通知するように設定します。Gitea リポジトリで Settings → Webhooks → Add Webhook → Gitea に移動し、以下を設定します:
- Target URL:
http://your-server:8080/generic-webhook-trigger/invoke?token=rfs-build-trigger - Content Type:
application/json - Trigger On: Push Events
保存後、
master にコミットをプッシュして Jenkins を確認します — 新しいビルドが自動的に表示されるはずです。ビルド番号をクリックし、Console Output をクリックすると進捗をリアルタイムで確認できます。
参考サイト
| リソース | リンク |
|---|---|
| RFS プロジェクトページ | /sharpmz-upgrades-rfs/ |
| RFS ユーザーマニュアル | /sharpmz-upgrades-rfs-usermanual/ |
| RFS テクニカルガイド | /sharpmz-upgrades-rfs-technicalguide/ |
| RFS ギャラリー | /sharpmz-upgrades-rfs-gallery/ |
| SFD-700 mkII デベロッパーズガイド | /sfd700-developersguide/ |
| picoZ80 デベロッパーズガイド | /picoz80-developersguide/ |
| FusionX デベロッパーズガイド | /tranzputer-fusionx-developersguide/ |
| GLASS Z80 アセンブラー | tools/glass-0.5.1.jar に同梱 |
| Zilog Z80 CPU ユーザーマニュアル | 標準データシート — バスタイミング、命令セット、レジスターリファレンス |
| CP/M 2.2 改変ガイド | Digital Research — CBIOS 設計リファレンス |