最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

ARM裸機(jī)開發(fā)篇3:ARM匯編語言程序設(shè)計(jì)

2021-11-01 14:19 作者:華清遠(yuǎn)見研發(fā)中心  | 我要投稿

寫在前面:

本文章為《ARM Cortex-A7裸機(jī)開發(fā)篇》系列中的一篇,全系列總計(jì)11篇。筆者使用的開發(fā)平臺為華清遠(yuǎn)見FS-MP1A開發(fā)板(STM32MP157開發(fā)板)。

針對FS-MP1A開發(fā)板,除了Cortex-A7裸機(jī)開發(fā)篇外,還包括其他多系列教程,包括Cortex-M4開發(fā)篇、FreeRTOS篇、Linux基礎(chǔ)及應(yīng)用開發(fā)篇、Linux系統(tǒng)移植篇、Linux驅(qū)動開發(fā)篇、硬件設(shè)計(jì)篇、人工智能機(jī)器視覺篇、Qt應(yīng)用編程篇、Qt綜合項(xiàng)目實(shí)戰(zhàn)篇等。歡迎關(guān)注,更多stm32mp157開發(fā)教程及視頻,可加技術(shù)交流Q群483143191,感謝關(guān)注。

FS-MP1A開發(fā)板詳情介紹:item.taobao.com/item.ht

1.1?GNU ARM匯編器支持的偽操作

1.1.1?偽操作概述

在ARM匯編語言程序中,有一些特殊指令助記符,這些助記符與指令系統(tǒng)的助記符不同,沒有相對應(yīng)的操作碼,通常稱這些特殊指令助記符為偽操作標(biāo)識符(directive),它們所完成的操作稱為偽操作。偽操作在源程序中的作用是為了完成匯編程序做各種準(zhǔn)備工作的,這些偽操作僅在匯編過程中起作用,一旦匯編結(jié)束,偽操作的使命就完成。

在ARM的匯編程序中,偽操作主要有符號定義偽操作、數(shù)據(jù)定義偽操作、匯編控制偽操作及其雜項(xiàng)偽操作等。

1.1.2?數(shù)據(jù)定義(Data Definition)偽操作

數(shù)據(jù)定義偽操作一般用于為特定的數(shù)據(jù)分配存儲單元,同時可完成已分配存儲單元的初始化。常見的數(shù)據(jù)定義偽操作有.byte、.short、.long、.quad、.float、.string、.asciz、.ascii和.rept。數(shù)據(jù)定義偽操作如下。

(1) 偽指令名:

.byte

用途:單字節(jié)定義。

用法:

.byte 1,2,0b01,0x34,072,'s' ;

(2) 偽指令名:

.short

用途:定義雙字節(jié)數(shù)據(jù),

用法:

.short 0x1234,60000 ;

(3) 偽指令名:

.long

用途: 定義4字節(jié)數(shù)據(jù)。

用法:

.long 0x12345678,23876565

(4) 偽指令名:

.quad:

用途:定義8字節(jié)。

用法:

.quad 0x1234567890abcd

(5) 偽指令名:

.float

用途:定義浮點(diǎn)數(shù)。

用法:

.float 0f311971.693993751E-40

(6) 偽指令名:

.string/.asciz/.ascii:

用途:定義多個字符串。

用法:

.string "abcd", "efgh", "hello!"

.asciz "qwer", "sun", "world!"

.ascii "welcome\0"(需要注意的是:.ascii偽操作定義的字符串需要在每行末尾添加結(jié)尾字符'\0')

(7) 偽指令名:

.rept/.endr

用途:重復(fù)定義偽操作。

用法:

.rept 3

.byte 0x23

.endr

(8) 偽指令名:

.equ/.set

用途:賦值語句,.equ(.set) 變量名,表達(dá)式。

用法:

.equ abc 3 @ abc=3

1.1.3?匯編控制偽操作

匯編控制偽操作用于控制匯編程序的執(zhí)行流程,常用的匯編控制偽操作包括以下幾條。

1、 .if、.else、.endif

a) 語法格式

.if、.else、.endif偽操作能根據(jù)條件的成立與否決定是否執(zhí)行某個指令序列。當(dāng).if后面的邏輯表達(dá)式為真,則執(zhí)行.if后的指令序列,否則執(zhí)行.else后的指令序列。其中,.else及其后指令序列可以沒有,此時,當(dāng).if后面的邏輯表達(dá)式為真,則執(zhí)行指令序列,否則繼續(xù)執(zhí)行后面的指令。

