ARM 匯編指令條件執行
在ARM模式下,任何一條數據處理指令可以選擇是否根據操作的結果來更新CPSR寄存器中的ALU狀態標志位。在數據處理指令中使用S后綴來實現該功能。
不要在CMP,CMN,TST或者TEQ指令中使用S后綴。這些比較指令總是會更新標志位。
在Thumb模式下,所有數據處理指令都更新CPSR中的標志位。有一個例外就是:當一個或更多個高寄存器被用在MOV和ADD指令時,此時MOV和ADD不能更新狀態標志.
幾乎所有的ARM指令都可以根據CPSR中的ALU狀態標志位來條件執行。參見表2-1條件執行后綴表。
在ARM模式下,你可以:
- 根據數據操作的結果更新CPSR中的ALU狀態標志;
- 執行其他幾種操作,但不更新狀態標志;
- 根據當前狀態標志,決定是否執行接下來的指令。
在Thumb模式,大多數操作總是更新狀態標志位,並且只能使用條件轉移指令(B)來實現條件執行。該指令(B)的后綴和在ARM模式下是一樣的。其他指令不能使用條件執行。
ALU狀態標志
CPSR寄存器包含下面的ALU狀態標志:
| N | Set when the result of the operation was Negative |
| Z | Set when the result of the operation was Zero |
| C | Set when the operation result in a Carry(發生進位,或借位) |
| V | Set when the operation caused oVerflow(操作造成溢出) |
| Q | ARM architecture v5E only sticky flag |
2.5.2 執行條件
N,Z,C,V相關的條件碼后綴如下表所列:

