Cプログラムをアセンブリプログラムに変換


Copyright(C) 3Sep2008
Copyright(C) 22Dec2003
coskx 

【1】はじめに

 この文書は「H8/3048のC言語で書かれたプログラム」を「アセンブリ言語で書かれたプログラム」にコンパイラを使って変換する方法を紹介し,その後でコンパイラが書き出したアセンブリプログラムを考察する。
 ここで使用するコンパイラは日立コンパイラVer2である。日立コンパイラVer1にはこのC→ASM変換機能がないためできない。

サンプルのダウンロード

ここのサンプルではリンカオプションなどが考慮されていないため,実際に動かすことはできない。

 

【2】変換の方法

 サンプルを解凍すると,LEDを点滅させるプログラム「led1st.c」が入っている。
 このCプログラム「led1st.c」のアイコンを,「c2asm.cmd」にドラッグ&ドロップすると,「led1st.src」と「led1st.lst」が作られる。
 「led1st.src」がアセンブリプログラムとなっている。「led1st.lst」はCソースリストである。
 どちらも,テキストファイルなので,通常のエディタで中を見ることができる。

 

【3】サンプルプログラムの変換

「led1st.c」と「led1st.src」を見比べてみよう。

led1st.c (LED点滅プログラム)

/*  msecwait関数で1秒ごとのLEDのON-OFFを行う  */
#include <3048fone.h>

void msecwait(int msec)
/*msec間なにもしない時間稼ぎ関数*/
{
    int i,j;
    for (i=0;i<msec;i++) {
        for (j=0;j<3352;j++);    /*3352は実測によって求めた値*/
    }
}

main()
{
    /*P5の下位2ビットを出力に設定*/
    /* P5のDDRの下位2ビットに1を与えるとこの設定になる*/
    /*DDRとはDataDirectionRegister(データ方向設定レジスタ)*/
    P5.DDR =  0x3;  /*0x3 = 00000011(二進数)*/
    while(1) {/*これは無限ループ*/
        /*LED0をONにする  P5のDRの第0ビットを1にする*/
        /*DRとはDataRegister(データレジスタ)*/
        P5.DR.BIT.B0=1;
        /*LED1をOFFにする  P5のDRの第1ビットを0にする*/
        P5.DR.BIT.B1=0;
        msecwait(1000);/*1000msecの間なにもしない*/
        /*LED0をOFFにする  P5のDRの第0ビットを0にする*/
        P5.DR.BIT.B0=0;
        /*LED1をONにする  P5のDRの第1ビットを1にする*/
        P5.DR.BIT.B1=1;
        msecwait(1000);
    }
}



led1st.src (コンパイラによって生成されたもの)

          .CPU        300HA
          .EXPORT     _msecwait
          .EXPORT     _main
          .SECTION    P,CODE,ALIGN=2
;*** File led1st.c  , Line 4     ; block
_msecwait:                       ; function: msecwait
;*** File led1st.c  , Line 6     ; block
;*** File led1st.c  , Line 8     ; expression statement
          SUB.W       R1,R1
;*** File led1st.c  , Line 8     ; for
          BRA         L11:8
L10:                             
;*** File led1st.c  , Line 8     ; block
;*** File led1st.c  , Line 9     ; expression statement
          MOV.W       #3352:16,E0
;*** File led1st.c  , Line 9     ; do
L12:                             
;*** File led1st.c  , Line 9     ; expression statement
          DEC.W       #1,E0
          BNE         L12:8
;*** File led1st.c  , Line 8     ; expression statement
          INC.W       #1,R1
L11:                             
;*** File led1st.c  , Line 8     ; expression statement
          CMP.W       R0,R1
          BLT         L10:8
;*** File led1st.c  , Line 11    ; block
          RTS
;*** File led1st.c  , Line 13    ; block
_main:                           ; function: main
;*** File led1st.c  , Line 14    ; block
          MOV.W       #1000:16,R6
;*** File led1st.c  , Line 18    ; expression statement
          MOV.B       #3:8,R0L
          MOV.B       R0L,@16777160:8
;*** File led1st.c  , Line 19    ; while
L13:                             
;*** File led1st.c  , Line 19    ; block
;*** File led1st.c  , Line 22    ; expression statement
          BSET.B      #0,@16777162:8
;*** File led1st.c  , Line 24    ; expression statement
          BCLR.B      #1,@16777162:8
