DQ3 K.Mix アイテムID9ビット拡張作業2

前回のエントリで領域の問題はクリアしたところまで説明しました。あとはPCのアイテム領域の操作、袋の中身の操作のプログラムを書くだけ、という話になりそうなのですが、以下の2点が問題となります。

  1. 対象範囲がかなり広いため、十分なテストが必要
  2. ビット単位でデータを詰めてセットしているので、アイテムの操作にまつわる処理量が10倍~に膨れ上がり、人が操作していて違和感を覚えるくらいに遅くなりかねない

1に関しては十分にテストをすればいいだけという話ではありますが、対象範囲を把握するのがかなり大変です。先日もテストプレイ中に間違った場所に情報をセットしてしまう箇所が残っていていつの間にかPCの最大MPが999になっていたりしました。一応そこそこテストをしてからテストプレイを開始したわけですが、ポロポロと問題が見つかるという始末です。PCのデータというかなりデリケートな箇所の操作なので、対処漏れがあると非常にまずいです。一通りテストしてOKだと思って別の方にテストプレイをしてもらったら対処漏れが見つかったりしたので(DQ3のアイテム操作のバリエーションは意外と多い)、rcまで出せたとしても正式版までは2ヶ月位置きたいと思っています。

2はデータ形式をこねくっている以上避けられません。各キャラクターのアイテム操作は最大12個なのであまり表面化しませんが、袋のアイテム操作は種類数が最大255(かそれ以上)になるので実装によっては処理に時間がかかり、突っかかるような感覚を覚えるケースがあります。オリジナルでは1バイトにアイテムID、個数を格納しているので、アイテムを並べ替えたり順番を1つずらしたりという操作は極めてシンプルに記述されています。以下一例。

  • SR:$045418 袋からアイテムを減らす
$04542C DEC $3825,X $3825+X– 個数を減らす
$04542F BNE #$1A if(z==off) goto $04544B 袋の対象のアイテム個数が0個になったら1個ずつ前にずらさないといけない
$045431 INX X++
$045432 CPX #$0100 X>=#$0100? 255種類最後までずらす
$045435 BCS #$0E if(c==on) goto $045445
$045437 LDA $3825,X A=$3825+X
$04543A STA $3824,X $3824+X=A 個数情報をずらす
$04543D LDA $3725,X A=$3725+X
$045440 STA $3724,X $3724+X=A アイテムID情報をずらす
$045443 BRA #$EC goto $045431
$045445 STZ $3824,X $3824+X=#$00 一番最後を#$00でうめる
$045448 STZ $3724,X $3724+X=#$00
$04544B REP #$30 m=off(A/M:16b) x=off(X/Y:16b)

アイテムIDを9ビット化した場合、アイテムID,個数情報が0ビットから始まるわけではなくなってしまうので単純にずらせばいいというわけにはいきません。アイテムID+個数情報をまとめて2バイトに固められたら楽ですが、その分領域を食うのでそれもできないというのが悩ましいところ。かくして空きを作らずビットの途中からアイテムIDを格納するような形態しかできないという結論に落ち着きました。次に問題となるのが「どうやってアクセスするか」です。ビットの途中から始まる情報にアクセスするためのSRはオリジナルの時点で存在しています。

  • SR:$025CC0 ターン開始時処理
$025CFD PEA #$23AC Push #$23AC
$025D00 PEA #$0018 Push #$0018
$025D03 PEA #$7E00 Push #$7E00
$025D06 JSL $C9029E SR: $09029E

なんとなく前後を見ていれば想像がつきますが、「$7E23ACの3-4ビットの値を取ってくる」のがSR: $09029Eらしいということがわかります。反対に値をセットするのがSR: $0902E9です。自分で0から実装するとなると結構面倒ですが、なかなか便利なSRです。次に中身を見てみます。

  • SR:$09029E RAM上情報取得
