6.5.3. C 言語からのハンドラのインストール

開発過程においては、例外ハンドラをメインアプリケーションからベクタに直接インストールしなければならない場合があります。このような場合には、必要とされる命令エンコーディング処理を適切なベクタアドレスに記述する必要があります。この作業は、ハンドラへの到達に分岐命令を使用する方法と、プログラムカウンタをロードする方法の両方で行うことができます。

分岐方式

必要な命令は以下のように構成できます。

  1. 例外ハンドラのアドレスを取得します。

  2. そのアドレスから、対応するベクタのアドレスを減算します。

  3. プリフェッチ分の 0x8 を減算します。

  4. バイトオフセットではなくワードオフセットになるように、上記の結果を 2 だけ右にシフトします。

  5. 得られた値の長さが 24 ビットであることを確認するため(分岐のオフセットは 24 ビットに制限されているため)、上位 8 ビットがクリアされていることを確認します。

  6. この結果と 0xEA000000 (分岐命令のオペコード)との論理和を取り、ベクタに配置する値を求めます。

Example 6.4 は、このアルゴリズムを実装する C 関数を示しています。

この C 関数は以下の引数を取ります。

  • ハンドラのアドレス

  • ハンドラをインストールする先のベクタのアドレス

この関数でハンドラをインストールしたり、ベクタの元の内容を戻したりすることができます。この結果を使用して、特定の例外に使用されるハンドラのチェインを作成できます。詳細については、例外ハンドラのチェインを参照して下さい。

以下のコードで上記のルーチンを呼び出し、IRQ ハンドラをインストールします。

unsigned *irqvec = (unsigned *)0x18;
Install_Handler ((unsigned)IRQHandler, irqvec);

この場合、戻された IRQ ベクタの元の内容は破棄されます。

Example 6.4. 分岐方式の実装

unsigned Install_Handler (unsigned routine, unsigned *vector)
/* Updates contents of 'vector' to contain branch instruction */
/* to reach ’routine’ from ’vector’.Function return value is */
/* original contents of 'vector'.*/
/* NB: ’Routine’ must be within range of 32MB from ’vector’.*/

{   unsigned vec, oldvec;
    vec = ((routine - (unsigned)vector - 0x8)>>2);
    if ((vec & 0xFF000000))
    {
        /* diagnose the fault */
        printf ("Installation of Handler failed");
        exit (1);
    }
    vec = 0xEA000000 | vec;
    oldvec = *vector;
    *vector = vec;
    return (oldvec);
}

PC のロード方法

必要な命令は以下のように構成できます。

  1. 例外ハンドラのアドレスを含むワードのアドレスを取得します。

  2. そのアドレスから、対応するベクタのアドレスを減算します。

  3. プリフェッチ分の 0x8 を減算します。

  4. 結果が 12 ビットで表現されていることを確認します。

  5. この結果と 0xe59FF000LDR pc, [pc,#offset] のオペコード)との論理和を取り、ベクタに配置する値を求めます。

  6. このハンドラのアドレスを記憶位置に配置します。

Example 6.5 は、この方式を実装する C ルーチンを示しています。

この例でも、戻される元の IRQ ベクタの内容は破棄されますが、この内容を使用してハンドラのチェインを作成できる場合があります。詳細については、例外ハンドラのチェインを参照して下さい。

Example 6.5. プログラムカウンタをロードする方法の実装

unsigned Install_Handler (unsigned location, unsigned *vector)

/* Updates contents of 'vector' to contain LDR pc, [pc, #offset] */
/* instruction to cause long branch to address in 'location'.*/
/* Function return value is original contents of 'vector'.*/

{   unsigned vec, oldvec;
    vec = ((unsigned)location - (unsigned)vector - 0x8) | 0xe59ff000;
    oldvec = *vector;
    *vector = vec;
    return (oldvec);
}

以下のコードで上記のルーチンを呼び出し、IRQ ハンドラをインストールします。

unsigned *irqvec = (unsigned *)0x18;
static unsigned pIRQ_Handler = (unsigned)IRQ_handler
Install_Handler (&pIRQHandler, irqvec);

Note

命令キャッシュとデータキャッシュが分離されているプロセッサを使用する場合は、キャッシュコヒーレンシの問題がベクタの新しい内容を使用するときの妨げにならないように注意する必要があります。

データキャッシュ(または少なくとも修正されたベクタを含むエントリ)は、新しいベクタの内容をメインメモリに書き込めるように、削除する必要があります。次に、新しいベクタの内容をメインメモリから読み出せるように、命令キャッシュをフラッシュする必要があります。

キャッシュの削除とフラッシュについては、ターゲットプロセッサのテクニカルリファレンスマニュアルを参照して下さい。

Copyright © 2002-2006 ARM Limited. All rights reserved.ARM DUI 0203GJ
Non-Confidential