提示:

.if、.else、.endif偽指令可以嵌套使用。

語法格式如下:

.if logical-expressing

{.else

…}

.endif logical-expression:

用于決定指令執(zhí)行流程的邏輯表達(dá)式。

b) 使用說明

當(dāng)程序中有一段指令需要在滿足一定條件時執(zhí)行,使用該指令。該操作還有另一種形式。

.if logical-expression

Instruction

.elseif logical-expression2

Instructions

.endif

該形式避免了if-else形式的嵌套,使程序結(jié)構(gòu)更加清晰、易讀。

2、 .macro、.endm

a) 語法格式

.macro偽操作可以將一段代碼定義為一個整體,稱為宏指令,然后就可以在程序中通過宏指令多次調(diào)用該段代碼。其中,$標(biāo)號在宏指令被展開時,標(biāo)號會被替換為用戶定義的符號。

宏操作可以使用一個或多個參數(shù),當(dāng)宏操作被展開時,這些參數(shù)被相應(yīng)的值替換。

宏操作的使用方式和功能與子程序有些相似,子程序可以提供模塊化的程序設(shè)計(jì)、節(jié)省存儲空間并提高運(yùn)行速度。但在使用子程序結(jié)構(gòu)時需要保護(hù)現(xiàn)場,從而增加了系統(tǒng)的開銷,因此,在代碼較短且需要傳遞的參數(shù)較多時,可以使用宏操作代替子程序。

包含在.macro和.endm之間的指令序列稱為宏定義體,在宏定義體的第一行應(yīng)聲明宏的原型(包含宏名、所需的參數(shù)),然后就可以在匯編程序中通過宏名來調(diào)用該指令序列。在源程序被編譯時,匯編器將宏調(diào)用展開,用宏定義中的指令序列代替程序中的宏調(diào)用,并將實(shí)際參數(shù)的值傳遞給宏定義中的形式參數(shù)。

提示:

.macro、.endm偽操作可以嵌套使用。

語法格式如下:

.macro

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

;code

.endm

(1) {$label}。

(9) $標(biāo)號在宏指令被展開時,標(biāo)號會被替換為用戶定義的符號。通常,在一個符號前使用“$”表示該符號被匯編器編譯時,使用相應(yīng)的值代替該符號。

(10) Macroname:所定義的宏的名稱。

(11) Parameter:宏指令的參數(shù)。當(dāng)宏指令被展開時將被替換成相應(yīng)的值,類似于函數(shù)中的參數(shù)。

b) 使用說明

在子程序代碼比較短,而需要傳遞的參數(shù)比較多的情況下可以使用宏匯編技術(shù)。

首先通過.macro和.endm偽操作定義宏,包括宏定義體代碼。在.macro偽操作之后的第一行聲明宏的原型,其中包含該宏定義的名稱及需要的參數(shù)。在匯編中可以通過該宏定義的名稱來調(diào)用它。當(dāng)源程序被編譯時,匯編器將展開每個宏調(diào)用,用宏定義體代替源程序中宏定義的名稱,并用實(shí)際參數(shù)值代替宏定義時的形式參數(shù)。

c) 示例

示例如下:

.macro SHIFTLEFT a, b

.if \b < 0

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

.exitm

.endif

MOV \a, \a, LSL #\b

.endm

3、 .mexit

a) 語法格式

.mexit用于從宏定義中跳轉(zhuǎn)出去。

b) 用法

只需要在宏定義的代碼中插入該指令即可。

.macro SHIFTLEFT a, b

.if \b < 0

mov \a, \a, ASR #-\b

.exitm

.endif

mov \a, \a, LSL #\b

.endm

1.1.4?雜項(xiàng)偽操作

ARM匯編中還有一些其他的偽操作,在匯編程序中經(jīng)常會被使用,包括以下幾條。

.arm .arm @ 定義以下代碼使用ARM指令集編譯

.code 32 .code 32 @作用同.arm

.code 16 .code 16 @作用同.thumb

.thumb .thumb @定義以下代碼使用Thumb指令集編譯

.section .section expr @定義域中包含的段。expr可以使.text,.data.,.bss

