ARM編程進階之一-ARM匯編偽指令
b)、編譯器是如何得出InitStack所代表的地址是0x64?
編譯器知道MOV R0, LR這條指令相對于整個程序的第1條指令的偏移量為0x64;同時又知道這個程序將來在內存中的運行地址為0x0,所以編譯器在編譯的時候(不是程序運行的時候)就可以確定InitStack所代表的地址為0x64+0x0=0x64。那么編譯器又是如何知道“程序將來在內存中的運行地址”的呢?其實,這個“程序將來在內存中的運行地址”,我通常稱它為“程序的期望運行地址”,簡稱“運行地址”,以后我也將這樣稱呼它。它其實是在編譯程序前由程序員告知編譯器的。
c)、如果將來程序并沒有運行在它的運行地址處,很顯然這個程序就會出問題。如何解決?
出問題的原因,顯然是由于ldr偽指令使用的絕對地址。所以,解決的辦法就是使用相對地址,進行相對尋址。這就要用到我們下面要介紹的另一條偽指令adr
2、adr
ADR偽指令的作用與LDR偽指令的作用相同,都是將標號所代表的地址賦予寄存器,不過2者的實現(xiàn)機制是完全不同的:ldr采用絕對地址,adr采用相對地址。
ADR偽指令將基于PC相對偏移的地址值讀取到寄存器中。在匯編源程序時,ADR偽指令被編譯器替換成一條合適的指令。通常,編譯器用一條ADD指令或SUB指令來實現(xiàn)該ADR偽指令的功能。
很顯然,由于將ADR R0, Delay替換為ADD r0, pc, #0x3c,而它是以當前指令的地址(pc的值)進行相對地址計算的。所以即使將來程序沒有實際運行在運行地址處,也不會有問題。
當然,這里還有2個問題:
a)、既然adr和ldr完成類似的功能,adr又能避免絕對地址的問題,還要ldr偽指令有何用?
這主要是因為,adr偽指令要求標號與adr偽指令必須在同一個段中(段的概念參見“ARM匯編偽操作”一文),而ldr偽指令則沒有這樣的要求。
b)、add r0, pc, #0x3c中的常數(shù)0x3c是放在機器指令12bit中的立即數(shù),這個立即數(shù)有可能不能被12bit表示出來。此時編譯會產生錯誤。如果出現(xiàn)這樣的情況,又應該如何辦?
使用下面要講的偽指令adrl
3、adrl
在匯編源程序時,ADRL偽指令被編譯器替換成兩條合適的指令。其本質是:將偏移量這個立即數(shù)(可能不能被12bit表示出來)拆分為2個可以被12bit表示的立即數(shù),然后用2條add(或sub)指令來替換adrl偽指令。
當然你會問,如果那個立即數(shù)非常特殊,無論如何也拆分不成2個可以被12bit表示的立即數(shù)(也就是說需要拆分為3個甚至更多的數(shù)),那又應該如何辦?關于這個問題,我在這里不予回答,不過你要記住一句話,如果機器智能到啥都能做的話,你作為程序員就失業(yè)了!哈哈!
4、nop
NOP是no operation的意思,就是CPU不做任何事的意思。這里千萬要明白,CPU一旦上電就將永不停歇地運行,絕不可能有一條指令能另CPU什么都不做。所以,該偽指令在匯編時將會被代替成“MOV R0,R0”指令。
NOP主要用于短延時操作,關于這一點,這里我要多說幾句。與應用程序不同,匯編程序通常要用于控制硬件,例如,你需要使用匯編程序要求某個硬件執(zhí)行某個操作(例如:初始化nandflash),并要在該操作完成后才能進行后面的操作,而硬件從接到命令到完成該操作需要一段時間(該時間很短,但對CPU而言卻較長,通常需要幾個指令周期)。此時,程序員就必須在發(fā)出命令的指令和后續(xù)操作之間做延時,通常的做法就是加入幾個NOP偽操作。
附:ldr指令與ldr偽指令的4種形式(這4種形式,極其容易讓初學者困惑,所以在此集中列出)
ldr r0, [r1] | 指令,將內存存放的內容加載到r0中 |
ldr r0, label | 指令,將標號label所代表的內存地址處存放的內容加載到r0中 |
ldr r0, =10000 | 偽指令,將常數(shù)10000賦予r0(采用ldr指令+文字池的方式實現(xiàn)) |
ldr r0, =lable | 偽指令,將標號label所代表的內存地址賦予r0 |
評論