;*** File led1st.c  , Line 25    ; expression statement
          MOV.W       R6,R0
          BSR         _msecwait:8
;*** File led1st.c  , Line 27    ; expression statement
          BCLR.B      #0,@16777162:8
;*** File led1st.c  , Line 29    ; expression statement
          BSET.B      #1,@16777162:8
;*** File led1st.c  , Line 30    ; expression statement
          MOV.W       R6,R0
          BSR         _msecwait:8
;*** File led1st.c  , Line 19    ; expression statement
          BRA         L13:8
;*** File led1st.c  , Line 32    ; block
          .END


いくつかの注釈

「.」で始まると疑似命令(機械語に翻訳されるわけではないので疑似命令と呼ばれる)
.CPU        300HA       対象とするCPUの設定
                CPUはH8/300H(H8/3048はその1つ)でアドバンストモード

.EXPORT     _msecwait     この名前は外部からの参照を許す(別のソースファイルから参照可能)
.EXPORT     _main           同上
                Cプログラムソースのでの名前「main」はアセンブリソースでは
                「_main」のように変化している。
.SECTION    P,CODE,ALIGN=2   Pセクションが始まる。
                                Pセクションがどこに置かれるかはリンカスクリプト(.sub)に書いてあり
                                リンカによって配置される。
                
ALIGN=2というのは「メモリ偶数番地からコードを置く」の意味。
                (メモリ偶数番地からコードを置くと,メモリから命令を取り出しやすい)


「;」があると,その行の「;」以降は注釈

行の先頭から書かれている「xxxx:」はラベル(ジャンプ先や変数名)
ラベルは命令や値の書かれているアドレスを表わしている。

行の先頭に1つ以上のスペースまたはタブがあると実行命令

「「#」+値」 その値を表す。
   値が何バイト(何ビット)で構成されているのかが大事であり,
   これは,
   「.B」なら「Byte」なので1バイト幅,
   「.W」なら「Word」なので2バイト幅,
   「.L」なら「Long word」なので4バイト幅
   の値を表すことで解決している

   例えば,「MOV.W       #1000:16,R6」は16ビットであらわされている値1000をWord単位(2バイト)で
   レジスタR6に格納している。

「「@」+「ラベル」」(「「@」+「変数名」」,「「@」+「アドレス」」も同じ意味である) メモリ上のそのアドレスにある値を表す。
   アドレスはある1バイトを指しているので,値が何バイト(何ビット)で構成されているのかが大事である。
   (C言語のポインタ変数は,どの型をさすポインタかを明らかにしている。)

   これは,
   「.B」なら「Byte」なので1バイト幅,
   「.W」なら「Word」なので2バイト幅,
   
「.L」なら「Long word」なので4バイト幅
   の値を表すことで解決している
。例えば「MOV.B       R0L,@16777160:8」はレジスタR0LをByte単位で,
   アドレス16777160へ格納せよの意味になる。「:8」は機械語に変換する際にアドレスを8ビットで表せの意味だが
   まともに考えると16777160を8ビットで表せるわけがないが,H8では特別なアドレスになっていて機械語では
   修飾語句とともに8ビットであらわされるので問題ない。

不要な行を取り去って,プログラムの実行部分を解釈してみる。

led1st.srcの主要部分

_msecwait:                            ; function: msecwait 引数はR0に入っている
          SUB.W       R1,R1           ; int i は R1 を使用しており,0→R1 (引き算 SUBtract)
          BRA         L11:8           ; L11へ相対ジャンプ(ブランチとも言う BRAnch)
                                      ; 8は8ビットで相対アドレスを表すの意味

L10:                              
          MOV.W       #3352:16,E0     ; 3352(16ビットの値)→E0 (データの移動 MOVe,実際には格納する)
L12:                              
          DEC.W       #1,E0           ; E0-- (減算 DECrement) 
                                      ; 驚くことに,E0を減算して繰返しを行っている(最適化コンパイルの結果)
          BNE         L12:8           ; E0!=0 ならL12へジャンプ(条件ジャンプ Branch Not Equal to)
                                      ; 8は8ビットのアドレス増分で相対アドレスを表すの意味
          INC.W       #1,R1           ; R1++ (INCrement)
L11:                              
          CMP.W       R0,R1           ; R0とR1を比較
          BLT         L10:8           ; R1がR0より小さかったらL10へジャンプ
                                      ;(条件ジャンプ Branch Less than or Equal to)

                                      ; 8は8ビットで相対アドレスを表すの意味
          RTS                         ; サブルーチンからリターン (ReTun Subroutine)


