stm32mp157 linux 開発ボードに基づく ARM ベアメタル開発チュートリアル 6: ARM アセンブリ言語プログラミング (シリアル)

序文:

現在、ARM Cortex-A7 ベアメタル開発ドキュメントとビデオは 2 回アップグレードされ、内容がより充実し、説明がより詳細になるように継続的に更新されています。全文で使用されている開発プラットフォームは Huaqing Yuanjian FS-MP1A 開発ボードです(STM32MP157開発ボード)

FS-MP1A 開発ボードには、Cortex-A7 ベアメタル開発に加えて、Cortex-M4 開発、FreeRTOS、Linux 基本およびアプリケーション開発、Linux システム移植、Linux ドライバー開発、ハードウェア設計などの他の一連のチュートリアルも含まれています。 、人工知能マシンビジョン、Qtアプリケーションプログラミング、Qt総合プロジェクト実戦など。さらに、Linux システム移植の章と Linux ドライバー開発の章のドキュメントとビデオもアップグレードされる予定ですので、ご期待ください。

開発ボードの詳細については、Huaqing Vision Online Lab (WeChat ID: hqyjlab)を参照して「

ARM アセンブリ言語プログラミング

GNU ARM アセンブラによってサポートされる擬似演算

疑似操作の概要

ARM アセンブリ言語プログラムには、いくつかの特殊な命令ニーモニックがあります。これらのニーモニックは命令システムのニーモニックとは異なり、対応するオペコードはありません。これらの特殊な命令ニーモニックは通常、疑似演算識別子 (ディレクティブ) と呼ばれ、演算は擬似演算識別子 (ディレクティブ) と呼ばれます。実行する操作は擬似操作と呼ばれます。ソースプログラムにおける疑似オペレーションの役割は、アセンブラのさまざまな準備を完了することであり、これらの疑似オペレーションはアセンブルプロセス中にのみ機能し、アセンブルが完了すると、疑似オペレーションの使命は完了します。

ARM のアセンブラでは、疑似操作には主にシンボル定義疑似操作、データ定義疑似操作、アセンブリ制御疑似操作、およびその他の疑似操作が含まれます。

データ定義の疑似操作

データ定義擬似操作は通常、特定のデータにストレージ ユニットを割り当てると同時に、割り当てられたストレージ ユニットの初期化を完了するために使用されます。一般的なデータ定義擬似操作は、.byte、.short、.long、.quad、.float、.string、.asciz、.ascii、および .rept です。データ定義の擬似動作は以下のとおりです。

 

アセンブリ制御擬似演算は、アセンブラプログラムの実行フローを制御するために使用され、一般的に使用されるアセンブリ制御擬似演算には次のようなものがあります。

1、.if、.else、.endif

文法形式

.if、.else、および .endif 擬似操作は、条件が true かどうかに応じて、一連の命令を実行するかどうかを決定できます。.if の後ろの論理式が true の場合は .if の後の命令列を実行し、そうでない場合は .else の後の命令列を実行します。このうち、.else以降の命令列が存在しない場合もありますが、このとき、.ifの後ろの論理式が真の場合はその命令列を実行し、そうでない場合は以降の命令を実行し続けます。

ヒント:

.if、.else、.endif ディレクティブはネストできます。

構文形式は次のとおりです。

コード例 46-1 使用例

1 .if logical-expressing

2 …

3 .else

4 …

5 .endif logical-expression:

命令実行の流れを決定するために使用される論理式。

プログラム中に特定の条件が満たされたときに実行する必要がある命令がある場合にこの命令を使用します。この操作には別の形式もあります。

コード例 46-2 使用例

1 .if logical-expression

2 Instruction

3 .elseif logical-expression2

4 Instructions

5 .endif

この形式では、if-else 形式のネストが回避され、プログラム構造がより明確で読みやすくなります。

2、.マクロ、.endm

文法形式

