CD-ROM2 の読み込み時間の厳密な再現の必要性 / その3

David Shadoff さんの実測と再現ルーチンのおかげで、問題となっていた多数のソフトの動作の改善がみられました.

用語の規定

この文章内では下記といたします.

  • SEEK TIME: 読み込みコマンドを受けてから光学ヘッドが移動する時間.
  • READ TIME: 1 frame を読み込む時間. Compact Disc の 規格上 1/75 秒.
  • LOAD TIME: 読み込みコマンドを受けてからすべてのデータを読み込む時間. 細かい部分を無視すると SEEK + READ * sectors の所要時間.
  • 1 frame: Compact Disc のデータ単位. 2352 bytes. *1
  • 1 sector: CD-ROM で 1 frame の中の管理情報を抜いたデータ単位. 2048 bytes. 16 進数で 0x800 bytes.

SEEK TIME

機械的要素と物理的要素が関係するためわたしには理解が難しく、 David さんがほぼ解明してくださいました.

SEEK TIME はヘッドの移動距離が主なパラメータです. CD のデータは内周から外周にらせん型で等密度で記録されています. つまり1周あたりの frame 数が異なるためにセクタ番号の差分を割り算するという単純式では所要時間を算出できません. またモーターが停止から制動する時間の算出も簡単な数式ではありません. それに加えて、実物の経年劣化も時間が変わる要素となります.

実測は David さんが PCE 向けにプログラムを組んで PCE の VSync (約 1/ 60 秒単位) で計測してくださいました. 大きな傾向としては下記となります.

  • 同じ移動距離であっても vsync 単位でばらつきがおきる
  • 移動距離が大きいとばらつきは最大 20 vsync もでる
  • メンテナンスをしてあれば機種別の所要時間平均値は大体同じだが PI-CD1 はおそいらしい

この計測情報や問題となるソフトで必要な経過時間を元にらせん構造からの移動時間の算出ルーチンを David さんが作ってくださいました.

これらの計測を元に UperGrafx や Mednafen で問題となるソフトは改善しました. この再現によりビジュアルシーンでの絵と音のずれは直りましたが、場合によっては実機でさえ問題が起きたことを確認しています.

SEEK TIME の再現で一番の問題となっていたのは TJCD2023 夢幻戦士ヴァリスのオープニングデモ後半の下校シーンです. このプログラムは内周端から外周端の遠い距離に移動することと、CD SEEK の同期をタイミングとっていませんでした. 必要な SEEK TIME 時間は約 1.6 秒で現状のエミュレータではパッチをあてないとちゃんと動かないはずです.

READ TIME

SEEK TIME の実装によりビジュアルシーンの同期ずれは直りました. ADPCM 再生中にしてはいけない ADPCM RAM への読み込みの問題が直らないソフトがわずかにありました.

David さんの調査で最も厳密なタイミングを求めるソフトは HECD4007 ブランディッシュのオープニングデモ後半の「この大穴があんたの墓になるのよ!」でした. これも実機でさえ起きる不具合で、現状のエミュレータでもちゃんと動かないと思われます.

不具合の原理は下記となります.

  1. ADPCM 再生を開始
  2. CD 読み込みのコマンドを発行し、転送先を CPU にする (わりと長い)
  3. (タイミングが正確なら) ADPCM の再生が終わる (PCEソフト側が同期をとっていない)
  4. CD 読み込みのコマンドを発行し、転送先を ADPCM RAM にする.
  5. ADPCM 再生途中に CD 読み込みをしてしまい、 ADPCM の再生が延長される

2. の読み込みコマンドの所要時間内に ADPCM の再生が終わることが必須となります. 先述の SEEK TIME の再現がない場合ここは 1.6 秒も早かったです, SEEK TIME の再現をいれてもまだ 0.2 秒早かったです.


ここでタイミングチャートの作図をし、1 sector あたりの読み込み時間を分散して増やして、2. の読み込み時間を 0.2 秒増やすことにしました. つまり PCE の厳密な CD-ROM の 1 sector の READ TIME は、 CDDA での 1 frame の読み込み規格値 1/75 秒に 0.0017 秒を加算した値であると決めました. 作図上 no seek time と書いたところは実機では約 0.0017 秒の内部処理時間があると予想しています.

