ARMのゴテゴテした命令

ARM Cortex-Rを触っている話を書いているが、

アセンブラを触っているとサンプルコードにこんな命令が。

ldrhne r0, [lr, #-2]

ldrhneって一体どんな命令だと調べてもすぐには出てこない。

実はこれldr命令に2つの装飾が付いているのだ。


ARMの命令は基本的な命令にいろいろな付加機能を付けることができる。

1つが条件付き命令である。

プログラムでは演算結果に応じて処理を分岐することはよくある。

前の演算結果でZフラグが0(演算結果が0以外、引き算して0以外は不一致を表す)でLABEL1に分岐するのは、こう書く。

bne LABEL1

実はB命令をNE(Z=0)という条件で装飾したものという位置づけである。

なので、他の命令も多くはこういう装飾ができてしまう。

movne r0, #10

MOV命令をNEで装飾し、Z=0のときにr0に10を格納するという意味になる。

1命令やるかやらないか程度ならジャンプしなくてよいと。


各種演算命令では結果に応じてフラグを変更して分岐などに使うことができるが、

そのようなフラグを変えたくないというニーズもありうる。

各種の演算命令にSを付けるとフラグに反映するというルールがある。

subs r0,r0,r1
sub r2,r2,r3

こうやって並んでいるとr0-r1の計算結果に応じたフラグが残ると。

さらに各種の命令にはビットシフトを装飾できる。

orr r0,r0,r1,lsl #3

これで r0=r0|(r1<<8)というような操作になる。

なので単なるビットシフト命令はmovのエイリアスという扱いになるそうで、

lsl r0, r0, #3
mov r0, r0, lsl #3

上の書き方をしても下の書き方に相当する命令が生成されると。


メモリ上のデータをレジスタに格納するLDR命令にはビット幅のバリエーションがある。

ldrhだとハーフワード(16bit幅)、ldrbだとバイト幅と。

さらに符号拡張との組み合わせもあって、ldrshだと符号付きハーフワードと。

あと、参照先のアドレスへの加算も1命令で書けてしまう。

ldr r0,[r2, +#2]
ldr r0,[r2, r3, lsl #2]

なんて書き方をすると r0=*(r2+2) とか r0=(r2+r3<<2) のような意味になる。

レジスタの値をビットシフトして加算するというのは配列の参照で出番が多い。


で、冒頭に書いたのは ldr + h + ne と分解して読むことが出来て

メモリ上のlr+2のアドレスにある値をハーフワードで読んでr0に格納する、

という動作をフラグがZ=0の場合のみ実行するということを表している。

ゴテゴテした命令なので単純に調べてもひっかからないが、

分解すると実はそういう意味があると。


この命令、調べてみるとSVCハンドラのサンプルコードがひっかかる。

msr r0,spsr
tst r0,#0x20
ldrhne r0,[lr,#-2]
bicne r0,r0,#0xFF00
ldreq r0,[lr,#-4]
biceq r0,r0,#0xFF000000

実はSVC割り込みでSVC番号を確認するには、命令を確認しないといけない。

しかも厄介なことにCortex-M以外ではSVC命令が2種類考えられる。

1つは32bit固定のARM形式の命令、もう1つは可変長のThumb形式の命令。

SVC命令はThumb形式ならば16bitで表される。

そこで、SVC割り込み前のフラグ(SPSR)を見て、Tビットを判定する。

TビットはSVC割り込み前にThumb形式の命令を実行していた否かを表す。

lrレジスタには割り込みからの復帰先のアドレスが入っているので、

lrより1つ前の命令はSPSR.T=1の場合は-2、SPSR.T=0の場合は4引けばよい。

SPSR.Tの判定結果に応じてSVC命令部分をレジスタにロードする方法を分けていると。

それでARM命令だと下位24bit、Thumb形式だと下位8bitがSVC番号になっているので、

これを抽出するのにbic命令を使っているが、これも条件に応じて分けている。

bic命令は指定されたビットをクリアするという意味である。


Thumb形式で書かれたプログラムへジャンプするときは、

アドレスを奇数アドレスにすることで識別するという考えがある。

Cortex-MではThumb形式だけをサポートするから、プログラムは奇数アドレスでの表記ばかりになる。

こういう命令セットの使い分けも面倒な話である。

ちょっとこれでひっかかったところがあった。


というわけでARMはいろいろゴテゴテしているという話だった。

こんなの手で書こうって言っても無理があるので、

普通はコンパイラに任せればよいのだが、どうしてもアセンブラで書かないといけないところもある。

こんな条件付き命令とかガリガリ使うアセンブラを読み書きするのは初めてで、

なんだこれ? となるのも仕方ない話である。