.macro 擬似操作では、マクロ命令と呼ばれるコード全体を定義でき、そのコードはプログラム内のマクロ命令を通じて複数回呼び出すことができます。このうち、$ラベルを展開すると、ラベルがユーザー定義の記号に置き換えられます。

マクロ アクションは 1 つ以上の引数を取ることができ、マクロ アクションが展開されるときに対応する値に置き換えられます。

マクロ操作の使用法と機能はサブルーチンに似ており、サブルーチンはモジュール式プログラミングを提供し、記憶領域を節約し、動作速度を向上させることができます。ただし、サブルーチン構造を使用するとサイトを保護する必要があり、システムのオーバーヘッドが増加するため、コードが短く、渡すパラメータが多い場合には、サブルーチンの代わりにマクロ操作を使用することができます。

.macro と .endm の間に含まれる命令列はマクロ定義本体と呼ばれ、マクロ定義本体の 1 行目でマクロのプロトタイプ (マクロ名と必要なパラメータを含む) を宣言し、それを渡すことができます。一連の命令を呼び出すためのアセンブラ マクロ名。ソースプログラムがコンパイルされると、アセンブラはマクロ呼び出しを展開し、プログラム内のマクロ呼び出しをマクロ定義内の命令シーケンスに置き換え、実パラメータの値をマクロ定義内の仮パラメータに渡します。

ヒント:

.macro および .endm 擬似操作はネストできます。

構文形式は次のとおりです。

コード例 46-3 使用例

1 .macro

2 {$label} macroname {$parameter{,$parameter} … }

3 ;code

4 .endm

パラメータの説明

{$label}: $ ラベルを展開すると、ラベルはユーザー定義のシンボルに置き換えられます。通常、シンボルを使用する前に、

「$」は、シンボルがアセンブラによってコンパイルされるときに、対応する値を使用してシンボルを置き換えることを示します。

{マクロ名}: 定義されたマクロの名前。

{パラメータ}: マクロコマンドのパラメータ。マクロ命令が展開されると、関数のパラメータと同様に、対応する値に置き換えられます。

例は次のとおりです。

コード例46-4 使用例

1 .macro SHIFTLEFT a, b

2 .if \b < 0

3 MOV \a, \a, ASR #-\b

4 .exitm

5 .endif

6 MOV \a, \a, LSL #\b

7 .endm

.exitm はマクロ定義から飛び出すために使用されます。この命令をマクロ定義のコードに挿入するだけで済みます。

サブルーチンコードが比較的短く、渡すべきパラメータが多い場合には、マクロアセンブリ技術を使用できます。

まず、マクロ定義の本体コードを含め、.macro および .endm 擬似操作を通じてマクロを定義します。.macro 疑似操作の後の最初の行は、マクロ定義の名前と必要なパラメーターを含むマクロのプロトタイプを宣言します。アセンブリ内のマクロ定義の名前で呼び出すことができます。ソース プログラムがコンパイルされると、アセンブラは各マクロ呼び出しを展開し、ソース プログラム内のマクロ定義の名前をマクロ定義の本体に置き換え、マクロ定義の仮パラメータを実際のパラメータ値に置き換えます。

3. その他の疑似操作

ARM アセンブリには他にもいくつかの疑似操作があり、アセンブラ プログラムでよく使用されます。これには次のようなものがあります。

 

ARM アセンブラがサポートするディレクティブ

ARM アセンブラは、アセンブリ段階で ARM または Thumb (または Thumb-2) 命令 (または一連の命令) に変換される ARM 擬似命令をサポートします。ARM ディレクティブには、ADR、ADRL、LDR などが含まれます。

ADR 指令

文法形式

ADR ディレクティブは、狭い範囲のアドレス読み取りディレクティブです。ADR 擬似命令は、PC の相対オフセットに基づいて、またはレジスタの相対オフセットに基づいてアドレス値をレジスタに読み取ります。アドレス値がバイトアラインされている場合、値の範囲は -255 ~ 255 です。アドレス値はワードアラインされ、値の範囲は -1020 ~ 1020 です。アドレス値が 16 バイトでアライメントされている場合、その値の範囲はより大きくなります。

