天天看點

B BL指令淺析

B或BL指令引起處理器轉移到“子程式名”處開始執行。兩者的不同之處在于BL指令在轉移到子程式執行之前,将其下一條指令的位址拷貝到R14(LR,鍊 接寄存器)。由于BL指令儲存了下條指令的位址,是以使用指令“MOV PC ,LR”即可實作子程式的傳回。而B指令則無法實作子程式的傳回,隻能實作單純的跳轉。使用者在程式設計的時候,可根據具體應用選用合适的子程式調用語句。

   AREA Init,CODE,READONLY  

;該僞指令定義了一個代碼段,段名為Init,屬性隻讀

ENTRY                      ;程式的入口點辨別

.

.

bl delay                 ;調用延遲

.

.

mov pc,lr                 ;傳回

下面的在BLOG中看到覺得講得比較詳細就拷過來了

ARM彙編指令的一些總結

ARM彙編指令很多,但是真正常用的不是很多,而且需要認真琢磨的又更少了。

比較有用的是MOV B BL LDR STR

還是通過具體彙編代碼來學習吧。

@ disable watch dog timer      

mov   r1, #0x53000000   //立即數尋址方式 

mov   r2, #0x0 

str   r2, [r1]        

立即數尋址方式,立即數要求以“#”作字首,對于十六進制的數,還要求在#後面加上0x或者&。STR是比較重要的指令了,跟它對應的是LDR。 ARM指令集是加載/存儲型的,也就是說它隻處理在寄存器中的資料。那麼對于系統存儲器的通路就經常用到STR和LDR了。STR是把寄存器上的資料傳輸 到指定位址的存儲器上。它的格式我個人認為很特殊:

STR(條件) 源寄存器,<存儲器位址>

比如 STR R0, [R1] ,意思是R0-> [R1],它把源寄存器寫在前面,跟MOV、LDR都相反。

LDR應該是非常常見了。LDR就是把資料從存儲器傳輸到寄存器上。而且有個僞指令也是LDR,是以我有個百思不得其解的問題。看這段代碼:

mov r1, #GPIO_CTL_BASE 

add   r1, r1, #oGPIO_F 

ldr   r2,=0x55aa   // 0x55aa是個立即數啊,前面加個=幹什麼? 

對于當中的ldr 那句,我就不明白了,如果你把=去掉,是不能通過編譯的。我查了一些資料,個人感覺知道了原因:這個=應該表示LDR不是ARM指令,而是僞指令。作為僞指令的時候,LDR的格式如下:

LDR 寄存器, =數字常量/Label

它的作用是把一個32位的位址或者常量調入寄存器。嗬嗬,那大家可能會問,

“MOV r2,#0x55aa”也可以啊。應該是這樣的。不過,LDR是僞指令啊,也就是說編譯時編譯器會處理它的。怎麼處理的呢?——規則如下:如果該數字常量 在MOV指令範圍内,彙編器會把這個指令作為MOV。如果不在MOV範圍中,彙編器把該常量放在程式後面,用LDR來讀取,PC和該常量的偏移量不能超過 4KB。

然後說一下跳轉指令。ARM有兩種跳轉方式。

(1) mov pc <跳轉位址〉

這種向程式計數器PC直接寫跳轉位址,能在4GB連續空間内任意跳轉。

(2)通過 B BL BLX BX 可以完成在目前指令向前或者向後32MB的位址空間的跳轉(為什麼是32MB呢?寄存器是32位的,此時的值是24位有符号數,是以32MB)。

B是最簡單的跳轉指令。要注意的是,跳轉指令的實際值不是絕對位址,而是相對位址——是相對目前PC值的一個偏移量,它的值由彙編器計算得出。

BL非常常用。它在跳轉之前會在寄存器LR(R14)中儲存PC的目前内容。BL的經典用法如下:

bl NEXT   ; 跳轉到NEXT 

…… 

NEXT 

…… 

mov pc, lr    ; 從子程式傳回。 

最後提一下Thumb指令。ARM體系結構還支援16位的Thumb指令集。Thumb指令集是ARM指令集的子集,它保留了32位代碼優勢的同時還大大 節省了存儲空間。由于Thumb指令集的長度隻有16位,是以它的指令比較多。它和ARM各有自己的應用場合。對于系統性能有較高要求,應使用32位存儲 系統和ARM指令集;對于系統成本和功耗有較高要求,應使用16位存儲系統和ARM指令集。 

對ARM異常(Exceptions)的了解

分類:技術筆記

畢設筆記

1.對ARM異常(Exceptions)的了解

所有的系統引導程式前面中會有一段類似的代碼,如下:

.globl _start                    ;系統複位位置

_start: b       reset            ;各個異常向量對應的跳轉代碼

ldr     pc, _undefined_instruction ;未定義的指令異常

