ちょっと趣向を変えてみまして、wizardryをサーチしてみます。 ターゲットは「後衛キャラを直接攻撃可能に」です。 とっとと戦闘に入り、ゼロページを眺めながらコマンドを入力。 すると、06番地と38番地が、一人目=0、二人目=1…と連動している。 どちらが適当かこの時点では判断しかねるが、06番地のほうはゴミが混ざっているようなので、 38番地に"on read"でブレイクポイントを追加。 するとコマンドを決定した瞬間にブレイクがかかるが、関係なさそうなので無視して続行。 次々とブレイクするプログラムを追いかけていくと、6回目のブレイクで、 F527 lda $38 F529 cmp #$03 F52B bcs $F532 という個所を発見。 このルーチンだと38番地の値が、0〜2=キャリーフラグon、3以上=キャリーフラグoffになるので、 見事に直接攻撃可能なキャラクタナンバーと一致。 早速"cmp #$03"(C9 03)を"cmp #$06"(C9 06)に書き換える(別に次を"bcs $F52D"にしてもいいんだけど) 見事後衛キャラクターのコマンドに「FIGHT」が追加されました。 これを暗号化すると E04AE6C4 ということで実機で動作確認。結果問題なし。ど楽勝でした。 なので調子に乗ってリルガミンも行ってみます。 n人目指示アドレスは3F番地。 #1と同じ手順でプログラムを追っていくと… AB6C lda $3F AB6E cmp #$03 AB70 bcs $AB77 予想はしていたが、まったくいっしょなのね(ただしここに来るまでにブレイクした回数は#1よりずっと多かった) とっとと暗号化しましょう E04A9CBA 実機での動作も問題なし。 次、ダイヤモンド。 n人目指示アドレスは#2と同じく3F番地。 以下同手順にて、 A09E lda $3F A0A0 cmp #$03 A0A2 bcs $A0A9 コード暗号化して、 E04AD980 動作確認問題なし。 この程度なら本当に誰でも出来るレベルなので、みんなファミコン改造やろうよ。 --以下6月8日追加-- 2chにてリクエストが挙がっている次の課題「装備不能武器も装備可能」をサーチしてみる。 まず、職業の数値を調べると、一人目の職業が600B番地に格納されているので、 まず"on read"でブレイクポイントを仕掛けてみる。すると、 AA9D lda($2E),y [$600B] AA9F sta $0252 という命令でブレイク。どうも何番目のキャラクタを選んでも、一旦0252番地に格納し直しているらしい。 なので今度は0252番地に"on read"でブレイクポイントを仕掛ける。 今度は、 AD06 ldx $0252 AD09 lda $EF7A,x AD0C sta $0254 という命令でブレイク。どうも"lda $EF7A,x"という命令が気になったので、EF7A番地以降をダンプしてみると、 01 02 04 08 10 20 40 80 の順に数値が並んでいた。どうやら、職業のナンバーを数値からフラグ化しているようである。 対応は以下の通り。 FIG=0 01 MAG=1 02 PRI=2 04 THI=3 08 BIS=4 10 SAM=5 20 LOR=6 40 NIN=7 80 こうすれば、アイテムのパラメータとして例えば"E1"が指定されれていれば、職業フラグとANDすることによって、 忍者、ロード、侍、戦士が装備可能であることになる。 そして次の行で0254番地に職業フラグが保存されているので、さらにここに"on read"でブレイクポイントを仕掛ける。 すると次はここでブレイク。 AD43 lda $0530 AD46 and $0254 AD49 beq $AD67 ほらきたAND命令。おそらく0530番地にはアイテムのパラメータがロードされているのだろう。 AND演算でゼロフラグが立つ=職業フラグが立っていない=装備不能なので、 "beq $AD67"(F0 1C)を"beq $AD4B"(F0 00)に変更すれば良い。 装備不可を表す"#"は付くものの、EQUIPを選べばしっかり選択肢に表示される。 しかし、なんと"#"付きのアイテムは、装備欄で不確定名が表示されてしまう。これはまずい。 プログラムを下って調べてみると、装備可能=0x24、装備不能=0x2C、未鑑定=0x2Eとしてラベルがつけられており、 装備可能以外は同じ扱いになってしまっているのだ。 これを回避するために、ステータスを表示した時点で、装備不能マークをつけないようにするため、 再度600B番地に"on read"でブレイクポイントを仕掛けてからステータス画面を開く。 するとこんなルーチンを発見。 D1FF lda($2E),y [$600B] D201 tay D202 lda $EF7A,y D205 ldy #$03 D207 and $0530 D20A beq $D20D D20C iny D20D lda $D211,y どこかで見たアドレスがずらりと並んでいる(笑 最後の行で参照している値を調べてみると、D214番地(y=03のとき)が0x2C、D215番地(y=04のとき)が0x24であり、 先に調べた装備可否のラベルの値と一致する。 そのため、装備可能と判定させるには、"beq $D20D"(F0 01)を"beq $D20C"(F0 00)にすればよい。 これで邪魔な"#"マークは消滅する。このコードだけで実際の装備判定もかねていればどれほど良かったか… しかし、これではまだ終わらない。属性専用装備というものがあるため、それも回避しなくてはいけない。 一人目の属性は600C番地に格納されているためここに"on read"でブレイクポイントを仕掛ける。 AA8D lda($2E),y [$600C] AA8F sta $0253 …どっかで見たルーチンですな。0253番地に"on read"でブレイクポイントを仕掛ける。 AD0F ldx $0253 AD12 lda $EF7A,x AD15 sta $0255 職業のときとまったく同じですな。 対応表は以下の通り。 G=0 01 N=1 02 E=2 04 ここまで来たらやることは同じ。0255番地に"on read"でブレイクポイントを仕掛けて装備をしてみる。 ADA3 lda $0533 ADA6 and $0255 ADA9 beq $ADB0 …ここまで同じだと調子狂いますな。 "beq $ADB0"(F0 05)を"beq $ADAB"(F0 00)に書き換えて、試しにGOODのキャラで"HELMofEVIL"を装備してみる。 ばっちり装備できて呪われませんでした。 ちなみにすぐ下に呪いのアイテムの判定ルーチンもあるのですが、 いいかげん行数が多すぎるのと、わざわざ装備する意味がないので、今回は無視します。 暗号化すると FC13F1BC<職業制限解除 FCE1C9EC<装備不能マーク消去 FC599AD2<属性制限解除 となります。 リルガミンとダイヤモンドはまた明日。 --以下6月9日追加-- よくよく調べてみると、属性装備が可能になっても、結局ACは属性に依存することが発覚。 しかし、諸事情によりこのまま続行します。 リルガミンの遺産。 いきなりデータの格納方式が全然違う。 職業、属性、種族が1バイトにまとめられており、一人目は640A番地。 内容は、 種族 Hum=+0x00 Elf=+0x20 Dwa=+0x40 Gno=+0x60 Hob=+0x80 職業 Fig=+0x00 Mag=+0x04 Pri=+0x08 Thi=+0x0C Bis=+0x10 Sam=+0x14 Lor=+0x18 Nin=+0x1C 属性 G=+0x00 N=+0x01 E=+0x02 …面倒だなぁ。 とりあえずここにブレイクポイントを"on read"で仕掛ける。 C993 lda ($3D),y [$640A] C995 sta $50 どうやら今回は50番地が一時アドレスのようである。なのでここにブレイクポイントを"on read"で仕掛ける。 D15F lda $50 D161 lsr a D162 lsr a D163 and #$07 D165 sta $03 ここですね。この右シフト2回+"and #07"によって、前作と同じ職業番号が03番地に格納されます。 --以下6月10日追加-- なぜここで一旦切れているか、といいますと。 上の部分まで解析した後、03番地にブレイクポイントを仕掛けてあーでもないこーでもないとやっておったわけですが、 完全な遠回りであったことが発覚。 先のルーチンの周りを見てみると、 D157 lda $40,x D159 and #$40 D15B cmp #$40 D15D beq $D172 D15F lda $50 D161 lsr a D162 lsr a D163 and #$07 D165 sta $03 D167 ldy #$2F D169 lda ($04),y D16B eor #$FF D16D ldy $03 D16F and $D173,y D172 rts となっておるわけですが、結論から言うとD159番地の"and #$40"(29 40)を"lda #$40"(A9 40)にするとOKでした。 何故か。 まずD169番地でロードされているのが、アイテムに設定された装備可能職業のフラグになります。 これが直後の"eor #$FF"によってビットが反転し、装備可能な職業のビットが0になります。 次にD16F番地の命令で、AレジスタにANDされる値は、D713番地から順に見ていくと、 01 02 04 08 10 20 40 80 の順に数値が並んでいます。これは職業フラグなのですが、ここでANDをかけた直後にリターン命令があるということは、 何らかの形で、ゼロフラグが立った状態でリターンすれば、 結果として職業フラグと関係なく装備できるということになります。 そこでちょうど同じ数値が並んでいる上記の個所を書き換えれば、条件が成立するというわけです。 本作ではマークによる識別判定は無いようなので、次は属性判定を調べるために、 再度50番地にブレイクポイントを"on read"で仕掛けます。 すると、 D133 lda $50 D135 and #$03 D137 cmp ($04),y D139 beq $D141 という個所がヒット。0x03でANDすると属性の部分が残るので、非常にくさいです。 また、分岐命令の飛び先であるD141番地はリターン命令になっているので、 なんとかゼロフラグを立てた状態で、リターンに進めればよい…と思ったのですが、 この先プログラムを追っていくと、真っ先に出てくる命令がデクリメント命令だったりするので、 ゼロフラグは意味を持ちません。 よって"beq $D141"(F0 06)を無理矢理"rts"(60)に書き換えます。 A6AB9A46<職業制限解除 D3DA267C<属性制限解除 続いてダイヤモンド。 これに関しては、実はキャラクタデータの並びはリルガミンと同じで、一時保存アドレスも50番地と同じ。 で同じようにブレイクポイントを仕掛けると、 D15D lda $40,x D15F and #$40 D161 cmp #$40 D163 beq $D178 D165 lda $50 D167 lsr a D168 lsr a D169 and #$07 D16B sta $03 D16D ldy #$11 D16F lda ($04),y D171 eor #$FF D173 ldy $03 D175 and $D179,y D178 rts と見事にほぼ一致。なので書き換えもD15F番地の"and #$40"(29 40)を"lda #$40"(A9 40)にするだけ。 属性のほうも、 D137 lda $50 D139 and #$03 D13B cmp ($04),y D13D beq $D145 と同じなので、"beq $D145"(F0 06)を"rts"(60)に書き換えて終了。 A6AB91D2<職業制限解除 D3DA955A<属性制限解除 で、実はシナリオ#1に戻りまして、なんとMURAMASA BLADE等のスペシャルパワーが開放できない事に気づきまして、 また急いで調べてみたのですが、装備自体の鍵になった0254番地へのアクセスではヒットせず、 その一段階前の数値が格納されている0252番地をロードしている個所で B675 ldx $0252 B678 lda $EF7A,x B67B and $0530 B67E beq $B695 というルーチンがあり、ここの"beq $B695"(F0 15)を"beq $B680"(F0 00)に書き換えると使えるようになりました。 続いてシナリオ#2ですが、これも50番地ではヒットせず、640A番地(一人目の場合)をロードしている個所で B1AD lda ($3D),y [$640A] B1AF lsr a B1B0 lsr a B1B1 and #$07 B1B3 tax B1B4 ldy #$2F B1B6 lda ($55),y B1B8 and $B203,x B1BB beq $B200 というのがあったので、そのまま"beq $B200"(F0 43)を"beq $B1BD"(F0 00)にすると無事使えるようになりました。 シナリオ#3に関しても、#2とまったく同じ手順で B001 lda ($3D),y [$640A] B003 lsr a B004 lsr a B005 and #$07 B007 tax B008 ldy #$11 B00A lda ($55),y B00C and $B15E,x B00F beq $B054 となり、"beq $B054"(F0 43)を"beq $B011"(F0 00)に書き換えて終了。 SP解放可能 FC4E624E<#1 FCC4E638<#2 FCC4FFC6<#3 --以下1月3日追加-- 2ちゃんねるで依頼が出てるね。 一丁やったろやないかい、と。 まずは簡単そうな「戦闘後宝箱が出てくる」から。 VirturNESで比較サーチをくりかえしてみると、0x7DEE番地が0x02のときに宝箱が出る。 なので早速ここにon readでブレイクポイントを仕掛けて戦闘開始。 戦闘終了直後にブレイク発生。 EA0F ldy #$5E EA11 lda $7DEE <<ここでブレイク EA14 cmp #$02 EA16 bne $EA1A EA18 ldy #$5F これはわかりやすい。EA16番地の分岐命令を潰して"bne $EA1A"(D0 02)を"bne $EA18"(D0 00)にしてしまえばOK。 戦闘終了後宝箱出現 FC9330CE さて問題はもう一つの依頼「経験値分割されない」。 まず戦闘終了後に加算される経験値の数値をサーチしてみると、0x7DF0〜0x7DF5番地にかけてビッグエンディアンの十進数表記、つまりたとえば320だったら"03 20"というふうに格納されている。 さてこのアドレスを見ながら戦闘をしてみると、敵を倒すごとに値が加算され、戦闘終了時にがくっと減る、つまり分割されていることがわかった。 ならばということでこの範囲にon readでブレイクポイントを仕掛けて戦闘を終了させる。 E5A7 ldx #$05 E5A9 lda $7DF0,x [$7DF5] <<ここでブレイク E5AC sta $0A,x [$000F] E5AE dex E5AF bpl $E5A9 E5B1 ldx $7AC4 E5B4 beq $E5E0 E5B6 jsr $F049 さあ困った。ゼロページへ一旦飛ばしているということは、先の処理はかなり複雑と見ていい。ではどうするか。 まずこの直後に読み込まれる0x7AC4番地を調べてみる。すると、ここが現在生き残っているパーティメンバーの数だと判明。直後のbeq命令は、どうやらゼロ除算回避の処理のようだ。 ということは、ここで"1"を読み込ませることが出来れば、常に分割されない経験値が入手できることになるのだが、さてこれが1バイトの書き換えで実現できるか。 色々考えた挙げ句、反則技を使うことにした。 まず、0xC000〜0xFFFF番地の間の下位バイトが0xC4のアドレスでデータが0x01のアドレスを探す。これは運良く0xCEC4番地が該当した。 この部分は現在走っているプログラムと同じバンクなので、このプログラムが走っている間は常に一定の値になる。 これを利用して、このアドレスからxレジスタに数値を読み込むよう、0xE5B1番地を"ldx $7AC4"(AE C4 7A)から"ldx $CEC4"(AE C4 CE)に書き換える。 で出来たコードはこれ。 経験値分割されない 90FDF950 ついでにシナリオ#2#3もいっとこう。 まず#2。 0x059E番地が0x02のときに宝箱が出るので、on readでブレイクポイントを仕掛けて戦闘開始。 8606 lda $059E <<ここでブレイク 8609 lsr a 860A tax 860B lda $024B,x [$????] 860E asl a 860F tax 8610 lda $8BF0,x [$????] 8613 sta $B2 8615 lda $8BF1,x [$????] 8618 sta $B3 シ、シフト命令? あかんこりゃ手に負えんわ。反則技発動。 0x8C9E番地が0x02なのでここを読み込むように0x8606番地を"lda $059E"(AD 9E 05)から"lda $8C9E"(AD 9E 8C)に変更。 これで宝箱は出るようにはなったが、元々持っていない場合はどうしようもない模様。 次、経験値。戦闘中の格納アドレスは0x059F〜0x05A4で格納方式、計算のタイミングは#1と同じ。よって同じように調べてみると、 84F0 ldx #$05 84F2 lda $059F,x [$05A4] <<ここでブレイク 84F5 sta $2A,x [$002F] 84F7 dex 84F8 bpl $84F2 84FA rts ほぼ同じ処理だが、rtsで戻っているのでさらに追跡。 84DC ldx #$00 84DE ldy #$00 84E0 lda $0370,x [$0370] 84E3 cmp #$03 84E5 bcs $84E8 84E7 iny 84E8 inx 84E9 cpx $6B81 84EC bne $84E0 84EE tya 84EF rts えーと、0x0370番地からの6バイトは、現在のパーティメンバーの状態をあらわしていて、 00 (正常) 01 ねむっている 02 おそれている 03 まひしている 04 いしになった 05 しんでいる 06 はいになった 07 うしなわれた ということで、cmp命令で生き残っているかどうかを振り分けている模様。 つまり最終的にrts命令の前にaレジスタにセットされるのが生き残った人数ということになる。 がしかしこれをどうするかが問題なんだが、うまいこと思いつかないのでサブルーチンの呼び出しまで戻ってみる。 8410 jsr $84F0 8413 jsr $84DC 8416 jsr $FB92 8419 ldx #$05 841B lda $2A,x [$002F] 841D sta $059F,x [$05A4] 8420 dex 8421 bpl $841B ちょっとまてよ、これもしかして0x8416番地のサブルーチン分岐を潰せばそのまま除算されずに通るんじゃないか? ということで"jsr $FB92"(20 92 FB)を"ldx $FB92"(AE 92 FB)にしてみると無事分割がキャンセルされた。 次#3。 0x05BE番地が0x02のときに宝箱が出るので、on readでブレイクポイントを仕掛けて戦闘開始。 8574 lda $05BE <<ここでブレイク 8577 lsr a 8578 tax 8579 lda $024B,x [$????] 857C asl a 857D tax 857E lda $8B37,x [$????] 8581 sta $B2 8583 lda $8B38,x [$????] 8586 sta $B3 処理そのものは#2と全く同じなので、同じように反則技で乗り切るかと思ったんだけど、痛いことに同バンク内に使えそうなアドレスがない。 仕方がないので0x05BE番地に、on writeでブレイクポイントを仕掛けて戦闘開始。 したんだけれども、スタックを操作してサブルーチンからの戻りを分岐させているので、書き換えは無茶と判断。 おとなしく 0x8577番地から2バイト(4A AA)を"ldx #$01"(A2 01)に書き換えて妥協。非常に口惜しい。 次、経験値。戦闘中の格納アドレスは0x05BF〜0x05C4。#2と同じ方法で調べる。 8506 ldx #$05 8508 lda $05BF,x [$05C4] <<ここでブレイク 850B sta $2A,x [$002F] 850D dex 850E bpl $8508 8510 rts で、サブルーチンから復帰したところは 8423 jsr $8506 8426 jsr $84F2 8429 jsr $E0E7 842C ldx #$05 842E lda $2A,x [$002F] 8430 sta $05BF,x [$05C4] 8433 dex 8434 bpl $842E よって"jsr $E0E7"(20 E7 E0)を"ldx $E0E7"(AE E7 E0)に書き換え。 では暗号化してまとめ。 #2 戦闘終了後宝箱出現 A9225444 経験値分割されない BFB25C6A #3 戦闘終了後宝箱出現 E80F17E6 F9548A60 経験値分割されない BFB2EE04 --以下1月9日追加-- そういえば善のニンジャとか簡単につくれないかなあとか思ったりしたので解析。 #1から。 まずキャラクターメイキング時の属性を調べると、G=0x00、0N=0x01、E=0x02で0x0253番地に格納されているので、ここにon readでブレイクポイントをセットし、ボーナスポイントを振り分ける。 9ED0 lda #$FF 9ED2 sta $6D 9ED4 ldx #$05 9ED6 ldy $0253 <<ここでブレイク 9ED9 lda $67,x [$006C] 9EDB and $6D 9EDD and EF1F,y [$????] 9EE0 sta $6D 9EE2 dex 9EE3 bpl $9ED9 9EE5 rts ここで出てきている番地を調べてみると、0x6D番地で八つの職業のフラグが一括管理されていることがわかった。 で0xEF1F番地からの3バイトは属性ごとの可能な職業、0x67番地からの6バイトは現在の特性値で可能な職業のフラグが格納されており、全てをAND演算することで最終的に現在可能な職業をふるい分けている。 とここで、最初に0xFFが0x6D番地に代入されていることに注目。 0x9EE0番地のsta命令が発生するまではこの値は変更されない。ならば、ここを"sta $6D"(85 6D)から"lda $6D"(A5 6D)に書き換えることで職業フラグを全て立てたまま処理を終えることが出来る。 なおこの処理は転職の処理も兼ねているため、そのまま転職も可能。 続いて#2。 #1で格納方法が大体わかったので、ゼロページを眺めつつ特性値を振り分け、職業フラグを0x59番地と特定。ここにon writeでブレイクポイントを仕掛ける。 D9A0 lda #$07 D9A2 sta $0F D9A4 lda #$00 D9A6 sta $59 <<ここでブレイク D9A8 sta $5A D9AA lda $0F D9AC asl a D9AD asl a D9AE asl a D9AF tay D9B0 ldx #$00 D9B2 lda $4D,x [$004D] D9B4 cmp $DB0B,y [$????] D9B7 bcc $D9F5 D9B9 inx D9BA iny D9BB cpx #$06 D9BD bne $D9B2 D9BF ldx $42 D9C1 lda $DB0B,y [$????] D9C4 and $DB4B,x [$????] D9C7 beq $D9F5 D9C9 iny D9CA lda $DB0B,y [$????] D9CD ora $59 D9CF sta $59 <<ここでもブレイク D9D1 inc $5A D9D3 ldx $0F D9D5 lda $DB54,x [$????] D9D8 tax D9D9 lda $0F D9DB asl a D9DC asl a D9DD asl a D9DE ldy $77FB D9E1 beq $D9E5 D9E3 ora #$40 D9E5 tay D9E6 lda $D8AD,y [$????] D9E9 cmp #$20 D9EB beq $D9F5 D9ED sta $0110,x D9F0 inx D9F1 iny D9F2 jmp $D9E6 D9F5 dec $0F D9F7 bpl $D9AA D9F9 rts #1とは逆に、最初に0x00を格納しているうえに、書き込みのための代入値が他にも流用されていて非常に面倒。 直前にor命令がある0xD9CF番地のストア命令にたどり着けば良さそうだが、途中でiny命令が2回もあるため直接飛ばすと問題がある。 なのでその手前の分岐命令二つを、0xD9B7番地は"bcc $D9F5"(90 3C)を"bcc $D9B9"(90 00)に、0xD9C7番地は"beq $D9F5"(F0 2C)を"beq $D9C9"(F0 00)に書き換える。 それぞれ0xD9B7番地が特性値、0xD9C7番地が属性による制限の解除になる。 転職の方は DA3A ldx #$00 DA3C lda $4D,x [$004D] DA3E cmp $DB0B,y [$????] DA41 bcc $DA96 DA43 inx DA44 iny DA45 cpx #$06 DA47 bne $DA3C DA49 ldx $42 DA4B lda $DB0B,y [$????] DA4E and $DB4B,x [$????] DA51 beq $DA96 DA53 iny DA54 lda $DB0B,y [$????] DA57 ora $59 DA59 sta $59 <<ここでブレイク DA5B inc $5A と同じようになっているので、0xDA41番地は"bcc $DA96"(90 53)を"bcc $DA43"(90 00)に、0xDA51番地は"beq $DA96"(F0 43)を"beq $DA53"(F0 00)に書き換える。 といいたいところなのだが、転職の場合属性制限を解除してしまうと枠が六つしかないため一番下がはみ出してしまうのでやめておいた方がいい。 #3は#2とほぼ変わらず、キャラクタメイキングは D980 ldx #$00 D982 lda $4D,x [$004D] D984 cmp $DB0B,y [$????] D987 bcc $D9C5 D989 inx D98A iny D98B cpx #$06 D98D bne $D982 D98F ldx $42 D991 lda $DB0B,y [$????] D994 and $DB4B,x [$????] D997 beq $D9C5 D999 iny D99A lda $DB0B,y [$????] D99D ora $59 D99F sta $59 <<ここでブレイク D9A1 inc $5A で0xD987番地は"bcc $D9C5"(90 3C)を"bcc $D989"(90 00)に、0xD997番地は"beq $D9C5"(F0 2C)を"beq $D999"(F0 00)に書き換える。 転職の方も DA0A ldx #$00 DA0C lda $4D,x [$004D] DA0E cmp $DB0B,y [$????] DA11 bcc $DA66 DA13 inx DA14 iny DA15 cpx #$06 DA17 bne $DA0C DA19 ldx $42 DA1B lda $DB0B,y [$????] DA1E and $DB4B,x [$????] DA21 beq $DA66 DA23 iny DA24 lda $DB0B,y [$????] DA27 ora $59 DA29 sta $59 <<ここでブレイク DA2B inc $5A なので同じく0xDA11番地は"bcc $DA66"(90 53)を"bcc $DA13"(90 00)に、0xDA21番地は"beq $DA66"(F0 43)を"beq $DA23"(F0 00)に書き換える。 暗号化してまとめ。 #1 F18E606A 作成・転職時職業制限解除 #2 FC1180EC 作成時職業特性値制限解除 FC063E58 作成時職業属性制限解除 FCD31D96 転職時職業特性値制限解除 FCC4A3B4 転職時職業属性制限解除 #3 FC113864 作成時職業特性値制限解除 FC068646 作成時職業属性制限解除 FCD3A588 転職時職業特性値制限解除 FCC41B12 転職時職業属性制限解除 --以下4月30日追加-- なんと一年以上放置してしまったのであるよ。 さて今回のお題はボルタック商店。 シナリオ#1から。 まずメモリ上のデータの位置だが、0x7401番地から0x7465番地にかけて、アイテムごとに個数が格納されている。 そこでまず0x7401番地にon readでブレイクポイントをしかける。 $95C1:A9 00 LDA #$00 $95C3:85 38 STA $0038 $95C5:A9 C2 LDA #$C2 $95C7:85 30 STA $0030 $95C9:A9 20 LDA #$20 $95CB:85 31 STA $0031 $95CD:BD 01 74 LDA $7401,X [$7401] <<ここでブレイク $95D0:F0 20 BEQ $95F2 $95D2:8A TXA $95D3:20 B5 D0 JSR $D0B5 $95D6:AD 24 05 LDA $0524 $95D9:D0 17 BNE $95F2 $95DB:20 3F 96 JSR $963F $95DE:A5 30 LDA $0030 $95E0:18 CLC $95E1:69 20 ADC #$20 $95E3:85 30 STA $0030 $95E5:90 02 BCC $95E9 $95E7:E6 31 INC $0031 $95E9:E6 38 INC $0038 $95EB:A6 38 LDX $0038 $95ED:E0 08 CPX #$08 $95EF:D0 01 BNE $95F2 $95F1:60 RTS ブレイクがかかった0x95CD番地のlda命令でアイテムの個数が読み込まれ、 直後のbeq命令で個数が零であれば分岐するようになっているため、 この分岐を無視するように書き換えてやる。 今回は、直前にaレジスタに0x20が格納されているので、次のように書き換える。 95CD:BD 01 74 LDA $7401,X [$7401] ↓ 95CD:1D 01 74 ORA $7401,X [$7401] これで個数が零のアイテムも買えるようになったが、まだ呪われたアイテムは陳列されない。 そこでプログラムを少し下ってみると、先ほどの分岐命令と同じ飛び先のbne命令が0x95D9番地にある。 そのためここにアタリをつけ、分岐をつぶしてみる。 95D9:D0 17 BNE $95F2 ↓ 95D9:D0 00 BNE $95DB これで呪われたアイテムも陳列されるようになった。 ……と思ったら、このコードはページを下にスクロールさせたときにしか効かず、 ページを上にスクロールさせた場合は通常の処理が実行されてしまう。 不便なので、同じ手順で是正する。 $95FA:A2 00 LDX #$00 $95FC:86 6F STX $006F $95FE:4C CD 95 JMP $95CD $9601:86 6F STX $006F $9603:A9 07 LDA #$07 $9605:85 38 STA $0038 $9607:A9 A2 LDA #$A2 $9609:85 30 STA $0030 $960B:A9 21 LDA #$21 $960D:85 31 STA $0031 $960F:BD 01 74 LDA $7401,X [$7401] <<ここでブレイク $9612:F0 1E BEQ $9632 $9614:8A TXA $9615:20 B5 D0 JSR $D0B5 $9618:AD 24 05 LDA $0524 $961B:D0 15 BNE $9632 $961D:20 3F 96 JSR $963F $9620:A5 30 LDA $0030 $9622:38 SEC $9623:E9 20 SBC #$20 $9625:85 30 STA $0030 $9627:B0 02 BCS $962B $9629:C6 31 DEC $0031 $962B:C6 38 DEC $0038 $962D:A6 38 LDX $0038 $962F:10 01 BPL $9632 $9631:60 RTS よって 960F:BD 01 74 LDA $7401,X [$7401] ↓ 960F:1D 01 74 ORA $7401,X [$7401] 961B:D0 15 BNE $9632 ↓ 961B:D0 00 BNE $9632 これで一応完成。 つづいてシナリオ#2。 メモリ上のデータの位置は、0x7780番地から0x77ED番地にかけて。 なので0x7780番地にon readでブレイクポイントをしかける。 $C940:A2 00 LDX #$00 $C942:B9 80 77 LDA $7780,Y [$7780] <<ここでブレイク $C945:30 02 BMI $C949 $C947:D0 0A BNE $C953 $C949:C8 INY $C94A:C0 6E CPY #$6E $C94C:D0 F4 BNE $C942 $C94E:A0 00 LDY #$00 $C950:4C 42 C9 JMP $C942 $C953:98 TYA $C954:95 48 STA $48,X [$0048] $C956:A9 00 LDA #$00 $C958:95 40 STA $40,X [$0040] $C95A:E8 INX $C95B:E0 08 CPX #$08 $C95D:D0 EA BNE $C949 $C95F:60 RTS ブレイクがかかった0xC942番地のlda命令でアイテムの個数が読み込まれ、 直後のbmi・bne命令で個数が零か負の数であればループ処理によって分岐し、 次のアイテムに進むようになっているため、かならずここで分岐するように書き換えてやる。 0xC945番地の引数である0x02をaレジスタにoraしてやれば、次のbne命令で確実に分岐するので、 以下のように書き換える。 C945:30 02 BMI $C949 ↓ C945:09 02 ORA $C949 今回は#1とちがい、この状態でも呪われたアイテムは陳列されるが、 やはり逆順でスクロールさせた際の挙動があやしいので、是正する。 $C6F8:A2 02 LDX #$02 $C6FA:A4 48 LDY $0048 $C6FC:B9 80 77 LDA $7780,Y [$7780] <<ここでブレイク $C6FF:30 02 BMI $C703 $C701:D0 08 BNE $C70B $C703:88 DEY $C704:10 F6 BPL $C6FC $C706:A0 6D LDY #$6D $C708:4C FC C6 JMP $C6FC $C70B:CA DEX $C70C:D0 F5 BNE $C703 $C70E:4C C1 C6 JMP $C6C1 先の処理と同様に $C6FF:30 02 BMI $C949 ↓ $C6FF:09 02 ORA $C949 これですべてのアイテムが買えるようになったが、まだ問題がある。 先に条件分岐の説明で「零か負の数」と書いたが、 零のときにアイテムを購入すると、個数が-1になってしまい、少々まずい。 そこで、無限に買えるアイテムは個数が0x7Fなので、これに修正する。 ブレイクポイントは同じく0x7780番地にon read。 $C79D:BD 80 77 LDA $7780,X [$7780] <<ここでブレイク $C7A0:C9 7F CMP #$7F $C7A2:F0 05 BEQ $C7A9 $C7A4:DE 80 77 DEC $7780,X [$7780] <<ここでブレイク $C7A7:F0 05 BEQ $C7AE $C7A9:A0 0A LDY #$0A $C7AB:4C 23 C7 JMP $C723 $C7AE:A4 48 LDY $0048 $C7B0:20 40 C9 JSR $C940 $C7B3:20 52 FD JSR $FD52 $C7B6:A4 36 LDY $0036 $C7B8:20 D4 EF JSR $EFD4 $C7BB:A0 0D LDY #$0D $C7BD:4C 23 C7 JMP $C723 $C7C0:20 91 C9 JSR $C991 $C7C3:A5 57 LDA $0057 $C7C5:D0 01 BNE $C7C8 $C7C7:60 RTS ちょうど0xC7A0番地のcmp命令の引数が0x7Fなので、これを利用してlda命令でaレジスタに値をセットし、 次のdec命令をsta命令に書き換えてメモリに書き込むようにする。 $C7A0:C9 7F CMP #$7F ↓ $C7A0:A9 7F LDA #$7F $C7A4:DE 80 77 DEC $7780,X [$7780] ↓ $C7A4:9D 80 77 STA $7780,X [$7780] これで完成。 つづいてシナリオ#3。 メモリ上のデータの位置は、0x6D0E番地から0x6D97番地にかけて。 なので0x7780番地にon readでブレイクポイントをしかける。 $C977:A2 00 LDX #$00 $C979:B9 0E 6D LDA $6D0E,Y [$6D0E] <<ここでブレイク $C97C:F0 0B BEQ $C989 $C97E:94 48 STY $48,X [$0048] $C980:A9 00 LDA #$00 $C982:95 40 STA $40,X [$0040] $C984:E8 INX $C985:E0 08 CPX #$08 $C987:F0 26 BEQ $C9AF $C989:C8 INY $C98A:C0 8A CPY #$8A $C98C:D0 EB BNE $C979 また処理がかわっている。 今回は負の数はチェックされていないようなので、そのまま分岐をつぶす。 C97C:F0 0B BEQ $C989 ↓ C97C:F0 00 BEQ $C97E つづいて上スクロールだが、0x6D0E番地の監視ではなぜかひっかからないので、 0x6D0F番地にブレイクポイントをしかける。 $C734:A2 02 LDX #$02 $C736:A4 48 LDY $0048 $C738:D0 02 BNE $C73C $C73A:A0 89 LDY #$89 $C73C:B9 0E 6D LDA $6D0E,Y [$6D0F] <<ここでブレイク $C73F:F0 03 BEQ $C744 $C741:CA DEX $C742:F0 B9 BEQ $C6FD $C744:88 DEY $C745:4C 38 C7 JMP $C738 これも先の処理と同じく。 C73F:F0 03 BEQ $C744 ↓ C73F:F0 00 BEQ $C741 ここまでの改竄で呪われたアイテムも陳列される。 また先に書いた通り、今回は負の数のチェックが存在しないので、 #1と同じく零個のアイテムを買った場合はそのまま無限化されるため、 これ以上の処理は必要ないだろう。 #1 ボルタック商店 ストック0のアイテムも陳列 1622C810 1622DAB2 *ストック0のアイテムを購入すると無限化します 呪われたアイテムも陳列 FC6076BE FC4E63BA #2 ボルタック商店 ストック0以下のアイテムも陳列 A571BA44 A5716256 買ったアイテムが無限化 A6A7A8AC 14B97EBA #3 ボルタック商店 ストック0のアイテムも陳列 FCCE4F38 FCCFFF68 *ストック0のアイテムを購入すると無限化します