構文形式は次のとおりです。

ADR{c}{.W} レジスタ、ラベル

{c}: オプションの命令実行条件。

{.W}: オプション。命令幅を指定します (Thumb-2 命令セットでサポートされます)。

{register}: ターゲットレジスタ。

{label}: PC ベースまたはレジスタを使用した式。

使用説明書

ADR ディレクティブは、アセンブラによって単一の命令にコンパイルされます。アセンブラは通常、疑似演算のアドレスロード機能を実現するためにADD命令やSUB命令を使用します。ADR 疑似命令の機能を命令で実装できない場合、アセンブラはエラーを報告します。

サンプルコード46-5 使用例

1 adr r1, init_stack

2 ;相当于下面的arm指令:

3 sub/add r1, pc, offset_to_init_stack

4 ...

5 init_stack: ...

LDR ディレクティブ

文法形式

LDR ディレクティブは、32 ビット定数とアドレスをレジスタにロードします。

構文形式は次のとおりです。

LDR{cond}{.W} レジスタ,=[expr|label-expr]

{c}: オプションの命令実行条件。

{.W} は命令幅を指定します (Thumb-2 命令セットでサポートされます)。

{register}: ターゲットレジスタ。

{expr}: 32 ビットの定数式。アセンブラは、expr の値に応じて、LDR 擬似命令を次のように処理します。

① expr で表されるアドレス値が MOV 命令または MVN 命令のアドレス値の範囲を超えない場合、アセンブラは LDR 命令を MOV 命令と MVN 命令のペアに置き換えます。

② expr で表される命令のアドレス値が MOV 命令または MVN 命令のアドレス範囲を超える場合、アセンブラは定数をデータ バッファ プールに入れ、PC ベースの LDR 命令を使用して定数を読み出します。

{ラベル式}

プログラム関連または宣言された extern 式。アセンブラは、label-expr 式の値をデータ バッファ プールに置き、プログラム依存の LDR 命令を使用して値をレジスタにフェッチします。

label-expr が extern 式として宣言されている場合、アセンブラはリンク再配置擬似操作をオブジェクト ファイルに挿入し、アドレスはリンク時にリンカによって生成されます。

使用説明書

ロードする定数がMOV命令、MVN命令の範囲を超える場合にはLDR命令を使用してください。

LDR 命令によってロードされるアドレスは絶対アドレス、つまり PC 相対アドレスです。

ロードするデータが MOV 命令や MVN 命令で直接ロードできない場合、その値をデータ キャッシュ プールに格納する必要がありますが、このとき、LDR 疑似命令での PC 値との間には一定のオフセットが発生します。データ キャッシュ プール内のターゲット データのアドレス。ARM または 32 ビット Thumb-2 命令の場合、範囲は -4 ~ 4KB で、Thumb または 16 ビット Thumb-2 命令の場合、範囲は 0 ~ 1KB です。定数0xff0をr1に読み込みます。

コード例46-6 使用例

1 ldr r3,=0xff0 ;将常数 0xff0 读到 r1 中

2 ;相当于下面的 ARM 指令:

3 mov r3,#0xff0

定数 0xfff を R1 に読み取ります。

コード例 46-7 使用例

1 ldr r1,=0xfff ;将常数0xfff读到r1中

2 ; 相当于下面的arm指令:

3 ldr r1,[pc,offset_to_litpool]

4 …

5 litpool .word 0xfff

 場所ラベルのアドレスを R1 に読み込みます

コード例 46-8 使用例

1 ldr r2,=place

2 ;相当于下面的arm指令:

3 ldr r2,[pc,offset_to_litpool]

4 …

5 litpool .word place

ARM アセンブリ言語のプログラム構造

