ARM編程進(jìn)階之一-ARM匯編偽指令
ARM匯編偽指令共4條:ldr、adr、adrl、nop
本文引用地址:http://m.butianyuan.cn/article/201611/322178.htm1、ldr
首先我們來回答“基本尋址模式與基本指令”一文中提出的問題。“如果我們需要mov r0, #10000這樣的指令,應(yīng)該怎么辦?(常數(shù)10000不能在機器指令32bit中的低12bit中被表示出來)”。當(dāng)你進(jìn)行編譯的時候,“Error:All70E”的錯誤就會出現(xiàn),如下圖。
其實,這個問題很容易解決,只需要將mov r0, #10000換為ldr r0, =10000即可。為什么這樣就可以了呢?因為,這里的ldr r0, =10000并非我們已經(jīng)學(xué)過的ldr指令,而是一條偽指令,編譯器會將這條偽指令替換為:
ldr r0, [pc, #-4]
DCD 10000
DCD所分配的內(nèi)存空間中存放了整數(shù)10000,該內(nèi)存空間被稱為literal pool,中文名稱“文字池”。由于整個程序都是由編譯器編譯的(包括文字池的分配),所以很顯然編譯器能夠知道ldr指令在內(nèi)存中的地址與文字池在內(nèi)存中的位置之間的偏移量,因此編譯器就可以正確地使用以pc為基址,采用相對尋址的ldr指令將文字池中的數(shù)取出加載到寄存器r0中。由此可見,編譯器對于ldr r0, =10000這條偽指令的處理,其實質(zhì)是:
在匯編源程序時,LDR偽指令被編譯器替換成一條合適的指令和存放常數(shù)的文字池。匯編器將常量放入文字池,并使用一條程序相對偏移的LDR指令從文字池讀出常量。
由于,4byte可以存放任何int型整數(shù),這樣一來,常數(shù)就可以是任何int型整數(shù),而不再受制于12bit的限制。當(dāng)然此時的常數(shù)是存放在內(nèi)存中的,而不是存放在機器指令的32bit編碼中的。
額外說明:
a)、ldr r0, [pc, #-4]中是-4,而不是+4,是由于流水線的原因(參見“流水線對PC值的影響”一文)。今后對于流水線的這種影響,我將不再予以特別說明。
b)、從指令位置到文字池的偏移量必須小于4KB
c)、從語法上來看,與ARM指令的LDR相比,偽指令LDR的參數(shù)有“=”號,沒有“#”號
d)、如果常數(shù)能夠被12bit表示出來,例如:ldr r0, =0x100,那么,編譯器對該偽指令的處理,是使用MOV(或者M(jìn)VN)指令代替該LDR偽指令,例如:mov r0, #0x100,而不會采用ldr指令+文字池的方式。
除了 ldr 寄存器, =常數(shù) 這種形式外,還有l(wèi)dr 寄存器, =標(biāo)號 這種形式也經(jīng)常被使用,下面我就來講解這種形式的ldr偽指令。
由上圖可見:ldr pc, =InitStack這條偽指令的作用是將標(biāo)號InitStack所代表的地址賦予pc。 這里會使我們產(chǎn)生幾個疑問:
a)、為什么不使用bl InitStack,而要使用ldr pc, =InitStack?
這是因為bl指令的跳轉(zhuǎn)范圍是正負(fù)32M,而InitStack所代表的位置有可能距離ldr pc, =InitStack超過32M,此時bl就無能為力了。竟然存在這么大范圍跳轉(zhuǎn)的程序(這似乎意味著編譯出來的二進(jìn)制可執(zhí)行文件的大小會操作32M),這一點似乎令我們感到非常震驚。事實上是:大小超過32M的可執(zhí)行程序的確幾乎不可能出現(xiàn),但即使是很小的二進(jìn)制程序中也可能進(jìn)行大范圍(超過32M)的跳轉(zhuǎn),這一點在bootloader程序中幾乎是必然的。
評論