ColdFire でアセンブラ (2) - レジスタを使ってくれ [ColdFire (ColdeFire) V1]
前回は一部だけをアセンブラで書いた関数を作りました。 今回は、全てをアセンブラで記述します。
関数ごとアセンブラにする
関数ごとアセンブラで記述するには、関数に"__asm""指示子をつけます。
__asm dword func2(dword a, dword b) { dword x; move a,x // x = a add b,x // x *= b move x,d0 // return value rts }
前回と同じように二つの引数の和を返す関数です。 作業領域として使用している変数xを使って、 直接加算操作を行っています。 コンパイルすると、このようになります。
; 29: __asm dword func2(dword a, dword b) { ; 30: dword x; ; 31: move a,x // x = a 0x00000000 _func2: ; func2: 0x00000000 0x2E80 move.l d0,(a7) ; ; 32: add b,x // x *= b 0x00000002 0xD397 add.l d1,(a7) ; ; 33: move x,d0 // return value 0x00000004 0x2017 move.l (a7),d0 ; ; 34: rts 0x00000006 0x4E75 rts
一見、うまくできたように見えますが、 作業領域として使用している(a7)には、 リターン・アドレスが入っているため、 関数から戻るときに暴走してしまいます。
作業領域を安全に確保する
先のプログラムでは、作業領域が確保されませんでした。 作業領域を確実に確保するために、 frallocとfrfreeという 擬似命令が用意されています。
__asm dword func3(dword a, dword b) { dword x; fralloc move a,x // x = a add b,x // x *= b move x,d0 // return value frfree rts }
アセンブラの記述の前後にfrallocとfrfreeを 入れるだけです。
; 23: __asm dword func3(dword a, dword b) { ; 23: { ; 24: dword x; ; 25: fralloc 0x00000000 _func3: ; func3: 0x00000000 0x4E560000 link a6,#0 ; ; 26: move a,x // x = a 0x00000004 0x2400 move.l d0,d2 ; ; 27: add b,x // x *= b 0x00000006 0xD481 add.l d1,d2 ; ; 28: move x,d0 // return value 0x00000008 0x2002 move.l d2,d0 ; ; 29: frfree 0x0000000A 0x4E5E unlk a6 ; ; 30: rts 0x0000000C 0x4E75 rts 0x0000000E 0x51FC trapf
frallocは、レジスタに優先的に作業領域を割り当てます。 このため、変数xもレジスタに割り当てられました。
局所変数が多くなったらどうなる
上の例では、レジスタですべての作業領域をまかなうことができました。 それでは、レジスタでまかなえないほど多くの局所変数が定義されたらどうなるでしょうか。
__asm dword func4(dword a, dword b) { dword x, w1, w2, w3, w4; fralloc move a,x // x = a add b,x // x *= b add x,w1 add x,w2 add x,w3 add x,w4 move x,d0 // return value frfree rts }
局所変数w1からw4が、 新たに使用されています。 もちろん、このプログラムに深い意味はありません。 コンパイルすると、このようになります。
; 33: __asm dword func4(dword a, dword b) { ; 33: { ; 34: dword x, w1,w2,w3,w4; ; 35: fralloc 0x00000000 _func4: ; func4: 0x00000000 0x4E560000 link a6,#0 0x00000004 0x4FEFFFF4 lea -12(a7),a7 0x00000008 0x48D700E0 movem.l d5-d7,(a7) ; ; 36: move a,x // x = a 0x0000000C 0x2400 move.l d0,d2 ; ; 37: add b,x // x *= b 0x0000000E 0xD481 add.l d1,d2 ; ; 38: add x,w1 0x00000010 0xDE82 add.l d2,d7 ; ; 39: add x,w2 0x00000012 0xDC82 add.l d2,d6 ; ; 40: add x,w3 0x00000014 0xDA82 add.l d2,d5 ; ; 41: add x,w4 0x00000016 0xD282 add.l d2,d1 ; ; 42: move x,d0 // return value 0x00000018 0x2002 move.l d2,d0 ; ; 43: frfree 0x0000001A 0x4CD700E0 movem.l (a7),d5-d7 0x0000001E 0x4E5E unlk a6 ; ; 44: rts 0x00000020 0x4E75 rts 0x00000022 0x51FC trapf
関数の最初の部分で、 スタックに12バイトのエリアを確保して、そこにD5-D7のレジスタを退避させています。 これで、多くの作業領域を使用することができるようになりました。
なるほど!、このCソースにアセンブラコードを容易に埋め込める機能は便利ですが、挙動を理解して使わないとあれですね。
特に先頭の例題なんか、リストを出力するなり逆アセンブルするまで嵌っていそう。
by hamayan (2008-01-20 22:14)
基本的に、コンパイラを信用していないので、すっかり Disassembler とお友達です。
他にも理解しがたい現象(ミスアセンブル?)も見つけちゃったので、後ほど書きます。
by noritan (2008-01-21 08:09)