_main:                                ; function: main
          MOV.W       #1000:16,R6     ; 値1000(16ビット定数)をR6に用意しておく
          MOV.B       #3:8,R0L        ; 3(8ビット定数) → R0L
          MOV.B       R0L,@16777160:8 ; R0L → 16777162番地へ8ビットサイズで格納 P5.DDR =  0x3;
L13:                              
          BSET.B      #0,@16777162:8  ; ビットセット命令 P5.DR.BIT.B0=1;
          BCLR.B      #1,@16777162:8  ; ビットクリア命令 P5.DR.BIT.B1=0;
          MOV.W       R6,R0           ; R6→R0 すなわち 1000→R0  R0は引数
          BSR         _msecwait:8     ; msecwait(1000); BranchSubRoutine
                                      ; 8は8ビットで相対アドレスを表すの意味
          BCLR.B      #0,@16777162:8  ; P5.DR.BIT.B0=0;
          BSET.B      #1,@16777162:8  ; P5.DR.BIT.B1=1;
          MOV.W       R6,R0           ; R6→R0 すなわち 1000→R0
          BSR         _msecwait:8     ; msecwait(1000);
                                      ; 8は8ビットで相対アドレスを表すの意味
          BRA         L13:8           ; 無条件にL13へジャンプ
                                      ; 8は8ビットで相対アドレスを表すの意味


アセンブリ言語のマニュアルは
H8命令ニーモニクの説明マニュアル 学内高速読み取りバッファより
を見ること。

 

【4】和と差のプログラム

グローバル変数で和と差を求めるCプログラムをアセンブリプログラムに変換してみよう。
H8コンパイラはintは16bit扱いである。(int = short int)

sumdiff.c

int data1=123;
int data2=456;
int sum;
int diff;

int main()
{
    sum=data1+data2;
    diff=data1-data2;
}

sumdiff.src

          .CPU        300HA
          .EXPORT     _data1
          .EXPORT     _data2
          .EXPORT     _sum
          .EXPORT     _diff
          .EXPORT     _main
          .SECTION    P,CODE,ALIGN=2
;*** File sumdiff.c , Line 6     ; block
_main:                           ; function: main
;*** File sumdiff.c , Line 7     ; block
;*** File sumdiff.c , Line 8     ; expression statement
          MOV.W       @_data1:24,R0
          MOV.W       @_data2:24,R1
          ADD.W       R1,R0
          MOV.W       R0,@_sum:24
;*** File sumdiff.c , Line 9     ; expression statement
          MOV.W       @_data1:24,R0
          SUB.W       R1,R0
          MOV.W       R0,@_diff:24
;*** File sumdiff.c , Line 10    ; block
          RTS
          .SECTION    D,DATA,ALIGN=2
_data1:                          ; static: data1
          .DATA.W     H'007B
_data2:                          ; static: data2
          .DATA.W     H'01C8
          .SECTION    B,DATA,ALIGN=2
_sum:                            ; static: sum
          .RES.W      1
_diff:                           ; static: diff
          .RES.W      1
          .END

プログラムはPセクションに,初期値のある変数はDセクションに,初期値のない変数はBセクションに置かれているのがわかる。
各セクションがメモリ上のどこのアドレスに置かれるかは,リンカスクリプトに書いてあり,リンカが決めてくれる。

_data1:                          ; static: data1
          .DATA.W     H'007B
変数data1をワードサイズ(2バイト)で確保し,
値0x7B(=123)を設定する

_sum:                            ; static: sum
          .RES.W      1
変数sumをワードサイズ(2バイト)で確保する。
「RES」は「Reserves data area」の意味

sumdiff.srcの主要部分

_main:                                ; function: main
          MOV.W       @_data1:24,R0   ; data1の値(アドレスが24ビットで表現)→(word幅で)→R0
          MOV.W       @_data2:24,R1   ; data2の値(アドレスが24ビットで表現)→(word幅で)→R1
          ADD.W       R1,R0           ; R0+R1→(word幅で)→R0
          MOV.W       R0,@_sum:24     ; R0→(word幅で)→sum(アドレスが24ビットで表現)へ保存

          MOV.W       @_data1:24,R0   ; data1の値(アドレスが24ビットで表現)→(word幅で)→R0
          SUB.W       R1,R0           ; R0-R1→(word幅で)→R0
          MOV.W       R0,@_diff:24    ; R0→(word幅で)→diff(アドレスが24ビットで表現)へ保存
          RTS                         ; mainを呼び出したスタートアップルーチンへ戻る

