MSXのゲームROMを読み出してみました

前回MSX用のROMカートリッジからROMを取り出しました。今回は取り出したROMからデータを読み出してエミュレータで起動させてみたいと思います。

前回の記事はこちら

ROMのピン配置

取り外したROMのピン配置をネットで探してみたところ、28Pin DIPの 1Mbit のメガロムについては大体以下のピン配置になっているようです。

似非RAMディスクの作り方のサイトにも載っていました。

1Mbit = 128KiB。217 = 128*1024 なので、アドレスバスは17本もあるんですね。データバス8本と制御線(/CS)も合わせると、26本ものGPIOが必要になります。

BASEBALLから取り外した128Kbit ROMも調べてみました。128Kbit〜1Mbit ROM を並べるとこんな感じです。

容量が倍に増えるとともにアドレス線が1本必要になるので、制御線や電源用のPinがアドレス線に侵略されていっているのが分かりますね。

I/OエキスパンダーでGPIOを拡張

28本あるピンのうち、電源を除く26本すべてを制御する必要があることが分かりました。さて、どうやって制御しましょうか。

最初はシフトレジスタの74HC595あたりで作ろうかと思ったのですが秋月でちょうど良さそうなICを見つけました。その名もI/Oエキスパンダー!!

I2CとSPIの2種類あるようです。1個のICで16本のGPIOを追加することができるスグレモノ。全てのピンで入出力も個別に設定できるし、今回は使わないけど割り込みピンとしても使えるようです。このICを2個使えば32本のGPIOを制御できるのでROMの全ピンをカバーできます。今回はこれを使うことにしました。

SPIの方が高速そうですが、今回は特に速度は求めていないので値段の安いI2Cの方を買いました。

I2Cのホストにはひとまず Raspberry Pi を使うことにしました。気軽にLinuxからI2Cを使えるしROMを吸い出したあとのバイナリデータの取り扱いも楽そうなので。

Raspberry Pi の I/O は 3.3V なので5Vに変換するためI2Cレベル変換モジュールも購入しました。

ROMリーダの回路図

色々と試行錯誤して動作した回路を回路図に起こしてみたのがこちらです。(クリックで拡大)

I2Cのレベル変換モジュール AE-PCA9306 はプルアップ抵抗を内蔵しているため、外付けでプルアップする必要がありません。

MCP23017 は A0〜A2 を設定することで、アドレスを0x20〜0x27の範囲で変更することができます。2つのMCP23017でアドレスを分けることで同一のI2Cバスに接続させました。ちょっとハマったのは/RESETピンでした。オープンだと通信できたりできなかったりと変な挙動をするので、確実にHiにしておきましょう。

5Vと3.3Vの電源はRaspberry Piの1Pin(3.3V)、2Pin(5V)、6Pin(GND)から取得します。I2CのSCLとSDAはRaspberry Piの 3Pin(SDA) と 5Pin(SCL)に繋げます。結果的に1〜6Pinのみで事足ります。

元絵は Raspberry Pi Documentation © 2012-2024 Raspberry Pi Ltd より

ブレッドボードでROMリーダを作る

ブレッドボードに実装してみました。ブレッドボードとジャンパーも秋月で購入しました。

で、出来上がったROMリーダがこちら。

すごい数の配線になってしまいました・・。

左側にある緑の基板がI2Cのレベル変換モジュールです。

中央のROMを挟んで、左側がアドレスバス16本を制御する MCP23017 (I2Cアドレス0x20)で、右側がデータバスとA16, /CSを制御する MCP23017 (I2Cアドレス0x21)です。

Raspberry PiでI2Cを使う

Raspberry Piは初期状態ではI2Cが使えるようになっていないため有効化します。Raspberry Piにディスプレイを繋げてデスクトップ環境として使っている場合は「Raspberry Piの設定」というアプリでI2Cを有効化できるようです。

私はヘッドレスの Raspberry Pi OS Lite を書き込んだため、CLIの設定コマンド raspi-config で設定しました。

$ sudo raspi-config

以下の画面のように進んでいくとI2Cを有効化できます。

ここで再起動すればI2Cが使えるようになるのですが、デフォルトでは I2C のクロックが 100kHz と遅いので、1MHz に変更します。データシートによるとMCP23017は1.7MHzまで対応しているようです。

変更するファイルは /boot/config.txt です。sudo vi /boot/config.txt などでファイルを開くと以下の行が見つかるはずです。

dtparam=i2c_arm=on

これを以下のように変更して再起動します。

dtparam=i2c_arm=on,i2c_arm_baudrate=1000000

ちなみに、100kHzで1MbitのROMを読み出したところ、6分半かかりました。1MHzでは約1分でした。

i2cグループに追加

もう1つ設定しておきます。ユーザをi2cグループに追加することで sudo を不要にします。

/dev/i2c* のオーナーを確認すると、User:root, Group:i2c であることが分かります。そして、i2cグループには読み書き可能な権限が設定されています。

