2.2 匯編偽指令
匯編語(yǔ)言分成兩塊:標準指令集和非標準指令集。偽指令屬于非標準指令集。
什么是偽指令?
類(lèi)似于宏的東西,把復雜的有好幾天指令進(jìn)行跳轉的完成的小功能級進(jìn)行新的標簽設定,這就是偽指令。
類(lèi)似于學(xué)c語(yǔ)言的時(shí)候的預處理,在預處理的時(shí)候把它定義于一堆的宏轉化為真正的c語(yǔ)言的代碼。同樣,偽指令是在定義好之后的匯編,匯編的時(shí)候會(huì )把它翻譯成標準指令,也許一條簡(jiǎn)單的偽指令可以翻譯成很多條標準的匯編指令集,所以這就是偽指令最重要的作用。
我們前面說(shuō)的 CODE16
CODE32
也是偽指令,用來(lái)指定其后的代碼格式。
偽指令的作用?
基本的指令可以做各類(lèi)操作了,但操作起來(lái)太麻煩了。偽指令定義了一些類(lèi)似于帶參數的宏,能夠更好的實(shí)現匯編程序邏輯。(比如我現在要設置一個(gè)值給寄存器R0,但下次我修改了寄存器R0之后又需要讀出來(lái)剛才的值,那我們就要先臨時(shí)保存值到SPSR,CPSR,然后不斷切換。)
偽指令只是在匯編器之前作用,匯編以后翻譯為標準的匯編令集。
偽指令的類(lèi)別偽指令可分為ARM匯編偽指令和GNU匯編偽指令
ARM匯編偽指令是ARM公司的,GNU匯編偽指令是GNU平臺的。他們有自己的匯編器,不同的匯編器的解釋語(yǔ)法可以設成不同。
在這里插入圖片描述
2.2.1 GNU匯編偽指令
這里列出部分偽指令說(shuō)明,具體的偽指令可以結合 ARM匯編偽指令分析:
bit 11-8 | 7-0 |
---|---|
.word | 分配一個(gè)4字節空間 |
.byte | 定義單字節數據 |
.short | 定義雙字節數據 |
.long | 定義一個(gè)4字節數據 |
.equ | 賦值語(yǔ)句:.equ a, 0x11 |
.align | 數據字節對齊:.align 4 (4字節對齊) |
.global | 定義全局符號:.global Default_Handler |
.end | 源文件結束 |
2.2.2 ARM匯編偽指令
在我的另一篇博文:STM32的啟動(dòng)過(guò)程(startup_xxxx.s文件解析)
里面有過(guò)一些對偽指令意思的的說(shuō)明,下面也列出部分說(shuō)明:
AREA:
用于定義一個(gè)代碼段或數據段。屬性字段表示該代碼段(或數據段)的相關(guān)屬性,多個(gè)屬性用逗號分隔。其中,段名若以數字開(kāi)頭,則該段名需用?“?|?”?括起來(lái):
ALIGN:
ALIGN?偽指令可通過(guò)添加填充字節的方式,使當前位置滿(mǎn)足一定的對其方式。其中,表達式的值用于指定對齊方式,可能的取值為2的冪,如?1?、2?、4?、8?、16?等。若未指定表達式,則將當前位置對齊到下一個(gè)字的位置。
CODE16和CODE32:
指定其后面的指令為 ARM 指令還是?Thumb?指令,前面介紹過(guò)。
ENTRY:
用于指定匯編程序的入口點(diǎn)。在一個(gè)完整的匯編程序中至少要有一個(gè)?ENTRY?(也可以有多個(gè),當有多個(gè)?ENTRY?時(shí),程序的真正入口點(diǎn)由鏈接器指定),但在一個(gè)源文件里最多只能有一個(gè)?ENTRY。
在startup_stm32f103xg.s
里面就沒(méi)有。
END:
用于通知編譯器已經(jīng)到了源程序的結尾。IMPORT 和 EXPORT:
IMPORT 定義表示這是一個(gè)外部變量的標號,不是在本程序定義的 EXPORT 表示本程序里面用到的變量提供給其他模塊調用的
2.2.3 LDR
和 ADR
LDR
偽指令:
簡(jiǎn)單介紹了偽指令基礎,回到上一小結留下的問(wèn)題,想要把任意值復制給 R0,怎么處理,我們使用偽指令:LDR R0, =value
編譯器會(huì )把“偽指令”替換成真實(shí)的指令:
LDR R0, =0x12
0x12是立即數,那么替換為:MOV R0, #0x12
LDR R0, =0x12345678
0x12345678不是立即數,那么替換為:LDR R0, [PC, #offset]
// 2. 使用Load Register讀內存指令讀出值,offset是鏈接程序時(shí)確定的 ……Label DCD 0x12345678
// 1. 編譯器在程序某個(gè)地方保存有這個(gè)值
ADR
偽指令:
ADR的意思是:address,用來(lái)讀某個(gè)標號的地址:ADR{cond} Rd, labe1
ADR R0, Loop
...
Loop
ADD R0, R0, #1
;(它是“偽指令”,會(huì )被轉換成某條真實(shí)的指令,比如:)
ADD R0, PC, #val ; loop的地址等于PC值加上或者減去val的值,val的值在鏈接時(shí)確定,
...
Loop
ADD R0, R0, #1
2.3 ARM匯編指令集
在《ARM Cortex-M3與Cortex-M4權威指南》一文中第5章節有詳細的指令集說(shuō)明:匯編指令可以分為幾大類(lèi):數據處理、內存訪(fǎng)問(wèn)、跳轉、飽和運算、其他指令。
數據傳輸命令 MOV
MOV指令,用于將數據從一個(gè)寄存器拷貝到另外一個(gè)寄存器,或者將一個(gè)立即數傳遞到寄存器。
MOV指令的格式為:MOV{條件}{S} 目的寄存器,源操作數
MOV R0,R1 ;@將寄存器R1中的數據傳遞給R0,即R0=R1
MOV R0, #0X12 ;@將立即數0X12傳遞給R0寄存器,即R0=0X12
狀態(tài)寄存器訪(fǎng)問(wèn) MRS 和 MSR
MRS指令,用于將特殊寄存器(如CPSR和SPSR)中的數據傳遞給通用寄存器。
MSR指令,和MRS相反,用來(lái)將普通寄存器的數據傳遞給特殊寄存器。
;M3/M4
MRS R0, APSR ;單獨讀APSR
MRS R0, PSR ; 讀組合程序狀態(tài)
;A7
MRS R0, CPSR ; 讀組合程序狀態(tài)
...
MSR CPSR,R0 ;傳送R0的內容到CPSR
存儲器訪(fǎng)問(wèn) LDR 和 STR
LDR:
LDR 指令用于從存儲器中將一個(gè)32位的字數據傳送到目的寄存器中。該指令通常用于從存儲器中讀取32位的字數據到通用寄存器,然后對數據進(jìn)行處理。
指令的格式為:LDR{條件} 目的寄存器,<存儲器地址>
當程序計數器PC作為目的寄存器時(shí),指令從存儲器中讀取的字數據被當作目的地址,從而可以實(shí)現程序流程的跳轉。
LDRB: 字節操作
LDRH: 半字操作
LDR Rd, [Rn , #offset] ;從存儲器Rn+offset的位置讀取數據存放到Rd中。
...
LDR R0, =0X02077004 ;偽指令,將寄存器地址 0X02077004 加載到 R0 中,即 R0=0X02077004
LDR R1, [R0] ;讀取地址 0X02077004 中的數據到 R1 寄存器中
...
LDR R0,[R1,R2] ;將存儲器地址為R1+R2的字數據讀入寄存器R0。
LDR R0,[R1,#8] ;將存儲器地址為R1+8的字數據讀入寄存器R0。
...
LDR R0,[R1,R2,LSL#2]! ;將存儲器地址R1+R2×4的字數據讀入寄存器R0,并將新地址R1+R2×4寫(xiě)入R1。
LDR R0,[R1],R2,LSL#2 ;將存儲器地址R1的字數據讀入寄存器R0,并將新地址R1+R2×4寫(xiě)入R1。
...
LDRH R0,[R1] ;將存儲器地址為R1的半字數據讀入寄存器R0,并將R0的高16位清零。
STR:
STR 指令用于從源寄存器中將一個(gè)32位的字數據傳送到存儲器中。該指令在程序設計中比較常用,且尋址方式靈活多樣,使用方式可參考指令LDR。
指令的格式為:STR{條件} 源寄存器,<存儲器地址>
STRB: 字節操作,從源寄存器中將一個(gè)8位的字節數據傳送到存儲器中。該字節數據為源寄存器中的低8位。
STRH: 半字操作,從源寄存器中將一個(gè)16位的半字數據傳送到存儲器中。該半字數據為源寄存器中的低16位。
STR Rd, [Rn, #offset] ;將Rd中的數據寫(xiě)入到存儲器中的Rn+offset位置。
LDR R0, =0X02077004 ;將寄存器地址 0X02077004 加載到 R0 中,即 R0=0X02077004
LDR R1, =0X2000060c ;R1 保存要寫(xiě)入到寄存器的值,即 R1=0X2000060c
STR R1, [R0] ;將 R1 中的值寫(xiě)入到 R0 中所保存的地址中
STR R0,[R1],#8 ;將R0中的字數據寫(xiě)入以R1為地址的存儲器中,并將新地址R1+8寫(xiě)入R1。
STR R0,[R1,#8] ;將R0中的字數據寫(xiě)入以R1+8為地址的存儲器中。
壓棧和出棧 PUSH 和 POP
PUSH :
壓棧,將寄存器中的內容,保存到堆棧指針指向的內存上面,將寄存器列表存入棧中。
PUSH < reg list >
POP :
出棧,從棧中恢復寄存器列表
POP < reg list >
push {R0, R1} ;保存R0,R1
push {R0~R3,R12} ;保存 R0~R3 和 R12,入棧
pop {R0~R3} ;恢復R0 到 R3 ,出棧
以M3內核來(lái)舉個(gè)例子:
假設當前 MSP 值為 0x2000 2480;寄存器 R0 的值為 0x3434 3434 寄存器 R1 的值為 0x0000 1212 寄存器 R2 的值為 0x0000 0000
執行push {R0, R1,R2}
之后,
內存地址的數據為:0x2000 2474的值為: 0x3434 3434 (R0的值) 0x2000 2478的值為: 0x0000 1212 (R1的值) 0x2000 247C的值為: 0x0000 0000 (R2的值) MSP 的值變成 0x2000 2474
高位寄存器保存到高地址,先入棧,如果是POP,數據先出到低位寄存器
跳轉指令 B 和 BL
B :
ARM 處理器將立即跳轉到指定的目標地址,不再返回原地址。
B指令的格式為:B{條件} 目標地址
注意存儲在跳轉指令中的實(shí)際值是相對當前PC值的一個(gè)偏移量,而不是一個(gè)絕對地址,它的值由匯編器來(lái)計算。
//設置棧頂指針后跳轉到C語(yǔ)言
_start:
ldr sp,=0X80200000 ;設置棧指針
b main ;跳到 main 函數
BL :
BL 跳轉指令,在跳轉之前會(huì )在寄存器LR(R14)中保存當前PC寄存器值,所以可以通過(guò)將LR 寄存器中的值重新加載到PC中來(lái)繼續從跳轉之前的代碼處運行,是子程序調用的常用的方法。
BL loop ;跳轉到標號loop處執行時(shí),同時(shí)將當前的PC值保存到R14中
BLX:
該跳轉指令是當子程序使用Thumb指令集,而調用者使用ARM指令集時(shí)使用。
BLX指令從ARM指令集跳轉到指令中所指定的目標地址,并將處理器的工作狀態(tài)有ARM狀態(tài)切換到Thumb狀態(tài),該指令同時(shí)將PC的當前內容保存到寄存器R14中。
BX:
BX指令跳轉到指令中所指定的目標地址,目標地址處的指令既可以是ARM指令,也可以是Thumb指令。
算數運算指令
算數運算指令和下面的邏輯運算指令表格摘自《【正點(diǎn)原子】I.MX6U嵌入式Linux驅動(dòng)開(kāi)發(fā)指南》
邏輯運算指令
-
ARM
+關(guān)注
關(guān)注
134文章
8726瀏覽量
363108 -
寄存器
+關(guān)注
關(guān)注
30文章
5167瀏覽量
118237 -
PC
+關(guān)注
關(guān)注
9文章
1981瀏覽量
153058 -
匯編語(yǔ)言
+關(guān)注
關(guān)注
14文章
404瀏覽量
35406
發(fā)布評論請先 登錄
相關(guān)推薦
評論