基于stm32mp157 linux開發(fā)板ARM裸機(jī)開發(fā)教程5:ARM微處理器指令系統(tǒng)(連載中)
前言:
目前針對ARM Cortex-A7裸機(jī)開發(fā)文檔及視頻進(jìn)行了二次升級持續(xù)更新中,使其內(nèi)容更加豐富,講解更加細(xì)致,全文所使用的開發(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)篇等。除此之外計(jì)劃針對Linux系統(tǒng)移植篇、Linux驅(qū)動開發(fā)篇均會進(jìn)行文檔及視頻的二次升級更新敬請關(guān)注!
開發(fā)板更多資料可關(guān)注華清遠(yuǎn)見在線實(shí)驗(yàn)室(微信號:hqyjlab)領(lǐng)取~~~
ARM 微處理器指令系統(tǒng)
ARM 指令集可以分為跳轉(zhuǎn)指令、數(shù)據(jù)處理指令、程序狀態(tài)寄存器傳輸指令、Load/Store 指令、協(xié)處理
器指令和異常中斷產(chǎn)生指令。根據(jù)使用的指令類型不同,指令的尋址方式分為數(shù)據(jù)處理指令尋址方式和內(nèi)存訪問指令尋址方式。
本章主要介紹 ARM 匯編語言。主要內(nèi)容如下:
? ARM 處理器的尋址方式。
? ARM 處理器的指令集
ARM 指令的組成
<opcode> {<c>} {S} <Rd>,<Rn>,<shifter_operand>
指令解析:
<opcode>: 要執(zhí)行的指令
{<c>}: 為指令執(zhí)行的條件碼。當(dāng)忽略< c >時(shí),指令為無條件執(zhí)行。
表 44.7.2.1 指令執(zhí)行條件

{S}: 決定指令的操作是否影響 CPSR。在異常返回時(shí),如果操作的目標(biāo)是 PC 寄存器時(shí),S 標(biāo)志會同
時(shí)將 SPSR 寄存器恢復(fù)到 CPSR 中。
<Rd>: 為目標(biāo)寄存器。
<Rn>: 操作數(shù)所在的寄存器。
<shifter_operand>有 11 種形式,如表所示

以下是關(guān)于 MOV 指令的一些示例:
示例代碼 45-1 示例
1 MOV R0,#2
2 ADDS R0,R0,R1
3 MOV R2,R0
4 MOV R1, R0, LSL #2
ARM 處理器尋址方式
ARM 指令的尋址方式分為數(shù)據(jù)處理指令尋址方式和內(nèi)存訪問指令尋址方式。
數(shù)據(jù)處理指令尋址方式
數(shù)據(jù)處理指令尋址方式可以分為以下幾種。
? 立即數(shù)尋址方式。
? 寄存器尋址方式。
? 寄存器移位尋址方式。
1、 立即數(shù)尋址方式
指令中的立即數(shù)是由一個(gè) 8bit 的常數(shù)移動 4bit 偶數(shù)位(0,2,4,…,26,28,30)得到的。所以,每一條指令都包含一個(gè) 8bit 的常數(shù) X 和移位值 Y,得到的立即數(shù) = X 循環(huán)右移(2×Y),如圖所示。

立即數(shù)為什么需要通過上述運(yùn)算得到呢?這里我們以 ldr 指令的機(jī)器碼為例來分析原因。

上圖中 imm12 就是立即數(shù)所占用的位域,在這里可以看到 imm12 的區(qū)域只有 12bit,要用一個(gè) 12bit
的編碼來表示任意的 32bit 數(shù)是絕對不可能的。而然在實(shí)際的開發(fā)過程中又要用 12bit 的編碼來表示 32bit
數(shù)。那么只有在表示數(shù)的數(shù)量上做限制,通過編碼來實(shí)現(xiàn)用 12bit 的編碼來表示 32bit 數(shù)。
在上面我們提到了立即數(shù) = X 循環(huán)右移(2×Y),盡管表示的范圍變大了, 但是 12 位所能表現(xiàn)的數(shù)字的個(gè)數(shù)是一定的. 因此, ARM 規(guī)定并不是所有的 32 位常數(shù)都是合法的立即數(shù), 只有通過上面的構(gòu)造方法得到的才是合法的立即數(shù)。
ARM 匯編編譯器按照下面的規(guī)則來產(chǎn)生立即數(shù)的編碼:
當(dāng)立即數(shù)數(shù)值在 0~0xFF 范圍時(shí),令 X=立即數(shù),Y=0。
其他情況下,匯編編譯器選擇使 Y 數(shù)值最小的編碼方式。
則來產(chǎn)生立即數(shù)的編碼:
下面列舉了一些有效的立即數(shù):
0xFF:X=0xFF,Y=0
0x104:X=0x41,Y=15
0xFF0:X=0xFF,Y=14
0xF000000F:X=0xFF,Y=2
下面是一些無效的立即數(shù):
0x101、0x102、0xFF1、0xFF04、0xFF003、0xFFFFFFFF、0xF000001F
下面是一些應(yīng)用立即數(shù)的指令
示例代碼 45-2 立即數(shù)
1 MOV R0,#0 ;送0到R0
2 ADD R3,R3,#1 ;R3的值加1
3 CMP R7,#1000 ;將R7的值和1000比較
4 BIC R9,R8,#0xFF00 ;將 R8 中 8~15 位清零,結(jié)果保存在 R9 中
2、 寄存器尋址方式
寄存器的值可以被直接用于數(shù)據(jù)操作指令,這種尋址方式是各類處理器經(jīng)常采用的一種方式,也是一種執(zhí)行效率較高的尋址方式,如:
示例代碼 45-3 寄存器尋址
1 MOV R2,R0 ;R0的值送R2
2 ADD R4,R3,R2 ;R2加R3,結(jié)果送R4
3 CMP R7,R8 ;比較 R7 和 R8 的值
3、 寄存器移位尋址方式
和寄存器尋址類似,只是操作前需要對寄存器操作數(shù)進(jìn)行移位操作。寄存器的值在被送到 ALU 之前,可以事先經(jīng)過桶形移位寄存器的處理。預(yù)處理和移位發(fā)生在同一周期內(nèi),所以有效地使用移位寄存器,可以增加代碼的執(zhí)行效率。
LSL<c> <Rd>, <Rm>, #<imm5>
LSR<c> <Rd>, <Rm>, #<imm>
ASR<c> <Rd>, <Rm>, #<imm>
ROR{S}<c> <Rd>, <Rm>, #<imm>
RRX{S}<c> <Rd>, <Rm>
{<c>}: 為指令執(zhí)行的條件碼。當(dāng)忽略< c >時(shí),指令為無條件執(zhí)行。
<Rd>: 為目標(biāo)寄存器。
<Rm>: 操作數(shù)所在的寄存器。
<imm>: 移位量,范圍 1-32。

帶擴(kuò)展的循環(huán)右移(RRX): 操作數(shù)右移一位,移位空出的高位用 C 標(biāo)志的值填充。
下面是一些在指令中使用了移位操作的例子:
示例代碼 45-4 寄存器移位尋址
1 ADD R2, R0, R1, LSR #5 ;將R1的值邏輯右移5位后與R0相加結(jié)果傳入R2
2 MOV R1, R0, LSL #2 ;將R0的值邏輯左移2位后將結(jié)果傳入R1
3 SUB R1, R2, R0, LSR #4 ;用R2的值減去將R0的值邏輯右移4位后的值將結(jié)果傳入R1
4 MOV R2, R4, ROR R0 ;將R4的值循環(huán)右移R0次,結(jié)果傳入R2
內(nèi)存訪問指令尋址方式
內(nèi)存訪問指令的尋址方式可以分為以下幾種。
? 字及無符號字節(jié)的 Load/Store 指令的尋址方式。
? 雜類 Load/Store 指令的尋址方式。
? 批量 Load/Store 指令的尋址方式。
? 協(xié)處理器 Load/Store 指令的尋址方式。
1、 字及無符號字節(jié)的 Load/Store 指令的尋址方式
字及無符號字節(jié)的 Load/Store 指令語法格式如下:
LDR|STR{<cond>}{B}{T} <Rd>,<addressing_mode>


上表中,“!”表示完成數(shù)據(jù)傳輸后要更新基址寄存器。
2、 雜類 Load/Store 指令的尋址方式
使用該類尋址方式的指令的語法格式如下:
LDR|STR{<cond>}H|SH|SB|D <Rd>, <addressing_mode>
使用該類尋址方式的指令包括(有符號/無符號)半字 Load/Store 指令、有符號字節(jié) Load/Store 指令和雙字 Load/Store 指令。
表 45.2.2.2 <addressing_mode>尋址方式

3、 堆棧操作尋址方式
堆棧操作尋址方式和批量 Load/Store 指令尋址方式十分類似。但對于堆棧的操作,數(shù)據(jù)寫入內(nèi)存和從內(nèi)存中讀出要使用不同的尋址模式,因?yàn)檫M(jìn)棧操作(pop)和出棧操作(push)要在不同的方向上調(diào)整堆棧。
該類指令的語法格式如下:
LDM|STM {<amode>}{<cond>}<addressing_mode> <Rn>{!},<registers><^>
下面詳細(xì)討論如何使用合適的尋址方式實(shí)現(xiàn)數(shù)據(jù)的堆棧操作。
根據(jù) amode 不同的尋址方式,將堆棧分為以下 4 種。
1) 滿棧:堆棧指針指向棧頂元素(last used location)。
2) 空棧:堆棧指針指向第一個(gè)可用元素(the first unused location)。
3) 遞減棧:堆棧向內(nèi)存地址減小的方向生長。
4) 遞增棧:堆棧向內(nèi)存地址增加的方向生長。
根據(jù)堆棧的不同種類,將其尋址方式分為以下 4 種。
1) 滿遞減 FD(Full Descending)。
2) 空遞減 ED(Empty Descending)。
3) 滿遞增 FA(Full Ascending)。
4) 空遞增 EA(Empty Ascending)。

如表所示列出了堆棧的尋址方式和批量 Load/Store 指令尋址方式的對應(yīng)關(guān)系。

批量 Load/Store 指令尋址方式
批量 Load/Store 指令將一片連續(xù)內(nèi)存單元的數(shù)據(jù)加載到通用寄存器組中或?qū)⒁唤M通用寄存器的數(shù)據(jù)存儲到內(nèi)存單元中。
批量 Load/Store 指令的尋址模式產(chǎn)生一個(gè)內(nèi)存單元的地址范圍,指令寄存器和內(nèi)存單元的對應(yīng)關(guān)系滿足這樣的規(guī)則,即編號低的寄存器對應(yīng)于內(nèi)存中低地址單元,編號高的寄存器對應(yīng)于內(nèi)存中的高地址單元。
該類指令的語法格式如下:
LDM|STM {<amode>}{<cond>}<addressing_mode> <Rn>{!},<registers><^>

協(xié)處理器 Load/Store 尋址方式
協(xié)處理器 Load/Store 指令的語法格式如下:
MCR<c> <coproc>, <opc1>, <Rt>, <CRn>, <CRm>{, <opc2>}
MRC<c> <coproc>, <opc1>, <Rt>, <CRn>, <CRm>{, <opc2>}
<c>:為指令執(zhí)行的條件碼。當(dāng)忽略< c >時(shí),指令為無條件執(zhí)行
<coproc>:協(xié)處理器名稱,范圍 p0-p15;
<opc1>:協(xié)處理器操作碼,范圍 0-15;
<Rt>:源寄存器,MCR 指令是將 Rt 寄存器寫入?yún)f(xié)處理器,MRC 指令是將協(xié)處理器的內(nèi)容讀取到 Rt 寄存器;
<CRn>:協(xié)處理器的目標(biāo)寄存器;
<CRm>:協(xié)處理器中附加的目標(biāo)寄存器或者源操作寄存器,如果不需要附加信息就設(shè)置為 C0,否則結(jié)果不可預(yù)測;
<opc2>:可選的協(xié)處理器特定操作碼,當(dāng)不需要時(shí)置 0。
關(guān)于 CRn、opc1、CRm、opc2 等操作,以下給出了 CP15 協(xié)處理器的寄存器排布。操作不同的協(xié)處理器時(shí)對應(yīng)著不同的取值

例如前面講到的 Cache 操作指令
示例代碼 45-5 使能 ICache
1 /******Cache Test*******/
2 mrc p15,0,r1,c1,c0,0
3 orr r1, r1, #(1 << 2) // Set C bit 整體使能Cache
4 orr r1, r1, #(1 << 12) //Set I bit 使能ICache
5 mcr p15,0,r1,c1,c0,0
6 /******End Test******/
如果需要操作其它協(xié)處理器可以通過查閱《ARM? Architecture Reference Manual》或者《Cortex-A7 MPCore Technical Reference Manual》官方文檔進(jìn)行查閱。
ARM 處理器指令集
數(shù)據(jù)操作指令
數(shù)據(jù)操作指令是指對存放在寄存器中的數(shù)據(jù)進(jìn)行操作的指令。主要包括數(shù)據(jù)傳送指令、算術(shù)指令、邏輯指令、比較與測試指令及乘法指令。
如果在數(shù)據(jù)處理指令前使用 S 前綴,指令的執(zhí)行結(jié)果將會影響 CPSR 中的標(biāo)志位。數(shù)據(jù)處理指令如表所示。

1、 MOV 指令
MOV 指令是最簡單的 ARM 指令,執(zhí)行的結(jié)果就是把一個(gè)數(shù) N 送到目標(biāo)寄存器 Rd,其中 N 可以是寄存器,也可以是立即數(shù)。
MOV 指令多用于設(shè)置初始值或者在寄存器間傳送數(shù)據(jù)。
MOV 指令將移位碼(shifter_operand)表示的數(shù)據(jù)傳送到目的寄存器 Rd,并根據(jù)操作的結(jié)果更新 CPSR中相應(yīng)的條件標(biāo)志位。
指令的語法格式:
MOV{<c>}{S} <Rd>,<shifter_operand>
{<c>}:為指令執(zhí)行的條件碼。當(dāng)忽略< c >時(shí),指令為無條件執(zhí)行。
{S}:決定指令的操作是否影響 CPSR。
<Rd>:目的寄存器。如果 R15 是目的寄存器,將修改程序計(jì)數(shù)器或標(biāo)志。這用于被調(diào)用的子函數(shù)結(jié)束后返回到調(diào)用代碼,方法是把連接寄存器的內(nèi)容傳送到 R15。
<shifter_operand>:要傳送到 Rd 寄存器的數(shù)據(jù),可以是立即數(shù)、寄存器或者通過移位操作得到指令舉例:
示例代碼 45-6 mov 示例
1 mov r0, r0 ; R0=R0 NOP 指令
2 mov r0, r0, lsl#3 ; R0=R0*8
3 mov pc, lr ; 退出到調(diào)用者,用于普通函數(shù)返回,PC即是R15
4 movs pc, lr ; 退出到調(diào)用者并恢復(fù)標(biāo)志位,用于異常函數(shù)返回
MOV 指令主要完成以下功能。
? 將數(shù)據(jù)從一個(gè)寄存器傳送到另一個(gè)寄存器。
? 將一個(gè)常數(shù)值傳送到寄存器中。
? 當(dāng) PC(R15)用做目的寄存器時(shí),可以實(shí)現(xiàn)程序跳轉(zhuǎn)。如“MOV PC,LR”,所以這種跳轉(zhuǎn)可以實(shí)現(xiàn)子程序調(diào)用及從子程序返回,代替指令“B,BL”。
? 當(dāng) PC 作為目標(biāo)寄存器且指令中 S 位被設(shè)置時(shí),指令在執(zhí)行跳轉(zhuǎn)操作的同時(shí),將當(dāng)前處理器模式的 SPSR 寄存器的內(nèi)容復(fù)制到 CPSR 中。這種指令“MOVS PC LR”可以實(shí)現(xiàn)從某些異常中斷中返回。
MVN 指令
MVN 是反相傳送(Move Negative)指令。它將操作數(shù)的反碼傳送到目的寄存器。
MVN 指令多用于向寄存器傳送一個(gè)負(fù)數(shù)或生成位掩碼。
MVN 指令將 shifter_operand 表示的數(shù)據(jù)的反碼傳送到目的寄存器 Rd,并根據(jù)操作結(jié)果更新 CPSR 中相應(yīng)的條件標(biāo)志位。
指令的語法格式:
MVN{<c>}{S} <Rd>,<shifter_operand>
{<c>}:為指令執(zhí)行的條件碼。當(dāng)忽略< c >時(shí),指令為無條件執(zhí)行。
{S}:決定指令的操作是否影響 CPSR。
<Rd>:目的寄存器。
<shifter_operand>:要傳送到 Rd 寄存器的數(shù)據(jù),可以是立即數(shù)、寄存器或者通過移位操作得到。這是邏輯非操作而不是算術(shù)操作,這個(gè)取反的值加 1 才是它的取負(fù)的值。
指令舉例:
示例代碼 45-7 mvn 示例
1 mvn r0, #4 ; r0 = -5
2 mvn r0, #0 ; r0 = -1
MVN 指令主要完成以下功能:
? 向寄存器中傳送一個(gè)負(fù)數(shù)。
? 生成位掩碼(Bit Mask)。
? 求一個(gè)數(shù)的反碼。
AND 指令
AND 指令將 shifter_operand 表示的數(shù)值與寄存器 Rn 的值按位(bitwise)做邏輯與操作,并將結(jié)果保存到目標(biāo)寄存器 Rd 中,同時(shí)根據(jù)操作的結(jié)果更新 CPSR 寄存器。
指令的語法格式:
AND{<c>}{S} <Rd>,<Rn>,<shifter_operand>
{<c>}:為指令執(zhí)行的條件碼。當(dāng)忽略< c >時(shí),指令為無條件執(zhí)行。
{S}:決定指令的操作是否影響 CPSR。
<Rd>:目的寄存器。
<Rn>:第一個(gè)操作數(shù)寄存器。
<shifter_operand>:要和 Rn 寄存器做與操作的數(shù)據(jù)
指令舉例:
示例代碼 45-8 and 示例
1 and r0, r0, #3 ;保留r0中的0位和1位,丟棄其余的位。
2 and r2,r1,r3 ;r2 = r1&r3
3 ands r0,r0,#0x01 ;r0 = r0&0x01,取出最低位數(shù)據(jù)
ORR 指令
ORR(Logical OR)為邏輯或操作指令,它將第 2 個(gè)源操作數(shù) shifter_operand 的值與寄存器 Rn 的值按位做“邏輯或”操作,結(jié)果保存到 Rd 中。
指令的語法格式:
ORR{<c>}{S} <Rd>,<Rn>,<shifter_operand>
{<c>}:為指令執(zhí)行的條件碼。當(dāng)忽略< c >時(shí),指令為無條件執(zhí)行。
{S}:決定指令的操作是否影響 CPSR。
<Rd>:目的操作數(shù)
<Rn>:第一個(gè)操作數(shù)。
<shifter_operand>:要與 Rn 做邏輯或的數(shù)
指令舉例:
示例代碼 45-9 orr 示例
1 orr r0, r0, #3 ;設(shè)置r0中位0和1
2 orr r0,r0,#0x0f ;將r0的低4位置1
3 ; 使用orr指令將r2的高8位數(shù)據(jù)移入到r3的低8位中。
4 mov r1,r2,lsr #4
5 orr r3,r1,r3,lsl #8
BIC 位清零指令
BIC(Bit Clear)位清零指令,將寄存器 Rn 的值與第 2 個(gè)源操作數(shù) shifter_operand 的值的反碼按位
做“邏輯與”操作,結(jié)果保存到 Rd 中。
指令的語法格式:
BIC{<c>}{S} <Rd>,<Rn>,<shifter_operand>
{<c>}:為指令執(zhí)行的條件碼。當(dāng)忽略< c >時(shí),指令為無條件執(zhí)行。
{S}:決定指令的操作是否影響 CPSR。
<Rd>:目的操作數(shù)
<Rn>:第一個(gè)操作數(shù)。
<shifter_operand>:取反碼與 Rn 做“邏輯與”操作
指令舉例:
示例代碼 45-10 bic 示例
1 bic r0, r0, #0x1011 ;清除r0中的位12、4和0位,保持其余的不變
2 bic r1, r2, r3 ;將 r3 和 r2 做“邏輯與”操作,結(jié)果保存到 r1 中
EOR 指令
EOR(Exclusive OR)指令將寄存器 Rn 中的值和 shifter_operand 的值執(zhí)行按位“異或”操作,并將執(zhí)行結(jié)果存儲到目的寄存器 Rd 中,同時(shí)根據(jù)指令的執(zhí)行結(jié)果更新 CPSR 中相應(yīng)的條件標(biāo)志位。
指令的語法格式:
EOR{<c>}{S} <Rd>,<Rn>,<shifter_operand>
{<c>}:為指令執(zhí)行的條件碼。當(dāng)忽略< c >時(shí),指令為無條件執(zhí)行。
{S}:決定指令的操作是否影響 CPSR。
<Rd>:目的寄存器。
<Rn>:第一個(gè)操作數(shù)寄存器。
<shifter_operand>:要和 Rn 寄存器做異或操作的數(shù)據(jù)
指令舉例:
示例代碼 45-11 eor 示例
1 eor r0, r0, #3 ;反轉(zhuǎn)r0中的位0和1
2 eor r1,r1,#0x0f ;將r1的低4位取反
3 eor r2,r1,r0 ;r2=r1∧r0
4 eors r0,r5,#0x01 ;r0=r5∧0x01 影響標(biāo)志位
SUB 指令
SUB(Subtract)指令從寄存器 Rn 中減去 shifter_operand 表示的數(shù)值,并將結(jié)果保存到目標(biāo)寄存器 Rd
中,并根據(jù)指令的執(zhí)行結(jié)果設(shè)置 CPSR 中相應(yīng)的標(biāo)志位。
指令的語法格式:
SUB{<c>}{S} <Rd>,<Rn>,<shifter_operand>
{<c>}:為指令執(zhí)行的條件碼。當(dāng)忽略< c >時(shí),指令為無條件執(zhí)行。
{S}:決定指令的操作是否影響 CPSR。
<Rd>:目的寄存器。
<Rn>:被減數(shù)。
<shifter_operand>:減數(shù)
指令舉例:
示例代碼 45-12 sub 示例
1 sub r0, r1, r2 ;r0 = r1?r2
2 sub r0, r1, #256 ;r0 = r1?256
3 sub r0, r2, r3,lsl#1 ;r0 = r2?(r3<<1)
RSB 指令
RSB(Reverse Subtract)指令從寄存器 shifter_operand 中減去 Rn 表示的數(shù)值,并將結(jié)果保存到目標(biāo)寄
存器 Rd 中,并根據(jù)指令的執(zhí)行結(jié)果設(shè)置 CPSR 中相應(yīng)的標(biāo)志位。
指令的語法格式:
RSB{<c>}{S} <Rd>,<Rn>,<shifter_operand>
{<c>}:為指令執(zhí)行的條件碼。當(dāng)忽略< c >時(shí),指令為無條件執(zhí)行。
{S}:決定指令的操作是否影響 CPSR。
<Rd>:目的寄存器。
<Rn>:減數(shù)。
<shifter_operand>:被減數(shù)
指令舉例:
下面的指令序列可以求一個(gè) 64 位數(shù)值的負(fù)數(shù)。64 位數(shù)放在寄存器 R0 與 R1 中,其負(fù)數(shù)放在 R2 和 R3中。其中 R0 與 R2 中放低 32 位值。
示例代碼 45-13 rsb 示例
1 rsbs r2,r0,#0
2 rsc r3,r1,#0
SBC 指令
SBC(Subtract with Carry)指令用于執(zhí)行操作數(shù)大于 32 位時(shí)的減法操作。該指令從寄存器 Rn 中減去shifter_operand 表示的數(shù)值,再減去寄存器 CPSR 中 C 條件標(biāo)志位的反碼[NOT(Carry flag)],并將結(jié)果保存到目標(biāo)寄存器 Rd 中,并根據(jù)指令的執(zhí)行結(jié)果設(shè)置 CPSR 中相應(yīng)的標(biāo)志位。
指令的語法格式:
SBC{<c>}{S} <Rd>,<Rn>,<shifter_operand>
{<c>}:為指令執(zhí)行的條件碼。當(dāng)忽略< c >時(shí),指令為無條件執(zhí)行。
{S}:決定指令的操作是否影響 CPSR。
<Rd>:目的寄存器。
<Rn>:被減數(shù)。
<shifter_operand>:減數(shù)
指令舉例:
下面的程序使用 SBC 實(shí)現(xiàn) 64 位減法,(R1,R0)?(R3,R2),結(jié)果存放到(R1,R0)。
示例代碼 45-14 sbc 示例
1 subs r0,r0,r2
2 sbcs r1,r1,r3
0、RSC 指令
RSC(Reverse Subtract with Carry)指令從寄存器 shifter_operand 中減去 Rn 表示的數(shù)值,再減去寄存器 CPSR 中 C 條件標(biāo)志位的反碼[NOT(Carry Flag)],并將結(jié)果保存到目標(biāo)寄存器 Rd 中,并根據(jù)指令的執(zhí)行結(jié)果設(shè)置 CPSR 中相應(yīng)的標(biāo)志位。
指令的語法格式:
RSC{<c>}{S} <Rd>,<Rn>,<shifter_operand>
{<c>}:為指令執(zhí)行的條件碼。當(dāng)忽略< c >時(shí),指令為無條件執(zhí)行。
{S}:決定指令的操作是否影響 CPSR。
<Rd>:目的寄存器。
<Rn>:減數(shù)。
<shifter_operand>:被減數(shù)
指令舉例:
下面的程序使用 RSC 指令實(shí)現(xiàn)求 64 位數(shù)值的負(fù)數(shù)。
示例代碼 45-15 rsc 示例
1 rsbs r2,r0,#0
2 rsc r3,r1,#0
ADD 指令
ADD 指令將寄存器 shifter_operand 的值加上 Rn 表示的數(shù)值,并將結(jié)果保存到目標(biāo)寄存器 Rd 中,并根據(jù)指令的執(zhí)行結(jié)果設(shè)置 CPSR 中相應(yīng)的標(biāo)志位。
指令的語法格式:
ADD{<c>}{S} <Rd>,<Rn>,<shifter_operand>
{<c>}:為指令執(zhí)行的條件碼。當(dāng)忽略< c >時(shí),指令為無條件執(zhí)行。
{S}:決定指令的操作是否影響 CPSR。
<Rd>:目的寄存器。
<Rn>:第一個(gè)操作數(shù)。
<shifter_operand>:要加的數(shù)
指令舉例:
示例代碼 45-16 add 示例
1 add r0, r1, r2 ; r0 = r1 + r2
2 add r0, r1, #256 ; r0 = r1 + 256
3 add r0, r2, r3,lsl#1 ; r0 = r2 + (r3 << 1)
ADC 指令
ADC 指令將寄存器 shifter_operand 的值加上 Rn 表示的數(shù)值,再加上 CPSR 中的 C 條件標(biāo)志位的值,將結(jié)果保存到目標(biāo)寄存器 Rd 中,并根據(jù)指令的執(zhí)行結(jié)果設(shè)置 CPSR 中相應(yīng)的標(biāo)志位。
指令的語法格式:
ADC{<c>}{S} <Rd>,<Rn>,<shifter_operand>
{<c>}:為指令執(zhí)行的條件碼。當(dāng)忽略< c >時(shí),指令為無條件執(zhí)行。
{S}:決定指令的操作是否影響 CPSR。
<Rd>:目的寄存器。
<Rn>:第一個(gè)操作數(shù)。
<shifter_operand>:要加的數(shù)
指令舉例:
ADC 指令將把兩個(gè)操作數(shù)加起來,并把結(jié)果放置到目的寄存器中。它使用一個(gè)進(jìn)位標(biāo)志位,這樣就可以做比 32 位大的加法。下面的例子將加兩個(gè) 128 位的數(shù)。
128 位結(jié)果:寄存器 R0、R1、R2 和 R3。
第一個(gè) 128 位數(shù):寄存器 R4、R5、R6 和 R7。
第二個(gè) 128 位數(shù):寄存器 R8、R9、R10 和 R11。
示例代碼 45-17 adc 示例
1 adds r0, r4, r8 ;加低端的字
2 adcs r1, r5, r9 ;加下一個(gè)字,帶進(jìn)位
3 adcs r2, r6, r10 ;加第三個(gè)字,帶進(jìn)位
4 adcs r3, r7, r11 ;加高端的字,帶進(jìn)位
CMP 指令
CMP(Compare)指令使用寄存器 Rn 的值減去 shifter_operand 的值,根據(jù)操作的結(jié)果更新 CPSR 中相應(yīng)的條件標(biāo)志位,以便后面的指令根據(jù)相應(yīng)的條件標(biāo)志來判斷是否執(zhí)行。
指令的語法格式:
CMP{<c>} <Rn>,<shifter_operand>
{<c>}:為指令執(zhí)行的條件碼。當(dāng)忽略< c >時(shí),指令為無條件執(zhí)行。
<Rn>:第一個(gè)操作數(shù)。
<shifter_operand>:比較的數(shù)
指令舉例:
CMP 指令允許把一個(gè)寄存器的內(nèi)容與另一個(gè)寄存器的內(nèi)容或立即值進(jìn)行比較,更改狀態(tài)標(biāo)志來允許進(jìn)行條件執(zhí)行。它進(jìn)行一次減法,但不存儲結(jié)果,而是正確地更改標(biāo)志位。標(biāo)志位表示的是操作數(shù) 1 與操作數(shù) 2 比較的結(jié)果(其值可能為大于、小于、相等)。如果操作數(shù) 1 大于操作數(shù) 2,則此后的有 GT 后綴的
指令將可以執(zhí)行。
顯然,CMP 不需要顯式地指定 S 后綴來更改狀態(tài)標(biāo)志。
示例代碼 45-18 cmp 示例
1 cmp r1,#10 ;比較r1和立即數(shù)10是否相等
2 beq loop
通過上面的例子可以看出,CMP 指令與 SUBS 指令的區(qū)別在于 CMP 指令不保存運(yùn)算結(jié)果,在進(jìn)行兩個(gè)數(shù)據(jù)大小判斷時(shí),常用 CMP 指令及相應(yīng)的條件碼來進(jìn)行操作。
CMN 指令
CMN(Compare Negative)指令使用寄存器 Rn 的值減去 shifter_operand 的負(fù)數(shù)值,根據(jù)操作的結(jié)果更新 CPSR 中相應(yīng)的條件標(biāo)志位,以便后面的指令根據(jù)相應(yīng)的條件標(biāo)志來判斷是否執(zhí)行。
指令的語法格式:
CMN{<c>} <Rn>,<shifter_operand>
{<c>}:為指令執(zhí)行的條件碼。當(dāng)忽略<c>時(shí),指令為無條件執(zhí)行。
<Rn>:第一個(gè)操作數(shù)。
<shifter_operand>:比較的數(shù)
指令舉例:
CMN 指令將寄存器 Rn 中的值加上 shifter_operand 表示的數(shù)值,根據(jù)加法的結(jié)果設(shè)置 CPSR 中相應(yīng)的條件標(biāo)志位。寄存器 Rn 中的值加上 shifter_operand 的操作結(jié)果對 CPSR 中條件標(biāo)志位的影響,與寄存器Rn 中的值減去 shifter_operand 的操作結(jié)果的相反數(shù)對 CPSR 中條件標(biāo)志位的影響有細(xì)微差別。
下面的指令使 R0 值加 1,判斷 R0 是否為 1 的補(bǔ)碼,若是,則 Z 置位。
示例代碼 45-19 cmn 示例
1 cmn r0,#1
TST 測試指令
TST(Test)測試指令用于將一個(gè)寄存器的值和一個(gè)值進(jìn)行比較。條件標(biāo)志位根據(jù)兩個(gè)操作數(shù)做“邏輯與”后的結(jié)果設(shè)置。
TST{<c>} <Rn>,<shifter_operand>
{<c>}:為指令執(zhí)行的條件碼。當(dāng)忽略< c >時(shí),指令為無條件執(zhí)行。
<Rn>:第一個(gè)操作數(shù)。
<shifter_operand>:比較的數(shù)
指令舉例:
下面的指令測試在 R0 中是否設(shè)置了位 0。
示例代碼 45-20 tst 示例
1 tst r7,#0x4 ;測試r7寄存器的值第2位是否置位
2 addeq r6,r7 ;如果置位執(zhí)行r6+r7
3 addne r8,r7 ;如果沒有置位執(zhí)行 r8+r7
TST 指令類似于 CMP 指令,不產(chǎn)生放置到目的寄存器中的結(jié)果。而是在給出的兩個(gè)操作數(shù)上進(jìn)行操作并把結(jié)果反映到狀態(tài)標(biāo)志上。使用 TST 指令來檢查是否設(shè)置了特定的位。操作數(shù) 1 是要測試的數(shù)據(jù)字而操作數(shù) 2 是一個(gè)位掩碼。經(jīng)過測試后,如果匹配則設(shè)置 Z 標(biāo)志,否則清除它。與 CMP 指令一樣,該指令不需要指定 S 后綴。
TEQ 指令
TEQ(Test Equivalence)指令用于將一個(gè)寄存器的值和一個(gè)算術(shù)值做比較。條件標(biāo)志位根據(jù)兩個(gè)操作數(shù)做“邏輯異或”后的結(jié)果設(shè)置。以便后面的指令根據(jù)相應(yīng)的條件標(biāo)志來判斷是否執(zhí)行。
指令的語法格式:
TEQ{<c>} <Rn>,<shifter_operand>
{<c>}:為指令執(zhí)行的條件碼。當(dāng)忽略< c >時(shí),指令為無條件執(zhí)行。
<Rn>:第一個(gè)操作數(shù)。
<shifter_operand>:比較的數(shù)。
指令舉例:
示例代碼 45-21 teq 示例
1 teq r0, r1 ;r0與r1是否相等
2 addeq r0, r0, #1 ;若 r0==r1, eq 為真,則 r1=r1+1
乘法指令
ARM 乘法指令完成兩個(gè)數(shù)據(jù)的乘法。兩個(gè) 32 位二進(jìn)制數(shù)相乘的結(jié)果是 64 位的積。在有些 ARM 的處理器版本中,將乘積的結(jié)果保存到兩個(gè)獨(dú)立的寄存器中。另外一些版本只將最低有效 32 位存放到一個(gè)寄存器中。無論是哪種版本的處理器,都有乘—累加的變型指令,將乘積連續(xù)累加得到總和。而且有符號數(shù)和無符號數(shù)都能使用。對于有符號數(shù)和無符號數(shù),結(jié)果的最低有效位是一樣的。因此,對于只保留 32 位結(jié)果的乘法指令,不需要區(qū)分有符號數(shù)和無符號數(shù)這兩種情況。
如表所示為各種形式乘法指令的功能。

