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