SSブログ

CodeWarrior が作る ColdFire V1 の割り込みサービスルーチンを調べた [ColdFire (ColdeFire) V1]このエントリーを含むはてなブックマーク#

CodeWarrior V6.1 で ColdFire V1 のプログラムを作成するとき、 割り込みサービスルーチン (Interrupt Service Routine : ISR) を どうやって表現するかを考えました。 しかも、アセンブラで。

ColdFireでは、実行中のプログラムをある種の要因で中断する操作を「例外」と呼んでいます。 ここでは、なじみの言葉である「割り込み」を使っていますが、 扱っている内容は、「例外」でも同じです。

Cでの基本的な記述方法

Cで ISR を記述するときには、普通の関数定義の戻り値型の代わりに、 「__interrupt <ベクタ番号>」を追加します。

__interrupt VectorNumber_Vtrap0 trap0_isr (void) {
}

これで、CodeWarriorは、正しいISRを作成してくれます。

0x00000000                    _trap0_isr:
;                             trap0_isr:
0x00000000  0x4E73                   rte      
0x00000002  0x51FC                   trapf    

普通の関数の最後が"RTS" (ReTurn from Subroutine) 命令になっているところが、 "RTE" (ReTurn from Exception) 命令になっています。 この結果を見て、 「なんだ、そんなに難しい話じゃないんだな。」と、最初は思ったのです。

保護すべきレジスタは、どれだ

ColdFireでは、割り込みが発生してもレジスタを自動的にはスタックに退避してはくれません。 これは、HCS08と異なっている点です。 そのため、ISRの中でレジスタを退避させる操作が必要になってきます。

まずは、こんなプログラムを作成してみました。

typedef long Data;
typedef long *Pointer;

void func(void) {
  Data    x1,x2,x3,x4,x5,x6,x7,x8;
  Pointer y1,y2,y3,y4,y5,y6,y7,y8;
  for (x1=0,y1=0;x1<10;x1++,y1++)
  for (x2=0,y2=0;x2<10;x2++,y2++)
  for (x3=0,y3=0;x3<10;x3++,y3++)
  for (x4=0,y4=0;x4<10;x4++,y4++)
  for (x5=0,y5=0;x5<10;x5++,y5++)
  for (x6=0,y6=0;x6<10;x6++,y6++)
  for (x7=0,y7=0;x7<10;x7++,y7++)
  for (x8=0,y8=0;x8<10;x8++,y8++) {
  }
}
__interrupt VectorNumber_Vtrap0 trap0_isr (void) {
  Data    x1,x2,x3,x4,x5,x6,x7,x8;
  Pointer y1,y2,y3,y4,y5,y6,y7,y8;
  for (x1=0,y1=0;x1<10;x1++,y1++)
  for (x2=0,y2=0;x2<10;x2++,y2++)
  for (x3=0,y3=0;x3<10;x3++,y3++)
  for (x4=0,y4=0;x4<10;x4++,y4++)
  for (x5=0,y5=0;x5<10;x5++,y5++)
  for (x6=0,y6=0;x6<10;x6++,y6++)
  for (x7=0,y7=0;x7<10;x7++,y7++)
  for (x8=0,y8=0;x8<10;x8++,y8++) {
  }
}

プログラム自体に意味はありません。 全てのレジスタを使ってもまかなえないほどの局所変数を使わせたかったのです。 このプログラムをコンパイルさせると、それぞれの関数の冒頭は、以下のようになっています。

0x00000000                    _func:
;                             func:
0x00000000  0x4FEFFFD0               lea      -48(a7),a7
0x00000004  0x48EF5CF8000C           movem.l  d3-d7/a2-a4/a6,12(a7)

関数の中では、D0-D7/A0-A2/A4/A6を作業領域として使っています。 つまり、D0/D1/D2/A0/A1の五つのレジスタはまったく保護されていません。

0x00000000                    _trap0_isr:
;                             trap0_isr:
0x00000000  0x4FEFFFBC               lea      -68(a7),a7
0x00000004  0x48EF5FFE0010           movem.l  d1-d7/a0-a4/a6,16(a7)

これに対して、ISRの中では、D1-D7/A0-A4/A6を作業領域として使っています。 つまり、全てのレジスタが保護されていることになります。 なぜ、D0を使っていないのか? 理由は不明です。

関数の中にアセンブラを混ぜる