この時間は割と信憑性があるようで、似た症状が起きる TJCD2026 F1チームシミュレーション PROJECT.F や実時間で高い読み込み精度を要求する HCD5076 空想科学世界ガリバーボーイ の Arcade Card での HuVideo での問題も起きなくなりました.

同期をとらないソフトたち

HuVideo *2は明確に理由があるので仕方ないですが、CD SEEK や ADPCM 再生完了の同期をとらないソフトは場合によっては実機でも起きる潜在的なバグたちでした.
逆説的に申し上げますと同期をとっているソフトは SEEK TIME や READ TIME が違ってもちゃんと動きます. 問題が起きるかどうかはゲームソフトのプログラマの性格に依存するところが大きいです.

CD-ROM2 互換機能でちゃんと動作しないソフトはかなり減りましたが、別の原因で止まってしまうソフトがまだ数本あります. これらのソフトはどういうわけか1995 年以降の PCE の市場がほぼ終わってるときにでていたソフトたちで、特別なテクニックを使っているわけでもなく純粋に何をしているのかわからないひどいプログラムです. これは筆者の勝手な予想ですが、その当時 PCE のソフトを制作する会社なんてものは1社か2社程度で、汚いソースコードをひたすら使い回していたのではないかと思ってしまいます.

*1:frame とあるが video でいう frame (約 1/60 秒)は全く関係ない

*2:ブランディッシュもそうかも

自分で作った潜在的なバグ

最近他人が作った潜在的なバグに言及しておりますが、upergrafx のソースコードにも本来ならすぐに起きるはずのバグが起きずに放置、その後の謎の現象となり、調査したら稚拙なバグが2件も見つかってお恥ずかしい限りです.

  • write strobe の正負の論理を間違える
  • address decoder で read 用 databus 入力で read strobe をデコードしてない

address decoder のほうはなんでいままで正常に意図通りに動いていたのが不思議なぐらいのバグでした. そういう変なバグがあると開発が停滞しますが、そこを抜けたので今回の回収も落ち着くといいのですが...

CD-ROM2 の読み込み時間の厳密な再現の必要性 / その2

CD-ROM の 1 sector のロード時間を規格通り 1/75 秒に設定したところ、ロード時間が早いために問題になっていたソフトが結構直りました. 一方直らなかったソフトの傾向を見ると下記のようでした.

ロード時間の前後の処理時間を要求するもの

1 sector のロード時間を設定しても、その前後の準備時間や後処理の時間はわからないままです. 準備時間はシーク時間で別に書くとして、後処理のレジスタの変化の微妙なタイミング要求するソフトまでありました.

このような高い精度を要求するソフトは...正直に申しまして...プログラムが汚く読めたものではありません. 初期化やタイミング同期を省略しているので本物のハードのタイミングでのみ奇跡的に動いているものが多いです. これはプレイヤーが遊んで名作かというのは関係なくプログラマの性格やそのソースコードを使い回す職場が原因で困りものです.

シーク時間の再現を要求するもの

CDDA の音ズレの原因がロードではなくシークの時間の再現がいるようです. シーク時間は CD のレンズが物理的に動く時間で実機からの計測がいります. これもシークしてから再生すればいいだけなんですがとってないみたいです.

当ユニットのユーザーさんの間では人気のスーパーダライアスでは最初のクレジット音とゲームの開始も同期を取らずにシーク時間決め打ちで動いているようです. mednafen でもシーク時間を再現してないみたいで、ゲームを遊ぶ側にとってはダメな演出になっています.

ADPCM 用 RAM のメモリアクセス待ちの再現を要求するもの

風の伝説ザナドゥで港のシーンにいく前に CDDA 再生したまま, ADPCM 用の RAM (本物は DRAM) から VRAM に転送しているようでして. メモリアクセス待ちを設定しないのかずれてしまいます.

これは実機からの計測で待ち時間の法則性を得て再現性をあげる必要があります. 元のプログラムで CDDA を PAUSE するか時刻指定で再生し直すだけで同期はとれたと思うのですが...

実機計測かパッチか

(次回に続く)

CD-ROM2 の読み込み時間の厳密な再現の必要性 / その1

現在の不具合報告に登録してあるソフトの不具合の原因が読み込み時間が実機と違うのでちゃんと動かないというのがある程度わかっています.

別件で mednafen の document を読んでいたら pce_fast module で CD-ROM の読み込み時間を設定で変えられることをしりました. それが再現するかみてみたら大半は再現しました. mednafen で pce と pce_fast と2つ emulation engine が存在するのはこの理由でしょう. 他のエミュレータがこの乖離をその場しのぎのパッチで補正するよりかは、わたしとしては*1 mednafen はいい選択と思います.

PCE の CD-ROM2 の場合、読み込み速度がかわっていても同期を取る仕組みはあります. 問題にならないソフトはちゃんと同期をとっているんですが、問題になっているソフトの大半は同期を取っていないとか初期化を省略しているとかの潜在的なバグが表にでてきてしまっています. 潜在的なバグを直すことはよいのですが、意図的に同期を取らないソフトもわずかに存在します.
意図的に同期を取らないソフトは HuVideo を使うものとブランディッシュぐらいです*2.

というわけで厳密な読み込み時間の再現を実装してみますが、意図的なものを動くことを目標にしまして、潜在的なバグは回避策がなければパッチをあてるほうが無難なのかもしれません.

*1:ゲームを遊ぶことだけを優先せずに現状動作がおかしくても再現度をあげるとかデバッグ情報が豊富という視点

*2:手元にイメージがないんですがネクスザール(通常版)もそうな気がします

9821 のハードディスク相当を復旧する / その4 (終)

Filesystem 関連

前回細かく書いていたのは LBA=0 を PC-98 用, AT 用に調整すれば現行のパソコンから普通にファイルシステムにアクセスできるだろうという考えでした.

結論からいいまして下記でした.

  • NEC 5.0 の FAT header のフォーマットが謎で RCF-X 64MB で発生して 2GB の SD カードでは NEC 6.2 だけだった
  • NEC 6.2 だけのほうは AT 用の MBR として LBA #88 からにすれば普通に mount できた
  • FAT header の offet 0x1fe に data 0x55, 0xaa が規格上いるはずが NEC は書いてないし, 今の Windows でも文句をいわない
  • RCF-X 64MB は LBA #88 から FAT として Windows からフォーマットした場合は PC-98MS-DOS からも使える
  • どちらの場合も 1 cylinder = 8 Heads , 17 Sectors; 8 * 17 * 0x200 -> 0x11000 となる.

当然ながら MBR だけ書き換えて mount するという考えは別の方がちゃんとしたツールを作っていましたので NEC 5.0 の謎フォーマットだけが問題でした.

TCP/IP

LGY-98 を持っていましたので下記の文書のままでそのまま使えました.
http://www.qsl.net/ja0rug/teene.html

TEEN 用の ftp client やWindows共有フォルダアクセスツールも普通に使えました. Windows のほうはセキュリティ甘めが必要でした, パスワード有りはやってみたけどだめでした.
つまりオンラインのファイルの受け渡しも問題なくできます.

ゲーム

ちゃんと動きました. でもゲームやるだけならエミュレータのほうが楽かなと思いました. 何より 2018 年の常識では PC-9821 の寸法は大きすぎで邪魔です. 2010年あたりからコンピュータ用の機器がどんどん小さく高性能になっていくのはなんとなく気付いていましたが、 1997 年の常識とは全然違う物でした.

おわり

SCSI カードが動かなくなっていたので、 16GB や 8GB の flash media が使えません. FreeBSD (98) の導入はやらずにここで終わりにします. LGY-98 や MS-DOS 起動ディスクの所持がハードルでしたが、それ以外は機材も簡単に揃いますし、ソフトの操作も経験があれば難しいことはありませんでした.

一応この機材を残していた理由が PC-9821 実機ででないとできないことのためで、それが PC-98 向けではないフロッピーディスクの解析でした. 5 インチディスクとかファイルシステムがない特殊用途とか、そういう依頼がたまにあったので残していました. それも死んでいた期間に依頼があったわけではなかったし、私だけがそれをやれるという分野でもないように感じました.

