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

前回 ADPCM memory controller の改修でシャーロックホームズの探偵講座が動くようになりました. しかし内部の動画の再生で読み込みが早くてそれの調整をしていました. こういう調整は特定のソフトにだけ専用のその場しのぎのパラメータを振ることで短期的に解決できますし、ゲームを遊びたいだけの人は高い再現性と誤解してくれます*1. 当プロジェクトは闇の部分をなくすようにしております.

話がそれました. この手の同期をとらないプログラムは読み込み時間の精度をあげねばなりません. シャーロックホームズがちゃんと動いても HuVideo がちゃんと動かないので原因を調べていたら至近距離の seek time の計算値が間違っていることに気づきました. シーケンシャルアクセスの続き扱いの read command なので seek time ではなく setup time の名前が適切ですが、処理上は seek time と呼びます.

ここまでくると"CD-ROM2 の読み込み時間の厳密な再現の必要性 / その3"で調整した値は意味がないものになってしまいます. 先述の動画系ソフトに合わせて ADPCM 再生と書き込みの同期をとらない F1チームシミュレーション PROJECT.F とブランディッシュもやり直しとなってしまいます.

*1: (暗黙的パッチ当てまくりのエミュレータ名)では正しく動くのですけどといわれると説明するのに困ります

ADPCM memory controler の改修

前回 ADPCMfifo の深さが 0x400 word ありましたが、他の fifo と比べて更新頻度がとても遅いので fifo の深さを 0 M4K で最小の深さの 4 word にしました.

4 word にしたところで dead lock している場所が見つかったので直しました(厳密には知っていたけど問題ではないと思い込んでいたので放置してた). 先日シャーロックホームズの探偵講座を解析したら別の場所での dead lock がかかっていたのでそれを直したら、放置状態となっていた複数のソフトの問題も改善いたしました.

EP2C5 は前回から 1 M4K 減りましたので subchannel 用 fifo も割り当てられそうです. 去年ちょっとやってみようとしたのですが、 BIOS の挙動がよくわからず解析したいのに subchannel fifo をサポートしてる mednafen のデバッガが使いづらくてよくわかりませんでした. これはあとで考えます.

ADPCM memory controller の仕様

かなりの試行錯誤がありましたがようやく完成した気がしますので現在の実装の仕様を書いておきます.

基本的なこと - 理論

UperGrafx はエミュレータと異なり大半をハードウェアで実装しております. ソフトのエミュレータの場合、仮想的に時間を止めて共有RAMを自由に操作できるのですが、時間を止められないハードウェアの場合は優先度を決めたりバッファにため込んで処理をする必要があります.

このためソフトウェアエミュレータソースコードは私が読んで資料にはなりますが、それを手直ししてコンパイルしてハードウェアにあてるというのは根本的に原理が違いますのでできません. だから開発に手間と時間がかかっております.

基本的なこと - データの流れ

下記の流れです.

  • 前段の入力 CD-ROM fifo と 6280 からの data を受け取り、 ADPCM 用 RAM へ書き込む
  • 後段への出力は 6280 と MSM5205 (decoder) の2つあります.
    • 6280 は ADPCM 用 RAM を読みに行きます.
    • MSM5205 は再生時に定期的にデータを読みに行きます.
  • これらの処理は優先度を決めますが、基本的にすべての処理要求を完了する必要があります.

処理要求

各方面からの要求は複数あります.

  • 6280
    • RESET
    • voice decoder play request (play)
    • length register latch
    • read address register latch
    • write address register latch
    • read data to 6280 (write)
    • write data from 6280 (read)
  • CD-ROM fifo
    • write data by DMA (write)
  • MSM5205
    • read data by decoder (play, read)

memory controller が動いているときも 6280 の要求はとりこぼさないように内部のレジスタで状態を管理する必要があります. idle 状態になったとき上記の 9 通りの処理要求に優先度をつけて順番に処理します. 優先度は下記となっております.

  • RESET
  • play 中で decoder 行き fifo が full ではないとき
    • fifo への書き込み (READ)
  • DMA 転送 が有効で CD-ROM fifo が empty ではないとき
    • RAM への書き込み (WRITE)
  • READ address の更新
  • WRITE address の更新
  • 6280 からの読みこみ (READ)
  • 6280 からの書き込み (WRITE)

