FPGAでナムコの古いアーケードゲーム基板を再現する「MPGKPlayer」を入手した。所持基板であるドルアーガの塔を動作させるまでに必要な手順などをここに記する事にした。
先に結論から言うが、これはかなり凄い再現性であった。まずはゲームを動かす前段階、導入の仕方について書いていく。
MPGKPlayerの構成
メイン基板
MPGKPlayerはJAMMA接続するメイン基板と、ゲームタイトルのROMデータを搭載させたサブ基板の2枚構成からなる。
メイン基板にはリセットスイッチとテストスイッチ(S1のみ)、ディップスイッチが2個ある。また、音量ボリュームを搭載している。これらは実基板のそれと同様の働きをする。
よく見るとホロシールの右下に5V/3.3Vの切替ジャンパがある。これはRGB出力をアップスキャンコンバータ等でHDMIなどにしてモニターに映し出した際に画面が明るすぎた場合の対策用になる。デフォルト(5V)であっても実基板より暗めに調整はしてあるが、それでも厳しい場合はこのジャンパを3.3V側で短絡させるようはんだ処理(ソルダージャンパ)をすると良い。
サブ基板
一方で、サブ基板のカートリッジ側はシンプルな作りだ。32pinのソケットが付いていてEPROMが1個取付けられる。ただしEPROMの種類と書き込んだゲームタイトルに合わせたはんだ処理が必要となる。
27Eとピン互換のある27C040のEPROMに、ドルアーガの塔のデータを書き込んだ場合でのはんだ処理をお見せする。必ず27Eとシルクされている”上部2か所”をはんだ処理すること。処理後は念のためテスターで導通確認すると良いだろう。
ROMの吸い出し方(実例)
ドルアーガの塔(新Ver)でのEPROM・BPROMの一覧と、書き込み先EPROMのアドレスとの紐づけとをまとめたものを下図に示す。他のゲームタイトルの場合は各々で読み替えていただきたい。
尚、この一覧表は私が実基板を基に調べたものであり、基板に実装された部品に違いがあったり、バージョンの違い、その他タイトルとの整合性といった考慮は一切していない。あくまで参考として見ていただきたい。
EPROMを読み込む
さて、ここからが当ブログの本題と言えよう。どのようにしてこれらのROMを実基板から吸い出したら良いのか。まずはEPROMの方だが、これは比較的簡単だ。Amazonなどで購入できる安価なROMライタを使用する事だ。
私はこの「TL866-II Plus」を利用している。OSは最新のWindows11にも対応。付属ソフトの「Xgpro」を使って対応しているEPROMを選択して読み取っていく。
「Select IC」でICの種類を選択する。そのあとに「Options」からPin DetectとCheck IDのチェックは外しておく。もしうまく読み込み出来なくても、他のメーカーの互換型番を選択すると読み込めるので設定を都度変えて読み込む。
読み込めたら、一覧表のROMに合わせてbinデータを決めて保存する。これを各EPROMで行っていこう。
BPROMを読み込む
続いてBPROMになる。EPROM同様に「BPROMに対応したROMライタを使用する」のが一番の近道ではあるが、安価で売られているEPROMライタには、MPGKPlayer対応ゲームタイトルで使用されているBPROMには非対応である場合がほとんどだ。そして数少ない対応済みのROMライタは高価なものが多いため読み込むための難易度はグンと高くなってしまう。
そして今回は下記の3種類のBPROMがターゲットとなる。EPROMを読み込んだ「TL866-II plus」の対応ROMリストには残念ながらこれらBPROMは含まれていなかった。
先に答えを載せてしまうが、私は下記のデバイスを自作してBPROMの読み込みに成功したのであった!
ラズパイで簡易BPROMリーダー
ラズパイを簡易BPROMリーダーにするために必要な部品は下記の通りとなる。
ラズパイ 1台
ブレッドボード (サンハヤト SAD-101等) 2個以上
8回路3ステートバスバッファ(TC74HC541AP) 2個
ゼロプレッシャーソケット(※推奨) 1個
金属皮膜抵抗 50kΩ 数本(10本以内)
金属皮膜抵抗 100kΩ 数本(10本以内)
ジャンプワイヤ オスーオス 適量
ジャンプワイヤ オスーメス 適量
これらを使ってデバイスを組むための回路図、ブレッドボード配線図、実行プログラムと順に説明していく。
回路図
ラズパイで送受信する信号は3.3Vである。BPROMが送受信できる5V信号とのレベル変換のために3ステートバスバッファと抵抗による分圧回路を取り入れることで読み込みを実現している。
ブレッドボード配線図
回路図の内容を基にブレッドボードの配線をしていく。BPROM(型番:MB7122)を読み込ませたい時の接続例となる。勿論だがピン配置が違うBPROMを読み込みたい場合は配線等を組み替える必要がある。
私の場合、万が一にでもBPROMのピンが弱っているとブレッドボードに抜き差しする際に破損してしまう事を恐れて、ゼロプレッシャーソケットを間に着けている。
※ゼロプレッシャーであっても劣化したピンにダメージを与えていたため、このあと2本ピン移植を行う羽目になった
プログラム
ブレッドボードの配線が終われば、最後にBPROMからデータを吸い出すPythonプログラムを作成する。
import RPi.GPIO as GPIO import time, re from struct import unpack, pack # MB7052 PROM is a 256words x 4bit TTL PROM import os #rom5.bin initialize if(os.path.isfile('rom6')): os.remove('rom6') if(os.path.isfile('romMemory_MB7052_ROM6.txt')): os.remove('romMemory_MB7052_ROM6.txt') #GPIO.cleanup() GPIO.setmode(GPIO.BCM) #Use chip numbering scheme #G! is Chip enable G = 11 # GPIO out used for G! GPIO.setup (G, GPIO.OUT) #G! #Set the chip in standby mode: GPIO.output(G,1) #G! - high #Address pins set for output A0:A7 outputs = [2,3,4,17,27,22,10,9] for n in range(len(outputs)): GPIO.setup (outputs[n], GPIO.OUT) #Data pins set for input (pull down for zeroes): O1:O4 inputs = [18,23,24,25] for n in range(len(inputs)): GPIO.setup (inputs[n], GPIO.IN, pull_up_down = GPIO.PUD_DOWN) def dec_to_bin(x): return int(bin(x)[2:]) # Set address and read memory (4096bit/512 addresses) for address in range(2**len(outputs)): print 'Setting Address:' + str(address) print (dec_to_bin(address)) for i in range(len(outputs)): bitpos = i bit = (address >> (bitpos))&1 # Lowest bit first #print 'Bit Posn :' + str(i) + ' value ' + str(bit) GPIO.output(outputs[i],bit) print "Turning on the chip and setting it to output mode." GPIO.output(G,0) #CE - low - turn on the chip time.sleep(0.2) # Read Inputs result = [None] * len(inputs) binary = 0 for n in range(len(inputs)): result[n] = GPIO.input(inputs[n]) binary = binary + 2**(3-n)*result[n] print result print hex(binary)[2:] GPIO.output(G,1) #CE - high - standby mode # Write to file, print LSB - MSB left to right print 'Writing Result to File: romMemory_MB7122_ROM6.txt' mybin = open("rom6","ab") with open("romMemory_MB7052_ROM6.txt","a") as myfile: myfile.write('Memory Address: ' + str(address) + ' Value (LSB-MSB) : '+ (''.join(str(result)))) myfile.write('\n') mybin.write(pack('b',binary)) print 'Setting Outputs to 0, cleaning up' # Set Address outputs to 0 for i in range(len(outputs)): GPIO.output(outputs[i],0) mybin.close() myfile.close() GPIO.cleanup() print 'Done'
プログラム及び回路図は上記サイトのものを使用させていただいた。
注意点としては、BPROMの型番ごとにプログラムを一つ作成する必要があるということ。一つのプログラムでいくつものBPROMに対応させようとすると煩雑になる。
あまりプログラミングに詳しくない私が、対応していないBPROM用のプログラムを誤って実行させることによってBPROM等を破損させるというリスクを少しでも軽減したいのが目的だ。
いつか需要があれば型番を選択して読み込むプログラムとして作り直したくもなるが、そのための配線もそれぞれのBPROMに対応したものを用意しなくてはならず実現は難しい所だ。手間を惜しまず一つずつ着実に読み込む方が良いだろう。
EPROMに書き込む
全てのROMデータを吸い出して保存ができたらEPROMに書き込みを行う。ドルアーガの塔(新Ver)における、各ROMの開始・終了アドレスとファイルの関係性をもう一度示す。
合計が512kバイト(4Mビット)になるよう調整するためにはブランクデータが3か所必要となる。これはLinuxのシェル上で、任意のコードで埋めたバイナリファイルを下記のコマンドにて作成する。私は「C0」でブランクデータを作ったが任意のコードに変更して作成しても構わない。一目見てブランクだとわかるようにすると良いだろう。
dd if=/dev/zero of= blank1z bs= 18912 count=1
dd if=/dev/zero of= blank2z bs= 425984 count=1perl -p -e ‘s/\x00/\xC0/g’ blank1z >blank1
perl -p -e ‘s/\x00/\xC0/g’ blank1z >blank2
ROMデータとブランクデータを同じフォルダに置いてバッチファイルを実行する。これはWindows上のDOSプロンプトなどで下記のコマンドにて作成する。
copy /b rom1 + rom2 + rom3 + rom4 + rom5 + rom6 + tom7 + rom8 + blank1 + rom9 + rom9 + rom10 + rom10 +blank2 druaga.rom
これで512kバイトのdruaga.romが出来上がった。あとはこれをEPROMにROMライタで書き込んだものをサブ基板に取り付ければ完了だ。
最後は少し端折ってしまうが、この後続きの「動作編」を作成するのでそのときにもう少し追記することとする。一旦この「導入編」はここで終わらせる。
コメント
導入編の作成ありがとうございます!参考にさせていただきます
❏やはりBPROM読み出しが少しハードル高めですね…
❏フランクファイルの作成は fsutil を試してみようと思います
❏ブランクファイルを間に挟む場所がタイトルごとに違うので注意ですね
❏copy /b のコマンド、久しぶりに見た気がします
コメントありがとうございます。
自分なりにやりやすいコマンド使ったので、perl以外にもさまざまなやり方があると思っています。
fsutilですか、使ったことないので一度試してみます!