說明:
1) “RdHi:RdLo”是由 RdHi(最高有效 32 位)和 RdLo(最低有效 32 位)連接形成的 64 位
數(shù),“[31:0]”只選取結(jié)果的最低有效 32 位。
2) 簡單的賦值由“:=”表示。
3) 累加(將右邊加到左邊)是由“+=”表示。
4) 各個(gè)乘法指令中的位 S(參考下文具體指令的語法格式)控制條件碼的設(shè)置會產(chǎn)生以下結(jié)果。
1) 對于產(chǎn)生 32 位結(jié)果的指令形式,將標(biāo)志位 N 設(shè)置為 Rd 的第 31 位的值;對于產(chǎn)生長結(jié)果的
指令形式,將其設(shè)置為 RdHi 的第 31 位的值。
2) 對于產(chǎn)生 32 位結(jié)果的指令形式,如果 Rd 等于零,則標(biāo)志位 Z 置位;對于產(chǎn)生長結(jié)果的指令形式,RdHi 和 RdLo 同時(shí)為零時(shí),標(biāo)志位 Z 置位。
3) 將標(biāo)志位 C 設(shè)置成無意義的值。
4) 標(biāo)志位 V 不變。
?MUL 指令
MUL(Multiply)32 位乘法指令將 Rm 和 Rs 中的值相乘,結(jié)果的最低 32 位保存到 Rd 中。
指令的語法格式:?
MUL{<c>}{S} <Rd>,<Rm>,<Rs>
{<c>}:為指令執(zhí)行的條件碼。當(dāng)忽略< c >時(shí),指令為無條件執(zhí)行。
{S}:決定指令的操作是否影響 CPSR。
<Rd>:目的寄存器。
<Rn>:第一個(gè)操作數(shù)。
<Rs>:要與 Rn 相乘的數(shù)
指令舉例:
示例代碼 45-22 mul 示例
1 mul r1, r2, r3 ;r1 = r2 × r3
2 muls r0, r3, r7 ;r0 = r3 × r7
MLA 指令
MLA(Multiply Accumulate)32 位乘—累加指令將 Rm 和 Rs 中的值相乘,再將乘積加上第 3 個(gè)操作數(shù),結(jié)果的最低 32 位保存到 Rd 中。
指令的語法格式:
MLA{<c>}{S} <Rd>,<Rm>,<Rs>,<Rn>
{<c>}:為指令執(zhí)行的條件碼。當(dāng)忽略< c >時(shí),指令為無條件執(zhí)行。
{S}:決定指令的操作是否影響 CPSR。
<Rd>:目的寄存器。
<Rm>:第一個(gè)乘數(shù)。
<Rs>:第二個(gè)乘數(shù)。
<Rn>:與 Rm 與 Rs 的積相加。
指令舉例:
下面的指令完成 R1 = R2×R3+R0 的操作。
示例代碼 45-23 mla 示例
1 mla r1, r2, r3, r0
UMULL 指令
UMULL(Unsigned Multiply Long)為 64 位無符號乘法指令。它將 Rm 和 Rs 中的值做無符號數(shù)相乘,結(jié)果的低 32 位保存到 RdLo 中,高 32 位保存到 RdHi 中。
指令的語法格式:
UMULL{<c>}{S} <RdLo>,<RdHi>,<Rm>,<Rs>
{<c>}:為指令執(zhí)行的條件碼。當(dāng)忽略<c>時(shí),指令為無條件執(zhí)行。
{S}:決定指令的操作是否影響 CPSR。
<Rm>:第一個(gè)乘數(shù)。
<Rs>:第二個(gè)乘數(shù)
< RdHi >:Rm 與 Rs 的積的高 32 位
< RdLo >:Rm 與 Rs 的積的低 32 位
指令舉例:
示例代碼 45-24 umull 示例
1 umull r0, r1, r5, r8 ; (R1,R0) = R5 × R8
UMLAL 指令
UMLAL(Unsigned Multiply Accumulate Long)為 64 位無符號長乘—累加指令。指令將 Rm 和 Rs 中的值做無符號數(shù)相乘,64 位乘積與 RdHi、RdLo 相加,結(jié)果的低 32 位保存到 RdLo 中,高 32 位保存到RdHi 中。
指令的語法格式:
UMALL{<c>}{S} <RdLo>,<RdHi>,<Rm>,<Rs>
{<c>}:為指令執(zhí)行的條件碼。當(dāng)忽略<c>時(shí),指令為無條件執(zhí)行。
{S}:決定指令的操作是否影響 CPSR。
<Rm>:第一個(gè)乘數(shù)。
<Rs>:第二個(gè)乘數(shù)
<RdHi>:與 Rm 與 Rs 的積的高 32 位相加
<RdLo>:與 Rm 與 Rs 的積的低 32 位相加
指令舉例:
示例代碼 45-25 umlal 示例
1 umlal r0, r1, r5,r8 ;(r1,r0) = r5 × r8+(r1,r0)
SMULL 指令
SMULL(Signed Multiply Long)為 64 位有符號長乘法指令。指令將 Rm 和 Rs 中的值做有符號數(shù)相乘,結(jié)果的低 32 位保存到 RdLo 中,高 32 位保存到 RdHi 中。
指令的語法格式:
SMULL{<c>}{S} <RdLo>,<RdHi>,<Rm>,<Rs>
{<c>}:為指令執(zhí)行的條件碼。當(dāng)忽略<c>時(shí),指令為無條件執(zhí)行。
{S}:決定指令的操作是否影響 CPSR。
<Rm>:第一個(gè)乘數(shù)。
<Rs>:第二個(gè)乘數(shù)
< RdHi >:Rm 與 Rs 的積的高 32 位
< RdLo >:Rm 與 Rs 的積的低 32 位
指令舉例:
示例代碼 45-26 smull 示例
1 smull r2, r3, r7,r6 ;(r3,r2) = r7 × r6
SMLAL 指令
SMLAL(Signed Multiply Accumulate Long)為 64 位有符號長乘—累加指令。指令將 Rm 和 Rs 中的值做有符號數(shù)相乘,64 位乘積與 RdHi、RdLo 相加,結(jié)果的低 32 位保存到 RdLo 中,高 32 位保存到 RdHi
中。
指令的語法格式:
SMLAL{<c>}{S} <RdLo>,<RdHi>,<Rm>,<Rs>
{<c>}:為指令執(zhí)行的條件碼。當(dāng)忽略<c>時(shí),指令為無條件執(zhí)行。
{S}:決定指令的操作是否影響 CPSR。
<Rm>:第一個(gè)乘數(shù)。
<Rs>:第二個(gè)乘數(shù)
<RdHi>:與 Rm 與 Rs 的積的高 32 位相加
<RdLo>:與 Rm 與 Rs 的積的低 32 位相加
指令舉例:
示例代碼 45-27 smlal 示例
1 smlal r2, r3, r7,r6 ;(r3,r2)=r7×r6+(r3,r2)
Load/Store 指令
Load/Store 內(nèi)存訪問指令在 ARM 寄存器和存儲器之間傳送數(shù)據(jù)。ARM 指令中有 3 種基本的數(shù)據(jù)傳送指令。
? 單寄存器 Load/Store 指令
這些指令在 ARM 寄存器和存儲器之間提供更靈活的單數(shù)據(jù)項(xiàng)傳送方式。數(shù)據(jù)項(xiàng)可以是字節(jié)、
16 位半字或 32 位字。
? 多寄存器 Load/Store 內(nèi)存訪問指令
這些指令的靈活性比單寄存器傳送指令差,但可以使大量的數(shù)據(jù)更有效地傳送。它們用于進(jìn)程的進(jìn)入和退出、保存和恢復(fù)工作寄存器及復(fù)制存儲器中的一塊數(shù)據(jù)。
? 單寄存器交換指令
這些指令允許寄存器和存儲器中的數(shù)值進(jìn)行交換,在一條指令中有效地完成 Load/Store 操作。它們在用戶級編程中很少用到。它的主要用途是在多處理器系統(tǒng)中實(shí)現(xiàn)信號量(Semaphores)的操作,以保證不會同時(shí)訪問公用的數(shù)據(jù)結(jié)構(gòu)。
1、 單寄存器的 Load/Store 指令
LDR 指令
LDR 指令用于從內(nèi)存中將一個(gè) 32 位的字讀取到目標(biāo)寄存器。
指令的語法格式:
LDR{<c>} <Rd>,<addr_mode>
指令舉例:
示例代碼 45-28 ldr 示例
1 ldr r1,[r0,#0x12] ;將r0+12地址處的數(shù)據(jù)讀出,保存到r1中(r0的值不變)
2 ldr r1,[r0] ;將r0地址處的數(shù)據(jù)讀出,保存到r1中(零偏移)
3 ldr r1,[r0,r2] ;將r0+r2地址的數(shù)據(jù)讀出,保存到r1中(r0的值不變)
4 ldr r1,[r0,r2,lsl #2] ;將r0+r2×4地址處的數(shù)據(jù)讀出,保存到r1中(r0、r2的值不變)
5 ldr pc,[pc, #0x18] ;將程序跳轉(zhuǎn)到pc+0x18位置處
6 ldr rd,label ;label為程序標(biāo)號,label必須是當(dāng)前指令的-4~4kb范圍內(nèi)
7 ldr rd,[rn],#0x04 ;rn 的值用做傳輸數(shù)據(jù)的存儲地址。在數(shù)據(jù)傳送后,將偏移量 0x04 與 rn
相加,結(jié)果寫回到 rn 中。rn 不允許是 r15
STR 指令
STR 指令用于將一個(gè) 32 位的字?jǐn)?shù)據(jù)寫入到指令中指定的內(nèi)存單元。
指令的語法格式:
STR{<c>} <Rd>,<addr_mode>
指令舉例:
示例代碼 45-29 str 數(shù)據(jù)回寫
1 ldr r0, =0xE0200000
2 ldr r1, =0x00002222
3 str r1, [r0, #0x20]
LDRB 指令
LDRB 指令根據(jù) addr_mode 所確定的地址模式將 1 個(gè)字節(jié)(8bit)讀取到指令中的目標(biāo)寄存器 Rd。
指令的語法格式:
LDRB{<c>} <Rd>, <addr_mode>
STRB 指令
STRB 指令從寄存器中取出指定的 1 個(gè)字節(jié)(8bit)放入寄存器的低 8 位,并將寄存器的高位補(bǔ)0。
指令的語法格式:
STRB{<c>} <Rd>,<addr_mode>
) LDRH 指令
LDRH 指令用于從內(nèi)存中將一個(gè) 16 位的半字讀取到目標(biāo)寄存器。
如果指令的內(nèi)存地址不是半字節(jié)對齊的,指令的執(zhí)行結(jié)果不可預(yù)知。
指令的語法格式:
LDRH{<c>} <Rd>,<addr_mode>
STRH 指令
STRH 指令從寄存器中取出指定的 16 位半字放入寄存器的低 16 位,并將寄存器的高位補(bǔ)0。
指令的語法格式:
STRH{<c>} <Rd>,<addr_mode>
多寄存器的 Load/Store 內(nèi)存訪問指令
多寄存器的 Load/Store 內(nèi)存訪問指令也叫批量加載/存儲指令,它可以實(shí)現(xiàn)在一組寄存器和一塊連續(xù)的內(nèi)存單元之間傳送數(shù)據(jù)。LDM 用于加載多個(gè)寄存器,STM 用于存儲多個(gè)寄存器。多寄存器的 Load/Store內(nèi)存訪問指令允許一條指令傳送 16 個(gè)寄存器的任何子集或所有寄存器。多寄存器的 Load/Store 內(nèi)存訪問指令主要用于現(xiàn)場保護(hù)、數(shù)據(jù)復(fù)制和參數(shù)傳遞等。如表所示列出了多寄存器的 Load/Store 內(nèi)存訪問指令。
表 45.3.3.2 多寄存器操作指令

LDM 指令
LDM 指令將數(shù)據(jù)從連續(xù)的內(nèi)存單元中讀取到指令中指定的寄存器列表中的各寄存器中。當(dāng) PC 包含在LDM 指令的寄存器列表中時(shí),指令從內(nèi)存中讀取的字?jǐn)?shù)據(jù)將被作為目標(biāo)地址值,指令執(zhí)行后程序?qū)哪繕?biāo)地址處開始執(zhí)行,從而實(shí)現(xiàn)了指令的跳轉(zhuǎn)。
指令的語法格式:
LDM{<c>}<addressing_mode> <Rn>{!}, <registers>
STM 指令
STM 指令將指令中寄存器列表中的各寄存器數(shù)值寫入到連續(xù)的內(nèi)存單元中。主要用于塊數(shù)據(jù)的寫入、數(shù)據(jù)棧操作及進(jìn)入子程序時(shí)保存相關(guān)寄存器的操作。
指令的語法格式:
STM{<c>}<addressing_mode> <Rn>{!}, <registers>
批量數(shù)據(jù)傳送指令舉例
LDM/STM 批量加載/存儲指令可以實(shí)現(xiàn)在一組寄存器和一塊連續(xù)的內(nèi)存單元之間傳輸數(shù)據(jù)。LDM 為加載多個(gè)寄存器,STM 為存儲多個(gè)寄存器。允許一條指令傳送 16 個(gè)寄存器的任何子集或所有寄存器。指令格式如下:
?LDM{c}<模式> Rn{!},regist{?}
STM{c}<模式> Rn{!},regist{?}
LDM/STM 的主要用途有現(xiàn)場保護(hù)、數(shù)據(jù)復(fù)制和參數(shù)傳遞等。其模式有 8 種,其中前面 4 種用于數(shù)據(jù)塊的傳輸,后面 4 種是堆棧操作,如下所示。
(1) IA:每次傳送后地址加 4。
(2) IB:每次傳送前地址加 4。
(3) DA:每次傳送后地址減 4。
(4) DB:每次傳送前地址減 4。
(5) FD:滿遞減堆棧。
(6) ED:空遞增堆棧。
(7) FA:滿遞增堆棧。
(8) EA:空遞增堆棧。
其中,寄存器 Rn 為基址寄存器,裝有傳送數(shù)據(jù)的初始地址,Rn 不允許為 R15;后綴“!”表示最后的地址寫回到 Rn 中;寄存器列表 reglist 可包含多于一個(gè)寄存器或寄存器范圍,使用“,”分開,如{R1,R2,R6~R9},寄存器排列由小到大排列;“?”后綴不允許在用戶模式下使用,只能在系統(tǒng)模式下使用。若在LDM 指令用寄存器列表中包含有 PC 時(shí)使用,那么除了正常的多寄存器傳送外,將 SPSR 復(fù)制到 CPSR 中,這可用于異常處理返回;使用“?”后綴進(jìn)行數(shù)據(jù)傳送且寄存器列表不包含 PC 時(shí),加載/存儲的是用戶模式寄存器,而不是當(dāng)前模式寄存器。
示例代碼 45-30 批量數(shù)據(jù)傳送指令
1 LDMIA R0!,{R3~R9} ;加載R0指向的地址上的多字?jǐn)?shù)據(jù),保存到R3~R9中,R0值更新
2 STMIA R1!,{R3~R9} ;將R3~R9的數(shù)據(jù)存儲到R1指向的地址上,R1值更新
3 STMFD SP!,{R0~R7,LR} ;現(xiàn)場保存,將R0~R7、LR入棧
4 LDMFD SP!,{R0~R7,PC}? ;恢復(fù)現(xiàn)場,異常處理返回
在進(jìn)行數(shù)據(jù)復(fù)制時(shí),先設(shè)置好源數(shù)據(jù)指針,然后使用塊復(fù)制尋址指令 LDMIA/STMIA、LDMIB/STMIB、LDMDA/STMDA、LDMDB/STMDB 進(jìn)行讀取和存儲。而進(jìn)行堆棧操作時(shí),則要先設(shè)置堆棧指針,一般使用 SP 然后使用堆棧尋址指令 STMFD/LDMFD、STMED/LDMED、STMEA/LDMEA 實(shí)現(xiàn)堆棧操作。數(shù)據(jù)是存儲在基址寄存器的地址之上還是之下,地址是存儲第一個(gè)值之前還是之后、增加還是減少,如表所示。

