6.6.6. アプリケーションからの動的な SVC の呼び出し

状況によっては、SVC 番号がランタイム時までわからない SVC を呼び出さなければならない場合があります。このような状況は、例えば 1 つのオブジェクト上で実行される複数の処理が関連し合っていて、各処理ごとに SVC があるような場合に発生します。このような場合には、前述の方法は適切ではありません。

この状況には、以下のような方法で対処できます。

2 番目の方法としては、要求される処理を示す値をレジスタ(通常は r0 または r12)に渡すことで、アセンブリ言語で実装できます。これで、SVC ハンドラが適切なレジスタ内の値を基に動作するように書き直すことができます。

場合によっては値をコメントフィールドの SVC に渡す必要があるため、これらの 2 つの方法を組み合わせて使用することもあります。

例えば、オペレーティングシステムが 1 つの SVC 命令しか使用せず、レジスタを使用して要求される処理番号を渡すとします。この場合は残りの SVC 空間をアプリケーション固有の SVC に使用することができます。この方法は、特定のアプリケーションで、命令からの SVC 番号の抽出にかかるオーバーヘッドが大きすぎる場合に利用できます。ARM (0x123456)および Thumb (0xAB)のセミホステッド SVC は、この方法で実装されます。

Example 6.12 は、C の関数呼び出しをセミホスティング SVC に対応付けるときの __svc の使用方法を示しています。このサンプルは、主なサンプルディレクトリ ...\emb_sw_dev\source\retarget.cretarget.c に収録されています。

Example 6.12. C 関数のセミホスティング SVC への対応付け

#ifdef __thumb
/* Thumb Semihosting */
#define SemiSVC 0xAB
#else
/* ARM Semihosting */
#define SemiSVC 0x123456
#endif

/* Semihosting SVC to write a character */
__svc(SemiSVC) void Semihosting(unsigned op, char *c);
#define WriteC(c) Semihosting (0x3,c)

void write_a_character(int ch)
{
    char tempch = ch;
    WriteC( &tempch );
}

コンパイラには、r12 を使用して要求される処理番号の受け渡しを行うためのメカニズムが組み込まれています。AAPCS では r12ip レジスタであり、関数呼び出しの間だけ専用の役割が与えられます。それ以外のときは、このレジスタをスクラッチレジスタとして使用できます。前述のとおり、汎用 SVC への引数はレジスタ r0r3 で渡され、それらの値は必要に応じて r0r3 に返されます(アプリケーションからの SVC の呼び出し参照)。r12 に渡す処理番号は、汎用 SVC によって呼び出される SVC の値にすることができますが、必ずしもそうである必要はありません。

Example 6.13 は、汎用 SWI、つまり間接 SWI を使用する C コードを示しています。

Example 6.13. 

__svc_indirect(0x80)
    unsigned SVC_ManipulateObject(unsigned operationNumber,
                                  unsigned object,unsigned parameter);

unsigned DoSelectedManipulation(unsigned object,
                                unsigned parameter, unsigned operation)
{ return SVC_ManipulateObject(operation, object, parameter);
}

これによって以下のコードが生成されます。

DoSelectedManipulation PROC
        STMFD    sp!,{r3,lr}
        MOV      r12,r2
        SVC      0x80
        LDMFD    sp!,{r3,pc}
        ENDP

また、__svc の機構を使用して、C 言語から SVC 番号を r0 に入れて渡すことも可能です。例えば、SVC 0x0 が汎用 SVC として使用され、処理 0 が文字の読み出し、処理 1 が文字の書き込みである場合は、以下のように設定できます。

__svc (0) char __ReadCharacter (unsigned op);
__svc (0) void __WriteCharacter (unsigned op, char c);

上記は、以下のように定義しておくとわかりやすくなります。

#define ReadCharacter () __ReadCharacter (0);
#define WriteCharacter (c) __WriteCharacter (1, c);

しかし、r0 をこの方法で使用した場合、SVC にパラメータを渡すレジスタには 3 つのレジスタしか使用できません。通常、r0r3 よりも多くのパラメータをサブルーチンに渡す必要がある場合には、スタックを使用します。ただし、スタックに格納されるパラメータは、SVC ハンドラが使用するスーパーバイザスタックではなくユーザモードスタックに格納されるのが一般的であるため、SVC ハンドラから簡単にアクセスできません。

別の方法としては、レジスタの 1 つ(通常、r1)を使用して、他のパラメータが保存されているメモリブロックを参照する方法があります。

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