コンパイラを使用すると、名前付きレジスタ変数を使用して、ARM アーキテクチャベースのプロセッサのレジスタにアクセスできます。
名前付きレジスタ変数は register キーワードと __asm
キーワードを組み合わせて宣言されます。__asm
キーワードは、レジスタの名前を示す文字列からなるパラメータを 1
つ使用します。例えば、以下の例では、R0
をレジスタ r0
の名前付きレジスタ変数として宣言しています。
register int R0 __asm("r0");
名前付きレジスタ変数はグローバル変数として宣言できます。一部の名前付きレジスタ変数だけをローカル変数として宣言することができます。通常は、ベクタ浮動小数点(VFP)レジスタおよび主要なレジスタをローカル変数として宣言しません。R0 などの caller-save レジスタはローカル変数として宣言しないで下さい。caller-save レジスタは、サブルーチンが完了した後にレジスタの値を必要とする場合に呼び出し元がその値を保存する必要があるレジスタです。これらのレジスタをローカルに宣言したプログラムがコンパイルされた場合でも、実行時に予期しない動作が発生する可能性があります。
名前付きレジスタ変数の代表的な用途は、アプリケーションプログラムステータスレジスタ(APSR)内のビットにアクセスすることです。例
3-3 は、名前付きレジスタ変数を使用して、APSR にサチュレーションフラグ Q
を設定する方法を示しています。
Example 23. 名前付きレジスタ変数を使用して APSR 内にビットを設定する
#ifndef __BIG_ENDIAN // APSR のビットフィールドのレイアウトはエンディアン方式の影響を受ける 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 */ /* PSR を "apsr" レジスタのレジスタ変数として宣言する */ register PSR apsr __asm("apsr"); void set_Q(void) { apsr.b.Q = 1; }
Example 24 も参照して下さい。
Example 24. 名前付きレジスタ変数を使用して APSR 内の Q フラグをクリアする
register unsigned int _apsr __asm("apsr"); __forceinline void ClearQFlag(void) { _apsr = _apsr & ~0x08000000; // Q フラグをクリアする }
逆アセンブリ:
ClearQFlag MRS r0,APSR ; 以前は CPSR BIC r0,r0,#0x80000000 MSR APSR_nzcvq,r0; 以前は CPSR_f BX lr
Example 25も参照して下さい。
Example 25. 名前付きレジスタ変数を使用してスタックポインタを設定する
register unsigned int _control __asm("control"); register unsigned int _msp __asm("msp"); register unsigned int _psp __asm("psp"); void init(void) { _msp = 0x30000000; // メインスタックポインタを設定 _control = _control | 3; // プロセススタックでユーザモードに切り替え _psp = 0x40000000; // プロセススタックポインタを設定 }
逆アセンブリ(--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
命令で使用する変数を宣言するには、この命令の命令構文を参照し、変数を宣言するときにこの構文を使用します。詳細については、Example 26を参照して下さい。
Example 26. 名前付きレジスタ変数を使用してコプロセッサレジスタ内にビットを設定する
register unsigned int PMCR __asm(”cp15:0:c9:c12:0”); __inline void __reset_cycle_counter(void) { PMCR = 4; } 逆アセンブリ: __reset_cycle_counter PROC MOV r0,#4 MCR p15,#0x0,r0,c9,c12,#0 ; r0 から c9 に移動する BX lr ENDP
Example 26 では、cp15
コプロセッサに関連付けられた unsigned int 型のレジスタ変数として PMCR
が宣言されています。MCR
または MRC
命令には、CRn
= c9
、CRm = c12
、opcode1 = 0
、および opcode2
= 0
が使用されます。逆アセンブリの MCR
エンコーディングは、レジスタ変数宣言に対応します。
物理コプロセッサレジスタは、2 つのレジスタ番号(CRn
、CRm
)および
2 つのオペコード番号の組み合わせを使用して指定されています。これは、単一の物理レジスタにマップされます。
レジスタ内の個々のビットを操作する場合は同じ原則が適用されますが、C で通常の変数算術を記述します。すると、コンパイラによって、コプロセッサレジスタの読み出し-変更-書き込みが行われます。詳細については、Example 27を参照して下さい。
Example 27. 名前付きレジスタ変数を使用してコプロセッサレジスタ内でビットを操作する
register unsigned int SCTLR __asm(”cp15:0:c1:c0:0”); /* システムコントロールレジスタのビット 11 を設定する */ void enable_branch_prediction(void) { SCTLR |= (1 << 11); } 逆アセンブリ: __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
『アセンブラリファレンス』:
『コンパイラリファレンス』: