新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > arm匯編中的跳轉指令

        arm匯編中的跳轉指令

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

        范例demo.s:

        .equSTACK_BASE,0x0c002000

        .equSTACK_SIZE,0x00001000


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

        這是一個合法的匯編文件,它把堆棧基址設為0x0c002000,棧限設為0x0c001000,然后跳到entry所標識的C程序中執行。

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

        我們如果把上面代碼寫成:
        .text
        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

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

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

        ADR R2,LOOP ;將LOOP 的地址放入R2
        ADR R3,LOOP+4
        可以用ADR 加載地址,實現查表:

        ADR R0,DISP_TAB ;加載轉換表地址
        LDRB R1,[R0,R2] ;使用R2 作為參數,進行查表

        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以內。借此可以實現大范圍地址的跳轉,完成從flash到sdram的跳轉。


        關鍵詞: arm匯編跳轉指

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 新野县| 通许县| 太白县| 旬邑县| 明水县| 晴隆县| 太湖县| 海丰县| 昌都县| 西丰县| 浦东新区| 安乡县| 贵州省| 加查县| 红原县| 玉林市| 桂林市| 无棣县| 拜泉县| 吐鲁番市| 屏东县| 武穴市| 尚义县| 什邡市| 商都县| 普安县| 新蔡县| 北辰区| 郓城县| 水富县| 友谊县| 资中县| 和政县| 呼玛县| 宜黄县| 林芝县| 清水县| 吴桥县| 枞阳县| 同江市| 安泽县|