新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > ARM匯編 MOV PC,LR

        ARM匯編 MOV PC,LR

        作者: 時間:2016-11-09 來源:網絡 收藏

        終于明白這個LR寄存器了

        看下面這個ARM匯編

        本文引用地址:http://www.104case.com/article/201611/317869.htm

        BL NEXT ;跳轉到子程序

        ......... ;NEXT處執行

        NEXT

        ..........

        MOV PC,LR ;從子程序返回

        這里的BL是跳轉的意思,LR(R14)保存了返回地址

        PC(R15)是當前地址,把LR給PC就是從子程序返回

        這里有一下總結

        首先

        1.SP(R13) LR(R14)PC(R15)

        2.lr(r14)的作用問題,這個lr一般來說有兩個作用:
        1》.當使用bl或者blx跳轉到子過程的時候,r14保存了返回地址,可以在調用過程結尾恢復。
        2》.異常中斷發生時,這個異常模式特定的物理R14被設置成該異常模式將要返回的地址。

        另外注意pc,在調試的時候顯示的是當前指令地址,而用mov lr,pc的時候lr保存的是此指令向后數兩條指令的地址,大家可以試一下用mov pc,pc,結果得到的是跳轉兩條指令,這個原因是由于arm的流水線造成的,預取兩條指令的結果.

        3.》我以前看書不懂的地方

        子程序返回的三種方法

        現在總結如下

        1.MOV PC,LR

        2.BL LR

        3.在子程序入口處使用以下指令將R14存入堆棧

        STMFD SP!,{,LR}

        對應的,使用以下指令可以完成子程序的返回

        LDMFD SP!, {,LR}

        轉載自:http://blog.csdn.net/xgx198831/article/details/8333446

        匯編學習總結記錄

        1.1. 匯編學習總結記錄
        對于我們之前分析的start.S中,涉及到很多的匯編的語句,其中,可以看出,很多包含了很多種不同的語法,使用慣例等,下面,就對此進行一些總結,借 以實現一定的舉一反三或者說觸類旁通,這樣,可以起到一定的借鑒功能,方便以后看其他類似匯編代碼, 容易看懂匯編代碼所要表達的含義。
        1.1.1. 匯編中的標號=C中的標號
        像前面匯編代碼中,有很多的,以點開頭,加上一個名字的形式的標號,比如:

        1. reset:
        2. /*
        3. * set the cpu to SVC32 mode
        4. */
        5. mrs r0,cpsr
        中的reset,就是匯編中的標號,相對來說,比較容易理解,就相當于C語言的標號。
        比如,C語言中定義一個標號ERR_NODEV:
        1. ERR_NODEV: /* no device error */
        2. ... /* c code here */
        然后對應在別處,使用goto去跳轉到這個標號ERR_NODEV:
        1. if (something)
        2. goto ERR_NODEV ;

        匯編中的標號 = C語言中的標號Label
        1.1.2. 匯編中的跳轉指令=C中的goto
        對應地,和上面的例子中的C語言中的編號和掉轉到標號的goto類似,匯編中,對于定義了標號,那么也會有對應的指令,去跳轉到對應的匯編中的標號。
        這些跳轉的指令,就是b指令,b是branch的縮寫。
        b指令的格式是:
        b{cond} label
        簡單說就是跳轉到label處。
        用和上面的例子相關的代碼來舉例:
        1. .globl _start
        2. _start: b reset
        就是用b指令跳轉到上面那個reset的標號。

        匯編中的b跳轉指令 = C語言中的goto
        1.1.3. 匯編中的.globl=C語言中的extern
        對于上面例子中:

        .globl _start
        中的.global,就是聲明_start為全局變量/標號,可以供其他源文件所訪問。
        即匯編器,在編譯此匯編代碼的時候,會將此變量記下來,知道其是個全局變量,遇到其他文件是用到此變量的的時候,知道是訪問這個全局變量的。
        因此,從功能上來說,就相當于C語言用extern去生命一個變量,以實現本文件外部訪問此變量。

        匯編中的.globl或.global = C語言中的extern
        1.1.4. 匯編中用bl指令和mov pc,lr來實現子函數調用和返回
        和b指令類似的,另外還有一個bl指令,語法是:
        BL{cond} label
        其作用是,除了b指令跳轉到label之外,在跳轉之前,先把下一條指令地址存到lr寄存器中,以方便跳轉到那邊執行完畢后,將lr再賦值給pc,以實現函數返回,繼續執行下面的指令的效果。
        用下面這個start.S中的例子來說明:

        1. bl cpu_init_crit
        2. 。。。
        3. cpu_init_crit:
        4. 。。。
        5. mov pc, lr
        其中,就是先調用bl掉轉到對應的標號cpu_init_crit,其實就是相當于一個函數了,
        然后在cpu_init_crit部分,執行完畢后,最后調用 mov pc, lr,將lr中的值,賦給pc,即實現函數的返回原先 bl cpu_init_crit下面那條代碼,繼續執行函數。
        上面的整個過程,用C語言表示的話,就相當于
        1. 。。。
        2. cpu_init_crit();
        3. 。。。
        4. void cpu_init_crit(void)
        5. {
        6. 。。。
        7. }

        而關于C語言中,函數的跳轉前后所要做的事情,都是C語言編譯器幫我們實現好了,會將此C語言中的函數調用,轉化為對應的匯編代碼的。
        其中,此處所說的,函數掉轉前后所要做的事情,就是:
        函數跳轉前:要將當前指令的下一條指令的地址,保存到lr寄存器中。
        函數調用完畢后:將之前保存的lr的值給pc,實現函數跳轉回來。繼續執行下一條指令。
        而如果你本身自己寫匯編語言的話,那么這些函數跳轉前后要做的事情,都是你程序員自己要關心,要實現的事情。

        匯編中bl + mov pc,lr = C語言中的子函數調用和返回
        1.1.5. 匯編中的對應位置有存儲值的標號 = C語言中的指針變量
        像前文所解析的代碼中類似于這樣的:
        1. LABEL1:.word Value2
        比如:
        1. _TEXT_BASE:
        2. .word TEXT_BASE
        所對應的含義是,有一個標號_TEXT_BASE
        而該標號中對應的位置,所存放的是一個word的值,具體的數值是TEXT_BASE,此處的TEXT_BASE是在別處定義的一個宏,值是0x33D00000。
        所以,即為:
        有一個標號_TEXT_BASE,其對應的位置中,所存放的是一個word的值,值為TEXT_BASE=0x33D00000。
        總的來說,此種用法的含義,如果用C語言來表示,其實更加容易理解:
        int *_TEXT_BASE = TEXT_BASE = 0x33D00000
        即:
        int *_TEXT_BASE = 0x33D00000

        不過,對于這樣的類似于C語言中的指針的匯編中的標號,在C語言中調用到的話,卻是這樣引用的:
        1. /* for the following variables, see start.S */
        2. extern ulong _armboot_start; /* code start */
        3. extern ulong _bss_start; /* code + data end == BSS start */
        4. 。。。
        5. IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;
        6. 。。。
        而不是我原以為的,直接當做指針來引用該變量的方式:

        1. *IRQ_STACK_START = *_armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;

        其中,對應的匯編中的代碼為:

        1. .globl _armboot_start
        2. _armboot_start:
        3. .word _start
        所以,針對這點,還是需要注意一下的。至少以后如果自己寫代碼的時候,在C語言中引用匯編中的global的標號的時候,知道是如何引用該變量的。

        匯編中類似這樣的代碼:
        label1: .word value2
        就相當于C語言中的:
        int *label1 = value2
        但是在C語言中引用該標號/變量的時候,卻是直接拿來用的,就像這樣:
        label1 = other_value
        其中label1就是個int型的變量。
        1.1.6. 匯編中的ldr+標號,來實現C中的函數調用
        接著上面的內容,繼續解釋,對于匯編中這樣的代碼:
        第一種:
        ldr pc, 標號1
        。。。
        標號1:.word 標號2
        。。。
        標號2:
        。。。(具體要執行的代碼)
        或者是,
        第二種:
        ldr pc, 標號1
        。。。
        標號1:.word XXX(C語言中某個函數的函數名)
        的意思就是,將地址為標號1中內容載入到pc中。
        而地址為標號1中的內容,就是標號2。
        所以上面第一種的意思:
        就很容易看出來,就是把標號2這個地址值,給pc,即實現了跳轉到標號2的位置執行代碼,就相當于調用一個函數,該函數名為標號2.
        第二種的意思,和上面類似,是將C語言中某個函數的函數名,即某個地址值,給pc,實現調用C中對應的那個函數。
        兩種做法,其含義用C語言表達,其實很簡單:
        PC = *(標號1) = 標號2
        舉個例子就是:
        第一種:
        1. 。。。
        2. ldr pc, _software_interrupt
        3. 。。。
        4. _software_interrupt: .word software_interrupt
        5. 。。。
        6. software_interrupt:
        7. get_bad_stack
        8. bad_save_user_regs
        9. bldo_software_interrupt

        就是實現了將標號1,_software_interrupt,對應的位置中的值,標號2,software_interrupt,給pc,即實現了將pc掉轉到software_interrupt的位置,即實現了調用函數software_interrupt的效果。
        第二種:

        1. ldr pc, _start_armboot
        2. _start_armboot: .word start_armboot
        含義就是,將標號1,_start_armboot,所對應的位置中的值,start_armboot給pc,即實現了調用函數start_armboot的目的。
        其中,start_armboot是C語言文件中某個C語言的函數。

        匯編中,實現函數調用的效果,有如下兩種方法:
        方法1:
        ldr pc, 標號1
        。。。
        標號1:.word 標號2
        。。。
        標號2:
        。。。(具體要執行的代碼)
        方法2:
        ldr pc, 標號1
        。。。
        標號1:.word XXX(C語言中某個函數的函數名)
        1.1.7. 匯編中設置某個寄存器的值或給某個地址賦值
        在匯編代碼start.S中,看到不止一處, 類似于這樣的代碼:
        形式1:

        1. # define pWTCON0x53000000
        2. 。。。
        3. ldr r0, =pWTCON
        4. mov r1, #0x0
        5. str r1, [r0]
        或者:
        形式2:

        1. # define INTSUBMSK 0x4A00001C
        2. 。。。
        3. ldr r1, =0x7fff
        4. ldr r0, =INTSUBMSK
        5. str r1, [r0]
        其含義,都是將某個值,賦給某個地址,此處的地址,是用宏定義來定義的,對應著某個寄存器的地址。
        其中,形式1是直接通過mov指令來將0這個值賦給r1寄存器,和形式2中的通過ldr偽指令來將0x3ff賦給r1寄存器,兩者區別是,前者是因為已經確定所要賦的值0x0是mov的有效操作數,而后者對于0x3ff不確定是否是mov的有效操作數
        (如果不是,則該指令無效,編譯的時候,也無法通過編譯,會出現類似于這樣的錯誤:
        1. start.S: Assembler messages:
        2. start.S:149: Error: invalid constant -- `mov r1,#0xFFEFDFFF
        3. make[1]: * [start.o] 錯誤 1
        4. make: * [cpu/arm920t/start.o] 錯誤 2

        所以才用ldr偽指令,讓編譯器來幫你自動判斷:
        (1)如果該操作數是mov的有效操作數,那么ldr偽指令就會被翻譯成對應的mov指令。
        舉例說明:
        匯編代碼:

        1. # define pWTCON0x53000000
        2. 。。。
        3. ldr r0, =pWTCON
        被翻譯后的真正的匯編代碼:

        1. 33d00068: e3a00453mov r0, #1392508928 ; 0x53000000
        (2)如果該操作數不是mov的有效操作數,那么ldr偽指令就會被翻譯成ldr指令。
        舉例說明:
        匯編代碼:
        1. ldr r1, =0x7fff
        被翻譯后的真正的匯編代碼:

        1. 33d00080: e59f13f8ldr r1, [pc, #1016] ; 33d00480
        2. 。。。
        3. 33d00480: 00007fff.word 0x00007fff
        即把ldr偽指令翻譯成真正的ldr指令,并且另外分配了一個word的地址空間用于存放該數值,然后用ldr指令將對應地址中的值載入,賦值給r1寄存器。

        匯編中,一個常用的,用來給某個地址賦值的方法,類似如下形式:

        1. #define 宏的名字寄存器地址
        2. 。。。
        3. ldr r1, =要賦的值
        4. ldr r0, =宏的名字
        5. str r1, [r0]



        關鍵詞: ARM匯編MOVPCL

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 额敏县| 桓仁| 筠连县| 高雄市| 安陆市| 余姚市| 独山县| 杨浦区| 江永县| 荔浦县| 新竹县| 乌苏市| 固原市| 河间市| 石嘴山市| 枣强县| 东城区| 易门县| 武功县| 三门峡市| 孝昌县| 时尚| 浪卡子县| 宝兴县| 巴彦淖尔市| 启东市| 工布江达县| 武川县| 丰都县| 锦州市| 高州市| 马边| 嘉鱼县| 砚山县| 沧州市| 深州市| 蚌埠市| 金坛市| 濉溪县| 克山县| 长垣县|