memory 転送途中でも制御レジスタと入出力先の fifo の状態で処理を中断する必要があります.

  • RESET
  • PLAY register = 0
  • PLAY ための READ
    • decoder 行き fifo が full になる (4 word になったのでこの時間は短い)
  • DMA WRITE
    • play 途中に decoder 行き fifo が empty になる
    • CD-ROM fifo が empty になる
  • (6280 からの読み書きは 1 byte ですぐおわるので中断しません)

READ と WRITE は RAM を読み書きする間, PLAY は decoder が有効な間は 1 になりまして、status register に反映が必要です. READ と WRITE は(私の実装では) 同時に 1 になりませんが、 READ と PLAY, WRITE と PLAY は同時に 1 になります.

address register と length register

read address register は play と read で RAM を読み込んだときに byte 単位で increment します. address 0xffff の次は 0x0000 になります. length register は RAM を読み込んだときにbyte 単位で decrement します. address 0x0000 の次は 0x0000 のままです.

write address register と length register は write で RAM を書き込んだときに byte 単位で incrementします. address 0xffff の次は 0x0000 です. length register は 0xffff の場合は更新せずに 0xffff のままです.

ADPCM memory controller からの IRQ2 は細かい部分ははしょりますが下記となります.

  • 再生全部完了: length register == 0 && decoder 行き fifo.empty == 0
  • 再生半分完了は play & ~(length register bit 15)

特記事項

標準の BIOS を利用せずに CD ソフト側でレジスタを操作することがあります. こういう場合 CD ソフト側の出来がヒドイ*1 での制御手順が BIOS と異なる傾向がありますが無難に処理をやりきる必要があります.

シャーロックホームズの探偵講座では下記の対応が必要です.

  • 再生途中に address register や length register を更新しようとするのでできるだけ早めに更新する必要があります.
  • 6280 からの読み書きには完了フラグがありますが、書き込み完了フラグは無視しているのでデータはできるだけ書き込む必要があります.
    • 実機ではある程度取りこぼしている気がする
    • エミュレータでは取りこぼしてないようだ
    • UperGrafx では処理を間に合わせたので取りこぼしはないとおもう

*1:遊んで名作とか信頼できるメーカーとか関係ないです

EP2C5の内部資源削減おわり

sound_mixer

618 lc, 4 M4K -> 456 lc,3 M4K (注:sound_mixer の外に移動したものもあるので単純比較しづらい)

  • sound mixer は sound clock 側の処理を cpu clock 側での移動をしました. これによって cpu clock 側から sound clock 側へ制御データを渡す必要がなくなり dual clock fifo (浅いので 0 M4K) の削除をしました.
  • ADPCM 専用 fifo と CDDA 専用 fifo はともに dual clock でしたが single clock になり, CDDA 専用 fifo は CD-ROM fifo と兼用になりました.
  • cpu clock から sound clock への fifoADPCM と CDDA を加算していましたが、 0 M4K の浅い fifo で 17 bit 渡すよりかは浅くても 1 M4K の 25 bit にして 2 音源を加算するのは sound clock 側にしました. これにより 24bit 符号あり clipping 加算回路が1つ減りました. (なお加算回路が1個減っても雀の涙)
  • fadeout contoller は計算したところ更新のため時間精度をそこまでだす必要がなさそうなので精度を落としました.

sound_mixer.msm5205 (外)

msm5205 へ入力する更新頻度のための分周用定数テーブルは 16 word 10 + 3 +3 bit となっておりましたが. 16 word 1 + 3 + 3 bit に削減しました. この 10 bit の値は下記の式で計算してました.

(44.1 * 10 ** 3 * 128) / 9.214*10**6 / n 
n = 1 から 16

細かい近似の部分の説明は省略しますが、 n = 16 の値だけを算出しレジスタに書かれる n は param 回ループを回すと param/16 倍となり 10 bit の計算結果はいりません.
これでも 40 lc 程度削りました. (雀の涙)

sound_mixer.msm5205 (内)

2 M4K -> 1 M4K
PCM の値を更新する定数テーブルがありまして、 address 49 * 8 words, data 12 bit の ROM が必要でした. 計算は mame のコードを手直ししたものが下記になります.

