前回は、Armらしい命令ということで、オペランドの取り扱いで特徴でてるじゃないかと思われるところ数点を取り上げさせていただきました。今回は、フラグですね。如何にもArmっぽいフラグの取り扱いをする命令です。
※「ぐだぐだ低レベルプログラミング」投稿順indexはこちら
例によってサンプル用にそそくさと書いたサブルーチンの仕様から。前回は、16進数文字列を符号無し32ビット整数に変換するルーチンをサンプルにしました。今回はその逆で、符号無し32ビット整数を16進文字列に変換するルーチンにしてみました。こちらの方が簡単かな。以下、呼び出し側のC言語から見える仕様です。
- 第1引数に変換すべき32ビット整数を入れておく
- 第2引数に変換後の16進数文字列を格納すべきバッファへのポインタを与える(9文字はいるスペースが必ず必要)
- 戻り値は常に0.これは変換が常に成功の意味(後で拡張するかも)
- 変換結果の文字列は0から9までの文字とAからFまでの文字からなる常に8文字+終端nullのASCIIZ文字列
関数プロトタイプで書いておくとこんな感じ。
unsigned int tohex(unsigned int arg, char* hexstr);
さて、前回のようにエラーを含めたいろいろな入力パターンが考えられる場合と違い、常に8文字に変換するだけなのでプログラムは超簡単です。
.syntax unified .global tohex .text .align 4 tohex: push {r2,r3} ldr r3, =28 1: lsr r2, r0, r3 and r2, r2, 0xf cmp r2, 10 @ A addhs r2, r2, 7 add r2, r2, 0x30 strb r2, [r1] add r1, r1, 1 subs r3, r3, 4 bge 1b ldr r0, =0 strb r0, [r1] pop {r2,r3} bx lr
この短さの幾分かは、これから取り上げさせていただく、独特の命令のお陰であります。さて、どんな計算をやっているのかは、見れば分かると言ってしまえばそれまでですが、不親切ですな。一応書きます。
- 数値をMSB側から4ビットずつ取り出す。
- とりだした4ビットの数が0-9なら0x30を足すとアスキー文字の0から9になる。
- 10以上だったら、さらに+7すれば英大文字のAからFになる
- 上記を8回繰り返す。
さて、実際のコードでは2と3は順序逆でしたが、3のところを担っているのが、以下のところです。
cmp r2, 10 addhs r2, r2, 7
レジスタ r2 に16進数1桁分、4ビットの数値が入っているので、これと10を比べる(cmp)、そして、10より大きい(hs)だったら、r2に7を足したものをr2へ代入する。なお、前回も書きましたが、.syntax unified と宣言しているので、「Armらしい」即値数値に#記号をつけるという習慣には従っておりませぬ。
上の addhs命令は、条件つき実行というやつですね。add命令にhsという条件が付いている。普通のプロセッサであれば、cmpした後、条件分岐でaddするかしないか決めるものですが、Armの場合は分岐する必要がなくなるわけです。勿論、大なり、小なり、イコール、ノットイコール、符号付き、符号無しなど各種条件が皆使えます。Armだけがこのような命令を持っている分けではないですが、ほとんどの命令に条件を追加できるという点で、Armの特徴の一つになっています。ただし、コンテンポラリーなプロセッサ設計上は、結構、邪魔くさい仕様だと思われるので、そのうち邪険にされる可能性も無きにしも非ず。ま、今のところは、大手を振って使えるので使わせいただきましょう。
さらに、細かくて見逃しそうなところなのですが、下の減算命令もちょっと特徴だしています。
subs r3, r3, 4
r3から即値4を引いて、r3にしまう(ここも即値には#つけないsyntaxです)。引き算するだけなら sub で済むのですが、ここは subs です。先ほどは、条件判断の結果で加算するかしないか決めるのにhsというものを書いたのですが、ここのsは違います。
sがついていたら、演算結果をフラグに反映する
ってことです。CISC系のプロセッサであれば、演算したら必ずフラグに反映する、というのが普通(逆に言うと演算すると以前のフラグは上書きされてしまう)ですし、RISC系のプロセッサであれば、フラグに反映する(その後の分岐前提)ための比較系の演算とフラグに反映しないピュアな演算がある、というのが普通じゃないかと思います。結構、Armは良いとこ取りで、
フラグに反映させるか、しないかは選べる
のであります。ここのsは反映させる意味です。つまり、引いた結果でフラグを変え、その次の条件分岐命令を制御しているわけです。
bge 1b
ge = greater or equal です。符号付き数としての計算とみて、引いた結果が0か正の数だったならば、1bへ飛ぶ。ここで 1b は前々回にやった、後ろ向きのローカルラベルへの分岐です。ラベル的には一番近い 1: に飛びます。
小ネタですが、フラグの使い方は結構大事じゃないかと思います。といって、未だに、迷うこと頻りなのですが。