小坂の考察
test1はint(2byte)の変数を2つ受け取ってintの値を返している。
test1を呼び出す時,第1引数をR0に,第2引数をE0に載せて,test1にサブルーチンコールジャンプする。
test1では返す値をR0に載せてリターンジャンプしている。
test2はint(2byte)の変数を3つ受け取ってintの値を返している。
test2を呼び出す時,第1引数をR0に,第2引数をE0に,第3引数をR1に載せて,test2に飛ぶ。
test2では返す値をR0に載せて返している。
test3はlong int(4byte)の変数を2つ受け取ってlong intの値を返している。
test3を呼び出す時,第1引数をER0に,第2引数をER1に載せて,test3に飛ぶ。
test3では返す値をER0に載せて返している。
関数内ではER6を計算のために使うので,ER6の値を保護するため,push・popを行なって退避・復帰を行なっている。
test4はlong int(4byte)の変数を3つ受け取ってlong intの値を返している。
test4を呼び出す時,第1引数をER0に,第2引数をER1に載せて,第3引数をスタックに載せてtest4に飛ぶ。
test4では返す値をER0に載せて返している。
test4関数内ではER6を計算のために使うので,ER6の値を保護するため,push・popを行なって退避・復帰を行なっている。
関数に入ってきたときには,メモリのSP(stackpointer)の指す場所には,関数から戻るべき4byteのアドレスが載っており,
SP+4の位置には呼び出し側が載せた第3引数が載っている。
関数に入ってきたら,push ER6をするので,このpush命令直後では,SPの位置にpushされたER6の値,
SP+4の位置には関数から戻るべき4byteのアドレス,SP+8の位置には呼び出し側が載せた第3引数が載っている。
また,test4から戻った直後にはまだSPの位置には呼び出す前にpushした第3引数が入っているため,
これをpopしないとサブルーチンコール動作前の位置に戻らない。しかしpopすることでレジスタの不必要な変更をしたくない。そこでSPの値を4増やすだけで済む,ADDS.L #4,SPの命令を行なっている。
全体をまとめると,次のような規則になっているようである。
関数は他のところからも呼び出される可能性があるため,引数と返す値の取り扱いは,ある規則で統一的に作成されている。
呼び出し側もこの統一規則にしたがって引数を渡し,関数の値を受け取るようにすれば良い。
(1)関数呼び出しの時の引数に関して
- 2byteのint型なら,第1引数から順にR0,E0,R1,E1の順に載せられる。これ以上の数の引数はスタックに積まれる。
-
4byteのlong int型なら,第1引数がER0,第2引数がER1,第3以降の引数はスタックに積まれる。
いずれにしても,ER0とER1のみが,引数として利用され,ER2以降は引数伝達には使われない。
(2)関数の返す値について
- int型(2byte)ならR0に,long int型(4byte)ならER0に値を積んでいる。
(3)レジスタ利用の制限について
ER0,ER1の2つとER2以降のレジスタでは異なる制限がある
- 関数側では,ER0,ER1は必要な引数をこわさない限り自由に使える。
ER2,ER3,ER4,ER5,ER6に関しては呼び出し側で使っているかもしれないので,必要に応じてこれらのレジスタの値をスタックに退避
(push)させ,関数から戻る直前で復帰(pop)するように作る。
-
関数を呼び出す方は,ER0,ER1に関しては,引数渡しや関数の返す値に使う。もしこの2つのレジスタを使用中ならば,呼び出した関数中でこの2つは破壊されるかもしれないので,スタックに退避させるなどの対策をとる必要がある。
(上記の規則の一部は,下記のテストを追加することで確認できた。)
テストのCプログラム
int ans1;
int ans2;
test1(int a,int b,int c,int d)
{
return a+b+c+d;
}
test2(int a,int b,int c,int d,int e)
{
return a+b+c+d+e;
}
main()
{
ans1=test1(100,200,300,400);
ans2=test2(100,200,300,400,500);
}
翻訳されたアセンブリ言語ソース
_test1:
PUSH.W R6
MOV.W R0,R6
ADD.W E0,R6
ADD.W R1,R6
ADD.W E1,R6
MOV.W R6,R0
POP.W R6
RTS
_test2:
PUSH.W R6
MOV.W R0,R6
ADD.W E0,R6
ADD.W R1,R6
ADD.W E1,R6
MOV.W @(6:16,SP),R0
ADD.W R6,R0
POP.W R6
RTS
_main:
MOV.W #400:16,E1
MOV.W #300:16,R1
MOV.W #200:16,E0
MOV.W #100:16,R0
BSR _test1:8
MOV.W R0,@_ans1:24
MOV.W #500:16,R0
PUSH.W R0
MOV.W #400:16,E1
MOV.W #300:16,R1
MOV.W #200:16,E0
MOV.W #100:16,R0
BSR _test2:8
ADDS.L #2,SP
MOV.W R0,@_ans2:24
RTS
.SECTION B,DATA,ALIGN=2
_ans1:
.RES.W 1
_ans2:
.RES.W 1
.END