使用 LDM/STM 進(jìn)行數(shù)據(jù)復(fù)制。
示例代碼 45-31 數(shù)據(jù)復(fù)制
1 LDR R0,=SrcData ;設(shè)置源數(shù)據(jù)地址
2 LDR R1,=DstData ;設(shè)置目標(biāo)地址
3 LDMIA R0,{R2~R9} ;加載8字?jǐn)?shù)據(jù)到寄存器R2~R9
4 STMIA R1,{R2~R9} ;存儲寄存器 R2~R9 到目標(biāo)地址
使用 LDM/STM 進(jìn)行現(xiàn)場寄存器保護(hù),常在子程序或異常處理使用。
示例代碼 45-32 保護(hù)現(xiàn)場
1 SENDBYTE:
2 STMFD SP!,{R0~R7,LR} ;寄存器壓棧保護(hù)
3 ...
4 BL DELAY ;調(diào)用DELAY子程序
5 ...
6 LDMFD SP!,{R0~R7,PC} ;恢復(fù)寄存器,并返回
單數(shù)據(jù)交換指令
交換指令是 Load/Store 指令的一種特例,它把一個(gè)寄存器單元的內(nèi)容與寄存器內(nèi)容交換。交換指令是一個(gè)原子操作(Atomic Operation),也就是說,在連續(xù)的總線操作中讀/寫一個(gè)存儲單元,在操作期間阻止其他任何指令對該存儲單元的讀/寫。交換指令如表所示。

