2006.03.05
No.007 NDS改造に必要なARMの基礎知識

そもそもARMとは?
ニンテンドーDSのメイン/サブCPUとして採用されているARM(Advanced RISC Machines)は、
PSやN64で採用されているR系とは異なる仕様のRISCプロセッサです。
ほどほどの高性能かつ低消費電力という利点から、主に携帯端末に用いられることが多く、
それゆえに、ニンテンドーDSに非常に適したCPUと言えます。

CPUの特徴
・フラグフィールドの概念を用いて「ほぼ全ての命令に実行条件」を付けることが出来る。
・シフト(桁移動)命令が単独で存在していない(第2オペランドとして命令と同時実行)。
・複数レジスタを同時転送可能(ldm&stm)。
とまあ、ARMについて知らない人にとっては何が利点なのか理解に苦しむ素敵仕様満載です。

これらを全て説明していくと、それだけで本になってしまいますので、
今回は「あくまで改造の知識に限定」して説明します。

用語
まあこのくらい知っていればいいかなぁ…。
名称意味
レジスタ計算結果を一時的に格納しておく記憶素子。ARMで汎用的に使えるレジスタはr0〜r15の16個。
(実際にはr0〜r12とも言える。r13:スタックポインタ、r14:リターンアドレス、r15:プログラムカウンタに使用されている)
スタックポインタ一時的にレジスタの内容を逃がしておく場所(スタック)に対する指標。
r0〜r3の4つをスタックに逃がしてポインタを+4(-4)したり、など。
リターンアドレスARMではr14を「リンクレジスタ(lr)」と呼び、遠くの命令を呼ぶ(サブルーチンコール)前に、
コールの次の行のアドレスをr14に入れ、コール先の最後でr14の情報を便りに元の場所に帰る(bx r14)。
プログラムカウンタARMではr15を「プログラムカウンタ(pc)」と呼び、現在の命令の実行アドレスが格納される。
この命令に直接数値を代入してやると、本当に入れた値のアドレスにジャンプさせることもできる。
フラグフィールド計算した値がマイナスや0などの条件を満たすと変化し、値を利用して条件分岐を行ったりする。
(改造コードサーチに使うのであれば、実はこの動作を知らなくてもあまり問題が無かったりする)。
オペコード命令名のこと。加算命令add、減算命令sub、ジャンプ命令b、比較命令cmpなど、様々なものが用意されている。
オペランド命令に与える要素のこと。例えば add r0,r1,r2 (r0=r1+r2) であれば r0,r1,r2 がオペランドとなる。

命令(オペコード)
実際には結構たくさんあるのですが、パラメータ改造に必要なものは下のものくらいかなぁ。
命令名書式効果備考
addadd rd,rn,op2rd = rn + op2加算命令。add r0,r0,r0 (r0=r0+r0 ..要はr0×2)なんてことも可能。
subsub rd,rn,op2rd = rn - op2減算命令。sub r0,r0,#10 (r0=r0-10)とか。
movmov rn,op2rn = op2代入命令。mov r1,#0x4000000 (r1=0x4000000)とか。
cmpcmp rn,op2rn = op2比較命令。フラグフィールドにrn-op2の値を入れる(※詳細は後述)
ldrldr rd,addr_mode1rd=*((long)addr_mode1)ロード命令。アドレスaddr_mode1の値4バイトをレジスタrdに入れる。
ldrhldrh rd,addr_mode1rd=*((short)addr_mode1)ロード命令。アドレスaddr_mode1の値2バイトをレジスタrdに入れる。
ldrbldrb rd,addr_mode1rd=*((char)addr_mode1)ロード命令。アドレスaddr_mode1の値1バイトをレジスタrdに入れる。
strstr rd,addr_mode1*((long)addr_mode1)=rdストア命令。レジスタrdの4バイトをアドレスaddr_mode1に書き込む
strhstrh rd,addr_mode1*((short)addr_mode1)=rdストア命令。レジスタrdの2バイトをアドレスaddr_mode1に書き込む
strbstrb rd,addr_mode1*((char)addr_mode1)=rdストア命令。レジスタrdの1バイトをアドレスaddr_mode1に書き込む
bb addrr15(pc)=addrアドレスaddrにジャンプする。
blbl addrr14(lr)=r15+4 , r15=addrリターンアドレスをr14に入れてから、アドレスaddrにジャンプする。
bxbx regr15=regレジスタregの記すアドレスにジャンプ。ARM->THUMB切り替えにも使用。