.text .text {subsection} @將定義符開始的代碼編譯到代碼段或代碼子段(subsection)

.data .data {subsection} @將定義符開始的代碼編譯到數(shù)據(jù)段或數(shù)據(jù)子段(subsection)

.bss .bss {subsection} @將變量存放到.bss段或.bss的子段(subsection)

.align .align{alignment}{,fill}{,max} @通過用零或指定的數(shù)據(jù)進(jìn)行填充來使當(dāng)前位置與指定邊界對齊

.org .org offset{,expr} @指定從當(dāng)前地址加上offset開始存放代碼,并且從當(dāng)前地址到當(dāng)前地址加上offset之間的內(nèi)存單元,用零或指定的數(shù)據(jù)進(jìn)行填充

1.2?ARM匯編器支持的偽指令

ARM匯編器支持ARM偽指令,這些偽指令在匯編階段被翻譯成ARM或者Thumb(或Thumb-2)指令(或指令序列)。ARM偽指令包含ADR、ADRL、ldr等。

1.2.1?ADR偽指令

1、 語法格式

ADR偽指令為小范圍地址讀取偽指令。ADR偽指令將基于PC相對偏移地址或基于寄存器相對偏移地址值讀取到寄存器中,當(dāng)?shù)刂分凳亲止?jié)對齊時,取值范圍為?255~255,當(dāng)?shù)刂分凳亲謱R時,取值范圍為?1020~1020。當(dāng)?shù)刂分凳?6字節(jié)對齊時其取值范圍更大。

語法格式如下:

ADR{cond}{.W} register,label

a) cond

可選的指令執(zhí)行條件。

b) .W

可選項(xiàng)。指定指令寬度(Thumb-2指令集支持)。

c) register

目標(biāo)寄存器。

d) label

基于PC或具有寄存器的表達(dá)式。

2、 使用說明

ADR偽指令被匯編器編譯成一條指令。匯編器通常使用ADD指令或SUB指令來實(shí)現(xiàn)偽操作的地址裝載功能。如果不能用一條指令來實(shí)現(xiàn)ADR偽指令的功能,匯編器將報告錯誤。

3、 示例

示例如下。

ldr R4,=data+4*n ;n是匯編時產(chǎn)生的變量

; code

MOV pc,lr

data .word value0

; n-1條 DCD 偽操作

.word valuen ;所要裝載入R4的值

;更多 DCD 偽操作

1.2.2?ADRL偽指令

1、 語法格式

ADRL偽指令為中等范圍地址讀取偽指令。ADRL偽指令將基于PC相對偏移的地址或基于寄存器相對偏移的地址值讀取到寄存器中,當(dāng)?shù)刂分凳亲止?jié)對齊時,取值范圍為?64~64KB;當(dāng)?shù)刂分凳亲謱R時,取值范圍為?256~256KB。當(dāng)?shù)刂分凳?6字節(jié)對齊時,其取值范圍更大。在32位的Thumb-2指令中,地址取值范圍到達(dá)?1~1MB。

語法格式如下:

ADRL{cond} register,label

a) cond

可選的指令執(zhí)行條件。

b) register

目標(biāo)寄存器。

c) label

基于PC或具體寄存器的表達(dá)式。

2、 使用說明

ADRL偽指令與ADR偽指令相似,用于將基于PC相對偏移的地址或基于寄存器相對偏移的地址值讀取到寄存器中。所不同的是,ADRL偽指令比ADR偽指令可以讀取更大范圍的地址。這是因?yàn)樵诰幾g階段,ADRL偽指令被編譯器換成兩條指令。即使一條指令可以完成該操作,編譯器也將產(chǎn)生兩條指令,其中一條為多余指令。如果匯編器不能在兩條指令內(nèi)完成操作,將報告錯誤,中止編譯。

1.2.3?ldr偽指令

1、 語法格式

ldr偽指令裝載一個32位的常數(shù)和一個地址到寄存器。

語法格式如下:

ldr{cond}{.W} register,=[expr|label-expr]

(12) Cond

可選的指令執(zhí)行條件。

(13) .W

可選項(xiàng)。指定指令寬度(Thumb-2指令集支持)。

(14) register

目標(biāo)寄存器。

(15) expr

32位常量表達(dá)式。匯編器根據(jù)expr的取值情況,對ldr偽指令做如下處理。