演習課題
1.sumdiff.cにおいてlong int型の変数に変更して,アセンブリプログラムに変換して,主要部分を説明しなさい。
int型変数,long int型変数は,何バイトの値として扱われているか。アセンブリプログラムから読み取りなさい。
sumdiff.srcとここで作成したアセンブリプログラムでは,どこが異なるのか,またそれはなぜか考察しなさい。
(C2asm01.txt)

 

【5】メモリブロックの転送とフィル

メモリのある領域を別の領域にコピーする作業と,ある領域を特定のデータで埋めるプログラムを考えよう。
ポインタを用いて,srcの10バイトをdstにコピーし,tgtの10バイトをすべて0x55で埋める。

memoryblock.c

char src[10]={0,1,2,3,4,5,6,7,8,9};
char dst[10];
char tgt[10];

main()
{
    int i;
    char *ps,*pd;
    ps=src;
    pd=dst;
    for (i=0;i<10;i++) {
        *pd=*ps;
        pd++;
        ps++;
    }
    pd=tgt;
    for (i=0;i<10;i++) {
        *pd=0x55;
        pd++;
    }
}

memoryblock.src

          .CPU        300HA
          .EXPORT     _src
          .EXPORT     _dst
          .EXPORT     _tgt
          .EXPORT     _main
          .SECTION    P,CODE,ALIGN=2
;*** File memorybloc, Line 5     ; block
_main:                           ; function: main
          PUSH.L      ER4
;*** File memorybloc, Line 6     ; block
;*** File memorybloc, Line 9     ; expression statement
          MOV.L       #_src:32,ER4
;*** File memorybloc, Line 10    ; expression statement
          MOV.L       #_dst:32,ER1
;*** File memorybloc, Line 11    ; expression statement
          MOV.W       #10:16,E0
;*** File memorybloc, Line 11    ; do
L11:                             
;*** File memorybloc, Line 11    ; block
;*** File memorybloc, Line 12    ; expression statement
          MOV.B       @ER4,R0L
          MOV.B       R0L,@ER1
;*** File memorybloc, Line 13    ; expression statement
          INC.L       #1,ER1
;*** File memorybloc, Line 14    ; expression statement
          INC.L       #1,ER4
;*** File memorybloc, Line 11    ; expression statement
          DEC.W       #1,E0
          BNE         L11:8
;*** File memorybloc, Line 16    ; expression statement
          MOV.L       #_tgt:32,ER1
;*** File memorybloc, Line 17    ; expression statement
          MOV.W       #10:16,E0
;*** File memorybloc, Line 17    ; do
L12:                             
;*** File memorybloc, Line 17    ; block
;*** File memorybloc, Line 18    ; expression statement
          MOV.B       #85:8,R0L
          MOV.B       R0L,@ER1
;*** File memorybloc, Line 19    ; expression statement
          INC.L       #1,ER1
;*** File memorybloc, Line 17    ; expression statement
          DEC.W       #1,E0
          BNE         L12:8
;*** File memorybloc, Line 21    ; block
          POP.L       ER4
          RTS
          .SECTION    D,DATA,ALIGN=2
_src:                            ; static: src
          .DATA.B     H'00,H'01,H'02,H'03,H'04,H'05,H'06,H'07,H'08,H'09
          .SECTION    B,DATA,ALIGN=2
_dst:                            ; static: dst
          .RES.B      10
_tgt:                            ; static: tgt
          .RES.B      10
          .END

配列であっても,初期化されている変数はセクションDに,初期化されていない変数はセクションBに割り当てられている。

_src:                            ; static: src
          .DATA.B     H'00,H'01,H'02,H'03,H'04,H'05,H'06,H'07,H'08,H'09
変数srcをバイトサイズで10個確保し,
値0,1,2,3,4,5,6,7,8,9を設定する

memoryblock.srcの主要部分