$ ls -l /dev/i2c*
crw-rw---- 1 root i2c 89, 1 Apr 7 21:42 /dev/i2c-1
crw-rw---- 1 root i2c 89, 20 Apr 7 21:42 /dev/i2c-20
crw-rw---- 1 root i2c 89, 21 Apr 7 21:42 /dev/i2c-21

そこで、実行ユーザをi2cグループに所属させることで、sudoなしでi2cデバイスへのアクセスを可能にします。あるユーザをi2cグループに追加するには以下のコマンドを実行します。

$ sudo usermod -a -G i2c ユーザ名

一旦ログアウトしてログインし直すかシステムを再起動すれば /dev/i2c に自由にアクセスできるようになります。

自分が i2c グループに所属しているかどうかは groups コマンドで確認することができます。

$ groups
adm dialout cdrom sudo audio video plugdev games users input render netdev gpio i2c spi

Raspberry PiからI/Oエキスパンダーを制御する

今回使った Raspberry Pi 4 では、Pin3,4 の I2C は Linuxシステムでは /dev/i2c-1 として制御できるようです。正常に結線できていれば、i2cdetect で 0x20 と 0x21 のデバイスが検知できます。

$ i2cdetect -y -r 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: 20 21 -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

i2cグループに所属させた場合は sudo は不要で実行できるはずです。コマンドがインストールされていない場合は i2c-tools をインストールしてください。

sudo apt install i2c-tools

MCP23017のレジスタも読み出してみましょう。i2cdump でI2Cデバイスの一般的なレジスタ(レジスタアドレスが8bitのもの)であれば読み出すことができます。

$ i2cdump -y -r 0-0x1f 1 0x20
No size specified (using byte-data access)
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
$ i2cdump -y -r 0-0x1f 1 0x21
No size specified (using byte-data access)
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
10: 00 00 f3 00 00 00 00 00 00 00 00 00 00 00 00 00 ..?.............

MCP23017のレジスタを読み出すことができました。他にも i2cset, i2cget コマンドで1レジスタの読み書きなどもできます。

ROM読み出しスクリプト

MCP23017を制御できることがわかったところで、読み出しスクリプトを書いてみました。プロトタイプを手早く作るのに最適なPythonで書いてみました。smbus というI2Cを制御するライブラリもあるので簡単にMSP23017と通信することができました。

上のコードは1Mbit ROMにしか対応していませんが、ちょっと改造すれば他の容量のROMも読み出せるはずです。今後改良していければと思います。

実行すると、1Mbit ROMを読み出し、rom.bin というファイル名で出力します。

読み出したROMの先頭をダンプしてみました。0x41, 0x42 で始まっています。これはMSXのROMカートリッジのヘッダを示しています。少なくとも先頭はちゃんと読み出せていそうです。

$ hexdump -C rom.bin
00000000 41 42 f0 40 00 00 00 00 00 00 00 00 00 00 00 00 |AB.@............|
00000010 45 46 07 57 ff ff ff ff ff ff ff ff ff ff ff ff |EF.W............|
00000020 ff ff ff ff ff ff ff ff 81 01 01 02 80 f6 ff ff |................|
...

MSXエミュレータで動作確認

MSXエミュレータで吸い出したROMを動かしてみました。使ったエミュレータはopenmsxです。Ubuntuの公式リポジトリに入っているので sudo apt install openmsx でインストールが可能です。

吸い出したROMをRaspberry PiからPCに転送してエミュレータで動かしてみました。

$ openmsx -cart rom.bin

すると・・・

おーーー!起動しました!

続いて麻雀悟空も

メガロムコントローラの種類は自動で認識してくれるんですね。

どちらもちょっと遊んでみましたが動作がおかしくなることもなく正常に動作してくれました。正しくROMの読み出しができたようです。

今後の妄想

MCP23017を2つ使うと32本のGPIOを制御できるので、ROMを取り外さずにROMカートリッジの状態で読み出すROMリーダを作るのも面白そうです。

MSXのROMカートリッジには50Pinの端子があります。アドレス+データで24本使ってしまうので残りの8本で制御線を制御できれば実現できそうです。

ピン配置は以下のサイトにまとまっています。

これをみると、/CS1, /CS2, /SLTSL, /IORQ, /MREQ, /WR, /RD, RESET あたりを制御できれば、ROMやRAMカートリッジの読み書きができそうです。IOポートタイプもOK。

RFSH, WAIT, INT, M1, BUSDIR, CLOCK が読み書きできないですが、ROMカートリッジとしては使わないと思うのでなんとかなりそうです。

もう1つ、いまでもかろうじて手に入る SST39SF010 などのパラレルFlashのライタ/リーダとしても使ってみるのも面白そうです。おいおいこの辺も試してみたいと思っています。

最終的には基板化もしてみたいしたいですし、Raspberry PiではなくPCと直結できるようにしたいですね。Raspberry Pi PicoなどをUSB-I2Cブリッジにすれば実現できそうです。

まとめ

I/Oエキスパンダー(MCP23017)を使ってROMリーダを作り、MSXカートリッジから取り出した 1Mbit ROM からデータを吸い出すことができました。

吸い出したROMデータをエミュレータで実行して動作させることもできました。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です