ldr     pc, _software_interrupt     ;軟體中斷異常

ldr     pc, _prefetch_abort          ;記憶體操作異常

ldr     pc, _data_abort               ;資料異常

ldr     pc, _not_used                  ;未使用

ldr     pc, _irq                       ;慢速中斷異常

ldr     pc, _fiq                       ;快速中斷異常

從中我們可以看出,ARM支援7種異常。問題時發生了異常後ARM是如何響應的呢?第一個複位異常很好了解,它放在0x0的位置,一上電就執行它, 而且我們的程式總是從複位異常處理程式開始執行的,是以複位異常處理程式不需要傳回。那麼怎麼會執行到後面幾個異常處理函數呢?

看看書後,明白了ARM對異常的響應過程,于是就能夠回答以前的這個疑問。

當一個異常出現以後,ARM會自動執行以下幾個步驟:

(1)把下一條指令的位址放到連接配接寄存器LR(通常是R14),這樣就能夠在處理異常傳回時從正确的位置繼續執行。

(2)将相應的CPSR(目前程式狀态寄存器)複制到SPSR(備份的程式狀态寄存器)中。從異常退出的時候,就可以由SPSR來恢複CPSR。

(3) 根據異常類型,強制設定CPSR的運作模式位。

(4)強制PC(程式計數器)從相關異常向量位址取出下一條指令執行,進而跳轉到相應的異常處理程式中。

至于這些異常類型各代表什麼,我也沒有深究。因為平常就關心reset了,也沒有必要弄清楚。

ARM規定了異常向量的位址:

b       reset            ; 複位 0x0

ldr pc, _undefined_instruction ;未定義的指令異常 0x4

ldr     pc, _software_interrupt     ;軟體中斷異常    0x8

ldr     pc, _prefetch_abort          ;預取指令    0xc

ldr     pc, _data_abort               ;資料        0x10

ldr     pc, _not_used                  ;未使用      0x14

ldr     pc, _irq                       ;慢速中斷異常   0x18

ldr   pc, _fiq                       ;快速中斷異常    0x1c

這樣了解這段代碼就非常簡單了。碰到異常時,PC會被強制設定為對應的異常向量,進而跳轉到相應的處理程式,然後再傳回到主程式繼續執行。

這些引導程式的中斷向量,是僅供引導程式自己使用的,一旦引導程式引導Linux核心完畢後,會使用自己的中斷向量。

嗬嗬,這又有問題了。比如,ARM發生中斷(irq)的時候,總是會跑到0x18上執行啊。那Linux核心又怎麼能使用自己的中斷向量呢?原因在于 Linux核心采用頁式存儲管理。開通MMU的頁面映射以後,CPU所發出的位址就是虛拟位址而不是實體位址。就Linux核心而言,虛拟位址0x18經 過映射以後的實體位址就是0xc000 0018。是以Linux把中斷向量放到0xc000 0018就可以了。

MMU的兩個主要作用:

(1)安全性:規定通路權限

(2) 提供位址空間:把不連續的空間轉換成連續的。

第2點是不是實作頁式存儲的意思?

.globl _start ;系統複位位置

_start: b reset ;各個異常向量對應的跳轉代碼

ldr pc, _undefined_instruction ;未定義的指令異常

……

_undefined_instruction :

.word undefined_instruction

也許有人會有疑問,同樣是跳轉指令,為什麼第一句用的是 b reset;

而後面的幾個都是用ldr?

為了了解這個問題,我們以未定義的指令異常為例。

當發生了這個異常後,CPU總是跳轉到0x4,這個位址是虛拟位址,它映射到哪個實體位址

取決于具體的映射。

ldr pc, _undefined_instruction 

相對尋址,跳轉到标号_undefined_instruction,然而真正的跳轉位址其實是_undefined_instruction的内容——undefined_instruction。那句.word的相當于:

_undefined_instruction dw undefined_instruction (詳見畢設筆記3)。

這個位址undefined_instruction到底有多遠就難說了,也許和标号_undefined_instruction在同一個頁面,也許在 很遠的地方。不過除了reset,其他的異常是MMU開始工作之後才可能發生的,是以undefined_instruction 的位址也經過了MMU的映射。

在剛加電的時候,CPU從0x0開始執行,MMU還沒有開始工作,此時的虛拟位址和實體位址相同;另一方面,重新開機在MMU開始工作後也有可能發生,如果reset也用ldr就有問題了,因為這時候虛拟位址和實體位址完全不同。

是以,之是以reset用b,就是因為reset在MMU建立前後都有可能發生,而其他的異常隻有在MMU建立之後才會發生。用b reset,reset子程式與reset向量在同一頁面,這樣就不會有問題(b是相對跳轉的)。如果二者相距太遠,那麼編譯器會報錯的

繼續閱讀