アセンブリ言語プログラムの形式

ARM (Thumb) アセンブリ言語プログラムでは、.section を使用してセグメントを作成できます。各セグメントはセグメント名またはファイルの終わりで終わり、これらのセグメントではデフォルトのフラグが使用されます (a は許可されたセグメント、w は許可されたセグメント)。書き込み可能なセグメント、x は実行セグメントです。

セクションでは、.text、.data、.bss のサブセクションを定義できます。このことから、セグメントはコード セグメント、データ セグメント、その他のストレージ セグメントに分割できることがわかります。.text (テキスト セグメント) にはプログラム命令コードが含まれます。.data (データ セグメント) には定数、文字列などの固定データが含まれます。 ;.bss (初期化されていないデータ セグメント) には、初期化されていない変数、配列などが含まれます。プログラムが長い場合、複数のコード セグメントとデータ セグメントに分割することができ、プログラムがコンパイルされてドキュメントにリンクされると、最終的に複数のセグメントが実行可能イメージを形成します。

サンプルコード 46-9 使用例

1 .section .data

2 <initialized data here>

3

4 .section .bss

5 <uninitialized data here>

6

7 .section .text

8 .globl _start

9 _start:

10 <instruction code goes here>

プロシージャコール標準 AAPCS

異なるコンパイラでコンパイルされたプログラムが相互に呼び出せるようにするには、サブルーチン間の呼び出しに特定のルールを指定する必要があります。AAPCS はそのような標準の 1 つです。いわゆるAAPCS、英語の正式名称はProcedure Call Standard for the ARM Architecture (AAPCS)、つまりARMアーキテクチャのプロシージャコール標準です。これは、ABI (ARM アーキテクチャ用アプリケーション バイナリ インターフェイス (ABI) (基本標準) [BSABI]) 標準の一部です。

「--apcs」オプションを使用すると、ソース コードをシンボリック AAPCS 呼び出し標準オブジェクト コードにコンパイルするようにコンパイラーに指示できます。

知らせ:

「--apcs」オプションの使用はコード生成には影響しません。コンパイラは、ユーザーが選択した AAPCS を識別するために各セクションに対応する属性を配置するだけです。

属性。

1. AAPCS 関連のコンパイル/アセンブリ オプション

none: 入力ファイルが AAPCS ルールを使用しないことを指定します。

/interwork: 入力ファイルが ARM/Thumb インターワーク標準に​​準拠することを指定します。

/nointerwork: 入力ファイルが ARM/Thumb インターワークを使用できないことを指定します。これはコンパイラのデフォルトのオプションです。

/ropi: 入力ファイルが位置に依存しない読み取り専用であることを指定します。

/noropi: 入力ファイルが位置に依存しない読み取り専用ファイルであることを指定します。これはコンパイラのデフォルトのオプションです。

/pic: /ropi と同じ。

/nopic: /noropi と同じ。

/rwpi: 入力ファイルが位置に依存しない読み取りおよび書き込み可能なファイルであることを指定します。

/norwpi: 入力ファイルが位置に依存しない読み取りおよび書き込み可能なファイルであることを指定します。

/pid:同/rwpi。

/nopid: /norwpi と同じ。

/fpic: 入力ファイルが位置に依存しない読み取り専用コードにコンパイルされることを指定します。コード内のアドレスは FPIC アドレスです。

/swstackcheck: コンパイル中に入力ファイルのスタック チェックを使用します。

/noswstackcheck: コンパイル中に入力ファイルのスタック チェックを使用しません。これはコンパイラのデフォルトのオプションです。

/swstna: アセンブラがデータ スタック チェックを考慮せず、アセンブラにリンクされている他のプログラムがオプション /swst またはオプション /noswst を指定している場合、アセンブラはオプション /swstna を使用します。

2. ARMレジスタの使用規則

AAPCS は、ARM レジスタの使用規則を次のように定義します。

