新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > arm匯編中的跳轉(zhuǎn)指令

arm匯編中的跳轉(zhuǎn)指令

作者: 時(shí)間:2016-11-20 來源:網(wǎng)絡(luò) 收藏
ARM匯編中,常有兩種跳轉(zhuǎn)方法:b跳轉(zhuǎn)指令、ldr指令向PC賦值。
我自己經(jīng)過歸納如下:
(1)b label
該 指令完成的操作是pc<-label,將label處的地址傳給pc。b跳轉(zhuǎn)指令是相對跳轉(zhuǎn),依賴當(dāng)前PC的值,偏移量是通過該指令本身的 bit[23:0]算出來的,這使得使用b指令的程序不依賴于要跳到的代碼的位置,只看指令本身。即該分支指令的二進(jìn)制碼的后24位的實(shí)際的值是相對當(dāng)前 的 R15 的值的一個(gè)偏移量;而不是一個(gè)絕對地址。它的值由匯編器來計(jì)算,它是 24 位有符號數(shù),左移兩位后有符號擴(kuò)展為 32 位,表示的有效偏移為 26 位(+/- 32 M)。
(2)ldr pc, =label
該指令是一條偽指令,將內(nèi)存中的某個(gè)數(shù)據(jù)的位置(label處)賦給PC,同樣依賴當(dāng)前PC的值,但是偏移量是那個(gè)位置(label)的連接地址(運(yùn)行時(shí)的地址),所以可以用它實(shí)現(xiàn)從Flash到RAM的程序跳轉(zhuǎn),說白了,pc是個(gè)地址數(shù)值。
偽指令LDR 常用于加載芯片外圍功能部件的寄存器地址(32 位立即數(shù)),以實(shí)現(xiàn)各種
控制操作。
如:ldr r0,=5e000000 ;將外圍某IO端口寄存器的地址賦給r0,注意該立即數(shù)前面沒有#。
---------------------------------------------------------------------------
這里講一下為什么會有l(wèi)dr 偽指令

范例demo.s:

.equSTACK_BASE,0x0c002000

.equSTACK_SIZE,0x00001000


.text
ldrsp,=STACK_BASE
ldrsl,=STACK_BASE-STACK_SIZE
ldrpc,=entry

這是一個(gè)合法的匯編文件,它把堆棧基址設(shè)為0x0c002000,棧限設(shè)為0x0c001000,然后跳到entry所標(biāo)識的C程序中執(zhí)行。

下面我們假設(shè)符號“entry”的地址為0x0c000000。

我們?nèi)绻焉厦娲a寫成:
.text
movsp,#0x0c002000
movsl,#0x0c001000
movpc,#0x0c000000
匯編器會報(bào)錯:
demo.s: Assembler messages:
demo.s:2: Error: invalid constant -- `mov sp,#0x0c002000
demo.s:3: Error: invalid constant -- `mov sl,#0x0c001000

說起這個(gè)錯誤的原因可就話長了,簡而言之是因?yàn)镽ISC有一個(gè)重要的概念就是所有指令等長。在ARM指令集中,所有指令長度為4字節(jié)(Thumb指令是2 字節(jié))。那問題就來了,4字節(jié)是不可能同時(shí)存的下指令控制碼和32位立即數(shù)的,那么我要把一個(gè)32位立即數(shù)(比如一個(gè)32位地址值)傳送給寄存器該怎么 辦?
RISC CPU提供一個(gè)通用的方法就是把地址值作為數(shù)據(jù)而不是代碼,從存儲器中相應(yīng)的位置讀入到寄存器中。像在代碼一中,將所有讀取的32位數(shù)據(jù)放到label標(biāo) 注的內(nèi)存地址中,使用ldr偽指令,從該內(nèi)存處讀出該數(shù)據(jù)。因此label相當(dāng)于一個(gè)內(nèi)存地址。如下,給出了代碼一的反匯編代碼:讓我們在Linux環(huán)境下執(zhí)行下面的命令:
arm-elf-as -o demo.o demo.s
arm-elf-objdump -D demo.o

