ZX SpectrumがHacker Newsに登場、しかしテキストモードがROMのトレードオフを露呈
ZX Spectrumは、16K ROMに埋め込まれた対立を明らかにする2026年のシステムツアーを通じ、Hacker Newsに再び登場した。1文字を表示するだけなら簡単だ。しかし信頼できるマシンコードのテキスト出力を構築するには、文書化されていない前提、変更可能なシステム変数、永続する属性、ハードウェア固有の入力経路を理解しなければならない。
Michael Martinは2026年5月30日、マシンコードツアーを公開した。この記事は先行するBASICの探究を引き継ぐが、馴染みあるコマンドをZ80アセンブリへ置き換えるだけにはとどまらない。Sinclairの便利なプログラミング環境が終わり、緩やかな構造のファームウェアが始まる地点を示している。
この違いにより、この記事はレトロコンピューティングの枠を超えた意味を持つ。Commodoreのマシンは安定したKERNALジャンプテーブルを提供し、MSXはメーカーをまたぐファームウェア呼び出しを定義した。一方Spectrumは、少数のROMエントリポイントとシステム状態への直接アクセスを組み合わせることをプログラマーに促した。この方式は抽象化の層を省いた一方で、互換性確保とデバッグの負担を開発者に移した。
これは新発見の機能でも、現代製品の発表でもない。古い設計上の取引を綿密に検証したものだ。Spectrumはわずかなメモリ内で有用なプリミティブを提供したが、それらをクリーンなマシンコード向けプラットフォームへと仕上げることはなかった。
ZX Spectrum System Tourが実際に変えたこと
今回の新たな貢献は、テキスト出力からグラフィックス、入力、そして完成した実行中の画面までを結ぶ、一貫したマシンコード経路にある。
Spectrumに突如としてテキストモードが追加されたわけではない。Martinの記事は、散在していた複数の仕組みを実用的な一連の流れとして組み立てることで、利用可能な解説を変えている。コンパクトなHello Worldルーチンから始め、文字コード、カラー制御、カスタムグラフィックス、画面クリア、キーボードスキャン、ジョイスティック入力へと進む。
最初の手順では、Z80のAレジスタに保持された文字を出力するROM再スタートエントリRST $10を使う。再スタートとは、固定された低位メモリアドレスへのコンパクトな呼び出しだ。使用前に、出力先をメイン画面領域へ向けるため、例ではIY+2にあるTVFLAGへゼロを書き込む。
この一連の手順は、基本動作をほとんど現代的に見せる。プログラムはメッセージへのポインタを読み込み、1バイトを取り出し、プリンターを呼び出して、これを繰り返す。Martinはコードをアドレス$7000に配置し、BASIC用の領域を下側に残しつつ、16Kマシンで利用可能なメモリを維持している。
この単純さは、テキストが状態を必要とするまで続く。Spectrumの画面は通常、32文字からなる24行を表示する。ファームウェアはこれらの行を、22行の上部ウィンドウと、編集・ステータスメッセージに使われる2行の下部ウィンドウに分割する。Sinclairのオリジナルの表示仕様は、この分割を確認するとともに、256×192ピクセルのディスプレイを説明している。
このマシンには、従来型端末に相当する独立した文字表示ハードウェアはない。ROMは8×8のグリフをビットマップメモリに描画し、その後、対応するセルの色情報を書き込む。つまりテキスト出力はすでに、グラフィックスのレイアウト、現在の属性、カーソル位置、選択された出力チャネルに依存している。
Martinは続いて、16種類の定義済みセミグラフィックス文字とユーザー定義グラフィックスを扱う。セミグラフィックスは文字セルをブロックに分割し、簡単な図形を通常のテキストプリンター経由で表示できるようにする。ユーザー定義グラフィックスは$90から始まる文字コードを占有し、そのビットマップデータはUDGシステムポインタを介して参照される。
記事の最後の例は、これらの機能を組み合わせ、カスタムの傘画像を含むカラフルなバナーを作成する。4つの文字定義を読み込み、インラインの制御バイトを出力し、入力を待ってから画面を復元する。Martinによれば、ローダーとテープヘッダーを含めても、マシンコードパッケージのサイズは以前のBASIC版の半分未満だという。
この比較こそが、この出来事の本当の成果だ。記事は単に孤立したアドレスを紹介するのではない。プログラマーが隠れた状態への責任を引き受けるなら、SpectrumのROMがコンパクトなアプリケーションフレームワークとして機能し得ることを実証している。
Hacker Newsの読者が今なおこのROMに関心を寄せる理由
Spectrumは、馴染み深いシステム上の問題を、ほぼ完全に理解できるほど小さなマシンへ圧縮している。
この記事がHacker Newsに届いたのは、レトロハードウェアを検証可能なソフトウェアシステムとして扱っているからだ。主要な操作にはすべて可視の経路がある。文字は固定のROMエントリを通り、グリフを読み、ビットマップメモリに触れ、1つの属性バイトを適用し、システム状態として表現されたカーソルを進める。
現代の開発者も、はるかに大きなインターフェースの背後で同じ種類の問題に直面する。ライブラリは設定を保持する。出力ストリームには状態がある。互換性は、ドキュメントが保証していない可能性のある振る舞いに依存する。通常のインターフェースが制約となる場合、ハードウェア抽象化はエスケープハッチを露出させる。
Spectrumでは、こうした問題がZ80のアドレス空間に収まる。オリジナルモデルは3.5 MHzで動作するZ80Aプロセッサ、16K ROM、そして16Kまたは48KのRAMを採用していた。こうした制約により、あらゆる抽象化がメモリマップ上で可視化される。
特にディスプレイは示唆に富む。標準的なSpectrum画面は6,912バイトを占め、6,144バイトのモノクロビットマップと768バイトの属性領域で構成される。各属性バイトは、8×8セル1つに対して前景色、背景色、明るさ、点滅を指定する。
この設計はメモリを節約したが、近接するピクセルを1つの色選択へ結びつけた。よく知られる結果が属性衝突であり、異なる色のオブジェクトは、互いに影響を与えずに同じセル内を通過できない。各グリフがそのセルのいずれかに配置されるため、テキスト表示もこのアーキテクチャを引き継ぐ。
Martinの解説は第二の教訓も加える。小規模な文書化済みインターフェースが、必ずしも安定したプログラミングプラットフォームを作るとは限らない。Spectrumは有効な文字プリンターを提供するが、高度なプログラムはATTR-T、MASK-T、P-FLAG、SCR-CT、UDGといった変数のアドレスも把握しなければならない。
公式のシステム変数の説明では、BASICとROMルーチンが利用する共有メモリが記載されている。これらの値を直接変更しながらファームウェアを呼び出す方式は効率的だが、密結合を生む。プログラムは呼び出し可能なルーチンだけでなく、そのルーチンが期待する内部状態にも依存する。
ここでSpectrumは、より正式なファームウェア境界を中心に設計されたマシンと異なる。CommodoreのKERNALは、一般的なサービス向けに固定ジャンプベクターを使用した。MSXは、異なるメーカーのマシンを対象にできるよう呼び出しを標準化した。IBM PCのBIOSも、開発者が後に速度のためそれを迂回する場合であっても、ハードウェアの上位に呼び出し可能なサービスを確立した。
Sinclairのアプローチはそれほど形式的ではなかった。SinclairがインタープリターとROMの両方を制御していたため、BASICには十分機能した。アセンブリプログラマーに提供されたのは、広範な互換性契約ではなく、有用な実装詳細だった。
この取引こそ、関心が続く理由を説明する。Spectrumは、内部実装がいかにして公開インターフェースとなるかを、非常に明快に示すケーススタディだ。プログラマーがアドレスや癖に依存してソフトウェアを構築すると、設計者がその結果を意図していたかどうかにかかわらず、そうした詳細は変更しにくくなる。
真の対立は利便性と安定性にある
SpectrumのROMは単純なプログラムを容易にする一方、近道を取るたびにマシン固有の振る舞いへの依存を深める。
中心的な対立は、ZX Spectrum対Commodore 64ではない。Spectrum内部における利便性対安定性だ。システムへの直接アクセスはコードサイズを削減し、有用な機能を露出させる。同時に、より強力なファームウェア契約であれば内部に閉じ込められていたはずの前提に対し、ソフトウェア自身が責任を負うことになる。
テキスト属性を考えてみよう。文字コード$10から$17は、INK、PAPER、FLASH、BRIGHT、INVERSE、OVER、カーソル配置、タブを制御する。プログラムはこれらのバイトを文字列内に埋め込み、シーケンス全体をRST $10経由で送信できる。
これはコンパクトな仕組みだ。非表示バイトが後続テキストの解釈を変える端末エスケープシーケンスに似ている。独立した描画呼び出しを用いず、メッセージ自身に書式を持たせられる。
意外なのは、その状態が持続することだ。これらのマシンコード制御は、BASICのPRINT文に相当する処理の後もリセットされない。キャリッジリターンも、以前の状態を復元しない。そのため、書式設定が1つの文字列で終わると想定するヘルパーは、それ以降のすべての出力操作を変えてしまう可能性がある。
ROMは、複数のシステム変数を通じて一時的属性と永続的属性を追跡する。ATTR-Tには現在の色、明るさ、点滅の設定が入る。MASK-Tは、変更せずに維持すべきビットを決定する。永続的な対応変数は画面クリアに影響し、既定値を設定する。
この分担が機能するのは、BASICがより大きな言語操作の一部として管理しているからだ。アセンブリコードはその層の下から入る。BASICが通常提供するセットアップとクリーンアップの振る舞いを再現しなければならない。
画面クリアも同じ緊張関係を示す。ROMのCLSルーチンを呼び出せばディスプレイはクリアされるが、Martinによると、以後の出力先も下部ウィンドウにリダイレクトされる。また、画面のボーダーを上部・下部領域と完全に同期させるわけでもない。
彼のclrtoヘルパーはこの振る舞いを修正する。永続的属性を設定し、ボーダー色を導出し、マスクとモードフラグをクリアし、CLSを呼び出してから、上部画面向けにチャネル2を開く。一見基本的な操作が、小さな状態復元プロトコルとなる。
$1601にあるCHAN-OPENルーチンは、ROMが依然として有用である理由を示している。チャネルを開くことは、単にフラグを書き換えるより明確だ。しかしプログラムには依然として、直接の変数書き込みとポート$FEへの出力命令が必要になる。ファームウェアとハードウェアアクセスは絡み合ったままだ。
この組み合わせは、固定されたターゲットでは生産的になり得る。ROMの文字ラスタライザーやカーソル処理を複製する必要がない。開発者は、すべてのピクセルルーチンを書かずに、読みやすいテキスト、カラー制御、ウィンドウ動作、カスタムグリフを得られる。
コストはターゲットが変わったときに現れる。Martinは、互換性のないROMによって米国におけるSpectrumソフトウェアの互換性を損ねたTimex Sinclair 2068を取り上げている。固定ルーチンやシステムレイアウトに依存するプログラムは、同等の振る舞いを前提にできなかった。
従来のアプリケーションプログラミングインターフェースは、サポート対象の振る舞いと内部構成を分離する。Spectrumのアセンブリ環境が提供するのは、その境界の部分的な版にすぎない。ROM呼び出しが魅力的なのは、すでに存在するからだが、周辺の契約は開発者が部分的に再構築している。
これが、この解説が単なる別のHello World例以上の意味を持つ理由だ。隠れた契約を明示するのである。コードは、設定すべき状態、状態を変更するルーチン、その後に復元すべき値を記録している。
テキストモードの実体はビットマップと状態機械
これをテキストモードと呼ぶのは便利な略称だが、実装は共有される変更可能な状態によって制御されるビットマップレンダラーである。
「テキストモード」という表現は通常、文字コードとして格納された専用の文字セルを想起させる。ハードウェアは各コードに対応するグリフを取得し、自動的に描画する。1つのセルを変更するには、文字値と、場合によっては色の値を書き込めばよい。
オリジナルのSpectrumは異なる仕組みで動作する。ソフトウェアはROMプリンターを呼び出し、グラフィックスと同じビットマップにグリフのピクセルを描画する。別個の属性領域が、文字セル単位で色を供給する。視覚的なグリッドはプログラミング上の慣習であり、完全なハードウェアのテキストバッファではない。
この違いは、Martinの解説にある複数の仕組みを説明する。ROMは通常の文字、ブロックグラフィックス、ユーザー定義グリフを同じ経路で印字できる。いずれも8×8ピクセルのパターンになるためだ。プリンターは、グリフが文字を表すのか、傘の一部を表すのかを知る必要がない。
Spectrumの文字セットの上位部分は、この方式を支えている。コード$80から$8Fは16種類のブロック組み合わせを表す。$90で始まるコードはユーザー定義グラフィックスを参照する。後続のコードはBASICキーワードを符号化し、インタープリターがコマンドをコンパクトに保存できるようにする。
カスタムグラフィックスは間接参照に依存する。UDGシステム変数は、現在のユーザー定義ビットマップを指す。各文字は8バイトを占め、各行につき1バイトである。Martinのサンプルは32バイトをその領域にコピーし、傘を構成する隣接した4つの部品を定義する。
この間接参照は小規模ながら重要な抽象化だ。描画ルーチンに、ハードコードされた単一のグラフィックスアドレスは不要となる。プログラムはポインターを通じてアクティブな領域を見つけ、図形を置き換えられる。この振る舞いは、はるかに小さな規模の設定可能なフォントアトラスに似ている。
色はセルベースのままである。属性バイトは、インク色1つ、ペーパー色1つ、明るさビット、点滅ビットを割り当てる。個々のピクセルはセルがインクとペーパーのどちらを表示するかを決められるが、それぞれ無関係な色を選べるわけではない。
このメモリー節約型の設計では、書式設定コントロールが状態と画面メモリーの両方に対する操作となる。ROMが文字を印字するとき、ATTR-TとMASK-Tを参照し、ピクセルを書き込み、属性セルを更新する。透明なインクまたはペーパーのオプションは、バイト全体を置き換えるのではなく、選択したフィールドをマスクして機能する。
ROM逆アセンブルは、こうした効果の背後にある経路を明らかにするため、今も価値がある。この資料は、特にプログラムがマニュアルの表面的な説明を超える振る舞いに依存する場合、エントリポイントが実際に何を変更するのかを検証できる。
共有状態は微妙な失敗も生む。Martinの最初の印字ループは、文字列の終端としてゼロを使う。この方法は、ゼロが意味のあるデータになるまで機能する。完成版のバナーでは、ペーパーや明るさの設定を含め、値がゼロである制御引数を印字する必要がある。
そのため改訂版のループでは、番兵値として$FFを使う。このバイトはBASICキーワードCOPYを表すが、バナーはそれを印字しない。変更は小さいが、データ形式がその値を含むように拡張されると、帯域内の終端記号が破綻するという一般的なプロトコル上の問題を示している。
同じ問題は、ネットワークプロトコル、ファイル形式、コマンドストリーム、シリアライゼーションライブラリーにも現れる。バイトが区切り文字として安全なのは、ペイロードにそれが含まれない間だけだ。制御データと表示データが一つのストリームを共有するようになれば、フレーミングには明示的な設計が必要になる。
これがこの記事における最も強い現代的な教訓だ。マシンの制約は古いが、失敗のパターンは現在も通用する。共有状態、文書化されていない副作用、過負荷なバイト値、狭い互換性の前提はいまなおソフトウェアシステムを形作っている。
入力がファームウェアのトレードオフを完成させる
キーボードとジョイスティックの処理も、テキスト出力と同じパターンを示す。ポリシーが役立つ場合はファームウェアを使い、直接制御が重要な場合はそれを迂回する。
Martinの解説は、実用的なテキストシステムには対話が必要なため、画面出力からキーボード入力へ移る。Spectrumもまた二つの経路を提供する。プログラムはROMが準備したキーボード状態を利用することも、ハードウェアポートを直接読むこともできる。
ファームウェア経路は、マシンのフレーム割り込みに依存する。割り込みとは、ハードウェアによってトリガーされるサービスルーチンへの制御移行である。各ビデオフレームごとに、SpectrumのハンドラーはFRAMESタイマーを更新し、キーボードマトリクスを走査する。
キーを検出すると、ハンドラーはその入力をデコードし、文字をLAST-Kに保存する。また、FLAGSシステム変数のビット5をセットする。Martinのgetkeyルーチンは、Z80のHALT命令で待機し、フラグをテストして文字を取得し、フラグをクリアして戻る。
HALTの使用には意味がある。割り込みハンドラーが次のキーボード走査を実行するまで、ループには有用な仕事がない。その割り込みを待つことで、変更されていないフラグをプロセッサー速度のまま繰り返し読み取ることを避けられる。
この経路が提供するのは、生の電気的状態ではなく解釈済みの入力である。ROMはキーボードの組み合わせを理解し、それらを文字にマッピングする。プログラムはキーボードデコーダーを再実装せずにテキストを受け取れる。
直接入力では、その利便性を即時性と引き換えにする。Spectrumのキーボードは、I/Oポートを通じてアクセスするマトリクスとして構成されている。行を選択して返されたビットを確認すれば、現在どのキーが押し続けられているかが分かる。
Z80には歴史的な注意点がある。一部の入出力命令は8ビットのポートアドレスを公開しているように見えるが、IN A,(C)とOUT (C),Aはアドレスバス上に完全なBCレジスターを配置する。Sinclairはハードウェアインターフェースの設計時にこの挙動を利用した。
Martinの例では、ポート値$FDFEを通じてAキーを確認する。このコードは、ROMがキー押下を変換するのを待たない。マトリクス内の一つの位置についてハードウェアに問い合わせるため、継続的な方向入力状態が必要なゲームに有用だ。
Kempstonジョイスティックはより単純である。ポート$1Fを使用し、各ビットが方向とファイアボタンを表す。Martinの読み取りルーチンは、このビットフィールドを水平・垂直方向の差分とファイア値へ変換する。
これらの選択肢は、抽象化が存在してもプログラマーがそれを迂回する理由を示している。ファームウェアのキーボード入力は、テキストの入力やコマンド待機に適している。直接ポート読み取りは、同時移動、低遅延、繰り返しの状態確認に適している。
代償は移植性だ。Spectrumのキーボードマトリクスに結び付いたルーチンは、そのマシンの電気的配置を前提とする。Kempstonの読み取りルーチンは、その特定のインターフェースを前提とする。エミュレーターはこれらの挙動を再現しなければならず、別のハードウェアやジョイスティック規格には異なるコードが必要となる。
Martinは、エミュレーターにおける合成Shiftキーに関する不整合にも触れている。これは有益な懐疑的視点だ。技術的に正確なルーチンであっても、周辺実装がホスト入力を別の方法で解釈すれば、異なる動作をする可能性がある。
Hacker Newsへの掲載を、すべてのエミュレーターやSpectrumのバリエーションに対する広範な検証と誤解してはならない。リンクされた投稿は、提供されたスナップショットでは控えめな反応にとどまり、記録された議論もなかった。技術的価値は群衆の合意ではなく、再現可能なコード経路にある。
したがって読者は、主張を三つの層に分けるべきだ。オリジナルのSinclair文書は、マシンが意図した機能を確立する。ROM分析は実装上の挙動を明らかにする。Martinの例は一つの動作する開発経路を示すが、すべてのクローン、ROMリビジョン、インターフェース、エミュレーターで同一の結果を保証するものではない。
Hacker Newsの急増後に開発者が注視すべきこと
次の試金石は、この解説が短命なリンクではなく、永続的な技術インフラになるかどうかだ。
最初の兆候は、システム解説の継続である。Martinは明確な空白を残して終えている。これらの例は、以前のゲームのマシンコード版に必要な主要な表示、入力、アニメーションを再現できるが、サウンドとタイトル画面は未解決のままだ。グラフィックスまたはオーディオを扱う続編が出れば、同じ手法が文字中心の出力を超えて拡張できるかが分かるだろう。
二つ目の兆候は、対象環境をまたいだ再現性だ。開発者は、オリジナルの48Kハードウェア、後期のSpectrumモデル、一般的なエミュレーター、ROMのバリエーションで例をテストすべきである。出力が一致すれば、これらのルーチンを実用的な互換性レイヤーとして扱う根拠が強まる。相違があれば、直接的な状態アクセスがROM呼び出しの安定性を上回る地点が明らかになる。
三つ目の兆候は、コードが検査・再利用しやすくなるかどうかだ。ダウンロード可能な例、文書化されたビルド手順、固定されたテストイメージ、エミュレーター自動化があれば、記事は実行可能なリファレンスになる。Martinはすでに、最初のHello Worldの流れで使ったアセンブラー、テープパッケージングツール、FUSEエミュレーターを挙げている。これらの依存関係を保存することは、アセンブリーリストを保存するのと同じくらい重要だ。
より広い文書化の問題もある。レトロプラットフォームには豊富な情報がある一方で、権威は断片化されがちだ。マニュアルは意図された振る舞いを説明し、逆アセンブルは内部を明らかにし、コミュニティのリファレンスは誤りを正し、現代のチュートリアルが各要素をつなぐ。有用なプラットフォームガイドは、文書化された契約と観測された癖を明確に区別すれば、その断片化を軽減できる。
この流れを追う開発者は、一つの洗練された例を普遍的な規則に変えてしまわないよう注意すべきだ。ROMの直接呼び出しは、メモリーと開発工数を節約できる。ハードウェアへの直接アクセスは、応答性を改善できる。どちらも、テストされた正確な環境の外で互換性を保証するものではない。
その不確実性自体が価値の一部である。ZX Spectrumでは、文字列の1バイトからROMルーチン、システム変数、メモリーアドレス、表示セルまで、完全なスタックを通じて障害を追跡できる。現代のシステムで、この水準の検査を許すものはほとんどない。
最も有用な次の行動は単純だ。バナーを再現し、一つの前提を変え、何が壊れるかを観察する。コードを移動し、終端記号を変更し、属性を有効なままにし、誤ったチャンネルを選び、別のROMをテストする。Hacker Newsの瞬間は過ぎ去るだろうが、こうした実験は本当の教訓を残す。インターフェースは、プログラマーが呼び出すエントリポイントだけでなく、その状態と副作用によっても定義される。