_main:                             ; function: main
          PUSH.L      ER4          ; ER2〜ER7を使う場合は使うルーチン内でstackへ退避させ,
                                   ;  ルーチンから戻る時に復帰させる (コンパイラ仕様に依存)
          MOV.L       #_src:32,ER4 ; srcのアドレス→ER4 すなわちER4はポインタ変数ps
          MOV.L       #_dst:32,ER1 ; dstのアドレス→ER1 すなわちER1はポインタ変数pd
          MOV.W       #10:16,E0    ; 10→E0
L11:                              
          MOV.B       @ER4,R0L     ; ER4の指すアドレスのメモリの内容→R0L ★注1
          MOV.B       R0L,@ER1     ; R0L→ER1の指すアドレスへ保存
          INC.L       #1,ER1       ; ER1++ (pd++)
          INC.L       #1,ER4       ; ER4++ (ps++)
          DEC.W       #1,E0        ; 逆算カウンタ E0--
          BNE         L11:8        ; E0!=0の場合はL11へジャンプ

          MOV.L       #_tgt:32,ER1 ; tgtのアドレス→ER1 すなわちER1はポインタ変数pd
          MOV.W       #10:16,E0    ; 10→E0
L12:                              
          MOV.B       #85:8,R0L    ; 0x55→R0L
          MOV.B       R0L,@ER1     ; R0L→ER1の指すアドレスへ保存
          INC.L       #1,ER1       ; ER1++ (pd++)
          DEC.W       #1,E0        ; 逆算カウンタ E0--
          BNE         L12:8        ; E0!=0の場合はL12へジャンプ
          POP.L       ER4          ; ER4の復帰
          RTS

★注1 例えば,メモリのアドレスff800に1バイトの値123があり,レジスタER4はff800になっていた場合,
「MOV.B       @ER4,R0L」ではER4の保存値をアドレスとみなし,そ
アドレスに保存されている値123が,Byte幅で移動し,8ビット幅のレジス
タR0Lの値になる。

メモリのアドレスff800に1バイトの値0x45があり,ff801に値0x78があり,レジスタER4はff800になっていた場合,
「MOV.W       @ER4,R0」ではER4
の保存値をアドレスとみなし,そのアドレスのメモリのword幅(連続した2バイト)の内容0x4578が,word幅で移動し,16ビット幅のレジスタR0の値になる。

★mov命令の表記例

mov.w #123:16,r0       16ビット幅の値123をr0に保存
mov.w @123:24,r0       24ビット幅で表わされるアドレス123に保存されている値をr0に保存
            正確にはアドレス123から始まるWord幅の値(2バイトの値)
mov.w @abc:24,r0       ラベルabcで示され,24ビット幅で表わされるアドレス
に保存されている値をr0に保存
mov.w @er3,r0          レジスタer3(32ビット)に保存されている値をアドレスとみなし,そのアドレス
に保存
            されている値
をr0に保存

【6】関数呼び出し(2引数)と関数の戻り値

int型(short int)の引数を2つ受けて,int型(short int)の値を返す関数を呼び出すところを見てみよう。
コンパイラが,引数を持つ関数を機械語に変換する時は常に同じルールで変換する。また,関数値を持ち帰る場合についても,常に同じルールで変換する。
このルールがあるから,一度作成した関数を,別なプログラムで再利用してもうまく動作することができる。
多くのライブラリ関数は,機械語の関数となって,提供されているが,このルールが守られてる。
このルールはコンパイラのマニュアルに書いてあるが,自分でテストプログラムを作成してルールを読み取ってみる。

Cからアセンブリで書かれた関数を呼び出す場合や,アセンブリプログラムからCで書かれた関数を呼び出す場合には,ここにあるような簡単な検証で,関数呼び出しのルールを読み取ってから作成することになる。

add.c

int data1=128;
int data2=256;
int sum;

int addfunc(int a, int b)
{
    int ans;
    ans=a+b;
    return ans;
}

main()
{
    sum=addfunc(data1,data2);
}

add.src

          .CPU        300HA
          .EXPORT     _data1
          .EXPORT     _data2
          .EXPORT     _sum
          .EXPORT     _addfunc
          .EXPORT     _main
          .SECTION    P,CODE,ALIGN=2
;*** File add.c     , Line 5     ; block
_addfunc:                        ; function: addfunc
;*** File add.c     , Line 6     ; block
          MOV.W       R0,R1
;*** File add.c     , Line 8     ; expression statement
          ADD.W       E0,R1
;*** File add.c     , Line 9     ; return
          MOV.W       R1,R0
;*** File add.c     , Line 10    ; block
          RTS
