arm匯編中的跳轉(zhuǎn)指令
我自己經(jīng)過(guò)歸納如下:
(1)b label
該 指令完成的操作是pc<-label,將label處的地址傳給pc。b跳轉(zhuǎn)指令是相對(duì)跳轉(zhuǎn),依賴(lài)當(dāng)前PC的值,偏移量是通過(guò)該指令本身的 bit[23:0]算出來(lái)的,這使得使用b指令的程序不依賴(lài)于要跳到的代碼的位置,只看指令本身。即該分支指令的二進(jìn)制碼的后24位的實(shí)際的值是相對(duì)當(dāng)前 的 R15 的值的一個(gè)偏移量;而不是一個(gè)絕對(duì)地址。它的值由匯編器來(lái)計(jì)算,它是 24 位有符號(hào)數(shù),左移兩位后有符號(hào)擴(kuò)展為 32 位,表示的有效偏移為 26 位(+/- 32 M)。
該 指令完成的操作是pc<-label,將label處的地址傳給pc。b跳轉(zhuǎn)指令是相對(duì)跳轉(zhuǎn),依賴(lài)當(dāng)前PC的值,偏移量是通過(guò)該指令本身的 bit[23:0]算出來(lái)的,這使得使用b指令的程序不依賴(lài)于要跳到的代碼的位置,只看指令本身。即該分支指令的二進(jìn)制碼的后24位的實(shí)際的值是相對(duì)當(dāng)前 的 R15 的值的一個(gè)偏移量;而不是一個(gè)絕對(duì)地址。它的值由匯編器來(lái)計(jì)算,它是 24 位有符號(hào)數(shù),左移兩位后有符號(hào)擴(kuò)展為 32 位,表示的有效偏移為 26 位(+/- 32 M)。
(2)ldr pc, =label
該指令是一條偽指令,將內(nèi)存中的某個(gè)數(shù)據(jù)的位置(label處)賦給PC,同樣依賴(lài)當(dāng)前PC的值,但是偏移量是那個(gè)位置(label)的連接地址(運(yùn)行時(shí)的地址),所以可以用它實(shí)現(xiàn)從Flash到RAM的程序跳轉(zhuǎn),說(shuō)白了,pc是個(gè)地址數(shù)值。
偽指令LDR 常用于加載芯片外圍功能部件的寄存器地址(32 位立即數(shù)),以實(shí)現(xiàn)各種
控制操作。
如:ldr r0,=5e000000 ;將外圍某IO端口寄存器的地址賦給r0,注意該立即數(shù)前面沒(méi)有#。
---------------------------------------------------------------------------
這里講一下為什么會(huì)有l(wèi)dr 偽指令
范例demo.s:
.equSTACK_BASE,0x0c002000
.equSTACK_SIZE,0x00001000
.text
ldrsp,=STACK_BASE
ldrsl,=STACK_BASE-STACK_SIZE
ldrpc,=entry
這是一個(gè)合法的匯編文件,它把堆?;吩O(shè)為0x0c002000,棧限設(shè)為0x0c001000,然后跳到entry所標(biāo)識(shí)的C程序中執(zhí)行。
下面我們假設(shè)符號(hào)“entry”的地址為0x0c000000。
我們?nèi)绻焉厦娲a寫(xiě)成:匯編器會(huì)報(bào)錯(cuò):
demo.s: Assembler messages:
demo.s:2: Error: invalid constant -- `mov sp,#0x0c002000
demo.s:3: Error: invalid constant -- `mov sl,#0x0c001000
說(shuō)起這個(gè)錯(cuò)誤的原因可就話(huà)長(zhǎng)了,簡(jiǎn)而言之是因?yàn)镽ISC有一個(gè)重要的概念就是所有指令等長(zhǎng)。在ARM指令集中,所有指令長(zhǎng)度為4字節(jié)(Thumb指令是2 字節(jié))。那問(wèn)題就來(lái)了,4字節(jié)是不可能同時(shí)存的下指令控制碼和32位立即數(shù)的,那么我要把一個(gè)32位立即數(shù)(比如一個(gè)32位地址值)傳送給寄存器該怎么 辦?
RISC CPU提供一個(gè)通用的方法就是把地址值作為數(shù)據(jù)而不是代碼,從存儲(chǔ)器中相應(yīng)的位置讀入到寄存器中。像在代碼一中,將所有讀取的32位數(shù)據(jù)放到label標(biāo) 注的內(nèi)存地址中,使用ldr偽指令,從該內(nèi)存處讀出該數(shù)據(jù)。因此label相當(dāng)于一個(gè)內(nèi)存地址。如下,給出了代碼一的反匯編代碼:讓我們?cè)贚inux環(huán)境下執(zhí)行下面的命令:
arm-elf-as -o demo.o demo.s
arm-elf-objdump -D demo.o
結(jié)果:
0、4、8三行相當(dāng)于是代碼段,C,10,14相當(dāng)于是數(shù)據(jù)段,偽指令的變量定義存儲(chǔ)在這里。由于entry還沒(méi)連上目標(biāo)地 址,objdump反匯編會(huì)認(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偽指令中存儲(chǔ)的是一個(gè)相對(duì)偏移量,該偏移量是相對(duì)當(dāng)前pc值的一個(gè)偏移量。
------------------------------------------------------------------------------
該指令是一條偽指令,將內(nèi)存中的某個(gè)數(shù)據(jù)的位置(label處)賦給PC,同樣依賴(lài)當(dāng)前PC的值,但是偏移量是那個(gè)位置(label)的連接地址(運(yùn)行時(shí)的地址),所以可以用它實(shí)現(xiàn)從Flash到RAM的程序跳轉(zhuǎn),說(shuō)白了,pc是個(gè)地址數(shù)值。
偽指令LDR 常用于加載芯片外圍功能部件的寄存器地址(32 位立即數(shù)),以實(shí)現(xiàn)各種
控制操作。
如:ldr r0,=5e000000 ;將外圍某IO端口寄存器的地址賦給r0,注意該立即數(shù)前面沒(méi)有#。
---------------------------------------------------------------------------
這里講一下為什么會(huì)有l(wèi)dr 偽指令
范例demo.s:
.equSTACK_BASE,0x0c002000
.equSTACK_SIZE,0x00001000
.text
ldrsp,=STACK_BASE
ldrsl,=STACK_BASE-STACK_SIZE
ldrpc,=entry
這是一個(gè)合法的匯編文件,它把堆?;吩O(shè)為0x0c002000,棧限設(shè)為0x0c001000,然后跳到entry所標(biāo)識(shí)的C程序中執(zhí)行。
下面我們假設(shè)符號(hào)“entry”的地址為0x0c000000。
我們?nèi)绻焉厦娲a寫(xiě)成:
.text
movsp,#0x0c002000
movsl,#0x0c001000
movpc,#0x0c000000
movsp,#0x0c002000
movsl,#0x0c001000
movpc,#0x0c000000
demo.s: Assembler messages:
demo.s:2: Error: invalid constant -- `mov sp,#0x0c002000
demo.s:3: Error: invalid constant -- `mov sl,#0x0c001000
說(shuō)起這個(gè)錯(cuò)誤的原因可就話(huà)長(zhǎng)了,簡(jiǎn)而言之是因?yàn)镽ISC有一個(gè)重要的概念就是所有指令等長(zhǎng)。在ARM指令集中,所有指令長(zhǎng)度為4字節(jié)(Thumb指令是2 字節(jié))。那問(wèn)題就來(lái)了,4字節(jié)是不可能同時(shí)存的下指令控制碼和32位立即數(shù)的,那么我要把一個(gè)32位立即數(shù)(比如一個(gè)32位地址值)傳送給寄存器該怎么 辦?
RISC CPU提供一個(gè)通用的方法就是把地址值作為數(shù)據(jù)而不是代碼,從存儲(chǔ)器中相應(yīng)的位置讀入到寄存器中。像在代碼一中,將所有讀取的32位數(shù)據(jù)放到label標(biāo) 注的內(nèi)存地址中,使用ldr偽指令,從該內(nèi)存處讀出該數(shù)據(jù)。因此label相當(dāng)于一個(gè)內(nèi)存地址。如下,給出了代碼一的反匯編代碼:讓我們?cè)贚inux環(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:
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ù)段,偽指令的變量定義存儲(chǔ)在這里。由于entry還沒(méi)連上目標(biāo)地 址,objdump反匯編會(huì)認(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偽指令中存儲(chǔ)的是一個(gè)相對(duì)偏移量,該偏移量是相對(duì)當(dāng)前pc值的一個(gè)偏移量。
------------------------------------------------------------------------------
(3)此外,有必要回味一下adr偽指令,U-boot中那段relocate代碼就是通過(guò)adr實(shí)現(xiàn)當(dāng)前程序是在RAM中還是flash中。
ADR指令為小范圍的地址讀取偽指令.ADR指令將基于PC相對(duì)偏移的地址值讀取到寄存器中.在匯編編譯源程序時(shí),ADR偽指令被編譯器替換成一條合適的 指令.通常,編譯器用一條ADD指令或SUB指令來(lái)實(shí)現(xiàn)該ADR偽指令的功能,若不能用一條指令實(shí)現(xiàn),則產(chǎn)生錯(cuò)誤,編譯失敗.
ADR 偽指令格式如下
ADR{cond} register,exper 其中
register 加載的目標(biāo)寄存器
exper 地址表達(dá)式.當(dāng)?shù)刂分凳欠亲值佚R時(shí),取值范圍-255~255 字節(jié)之間;當(dāng)?shù)刂肥亲謱?duì)齊時(shí),取值范圍-1020~1020 字節(jié)之間.對(duì)于基于PC 相對(duì)偏移的地址 值時(shí),給定范圍是相對(duì)當(dāng)前指令地址后兩個(gè)字處(因?yàn)锳RM7TDMI 為三級(jí)流水線(xiàn)).
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就被解釋成上面這兩條指令,但是從反匯編程序中可以看出由于偏移量?jī)H為4k,_label的定義位置要和ldr指令相距在4k以?xún)?nèi)。借此可以實(shí)現(xiàn)大范圍地址的跳轉(zhuǎn),完成從flash到sdram的跳轉(zhuǎn)。
ADR指令為小范圍的地址讀取偽指令.ADR指令將基于PC相對(duì)偏移的地址值讀取到寄存器中.在匯編編譯源程序時(shí),ADR偽指令被編譯器替換成一條合適的 指令.通常,編譯器用一條ADD指令或SUB指令來(lái)實(shí)現(xiàn)該ADR偽指令的功能,若不能用一條指令實(shí)現(xiàn),則產(chǎn)生錯(cuò)誤,編譯失敗.
ADR 偽指令格式如下
ADR{cond} register,exper 其中
register 加載的目標(biāo)寄存器
exper 地址表達(dá)式.當(dāng)?shù)刂分凳欠亲值佚R時(shí),取值范圍-255~255 字節(jié)之間;當(dāng)?shù)刂肥亲謱?duì)齊時(shí),取值范圍-1020~1020 字節(jié)之間.對(duì)于基于PC 相對(duì)偏移的地址 值時(shí),給定范圍是相對(duì)當(dāng)前指令地址后兩個(gè)字處(因?yàn)锳RM7TDMI 為三級(jí)流水線(xiàn)).
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就被解釋成上面這兩條指令,但是從反匯編程序中可以看出由于偏移量?jī)H為4k,_label的定義位置要和ldr指令相距在4k以?xún)?nèi)。借此可以實(shí)現(xiàn)大范圍地址的跳轉(zhuǎn),完成從flash到sdram的跳轉(zhuǎn)。
評(píng)論