/* loop over all possible steps */
for (step = 0; step <= 48; step++) {
	/* compute the step value */
	int nib;
	int stepval = floor (16.0 * pow (11.0 / 10.0, (double)step));

	/* loop over all nibbles and compute the difference */
	for (nib = 0; nib < 16; nib++) {
		int16_t v = 0;
		if(nib & 4){
			v += stepval;
		}
		stapval >>= 1;
		if(nib & 2){
			v += stepval;
		}
		stapval >>= 1;
		if(nib & 1){
			v += stepval;
		}
		stapval >>= 1;
		v += stepval;
		if(nib & 8){
			v *= -1;
		}
		voice->diff_lookup[(step<<4) + nib] = v;
	}
}

nib の bit3 は 1 倍か -1 倍なので動的に計算します. nib の bit2:0 は乗算ぽいのですが右シフト演算が入るので乗算ではないです. ためしに乗算に置き換えてみたら voice はちゃんとでませんでした.

この式通りにハードウェアで加算する場合 fllor() の演算結果を 49 word x 12 bit の table にするので先述のように単純に ROM にすると 2 M4K も食います.

計算結果を眺めていたら 1/3 ぐらいは上位ビットが 0 で埋まっているのに気づきました. ROM の data width は 9 bit なので address に応じて有効な data bit 数を 6, 9, 12 にわけまして, 6 bit の領域の上位に 12 bit の bit11:9 を埋めるなどで詰めていったら 9bit x 512 word の 1 M4K に収まりました.

単純な ROM とは違いますが定数テーブルや加算回路がなくなりまして MSM5205 の回路使用量は半分になりました. (なお雀の涙)

videotomcu:vtom

1 M4K -> 0 M4K
使用していたのは 2bit で深さ 4 word だけでしたので logic cell にしました.
その代わりに audio_mixer の音声データ fifo に 1 M4K を割り当てました.

pce_bridge_cpu_vce.colorram

2 M4K -> 1 M4K
単純に無駄だったので減らしました.

終わり

  • audio_mixer のソースはかなりきれいになりました. 正直な感想は回路数は大して減ってないのがつらいです.
  • これらの作業により 3 M4K を捻出できましたので、 FIFO の深さを 0x800 byte, 4 M4K にすることができます.
  • なんで 2019年に EP2C5 にマジになっているのか、設計時点で EP2C5 を選んだことをすごく後悔しました.

MSM5205 のデコードの部分は処理速度を求めるわけでもないので、MCU に処理させても良さそうです. ソフトならこんなに無理して定数テーブル削るなんてやる必要ないですから. 構造的にはハードで転送をしているのでそこだけソフトでやるのはすごく手間なんでやりませんが、1から作り直すならエミュレータでいいかなと思いました.

data port の分離準備

CD-DA と CD-ROM の fifo の統合は試してみたところ、 CD-DA 再生中に制御コマンドで読み込む data に CD-ROM fifo を使っているので簡単に統合できないということが判明しました. 各 command の制御を自分のファームウェア(C)のソースコードをみながらここに書くのでそれをみて対策を考えていきます.

CD-ROM2 -> 6280 の scsi data port のみの記載で seek などの disc 操作は問題に関係ないので無視します.

command 0x00, 0x0d8, 0xd9, 0xda

  • ack_wait_out() で status 1 byte 転送

command 0x03, 0xdd, 0xde

  • command 受付後, datasend_nointerrupt() で data x byte 転送
    • command 0x03 -> 10 bytes
    • command 0xdd -> 10 bytes
    • command 0xde -> command buffer offset 1 の内容で data size がかわる. 0 -> 2 bytes, 1 -> 3 bytes, 2-> 4 bytes
  • datasend_nointerrupt() 内部の ack_wait_out() 呼び出しで status 1 byte 転送

command 0x08

  • command 受付後, ハードウェアにより fifo へ書き込み.
  • 準備完了後 data 転送開始. ACK 端子のハードウェア自動制御で高速転送
  • 転送完了後 ack_wait_out() で status 1 byte 転送

各 command status 送信後共通

  • ack_wait_out() で message 1 byte を送信
  • datafifo_empty_wait(); があるけどこれいるんだっけ?
  • ack_wait_out() で 応答後 scsi bus 解放

追記分: 対応完了

  • 該当のデータは入力源はすべて単なるレジスタとして実装しなおし、これらは MCU 側でソフトウェアで ACK を見張って handshake で data を渡すことにしました.
  • これいるんだっけ?と書いた部分は ACK の同期の代わりになっていましたが潜在的なバグだったのでちゃんと ACK の同期としました.
  • この問題をなおしたら fifo の統合ができました.

