目次
命令セットの概要
なぜ命令セットの理解が必要か?
今回は、初期化や割り込みハンドラを実装する場合に必要な命令セットの基本知識を学びます。デバッグを行う時やチューニングを行う場合に、命令セットの知識があることで、開発を円滑に進めることができます。命令の詳細については、「ArmおよびThumb-2 命令セットクイックリファレンスカード」を参照ください。
Cortex-Aがサポートする命令セット
Armプロセッサは、Arm命令(32ビット)セットとThumb命令(16ビット)セットの2つの命令セットを採用していました。Cortex-Aシリーズは、Arm命令セット(A32(Arm))とThumb命令セットの新版であるThumb-2命令セットを採用しています。
Arm命令セット
Arm命令セットは、32ビット固定長命令セットで、Armv4T、Armv5TEJおよびArmv6アーキテクチャで使用されています。性能が重視される場合や互換性のために、Cortex-AおよびCortex-Rプロファイルでもサポートされています。
Thumb-2命令セット
16ビット固定長命令セットとして導入され、Thumb-2技術の導入で、16ビットおよび32ビット命令長の混在命令セットです。Arm命令セットのパフォーマンスと、Thumb命令セットのコード密度の特徴をあわせ持つ、Cortexシリーズの主力命令セットです。
命令セットのプロセス
アセンブリ命令の基本形式
アセンブラ記述は、C言語の演算と同様に、後ろから前へ演算を行います。ADD(加算命令)を例に、アセンブリ命令の基本形式を示します。
オペランド数は、命令により異なりますので、「ArmおよびThumb-2 命令セットクイックリファレンスカード」で確認してください。
データ処理命令
データ処理命令とは、レジスタ間で転送、演算を行う命令の総称で、命令セットを学ぶ上での基本となります。すべてのArmプロセッサはロード/ストア・アーキテクチャを採用しており、演算処理はレジスタで行います。
- レジスタのデータを転送します。
- ミディエイト値をレジスタに代入します。
- イミディエイト値とレジスタ間で加算します。
- イミディエイト値とレジスタ間で減算します。
- レジスタ間を加算します。
- レジスタ間を減算します。
- イミディエイト値とレジスタ間でAND論理演算します。
- イミディエイト値とレジスタ間でOR論理演算します。
- レジスタ間でAND論理演算します。
- レジスタ間でOR論理演算します。
- イミディエイト値とレジスタ間でNOT AND論理演算します。
- レジスタ間でNOT AND論理演算します。
ロードストア命令
特定のアドレス(メモリまたはI/O)にリードまたはライトを行う場合に、ロードストア命令を使用して、アクセスを行います。アクセスするアドレスをレジスタに設定し、ロードストア命令1命令でアクセスを行います。
メモリからr0レジスタに“r1レジスタの値で示されるアドレス”からワード(32ビット)データをリードします。
r0レジスタのワード(32ビット)データを“r1レジスタの値で示されるアドレス”にライトします。
ダブルワード・ハーフワードサイズ・バイトサイズのアクセス命令も用意されています。
- LDRD/STRD ダブルワードサイズ(64ビット)
- LDRH/STRH ハーフワードサイズ(16ビット)
- LDRB/STRB バイトサイズ(8ビット)
32ビットデータ定数と命令表現
MOV命令を使用して、イミディエイトオペランドを設定する場合、「8ビット定数を任意の偶数ビット回、右にローテートすることで表現できる値」です。命令形式では、Immed_8(イミディエイトデータ)とRotate_imm(ローテーション)を使用します。
例えば、r1レジスタに0xFF000000を代入する場合、0xFFを8回ローテーションすることで、表現することができます。
上記の方法で設定値を設定できない場合は、リテラルプールを使用して、定数値をレジスタに設定します。この例では、0x12345678がLDR命令でr0レジスタに設定データをリードすることで、r0レジスタに設定されます。
DCD擬似命令は、アセンブリ言語で、ワードデータを4バイト境界で設定された初期値で初期化を行います。
Thumb-2命令では、さらに効率を良くするために、16ビット定数ロード命令(MOVW命令・MOVT命令)を利用して、32ビット定数ロードを行います。
LDR擬似命令とは?
定数値のサイズを判断して、複数の命令を使用することは、プログラミングする上で問題となります。この問題を解決する為に、LDR擬似命令を使用します。
LDR擬似命令を使うことで、定数値のサイズに関係なくコーディングが可能となります。アセンブラは、イミディエイト値のサイズにより、MOV命令やリテラルプールを使用するか判断します。
関数コールとスタックアクセス
関数コール(アセンブリ言語のサブルーチンコール)を行う場合、<label>のアドレスをpc(プログラムカウンタ)に設定し、戻りアドレスをlr(リンクレジスタ)に設定し、プログラムを実行します。
しかし、関数コールがネスティングする場合、lrが破壊されるので、スタックに退避することが必要になりますので、次の手順で関数コールを行います。
- 処理で使用する、レジスタとlrをスタックに退避。
- 関数呼び出し処理を実行。
- 処理で使用したレジスタとpcをスタックから復帰。
lrの値がpcに復帰されることで、関数コールを行った次の命令から実行を再開します。
C/C++言語から、アセンブリ言語の関数を呼び出す場合、スタックに退避・復帰を行うデータサイズは8バイト単位で行います。詳しくは、「AAPCS(Armアーキテクチャのプロシージャコール標準)」を参照ください。
スタックの動作
r2=0x1234、r3=0x5678、r5=0x2000、sp=0x100Cの場合のスタックの動作を確認します。
STMFD命令とLDMFD命令
STMFDとPUSH、LDMFDとPOPは同じ動作です。UAL(統一アセンブラ形式)では、PUSH/POPを使用します。
条件フラグと分岐命令
cpsr(プログラムステータスレジスタ)の条件フラグで、条件分岐等の動作を行います。
- N=負数であることを示します。
- Z=ゼロであることを示します。
- C=符号なし演算でのオーバーフローの発生を示します。
- V=符号あり演算でのオーバーフローの発生を示します。
条件フラグを更新する命令
- CMP r1, r0 ; r1-r0の結果を条件フラグに設定します。
NZCV(条件コードフラグ)を演算結果に従って更新します。
r1とr0が同じ値の場合は、条件フラグZ=1となります。 - TST r1, r0 ; r1 AND r0の結果を条件フラグ設定します。
NZCVを演算結果に従って更新します。
r1レジスタとr0レジスタをAND論理演算結果が0の場合に、条件フラグZ=1となります。
設定することで条件フラグを更新する命令
データ処理命令や乗算命令では演算結果を条件フラグに反映するか、しないかを選択できます。
ニーモニック | 意味 | 条件フラグ |
---|---|---|
EQ | 等しい | Z=1 |
NE | 等しくない | Z=0 |
CS/HS | キャリーセット/符号なし大または同じ | C=1 |
CC/LO | キャリーリセット/符号なし小 | C=0 |
MI | 負数 | N=1 |
PL | 正の数またはゼロ | N=0 |
VS | オーバーフロー | V=1 |
VC | オーバーフローでない | V=0 |
HI | 符号なし大 | C=1 and Z=0 |
LS | 符号なし小または同じ | C=0 or Z=1 |
GE | 符号付大または同じ | N=V |
LT | 符号付小 | N!=V |
GT | 符号付大 | Z=0 and N=V |
LE | 符号付小または同じ | Z=1 or N!=V |
AL | 常に成立 | — |
条件分岐命令
ループ処理を行う場合に、条件フラグの内容でプログラムの分岐先を変更することが必要になります。条件分岐命令を実行する前に、条件フラグを更新することが必要です。
ソフトウェアタイマー処理での条件フラグ更新例
SUBS命令でループカウンタ(r0レジスタ)を減算処理し、条件フラグを更新します。SUB命令で実行した場合は、このプログラムは正しく動作しません。
カレントプログラムステータスレジスタのリードライト
cpsr(カレントプログラムステータスレジスタ)にアクセスを行う場合は、MRS命令とMSR命令を使用します。
cpsrのアクセス命令
命令 | フィールド | プログラムステータスレジスタ データ位置 |
---|---|---|
cpsr_c | 制御フィールドマスクバイト | 0ビットから7ビット |
cpsr_x | 拡張フィールドマスクバイト | 8ビットから15ビット |
cpsr_s | ステータスフィールドマスクバイト | 16ビットから23ビット |
cpsr_f | フラグフィールドマスクバイト | 24ビットから31ビット |
cpsrのフィールドアクセス命令
コプロセッサアクセス命令
Cortex-Aシリーズは、キャッシュやMMUの制御を行う場合に、コプロセッサに設定を行う必要があります。コプロセッサアクセス命令は、使用するプロセッサのマニュアル(*1)を参照して頂くことが必要です。
命令 | 動作内容 |
---|---|
MRC | コプロセッサレジスタからレジスタに移動します。 |
MCR | Armレジスタからコプロセッサレジスタに移動します。 |
名称 | 略称 | R/W | 命令 |
---|---|---|---|
システム制御レジスタ | SCTLR | R | MRC p15, 0,<Rd>, c1, c0, 0 |
W | MCR p15, 0,<Rd>, c1, c0, 0 | ||
補助制御レジスタ | ACTLR | R | MRC p15, 0,<Rd>, c1, c0, 1 |
W | MCR p15, 0,<Rd>, c1, c0, 1 | ||
コプロセッサアクセス 制御レジスタ | CPACR | R | MRC p15,0,<Rd>,c1,c0,2 |
W | MCR p15,0,<Rd>,c1,c0,2 |
以下のマニュアルを参照ください。
Armアーキテクチャリファレンスマニュアル Armv7-AおよびArmv7-Rエディション
英語版 :Cortex-A9 Technical Reference Manual Revision:r4p1
日本語版:Cortex-A9 テクニカルリファレンス マニュアルリビジョン:r2p2
こちらも是非
“もっと見る” Cortex-A編
初期化処理
リセット例外からmain()関数を呼び出すまでの初期化は、ユーザが作成する部分とArmコンパイラが実行する部分に分けることができます。コードのコピーや初期化変数/未初期化変数の初期化は、リンカのメモリ配置設定を処理系ライブラリが実行します。
PMU(パフォーマンス監視ユニット)
PMUに関連するレジスタは、ユーザモードでのアクセスは禁止されていますので、PMUSERENR(ユーザイネーブルレジスタ)を特権モードでユーザモードアクセス許可を設定します。PMUSERENRについては、 後述の該当項目を参照ください。
TrustZone(セキュリティ拡張機能)
TrustZoneはCortex-Aシリーズの拡張機能で、大規模OSやアプリケーションが動作するノーマルワールドとセキュリティ関連が動作するセキュアワールドを導入しています。TrustZoneでは、ノーマルワールドメモリ空間とセキュアワールドメモリ空間の分離が可能です。