PC-9821 のハードディスク相当を復旧する / その2

手元にある Compact Flash は昔デジカメで使っていた RCF-X 64MB です. これは websitehttp://buffalo.jp/php/lqa.php?id=BUF9612 [コンパクトフラッシュを起動ディスクとして ご利用いただくことはできません。]とありましたので別のメディアも買いました.

Trancend TS16GCF133 と No brand EXTREME CF アダプター UDMA TYPE II SD です. TS16GCF133 はデータシートに True-IDE mode のことが明記, EXTREME CF... のほうは .com のほうの amazon に True-IDE mode support と書いてありました.

機材があらかた到着して,PC-9821V16 につけて動作させてみたところ TS16GCF133 は容量制限で memory check 以降の動作(OS 起動)になりませんでした. 容量制限,つまりこの機種の IDE port の固定ディスクは 4.x GB までです. そんなことは購入当時から知ってて PCI bus に挿して IDE コネクタが付いてる SCSI カードを持っていたので容量制限は関係ないと思っておりました. でもその SCSI カードがどうも動いてないみたいでした...

RCF-X 64MB も使えますし、EXTREME CF アダプターも使う SDCard が 2GB なら固定ディスクとして OS が起動できました.

PC-9821 のハードディスク相当を復旧する / その3

(その2はあとでかく) ひとまず Compact FlashMS-DOS が BOOT できるようになりましたので現行のパソコンから dd で dump してみました. 筆者は開発環境として msys2 を常用していますので dd なり /dev/sdx というデバイスは普段から利用できます.

実データ

LBA=0, IPL らしい.


LBA=1, パーティションテーブル.

説明はここにちょっとある. https://hp.vector.co.jp/authors/VA013937/editdisk/tech.html

LBA=2, OS 選択起動画面の文字列.


パーティションテーブルの開始シリンダから MS-DOS 6.2 の領域(つまり FAT) が始まる. これは FORMAT.EXE の固定ディスクの領域確保 (つまりいまでいう fdisk) らしい.

ここまでやって1シリンダが何バイトなのか不明だったのと、利用した古い 64MB の Compact FlashMBR なしのフォーマットと PC-98固定ディスクフォーマットと PC/AT固定ディスクフォーマットがまざりまくって、 FAT の先頭が3つありどれかわからなくなったのでやり直します.

 dd if=/dev/zero of=/dev/sdx bs=1M count=2

FAT filesystem の先頭

先頭 2Mbyte を data 0 で埋めた Compact Flash を接続し再度PC-98から固定ディスクの初期化とシステム転送をしてきました.

LBA=0x88, NEC 5.0 (?) の filesystem header

LBA=0x89, NEC 6.2 (?) の filesystem header

LBA=0x8a, FAT16 の cluster chain table

固定ディスク領域マップによるとこの領域はシリンダ 00001-00915, サイズ 00061 とのことです. 1シリンダが 0x11000 byte なのか 0x11200 byte なのか謎ですが filesystem header をみることにします.

JmpBoot 0xeb 0x45 0x90
OEMName "NEC  5.0"
BytesPerSector 1024
SectorPerCluster 2
ReservedSectorCount 1
NumFats 2
RootEntryCount 3072
TotalSector16 60390
Media 0xf8
FATSz16 59
SectorPerTrack 17
NumHeads 8
HideSector 136
TotSec32 0
DriveNumber 128
Reserved1 0x00
BootSignature 0x29
VolumeID 0x29 0xfe 0x07 0x10
VolumeLabel "NO NAME    "
FilesystemType "FAT16   "
BootSign 0x00 0x00
JmpBoot 0xeb 0x45 0x90
OEMName "NEC  6.2"
BytesPerSector 1024
SectorPerCluster 2
ReservedSectorCount 1
NumFats 2
RootEntryCount 3072
TotalSector16 60390
Media 0xf8
FATSz16 59
SectorPerTrack 17
NumHeads 8
HideSector 136
TotSec32 0
DriveNumber 128
Reserved1 0x00
BootSignature 0x29
VolumeID 0x29 0xc0 0x07 0x16
VolumeLabel "NO NAME    "
FilesystemType "FAT16   "
BootSign 0x00 0x00