EP2C5の資源不足を切り詰める

前回の異様に遅い data packet の対策ですが、異様に遅いとはいわないものの転送途中に fifo が空になる状態を見つけてしまいました. そこで 0x200 byte の深さの 1 data sector 0x800 byte まで増やしてみようという試みです.

しかし資源不足が深刻なため無駄に使っている RAM をどれか減らさねばなりません. 現在の使用状況を記載して考えることにしました.

audio_mixer.adpcm_fifo_singleclock

  • 1 M4K, 4bit x 0x400 word
  • single clock

先日から地味に行っている audio_mixer の簡素化で dual clock から single clock に変更しました. それはいいとして ADPCM data は他と比べて読み込みの頻度が遅いので 0x400 word もいるのか疑問です. ある程度深さがないと CD からの追記が間に合わなくなります.

audio_mixer.cdda_fifo_single

  • 1 M4K, 17bit x 0x100 word
  • single clock

adpcm 向け fifo 同様 single clock に変更しました. この fifo はなくせます. 詳しくは cdrom_fifo で説明します.

audio_mixer.msm5205_romtable

  • 2 M4K, 12 bit x 0x200 word
  • single clock

4 bit の memory data から 12 bit の voice data を算出する過程で利用します. 実使用は 12bit x 0x0188 word のためですから使ってない領域も結構多いのですがつめたらなんとかなるのでしょうか...?
この ROM を使わなくても動的に計算することは可能ですが 120 logic cell 増えます.

mcu.boot_inst_400, 同_100

  • 5 M4K, 16bit x 0x500 word
  • single clock

MCU の起動から、外部の serial flash memory に入っている命令データを外部の RAM に書き込みます. また MCU に入力された7種類の割り込みの処理もここでやりまして、内部 ROM で片付けるか、外部 RAM で処理するかになります.
内部 ROM は USB 通信経由での flash memory の書き込みをサポートしますので内部データ更新利用で必要です.

命令使用量は 0x500 word のうち 0x4a8 word ぐらい使っておりまして,はみ出ている 0xa8 word をなくせるか何度も検討していますがこれ以上命令を減らすとソフトとして正常に動きません. ある程度ユーザーへの利便性を犠牲にしてまで命令を減らすことも考えましたが、削れるのは 0x80 word 程度でした.

mcu.boot_data

  • 2 M4K, 8 bit x 0x400 word
  • single clock

現在の使用量は 0x252 bytes ですので 0x100 bytes 程度削れば 1 M4K として運用できる可能性があります. この RAM 領域は空きが多かった経緯がありましたが、最近 TOC data を RAM に全部コピーするようにしましたので、300 bytes も RAM を使うようになってしまいました.

変数はともかく定数テーブルの使用量も多いためそこを削れるか試算したところ 0x60 bytes 程度は削れそうです. しかし stack の容量としては 0x10 bytes は不足です. stack を使いすぎると変数を上書きしてしてしまうのでもっと削る必要があります.

以前のように TOC data を RAM にコピーせずにメモリーカードから読みに行くことも一応可能です. 互換性では CD 読んでる途中に TOC 読みに行くと CD が止まっちゃうのであまりよくないのですがそれが明確な問題にはなっていませんでした. ただその命令や専用レジスタをばっさり削除したので戻すのは気が進みません.

mcu.mcu_rs232_rx

  • 1 M4K, 8 bit x 0x200 word
  • single clock

PC からの USB 経由でのデータ通信に必要です. fifo をなくす場合は PC から 1 byte ずつ同期をとる必要があり、あまりよろしくありません.

pce_bridge_cpu_vce.colorram

  • 2 M4K, 16bit x 0x200 word
  • dual clock, dual port

6280 から vce 内部の color RAM を監視し、同じデータを保持します.
6280 側から書き込み, 720p 側から読み込みを行いますので dual clock かつ dual port となります.

この RAM は 1 M4K に削れます. data width を 16 bit とってますが, 必要なのは 9 bit だけです. 9bit x 0x200 words の場合は 1 M4K にできます. 16 bit のほうを使っていたのは bytemask 端子がついているので制御が楽なことと, 9bit のほうをちょっとやってみたらうまくいかなかったしそのときは容量に困っていなかったので放置し、忘れていました.

pce_cpu_linerfetch.backupram

  • 4 M4K, 8bit x 0x800 word
  • single clock, dual port