① 當(dāng)expr表示的地址值沒有超過MOV指令或MVN指令的地址取值范圍時,匯編器用一對MOV和MVN指令代替ldr指令。

② 當(dāng)expr表示的指令地址值超過了MOV指令或MVN指令的地址范圍時,匯編器將常數(shù)放入數(shù)據(jù)緩存池,同時用一條基于PC的ldr指令讀取該常數(shù)。

(16) label-expr

一個程序相關(guān)或聲明為外部的表達(dá)式。匯編器將label-expr表達(dá)式的值放入數(shù)據(jù)緩存池,使用一條程序相關(guān)ldr指令將該值取出放入寄存器。

當(dāng)label-expr被聲明為外部的表達(dá)式時,匯編器將在目標(biāo)文件中插入鏈接重定位偽操作,由鏈接器在鏈接時生成該地址。

2、 使用說明

當(dāng)要裝載的常量超出了MOV指令或MVN指令的范圍時,使用ldr指令。

由ldr指令裝載的地址是絕對地址,即PC相關(guān)地址。

當(dāng)要裝載的數(shù)據(jù)不能由MOV指令或MVN指令直接裝載時,該值要先放入數(shù)據(jù)緩存池,此時ldr偽指令處的PC值到數(shù)據(jù)緩存池中目標(biāo)數(shù)據(jù)所在地址的偏移量有一定限制。ARM或32位的Thumb-2指令中該范圍是?4~4KB,Thumb或16位的Thumb-2指令中該范圍是0~1KB。

3、 示例

(1)將常數(shù)0xff0讀到R1中。

ldr R3,=0xff0 ;

相當(dāng)于下面的ARM指令:

MOV R3,#0xff0

(2)將常數(shù)0xfff讀到R1中。

ldr R1,=0xfff ;

相當(dāng)于下面的ARM指令:

ldr R1,[pc,offset_to_litpool]

litpool .word 0xfff

(3)將place標(biāo)號地址讀入R1中。

ldr R2,=place ;

相當(dāng)于下面的ARM指令:

ldr R2,[pc,offset_to_litpool]

litpool .word place

1.3?GNU ARM 匯編語言的語句格式

在匯編語言程序設(shè)計(jì)中,經(jīng)常使用各種符號代替地址(addresses)、變量(variables)和常量(constants)等,以增加程序的靈活性和可讀性。盡管符號的命名由編程者決定,但并不是任意的,必須遵循以下的約定。

(1) 符號區(qū)分大小寫,同名的大、小寫符號會被編譯器認(rèn)為是兩個不同的符號。

(2) 符號在其作用范圍內(nèi)必須唯一。

(3) 自定義的符號名不能與系統(tǒng)的保留字相同。其中保留字包括系統(tǒng)內(nèi)部變量(built in variable)和系統(tǒng)預(yù)定義(predefined symbol)的符號。

(4) 符號名不應(yīng)與指令或偽指令同名。如果要使用和指令或偽指令同名的符號要用雙斜杠“//”將其括起來,如“//SSERT//”。

注意:雖然符號被雙斜杠括起來,但雙斜杠并非符號名的一部分。

(5) 局部標(biāo)號以數(shù)字開頭,其他的符號都不能以數(shù)字開頭。

1、 變量(variable)

程序中的變量是指其值在程序的運(yùn)行過程中可以改變的量。ARM(Thumb)匯編程序所支持的變量有3種。

? 數(shù)字變量(numeric)。

? 邏輯變量(logical)。

? 字符串變量(string)。

數(shù)字變量用于在程序的運(yùn)行中保存數(shù)字值,但注意數(shù)字值的大小不應(yīng)超出數(shù)字變量所能表示的范圍。

邏輯變量用于在程序的運(yùn)行中保存邏輯值,邏輯值只有兩種取值情況:真({TURE})和假({FALSE})。

字符串變量用于在程序的運(yùn)行中保存一個字符串,注意字符串的長度不應(yīng)超出字符串變量所能表示的范圍。

在ARM(Thumb)匯編語言程序設(shè)計(jì)中,可使用GBLA、GBLL、GBLS偽指令聲明全局變量,使用LCLA、LCLL、LCLS偽指令聲明局部變量,可使用SETA、SETL和SETS對其進(jìn)行初始化。

2、 常量(constants)