NEC 5.0 と NEC 6.2 の内容は大きな違いはないのですがその次に cluster chain がある NEC 6.2 を使うことにします.

PC-9821 のハードディスク相当を復旧する / その1

やることはよくある IDE コネクタに Compact Flash を接続するやつです.

2000年ぐらいにハードオフで本体が 500 円だったから買った PC-9821V16, 周辺機器を買いそろえたら結果としてえらい値段になりました. それからも時々 PC-98 ならではの性能で活躍していましたが、 2010年ごろに HDD が死んだこと忙しくなったことが複合して押し入れの奥にいましたが、復旧用のデータが見つかったのでやることにしました.

目的

  • ハードディスクを Compact Flash に代替する
  • DOS のゲームが動くようにする
  • TCP/IP で現行のパソコンと通信できるようにする

Compact Flash の下調べ

Compact FlashIDE port に挿して代用するのは当時からあった気がしますが、このご時世健康なパラレル IDE のハードディスクは手に入らないし、 Flash Memory はえらいやすいのでこれを利用します.

一応、電子回路設計をする立場なので一般ユーザーが相性という曖昧な言葉で片付けるようなことも技術書や仕様書を読んで下調べしました.

Compact Flash (PC card) を IDE port にさすコネクタはそういう用途前提に設計されているので、物理的な形状の変換だけで済みます. 電源電圧も 5V, 3.3V どちらでも動くようになっているのを確認しました.

Compact Flash のカードそのものはよくいわれているようにこの用途では電源投入時に True IDE mode になる Compact Flash が必要です. True IDE mode は規格上必ず実装されている必要があるが、電源投入時にそれであるかは個別の依存とのことです.

そのつぎに PC-98 側の IDE ハードディスクの容量制限があります. Compact Flash もそれらの制限容量以下のものを用意する必要があります. 目的が DOS のゲームであれば容量は 2GB で十分な気がします.

TCP/IP の下調べ

PC-98DOS という時代、 TCP/IP とかいうマイナープロトコルは話題に上がらなかった気がします. Windows でもダイアルアップでインターネットをしていたのか、 LAN をつないで PC-98 から TCP/IP 通信をするというのは個人では珍しかったのではないでしょうか.

時は流れ今更フロッピーディスクでファイルの受け渡しはかったるいです. そもそも今のパソコンにフロッピードライブなんてついてません.

以前 HDD が死ぬ前は Plamo Linux から TCP/IP を利用しておりました. しかしメインの OS は DOS であり、現行のパソコンとファイル転送するには Linux を立ち上げて ftp なり wget なり scp をして、マウントしておいた DOS パーティーションにアクセスというのをやっていましたが、OS 切り替えが面倒でした.

まだ使ってませんが, TEEN というソフトが MS-DOSTCP/IP 通信が出来るということがわかりましたのでファイル転送にはこれを活用しようと思います. こういうのが無料で手に入るって、中古のV16を500円で買ったときは知りませんでした.
http://www.pc88.gr.jp/teen/wiki/index.php

それらが全て落ち着いたら Plamo Linux は今は配布してないので FreeBSD (98) をいれて openssh でつなぐぐらいはやろうと思います. ただこの分野は(Windowsも含めて)わざわざ PC-98 でやる必要がないので微妙なところです.

下調べからの作業の流れ

  • Compact FlashIDE コネクタに挿す
  • MS-DOS が起動するフロッピーを用意して Compact Flash をフォーマットしてシステム転送
  • Compact Flash が BOOT できることを確認したら現行の PC に持っていって dd でディスクイメージを軽く解析
  • そのディスクイメージにツール,ゲーム,通信のデータを大量に入れ再度 dd で書き込む
  • 実機に持っていってそれらが動くことがわかったら TCP/IP 経由で足りないファイルを補充していく

ということを想定しながら部品を買いそろえました. (つづく)

