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