;*** File add.c     , Line 12    ; block
_main:                           ; function: main
;*** File add.c     , Line 13    ; block
;*** File add.c     , Line 14    ; expression statement
          MOV.W       @_data2:24,E0
          MOV.W       @_data1:24,R0
          BSR         _addfunc:8
          MOV.W       R0,@_sum:24
;*** File add.c     , Line 15    ; block
          RTS
          .SECTION    D,DATA,ALIGN=2
_data1:                          ; static: data1
          .DATA.W     H'0080
_data2:                          ; static: data2
          .DATA.W     H'0100
          .SECTION    B,DATA,ALIGN=2
_sum:                            ; static: sum
          .RES.W      1
          .END

add.srcの主要部分

_addfunc:                            ; function: addfunc
          MOV.W       R0,R1          ; R0 → R1
          ADD.W       E0,R1          ; R1 + E0 → R1
          MOV.W       R1,R0          ; R1 → R0 頭悪いようです
          RTS                        ; R0に戻り値を積んで戻ります

_main:                               ; function: main
          MOV.W       @_data2:24,E0  ; 第2引数をE0に積む
          MOV.W       @_data1:24,R0  ; 第1引数をR0に積む
          BSR         _addfunc:8     ; addfunc(R0,E0)

          MOV.W       R0,@_sum:24    ; R0の戻り値をsumに保存

          RTS

_mainの中で,第1引数はR0,第2引数はE0に積んで,関数addfunc()に行く。
関数addfunc()の中では,戻り値をR0に載せて,_mainに戻る。
これらの様子はコンパイラに依存する。(コンパイラマニュアルを参照)

関数addfunc()は無駄があります。第1引数R0,第2引数E0の和をR0に戻せばよいのだから,次のように書けるはずである。

関数addfuncの改善

_addfunc:                            ; function: addfunc
          ADD.W       E0,R0          ; R0 + E0 → R0
          RTS                        ; R0に戻り値を積んで戻ります

このように,Cコンパイラが作成した冗長なアセンブリプログラムを手で最適なプログラムに直すことを「hand optimize」と言う。

演習課題
2.次のCプログラムを,アセンブリプログラムに変換して,主要部分を説明しなさい。また4つの関数がどのようにアセンブリ言語に変換されているか違いを調べ,気づいた事柄・考察した事柄を述べなさい。(C2asm02.txt)
ヒント 関数test1では,どうせ何も使われないlocalsumの値の取扱いは...

int globalsum=0;

void test1(void)
{
    int localsum=0;
    int i;
    for (i=1;i<=10;i++) {
        localsum+=i;
    }
}

int test2(void)
{
    int localsum=0;
    int i;
    for (i=1;i<=10;i++) {
        localsum+=i;
    }
    return localsum;
}

void test3(void)
{
    static int staticsum;
    int i;
    for (i=1;i<=10;i++) {
        staticsum+=i;
    }
}

void test4(void)
{
    int i;
    for (i=1;i<=10;i++) {
        globalsum+=i;
    }
}

3.次のCプログラムを,アセンブリプログラムに変換して,主要部分を説明しなさい。また 4つの関数がどのようにアセンブリ言語に変換されているか違いを調べ,気づいた事柄・考察した事柄を述べなさい。1行ずつの説明はなくてもよい。特に第1 引数,第2引数,...はどのようにして関数に運ばれるのか,関数の戻す値はどのように運ばれるのかに着目して説明しなさい。

引数の取り扱い,戻り値の取り扱いのルールを明らかにしておくと,Cプログラムからアセンブリで記述した関数を呼び出す場合や,アセンブリプログラムからCで書いた関数を呼び出す時の作法がわかる。

ヒント1 0x123,0x456,0x789は10進表現では...
ヒント2 「MOV.L       @(8:16,SP),ER0」は,移動するデータはLongwordだから4バイト
     「@(8:16,SP)」は「スタックポインタの値+8(16ビット)」をアドレスとして,
     
そのアドレスの位置から始まる4バイトのデータをER0に代入する という意味である。
     例えば SPにはfff00が入っているとすると,アドレスfff08〜fff0bまでに置かれてい
     る4バイトのデータをER0に代入するの意味となる。
ヒント3 小坂ならこんな考察になります。
(C2asm03.txt)

int ans1;
long int ans2;

int test1(int a, int b)
{
    int ret;
    ret=a-b;
    return ret;
}