程序中的常量是指其值在程序的運(yùn)行過程中不能被改變的量。ARM(Thumb)匯編程序所支持的常量有數(shù)字常量、邏輯常量和字符串常量。

數(shù)字常量一般為32位的整數(shù),當(dāng)作為無符號數(shù)時,其取值范圍為0~232?1,當(dāng)作為有符號數(shù)時,其取值范圍為?231~231?1。匯編器認(rèn)為?n和232?n是相等的。對于關(guān)系操作,如比較兩個數(shù)的大小,匯編器將其操作數(shù)看做無符號的數(shù),也就是說“0>?1”,對匯編器來說取值為“假({FLASE})”。

邏輯常量只有兩種取值情況,真或假。

字符串常量為一個固定的字符串,一般用于程序運(yùn)行時的信息提示。

3、 程序中的變量代換

匯編語言中的變量可以作為一整行出現(xiàn)在匯編程序中,也可以作為行的一部分使用。如果在數(shù)字變量前面有一個代換操作符“$”,編譯器會將該數(shù)字變量的值轉(zhuǎn)換為十六進(jìn)制的字符串,并將該十六進(jìn)制的字符串代換“$”后的數(shù)字變量。

如果在邏輯變量前面有一個代換操作符“$”,編譯器會將該邏輯變量代換為它的取值(真或假)。如果在字符串變量前面有一個代換操作符“$”,編譯器會將該字符串變量的值代換“$”后的字符串變量。如果程序中需要字符“$”,則可以用“$$”來表示。匯編器將不進(jìn)行變量替換,而是將“$$”作為“$”。

4、 程序標(biāo)號(label)

在ARM匯編中,標(biāo)號代表一個地址,段內(nèi)標(biāo)號的地址在匯編時確定,而段外標(biāo)號地址值在鏈接時確定。根據(jù)標(biāo)號的生成方式,程序標(biāo)號分為以下3種。

? 程序相關(guān)標(biāo)號(Program-relative labels)。

? 寄存器相關(guān)標(biāo)號(Register-relative labels)。

? 絕對地址(Absolute address)。

程序相關(guān)標(biāo)號

程序相關(guān)標(biāo)號指位于目標(biāo)指令前的標(biāo)號或程序中的數(shù)據(jù)定義偽操作前的標(biāo)號。這種標(biāo)號在匯編時將被處理成PC值加上或減去一個數(shù)字常量。它常用于表示跳轉(zhuǎn)指令的目標(biāo)地址或代碼段中所嵌入的少量數(shù)據(jù)。

寄存器相關(guān)地址

這種標(biāo)號在匯編時將被處理成寄存器的值加上或減去一個數(shù)字常量。它常被用于訪問數(shù)據(jù)段中的數(shù)據(jù)。這種基于寄存器的標(biāo)號通常用MAP和FIELD偽操作定義,也可以用EQU偽操作定義。

絕對地址

絕對地址是一個32位的數(shù)字量,使用它可以直接尋址整個內(nèi)存空間。

5、 局部標(biāo)號

局部標(biāo)號是一個0~99之間的十進(jìn)制數(shù)字,可重復(fù)定義。局部標(biāo)號后面可以緊接一個通常表示該局部變量作用范圍的符號。局部變量的作用范圍為當(dāng)前段,也可以用偽操作ROUT來定義局部標(biāo)號的作用范圍。

局部標(biāo)號在子程序或程序循環(huán)中常被用到,也可以配合宏定義偽操作(.MACRO和.MEND)來使程序結(jié)構(gòu)更加合理。在同一個段中,可以使用相同的數(shù)字命名不同的局部變量。默認(rèn)情況下,匯編器會尋址最近的變量。也可以通過匯編器命令選項(xiàng)來改變搜索順序。

1.4?ARM 匯編語言的程序結(jié)構(gòu)

1.4.1?匯編語言的程序格式

在ARM(Thumb)匯編語言程序中可以使用.section來進(jìn)行分段,其中每一個段用段名或者文件結(jié)尾為結(jié)束,這些段使用默認(rèn)的標(biāo)志,如a為允許段,w為可寫段,x為執(zhí)行段。

在一個段中,我們可以定義下列的子段:

? .text

? .data

? .bss

? .sdata

? .sbss

