簡介:
ARM指令中多數據傳輸共有兩種:
LDM:(load much)多數據加載,將地址上的值加載到寄存器上
STM:(store much)多數據存儲,將寄存器的值存到地址上
主要用途:現場保護、數據復制、參數傳送等,共有8種模式(前面4種用於數據塊的傳輸,后面4種是堆棧操作)如下:
(1)IA:(Increase After) 每次傳送后地址加4,其中的寄存器從左到右執行,例如:STMIA R0,{R1,LR} 先存R1,再存LR
(2)IB:(Increase Before)每次傳送前地址加4,同上
(3)DA:(Decrease After)每次傳送后地址減4,其中的寄存器從右到左執行,例如:STMDA R0,{R1,LR} 先存LR,再存R1
(4)DB:(Decrease Before)每次傳送前地址減4,同上
(5)FD: 滿遞減堆棧 (每次傳送前地址減4)
(6)FA: 滿遞增堆棧 (每次傳送后地址減4)
(7)ED: 空遞減堆棧 (每次傳送前地址加4)
(8)EA: 空遞增堆棧 (每次傳送后地址加4)
注意:其中在數據塊的傳輸中是STMMDB和LDMIA對應,STMMIA和LDMDB對應
而在堆棧操作是STMFD和LDMFD對應,STMFA和LDMFA對應
格式:
LDM{cond} mode Rn{!}, reglist{^}
STM{cond} mode Rn{!}, reglist{^}
其中
Rn:基址寄存器,裝有傳送數據的起始地址,Rn不允許為R15;
!:表示最后的地址寫回到Rn中;
reglist:可包含多於一個寄存器范圍,用“,”隔開,如{R1,R2,R6-R9},寄存器由小到大順序排列;
^:不允許在用戶模式和系統模式下運行
數據塊的傳輸-實例:
Ldr R1,=0x10000000 //傳送數據的起始地址0x10000000
LDMIB R1!,{R0,R4-R6} //從左到右加載,相當於 LDR R0,10000004 LDR R4,10000008... ...
/*傳送前地址加+4,
所以地址加4,R0=0X1000004地址里的內容,
地址加4,R4=0X10000008地址里的內容,
地址加4,R5=0X1000000C地址里的內容,
地址加4,R6=0X10000010 地址里的內容,
由於!, 最后的地址寫回到R1中,R1=0X10000010 */
Ldr R1,=0x10000000 //傳送數據的起始地址0x10000000
LDMIA R1!,{R0,R4-R6} //從左到右加載,相當於 LDR R0,10000000 LDR R4,10000004... ...
/*傳送后地址加+4,
所以R0=0X10000000地址里的內容,地址加4,
R4=0X10000004地址里的內容,地址加4,
R5=0X10000008地址里的內容,地址加4,
R6=0X1000000C 地址里的內容,地址加4,
由於!,最后的地址寫回到R1中,所以R1=0X10000010 */
LDR R1,=0x10000000 //傳送數據的起始地址0x10000000
LDR R4,=0X10
LDR R5,=0X20
LDR R6,=0X30
STMIB R1,{R4-R6} //從左到右加載,相當於STR [R4],0X10000004 STR [R5],0X10000008 .....
/*傳送前地址加+4,所以0X10000004地址=0X10,0X10000008地址=0X20,0X1000000C地址=0X30 */
Ldr R1,=0x10000000 //傳送數據的起始地址0x10000000
LDR R4,=0X10
LDR R5,=0X20
LDR R6,=0X30
STMIA R1!,{R4-R6 }
/*傳送后地址加+4,所以0X10000000地址=0X10,0X10000004地址=0X20,0X10000008地址=0X30,由於!,最后的地址寫回到R1中,所以R1=0X1000000C */
中斷實例(利用STMDB和LDMIA保護現場,然后通過LR寄存器返回)
1.先設置棧sp,用於后面使用stmdb存儲寄存器數據
2.當產生異常時,便進入中斷:
sub lr, lr, #4
//首先將lr-4,因為arm流水線,lr=當前pc+8,由於pc+4段沒有執行,所以lr=(當前pc+8)-4;
stmdb sp!, { r0-r12,lr }
//每次傳送前-4,由於遞減,所以從右往左存儲寄存器
//所以sp-4=lr,sp-8=r12,... sp-56=r0; 由於!,所以最后的地址寫回到sp中,sp=sp-56;
ldr lr, =int_return //設置返回地址
ldr pc, =EINT_Handle //進入中斷服務函數,如果中途返回就會調用pc=lr,即可執行int_return;
int_return:
ldmia sp!, { r0-r12,pc }^
//每次傳送后+4,所以從左往右加載數據到寄存器
//所以r0=sp, r1=sp+4,...pc=sp+52;由於!,所以最后地址寫回到sp中,sp=sp+56;
//此時,sp=sp+56就等於最初棧頂值,pc=lr,然后返回到異常發生前的相應位置繼續執行。
//^ ^表示將spsr的值復制到cpsr,因為異常返回后需要恢復異常發生前的工作狀態