cmp命令と条件分岐
cmp命令を実行したり、上記演算命令にsを付ける(adds、subsなど)ことでフラグフィールドが更新され、
その後に続く命令の実行条件を操ることができます。
例えば cmp r0,#0x00 を実行すると r0 - 0 の結果がフラグフィールドに反映されるわけですが、
よほど複雑なルーチンを解析する場合でなければ「r0と0を比較」とだけ考えて読み進むだけでも大丈夫です。

さて、命令に実行条件を付加する場合は…… addeq とか sublt とか movge とか(ぉ
要は先ほどの命令の末尾に2文字の条件コードを付加してやることで、フラグフィールドの内容によって
命令実行の可否を切り替えることができます。
例えば…
:02002F34 E3510000 cmp r1,#0x0
:02002F38 03A00001 moveq r0,#0x1 ;r0=1(0x1)
:02002F3C 13A00000 movne r0,#0x0 ;r0=0(0x0)
r1レジスタが0かどうかを比較し、2つのmov命令は eq(Equal=等しい) と ne(NotEqual=等しくない) の条件付きです。
この場合、r1が0であればr1=1が実行され、そうでなければr1=0が実行されます。

条件コードは以下の通り。
条件コード条件式の意味C言語風
eqEqual (等しい)if(rn==op2)
neNot Equal (等しくない)if(rn!=op2)
ge大きいもしくは等しい(符号あり)if((signed)rn>=(signed)op2)
csもしくはhs大きいもしくは等しい(符号無し)if((unsigned)rn>=(unsigned)op2)
gt大きい(符号あり)if((signed)rn>(signed)op2)
hi大きい(符号無し)if((unsigned)rn>(unsigned)op2)
le小さいもしくは等しい(符号あり)if((signed)rn<=(signed)op2)
ls小さいもしくは等しい(符号無し)if((unsigned)rn<=(unsigned)op2)
lt小さい(符号あり)if((signed)rn<(signed)op2)
ccもしくはlo小さい(符号無し)if((unsigned)rn<(unsigned)op2)
miMinus (0未満)if(rn-op2<0)
plPlus (0以上)if(rn-op2>=0)
vsオーバーフローしている---
vcオーバーフローしていない---
まあ、cs/cc/vs/vcを除けば、大体略字から意味がわかるのですが。

オペランド
さて、レジスタやメモリ内容のデータを加減算したり比較したりする要素であるオペランドですが、
シフト・ローテート・イミディエイトといった用語がバシバシ出てくるので詳細は省略です('A`)
r0,r1 であれば「レジスタr0とレジスタr1」、r5,#0x10 であれば「レジスタr5と16進数0x10」と、
その程度を覚えておき、シフトなどは必要なときに各自でリファレンスマニュアルなどを参照してください。

最後に
なお、プログラミングのための基礎知識を付けたいのであれば、
CQ出版(株)Interface 2006年2月号「アセンブラ・プログラミングの基礎知識」がオススメです。
改造の知識を付けたい場合でも、CPU構造・メモリアクセス・レジスタ・スタックといった概念を
ゼロから学ぶことが出来るのでバックナンバーを注文してでも購入する価値はあります。
(X86、PowerPC、ARM、SH4と、様々なCPUを対象とした解説があるのも魅力的です)。
ただし、あくまで「基礎」だけなので、分厚いリファレンスマニュアルを読まなくても済む〜…
なんてことはまずありませんので、ご注意を。
>>次の講座へ進む