PCE の入力デバイス用ポートの仕様調査

毎度のごとく公式文書をみたわけではなく、インターネット上に転がっている信憑性の低い情報を元にしていますのでこの情報も信憑性は低いです. 実ソフトの逆アセンブルと分析もいまのところしてません.

pinout

+5V, GND, D3:0, Q1:0

software からの仕様は address 0x1ff000 (0xff:$1000) にある. D3:0 は read data bit 3:0 で Q1:0 は write data bit1:0 となっている.

device: 2 ボタンパッド

74157.SEL = Q0
74157.CLR = Q1
D = ボタン4つ x2
  • 出力端子が 74157 の制御線につながっている単純なもの.
  • Q0 = 0 の場合 D3:0 = 4'b000 となる.
  • SEL を page として, 入力端子を切り替える.

device: 6 ボタンパッド

3 ボタンパッドも設計はほぼ同じなのでそれは省略する. モード切替スイッチも省略する.

74157 #10
CLR = Q1
SEL = Q0
xD = ボタン4つx2

74157 #11
CLR = Q1
SEL = Q0
0D = ボタン4つ
1D = 4'b0000

74157 #0
CLR = Q1
SEL = 74163.QA
0D = 74157#10.Q
1D = 74157#11.Q

74163
CLR = H (H固定なので74161を使っても動作は全く同じ)
CLK = Q1
LOAD = H
PE = H
LE = H
  • 74HC157 が 3つあり, ボタンからポートへの流れは (#1.0 @ #1.1) -> #0 -> PCE となる.
  • 74HC163 は increment counter 固定. 初期値が不定で設定ができないため、 page の確認はボタンが何も押されてないこと前提として page1.1 の D3:0 が 4'b0000 を見つけるぐらいだと思う.
  • page 0 か page 1 の切り替えは Q1 を 0->1 にする.
  • page x.0 か page x.1 の切り替えは Q0 で決める.
  • 2ボタンパッド同様 Q0 = 0 の時は D3:0 は 4'b0000 となる.

device: multitap

回路図が見つからなかったのでソースのコメントに頼る.

  • Q0 == 1 && Q1 0 -> 1 の場合 port = 0
  • Q1 == 0 && Q0 0 -> 1 の場合 port += 1
  • multitap の Q1:0 は接続されたデバイスが利用するときのみそこへ Q1:0 を送る仕組みだと思う,
  • port の値が 5 を越えた場合はどうなるか不明

device: memory base 128

serial clock = Q1
serial write data = Q0
  • シリアルデータ送受信デバイス.
  • 他のデバイス同様 Q1 == 1 の時に Q0 書込み, D3:0 の読み込みを行なう.
  • シリアルデータは LSB->MSB に順番で送る
bit  |write data (1bit)                |read data (4bits)
 0- 9|activation data 10'b10_1010_1000 |0000
10   |R/W select 0:write 1:read        |0100
11-20|set address bit 16:7 (bit6:0 = 0)|0000
21-40|set length bit 19:0              |0000
41-  |data * length                    |000d
last |?                                |0000
  • 最初に10 bit 送った後に bit10 の read data が 4'b0100 でデバイスが接続されていることを見つける.
  • addrss は RAM の address bus と同じ 17bit. bit6:0 = 0 で固定.
  • length は byte 単位ではなく bit 単位で計算するらしいので 17 (address: 0x00000-0x1ffff) + 3 (bit: 0 to 7) となる.
  • multitap との連動の場合は memory base 128 と multitap を挿す組み合わせが何通りかあってそこはいまのところよくわからない.

じゅうべえくえすとの敵が出る数の解析

メガトンコインRTAという動画でキノコングという敵が1匹でるべきである場面でそれが2匹でる, その原因は5つもバージョン違いだろう. という考察が書かれていました. 5つは多すぎでおかしいと思ったのでそれを調べました.

バージョン違いはあるが...

5個の根拠として[b]とか[h]とかついているファイル名があると書かれておりましたが、これは dump の手順を間違えているファイルが過去の混乱の遺物として残っているだけなので無視します.

かなり信憑性のある nescartdb は1つ登録があります. そこそこ信憑性のある MAME の hash/nes.xml には2つ登録があります.

この2つを見比べてみましたが、本当にバージョン違いなのか判断には困りました. 命令が増えたり減ったりしているのでそこの差分の部分をアセンブルするから命令とaddressが差分点からずれるていくものが普通のバージョン違いです. これは lda abs (3byte) 命令を jsr abs (3byte) 命令 にしていて ROM の末尾に命令を増やしています.
この手法は第3者がパッチを当てる場合は自分もよくやるんですが、その jsr 命令の中身が何かの計算の補正だったのでプログラマ当人かはわかりませんがたぶん当時の関係者が手をいれたものと思われます. *1

どちらとも敵の数は基本2匹、たまに1匹

両方のバージョンでキノコングが1匹でるはずであろうというところを確認しましたが、基本2匹、たまに1匹出るのでバージョン違いの可能性は低いと思います. (その2つ以外のバージョンがあるならべつの可能性はありそうだけど、1度生産して終わりの販売規模のこのソフトにはちょっと考えられない)

1匹でる条件を調べてみた

エンカウントで trace log を取りました. CPU address $6050 が敵の数なのはわかりました.

F969: lda ($fc), y <- 敵のテーブルのポインタらしい
F96B: lsr a
F96C: lsr a
F96D: lsr a
F96E: lsr a
F96F: lsr a
F970: sta $6050 <- bit7:6 を敵の数にしている

(中略)

8987: lda $6050
898A: clc
898B: adc $6051
898E: adc $6052
8991: rts
8940: cmp #$02
8942: bcc $8986 ;敵の数が2以上か
8944: jsr $f195
F195: lda $038e ;乱数生成
F198: clc
F199: adc #$17
F19B: sta $038e
F19E: lda $21 ;frame カウンタらしい
F1A0: clc
F1A1: adc #$b1
F1A3: adc $5000 ;φ2カウンタ bit7:0
F1A6: sta $21
F1A8: lda $5800 ;φ2カウンタ bit15:8
F1AB: eor $22
F1AD: adc $21
F1AF: eor $1f
F1B1: adc $038e
F1B4: sta $21
F1B6: rts
8947: lsr a
8948: bcs $8986 ;乱数 bit0 == 0 なら減らさないので分岐
894A: lsr a
894B: bcs $8986 ;乱数 bit1 == 0 なら減らさないので分岐
894D: lsr a
894E: and #$03
8950: cmp #$03
8952: beq $8986 ;乱数 bit3:2 == 3 なら分岐
8954: tay
8955: lda $6050, y ;敵を減らすなら $6050 で乱数 bit3:2 == 0 でいてほしい
8958: beq $8986 ;それが 0 なら減らさないので分岐
895A: sec
895B: sbc #$01
895D: sta $6050, y ;敵の数を1つ減らす
8960: bne $8986
8986: rts

乱数生成のサブルーチンはミサイルポッドの確率を出してた動画(じゅうべえくえすとの鉄パイプ乱数を観察)でもでてきたものと同じです. PC (program counter) $8955 の y の値がちょっと気になるのですが変数 $6050 は敵の数で, 変数 $6051, $6052 は未調査ですが敵に関するパラメータだと思います. (別種類の敵の数かも)

このログからの考察ですと、下記のポイントとなります.

  • 敵がでてくるときに乱数を引く
  • 乱数の bit3:0 == 0 の場合は敵の数を1減らす
  • 敵の数が1匹減る条件は 1/16 (6.2%)
  • この命令は通常敵が出るときでも実行する(ボスはやらない)
  • どちらのバージョンもここに違いはない

おまけ:ランダムエンカウントをなくす方法

FA1B: cmp $d2 
FA1D: bcs $fa22 
FA1F: inc $04c9 <= これを nop にするとランダムエンカウントがきえる 
FA22: jmp $fe05

cmp $d2 をユーザー入力にしてスタートボタンを押したときだけ敵が出るようにアレンジするとすごく遊びやすくなると思います.

*1:第3者がやるならエミュレータで動くように無理矢理書き換えるとか、ゲームを有利に進める目的なのでたぶん白