ARM指令A(yù)DR和LDR淺析
LDR R0, _START ;指將_START標(biāo)記的內(nèi)存位置的值載入到R0。
但是,ARM匯編器又為LDR賦予另一個(gè)偽指令含義:用于地址讀取。這完全是兩種不同的應(yīng)用,但都是用LDR表示,所以很容易混淆。“用于地址讀取”是指將用于表示“地址”的值寫入到寄存器中,該寄存器中存儲(chǔ)的就是地址,這非常類似于指針。LDR作為偽指令的用法是:在標(biāo)號(hào)或立即數(shù)前面加上等號(hào)表示地址。例如:
LDR R0, =_START ;表示將_START標(biāo)號(hào)所在的地址寫入R0
LDR R0, =0x12345678 ;表示將0x12345678這個(gè)地址值寫入R0
因?yàn)長DR本身既是指令又是偽指令,而比較難于理解的是偽指令LDR,所以在此先聲明,本文以下只討論LDR偽指令,所稱的LDR也只是作為偽指令使用的LDR,不要與LDR指令混淆。
當(dāng)LDR作為偽指令使用時(shí),就又引出了另一個(gè)近親,ADR偽指令。
ADR和LDR都是用于讀取地址的偽指令,都是把一個(gè)地址寫入到一個(gè)寄存器中,都是類似于指針的作用,被寫入的寄存器中的值將被用于尋址內(nèi)存。
但ADR只能使用標(biāo)號(hào),不能使用立即數(shù),也沒有等號(hào),它的典型用法是:
ADR R0, _START ;表示將_START標(biāo)號(hào)所在的地址寫入R0
但它們還有更本質(zhì)的區(qū)別,ADR是讀取相對(duì)地址,LDR是讀取絕對(duì)地址。有人會(huì)覺得這又有什么關(guān)系呢,反正就是地址的不同表示方法唄。雖然是這樣說,但是這里面關(guān)系可大了。代碼的存儲(chǔ)位置都是在連接的時(shí)候由連接文件指定的,而且在運(yùn)行時(shí)還可能會(huì)被到不同的位置。如果把一段代碼里每個(gè)涉及存儲(chǔ)地址的地方都用相對(duì)首地址的偏移量來表示,例如_START+0x800,那么這段代碼無論將來被到哪里,都能運(yùn)行無誤。但是如果每個(gè)地址都是寫死的絕對(duì)地址,例如 0x30008000,那么必須將這段代碼到內(nèi)存的該地址位置才能正確運(yùn)行。所以,看起來相對(duì)地址比絕對(duì)地址更靈活。事實(shí)也確實(shí)如此,ADR因?yàn)楦鼘R?,所以編譯出來的代碼比LDR常常更有效率。
但是,好用是要有代價(jià)的,ADR的地址讀取范圍有限且很小,LDR則可以實(shí)現(xiàn)任意地址值的讀取。為什么會(huì)這樣?還要從ADR和LDR之所以被稱為偽指令談起。偽指令就是因?yàn)樗鼈儾⒉皇菍?shí)際執(zhí)行的指令,而是匯編器要在匯編階段將它們替換成可執(zhí)行的ARM指令。那么實(shí)際執(zhí)行的是什么指令呢?
LDR后面的地址范圍如果未超出MOV的范圍,會(huì)被匯編器替換成MOV指令,如果超出了MOV范圍,則匯編器要為它再開個(gè)內(nèi)存位置,叫內(nèi)存池,專門存儲(chǔ)這個(gè)值,因?yàn)閮?nèi)存能存儲(chǔ)32位的值,從內(nèi)存池中把這個(gè)值取出來給寄存器,就實(shí)現(xiàn)了任意地址值的加載。內(nèi)存池中的數(shù)在運(yùn)行中不能變,寫的是幾就是幾,所以內(nèi)存池是LDR的優(yōu)勢,同時(shí)也是它的劣勢。再說一下為什么MOV會(huì)有范圍限制,因?yàn)锳RM指令是定長的,只能是32位,MOV指令字本身要占去幾位,剩余的位數(shù)才能給有效數(shù)據(jù)使用,所以能存儲(chǔ)在MOV指令碼里的數(shù)據(jù)肯定達(dá)不到32位,要想取32位數(shù)就只能用內(nèi)存池。ARM指令說明里面有MOV詳細(xì)的換算過程,不詳述。
ADR偽指令可以在運(yùn)行時(shí)讀取相對(duì)位置。但是有范圍限制,因?yàn)锳DR要使用相對(duì)地址就要在運(yùn)行中找出當(dāng)前地址位置,對(duì)ADR偽指令,匯編器是通過使用加減法指令來替換的。遇到ADR,編譯器會(huì)用一條ADD或SUB當(dāng)前PC寄存器和一個(gè)常數(shù)來實(shí)現(xiàn)ADR偽指令的功能,這樣就實(shí)現(xiàn)了運(yùn)行時(shí)相對(duì)當(dāng)前地址的讀取。若不能用一條指令替換,則產(chǎn)生錯(cuò)誤,編譯失敗。而這里因?yàn)锳DD和SUB又遇到了與MOV同樣的范圍問題,所以ADR不可能像LDR從內(nèi)存池取數(shù)那樣實(shí)現(xiàn)任意32位地址的讀取。確切的說,ADR的地址讀取范圍是:
當(dāng)?shù)刂分凳亲止?jié)對(duì)齊時(shí),其取指范圍為: -255 ~ 255B;
當(dāng)?shù)刂分凳亲謱?duì)齊時(shí),其取指范圍為: -1020 ~ 1020B;
如果要實(shí)現(xiàn)大一點(diǎn)的跳轉(zhuǎn),可以使用ADRL偽指令,在匯編器編譯源程序時(shí),ADRL偽指令被替換成兩條合適的指令。若不能用兩條指令實(shí)現(xiàn),則產(chǎn)生錯(cuò)誤,編譯失敗。
當(dāng)?shù)刂分凳亲止?jié)對(duì)齊時(shí),其取指范圍為: -64K~64K;
當(dāng)?shù)刂分凳亲謱?duì)齊時(shí),其取指范圍為: -256K~256K;
以此類推,雖然ARM匯編器并沒有提供能編譯成三條合適指令的ADR偽指令,但是想象一下,如果能編譯成三條,ADR讀取的地址范圍就會(huì)更大了。但那樣效率又降低了,所以還是不劃算,ARM果斷放棄,真有那種需要,那就自己換算地址去寫吧。匯編雖然很低層,還不足夠低層。
評(píng)論