CPUの動作にかかわるような低レベルな操作は、アセンブラを使う必要があります。 Cで記述された関数の中にアセンブラでプログラムを記述するときには、 "__asm"キーワードを使います。

Data value;

__interrupt VectorNumber_Vtrap1 trap1_isr (void) {
  __asm {
    move usp,a0
    move (a0),d0
    move d0,value
  }
}

このISRでは、ユーザ・スタックの内容を取り出して変数valueに 代入しています。 取り出した内容に特に意味があるわけではありません。 このプログラムをコンパイルした結果を見てみます。

0x00000000                    _trap1_isr:
;                             trap1_isr:
0x00000000  0x2F08                   move.l   a0,-(a7)
0x00000002  0x2F00                   move.l   d0,-(a7)
;
;   53:     move usp,a0 
;
0x00000004  0x4E68                   move     usp,a0
;
;   54:     move (a0),d0 
;
0x00000006  0x2010                   move.l   (a0),d0
;
;   55:     move d0,value 
;   56:       } 
;
0x00000008  0x23C000000000           move.l   d0,_value
;
;   57: } 
;
0x0000000E  0x201F                   move.l   (a7)+,d0
0x00000010  0x205F                   movea.l  (a7)+,a0
0x00000012  0x4E73                   rte      

どうやら、アセンブラ・プログラムの中でD0とA0が作業領域として使われているのを認識して、 その値をスタックに退避させてくれるようです。

アセンブラだけで記述したC関数?

上と同じ操作をアセンブラだけで、このように書くことが出来ます。

asm __interrupt VectorNumber_Vtrap2 trap2_isr (void) {
  move usp,a0
  move (a0),d0
  move d0,value
  rte
}

これをコンパイルすると、このようになります。

0x00000000                    _trap2_isr:
;                             trap2_isr:
0x00000000  0x4E68                   move     usp,a0
;
;   61:   move (a0),d0 
;
0x00000002  0x2010                   move.l   (a0),d0
;
;   62:   move d0,value 
;
0x00000004  0x23C000000000           move.l   d0,_value
;
;   63:   rte 
;
0x0000000A  0x4E73                   rte      

この場合には、レジスタを退避させるコードは生成されません。 従って、正しいISRとするために、レジスタを退避させるコードを追加する必要があります。 ただ、コンテキストスイッチに使う場合のように、勝手にスタックを使われると困る場合もあるので、 一概にどちらが使いやすいとも言えません。 はっきり言えるのは、「コンパイル結果を確認しながら使う」事が必要だということでしょう。

Cとアセンブラの連携

このコンパイラのアセンブラは、かなり賢くて、Cで定義した変数などをそのまま使うことができます。

__interrupt VectorNumber_Vtrap3 trap3_isr (void) {
  Pointer y1;
  __asm {
    move usp,a0
    move a0,y1
  }
  value = *y1;
}

これもプログラム自身には、さして意味がありません。 uspレジスタの内容をCの局所変数であるy1に代入し、 Cの記述で、スタックの内容を取り出して、valueに保存します。 コンパイルするとこうなります。

0x00000000                    _trap3_isr:
;                             trap3_isr:
0x00000000  0x4FEFFFF0               lea      -16(a7),a7
0x00000004  0x48EF03020004           movem.l  d1/a0-a1,4(a7)
;
;   71:     move usp,a0 
;
0x0000000A  0x4E68                   move     usp,a0
;
;   72:     move a0,y1 
;   73:       } 
;
0x0000000C  0x2E88                   move.l   a0,(a7)
;
;   74:   value = *y1; 
;
0x0000000E  0x2257                   movea.l  (a7),a1
0x00000010  0x2211                   move.l   (a1),d1
0x00000012  0x2B410000               move.l   d1,_value(a5)
;
;   75: }
;
0x00000016  0x4CEF03020004           movem.l  4(a7),d1/a0-a1
0x0000001C  0x4FEF0010               lea      16(a7),a7
0x00000020  0x4E73                   rte      
0x00000022  0x51FC                   trapf    

Cでは書けないUSPレジスタの値を取り出す部分だけを アセンブラにしただけなので、 取り出した値の操作はCで記述することが出来て便利です。


nice!(0)  コメント(0)  トラックバック(0)  このエントリーを含むはてなブックマーク#

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

トラックバックの受付は締め切りました

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。