先日「熱血アセンブラ入門を読んだ」という記事を書きましたが、
お正月休みで時間もあるので、個人的に気になっていたRXのアセンブラについて少し調べてみました。備忘録がてら調べた内容を挙げておきたいと思います。
調べた内容
開発環境はHEW(C/C++ compiler package for RX family V.1.02 Release 01)を使いアセンブラを見てみました。
解析してみたコードは「熱血アセンブラ入門」のsample.cのコードの一部と個人的に気になったコードの書き方などでどのようなアセンブラが出力されているのか確かめてみました。
HEWを使ってアセンブラを確認する方法は大きく2つあります。
1.アセンブラリストファイルを出力する。
"ビルド"→"RX Standard toolchain"→"コンパイラ"タブのカテゴリから"リスト"を選択し、"リスト出力"にチェックを入れます。
ビルドの成果物のディレクトリ(初期設定だとDebugやRelease)にファイル名.lstというファイルが出力されておりここにC言語のコードに対応するアセンブラが出力されます。
2.デバッグ中に逆アセンブリを表示する
HEWをデバッグ動作させている最中に"表示"→"逆アセンブリ"から該当ファイルの逆アセンブリを確認できます。
今回私はシミュレータによるデバッグにて2の方法で、逆アセンブリを確認してみました。
デバッグをしながらだと、レジスタやメモリ上の値を変えて実験できるので理解が深まります。
※1の方法も試してみましたが出力されるファイルに色々な情報が詰め込まれていて少し見づらかったということもあります。
予習
アセンブラを見てみる前にRXのアーキテクチャについてハードウェアマニュアルを読んで概要を押さえおきます。
- 制御レジスタ(9本)
- 割り込みスタックポインタ(ISP)
- ユーザスタックポインタ(USP)
- 割り込みテーブルレジスタ(INTB)
- プログラムカウンタ(PC)
- プロセッサステータスワード(PSW)
- バックアップPC(BPC)
- バックアップPSW(BPSW)
- 高速割り込みベクタレジスタ(FINTV)
- 浮動小数点ステータスワード(FPSW)
ちょっと変わったところだとバックアップPCとバックアップPSWというのがある点でしょうか。
マニュアルによれば高速割り込み時にPCとPSWの値を退避するレジスタのようです。
確かに割り込み処理の処理速度を高めるためには退避用のレジスタがあると便利ですね。
また、ISPという割り込みスタックポインタという割り込み専用のスタックポインタもあります。
こちらは、リアルタイムOSを使う際にタスクと割り込みでスタック領域を分ける事でRAMの使用量を節約するのに活用するそうです。
割り込み処理内でローカル変数を多用するような場合は役に立ちそうですね。
ただし、こういった特殊なレジスタが多くなるとベンダー(ルネサス)以外がコンパイラを作るときの苦労が増えそうではあります。
GCCの開発者はこういうCPU向けにコンパイラを移植する際にどこら辺まで気にされているか気になる所です。
※ルネサスのコンパイラではオプションでこれらのレジスタを活用するかどうかを決めれるようになっているようですが。
C言語ソースコードとアセンブラ出力結果
0 を返す
int return_zero() { return 0; }
6601 MOV.L #0H,R1 02 RTS
0を返す関数。
R1レジスタに0を代入しているので戻り値の受け渡しにはR1レジスタを使うようです。
#0H,R1と並んでいるのでオペランドの指定はsrc→dstの並びになるようです。
また、RXはCISC系のCPUなので命令長は可変長ですね。
1 を返す
int return_one() { return 1; }
6611 MOV.L #1H,R1 02 RTS
int型サイズを返す
int return_int_size() { return sizeof(int); }
6641 MOV.L #4H,R1 02 RTS
ポインタ型サイズを返す
int return_pointer_size() { return sizeof(int *); }
6641 MOV.L #4H,R1 02 RTS
RXは32bitマイコンですのでint型・ポインタ型は4byteになるようです。
short型サイズを返す
int return_short_size() { return sizeof(short); }
6621 MOV.L #2H,R1 02 RTS
shortは2byte。
longを返す
int return_long_size() { return sizeof(long); }
6641 MOV.L #4H,R1 02 RTS
long型は4byte。
short型の値を返す
short return_short() { return 0x7788; }
FB1A8877 MOV.L #7788H,R1 02 RTS
short型の値もR1レジスタでそのまま返しています。
レジスタへの値設定はMOV.L命令で行われています。
Lはロング(32bit)サイズ、Wはワード(16bit)、Bはバイト(8bit)になりますので
short型の値を返す場合でもレジスタには32bitで書き込みを行っているようです。(上位ビットは0埋め)
ちなみに呼び出し側でどうなるか気になったので
main() { volatile short hoge; hoge =return_short(); if (hoge != 0) { while(1); } }
というコードを書いてアセンブラを見てみました。
呼び出し側は
6040 SUB #4H,R0 399FFF BSR.W _return_short D301 MOV.W R1,[R0] DC0E MOV.W [R0],R14 610E CMP #0H,R14 13 BEQ.S 0FFFF85E0H 2E00 BRA.B 0FFFF85DEH
というアセンブラになっておりました。
ローカル変数hogeへの書き込みはMOV.W R1,[R0]となっているため16bitで書き込まれているように見えます。
ところが、if文の比較は
CMP #0H,R14となっております。CMPはサイズを指定していないので32bitの比較になりそうです。
おやおや!このままだと16bitと32bitの比較になるぞ!コンパイラのバグか!?
と不思議に思い、気になってソフトウェアマニュアルのMOV命令の章を確かめてみました。
その結果分かったのですが、レジスタへの転送はMOV.W,MOV.Bであっても32bitに拡張されて転送されるとのことでした。
やれやれ、人騒がせなアセンブラですね。だったらレジスタへの書き込みには最初からMOV.Lにしておけよと思うところでした。
ちなみにwhile(1)の無限ループは
2E00 BRA.B 0FFFF85DEH
で表現されています。
0FFFF85DEHというのはこの行のアドレスなのでRXでは、
無限ループ=同一行へのジャンプ命令
によって実現しているようです。
実は、RXのプログラムカウンタは「次命令のアドレス」ではなくて「現在実行中の命令アドレス」を指しているそうです。
なのでプログラムカウンタにアドレスを直接設定してもあまり意味がないのでこのような形になるのかもしれません(あくまで推測ですが)
long型の値を返す
long return_long() { return 0x778899aa; }
FB12AA998877 MOV.L #778899AAH,R1 02 RTS
long型も4byteなのでint型と特に違いは無いようです。
マイナス値を返す場合
short return_short_upper() { return 0xffee; }
FB16EE MOV.L #-12H,R1 02 RTS
マイナスの値も定数値として指定できるようです。
足し算1
int add(int a, int b) { return a + b; }
4B21 ADD R2,R1 02 RTS
足し算2
int add3(int a, int b, int c) { return a + b + c; }
4B21 ADD R2,R1 4B31 ADD R3,R1 02 RTS
インクリメント
int inc(int a) { return ++a; }
6211 ADD #1H,R1 02 RTS
ADD命令で単純に1加算しています。
今後やってみたい事
- FPGAを勉強して、簡単なCPUを作ってみたい。
- ちょっとアセンブラが読めるようになってきたので勉強がてらリバースエンジニアリングにでも手を出してみよう。
RXのアセンブラを見てみたので今度はARMのアセンブラも読んでみたいと思います。
amazonでARMのアセンブラに関する本も買ってしまいました。
- 作者: 出村成和
- 出版社/メーカー: シーアンドアール研究所
- 発売日: 2013/06/22
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (5件) を見る
そういえば、むかしリバースエンジニアリングの勉強用にと「はじめて読むPentiumマシン語入門」を買って読みましたが、これを機にもう一回読んでみたいと思います。
- 作者: 蒲地輝尚,水越康博
- 出版社/メーカー: アスキー
- 発売日: 2004/07/01
- メディア: 単行本
- 購入: 21人 クリック: 2,328回
- この商品を含むブログ (26件) を見る
最近ではソフトウェアの抽象化が進む一方で、アセンブラ言語のような下層の技術についてはあまり流行とかが無いです。
ブログや雑誌などでは、「Javascript界隈」とかよく言われますが、「アセンブラ界隈」とか「組込界隈」とはあまり言わないですもんね。
※「組込界隈」って文字が既に一見さんお断り的な重々しい雰囲気をだしている気がする(笑)
個人的には最新の技術動向を押さえて何か作るのも嫌いではないですが、技術の動作原理や構造を理解することの方がなんとなく性に合っているようなので今年もそのスタンスを変えずに勉強していきたいと思います。