パラメータは、レジスタ R0、R1、R2、および R3 を介してサブルーチン間で受け渡されます。パラメータが 4 つを超える場合、余分な部分はスタックに渡されます。呼び出されたサブルーチンは、戻る前にレジスタ R0 ~ R3 の内容を復元する必要はありません。

サブルーチンでは、レジスタ R4 ~ R11 はローカル変数を格納するために使用されます。レジスタ R4 ~ R11 の一部のレジスタがサブルーチンで使用されている場合、これらのレジスタの値はサブルーチンの開始時に保存され、サブルーチンで使用されないレジスタについては戻る前にこれらのレジスタの値が復元される必要があります。サブルーチンでは、これらの操作は必要ありません。Thumb プログラムでは、通常、ローカル変数の格納に使用できるのはレジスタ R4 ~ R7 のみです。

レジスタ R12 は、サブルーチン間のスクラッチ レジスタとして使用されます (SP を保存するために使用され、関数が戻ったときにこのレジスタを使用してスタックをポップします)。ip として示されます。この使用規則は、サブルーチン間のコード セグメントをリンクする際によく使用されます。

レジスタ R13 は、sp で示されるデータ スタック ポインタとして使用されます。レジスタ R13 はサブルーチン内の他の目的に使用できません。サブルーチンに入るときのレジスタ sp の値は、サブルーチンを出るときの値と等しくなければなりません。

レジスタ R14 は接続レジスタと呼ばれ、lr と表されます。サブルーチンの戻りアドレスを保持するために使用されます。戻りアドレスをサブルーチンに保存すると、レジスタ R14 を他の目的に使用できます。

レジスタ R15 はプログラム カウンタであり、pc と表示されます。他の目的には使用できません。

関数呼び出し時の ARM レジスタの保護規則を図に示します。

 

3. AAPCSの使用例

簡単な C プログラムを作成し、逆アセンブリ + シングルステップ デバッグを通じて、AAPCS ルールを検証して学習します。プロジェクト c_AAPCS プロジェクトを開くことができます。main.c の内容は次のとおりです。

コード例 46-10 AAPCS の使用例

1 int add(int a, int b, int c, int d)

2 {

3 int e;

4 e = a+b+c+d;

5 return e;

6 }

7

8 int main()

9 {

10 int a,b,c,d,e;

11 a = 1;

12 b = 2;

13 c = 3;

14 d = 4;

15 e = add(a,b,c,d);

16

17 return 0;

18 }

アセンブリ ウィンドウに戻ると、add サブ関数を呼び出す前にメイン関数で R0=1、R1=2、R2=3、R3=4 を設定し、R0、R1、R2、R3 をパラメータとして渡していることがわかります。サブ関数に add を与えます。

 

AAPCS ルールによれば、add サブ関数は R0、R1、R2、および R3 を直接使用するため、これらのレジスタを保護する必要はありません。ただし、R11 を使用するため、ルールに従って保護が必要となるため、R11 は保護のためにスタックにプッシュされます。最終的な戻り値は R0 経由で送信されます。

 

思考: プログラミング テストが 5 つのパラメータの場合。

ARM 擬似命令実験

目的

ARM アセンブリ言語の基本的な使用法といくつかの疑似命令の使用法をマスターします。

アセンブリプロジェクトとシミュレーションを構築するための Eclipse 開発ツールに精通している。

 実験原理

上で説明した RAM アセンブリ言語の使用構文と機能に従って、2 つのメモリに格納されているデータを加算する演算を実現するアセンブリ プログラムを作成します。

実験内容

アセンブラ プログラムは次のように設計されています。

サンプルコード 46-11 擬似命令の場合

1 .text

2 .global _start

3 _start:

4

5 .code 32

6

7

8 mov r1, #0

9

10 ldr r2,=myarray

11 loop:

12

13 ldr r3,[r2],#4

14 add r1,r1,r3

