STMFD
- ST - store
- M - Multiple
- F - FULL
- D - Descending
LDMFD
- LD - Load
- M - Multiple
- F - FULL
- D - Descending
棧指針通臣柙蓿可以指向不同的位置佣谐。棧指針指向棧頂元素(即最后一個入棧的數(shù)據(jù)元素)時稱為FULL棧肚吏;棧指針指向與棧頂元素相鄰的一個可用書局單元時稱為EMPTY棧方妖。
數(shù)據(jù)棧的增長方向也可以不同。當數(shù)據(jù)棧向內(nèi)存地址減小的方向增長時罚攀,稱為Descending棧党觅;當數(shù)據(jù)棧向內(nèi)存地址增加的方向增長時,稱為 Ascending棧
綜合上面兩點斋泄,可以存在以下四種數(shù)據(jù)棧:
- FD - Full Descending
- ED - Empty Descending
- FA - Full Ascending
- EA - Empty Ascending
因此實際上存在下面這些批量load/save指令:
LDMFA, LDMFD, LDMEA, LDMED
STMED, STMEA, STMFD, STMFA
給定數(shù)據(jù)棧對應著的特定批量load/save指令杯瞻,也決定了地址變化方式:
比如FD棧,對應的批量傳送指令是LDMFD/STMFD炫掐,對應的地址變化方式是:
- IA(事后遞增方式)
- DB(事先遞減方式)
STMFD SP!, {R0~R7, LR}
start_address = sp - 9 * 4 //先壓棧魁莉,然后增長sp
end_address = sp - 4
把寄存器r0~r7和LR共9個寄存器,存儲到start_address開始, 到end_address結(jié)束的棧中旗唁,并且修改SP的值(SP變衅枧ā),相當于壓棧检疫。
LDMFD SP!, {R0~R7, LR}
start_address = SP
end_address = SP + 9 * 4
把堆棧從start_address開始讶请,到end_address內(nèi)的值恢復到寄存器R0, R1... R7和LR中,并修改SP的值(SP變大)屎媳,相當于出棧夺溢。
首先一句話說一下stmdb和ldmia指令的作用:
stmdb和ldmia指令一般配對使用,stmdb用于將寄存器壓棧烛谊,ldmia用于將寄存器彈出棧风响,作用是保存使用到的寄存器。
ARM指令的多數(shù)據(jù)傳輸(STM晒来、LDM)中钞诡,提到:多寄存器的Load和Store指令分為2組:一組用于數(shù)據(jù)的存儲與讀取,對應于IA湃崩、IB荧降、DA、DB攒读,一組用于堆棧操作朵诫,對應于FD、ED薄扁、FA剪返、EA,兩組中對應的指令含義相同邓梅。
即:
STMIB(地址先增而后完成操作)脱盲、STMFA(滿遞增堆棧);
STMIA(完成操作而后地址遞增)日缨、STMEA(空遞增堆棧)钱反;
STMDB(地址先減而后完成操作)、STMFD(滿遞減堆棧)匣距;
STMDA(完成操作而后地址遞減)面哥、STMED(空遞減堆棧)。
上述各組2個指令含義相同只是適用場合不同毅待,同理有:
LDMIB尚卫、LDMED;
LDMIA尸红、LDMFD吱涉;
LDMDB刹泄、LDMEA;
LDMDA怎爵、LDMFA循签。
IA模式表示:每次傳送后地址+4;(After Increase)DB模式表示:每次傳送前地址-4疙咸;(Before Decrease)多寄存器加載/存儲指令共有8種模式(4個用與數(shù)據(jù)塊的傳輸县匠,4個用于棧操作)
舉例一:
指令:stmdb sp!,{r0-r12,lr}
含義:sp = sp - 4,先壓lr撒轮,sp = lr(即將lr中的內(nèi)容放入sp所指的內(nèi)存地址)乞旦。sp = sp - 4,再壓r12题山,sp = r12兰粉。sp = sp - 4,再壓r11顶瞳,sp = r11......sp = sp - 4玖姑,最后壓r0,sp = r0慨菱。
如果想要將r0-r12和lr彈出焰络,可以用ldmia指令:
指令:ldmia sp!,{r0-r12,lr}
舉例二:
STMIA, 比如當前r0指向的內(nèi)存地址是 0x1000符喝,STMIA R0!,{R1-R7} 就是 首先把r1存入 0x1000,然后r2存入0x1004闪彼,然后r3存入0x1008,如果是32位的處理器就是每次加4個字節(jié)协饲,以此類推把 r1-r7按照遞增的地址存入畏腕,這個r0!就是從r0的地址開始存的意思。STMDB則是地址從r0開始減少茉稠,依次存儲描馅。
第二部分代碼說明:
先看個例子:
void test2(int a,int b,int c)
{
int k=a,j=b,m=c;
}
GCC反匯編:
00000064 <test2>:
mov ip, sp //IP=SP;保存SP
stmdb sp!, {fp, ip, lr, pc} //先對SP減4,再對fp而线,ip铭污,lr,pc壓棧吞获。---------1
sub fp, ip, #4 ; 0x4 //fp=ip-4况凉;此時fp指向棧里面的“fp”
sub sp, sp, #24 ; 0x18 //分配空間
str r0, [fp, #-28] //
str r1, [fp, #-32] //
str r2, [fp, #-36] //參數(shù)壓棧
ldr r3, [fp, #-28] //
str r3, [fp, #-24] //
ldr r3, [fp, #-32] //
str r3, [fp, #-20] //
ldr r3, [fp, #-36] //
str r3, [fp, #-16] //
sub sp, fp, #12 ; 0xc //sp=fp-12谚鄙;此時sp指向棧里面的lr
ldmia sp, {fp, sp, pc} //彈棧pc=lr各拷,sp=ip,fp=fp闷营。然后地址加4---------1
匯編基礎:
stmdb sp!, {fp, ip, lr, pc}
//sp=sp-4,sp=pc;先壓PC
//sp=sp-4,sp=lr;再壓lr
//sp=sp-4,sp=ip;再壓ip
//sp=sp-4,sp=fp;再壓fp
ldmia sp, {fp, sp, pc}
//和stmdb成對使用烤黍,
//fp=sp,sp=sp+4;先彈fp
//sp=sp,sp=sp+4;先彈sp知市,此處的彈出不會影響sp,因為ldmia是一個機器周期執(zhí)行完的速蕊。
//pc=sp,sp=sp+4;先彈pc
LDRH R0, [R13, #0xC] //加載無符號半字數(shù)據(jù)嫂丙,即低16位
LDRB R0, [R13, #0x4] //加載一字節(jié)數(shù)據(jù),即低8位规哲。
注意1:R11=fp;R12=ip;R13=SP跟啤;R14=LR;R15=PC唉锌;R0,R1,R2用于傳遞參數(shù)和存放函數(shù)返回值隅肥。
**注意2: ** 低地址的寄存器被壓入低地址內(nèi)存中,也就是說如果向下增長袄简,高地址寄存器先壓腥放,向上增長測試低地址先壓。
注意3:根據(jù)“ARM-thumb 過程調(diào)用標準”:
- r0-r3 用作傳入函數(shù)參數(shù)绿语,傳出函數(shù)返回值秃症。在子程序調(diào)用之間,可以將 r0-r3 用于任何用途吕粹。被調(diào)用函數(shù)在返回之前不必恢復 r0-r3种柑。---如果調(diào)用函數(shù)需要再次使用 r0-r3 的內(nèi)容,則它必須保留這些內(nèi)容匹耕。
- r4-r11 被用來存放函數(shù)的局部變量莹规。如果被調(diào)用函數(shù)使用了這些寄存器,它在返回之前必須恢復這些寄存器的值泌神。
- r12 是內(nèi)部調(diào)用暫時寄存器 ip良漱。它在過程鏈接膠合代碼(例如,交互操作膠合代碼)中用于此角色欢际。在過程調(diào)用之間母市,可以將它用于任何用途。被調(diào)用函數(shù)在返回之前不必恢復 r12损趋。
- 寄存器 r13 是棧指針 sp患久。它不能用于任何其它用途。sp 中存放的值在退出被調(diào)用函數(shù)時必須與進入時的值相同浑槽。
- 寄存器 r14 是鏈接寄存器 lr蒋失。如果您保存了返回地址,則可以在調(diào)用之間將 r14 用于其它用途桐玻,程序返回時要恢復
- 寄存器 r15 是程序計數(shù)器 PC篙挽。它不能用于任何其它用途。
- 在中斷程序中镊靴,所有的寄存器都必須保護铣卡,編譯器會自動保護R4~R11链韭,所以一般你自己只要在程序的開頭
sub lr,lr,#4
stmfd sp!,{r0-r3,r12,lr};
// 保護R0~R3煮落,R12,LR就可以了敞峭,除非你用匯編人為的去改變R4~R11的值。(具體去看UCOS os_cpu_a.S中的IRQ中斷的代碼)
補充:
寄存器名字
Reg # APCS 意義
R0 a1 工作寄存器
R1 a2 "
R2 a3 "
R3 a4 "
R4 v1 必須保護
R5 v2 "
R6 v3 "
R7 v4 "
R8 v5 "
R9 v6 "
R10 sl 棧限制
R11 fp 楨指針
R12 ip
R13 sp 棧指針
R14 lr 連接寄存器
R15 pc 程序計數(shù)器
回溯結(jié)構(gòu)
寄存器 fp (楨指針)應當是零或者是指向棽醭穑回溯結(jié)構(gòu)的列表中的最后一個結(jié)構(gòu)旋讹,提供了一種追溯程序的方式,來反向跟蹤調(diào)用的函數(shù)轿衔。
回溯結(jié)構(gòu)是:
地址高端
保存代碼指針 [fp] fp 指向這里
返回 lr 值 [fp, #-4]
返回 sp 值 [fp, #-8]
返回 fp 值 [fp, #-12] 指向下一個結(jié)構(gòu)
[保存的 sl]
[保存的 v6]
[保存的 v5]
[保存的 v4]
[保存的 v3]
[保存的 v2]
[保存的 v1]
[保存的 a4]
[保存的 a3]
[保存的 a2]
[保存的 a1]
[保存的 f7] 三個字
[保存的 f6] 三個字
[保存的 f5] 三個字
[保存的 f4] 三個字
pc 總是包含下一個要被執(zhí)行的指令的位置骗村。
lr (總是)包含著退出時要裝載到 pc 中的值。在 26-bit 位代碼中它還包含著 PSR呀枢。
sp 指向當前的棧塊(chunk)限制胚股,或它的上面。這是用于復制臨時數(shù)據(jù)裙秋、寄存器和類似的東西到其中的地方琅拌。在 RISC OS 下,你有可選擇的至少 256 字節(jié)來擴展它摘刑。
fp 要么是零进宝,要么指向回溯結(jié)構(gòu)的最當前的部分。