示例1
ADD r0, r1, r2 ; r0 = r1 + r2, 不更新標志位 ADDS r0, r1, r2 ; r0 = r1 + r2, 后綴S表示更新標志位 ADCSS r0, r1, r2 ; If C 標志為1,則執行r0 = r1 + r2, 且更新標志, CMP r0, r1 ; CMP指令肯定會更新標志.
示例2 求最大公約數
gcd CMP r0, r1 BEQ end ; r0 = r0 結束程序 BLT less ; r0 < r1 跳轉至 less SUB r0, r0, r1 ; r0 > r1時 r0 = r0 - r1 B gcd ; 條件都不滿足是繼續循環 less SUB r1, r1, r0 ; r0 < r1 r1 = r1 - r0 B gcd end
示例3
MAIN
mov r1, #1 mov r2, #1 cmp r1, r2 beq FUNC ;if(eq) b FUNC => 實質:if(z == 1) b FUNC bne FUNC ;實質:if(z == 0) b FUNC mov r3, #3 mov r4, #4 FUNC mov r5, #5 mov r6, #6
尋址方式
基地址變址尋址方式
| 種類 | 格式 | 模式 |
| 1 | [Rn, #±<offset_12]> | 立即數前索引尋址 |
| 2 | [Rn, ±Rm] | 寄存器前索引尋址 |
| 3 | [Rn, Rm, <shift>#<offset_12>] | 寄存器位移的前索引尋址 |
| 4 | [Rn, #±<offset_12>]! | 立即數自動索引尋址 |
| 5 | [Rn, ±Rm]! | 寄存器自動索引尋址 |
| 6 | [Rn, Rm, <shift<#<offset_12>]! | 寄存器移位的自動索引尋址 |
| 7 | [Rn], #±<offset_12> | 立即數后索引尋址 |
| 8 | [Rn], ±<Rm> | 寄存器后索引尋址 |
| 9 | [Rn], ±<Rm>, <shift>#<offset_12> | 帶移位的寄存器后索引尋址 |
寄存器前索引尋址
mov r0, #0x40000000 mov r1, #0xF000000F str r1, [r0, #4] ;將 r1 中的數據存儲到地址為 r0 + 4 的內存空間中
寄存器移位的前索引尋址
mov r2, #8 str r1, [r0, r2, lsl #1] ;將 r1 中的數據存儲到地址為 r0 + (r2 << 1)的內存空間中
寄存器后索引尋址
str r1, [r0], #4 ;將 r1 中的數據存儲到地址為 r0 的內存空間中,然后 r0 = r0 + 4
寄存器自動索引尋址
str r1, [r0, #4]! ;將 r1 中的數據存儲到 r0 + 4 的內存空間中, 然后 r0 = r0 + 4
多寄存器尋址/塊拷貝尋址 LDM STM
對棧進行操作是,使用較多 比如說 用 IA 自動索引尋址存,就用 DB 自動索引讀
Load/Store指令
Load/store是一組內存訪問指令,用來在ARM寄存器和內存之間進行數據傳送,ARM指令中有3種基本的數據傳送指令。
- 單寄存器 Load/Store 內存訪問指令(single register):這些指令為ARM寄存器和存儲器提供了更靈活的單數據項傳送方式。數據可以使字節,16位半字或32位字
- 多寄存器 Load/Store 內存訪問指令:可以實現大量數據的同時傳送,主要用於進程的進入和退出、保存和恢復工作寄存器以及復制寄存器中的一片(一塊)數據
- 但寄存器交換指令(single register swap): 實現寄存器數據和內存數據進行交換,而且是在一條指令中完成,執行過程中不會受到中斷干擾
單寄存器Load/Store指令
LDR指令:用於從內存中將一個32位的字讀取到目標寄存器 指令格式 LDR{<cond>} <Rd>, <addr_mode>
;指令舉例 LDR R1, [R0, #0x12] ;將R0 + 12地址處的數據讀出,保存到R1中(R0保持不變)
LDR Rd,[Rn], #0x04 ;立即數后索引尋址,Rn的值用作傳輸數據的存儲地址。在數據傳送后,將偏移量 0x04 與Rn相加,記過寫回到 Rd中
STR指令:用於將一個32位的字節數據寫入到指令中指定的內存單元 指令格式 STR{<cond>} <Rd>,<addr_mode>
;LOAD/STORE指令 ;向內存寫入數據 STR mov r0, #40000000 mov r1, #ff000000 str r1, [r0] ;用於將一個32位的字數據寫入到指令指定的內存單元(以 r0 值為起始地址的 4byte 內存單元) strb r1, [r0] ;將r1 中的 [7:0]存儲到 r0 對應的內存空間 strh r1, [r0] ;將r1 中的 [15:0]存儲到 r0 為起始地址的兩個字節的內存空間 ;在對數據進行操作時,應根據數據本身的屬性(幾個字節)進行操作
多寄存器Load/Store指令
LDM指令:實現數據從連續的內存單元中讀取到指令指定的寄存器列表中。值得注意的是 LDM指令和STM指令:序號的寄存器對應內存中高地址的數據
指令格式: LDM{<cond>}<addressing_mode> <Rn>{!}, <registers>
STM指令:實現將指令中寄存器列表里的數值寫入到一塊連續的內存單元之中
指令格式: STM{<cond>} <addressing_mode> <Rn>{!}, <register>
LDM/STM <cond>的幾種尋址方式:
| 格 式 | 地址變化格式 | ||
| 1 | IA(Increment After) | 后遞增方式 | 先存儲后增長 |
| 2 | IB(Increment Before) | 先遞增方式 | 現增長后存儲 |
| 3 | DA(Decrement After) | 后遞減方式 | 先存儲后遞減 |
| 4 | DB(Decrement Before) | 先遞減方式 | 先遞減后存儲 |
mov r0, #0x40000000 mov r1, #1 mov r2, #2 mov r3, #3 ldm r0, {r1-r3} stm r0, {r1-r3} ;將 r1 - r3 寄存器(連續寄存器)中的數據存儲到 以 r0 位起始地址的內存空間中 stm r0, {r3, r1} ;將 r1 、 r3 寄存器(不連續寄存器)中的數據存儲到 以 r0 位起始地址的內存空間中 ;批量操作時,低編號的寄存器數據對應內存中的低地址 stm r0!, {r1-r3} ;寄存器批量存儲,也適用自動索引尋址, 操作完成后 r0 = r0 + (寄存器個數)*4
跳轉指令
B和BL跳轉指令
MAIN mov r0, #2 mov r1, #3 mov lr, pc ;B 指令不會自動保存 PC;手動保存返回地址、當前PC b lable ;跳轉到標號 FUNC (標號的實質是地址) mov r3, r0 FUNC sub r0, r1, r0 mov pc,lr ;返回主程序繼續執行
跳轉指令是改變指令執行順序的標准方式 (控制流指令)
B 和 BL 指令都是 相對跳轉(短跳轉) 指令,通過偏移量跳轉, 最大跳轉距離是 ±32M
使用 mov pc, <>可以實現 對跳轉(長跳轉)(不會保存當前 PC 值)
關於B指令和BL指令最大跳轉距離是 ±32M:
ARM匯編每條指令占用 4byte,生成機器碼 B 、BL存放在bits[31:24], bits[23:0]是立即數空間,可以表示 2^24個地址。由於每條匯編指令占用4byte字節(最低兩位都為 0),因此可以使用 24位二進制數來表示 26 位的尋址空間。
B指令和BL指令的區別:
- B指令在使用時不會對當前 PC 值進行保存,為保證跳轉指令執行后能正常返回,要手動對其進行保存
- BL (帶連接的跳轉指令)能夠在跳轉時對當前 PC 值自動進行保存。
帶狀態切換的跳轉指令 BX
不會保存跳轉前 PC 值 ±32M范圍
帶狀態切換(ARM&Thmub)的跳轉指令BX ,語法格式:BX{<cond>} <Rm> ,使程序跳轉到指令中參數 Rm 指定的地址進行執行。並將 Rm 的第 0 位復制到 CPRS 中的 T 位, bit[31: 1]存入PC。
PC也可以作為 Rm 寄存器使用,當PC作為 Rm 使用時,指令 BX PC 便跳轉到當前指令下面第 2 條指令出執行(三級流水線:當前正在執行的指令地址為 PC - 8,正在譯碼的指令為 PC - 4, 正在取指的指令是 PC)。但這種方式不值得推薦,最好使用下面的方式完成這種跳轉。
mov pc, pc
;或者
add pc, pc, #0
帶狀態切換的連接跳轉指令
與BX功能一致,但會自動保存跳轉時的 PC 值,語法格式: BLX <target_add> 。target_add為指令的跳轉目標地址,該地址是一個立即數
狀態(寄存器)操作指令
ARM指令集提供了兩條指令(MSR,MRS),用來操作狀態寄存器PSR
MRS指令,語法格式 MRS {cond} Rd, PSR 用於把CPSR或SPSR的值傳送到一個寄存器;
MSR指令,語法格式 MSR {cond} PSR_filed, #immed_8r 或 MSR {cond} PSR_filed, Rm 用於把一個寄存器的值或一個立即數傳送到CPSR或SPSR;使用這兩條只能可以實現對程序狀態寄存器的狀態修改操作。
在使用MSR指令對PSR進行修改操作時,要通過field設置狀態寄存器需要操作的域。狀態寄存器的32位被分為4個8位的域(filed)分別為:bits[31:24]條件域,用 F 表示; bits[23: 16]狀態域,用 S 表示;bits[15:8]預留域,用 X 表示;bits[7:0]控制域,用 C 表示。
;程序舉例:是能IRQ中斷 ENABLE_IRQ MRS R0,CPSR BIC R0,R0,#080 ;將CPSR bit7 I 清零,是能IRQ MSR CPSR_c,R0 ;典型的讀-改-回寫操作 MOV PC,LR
異常產生指令
ARM指令集提供了兩條產生異常的指令(中斷指令 SWI,斷點中斷指令 BKPT),通過這兩條指令可以用軟件的方法實現異常。
中斷指令
軟件中斷指令 SWI (software Interrupt)用於產生軟中斷,實現從用戶模式切換到管理模式,CPSR保存到管理模式的 SPSR 中,執行轉移到 SWI 向量。
語法格式 SWI {<cond>} <immed_24> immed_24 為中斷號
棧
棧:按增長方向分為 遞增棧;遞減棧
按壓棧操作順序(sp位置)分為;滿棧和空棧
滿棧(FULL):sp指向棧頂元素
空棧(EMPTY):sp指向棧頂元素相鄰位置
根據棧的不同分類,將其尋址方式分為以下4種
1)滿遞減 FD(Full Descending)
2)空遞減 ED(Empty Descending)
3)滿遞增 FA(Full Asceding)
4)空遞增 EA(Empty Ascending)
習慣上大都使用滿減棧。
壓棧出棧的幾種操作方式
;初始化棧 mov sp, #0x40000018 MAIN mov r0, #1 mov r1, #1 bl FUNC add r2, r0, r1 FUNC ;壓棧保護現場 stmfd sp!, {r0, r1} mov r0, #2 mov r1, #4 sub r2, r1, r0 ;出棧恢復現場 ldmfd sp!, {r0, r1} mov pc, lr ;初始化棧 MAIN mov r0, #1 mov r1, #1 bl FUNC add r2, r0, r1 FUNC ;壓棧保護現場 stmfd sp!, {r0, r1, lr} mov r0, #2 mov r1, #4 sub r2, r1, r0 ;出棧恢復現場 ldmfd sp!, {r0, r1, pc}
swp r0, r1, [r2]
;將r0中的數據放入內存地址是r2的地址空間,同時將r2中的值放入r1寄存器
;處理器與內存之間進行數據交換 交換過程不會被打斷
