Namco 340 を使ってるファミカセ基板の flash 化

ファミスタハッカーの方からご依頼を受けて作りました. 163 とは異なりかなり単純化されているので改造作業の難易度は普通です. ただし改造できる基板の入手難易度は高いです.

配線

基板の型番は CS 0003 で違うものは via の位置が異なる可能性が高いです.
f:id:na6ko:20190318111220j:plainf:id:na6ko:20190318111215j:plain

[Program Flash Memory]
A18 = fixed (5V か GND で固定する)
A17 = 340.ProgramROM.A17 (ピン番号8)
A16 = 340.ProgramROM.A16 = Mask ROM ピン番号 22
A15:0 = Mask ROM から変更なし
D7:0 = Mask ROM から変更なし
CE# = cardedge.CPU.ROMSEL# = 340.CPU.ROMSEL# = そこらへんの via からとる
WE# = cardedge.CPU.R/W = (同上の流れ)
OE# = 340.ProgramROM.EN# = Mask ROM ピン番号 20

[Character Flash Memory]
A18 = fixed
A17 = 340.CharacterROM.A17 (ピン番号45)
A16 = 340.CharacterROM.A16 = Mask ROM ピン番号 22
A15:0 = Mask ROM から変更なし
D7:0 = Mask ROM から変更なし
CE# = cardedge.PPU.A13 = 340.PPU.A13 = via は ROM の下でとれませんでした
OE# = cardedge.PPU.RD# = 340.PPU.RD# = そこらへんの via からとる
WE# = cardedge.PPU.WR#

補足

  • 両方の ROM とも A18 はあるみたいですが、実際に使う場面がなさそうなのと自分が2011年に確認した分には不確定だったのでなしにしました.
  • いまもガバガバなんですけど anago からでてくる昔書いた英語の文法がもっとガバガバで恥ずかしいです.
  • 163 使用基板の flash 化の改造記事を今見たら狂気じみていて頭がおかしいです. 163 系の基板と仕様のバリエーションも豊富で混乱します.
  • 340 (175) のソフトは多いので flash cartridge を作る価値はアリだと思いました.

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 でも十分に早くこのような問題はありません.