對于搞嵌入式驅動或者操作系統的人來說,掌握匯編語言的使用還是比較重要的,畢竟有時候在分析定位問題的時候,多多少少都會有匯編的身影。本文主要講講ARM指令集格式以及常用的ARM匯編指令(主要包括LDR和STR指令,LDM和STM指令,push和pop指令,MOV指令,CPS指令,MRS和MSR指令,MRC和MCR指令,其余指令暫時沒列出來,用到時可以查看ARM手冊進行了解)。
ARM指令集格式
ARMv7架構是一個32位的處理器架構。同時ARM架構是一個加載/存儲體系結構,所有的數據處理操作需要在通用寄存器中完成。
要學習了解處理器的匯編指令,那么首先可以看看匯編指令的通用表達式,具體的指令也就是使用具體的指令和參數代替通用表達式的參數。ARM指令集的指令表達如下所示:
opcode{}{S} , {, }
opcode:指令助記符,比如LDR,STR,MOV等。
{}:大括號括起來的內容表示可選。
<>:<>括起來的內容是必須的。
cond:條件碼,比如EQ,NE,CS等,條件碼的內容如下圖所示:
Snipaste_2023-09-22_20-22-54
Snipaste_2023-09-22_20-25-47
S:可選的后綴,如果指令中添加了S,那么指令的執行結果將會影響到CPSR寄存器的標志位域。
Rd:目標寄存器。
Rn:第一個操作數寄存器。
Rm:第二個操作數寄存器。
在了解了ARM指令的表達式之后,下面就講講常用的匯編指令。
LDR和STR指令
LDR指令用于從內存中讀取數據存儲到通用寄存器中。STR指令用于將通用寄存器中的值存儲到內存中。LDR指令的語法如下所示:
LDR{type}{T}{cond} Rt, [Rn {, #offset}] LDR{type}{cond} Rt, [Rn, #offset]! LDR{type}{T}{cond} Rt, [Rn], #offset LDR{type}{cond} Rt, [Rn, +/-Rm] LDR{type}{cond} Rt, [Rn, +/-Rm]! LDR{type}{T}{cond} Rt, [Rn], +/-Rm
STR指令的語法如下所示:
STR{type}{T}{cond} Rt, [Rn {, #offset}] STR{type}{cond} Rt, [Rn, #offset]! STR{type}{T}{cond} Rt, [Rn], #offset STR{type}{cond} Rt, [Rn, +/-Rm] STR{type}{cond} Rt, [Rn, +/-Rm]! STR{type}{T}{cond} Rt, [Rn], +/-Rm
type:操作的數據寬度,可以是:B(unsigned byte),SB(signed byte),H(unsigned halfword),SH(signed halfword)。
cond:條件碼。
Rt:目標寄存器。
Rn:存儲內存操作基地址的寄存器。
Rm:存儲偏移量的寄存器。
offset:立即數。
!:如果存在,表示最終的地址要寫回Rn。
T:表示處理器是在用戶模式下訪問內存地址。
加載存儲指令有4種尋址方式,LDR的操作描述如下(STR指令的操作類似):
寄存器尋址:要尋址的地址存放在寄存器中。
前變基尋址:在內存訪問之前,將寄存器中的內存地址加上偏移量之后作為新的內存地址進行內存訪問。指令形式為:LDR Rt, [Rn, Op2]。偏移量Op2可以是正數或者是負數,可以是一個立即數,可以是另一個寄存器的值,可以是另一個寄存器中的數據進行移位之后的值。
帶寫回的前變基尋址:指令形式為:LDR Rt, [Rn, Op2]!。該尋址模式和前變基尋址一樣,只是在訪問完內存之后Rn寄存器中的值就更新為運算之后得到的新內存地址的值。
帶寫回的后變基尋址:指令形式為:LDR Rt, [Rn], #offset和LDR Rt, [Rn], +/-Rm。將寄存器Rn中存儲的數值作為內存地址,將該內存地址中的數據讀出來存儲到Rt寄存器中,然后將內存地址加減立即數offset或者Rm寄存器中的數值得到新的內存地址存儲到Rn寄存器中。
上面說的這些有可能不太好懂,下面簡單的列舉幾個例子吧:
# 寄存器尋址 # 將R1地址處的數據讀出,保存到R0中 LDR R0, [R1] # 前變基尋址 # 將(R1+0x4)地址處的數據讀出,保存到R0中 LDR R0, [R1, #0x4] # 將(R1-0x4)地址處的數據讀出,保存到R0中 LDR R0, [R1, #-0x4] # 將(R1+R2)地址處的數據讀出,保存到R0中 LDR R0, [R1, R2] # 將((R1+(R2<<2))地址處的數據讀出,保存到R0中 LDR R0, [R1, R2, LSL #2] # 帶寫回的前變基尋址 # 將(R1+R2)地址處的數據讀出,保存到R0中,然后更新R1=R1+R2 LDR R0, [R1, R2]! # 將((R1+(R2<<2))地址處的數據讀出,保存到R0中,然后更新R1=((R1+(R2<<2)) LDR R0, [R1, R2, LSL #2]! # 帶寫回的后變基尋址 # 將R1地址處的數據讀出,保存到R0中,然后更新R1=R1+0x4 LDR R0, [R1], #0x4 # 將R1地址處的數據讀出,保存到R0中,然后更新R1=R1-0x4 LDR R0, [R1], #-0x4 # 將R1地址處的數據讀出,保存到R0中,然后更新R1=R1+R2 LDR R0, [R1], R2 # 將R1地址處的數據讀出,保存到R0中,然后更新R1=R1-R2 LDR R0, [R1], -R2
STR指令的操作和LDR指令類似,這里就不列舉了。
LDR偽指令
LDR相關的偽指令語法如下所示:
# 將數據 constant 加載到 Rt 寄存器中 LDR Rt, =constant # 將 label 所代表的地址加載到 Rt 寄存器中 LDR Rt, =label
下面是LDR偽指令簡單的使用:
# 將 0xaa 加載到 R0 寄存器 LDR R0, =0xaa # 將 _start 所代表的地址加載到 R0 寄存器 LDR R0, =_start
LDM和STM指令
LDM指令用于加載指定地址上的數據保存到一個或者多個寄存器中。STM指令用于將一個或者多個寄存器中的數據存儲到指定地址上。LDM和STM指令主要用于現場保護和數據復制。
LDM指令的語法如下所示:
LDM{addr_mode}{cond} Rn{!},reglist{^}
STM指令的語法如下所示:
STM{addr_mode}{cond} Rn{!},reglist{^}
addr_mode:地址模式,用于數據塊傳輸的地址模式,如下所示:
IA:每次傳送后地址加4。 IB:每次傳送前地址加4。 DA:每次傳送后地址減4。 DB:每次傳送前地址減4。
也可以使用相應的面向堆棧的尋址模式,如下所示:
FD:滿遞減堆棧(每次傳送前地址減4)。 ED:空遞增堆棧(每次傳送前地址加4)。 FA:滿遞增堆棧(每次傳送后地址減4)。 EA:空遞增堆棧(每次傳送后地址加4)。
cond:條件碼。
Rn:Rn存儲了用于傳輸的初始地址。
!:如果存在,表示最終的地址要寫回Rn。
reglist:用{}括起來的一個寄存器或者多個寄存器組成的列表。它可以是一個寄存器范圍。如果{}中的寄存器超過一個,那么寄存器或者寄存器范圍之間通過逗號(,)分隔。
^:如果在除了USR模式和SYS模式下存在該符號,意味著將發生下述的兩個動作:
當寄存器列表中不包含PC時,加載/存儲的是USR模式的寄存器,而不是當前模式的寄存器。
在使用LDM指令時,如果寄存器列表中包含PC時,那么除了正常的多寄存器傳送外,會將SPSR 拷貝到CPSR 中,這可用于異常處理返回。
上面的內容可能不是很好理解,下面簡單的列舉寫例子:
LDMIA和STMIA例子
LDMIA例子如下所示:
# IA:每次傳送后地址加4,下面是指令執行流程的分解: # R0=[R1] # R2=[R1+4] # R3=[R1+8] # R4=[R1+12] LDMIA R1, {R0, R2-R4} # IA:每次傳送后地址加4,最終地址要寫回R1,下面是指令執行流程的分解: # R0=[R1], R1=R1+4 # R2=[R1], R1=R1+4 # R3=[R1], R1=R1+4 # R4=[R1], R1=R1+4 LDMIA R1!, {R0, R2-R4}
STMIA例子如下所示:
# IA:每次傳送后地址加4,下面是指令執行流程的分解: # [R1]=R0 # [R1+4]=R2 # [R1+8]=R3 # [R1+12]=R4 STMIA R1, {R0, R2-R4} # IA:每次傳送后地址加4,最終地址要寫回R1,下面是指令執行流程的分解: # [R1]=R0, R1=R1+4 # [R1]=R2, R1=R1+4 # [R1]=R3, R1=R1+4 # [R1]=R4, R1=R1+4 STMIA R1!, {R0, R2-R4}
LDMIB和STMIB例子
LDMIB例子如下所示:
# IB:每次傳送前地址加4,下面是指令執行流程的分解: # R0=[R1+4] # R2=[R1+8] # R3=[R1+12] # R4=[R1+16] LDMIB R1, {R0, R2-R4} # IB:每次傳送前地址加4,最終地址要寫回R1,下面是指令執行流程的分解: # R1=R1+4, R0=[R1] # R1=R1+4, R2=[R1] # R1=R1+4, R3=[R1] # R1=R1+4, R4=[R1] LDMIB R1!, {R0, R2-R4}
STMIB例子如下所示:
# IB:每次傳送前地址加4,下面是指令執行流程的分解: # [R1+4]=R0 # [R1+8]=R2 # [R1+12]=R3 # [R1+16]=R4 STMIB R1, {R0, R2-R4} # IB:每次傳送前地址加4,最終地址要寫回R1,下面是指令執行流程的分解: # R1=R1+4, [R1]=R0 # R1=R1+4, [R1]=R2 # R1=R1+4, [R1]=R3 # R1=R1+4, [R1]=R4 STMIB R1!, {R0, R2-R4}
LDMDA和STMDA例子
LDMDA例子如下所示:
# DA:每次傳送后地址減4,下面是指令執行流程的分解: # R4=[R1] # R3=[R1-4] # R2=[R1-8] # R0=[R1-12] LDMDA R1, {R0, R2-R4} # DA:每次傳送后地址減4,最終地址要寫回R1,下面是指令執行流程的分解: # R4=[R1], R1=R1-4 # R3=[R1], R1=R1-4 # R2=[R1], R1=R1-4 # R0=[R1], R1=R1-4 LDMDA R1!, {R0, R2-R4}
STMDA例子如下所示:
# DA:每次傳送后地址減4,下面是指令執行流程的分解: # [R1]=R4 # [R1-4]=R3 # [R1-8]=R2 # [R1-12]=R0 STMDA R1, {R0, R2-R4} # DA:每次傳送后地址減4,最終地址要寫回R1,下面是指令執行流程的分解: # [R1]=R4, R1=R1-4 # [R1]=R3, R1=R1-4 # [R1]=R2, R1=R1-4 # [R1]=R0, R1=R1-4 STMDA R1!, {R0, R2-R4}
LDMDB和STMDB例子
LDMDB例子如下所示:
# DB:每次傳送前地址減4,下面是指令執行流程的分解: # R4=[R1-4] # R3=[R1-8] # R2=[R1-12] # R0=[R1-16] LDMDB R1, {R0, R2-R4} # DB:每次傳送前地址減4,最終地址要寫回R1,下面是指令執行流程的分解: # R1=R1-4, R4=[R1] # R1=R1-4, R3=[R1] # R1=R1-4, R2=[R1] # R1=R1-4, R0=[R1] LDMDB R1!, {R0, R2-R4}
STMDB例子如下所示:
# DB:每次傳送前地址減4,下面是指令執行流程的分解: # [R1-4]=R4 # [R1-8]=R3 # [R1-12]=R2 # [R1-16]=R0 STMDB R1, {R0, R2-R4} # DB:每次傳送前地址減4,最終地址要寫回R1,下面是指令執行流程的分解: # R1=R1-4, [R1]=R4 # R1=R1-4, [R1]=R3 # R1=R1-4, [R1]=R2 # R1=R1-4, [R1]=R0 STMDB R1!, {R0, R2-R4}
現場保護
在數據塊的傳輸中:STMDB和LDMIA對應使用,STMIA和LDMDB對應使用。
在堆棧操作中:STMFD和LDMFD對應使用,STMFA和LDMFA對應使用。
在子程序或者異常處理時,使用LDMFD和STMFD進行現場保護的例子如下:
# 將R0-R7和LR入棧 STMFD SP!, {R0-R7, LR} # 功能代碼 MOV R0, #0x00 MOV R1, #0x11 MOV R2, #0x22 # 將R0-R7和LR出棧 LDMFD SP!, {R0-R7, LR}
同樣的可以使用STMDB和LDMIA指令進行現場保護,因此上述代碼可以修改成下述形式:
# 將R0-R7和LR入棧 STMDB SP!, {R0-R7, LR} # 功能代碼 MOV R0, #0x00 MOV R1, #0x11 MOV R2, #0x22 # 將R0-R7和LR出棧 LDMIA SP!, {R0-R7, LR}
push和pop指令
push和pop指令主要用于子程序或者異常的現場保護。push指令用于將寄存器內容壓入堆棧。pop指令用于將堆棧中的內容恢復到寄存器中。
push指令的語法如下所示:
PUSH{cond} reglist
pop指令的語法如下所示:
POP{cond} reglist
cond:條件碼。
reglist:用{}括起來的一個寄存器或者多個寄存器組成的列表。它可以是一個寄存器范圍。如果{}中的寄存器超過一個,那么寄存器或者寄存器范圍之間通過逗號(,)分隔。
push指令等價于STMDB指令。pop指令等價于LDMIA指令。
使用push指令和pop指令保護現場的例子如下所示:
# 將R0-R7和LR入棧 push {R0-R7, LR} # 功能代碼 MOV R0, #0x00 MOV R1, #0x11 MOV R2, #0x22 # 將R0-R7和LR出棧 pop {R0-R7, LR}
MOV指令
MOV指令主要用于將數據搬移到寄存器中。MOV指令的語法如下所示:
MOV{S}{cond} Rn, Rm MOV{cond} Rn, #imm
S:可選的后綴,如果指令中添加了S,那么指令的執行結果將會影響到CPSR寄存器的標志位域。
cond:條件碼。
Rn:目標寄存器。
Rm:源寄存器。
imm:立即數。
MOV指令的使用例子如下:
# 將 R1 寄存器中的內容搬移到 R0 寄存器 MOV R0, R1 # 將 0xaa 搬移到 R0 寄存器 MOV R0, #0xaa
CPS指令
可以通過CPS(Change Processor State)指令來修改處理器模式。CPS指令也可以用來使能或者禁止異常。
CPS指令的語法如下所示:
CPS #mode CPSIE iflags{, #mode} CPSID iflags{, #mode}
mode是處理器的模式編碼,比如在從其他模式下切換到SYS模式,使用下述代碼即可:
# 切換到SYS模式 CPS #0x1f
IE使能中斷或者終止。
ID禁止中斷或者終止。
iflags由下面的一種或者幾種組成:
a:表示異步終止(asynchronous abort);
i:表示中斷(IRQ);
f:表示快中斷(FIQ);
下述代碼是CPS指令的一些簡單用法:
# 使能中斷 CPSIE I # 禁止中斷 CPSID I # 使能異步終止和快中斷 CPSIE AF # 禁止異步終止和快中斷 CPSID AF # 使能中斷并切換到SYS模式 CPSIE I, #0x1f
MRS與MSR指令
MRS和MSR指令可用于讀寫程序狀態寄存器CPSR,APSR和SPSR。
在ARM處理器中,只有MRS指令可以從程序狀態寄存器CPSR,APSR和SPSR中讀出數據到通用寄存器中。MRS指令操作程序狀態寄存器的語法如下:
MRS{cond} Rd, psr
cond為條件碼。
Rd為目標寄存器,Rd不允許為R15。
psr為程序狀態寄存器CPSR,APSR或者SPSR。
MRS指令的示例代碼如下所示:
# 將CPSR寄存器的值讀取到R0中 MRS R0, CPSR # 將SPSR寄存器的值讀取到R1中 MRS R1, SPSR # 將APSR寄存器的值讀取到R2中 MRS R2, APSR
MSR指令可以用來寫程序狀態寄存器CPSR,APSR和SPSR的全部或者部分域。MSR指令操作程序狀態寄存器的語法如下:
MSR{cond} psr, #constant MSR{cond} psr, Rm MSR{cond} psr_fields, #constant MSR{cond} psr_fields, Rm
cond為條件碼。
psr為程序狀態寄存器CPSR或者SPSR。
constant是一個8位立即數。ARM文檔對于constant的介紹如下:
constant is an 8-bit pattern rotated by an even number of bits within a 32-bit word. (Not available in Thumb.)
Rm是源寄存器。
fields由下面的一個或者多個組合而成:
c:xPSR[7:0],控制位域;
x:xPSR[15:8],擴展位域;
s:xPSR[23:16],狀態位域;
f:xPSR[31:24],標志位域;
MSR指令的示例代碼如下所示:
# 切換到SYS模式 MRS R0, CPSR ORR R0, R0, #0x1f MSR CPSR, R0 # 切換到SYS模式 MSR CPSR_c, #0xDF
只有在除用戶模式外的其他模式下才能夠修改狀態寄存器。
MRC和MCR指令
ARMv7-A體系結構的處理器提供了MRC和MCR指令用于對協處理器進行讀寫操作。MRC指令用于將協處理器中的寄存器數據讀取到ARM通用寄存器中。MCR指令用于將ARM通用寄存器中的數據寫入到協處理器的寄存器中。
MRC
MRC指令的語法如下所示:
MRC{cond} coproc, opc1, Rt, CRn, CRm{, opc2}
cond為條件碼。
coproc為協處理器名稱,CP0~CP15協處理器分別對應名稱p0~p15。
opc1為協處理器要執行的操作碼,取指范圍為0~7。
Rt為ARM通用寄存器,用于存儲讀取到的協處理器寄存器數據。
CRn為協處理器寄存器,對于CP15協處理器來說,CRn取值范圍為c0~c15。
CRm為協處理器寄存器,對于CP15協處理器來說,通過CRm和opc2一起來確定CRn對應的具體寄存器。
opc2為可選的協處理器執行操作碼,取指范圍為0~7,當不需要的時候要設置為0。
MRC指令使用示例如下:
# 讀取主ID寄存器 MIDR 的數據到 R0 中. MRC p15, 0, R0, c0, c0, 0
MCR
MCR指令的語法如下所示:
MCR{cond} coproc, opc1, Rt, CRn, CRm{, opc2}
cond為條件碼。
coproc為協處理器名稱,CP0~CP15協處理器分別對應名稱p0~p15。
opc1為協處理器要執行的操作碼,取指范圍為0~7。
Rt為ARM通用寄存器,用于存儲要寫入到協處理器寄存器中的數據。
CRn為協處理器寄存器,對于CP15協處理器來說,CRn取值范圍為c0~c15。
CRm為協處理器寄存器,對于CP15協處理器來說,通過CRm和opc2一起來確定CRn對應的具體寄存器。
opc2為可選的協處理器執行操作碼,取指范圍為0~7,當不需要的時候要設置為0。
MCR指令使用示例如下:
# 將 R0 中的配置數據寫入到 SCTLR MCR p15, 0, R0, c1, c0, 0
-
處理器
+關注
關注
68文章
19259瀏覽量
229652 -
ARM
+關注
關注
134文章
9084瀏覽量
367384 -
操作系統
+關注
關注
37文章
6801瀏覽量
123283 -
匯編指令
+關注
關注
0文章
38瀏覽量
11450 -
指令集
+關注
關注
0文章
222瀏覽量
23378
原文標題:ARMv7-A 那些事 - 6.常用匯編指令
文章出處:【微信號:嵌入式那些事,微信公眾號:嵌入式那些事】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論