4.14 名前付きレジスタ変数を使用したレジスタへのアクセスに対するコンパイラサポート

名前付きレジスタ変数を使用して、ARM アーキテクチャベースプロセッサのレジスタにアクセスできます。

名前付きレジスタ変数は register キーワードと __asm キーワードを組み合わせて宣言されます。__asm キーワードは、レジスタの名前を示す文字列からなるパラメータを 1 つ使用します。たとえば、以下のサンプルでは、R0 をレジスタ r0 の名前付きレジスタ変数として宣言しています。
register int R0 __asm("r0");
レジスタに名前を付ける際、同サイズであればいずれの型でも、名前付きレジスタ変数の宣言に使用できます。型は構造体でも可能ですが、ビットフィールドのレイアウトはエンディアン方式の影響を受けます。
コアレジスタは、ローカルではなく、グローバル名前付きレジスタ変数として宣言しなければなりません。それらをローカルに宣言しても、プログラムがコンパイルする可能性はありますが、そうした場合、実行時に予期しない動作が発生する可能性があります。その他のレジスタの場合、名前付きレジスタ変数の有効範囲に制約はありません。

グローバル名前付きレジスタ変数は、プログラムに対してグローバルなのではなく、宣言されたソースファイルに対してグローバルです。この変数は、マルチファイルのコンパイルを使用するか、ヘッダファイル内で宣言しない限り、他のファイルには影響しません。
名前付きレジスタ変数の代表的な用途は、アプリケーションプログラムステータスレジスタ(APSR)内のビットにアクセスすることです。以下のサンプルは、名前付きレジスタ変数を使用して、APSR にサチュレーションフラグ Q を設定する方法を示しています。
#ifndef __BIG_ENDIAN // bitfield layout of APSR is sensitive to endianness
typedef union
{
    struct
    {
        int mode:5;
        int T:1;
        int F:1;
        int I:1;
        int _dnm:19;
        int Q:1;
        int V:1;
        int C:1;
        int Z:1;
        int N:1;
    } b;
    unsigned int word;
} PSR;
#else /* __BIG_ENDIAN */
typedef union
{
    struct 
    {
        int N:1;
        int Z:1;
        int C:1;
        int V:1;
        int Q:1;
        int _dnm:19;
        int I:1;
        int F:1;
        int T:1;
        int mode:5;
    } b;
    unsigned int word;
} PSR;
#endif /* __BIG_ENDIAN */
/* Declare PSR as a register variable for the "apsr" register */
register PSR apsr __asm("apsr");
void set_Q(void)
{
    apsr.b.Q = 1;
}
次の例は、名前付きレジスタ変数を使用して APSR 内の Q フラグをクリアする方法を示しています。
register unsigned int _apsr __asm("apsr");
void ClearQFlag(void)
{
    _apsr = _apsr & ~0x08000000; // Q フラグをクリア
}
このサンプルを --cpu=7-M を使用してコンパイルすると、以下のアセンブリコードが得られます。
ClearQFlag
    MRS    r0,APSR ; formerly CPSR
    BIC    r0,r0,#0x80000000
    MSR    APSR_nzcvq,r0; formerly CPSR_f
    BX     lr
次の例は、名前付きレジスタ変数を使用してスタックポインタをセットアップする方法を示しています。
register unsigned int _control __asm("control");
register unsigned int _msp     __asm("msp");
register unsigned int _psp     __asm("psp");
void init(void)
{
    _msp = 0x30000000;        // set up Main Stack Pointer
    _control = _control | 3;  // switch to User Mode with Process Stack
    _psp = 0x40000000;        // set up Process Stack Pointer
}
このサンプルを --cpu=7-M を使用してコンパイルすると、以下のアセンブリコードが得られます。
init
    MOV    r0,0x30000000
    MSR    MSP,r0
    MRS    r0,CONTROL
    ORR    r0,r0,#3
    MSR    CONTROL,r0
    MOV    r0,#0x40000000
    MSR    PSP,r0
    BX     lr
また、名前付きレジスタ変数を使用して、コプロセッサ内のレジスタにアクセスすることもできます。宣言内の文字列構文は、変数の使用方法に対応します。たとえば、MCR 命令で使用する変数を宣言するには、この命令の命令構文を参照し、変数を宣言するときにこの構文を使用します。次のサンプルは、名前付きレジスタ変数を使用してコプロセッサレジスタ内にビットを設定する方法を示しています。
register unsigned int PMCR __asm("cp15:0:c9:c12:0");
void __reset_cycle_counter(void)
{
    PMCR = 4;
}
このサンプルを --cpu=7-M を使用してコンパイルすると、以下のアセンブリコードが得られます。
__reset_cycle_counter PROC
    MOV    r0,#4
    MCR    p15,#0x0,r0,c9,c12,#0      ; move from r0 to c9
    BX     lr
    ENDP
前述のサンプルでは、cp15 コプロセッサに関連付けられた 符号なし整数 型のレジスタ変数として PMCR が宣言されています。CRn = c9CRm = c12opcode1 = 0、および opcode2 = 0 は、MCR または MRC 命令で使用されます。逆アセンブリの MCR エンコーディングは、レジスタ変数宣言に対応します。
物理コプロセッサレジスタは、2 つのレジスタ番号(CRnCRm)および 2 つのオペコード番号の組み合わせを使用して指定されています。これは、単一の物理レジスタにマップされます。
レジスタ内の個々のビットを操作する場合は同じ原則が適用されますが、C で通常の変数算術を記述します。すると、コンパイラによって、コプロセッサレジスタの読み出し-変更-書き込みが行われます。次のサンプルは、名前付きレジスタ変数を使用してコプロセッサレジスタ内でビットを操作する方法を示しています。
register unsigned int SCTLR __asm("cp15:0:c1:c0:0");
/* Set bit 11 of the system control register */
void enable_branch_prediction(void)
{
    SCTLR |= (1 << 11);
}
このサンプルを --cpu=7-M を使用してコンパイルすると、以下のアセンブリコードが得られます。
__enable_branch_prediction PROC
    MRC    p15,#0x0,r0,c1,c0,#0
    ORR    r0,r0,#0x800
    MCR    p15,#0x0,r0,c1,c0,#0
    BX     lr
    ENDP
関連する参考文書
10.5 __asm
10.159 名前付きレジスタ変数
関連情報
アプリケーションプログラムステータスレジスタ
MRC および MRC2
非機密扱いPDF file icon PDF 版ARM DUI0472LJ
Copyright © 2010-2015 ARM.All rights reserved.