ウィザードの城のREMステートメントの謎

ウィザードの城のREMステートメントの謎

REMステートメントの謎

1980年のマイクロコンピュータゲームウィザードの城(Exidy Sorcerer向けに書かれました)では、ソースコードの最初の行は 10 REM"_(C2SLFF4 と表示されます。これはタイポやゴミテキストのように見えますが、実際にはゲームの疑似乱数生成器(PRNG)のシードに使用する RANDOMIZE 関数をシミュレートするために、BASICコメントとして偽装されたZ80マシンコードルーチンです。

マシンコードがどのように隠されたか

Exidy Sorcererでは、BASICインタプリタはコマンドをトークン化し、アドレス469から始まるRAMにリンクリストとして格納します。プログラムノードの構造には、次のポインタ、行番号、トークン化されたコマンド、および行のテキストが含まれます。

プログラムの最初の行については:

  • アドレス 469-470: 次のポインタ
  • アドレス 471-472: 行番号 (10)
  • アドレス 473: REM トークン (0xC3)
  • アドレス 474: REM テキストの開始

USR() 関数を使用すると(アドレス 260 と 261 の POKE コマンドで指定されたマシンコードアドレスにジャンプします)、ゲームは REM ステートメントのテキストを実際の Z80 命令として実行します。具体的には、コード POKE 260,218: POKE 261,1USR() 関数にアドレス 474 へジャンプするよう指示します。これはまさに REM テキストが始まる場所です。

Z80ルーチンの解析

REM テキストをASCIIとして逆アセンブルしようとした初期の試みは、オリジナルの雑誌掲載の文字がASCIIエンコードされていなかったため失敗しました。テープイメージから実際のバイトを分析すると、次のルーチンが明らかになりました:

LD A,R       ; Copy R register to accumulator
JR Z,-4      ; If result is 0, jump back to previous instruction
LD (F7FF),A  ; Copy accumulator into address F7FFh
RET          ; Return to BASIC

PRNGシーディングプロセス

  1. エントロピー ソース: このルーチンは R レジスタを読み取り、命令フェッチ中に頻繁にインクリメントされるため、ユーザーがゲームを開始する正確なタイミングに基づく半乱数値を提供します。
  2. ゼロチェック: 値がゼロでないことを確認し、PRNGのシーディングが悪くなるのを防ぎます。
  3. メモリマッピング: 値はアドレス 0xF7FF に格納され、これはメモリマップされたスクリーンテキストの右下の文字位置に対応します。
  4. BASIC からの取得: BASICコードはその後 T = PEEK(-2049) (ここで -2049 は 0xF7FF)を使用してこの値を読み取り、Q = RND(-(2*T+1)) を使って RND() 関数のシードを行います。

R レジスタは下位7ビットしかインクリメントしないため、この手法によりゲームは最大127種類のランダムに生成されたダンジョンに制限されました。

"Typing-In" パラドックス

1980年代、プログラマーは雑誌から直接コードを入力していました。ただし、ウィザードの城REM ステートメントは、リテラルなASCII文字として入力しても機能しませんでした。オリジナルのユーザーがこれを実装した方法については、以下の2つの説明が考えられます:

  1. 直接POKE: 作者はプレースホルダーの REM を入力し、その後 POKE コマンドを使って非ASCIIバイトをメモリに手動で挿入した可能性があります。
  2. 特別なキー組み合わせ: 調査によると、Exidy Sorcerer には Graphic+Shift キーの組み合わせがあり、これによりユーザーは 0x80 から 0xFF のトークンと値を入力でき、これがリストに見えるグリフに対応します。

コミュニティからの技術的洞察

この技術の分析は、固定されたRAM位置とマルチプロセッシングの欠如により、BASICとマシンコードのシームレスなブレンドが可能だった初期のホームコンピュータの決定論的な性質を浮き彫りにします。

"ほとんどのタイプインハイブリッドBASIC-マシン言語プログラムには、オペコードを含む多数のDATAステートメントと、それらをREADしてPOKEするループがあります... Commodore 64 BASICのLOAD"WHATEVER",8コマンドは、検証なしでBASICテキストエリア内の任意のサイズのファイルをロードします。" — @RiverCrochet

同様の技術は、 Sinclair ZX81 などの他のプラットフォームでも使用されており、専用のメモリブロックや DATA ステートメントがない場合、REM ステートメントやその他の無視される行を使ってマシンコードを格納していました。

Sources