由此我們可知道,段可以分為代碼段、數(shù)據(jù)段及其他存儲用的段,.text(正文段)包含程序的指令代碼;.data(數(shù)據(jù)段)包含固定的數(shù)據(jù),如常量、字符串;.bss(未初始化數(shù)據(jù)段)包含未初始化的變量、數(shù)組等,當(dāng)程序較長時,可以分割為多個代碼段和數(shù)據(jù)段,多個段在程序編譯鏈接時最終形成一個可執(zhí)行的映像文件。

.section.data

< initialized data here>

.section .bss

< uninitialized data here>

.section .text

.globl _start

_start:

<instruction code goes here>

1.4.2?匯編語言子程序調(diào)用

在ARM匯編語言程序中,子程序的調(diào)用一般是通過BL指令來實(shí)現(xiàn)的。在程序中,使用指令“BL子程序”名即可完成子程序的調(diào)用。

該指令在執(zhí)行時完成如下操作:將子程序的返回地址存放在連接寄存器LR中,同時將程序計(jì)數(shù)器PC指向子程序的入口點(diǎn)。當(dāng)子程序執(zhí)行完畢需要返回調(diào)用處時,只需要將存放在LR中的返回地址重新復(fù)制給程序計(jì)數(shù)器PC即可。在調(diào)用子程序的同時,也可以完成參數(shù)的傳遞和從子程序返回運(yùn)算的結(jié)果,通??梢允褂眉拇嫫鱎0~R3完成。

注意:

同編譯器編譯的代碼間的相互調(diào)用,要遵循AAPCS(ARM Architecture)。詳見ARM編譯工具手冊。

以下是使用BL指令調(diào)用子程序的匯編語言源程序的基本結(jié)構(gòu):

.text

.global _start

_start:

ldr R0,=0x3FF5000

ldr R1,0xFF

STR R1,[R0]

ldr R0,=0x3FF5008

ldr R1,0x01

STR R1,[R0]

BL PRINT_TEXT

PRINT_TEXT:

MOV PC,BL

1.4.3?過程調(diào)用標(biāo)準(zhǔn)AAPCS

為了使不同編譯器編譯的程序之間能夠相互調(diào)用,必須為子程序間的調(diào)用規(guī)定一定的規(guī)則。AAPCS就是這樣一個標(biāo)準(zhǔn)。所謂AAPCS,其英文全稱為Procedure Call Standard for the ARM Architecture(AAPCS),即ARM體系結(jié)構(gòu)過程調(diào)用標(biāo)準(zhǔn)。它是ABI(Application Binary Interface(ABI)for the ARM Architecture (base standard) [BSABI])標(biāo)準(zhǔn)的一部分。

可以使用“--apcs”選項(xiàng)告訴編譯器將源代碼編譯成符號AAPCS調(diào)用標(biāo)準(zhǔn)的目標(biāo)代碼。

注意:

使用“--apcs”選項(xiàng)并不影響代碼的產(chǎn)生,編譯器只是在各段中放置相應(yīng)的屬性,標(biāo)識用戶選定的AAPCS屬性。

1、 AAPCS相關(guān)的編譯/匯編選項(xiàng)

? none:指定輸入文件不使用AAPCS規(guī)則。

? /interwork:指定輸入文件符合ARM/Thumb交互標(biāo)準(zhǔn)。

? /nointerwork:指定輸入文件不能使用ARM/Thumb交互。這是編譯器默認(rèn)選項(xiàng)。

? /ropi:指定輸入文件是位置無關(guān)只讀文件。

? /noropi:指定輸入文件是非位置無關(guān)只讀文件。這是編譯器默認(rèn)選項(xiàng)。

? /pic:同/ropi。

? /nopic:同/noropi。

? /rwpi:指定輸入文件是位置無關(guān)可讀可寫文件。

? /norwpi:指定輸入文件是非位置無關(guān)可讀可寫文件。

? /pid:同/rwpi。

? /nopid:同/norwpi。

? /fpic:指定輸入文件編譯成位置無關(guān)只讀代碼。代碼中地址是FPIC地址。

? /swstackcheck:編譯過程中對輸入文件使用堆棧檢測。

? /noswstackcheck:編譯過程中對輸入文件不使用堆棧檢測。這是編譯器默認(rèn)選項(xiàng)。

