ウィザードの城の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,1 が USR() 関数にアドレス 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シーディングプロセス
- エントロピー ソース: このルーチンは
Rレジスタを読み取り、命令フェッチ中に頻繁にインクリメントされるため、ユーザーがゲームを開始する正確なタイミングに基づく半乱数値を提供します。 - ゼロチェック: 値がゼロでないことを確認し、PRNGのシーディングが悪くなるのを防ぎます。
- メモリマッピング: 値はアドレス
0xF7FFに格納され、これはメモリマップされたスクリーンテキストの右下の文字位置に対応します。 - BASIC からの取得: BASICコードはその後
T = PEEK(-2049)(ここで -2049 は 0xF7FF)を使用してこの値を読み取り、Q = RND(-(2*T+1))を使ってRND()関数のシードを行います。
R レジスタは下位7ビットしかインクリメントしないため、この手法によりゲームは最大127種類のランダムに生成されたダンジョンに制限されました。
"Typing-In" パラドックス
1980年代、プログラマーは雑誌から直接コードを入力していました。ただし、ウィザードの城 の REM ステートメントは、リテラルなASCII文字として入力しても機能しませんでした。オリジナルのユーザーがこれを実装した方法については、以下の2つの説明が考えられます:
- 直接POKE: 作者はプレースホルダーの
REMを入力し、その後POKEコマンドを使って非ASCIIバイトをメモリに手動で挿入した可能性があります。 - 特別なキー組み合わせ: 調査によると、Exidy Sorcerer には
Graphic+Shiftキーの組み合わせがあり、これによりユーザーは 0x80 から 0xFF のトークンと値を入力でき、これがリストに見えるグリフに対応します。
コミュニティからの技術的洞察
この技術の分析は、固定されたRAM位置とマルチプロセッシングの欠如により、BASICとマシンコードのシームレスなブレンドが可能だった初期のホームコンピュータの決定論的な性質を浮き彫りにします。
"ほとんどのタイプインハイブリッドBASIC-マシン言語プログラムには、オペコードを含む多数のDATAステートメントと、それらをREADしてPOKEするループがあります... Commodore 64 BASICのLOAD"WHATEVER",8コマンドは、検証なしでBASICテキストエリア内の任意のサイズのファイルをロードします。" — @RiverCrochet
同様の技術は、 Sinclair ZX81 などの他のプラットフォームでも使用されており、専用のメモリブロックや DATA ステートメントがない場合、REM ステートメントやその他の無視される行を使ってマシンコードを格納していました。