エミュレータも良いけれど実機プレイもしてみたい。そんなあなたにオススメなのはCUBIC STYLEさんの同人ハードウェア3点セットです。必要なレシピは以下のとおり。
電子工作取扱店にて入手
50分ぐらいでサクっと環境は整いますので以下に手順を説明します。
ホスト名 | raspberrypi(デフォルトでOK) |
SSHを有効化する | パスワード認証を行う |
ユーザー名とパスワードを設定する | ユーザ名:pi パスワード:おまかせ |
Wi-Fiを設定する | SSIDとパスワードを入力 |
Wifiを使う国 | JP |
ロケール設定をする | タイムゾーン:Asia/Tokyo キーボードレイアウト:jp |
sudo apt update sudo apt upgrade -y sudo apt dist-upgrade -y sudo apt autoremove -y sudo apt autoclean sudo reboot
wget https://project-downloads.drogon.net/wiringpi-latest.deb sudo dpkg -i wiringpi-latest.deb
sudo raspi-config
「3 Interface Options」を選択。「I4 SPI」を選択。「Would you like the SPI interface to be enabled?」と表示されるので「Yes」を選択。「The SPI interface is enabled」と表示されます。「 OK 」を押したらメニュー画面に戻されるのでTabキーを使って「Finish」を選択。raspi-configを終了させます。
pi@raspberrypi:~ $ ls /dev | grep spi spidev0.0 spidev0.1
wget http://cubic-style.jp/rpa/raspiadvrw.deb sudo dpkg -i raspiadvrw.debrpaコマンドはroot 権限が必要なのでrootでログインするか、sudoで実行してください。
pi@raspberrypi:~ $sudo rpa Copyright (C) 2022 CUBIC STYLE rpa [-w gbarom] [-r dstfile] [-l size] [-L size(MB)]-c [-a address] -s Backup memory mode (Sram, Fram, Flash, ...Not yet supported EEPROM) -c ROM info -r <filename> Read ROM(or backup memory) -w <filename> Write ROM(or backup memory) -e block erase -E chip erase -B blank check -d dump -a rom address -b block address -n erase block num -h this help Main ROM mode => Cartridge type: Cubic Flash Cartridge Size: 256 Mbit Device: MX29GL256F Game title: test
以下、説明書からの抜粋です。
$ sudo rpa -r game.gba
$ sudo rpa -w game.gba
フラッシュカートリッジは書き込む前に、Flashを消して空の状態にする必要があります。フラッシュカートリッジの消去は-E オプションです。
カートリッジのバックアップメモリの読み書きは-sオプションを指定します。-s -rオプションで、バックアップメモリに保存されたセーブデータを保存します。-wオプションでセーブデータを書き込めます。
$ sudo rpa -s -r game.sav
両コネクタの端子が全結線されている通信ケーブルで本体と接続する必要があります。GBポケット用通信ケーブルを用いると、そのままのケーブルでマルチブートが可能です。GBA専用通信ケーブルは対応していません。
$ git clone https://github.com/cubicstyle/multiboot.git $ sudo multiboot mb.gba
$ sudo shutdown -h now
コマンド入力後、緑のLEDランプが点灯しなくなるまで待ちます(30秒ぐらい。あとは電源ケーブルを抜いてください。
ロムファイルを転送するにはTera Termウィンドウにドラッグ&ドロップします。OKボタンを押すとホームディレクトリに転送されます。他の方法としてはsambaを使うのもいいかもしれません。
#include "flash.h" #include "bg.h" //--------------------------------------------------------------------------- EWRAM_CODE void FlashInit(void) { BgDrawPrintf(0, 0, "MemoryBackup Cubic Test"); } //--------------------------------------------------------------------------- EWRAM_CODE void FlashExec(void) { if(FlashIsExist() == FALSE) { BgDrawPrintf(0, 2, "Flash not found"); for(;;){} } BgDrawPrintf(2, 1, "Type : Flash"); BgDrawPrintf(2, 2, "Id : %x", FlashGetId()); BgDrawPrintf(2, 4, "Erase 0, 1, 2"); FlashEraseSector(0); FlashEraseSector(1); FlashEraseSector(2); FlashByte(0x0000, 1); FlashByte(0x0001, 2); FlashByte(0x0002, 3); FlashByte(0x1000, 4); FlashByte(0x1001, 5); FlashByte(0x1002, 6); FlashByte(0x2000, 7); FlashByte(0x2001, 8); FlashByte(0x2002, 9); BgDrawPrintf(3, 5, "%x %x %x", FlashRead(0x0000), FlashRead(0x0001), FlashRead(0x0002)); BgDrawPrintf(3, 6, "%x %x %x", FlashRead(0x1000), FlashRead(0x1001), FlashRead(0x1002)); BgDrawPrintf(3, 7, "%x %x %x", FlashRead(0x2000), FlashRead(0x2001), FlashRead(0x2002)); BgDrawPrintf(2, 8, "Erase 1"); FlashEraseSector(1); BgDrawPrintf(3, 9, "%x %x %x", FlashRead(0x0000), FlashRead(0x0001), FlashRead(0x0002)); BgDrawPrintf(3, 10, "%x %x %x", FlashRead(0x1000), FlashRead(0x1001), FlashRead(0x1002)); BgDrawPrintf(3, 11, "%x %x %x", FlashRead(0x2000), FlashRead(0x2001), FlashRead(0x2002)); BgDrawPrintf(2, 12, "Erase Chip"); FlashEraseChip(); BgDrawPrintf(3, 13, "%x %x %x", FlashRead(0x0000), FlashRead(0x0001), FlashRead(0x0002)); BgDrawPrintf(3, 14, "%x %x %x", FlashRead(0x1000), FlashRead(0x1001), FlashRead(0x1002)); BgDrawPrintf(3, 15, "%x %x %x", FlashRead(0x2000), FlashRead(0x2001), FlashRead(0x2002)); BgDrawPrintf(2, 16, "Done"); } //--------------------------------------------------------------------------- EWRAM_CODE void FlashByte(u16 adr, u8 dat) { // Byte-Program FlashWrite(0x5555, 0xAA); FlashWrite(0x2AAA, 0x55); FlashWrite(0x5555, 0xA0); FlashWrite(adr, dat); // 20us FlashWait(400); } //--------------------------------------------------------------------------- EWRAM_CODE u8 FlashRead(u16 adr) { u8* p = (u8*)SRAM + adr; u8 ret = *p; return ret; } //--------------------------------------------------------------------------- EWRAM_CODE void FlashWrite(u16 adr, u8 cmd) { u8* p = (u8*)SRAM + adr; *p = cmd; __asm("NOP"); } //--------------------------------------------------------------------------- EWRAM_CODE void FlashEraseChip(void) { // Chip-Erase FlashWrite(0x5555, 0xAA); FlashWrite(0x2AAA, 0x55); FlashWrite(0x5555, 0x80); FlashWrite(0x5555, 0xAA); FlashWrite(0x2AAA, 0x55); FlashWrite(0x5555, 0x10); // 100ms FlashWait2(10); } //--------------------------------------------------------------------------- EWRAM_CODE void FlashEraseSector(u16 sec) { _ASSERT(sec < 0x10); // Sector-Erase FlashWrite(0x5555, 0xAA); FlashWrite(0x2AAA, 0x55); FlashWrite(0x5555, 0x80); FlashWrite(0x5555, 0xAA); FlashWrite(0x2AAA, 0x55); FlashWrite(sec * 0x1000, 0x30); // 25ms FlashWait2(3); } //--------------------------------------------------------------------------- EWRAM_CODE void FlashWait(u32 cnt) { for(vu32 i=0; i<cnt; i++) { // 1週 0.0625us(大雑把に) __asm("NOP"); } } //--------------------------------------------------------------------------- EWRAM_CODE void FlashWait2(u32 cnt) { for(vu32 i=0; i<cnt; i++) { // 1週 16.743ms while(REG_VCOUNT >= 160) {}; while(REG_VCOUNT < 160) {}; } } //--------------------------------------------------------------------------- // SST39VF010 : 0xD5BF // SST39VF020 : 0xD6BF // SST39VF040 : 0xD7BF EWRAM_CODE u16 FlashGetId(void) { // ID Entry FlashWrite(0x5555, 0xAA); FlashWrite(0x2AAA, 0x55); FlashWrite(0x5555, 0x90); // Read ID u8 id0 = FlashRead(0x0000); u8 id1 = FlashRead(0x0001); // ID Exit FlashWrite(0x5555, 0xAA); FlashWrite(0x2AAA, 0x55); FlashWrite(0x5555, 0xF0); return id0 | (id1 << 8); } //--------------------------------------------------------------------------- EWRAM_CODE bool FlashIsExist(void) { // 読み込み、書き込みテスト u8 t1 = FlashRead(0x7FFF); u8 t2 = ~t1; FlashWrite(0x7FFF, t2); if(FlashRead(0x7FFF) != t2) { FlashWrite(0x7FFF, t1); return TRUE; } return FALSE; }