? /swstna:如果匯編程序?qū)τ谑欠襁M(jìn)行數(shù)據(jù)棧檢查無所謂,而與該匯編程序連接的其他程序指定了選項(xiàng)/swst或選項(xiàng)/noswst,這時該匯編程序使用選項(xiàng)/swstna。

2、 ARM寄存器使用規(guī)則

AAPCS中定義了ARM寄存器使用規(guī)則如下:

子程序間通過寄存器R0、R1、R2、 R3來傳遞參數(shù)。如果參數(shù)多于4個,則多出的部分用堆棧傳遞。被調(diào)用的子程序在返回前無須恢復(fù)寄存器R0-R3的內(nèi)容。

在子程序中,使用寄存器R4-R11來保存局部變量。如果在子程序中使用到了寄存器R4-R11中的某些寄存器,子程序進(jìn)入時必須保存這些寄存器的值,在返回前必須恢復(fù)這些寄存器的值;對于子程序中沒有用到的寄存器則不必進(jìn)行這些操作。在Thumb程序中,通常只能使用寄存器R4-R7來保存局部變量。

寄存器R12用做子程序間scratch寄存器(用于保存SP,在函數(shù)返回時使用該寄存器出棧),記作ip。在子程序間的連接代碼段中常有這種使用規(guī)則。

寄存器R13用做數(shù)據(jù)棧指針,記作sp。在子程序中寄存器R13不能用做其他用途。寄存器sp在進(jìn)入子程序時的值和退出子程序時的值必須相等。

寄存器R14稱為連接寄存器,記作lr。它用于保存子程序的返回地址。如果在子程序中保存了返回地址,寄存器R14則可以用做其他用途。

寄存器R15是程序計(jì)數(shù)器,記作pc。它不能用做其他用途。

ARM寄存器在函數(shù)調(diào)用過程中的保護(hù)規(guī)則,如圖所示。


1.4.4?匯編語言程序設(shè)計(jì)舉例

通過組合使用條件執(zhí)行和條件標(biāo)志設(shè)置,可簡單地實(shí)現(xiàn)分支語句,不需要任何分支指令。這樣可以改善性能,因?yàn)榉种е噶顣加幂^多的周期數(shù);同時這樣做也可以減小代碼尺寸,提高代碼密度。

下面是一段C語言程序,該程序?qū)崿F(xiàn)了著名的Euclid最大公約數(shù)算法:

int gcd(int a, int b)

{

while (a != b)

{

if (a > b)

a = a - b;

else

b = b - a;

}

return a;

}

用ARM匯編語言重寫來重寫這個例子,如下所示。

Code1:

gcd:

CMP r0, r1

BEQ end

BLT less

SUB r0, r0, r1

B gcd

Less:

SUB r1, r1, r0

B gcd

充分地利用條件執(zhí)行修改上面的例子,得到Code2。

Code2:

gcd:

CMP r0, r1

SUBGT r0, r0, r1

SUBLT r1, r1, r0

BNE gcd

兩段代碼的比較如下。

? Code1:僅使用了分支指令。

? Code2:充分利用了ARM指令條件執(zhí)行的特點(diǎn),僅使用了4條指令就完成了全部算法。這對提供程序的代碼密度和執(zhí)行速度十分有幫助。

事實(shí)上,分支指令十分影響處理器的速度。每次執(zhí)行分支指令,處理器都會排空流水線,重新裝載指令。

1.5?ARM 偽指令實(shí)驗(yàn)

1.5.1?實(shí)驗(yàn)?zāi)康?/h1>

? 掌握 ARM匯編語言的基本使用和一些偽指令的使用;

? 熟悉 eclipse 開發(fā)工具建立匯編工程和仿真;

1.5.2?實(shí)驗(yàn)原理

根據(jù)上面闡述RAM 匯編語言的使用語法和功能,編寫匯編程序,實(shí)現(xiàn)將存放在兩個內(nèi)存中的數(shù)據(jù)相加的操作。

1.5.3?實(shí)驗(yàn)內(nèi)容

匯編程序設(shè)計(jì)如下

.text

.global _start

_start:

.code 32

mov r0, #0x9

nop

mov r1, #0

ldr r2,=myarray

loop:

ldr r3,[r2],#4

add r1,r1,r3

cmp r3,#0

bne loop

stop:

b stop

myarray:

.word 6

.word 24

.word 12

.word 0

.end

1.5.4?實(shí)驗(yàn)步驟

