(匯編)指令是CPU機(jī)器指令的助記符,經(jīng)過編譯后會(huì)得到一串10組成的機(jī)器碼,可以由CPU讀取執(zhí)行。 (匯編)偽指令本質(zhì)上不是指令(只是和指令一起寫在代碼中),它是編譯器環(huán)境提供的,目的是用來指導(dǎo)編譯過程,經(jīng)過編譯后偽指令最終不會(huì)生成機(jī)器碼。
ARM官方的ARM匯編風(fēng)格:指令一般用大寫、Windows中IDE開發(fā)環(huán)境(如ADS、MDK等)常用。如: LDR R0, [R1]
GNU風(fēng)格的ARM匯編:指令一般用小寫字母、linux中常用。如:ldr r0, [r1]
ARM采用RISC架構(gòu),CPU本身不能直接讀取內(nèi)存,而需要先將內(nèi)存中內(nèi)容加載入CPU中通用寄存器中才能被CPU處理。 ldr(load register)指令將內(nèi)存內(nèi)容加載入通用寄存器。 str(store register)指令將寄存器內(nèi)容存入內(nèi)存空間中。 ldr/str組合用來實(shí)現(xiàn) ARM CPU和內(nèi)存數(shù)據(jù)交換
寄存器尋址 mov r1, r2 立即尋址mov r0, #0xFF00 寄存器移位尋址mov r0, r1, lsl #3 寄存器間接尋址ldr r1, [r2] 基址變址尋址ldr r1, [r2, #4] 多寄存器尋址ldmia r1!, {r2-r7, r12} 堆棧尋址stmfd sp!, {r2-r7, lr}
相對(duì)尋址 beq flag
同一指令經(jīng)常附帶不同后綴,變成不同的指令。經(jīng)常使用的后綴有: B(byte)功能不變,操作長(zhǎng)度變?yōu)?位 H(half word)功能不變,長(zhǎng)度變?yōu)?6位 S(signed)功能不變,操作數(shù)變?yōu)橛蟹?hào) 如 ldr ldrb ldrh ldrsb ldrsh S(S標(biāo)志)功能不變,影響CPSR標(biāo)志位 如 mov和movsmovs r0, #0
1 2 3 4 5 6 7 | mov r0, r1 @ 相當(dāng)于C語言中的r0 = r1;
moveq r0, r1 @ 如果eq后綴成立,則直接執(zhí)行mov r0, r1;如果eq不成立則本句代碼直接作廢,相當(dāng)于沒有
@ 類似于C語言中 if (eq){r0 = r1;}
條件后綴執(zhí)行注意2點(diǎn):
1、條件后綴是否成立,不是取決于本句代碼,而是取決于這句代碼之前的代碼運(yùn)行后的結(jié)果。
2、條件后綴決定了本句代碼是否被執(zhí)行,而不會(huì)影響上一句和下一句代碼是否被執(zhí)行。
|
GT greater than LT less than
數(shù)據(jù)傳輸指令 mov mvn 算術(shù)指令add sub rsb adc sbc rsc 邏輯指令and orr eor bic 比較指令cmp cmn tst teq 乘法指令mvl mla umull umlal smull smlal 前導(dǎo)零計(jì)數(shù)clz
mrs & msr
mrs用來讀psr,msr用來寫psr CPSR寄存器比較特殊,需要專門的指令訪問,這就是mrs和msr。
b & bl & bx
b 直接跳轉(zhuǎn)(就沒打開算返回) bl branch and link,跳轉(zhuǎn)前把返回地址放入lr中,以便返回,以便用于函數(shù)調(diào)用 bx跳轉(zhuǎn)同時(shí)切換到ARM模式,一般用于異常處理的跳轉(zhuǎn)。
bne 指令 檢測(cè)到Z!=0 時(shí) 執(zhí)行跳轉(zhuǎn)
beq 指令 檢測(cè)到Z =0 時(shí) 執(zhí)行跳轉(zhuǎn)
ldr/str & ldm/stm & swp
單個(gè)字/半字/字節(jié)訪問 ldr/str 多字批量訪問 ldm/stm swp r1, r2, [r0] swp r1, r1, [r0]
合法立即數(shù)與非法立即數(shù)
ARM指令都是32位,除了指令標(biāo)記和操作標(biāo)記外,本身只能附帶很少位數(shù)的立即數(shù)。因此立即數(shù)有合法和非法之分。 合法立即數(shù):經(jīng)過任意位數(shù)的移位后非零部分可以用8位表示的即為合法立即數(shù)
swi(software interrupt)
軟中斷指令用來實(shí)現(xiàn)操作系統(tǒng)中系統(tǒng)調(diào)用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | mov(move) mov r1, r0 @兩個(gè)寄存器之間數(shù)據(jù)傳遞
mov r1, #0xff @ 將立即數(shù)賦值給寄存器
mvn和mov用法一樣,區(qū)別是mov是原封不動(dòng)的傳遞,而mvn是按位取反后傳遞
按位取反的含義:
譬如r1 = 0x000000ff,然后mov r0, r1 后,r0 = 0xff 但是我mvn r0, r1后,r0=0xffffff00
and 邏輯與
orr 邏輯或
eor 裸機(jī)異或
bic 位清除指令
bic r0,r1,#0x1f @ 將r1中的數(shù)的bit0到bit4清零后賦值給r0 0x1f = 0x0000001f=0x0000```11111
比較指令:
cmp cmp r0, r1 等價(jià)于 sub r2, r0, r1 (r2 = r0 - r1)
cmn cmn r0, r1 等價(jià)于 add r0, r1
tst tst r0, #0xf @測(cè)試r0的bit0~bit3是否全為0
teq
比較指令用來比較2個(gè)寄存器中的數(shù)
注意:比較指令不用后加s后綴就可以影響cpsr中的標(biāo)志位。
cpsr和spsr的區(qū)別和聯(lián)系:cpsr是程序狀態(tài)寄存器,整個(gè)SoC中只有1個(gè);而spsr有5個(gè),分別在5種異常模式下,作用是當(dāng)從普通模式進(jìn)入異常模式時(shí),用來保存之前普通模式下的cpsr的,以在返回普通模式時(shí)恢復(fù)原來的cpsr。
合法立即數(shù): 0x000000ff 0x00ff0000 0xf000000f
非法立即數(shù): 0x000001ff
|
mcr & mrc
mrc用于讀取CP15中的寄存器 mcr用于寫入CP15中的寄存器
SoC內(nèi)部另一處理核心,協(xié)助主CPU實(shí)現(xiàn)某些功能,被主CPU調(diào)用執(zhí)行一定任務(wù)。 ARM設(shè)計(jì)上支持多達(dá)16個(gè)協(xié)處理器,但是一般SoC只實(shí)現(xiàn)其中的CP15.(cp:coprocessor) 協(xié)處理器和MMU、cache、TLB等處理有關(guān),功能上和操作系統(tǒng)的虛擬地址映射、cache管理等有關(guān)。
mcr{<cond>} p15, <opcode_1>, <Rd>, <Crn>, <Crm>, {<opcode_2>} opcode_1:對(duì)于cp15永遠(yuǎn)為0 Rd:ARM的普通寄存器 Crn:cp15的寄存器,合法值是c0~c15 Crm:cp15的寄存器,一般均設(shè)為c0 opcode_2:一般省略或?yàn)?
舉例(來自于uboot)
mrc p15, 0, r0, c1, c0, 0 orrr0, r0, #1 mcr p15, 0, r0, c1, c0, 0
dr/str每周期只能訪問4字節(jié)內(nèi)存,如果需要批量讀取、寫入內(nèi)存時(shí)太慢,解決方案是stm/ldm ldm(load register mutiple) stm(store register mutiple)
舉例(uboot start.S 537行) stmiasp, {r0 - r12} 將r0存入sp指向的內(nèi)存處(假設(shè)為0x30001000);然后地址+4(即指向0x30001004),將r1存入該地址;然后地址再+4(指向0x30001008),將r2存入該地址······直到r12內(nèi)容放入(0x3001030),指令完成。 一個(gè)訪存周期同時(shí)完成13個(gè)寄存器的讀寫
8種后綴 ia(increase after)先傳輸,再地址+4 ib(increase before)先地址+4,再傳輸 da(decrease after)先傳輸,再地址-4 db(decrease before)先地址-4,再傳輸 fd(full decrease)滿遞減堆棧 ed(empty decrease)空遞減堆棧 fa(·······) 滿遞增堆棧 ea(·······)空遞增堆棧 四種棧 空棧:棧指針指向空位,每次存入時(shí)可以直接存入然后棧指針移動(dòng)一格;而取出時(shí)需要先移動(dòng)一格才能取出 滿棧:棧指針指向棧中最后一格數(shù)據(jù),每次存入時(shí)需要先移動(dòng)棧指針一格再存入;取出時(shí)可以直接取出,然后再移動(dòng)棧指針 增棧:棧指針移動(dòng)時(shí)向地址增加的方向移動(dòng)的棧 減棧:棧指針移動(dòng)時(shí)向地址減小的方向移動(dòng)的棧
ldmia r0, {r2 - r3} ldmiar0!, {r2 - r3}
感嘆號(hào)的作用就是r0的值在ldm過程中發(fā)生的增加或者減少最后寫回到r0去,也就是說ldm時(shí)會(huì)改變r(jià)0的值。
ldmfd sp!, {r0 - r6, pc} ldmfdsp!, {r0 - r6, pc}^
^的作用:在目標(biāo)寄存器中有pc時(shí),會(huì)同時(shí)將spsr寫入到cpsr,一般用于從異常模式返回。
總結(jié)
批量讀取或?qū)懭雰?nèi)存時(shí)要用ldm/stm指令 各種后綴以理解為主,不需記憶,最常見的是stmia和stmfd 謹(jǐn)記:操作棧時(shí)使用相同的后綴就不會(huì)出錯(cuò),不管是滿棧還是空棧、增棧還是減棧
偽指令不是指令,偽指令和指令的根本區(qū)別是經(jīng)過編譯后會(huì)不會(huì)生成機(jī)器碼。 偽指令的意義在于指導(dǎo)編譯過程。 偽指令是和具體的編譯器相關(guān)的,我們使用gnu工具鏈,因此學(xué)習(xí)gnu環(huán)境下的匯編偽指令。
gnu匯編中的一些符號(hào)
@ 用來做注釋??梢栽谛惺滓部梢栽诖a后面同一行直接跟,和C語言中//類似 # 做注釋,一般放在行首,表示這一行都是注釋而不是代碼。 :以冒號(hào)結(jié)尾的是標(biāo)號(hào) . 點(diǎn)號(hào)在gnu匯編中表示當(dāng)前指令的地址 # 立即數(shù)前面要加#或$,表示這是個(gè)立即數(shù)
常用gnu偽指令
.global _start@ 給_start外部鏈接屬性 .section .text@ 指定當(dāng)前段為代碼段 .ascii .byte .short .long .word .quad .float .string @ 定義數(shù)據(jù) .align 4@ 以16字節(jié)對(duì)齊 .balignl 16 0xabcdefgh @ 16字節(jié)對(duì)齊填充
偶爾
.end@標(biāo)識(shí)文件結(jié)束 .include@ 頭文件包含 .arm / .code32@聲明以下為arm指令 .thumb / .code16@聲明以下為thubm指令
最重要的幾個(gè)偽指令
ldr大范圍的地址加載指令 adr小范圍的地址加載指令 adrl中等范圍的地址加載指令 nop空操作
ARM中有一個(gè)ldr指令,還有一個(gè)ldr偽指令 一般都使用ldr偽指令而不用ldr指令
adr與ldr
adr編譯時(shí)會(huì)被1條sub或add指令替代,而ldr編譯時(shí)會(huì)被一條mov指令替代或者文字池方式處理; adr總是以PC為基準(zhǔn)來表示地址,因此指令本身和運(yùn)行地址有關(guān),可以用來檢測(cè)程序當(dāng)前的運(yùn)行地址在哪里 ldr加載的地址和鏈接時(shí)給定的地址有關(guān),由鏈接腳本決定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | int a;
while(1);
flag:
b flag
b .
IRQ_STACK_START:
.word 0x0badc0de
等價(jià)于 unsigned int IRQ_STACK_START = 0x0badc0de;
.align 4 @ 16字節(jié)對(duì)齊
.align 2 @ 4字節(jié)對(duì)齊
.balignl 16, 0xdeadbeef @ 對(duì)齊 + 填充
b表示位填充;align表示要對(duì)齊;l表示long,以4字節(jié)為單位填充;16表示16字節(jié)對(duì)齊;0xdeadbeef是用來填充的原料。
0x00000008: .balignl 16, 0xdeadbeef
0x0000000c 0xdeadbeef
0x00000010: 下一條指令
ldr指令: ldr r0, #0xff
偽指令: ldr r0, =0xfffl @涉及到合法/非法立即數(shù),涉及到ARM文字池
adr和ldr的差別:ldr加載的地址在鏈接時(shí)確定,而adr加載的地址在運(yùn)行時(shí)確定;所以我們可以通過adr和ldr加載的地址比較來判斷當(dāng)前程序是否在鏈接時(shí)指定的地址運(yùn)行。
|
|