PCE 用の backup RAM です. UGX-02 で FeRAM をパラレルからシリアルに変更した都合で、シリアルのバスは PCE の PCE と直結はできないため FPGA 内部にパラレル RAM を設けて動くようにしています.
物理的な設計の話になりますがパラレルの FeRAM が高騰した上に低品質という大変な目に遭いまして、これは 4 M4K も使いますが必要です.

pce_cpu_linerfetch.cdromfifo

  • 1 M4K, 8bit x 0x200 word
  • single clock

メモリカードの CDROM data を fifo にいれて PCE が読み込みます. 問題の原因はこの fifo の深さが 0x800 word ほしいということでした.
CDDA fifo は2日前まで dual clock でしたので別の fifo として独立しておりました. CDDA と CDROM は並列に動くことはありません signle clock 同士であるのなら 2つの fifo は統合して深さを 0x400 word にしたほうがいいです.
そして他のブロックから 2 M4K を確保して 4 M4K 割り当てれば当初の目的は達成できます. (subchannel fifo は独立してるんでしょうけど増やせば CD-G も見れます)

video.pce_bridge_vce_720p

  • 2 M4K, 16 bit x 0x200 word
  • dual clock

VDC から出てくる data (= color RAM address) を 720p 制御側にクロックをまたいで渡す fifo です. 720p が読み込み処理をする間に fifo はたまっていきますのでこの深さが必ず必要です.

video.chroma_y

  • 1 M4K, 8 bit x 0x200 word
  • single clock

VCE で白黒モードのときに RGB からモノクロの値を算出するときに使います.
PCE ではアナログ RGB では適用されません. PCE 側では NTSC でアナログ回路の場合は簡単に色情報をなくせるのでついてる機能です. ディジタルだとわざわざ回路の追加が必要です.
モノクロ機能を有効に使っているソフトもなかったはずなので極端なことを言えばなくしてもいいと思います.

videotomcu:vtom

  • 1 M4K, ? bit x 4 word
  • dual clock

720pで frame buffer が空いてる時間に MCU が instruction RAM として使うためのタイミング調整です. ここは深さは全くいらないのですが, M4K 使わずに logic cell に割り当てれば 0 M4K にできます. もちろん logic cell 不足にも悩んでいますので割り当てるかは慎重に行う必要があります.

対処すべき順番

ここまで書いてなんとなくわかりました.

  1. pce_bridge_cpu_vce.colorram の data width 9 bit 化
  2. cdda と cdrom の fifo の統合
  3. audio_mixer.msm5205_romtable, video.chroma_y, videotomcu:vtom のうちどれかを削れるか考える

SDCard の不調

開発をしている機材で動作がおかしいメモリーカードがたまにあります. 公開当初はこちらの実装が悪いので修正というのが多かったのですが、最近2例メモリーカードの方が悪いというのがありましたので掲載いたします.

注意

記載での使用モードは SPI mode での確認です. 各カードは1枚ずつの確認のため同じ型番のものがすべて該当するかは不明です.

CMD12 の規格違反, AUSDX64GUICL10-RA1

CMD12 (STOP_TRANSMISSION) は CMD18 (READ_MULTIPLE_BLOCK) 発行後に停止するコマンドです. このコマンドの後に R1b response がくるはずですが、このカードはそれがきません. R1b response を無視すれば意図通りには動くのですが本当のエラーの時の対処ができないので無視の処理はいれてありません.

異様に遅い data packet, SP064GBSTXBU1V20BS

CMD18 発行後、 data packet 単位にデータがきて、 data packet の区切りには待ち時間がいくつかあります.その時間や読み捨てるデータのために CDDA 再生には fifo をもうけてあります. そしてこのカードは CDDA 再生途中に fifo が空になりました.
調べたところ特定のセクタで次の data packet がくるのに 50 ms もかかりました. fifo の深さを 16 倍にすればこの遅さに耐えられますがいままでこのような不具合はみたことがなくカードの不備と判断いたしました.

これは seek 時間ではなく単なるシーケンシャルアクセスです. よくアクセス速度にかかれる UHS-1 とか CLASS 10 とかは SPI mode なので関係ないです. それと比べてとても遅いであろう MMC でもこのようなことは起きませんでした. もちろん CDDA 再生としては MMC でも十分に早くこのような問題はありません.

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:手元にイメージがないんですがネクスザール(通常版)もそうな気がします