SWP 字交換指令
SWP 指令用于將內(nèi)存中的一個(gè)字單元和一個(gè)指定寄存器的值相交換。操作過程如下:假設(shè)內(nèi)存單元地址存放在寄存器<Rn>中,指令將<Rn>中的數(shù)據(jù)讀取到目的寄存器 Rd 中,同時(shí)將另一個(gè)寄存器<Rm>的內(nèi)容寫入到該內(nèi)存單元中。
當(dāng)<Rd>和<Rm>為同一個(gè)寄存器時(shí),指令交換該寄存器和內(nèi)存單元的內(nèi)容。
指令的語法格式:
SWP{<c>} <Rd>,<Rm>,[<Rn>]
字節(jié)單元和一個(gè)指定寄存器的低 8 位值相交換,操作過程如下:假設(shè)內(nèi)存單元地址存放在寄存器<Rn>中,指令將<Rn>中的數(shù)據(jù)讀取到目的寄存器 Rd 中,寄存器 Rd 的高 24 位設(shè)為 0,同時(shí)將另一個(gè)寄存器<Rm>的低 8 位內(nèi)容寫入到該內(nèi)存字節(jié)單元中。當(dāng)<Rd>和<Rm>為同一個(gè)寄存器時(shí),指令交換該寄存器低 8 位內(nèi)容和內(nèi)存字節(jié)單元的內(nèi)容。
指令的語法格式:
SWP{<c>}B <Rd>,<Rm>,[<Rn>]
指令舉例:
示例代碼 45-33 swp 指令舉例
1 SWP R1,R1,[R0] ;將R1的內(nèi)容與R0指向的存儲單元內(nèi)容進(jìn)行交換
2 SWPB R1,R2,[R0] ;將 R0 指向的存儲單元內(nèi)容讀取一字節(jié)數(shù)據(jù)到 R1 中(高 24 位清零), 并將 R2 的內(nèi)容 寫入到該內(nèi)存單元中(最低字節(jié)有效),使用 SWP 指令可以方便地進(jìn)行信號量操作。
跳轉(zhuǎn)指令
跳轉(zhuǎn)(B)和跳轉(zhuǎn)連接(BL)指令是改變指令執(zhí)行順序的標(biāo)準(zhǔn)方式。ARM 一般按照字地址順序執(zhí)行指令,需要時(shí)使用條件執(zhí)行跳過某段指令。只要程序必須偏離順序執(zhí)行,就要使用控制流指令來修改程序計(jì)數(shù)器。盡管在特定情況下還有其他幾種方式實(shí)現(xiàn)這個(gè)目的,但轉(zhuǎn)移和轉(zhuǎn)移連接指令是標(biāo)準(zhǔn)的方式。跳轉(zhuǎn)指令改變程序的執(zhí)行流程或者調(diào)用子程序。這種指令使得一個(gè)程序可以使用子程序、if-then-else 結(jié)構(gòu)及循環(huán)。執(zhí)行流程的改變迫使程序計(jì)數(shù)器(PC)指向一個(gè)新的地址。

另一種實(shí)現(xiàn)指令跳轉(zhuǎn)的方式是通過直接向 PC 寄存器中寫入目標(biāo)地址值,實(shí)現(xiàn)在 4GB 地址空間中任意跳轉(zhuǎn),這種跳轉(zhuǎn)指令又稱為長跳轉(zhuǎn)。如果在長跳轉(zhuǎn)指令之前使用“MOV LR”或“MOV PC”等指令,可以保存將來返回的地址值,也就實(shí)現(xiàn)了在 4GB 的地址空間中的子程序調(diào)用。
1、 跳轉(zhuǎn)指令 B 及帶連接的跳轉(zhuǎn)指令 BL
跳轉(zhuǎn)指令 B 使程序跳轉(zhuǎn)到指定的地址執(zhí)行程序。帶連接的跳轉(zhuǎn)指令 BL 將下一條指令的地址復(fù)制到R14(即返回地址連接寄存器 LR)寄存器中,然后跳轉(zhuǎn)到指定地址運(yùn)行程序。需要注意的是,這兩條指令和目標(biāo)地址處的指令都要屬于 ARM 指令集。兩條指令都可以根據(jù) CPSR 中的條件標(biāo)志位的值決定指令是否執(zhí)行。
指令的語法格式:
B{L}{<c>} <target_address>
BL 指令用于實(shí)現(xiàn)子程序調(diào)用。子程序的返回可以通過將 LR 寄存器的值復(fù)制到 PC 寄存器來實(shí)現(xiàn)。下面 3 種指令可以實(shí)現(xiàn)子程序返回。
1) BX R14(如果體系結(jié)構(gòu)支持 BX 指令)。
2) MOV PC,R14。
3) 當(dāng)子程序在入口處使用了壓棧指令:
STMFD R13!,{<registers>,R14}
可以使用指令:
LDMFD R13!,{<registers>,PC}?
將子程序返回地址放入 PC 中。
ARM 匯編器通過以下步驟計(jì)算指令編碼中的 signed_immed_24。
1) 將 PC 寄存器的值作為本跳轉(zhuǎn)指令的基地址值。
2) 從跳轉(zhuǎn)的目標(biāo)地址中減去上面所說的跳轉(zhuǎn)的基地址,生成字節(jié)偏移量。由于 ARM 指令是字對齊的,該字節(jié)偏移量為 4 的倍數(shù)。
3) 當(dāng)上面生成的字節(jié)偏移量超過?33 554 432~+33 554 430 時(shí),不同的匯編器使用不同的代碼產(chǎn)生策略。否則,將指令編碼字中的 signed_immed_24 設(shè)置成上述字節(jié)偏移量的 bits[25:2]。
程序舉例:
程序跳轉(zhuǎn)到 LABLE 標(biāo)號處。
示例代碼 45-34 跳轉(zhuǎn)到標(biāo)號處
1 b lable
2 add r1,r2,#4
3 add r3,r2,#8
4 sub r3,r3,r1
5 lable:
6 sub r1,r2,#8
跳轉(zhuǎn)到絕對地址
示例代碼 45-35 跳轉(zhuǎn)到絕對地址?
1 b 0x1234
跳轉(zhuǎn)到子程序 func 處執(zhí)行,同時(shí)將當(dāng)前 PC 值保存到 LR 中。
示例代碼 45-36 子程序跳轉(zhuǎn)?
1 bl func
通過跳轉(zhuǎn)指令建立一個(gè)無限循環(huán)。
示例代碼 45-37 無限循環(huán)?
1 loop:
2 add r1,r2,#4
3 add r3,r2,#8
4 sub r3,r3,r1
5 b loop
通過使用跳轉(zhuǎn)使程序體循環(huán) 10 次。
示例代碼 45-38 有限循環(huán)?
1 mov r0,#10
2 loop:
3 subs r0,#1
4 bne loop
條件子程序調(diào)用示例。
示例代碼 45-39 條件調(diào)用?
1 cmp r0,#5 ;如果r0<5
2 bllt sub1 ;則調(diào)用
3 blge sub2 ;否則調(diào)用 sub2
帶狀態(tài)切換的跳轉(zhuǎn)指令 BX
帶狀態(tài)切換的跳轉(zhuǎn)指令(BX)使程序跳轉(zhuǎn)到指令中指定的參數(shù) Rm 指定的地址執(zhí)行程序,Rm 的第 0位復(fù)制到 CPSR 中 T 位,bit[31∶1]移入 PC。若 Rm 的 bit[0]為 1,則跳轉(zhuǎn)時(shí)自動將 CPSR 中的標(biāo)志位 T 置位,即把目標(biāo)地址的代碼解釋為 Thumb 代碼;若 Rm 的位 bit[0]為 0,則跳轉(zhuǎn)時(shí)自動將 CPSR 中的標(biāo)志位T 復(fù)位,即把目標(biāo)地址代碼解釋為 ARM 代碼。
指令的語法格式:
BX{<c>} <Rm>
當(dāng) Rm[1∶0]=0b10 時(shí),指令的執(zhí)行結(jié)果不可預(yù)知。因?yàn)樵?ARM 狀態(tài)下,指令是 4 字節(jié)對齊的。PC 可以作為 Rm 寄存器使用,但這種用法不推薦使用。當(dāng) PC 作為<Rm>使用時(shí),指令“BX PC”將程序跳轉(zhuǎn)到當(dāng)前指令下面第二條指令處執(zhí)行。雖然這樣跳轉(zhuǎn)可以實(shí)現(xiàn),但最好使用下面的指令完成這種跳轉(zhuǎn)。
MOV PC, PC或ADD PC, PC, #0
指令舉例:
轉(zhuǎn)移到 R0 中的地址,如果 R0[0]=1,則進(jìn)入 Thumb 狀態(tài)。
示例代碼 45-40 bx 指令示例?
1 bx r0
帶連接和狀態(tài)切換的連接跳轉(zhuǎn)指令 BLX 帶連接和狀態(tài)切換的跳轉(zhuǎn)指令(Branch with Link Exchange,BLX)使用標(biāo)號,用于使程序跳轉(zhuǎn)到 Thumb狀態(tài)或從 Thumb 狀態(tài)返回。該指令為無條件執(zhí)行指令,并用分支寄存器的最低位來更新 CPSR 中的 T 位,將返回地址寫入到連接寄存器 LR 中。
語法格式:
BLX <target_add>
其中,<target_add>為指令的跳轉(zhuǎn)目標(biāo)地址。該地址根據(jù)以下規(guī)則計(jì)算。
1) 將指令中指定的 24 位偏移量進(jìn)行符號擴(kuò)展,形成 32 位立即數(shù)。
2) 將結(jié)果左移兩位。
3) 位 H(bit[24])加到結(jié)果地址的第一位(bit[1])。
4) 將結(jié)果累加進(jìn)程序計(jì)數(shù)器(PC)中。
計(jì)算偏移量的工作一般由 ARM 匯編器來完成。這種形式的跳轉(zhuǎn)指令只能實(shí)現(xiàn)?32~32MB 空間的跳轉(zhuǎn)。左移兩位形成字偏移量,然后將其累加進(jìn)程序計(jì)數(shù)器(PC)中。這時(shí),程序計(jì)數(shù)器的內(nèi)容為 BX 指令地址加 8 字節(jié)。位 H(bit[24])也加到結(jié)果地址的第一位(bit[1]),使目標(biāo)地址成為半字地址,以執(zhí)行接下來的 Thumb 指令。計(jì)算偏移量的工作一般由 ARM 匯編器來完成。這種形式的跳轉(zhuǎn)指令只能實(shí)現(xiàn)?32~32MB 空間的跳轉(zhuǎn)。
指令舉例:
從 Thumb 狀態(tài)返回到 ARM 狀態(tài),使用 BX 指令。
示例代碼 45-41 Thumb 狀態(tài)返回 ARM 狀態(tài)
1 blx func
狀態(tài)操作指令
ARM 指令集提供了兩條指令,可直接控制程序狀態(tài)寄存器(Program State Register,PSR)。MRS 指令用于把CPSR或SPSR的值傳送到一個(gè)寄存器;MSR與之相反,把一個(gè)寄存器的內(nèi)容傳送到CPSR或SPSR。
這兩條指令相結(jié)合,可用于對 CPSR 和 SPSR 進(jìn)行讀/寫操作。程序狀態(tài)寄存器指令如表所示。
表 45.3.5.1 狀態(tài)操作指令