15 cmp r3,#0

16 bne loop

17

18 stop:

19 b stop

20

21 myarray:

22 .word 6

23 .word 24

24 .word 12

25 .word 0

26

27 .end

実験手順

プロジェクトのソースコードをインポートする

「既存プロジェクトのインポート」の章の「既存プロジェクトのインポート」を参照してください。

CD-ROM 実験ソース コード パス: [データ CD\Huaqing Vision-FS-MP1A Development Data-2020-11-06\02-Program Source Code\03-ARM Architecture and Interface Technology\Cortex-A7\h_test]

実験現象

「下のアイコン」を段階的にクリックして、Rn レジスタの変更を表示します。

 

 

3 つのデータの合計が R1 に格納され、R1 の最終値は 42 になります。

ARM インラインアセンブリ実験

目的

ARM アセンブリ言語の基本的な使用法とインライン アセンブリの使用法をマスターします。

アセンブリプロジェクトとシミュレーションを構築するための Eclipse 開発ツールに精通している。

実験原理

GCC インライン アセンブリの一般的な形式は次のとおりです。

asm(

コードリスト

: 出力演算子のリスト

: 入力演算子リスト

: 変更されたリソースのリスト

);

コードリストでは、各アセンブリステートメントを「 」で囲む必要があります。

コード例 46-12 インラインアセンブリの例

1 asm(

2 "add %0,%1,%2\n\t"

3 "mov r1,%1\n\t"

4 :"+r"(sum)

5 :"r"(a),"r"(b)

6 :"r0"

7 );

 詳細な事例

C コードにアセンブリを埋め込むには、asm キーワード、asm(); の使用が必要です。

 「」で囲まれた部分が命令部分です

 :パラメータ出力部、関数の戻り値

 :パラメータ入力部、関数の仮パラメータ

 : 装飾のリスト、インラインアセンブリの宣言部分、変更するリソース

 「r」はレジスタを使用してパラメータを保存します

 「i」は即値です

 「m」は有効なメモリアドレス

 「×」のみ入力可能

 + パラメータが読み取りおよび書き込み可能であることを示します

 書き込まないということは、パラメータが読み取り専用であることを意味します

 = 書き込みのみを意味します

 & 出力のみが可能

 %0 出力リストと入力リストの最初のメンバー

 %1 は出力リストと入力リストの 2 番目のメンバーです。

 %2 出力リストと入力リストの 3 番目のメンバー

上記の ARM アセンブリ言語の構文と関数に従って、2 つのパラメーターの最小公倍数を実現するインライン アセンブリ コードを C 言語で記述します。

実験内容

実験プログラムの設計は次のとおりです。

コード例 46-13 バイトスワップ

1 unsigned long ByteSwap(unsigned long val)

2 {

3 int ch;

4

5 asm volatile (

6 "eor r3, %1, %1, ror #16\n\t"

7 "bic r3, r3, #0x00ff0000\n\t"

8 "mov %0, %1, ror #8\n\t"

9 "eor %0, %0, r3, lsr #8"

10 : "=r" (ch)

11 : "0"(val)

12 : "r3"

13 );

14 }

15

16 int main(void)

17 {

18 unsigned long test_a = 0x1234,result;

19

20 result = ByteSwap(test_a);

21

22 printf("Result:%d\r\n", result);

23

24 return 0;

25 }

実験手順

プロジェクトのソースコードをインポートする

関連コンテンツについては、「既存プロジェクトのインポート」の章「既存プロジェクトのインポート」を参照してください。

CD-ROM 実験ソース コード パス: [データ CD\Huaqing Vision-FS-MP1A Development Data-2020-11-06\02-Program Source Code\03-ARM Architecture and Interface Technology\Cortex-A7\h_inline]

実験結果

「下のアイコン」をクリックすると、1 ステップで結果が表示されます。

 

 

おすすめ

転載: blog.csdn.net/u014170843/article/details/130687743