int test2(int a, int b, int c)
{
    int ret;
    ret=a-b-(c-1);
    return ret;
}

long int test3(long int a, long int b)
{
    long int ret;
    ret=a-b;
    return ret;
}

long int test4(long int a, long int b, long int c)
{
    long int ret;
    ret=a-b-(c-1);
    return ret;
}

int main(void)
{
    ans1=test1(0x123,0x456);
    ans1=test2(0x123,0x456,0x789);
    ans2=test3(0x123,0x456);
    ans2=test4(0x123,0x456,0x789);
}

C2asm03のアセンブリソース読み取りのためのヒント
mov.l @(8,sp),er1」の意味の確認
スタックの動きを見ること。
なおスタック付近の使用していないメモリは薄い色にしている。
説明
: a ff200
  FF200   >  push.l er2
  FF204   >  bsr ff208:8
  FF206   >  nop
  FF208   >  push.l er0
  FF20C   >  mov.l @(8,sp),er1
  FF212   >  pop.l er0
  FF216   >  rts
  FF218   >  .
: da ff200 ff217
  <ADDR>  <CODE>                <MNEMONIC> <OPERAND>
  FF200   01006DF2              PUSH.L     ER2
  FF204   5502                  BSR        FF208:8
  FF206   0000                  NOP       
  FF208   01006DF0              PUSH.L     ER0
  FF20C   01006F710008          MOV.L      @(H'00008:16,ER7),ER1
  FF212   01006D70              POP.L      ER0
  FF216   5470                  RTS       
: r
  PC=000000  CCR=80:I.......  SP=00FFFF10
  ER0=00000000  ER1=00000000  ER2=00000000  ER3=00000000
  ER4=00000000  ER5=00000000  ER6=00000000  ER7=00FFFF10
: .pc
  PC=000000  ?  ff200
  CCR=80  ?  .
: .er0
  ER0=00000000  ?  11111111
  ER1=00000000  ?  22222222
  ER2=00000000  ?  33333333
  ER3=00000000  ?  .
: r
  PC=0FF200  CCR=80:I.......  SP=00FFFF10
  ER0=11111111  ER1=22222222  ER2=33333333  ER3=00000000
  ER4=00000000  ER5=00000000  ER6=00000000  ER7=00FFFF10
: d fff00 fff1f
  <ADDR>                  <  D  A  T  A  >                     < ASCII CODE >
  FFF00   00 00 FF FF 08 00 FF FF  80 00 FF FF 00 00 FF FF   "................"
  FFF10   FF FF FF FF FF FF FF FF  FF FF FF FF 7F 00 00 00   "................"
: s
  PC=0FF204  CCR=80:I.......  SP=00FFFF0C
  ER0=11111111  ER1=22222222  ER2=33333333  ER3=00000000
  ER4=00000000  ER5=00000000  ER6=00000000  ER7=00FFFF0C
  FF200   01006DF2              PUSH.L     ER2
: d fff00 fff1f
  <ADDR>                  <  D  A  T  A  >                     < ASCII CODE >
  FFF00   00 00 FF FF 80 80 29 B0  00 0F F2 06 33 33 33 33   "......).....3333"
  FFF10   FF FF FF FF FF FF FF FF  FF FF FF FF 7F 00 00 00   "................"
: s
  PC=0FF208  CCR=80:I.......  SP=00FFFF08
  ER0=11111111  ER1=22222222  ER2=33333333  ER3=00000000
  ER4=00000000  ER5=00000000  ER6=00000000  ER7=00FFFF08
  FF204   5502                  BSR        FF208:8
: d fff00 fff1f
  <ADDR>                  <  D  A  T  A  >                     < ASCII CODE >
  FFF00   80 80 29 B0 00 0F F2 0A  00 0F F2 06 33 33 33 33   "..).........3333"
  FFF10   FF FF FF FF FF FF FF FF  FF FF FF FF 7F 00 00 00   "................"
: s
  PC=0FF20C  CCR=80:I.......  SP=00FFFF04
  ER0=11111111  ER1=22222222  ER2=33333333  ER3=00000000
  ER4=00000000  ER5=00000000  ER6=00000000  ER7=00FFFF04
  FF208   01006DF0              PUSH.L     ER0
: d fff00 fff1f
  <ADDR>                  <  D  A  T  A  >                     < ASCII CODE >
  FFF00   00 0F F2 0E 11 11 11 11  00 0F F2 06 33 33 33 33   "............3333"
  FFF10   FF FF FF FF FF FF FF FF  FF FF FF FF 7F 00 00 00   "................"
: s
  PC=0FF212  CCR=80:I.......  SP=00FFFF04
  ER0=11111111  ER1=33333333  ER2=33333333  ER3=00000000
  ER4=00000000  ER5=00000000  ER6=00000000  ER7=00FFFF04
  FF20C   01006F710008          MOV.L      @(H'00008:16,ER7),ER1
: d fff00 fff1f
  <ADDR>                  <  D  A  T  A  >                     < ASCII CODE >
  FFF00   00 0F F2 14 11 11 11 11  00 0F F2 06 33 33 33 33   "............3333"
  FFF10   FF FF FF FF FF FF FF FF  FF FF FF FF 7F 00 00 00   "................"
: s
  PC=0FF216  CCR=80:I.......  SP=00FFFF08
  ER0=11111111  ER1=33333333  ER2=33333333  ER3=00000000
  ER4=00000000  ER5=00000000  ER6=00000000  ER7=00FFFF08
  FF212   01006D70              POP.L      ER0
: d fff00 fff1f
  <ADDR>                  <  D  A  T  A  >                     < ASCII CODE >
  FFF00   80 80 29 B0 00 0F F2 18  00 0F F2 06 33 33 33 33   "..).........3333"
  FFF10   FF FF FF FF FF FF FF FF  FF FF FF FF 7F 00 00 00   "................"
: s
  PC=0FF206  CCR=80:I.......  SP=00FFFF0C
  ER0=11111111  ER1=33333333  ER2=33333333  ER3=00000000
  ER4=00000000  ER5=00000000  ER6=00000000  ER7=00FFFF0C
  FF216   5470                  RTS       
: d fff00 fff1f
  <ADDR>                  <  D  A  T  A  >                     < ASCII CODE >
  FFF00   80 80 29 B0 80 80 29 B0  00 0F F2 08 33 33 33 33   "..)...).....3333"
  FFF10   FF FF FF FF FF FF FF FF  FF FF FF FF 7F 00 00 00   "................"
: s
  PC=0FF208  CCR=80:I.......  SP=00FFFF0C
  ER0=11111111  ER1=33333333  ER2=33333333  ER3=00000000
  ER4=00000000  ER5=00000000  ER6=00000000  ER7=00FFFF0C
  FF206   0000                  NOP       


er2のスタックへの保存
ff208から始まるサブルーチンへ

サブルーチンの先頭でer0をスタックへ
スタックポインタ相対アドレス8にあるデータをer1へ格納
er0の復帰
サブルーチンからの復帰











各レジスタの確認


PCを上記のプログラムの先頭に設定



ER0,ER1,ER2の内容を追跡容易な値に変更




実行直前の各レジスタの確認


スタックポインタの指す番地周辺のメモリの確認
まだ有効なデータはない
SPはFFF10を指している


PUSH.L ER2の実行
SPが4つ上の(若い番地の)FFFF0Cを指している



スタックポインタの指す番地周辺のメモリの確認
ER2の33333333がスタックに格納されている

サブルーチンコール
PCはサブルーチンの先頭アドレスになり
スタックに戻り番地FF206を保存したから,
SPが4つ上の(若い番地の)FFFF08指している


スタックポインタの指す番地周辺のメモリの確認
SPが指す場所に戻り番地FF206がある


PUSH.L ER0の実行
SPがさらに4つ上の(若い番地の)FFFF04を指している



スタックポインタの指す番地周辺のメモリの確認
SPが指す場所にER0の値11111111がある


「SP+8のアドレス」にあるデータをER1へ
すなわち,このサブルーチン呼び出し直前にスタックに
積まれた値をER1に取り出したことになる。


スタックポインタの指す番地周辺のメモリの確認
スタックは変化しない


スタックに退避したデータをER0に復帰
SPは4つ下にさがる


スタックポインタの指す番地周辺のメモリの確認
(スタックは変化しないはずだが,モニタプログラムが
スタックの有効領域以外を使っているため,使用を
終わった領域は壊れている)

サブルーチンから戻る
スタックから戻り番地を取り出してPCに代入
SPは4つ下にさがる


スタックポインタの指す番地周辺のメモリの確認

1つのデータのみスタックに残っている