結(jié)果:
demo.o:fileformatelf32-littlearm

Disassemblyofsection.text:

00000000<.text>:
0: e59fd004 ldrsp,[pc,#4];c<.text+0xc>
4: e59fa004 ldrsl,[pc,#4];10<.text+0x10>
8: e59ff004 ldrpc,[pc,#4];14<.text+0x14>
c: 0c002000 stceq0,cr2,[r0]
10: 0c001000 stceq0,cr1,[r0]
14:00000000 andeqr0,r0,r0
Disassemblyofsection.data:

0、4、8三行相當(dāng)于是代碼段,C,10,14相當(dāng)于是數(shù)據(jù)段,偽指令的變量定義存儲在這里。由于entry還沒連上目標(biāo)地 址,objdump反匯編會認(rèn)為是0,我們先不管它。另外兩條LDR偽指令變成了實(shí)際的LDR指令!但目標(biāo)很奇怪,都是[pc, #4]。那好我們看看[pc, #4]是什么。
我們知道pc中存放的是當(dāng)前指令的下下條指令的位置,也就是.+8。那么上面的第一條指令ldrsp,[pc,#4]中的pc就是0x8,pc+4就是0xc,而[0xc]的內(nèi)容正是0x0c002000;同理,第二條ldr指令也是如此。顯然這里L(fēng)DR偽指令采用的是RISC通用的方法。
另外從反匯編的代碼可以看出ldr偽指令中存儲的是一個(gè)相對偏移量,該偏移量是相對當(dāng)前pc值的一個(gè)偏移量。
------------------------------------------------------------------------------
(3)此外,有必要回味一下adr偽指令,U-boot中那段relocate代碼就是通過adr實(shí)現(xiàn)當(dāng)前程序是在RAM中還是flash中。
ADR指令為小范圍的地址讀取偽指令.ADR指令將基于PC相對偏移的地址值讀取到寄存器中.在匯編編譯源程序時(shí),ADR偽指令被編譯器替換成一條合適的 指令.通常,編譯器用一條ADD指令或SUB指令來實(shí)現(xiàn)該ADR偽指令的功能,若不能用一條指令實(shí)現(xiàn),則產(chǎn)生錯誤,編譯失敗.
ADR 偽指令格式如下
ADR{cond} register,exper 其中
register 加載的目標(biāo)寄存器
exper 地址表達(dá)式.當(dāng)?shù)刂分凳欠亲值佚R時(shí),取值范圍-255~255 字節(jié)之間;當(dāng)?shù)刂肥亲謱R時(shí),取值范圍-1020~1020 字節(jié)之間.對于基于PC 相對偏移的地址 值時(shí),給定范圍是相對當(dāng)前指令地址后兩個(gè)字處(因?yàn)锳RM7TDMI 為三級流水線).
ADR 偽指令舉例如下;
LOOP MOV R1,#0xF0

ADR R2,LOOP ;將LOOP 的地址放入R2
ADR R3,LOOP+4
可以用ADR 加載地址,實(shí)現(xiàn)查表:

ADR R0,DISP_TAB ;加載轉(zhuǎn)換表地址
LDRB R1,[R0,R2] ;使用R2 作為參數(shù),進(jìn)行查表

DISP_TAB
DCB 0Xc0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90

4 ldr pc,_label
_label .word label
一般ldr pc,=label就被解釋成上面這兩條指令,但是從反匯編程序中可以看出由于偏移量僅為4k,_label的定義位置要和ldr指令相距在4k以內(nèi)。借此可以實(shí)現(xiàn)大范圍地址的跳轉(zhuǎn),完成從flash到sdram的跳轉(zhuǎn)。


關(guān)鍵詞: arm匯編跳轉(zhuǎn)指

評論


技術(shù)專區(qū)

關(guān)閉