09029E PHP Push P Flag
09029F SEI i=on
0902A0 REP #$30 m=off(A/M:16b) x=off(X/Y:16b)
0902A2 PHA Push A
0902A3 PHB Push DB
0902A4 PHY Push Y
0902A5 PHX Push X
0902A6 SEP #$10 x=on(X/Y:8b)
0902A8 LDA $0D,S A=Stack($0D)
0902AA TAX X=A
0902AB PHX Push X
0902AC PLB Pull DB
0902AD LDY #$00 Y=#$00
0902AF LDA $0E,S A=Stack($0E)
0902B1 AND ($10,S),Y A&=Stack($10+Y)
0902B3 STA $40 DP($40)=A
0902B5 LDA $0E,S A=Stack($0E)
0902B7 BEQ #$14 if(z==on) goto $0902CD
0902B9 LSR A>>1
0902BA BCS #$11 if(c==on) goto $0902CD
0902BC TAX X=A
0902BD LDA $0C,S A=Stack($0C)
0902BF LDY #$02 Y=#$02
0902C1 AND ($10,S),Y A&=Stack($10+Y)
0902C3 LSR A>>1
0902C4 ROR $40 DP($40)>>1
0902C6 TAY Y=A
0902C7 TXA A=X
0902C8 LSR A>>1
0902C9 TAX X=A
0902CA TYA A=Y
0902CB BCC #$F6 if(c==off) goto $0902C3
0902CD REP #$30 m=off(A/M:16b) x=off(X/Y:16b)
0902CF PLX Pull X
0902D0 PLY Pull Y
0902D1 PLB Pull DB
0902D2 LDA $05,S A=Stack($05)
0902D4 STA $0B,S Stack($0B)=A
0902D6 LDA $03,S A=Stack($03)
0902D8 STA $09,S Stack($09)=A
0902DA LDA $40 A=DP($40)
0902DC STA $07,S Stack($07)=A
0902DE TSC A=S
0902DF CLC c=off
0902E0 ADC #$0006 A+=(#$0006+c)
0902E3 TCS S=A
0902E4 PLA Pull A
0902E5 PLP Pull P Flag
0902E6 LDA $40 A=DP($40)
0902E8 RTL return

何をやっているかはともかく、ぱっと見て「クソ長い」というのはわかると思います。ちなみに値をセットするSRはこれより長いです。アイテムIDを取ってくるだけの「A=$3725+X」の1命令と同じことをするだけでもこの処理量に膨れ上がるわけです。DQ3は幸いにしてターン制RPGなのでリアルタイム性を求められるアクションゲームやATBを採用しているFF4-6と違い、複雑な処理を実装しても実害が出るケースはほとんど存在しないため、トータルのCPUのサイクル数(=処理量)というのには今まであまり気を配らずに作業をしてきましたが、流石に上記のような変更が更に256倍以上(アイテム種類分)になると目に見えて支障が出ます。当初はこのSRを使っていましたが、特定のケースで突っかかるような感じを覚えることがあったので(アイテムの種類をほぼ最大にして先頭付近にある1個しかないアイテムを袋から出すなど)、アイテム操作という移動中ユーザーが行う作業の半分以上を占めるであろう部分が快適にできないというのはよろしくないので結局専用のアクセスSRを作ることにしました。

長くなったのでまた次回。

コメント

  1. 匿名 より:

    初めまして、いつも楽しみにしています。暇つぶしに遊んでいたのが、いつしか次のバージョンをワクワクしながら待つようになりました。緻密な作業の中、大変だと思いますが心の底から応援しています。DQ3に着目してくださって本当にありがとうございます。素晴らしい!

    管理者より返信:

    プレイしていただきありがとうございます。暇な時にたまーにやり返してもらえると嬉しいです。アイテム数追加も含め今やってる作業が恐らく最後のメジャーアップデートになると思います(流石にもうネタがない)。いくつかの追加要素のうちまだ手が全くついていないものが多いのでいつリリースになるかわかりませんが、気長に待っていてください。