雷電 無敵改造
さて、改造の続き……
ボンバー無限と残機無限はできたので次は無敵とかできないかな……と考えたわけですが、無敵は数を数えてってわけにいかないので難しそうです。
で、いろいろ検索してたところMAMEにはチートがあるのを見つけました!
いや、それは知ってたんだけど普段MAMEで遊ぶことなんて無いのですっかり忘れてました。
実はMAMEのチート使うのも初めてです。
昨日のエントリにもコメントしてくださった方がいました。
今日はGUIのMAME plusというのを使ってみました。こんな感じ。
チートのメニューがありますが、これはどこかからチートファイルを見つけてこないと出てこないようです。
チートの中身はこんな感じです。ボンバーや自機の無限てありますね……
で、問題はこのチートがどのように定義されているかってところなんですが、なんとわかりやすいことにxmlファイルでチートの内容とメモリに与える値が定義されています。
<cheat desc="Infinite Lives PL1"> < script state="run"> <action>maincpu.pb@0BD5=0A</action> </script> </cheat> <cheat desc="Infinite Bombs PL1"> < script state="run"> <action>maincpu.pb@0BE3=06</action> </script> </cheat>
上のように、先日書き換えた残機やボンバー数が格納されているアドレスが書かれています。
MAMEでは、エミュレータ本体がエミュレートされているメモリ上にその値を継続して書き込んでくれるらしく、特にプログラムを書き換える必要はありません。
定義文から推測するに「script state」が"run"であるとメモリに対して常に値を書き込み続け、"on"であるとその場で一発書き込みって感じなんでしょうか。
他にも書式があるようですがそれについてはまた調べてみます。
さて、無敵のところを見てみましょう。
<cheat desc="Invincibility PL1"> < script state="run"> <action>maincpu.pb@1215=0B</action> </script> </cheat>
どうやら1215Hに0BHを書きこめば無敵になるみたいです。
自機の状態を表しているフラグみたいなものなんでしょうか。一応デバッガで該当メモリを見張ってみましょう。
詳細は省略しますが、調べてみるとやはり自機の状態を表しているようです。
復活時などの無敵状態 0BH 通常の状態 03H 死んだとき 00H
で、さらに昨日と同じ手順でこのアドレスに書き込んでいるルーチンを探しました。
ここですね。
FBCA8: 88 46 0B mov byte ptr [bp+0Bh],al
これを常に0BHを書きこむように直してやればよさそうですが……8086の命令がよくわからないので即値が書き込めませんw
しかも使えるのは3バイト限定でプログラムの前後に余裕はありません!
FBC9D: E8 08 00 call 0FBCA8h ←ここからコールされている FBCA0: C7 06 28 04 00 00 mov word ptr [428h],0h FBCA6: EB AD br 0FBC55h ←ここで以下のサブルーチンを飛び越している FBCA8: 88 46 0B mov byte ptr [bp+0Bh],al ←イマココ! FBCAB: 26 AD ldmw FBCAD: 8B D8 mov bw,aw ・ ・
ソースを読むとcall命令は3バイトなので、空いているメモリに無敵状態書き込みルーチンを置いてそこを呼び出すようにしましょう。
メモリの様子を見て、FF800Hから空いている感じなのでこんな感じで……
FBCA8: E8 55 3B call FF800h
呼び出し先はこうですね。
push ax mov al,0bh mov byte ptr [bp+0Bh],al pop ax ret
勝手サブルーチンでスタックポインタをいじっちゃうのはどうなの……とか思いましたが、まあ大丈夫かな?
さて、どうでしょうか……ゲームスタートしてみます。
アッー!リセットかかってタイトルに戻った!?ww
どういうことでしょうか?なぜかゲームが始まってほどなくリセットがかかってしまいます。
仕方ないのでデバッガで先程のcall命令にブレークポイントを設定。
プログラムが停止したところからステップ実行で進めてみます。
デバッガのコマンドは「bp FBCA8」ですね。
まずはcallに書き換えたところでプログラム停止。ここでF11を押してステップ……
アッー!?「call FF800h」なのにEF800hに飛んでる!?
これはセグメントを飛び越えられない的な何かですかね……この辺りの知識に乏しくて泣けます。
仕方ないので、サブルーチンの開始アドレスをもう少し低い方に移しました。
セグメント書き換える方法もあるんだろうけど、資料とか無いのでお手上げですw
FBCA8: E8 55 2E call FE800h
デバッガで確認すると、このアドレスは呼び出せる感じ。
じゃあプレイ続行で……
おおっ!無敵です!成功です!
攻撃してないのにアイテムキャリアが火を噴いているのがちょっと気になりますが……
あ、あれ!?敵も死なない…?ww
なんということでしょう!なんということでしょう!自分も死にませんが敵も死にません!w
ボンバーにも耐えてますww
敵が無敵なので中型機がボス戦まで着いてきてしまいます。
なんというカオス。
うーん、ボスはダメージ入るのか……
いずれにしても何かおかしい感じです。
ちょっと悩んでいたのですが、よくよく考えると無敵状態の数値を書き込んでいる命令にbpレジスタが使用されています。
呼び出し元がわからないんだからこのbpは不定ですよね……
mov byte ptr [bp+0Bh],al
上の命令にブレークポイントを設定してbpレジスタをよーく眺めてみたところ、自機の状態のアドレスに値を書き込んでいる場合と、それ以外のアドレスに値を書き込んでいる場合があるようです。
どうやら、自機、敵で状態を書き換えるルーチンは共用だったみたいです。
気づけばごく当然のことながら、ここに気づくまで1時間ぐらいかかってしまいました。思い込みってだめだよね……orz
で、サブルーチンを改良です。
mov byte ptr [bp+0Bh],al push ax mov al,0bh cmp [01215h],al je skip mov [01215h],al skip: pop ax ret
とりあえず、[bp+0Bh]にalレジスタは書きこんでおいて、その後で自機の状態をチェック。
無敵状態の0BH以外だったら改めて0BHを書き込むようにしました。
ちょっと効率悪い感じで、勝手サブルーチンもあまり巨大化させない方がいいんだろうけど、まあ仕方ないです。
いやまあ、ぶっちゃけ無敵状態のチェックとかいらないですけど、今後0BH以外の自機の状態が判明したときに柔軟に対応できるように準備しておきました。
さて、それじゃあこれをハンドアセンブルして……と、ここで行き詰まりました。
ハンドアセンブルの限界です。
いろいろと命令を増やしたので、雷電のソースから同じニーモニックを見つけられなくてコードがわかりません。
ぐぬぬ……
こ、困ったらツイートだよね……w
ツイッターでそんなことを書いてたら、ume3さんにnasmというアセンブラを教えていただきました。
なんでもV30特有の命令なんてそんなにないから8086でアセンブルするのが普通だとか……そうだったんですね。
勉強になります。
で、上のルーチンをアセンブルしてみました。
mov byte ptr [bp+0Bh],al 88 46 0B push ax 50 mov al,0bh B0 0B cmp [01215h],al 38 06 15 12 je skip 74 03 mov [01215h],al A2 15 12 skip: pop ax 58 ret C3
おおー!流石流石!
生成されたコードを手作業でまた交互に書きこんでいきます。
で、実行……
おおっ!キター!
敵にはきちんとダメージが入ります!そして自機は無敵!
なんとか完成しました。
もうよくわかってる人には簡単な改造だと思いますが、やっぱり自分でやってみないとわかりにくいですよね。
でもこれで少しコツが掴めてきました。
ちなみにROMからメモリへ交互にデータを読むのは、V30のバス幅が16bitであるのに対し、当時の一般的なEPROMのバス幅が8bitだったということをEXCEEDさんに教えていただきました。
id:EXCEEDさんのブログはよく読んでいるんですよね。
fujikkoさんにも、2つのROMから交互に読めば読み出し速度が最大1/2の遅さでも間に合うというメリットもあると教えていただきました。
うーん、わからないことはツイートすると賢くなりますねw
まとめ
新しいコール FBCA8H rai3.bin 1DE54 88 0B → E8 2E rai4.bin 1DE54 46 → 55 新しいサブルーチン FEB00H rai3.bin 1F580 16 C0 16 → 88 0B B0 38 15 74 A2 12 C3 rai4.bin 1F580 EF EE EF → 46 50 0B 06 12 03 15 58
無敵についてはROMに焼いて実機で動かしてみるつもりです。
動画録ればキャプチャしてマップ作れるかもしれないですね。
あ…!w