在指令語法中可看到一個(gè)稱為 fields 的項(xiàng),它可以是控制(C)、擴(kuò)展(X)、狀態(tài)(S)及標(biāo)志(F)的組合。
1、 MRS
MRS 指令用于將程序狀態(tài)寄存器的內(nèi)容傳送到通用寄存器中。
在 ARM 處理器中,只有 MRS 指令可以將狀態(tài)寄存器 CPSR 或 SPSR 讀出到通用寄存器中。
指令的語法格式:
MRS{c} Rd, PSR
其中,Rd 為目標(biāo)寄存器,Rd 不允許為程序計(jì)數(shù)器(PC)。PSR 為 CPSR 或 SPSR。
指令舉例:
示例代碼 45-42 讀取 CPSR 與 SPSR?
1 mrs r1,cpsr ;將cpsr狀態(tài)寄存器讀取,保存到r1中
2 mrs r2,spsr ;將 spsr 狀態(tài)寄存器讀取,保存到 r1 中
MRS 指令讀取 CPSR,可用來判斷 ALU 的狀態(tài)標(biāo)志及 IRQ/FIQ 中斷是否允許等;在異常處理程序中,讀 SPSR 可指定進(jìn)入異常前的處理器狀態(tài)等。MRS 與 MSR 配合使用,實(shí)現(xiàn) CPSR 或 SPSR 寄存器的讀—修改—寫操作,可用來進(jìn)行處理器模式切換,允許/禁止 IRQ/FIQ 中斷等設(shè)置。另外,進(jìn)程切換或允許異常中斷嵌套時(shí),也需要使用 MRS 指令讀取 SPSR 狀態(tài)值并保存起來。?
MSR
在 ARM 處理器中,只有 MSR 指令可以直接設(shè)置狀態(tài)寄存器 CPSR 或 SPSR。
指令的語法格式:
MSR{c} PSR_fields, #immed_8r
MSR{c} PSR_fields, Rm
其中,PSR 是指 CPSR 或 SPSR。<fields>設(shè)置狀態(tài)寄存器中需要操作的位。狀態(tài)寄存器的 32 位可以分為 4 個(gè) 8 位的域(field)。bits[31:24]為條件標(biāo)志位域,用 f 表示;bits[23:16]為狀態(tài)位域,用 s 表示;bits[15:8]為擴(kuò)展位域,用 x 表示;bits[7:0]為控制位域,用 c 表示;immed_8r 為要傳送到狀態(tài)寄存器指定域的立即數(shù),8 位;Rm 為要傳送到狀態(tài)寄存器指定域的數(shù)據(jù)源寄存器。
指令舉例:
示例代碼 45-43 MSR 指令示例
1 msr cpsr,#0xd3 ;cpsr[7:0]=0xd3,切換到管理模式
2 msr cpsr,r3 ;cpsr=r3
注意:只有在特權(quán)模式下才能修改狀態(tài)寄存器。
程序中不能通過 MSR 指令直接修改 CPSR 中的 T 位控制位來實(shí)現(xiàn) ARM 狀態(tài)/Thumb 狀態(tài)的切換,必須使用 BX 指令來完成處理器狀態(tài)的切換(因?yàn)?BX 指令屬轉(zhuǎn)移指令,它會打斷流水線狀態(tài),實(shí)現(xiàn)處理器狀態(tài)的切換)。MRS 與 MSR 配合使用,實(shí)現(xiàn) CPSR 或 SPSR 寄存器的讀—修改—寫操作,可用來進(jìn)行處理器模式切換及允許/禁止 IRQ/FIQ 中斷等設(shè)置。
程序狀態(tài)寄存器指令的應(yīng)用
使能 IRQ 中斷。
示例代碼 45-44 使能 IRQ 中斷
1 enable_irq:
2 mrs r0,cpsr
3 bic r0,r0,#0x80
4 msr cpsr_c,r0
5 mov pc,lr
禁止 IRQ 中斷。
示例代碼 45-45 禁止 IRQ 中斷
1 disable_irq:
2 mrs r0,cpsr
3 orr r0,r0,#0x80
4 msr cpsr,r0
5 mov pc,lr
?
設(shè)置中斷模式堆棧:
示例代碼 45-46 irq 模式堆棧
1 msr cpsr,#0xd2
2 ldr sp,stacksvc
協(xié)處理器指令
ARM 體系結(jié)構(gòu)允許通過增加協(xié)處理器來擴(kuò)展指令集。最常用的協(xié)處理器是用于控制片上功能的系統(tǒng)協(xié)處理器。例如,控制 Cache 和存儲管理單元的 cp15 寄存器。此外,還有用于浮點(diǎn)運(yùn)算的浮點(diǎn) ARM 協(xié)處理器,各生產(chǎn)商還可以根據(jù)需要開發(fā)自己的專用協(xié)處理器。
ARM 協(xié)處理器具有自己專用的寄存器組,它們的狀態(tài)由控制 ARM 狀態(tài)的指令的鏡像指令來控制。程序的控制流指令由 ARM 處理器來處理,所有協(xié)處理器指令只能同數(shù)據(jù)處理和數(shù)據(jù)傳送有關(guān)。按照 RISC 的Load/Store 體系原則,數(shù)據(jù)的處理和傳送指令是被清楚分開的,所以它們有不同的指令格式。ARM 處理器支持 16 個(gè)協(xié)處理器,在程序執(zhí)行過程中,每個(gè)協(xié)處理器忽略 ARM 和其他協(xié)處理器指令。當(dāng)一個(gè)協(xié)處理器硬件不能執(zhí)行屬于它的協(xié)處理器指令時(shí),將產(chǎn)生一個(gè)未定義指令異常中斷,在該異常中斷處理過程中,可以通過軟件仿真該硬件操作。如果一個(gè)系統(tǒng)中不包含向量浮點(diǎn)運(yùn)算器,則可以選擇浮點(diǎn)運(yùn)算軟件包來支持向量浮點(diǎn)運(yùn)算。
ARM 協(xié)處理器可以部分地執(zhí)行一條指令,然后產(chǎn)生中斷。如除法運(yùn)算除數(shù)為 0 和溢出,這樣可以更好地處理運(yùn)行時(shí)產(chǎn)生(run-time-generated)的異常。但是,指令的部分執(zhí)行是由協(xié)處理器完成的,此過程對ARM 來說是透明的。當(dāng) ARM 處理器重新獲得執(zhí)行時(shí),它將從產(chǎn)生異常的指令處開始執(zhí)行。對某一個(gè)協(xié)處理器來說,并不一定用到協(xié)處理器指令中的所有的域。具體協(xié)處理器如何定義和操作完全由協(xié)處理器的制造商自己決定,因此,ARM 協(xié)處理器指令中的協(xié)處理器寄存器的標(biāo)識符及操作助記符也有各種不同的實(shí)現(xiàn)定義。程序員可以通過宏定義這些指令的語法格式。
ARM 協(xié)處理器指令可分為以下 3 類。
? 協(xié)處理器數(shù)據(jù)操作。協(xié)處理器數(shù)據(jù)操作完全是協(xié)處理器內(nèi)部操作,它完成協(xié)處理器寄存器的狀態(tài)改變。如浮點(diǎn)加運(yùn)算,在浮點(diǎn)協(xié)處理器中兩個(gè)寄存器相加,結(jié)果放在第 3 個(gè)寄存器中。這類指令包括 CDP 指令。
? 協(xié)處理器數(shù)據(jù)傳送指令。這類指令從寄存器讀取數(shù)據(jù)裝入?yún)f(xié)處理器寄存器,或?qū)f(xié)處理器寄存器的數(shù)據(jù)裝入存儲器。因?yàn)閰f(xié)處理器可以支持自己的數(shù)據(jù)類型,所以每個(gè)寄存器傳送的字?jǐn)?shù)與協(xié)處理器有關(guān)。ARM 處理器產(chǎn)生存儲器地址,但傳送的字節(jié)由協(xié)處理器控制。這類指令包括LDC 指令和 STC 指令。
? 協(xié)處理器寄存器傳送指令。在某些情況下,需要 ARM 處理器和協(xié)處理器之間傳送數(shù)據(jù)。如一個(gè)浮點(diǎn)運(yùn)算協(xié)處理器,F(xiàn)IX 指令從協(xié)處理器寄存器取得浮點(diǎn)數(shù)據(jù),將它轉(zhuǎn)換為整數(shù),并將整數(shù)傳送到 ARM 寄存器中。經(jīng)常需要用浮點(diǎn)比較產(chǎn)生的結(jié)果來影響控制流,因此,比較結(jié)果必須傳送到 ARM 的 CPSR 中。這類協(xié)處理器寄存器傳送指令包括 MCR 和 MRC。
如表所示列出了所有協(xié)處理器處理指令。
表 45.3.6.1 協(xié)處理器操作指令