(1) 導(dǎo)入工程源碼

請參考第37章的導(dǎo)入已有工程章節(jié)。

光盤實(shí)驗(yàn)源碼路徑:【資料光盤\華清遠(yuǎn)見-FS-MP1A開發(fā)資料-2020-11-06\02-程序源碼\03-ARM體系結(jié)構(gòu)與接口技術(shù)\Cortex-A7\h_test】

1.5.5?實(shí)驗(yàn)現(xiàn)象

1、 單擊“

”單步,查看Rn寄存器的變化。

三個數(shù)據(jù)的和保存在R1中,最終R1的數(shù)值為42。

1.6?ARM 內(nèi)聯(lián)匯編實(shí)驗(yàn)

1.6.1?實(shí)驗(yàn)?zāi)康?/h1>

? 掌握 ARM匯編語言的基本使用和內(nèi)聯(lián)匯編用法;

? 熟悉 eclipse 開發(fā)工具建立匯編工程和仿真;

1.6.2?實(shí)驗(yàn)原理

根據(jù)上面闡述RAM 匯編語言的使用語法和功能,在C語言中編寫內(nèi)聯(lián)匯編代碼,實(shí)現(xiàn)得到兩個參數(shù)的最小公倍數(shù)。

1.6.3?實(shí)驗(yàn)內(nèi)容

實(shí)驗(yàn)程序設(shè)計(jì)如下

unsigned int gcd(unsigned int data1,unsigned int data2)

{

int arg1,arg2,result;

arg1 = data1;

arg2 = data2;

asm volatile

(

"mov r0, #0\n\t"

"mul r1,%1,%2\n\t"

"loop: \n\t"

"cmp %1,%2\n\t"

"subgt %1,%1,%2\n\t"

"sublt %2,%2,%1\n\t"

"bne loop\n\t"

"mov r2,%0\n\t"

"loop1: \n\t"

"cmp r1, #0\n\t"

"subgt r1, r1,r2\n\t"

"addgt r0, r0,#1\n\t"

"bne loop1\n\t"

"mov %0, r0\n\t"

: "=r" (result) //輸出

: "r"(arg1),"r"(arg2) //輸入

// : "r3"

);

return result;

}

int main(void)

{

unsigned long result;

unsigned int a = 6,b = 8;

result = gcd(a,b);

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

return 0;

}

1.6.4?實(shí)驗(yàn)步驟

(1) 導(dǎo)入工程源碼

相關(guān)內(nèi)容請參考Cortex-A7開發(fā)環(huán)境搭建章節(jié)導(dǎo)入已有工程。

光盤實(shí)驗(yàn)源碼路徑:【資料光盤\華清遠(yuǎn)見-FS-MP1A開發(fā)資料-2020-11-06\02-程序源碼\03-ARM體系結(jié)構(gòu)與接口技術(shù)\Cortex-A7\h_inline】

1.6.5?實(shí)驗(yàn)結(jié)果

1、 點(diǎn)擊“

”單步,查看result結(jié)果。

如圖所示,6和8的最小公倍數(shù)為24,結(jié)果正確。


硬件平臺:華清遠(yuǎn)見FS-MP1A開發(fā)板(STM32MP157)

部分開發(fā)教程下載:加QQ群483143191,群文件里有。

部分視頻課程收看:華清遠(yuǎn)見研發(fā)中心的個人空間_嗶哩嗶哩_Bilibili

淘寶購買鏈接:item.taobao.com/item.ht

手機(jī)淘寶分享碼:復(fù)制本行文字打開手淘?T4FPXn3YYJ2?


ARM裸機(jī)開發(fā)篇3:ARM匯編語言程序設(shè)計(jì)的評論 (共 條)

分享到微博請遵守國家法律
普格县| 华宁县| 开远市| 富民县| 宁波市| 安多县| 北安市| 工布江达县| 平和县| 日土县| 云安县| 三穗县| 阳东县| 新郑市| 齐齐哈尔市| 台南市| 宜宾县| 泉州市| 云南省| 木里| 陆河县| 德化县| 云阳县| 祁门县| 青阳县| 长春市| 西华县| 巴彦淖尔市| 正宁县| 奉化市| 芮城县| 什邡市| 大城县| 琼海市| 通榆县| 武鸣县| 沅陵县| 沙湾县| 大洼县| 通许县| 资阳市|