下面簡單介紹一下比較常用的 MCR 及 MRC 命令的用法
ARM 寄存器到協(xié)處理器寄存器的數(shù)據(jù)傳送指令 MCR
指令的語法格式
MCR<c> <coproc>, <opc1>, <Rt>, <CRn>, <CRm>{, <opc2>}
<c>:為指令執(zhí)行的條件碼。當(dāng)忽略< c >時(shí),指令為無條件執(zhí)行。
<coproc>:協(xié)處理器名稱,范圍 p0-p15;
<opc1>:協(xié)處理器操作碼,范圍 0-15;
<Rt>:源寄存器,將 Rt 寄存器寫入?yún)f(xié)處理器;
<CRn>:協(xié)處理器的目標(biāo)寄存器;
<CRm>:協(xié)處理器中附加的目標(biāo)寄存器或者源操作寄存器,如果不需要附加信息就設(shè)置為 C0,否則
結(jié)果不可預(yù)測;
<opc2>:可選的協(xié)處理器特定操作碼,當(dāng)不需要時(shí)置 0。
協(xié)處理器寄存器到 ARM 寄存器的數(shù)據(jù)傳送指令 MRC
指令的語法格式
MRC<c> <coproc>, <opc1>, <Rt>, <CRn>, <CRm>{, <opc2>}
<c>:為指令執(zhí)行的條件碼。當(dāng)忽略< c >時(shí),指令為無條件執(zhí)行。
<coproc>:協(xié)處理器名稱,范圍 p0-p15;
<opc1>:協(xié)處理器操作碼,范圍 0-15;
<Rt>:源寄存器,將協(xié)處理器的內(nèi)容讀取到 Rt 寄存器;
<CRn>:協(xié)處理器的目標(biāo)寄存器;
<CRm>:協(xié)處理器中附加的目標(biāo)寄存器或者源操作寄存器,如果不需要附加信息就設(shè)置為 C0,否則
結(jié)果不可預(yù)測;
<opc2>:可選的協(xié)處理器特定操作碼,當(dāng)不需要時(shí)置 0。
指令舉例
示例代碼 45-47 使能 ICache
1 /******Cache Test*******/
2 mrc p15,0,r1,c1,c0,0
3 orr r1, r1, #(1 << 2) // Set C bit 整體使能Cache
4 orr r1, r1, #(1 << 12) //Set I bit 使能ICache
5 mcr p15,0,r1,c1,c0,0
6 /******End Test******/
異常產(chǎn)生指令
ARM 指令集中提供了兩條產(chǎn)生異常的指令,通過這兩條指令可以用軟件的方法實(shí)現(xiàn)異常。如表所示為 ARM 異常產(chǎn)生指令。

軟件中斷指令(Software Interrupt,SWI)用于產(chǎn)生軟中斷,從而實(shí)現(xiàn)從用戶模式變換到管理模式,CPSR 保存到管理模式的 SPSR 中,執(zhí)行轉(zhuǎn)移到 SWI 向量,在其他模式下也可以使用 SWI 指令,處理器同樣切換到管理模式。
指令的語法格式。
SWI{<c>} <immed_24>
指令舉例
示例代碼 45-48 swi 指令示例
1 swi 0 ;產(chǎn)生軟中斷,中斷號為0
2 swi 0x123456 ;產(chǎn)生軟中斷,中斷立即數(shù)為 0x123456
使用 SWI 指令時(shí),通常使用以下兩種方法進(jìn)行參數(shù)傳遞。
下面的程序產(chǎn)生一個(gè)中斷號為 12 的軟中斷。
示例代碼 45-49 swi 指令示例
1 mov r0,#34 ;設(shè)置功能號為34
2 swi 12 ;產(chǎn)生軟中斷,中斷號為 12
下面的例子通過 R0 傳遞中斷號,R1 傳遞中斷的子功能號。
示例代碼 45-50 swi 指令示例
1 mov r0,#12 ;設(shè)置12號軟中斷
2 mov r1,#34 ;設(shè)置功能號為34
3 swi 0
ARM 匯編實(shí)驗(yàn)
實(shí)驗(yàn)?zāi)康?/strong>
了解程序的運(yùn)行過程
掌握 ARM 匯編語言的基本使用;
熟悉 eclipse 開發(fā)工具建立匯編工程和仿真;
實(shí)驗(yàn)原理
根據(jù)上面闡述 RAM 匯編語言的使用語法和功能,編寫匯編程序,實(shí)現(xiàn)一個(gè)簡單的數(shù)據(jù)運(yùn)算操作。
? 可執(zhí)行程序的組成部分
在 linux 下一個(gè)可執(zhí)行的 elf 應(yīng)用程序,通常包含以下幾部分內(nèi)容。

圖 45-3 可執(zhí)行程序的組成部分
這里涉及到了代碼段、數(shù)據(jù)段、未初始化數(shù)據(jù)、堆、棧,幾個(gè)部分:
代碼段:代碼段在內(nèi)存中被映射為只讀。通常是用來存放程序執(zhí)行的指令。
數(shù)據(jù)段:通常用來存放程序中已初始化的(非 0)全局變量和靜態(tài)局部變量,數(shù)據(jù)段的起始位置由鏈接定位文件確認(rèn),大小在編譯鏈接時(shí)自動分配。
未初始化數(shù)據(jù):通常用來存放程序中未初始化和初始化為 0 的全局變量的一塊內(nèi)存區(qū)域,在程序載入時(shí)清零。
堆:保存函數(shù)內(nèi)部動態(tài)分配(malloc 或 new)的內(nèi)存。
棧:保存函數(shù)的局部變量(不包括 static 修飾的變量),參數(shù)以及返回值。
在匯編語言中一個(gè)可執(zhí)行程序一般至少包含:代碼段+數(shù)據(jù)段+BSS 段。
? 位置有關(guān)和位置無關(guān):
位置無關(guān)碼就是和位置無關(guān),這種代碼放在什么位置都能正常運(yùn)行,所以地址是動態(tài)的不能固定的。而位置有關(guān)碼就是和某個(gè)具體位置有關(guān)的代碼,而這個(gè)具體位置就是我們的鏈接地址。
? 鏈接地址:
在程序編譯的時(shí)候,每個(gè)目標(biāo)文件都是由源代碼編譯得到,最終多個(gè)目標(biāo)文件鏈接生成一個(gè)最終的可執(zhí)行文件,而鏈接地址就是指示鏈接器,各個(gè)目標(biāo)文件的在可執(zhí)行程序中的位置。比如,一個(gè)可執(zhí)行程序 a.out 由 a.o、b.o、c.o 組成,那么最終的 a.out 中誰在前、誰在中間、誰在結(jié)尾,都可以通過制定鏈接地址來決定。
? 運(yùn)行地址:
程序?qū)嶋H在內(nèi)存中運(yùn)行時(shí)候的地址,比如 CPU 要執(zhí)行一條指令,那么必然要通過給 PC 賦值,從對應(yīng)的地址空間中去取出來,那么這個(gè)地址就是實(shí)際的運(yùn)行地址。
? 加載地址和存儲地址:
每一個(gè)程序一開始都是存放在 flash 中的,而運(yùn)行是在內(nèi)存中,這個(gè)時(shí)候就需要從 flash 中將指令讀取到內(nèi)存中(運(yùn)行地址),flash 的地址就是加載地址。

圖 45-4 鏈接地址和運(yùn)行地址對比圖
實(shí)驗(yàn)內(nèi)容
創(chuàng)建鏈接腳本
示例代碼 45-51 鏈接腳本
1 OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
2 OUTPUT_ARCH(arm)
3 ENTRY(_start)
4 SECTIONS
5 {
6 . = 0xc2000040;
7 . = ALIGN(4);
8 .text :
9 {
10 start.o(.text)
11 *(.text)
12 }
13 . = ALIGN(4);
14 .rodata :
15 { *(.rodata) }
16 . = ALIGN(4);
17 .data :
18 { *(.data) }
19 . = ALIGN(4);
20 .bss :
21 { *(.bss) }
22 }
創(chuàng)建 Makefile 編譯腳本
示例代碼 45-52 Makefile 文件
1 SHELL=C:\Windows\System32\cmd.exe
2
3 CROSS_COMPILE = arm-none-eabi4 NAME = h_project
5
6 CPPFLAGS := -nostdlib -nostdinc -g
7 CFLAGS := -Wall -O2 -fno-builtin -g
8
9 LD = $(CROSS_COMPILE)ld
10 CC = $(CROSS_COMPILE)gcc
11 OBJCOPY = $(CROSS_COMPILE)objcopy
12 OBJDUMP = $(CROSS_COMPILE)objdump
13
14 export CC LD OBJCOPY OBJDUMP AR CPPFLAGS CFLAGS
15
16 objs := start.o
17 all: $(objs)
18 $(LD) -T map.lds -o $(NAME).elf $^
19 $(OBJCOPY) -O binary $(NAME).elf $(NAME).bin
20 $(OBJDUMP) -D $(NAME).elf > $(NAME).dis
21
22 %.o : %.S
23 $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< -c
24
25 %.o : %.c
26 $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< -c
27
28 clean:
29 rm *.o *.elf *.bin
匯編程序設(shè)計(jì)如下
示例代碼 45-53 匯編示例
1 .text
2 .global _start
3 _start:
4
5 mov r0, #0x9
6 nop
7 mov r1, #0x7
8
9 bl add_sub
10
11 stop:
12 b stop
13
14 add_sub:
15 add r2, r0, r1 ; r2=0x9+0x7=0x10
16 sub r3, r0, r1 ; r3=0x9-0x7=0x2
17
18 mul r4, r0,r1 ; r4=0x9*0x7=0x3f
19 mov pc, lr
實(shí)驗(yàn)步驟
1、 導(dǎo)入工程源碼
請參考導(dǎo)入一個(gè)已有工程章節(jié)的導(dǎo)入一個(gè)已有工程。
光盤實(shí)驗(yàn)源碼路徑:【資料光盤\華清遠(yuǎn)見-FS-MP1A 開發(fā)資料-2020-11-06\02-程序源碼\03-ARM 體系
結(jié)構(gòu)與接口技術(shù)\Cortex-A7\h_project】
2、 打開“Register”顯示框
單擊 window -> show view -> Register,

3、 單步仿真
配置完成之后,點(diǎn)擊“ ”開始仿真,彈出 Debug 框。

單擊“ ”單步仿真。查看仿真現(xiàn)象。
實(shí)驗(yàn)現(xiàn)象
單擊“ ”單步,查看 Rn 寄存器的變化。


單步運(yùn)行可以看到 R2、R3 和 R4 的值變化。