我們?cè)趯W(xué)習(xí) Linux 嵌入式開發(fā)的時(shí)候,了解 ARM 匯編是很有必要的,雖然我們使用匯編編寫代碼的情況很少,但是有些情況下我們需要簡(jiǎn)單編寫一些匯編程序來協(xié)助我們調(diào)試板子(因?yàn)槲覀兊?i.MX6 UL 終結(jié)者開發(fā)板使用的 cpu 是 ARM Cortex-A7 架構(gòu)的,cpu 剛上電必須要運(yùn)行匯編代碼,來初始化 cpu 的一些內(nèi)部功能,然后設(shè)置好 C 語言的環(huán)境,才能運(yùn)行 C 語言),比如一塊板子焊接回來,我們燒寫鏡像沒有問題,但是燒寫完,串口沒有打印信息,不能正常啟動(dòng),這時(shí)我們應(yīng)該不能確定是軟件問題還是硬件問題,我們可以在匯編里面通過點(diǎn)燈(點(diǎn)亮板子上的 led)的方式來確定下 cpu 有沒有運(yùn)行,此時(shí)我們就要用到匯編,因?yàn)?C 語言還沒有被執(zhí)行。所以我們需要了解并掌握下 ARM 匯編。 81 .1 GNU 匯編語法 匯編語法 GNU 匯編語法適用于所有的架構(gòu),并不是 ARM 獨(dú)享的,GNU 匯編由一系列的語句組成,每行一條語句, 每條語句有三個(gè)可選部分,如下: label:instruction @ comment label 即標(biāo)號(hào),表示地址位置,有些指令前面可能會(huì)有標(biāo)號(hào),這樣就可以通過這個(gè)標(biāo)號(hào)得到指令的地址,標(biāo)號(hào)也可以用來表示數(shù)據(jù)地址。注意 label 后面的“:”,任何以“:”結(jié)尾的標(biāo)識(shí)符都會(huì)被識(shí)別為一個(gè)標(biāo)號(hào)。 instruction 即指令,也就是匯編指令或偽指令。 @符號(hào),表示后面的是注釋,就跟 C 語言里面的“/*”和“*/”一樣,其實(shí)在 GNU 匯編文件中我們也可以 使用“/*”和“*/”來注釋。 comment 就是注釋內(nèi)容。 比如下面的代碼: sum: MOVS R0, #0X5a @設(shè)置 R0=0X5a 上面代碼中“add”就是標(biāo)號(hào),“MOVS R0, #0X5a”就是指令,后面的“@設(shè)置 R0=0X5a”就是注釋。( 注意 :ARM 匯編中的指令 、 偽指令 、 偽操作 、 寄存器等 , 都可以全部使用大寫 , 也可以全部使用小寫 , 但是不能大小寫混用) 我們也可以使用.section 偽操作來定義一個(gè)段名,匯編系統(tǒng)中預(yù)定義了一些段名,如下: .text //代碼段 .bss //未初始化的數(shù)據(jù)段 .data //初始化的數(shù)據(jù)段 .rodata //只讀數(shù)據(jù)段 我們自己也可以使用.section 定義一個(gè)段,如下: .section .test_section @定義一個(gè) test_setcion 段 我們知道在 C 語言中,程序的入口是 main 函數(shù),在匯編中程序的入口標(biāo)號(hào)是“_start”,下面的代碼就是使用_start 作為入口標(biāo)號(hào): .global _start _start: ldr r0, =0x5a @r0=0x5a 上面代碼中.global 是偽操作,表示_start 是一個(gè)全局標(biāo)號(hào),類似 C 語言里面的全局變量,常見的偽操作有: .byte //定義單字節(jié)數(shù)據(jù),比如.byte 0x5a .short //定義雙字節(jié)數(shù)據(jù),比如.short 0x5a5a .long //定義 4 字節(jié)數(shù)據(jù),比如.long 0x5a5a5a5a .equ //賦值語句,格式為: .equ 變量名,表達(dá)式,比如.equ num, 0x5a,表 示 num=0x5a .align //數(shù)據(jù)字節(jié)對(duì)齊,比如:.align 4 表示 4 字節(jié)對(duì)齊 .end //表示源文件結(jié)束 .global //定義一個(gè)全局符號(hào),格式為:.global symbol,比如:.global _start 上面這些是最常見的偽操作,GNU 匯編還有其他的偽操作,如果想更箱子的了解偽操作可以參考《ARM Cortex-A(armV7)編程手冊(cè) V4.0》(光盤目錄:i.MX6UL 終結(jié)者光盤資料\10_其它參考資料) GNU 匯編也支持函數(shù),格式如下: .type fun_name, @function fun_name: #content ret .type 指令指定 fun_name 為其它匯編程序調(diào)用此函數(shù)時(shí)的地址 fun_name 也為函數(shù)名 @function 表示函數(shù)內(nèi)容開始 ret 指令表示函數(shù)結(jié)束,返回到父函數(shù)調(diào)用子函數(shù)處 定義函數(shù)的例子如下: .type add_fun, @function add_fun: add %ebx, %ebx movl %ebx, %eax ret 8.2 ARM 匯編指令 下面我們來學(xué)習(xí)下 ARM 的常用匯編指令,這里我們參考了文檔《ARM ArchitectureReference Manual ARMv7-Aand ARMv7-R edition》(光盤目錄:i.MX6UL 終結(jié)者光盤資料\10_其它參考資料)。 8.2.1 MOV 指令 MOV 指令用于將數(shù)據(jù)從一個(gè)寄存器拷貝到另外一個(gè)寄存器,或者將一個(gè)立即數(shù)傳遞到寄存器里面,使用示例如下: MOV R0,R1 @將寄存器 R1 中的數(shù)據(jù)傳遞給 R0,即 R0=R1 MOV R0, #0X12 @將立即數(shù) 0X12 傳遞給 R0 寄存器,即 R0=0X12 8.2.2 MRS 指令 MRS 指令用于將特殊寄存器(如 CPSR 和 SPSR)中的數(shù)據(jù)傳遞給通用寄存器,要讀取特殊寄存器的數(shù)據(jù)只能 使用 MRS 指令!使用示例如下: MRS R0, CPSR @將特殊寄存器 CPSR 里面的數(shù)據(jù)傳遞給 R0,即 R0=CPSR 8.2.3 MSR 指令 MSR 指令和 MRS 剛好相反,MSR 指令用來將普通寄存器的數(shù)據(jù)傳遞給特殊寄存器,也就是寫特殊寄存器,寫 特殊寄存器只能使用 MSR,使用示例如下: MSR CPSR, R0 @將 R0 中的數(shù)據(jù)復(fù)制到 CPSR 中,即 CPSR=R0 8.2.4 LDR 指令 LDR 指令用于從存儲(chǔ)器中將一個(gè) 32 位的字?jǐn)?shù)據(jù)傳送到目的寄存器中。該指令通常用于從存儲(chǔ)器中讀取32 位的字?jǐn)?shù)據(jù)到通用寄存器,然后對(duì)數(shù)據(jù)進(jìn)行處理。當(dāng)程序計(jì)數(shù)器 PC 作為目的寄存器時(shí),該指令從存儲(chǔ)器中讀取的字?jǐn)?shù)據(jù)被當(dāng)作目的地址,從而可以實(shí)現(xiàn)程序流程的跳轉(zhuǎn)。該指令在程序設(shè)計(jì)中比較常用,其尋址 方式靈活多樣。使用示例如下: LDR R0,[R1] //將存儲(chǔ)器地址為 R1 的字?jǐn)?shù)據(jù)讀入寄存器 R0 LDR R0,[R1,R2] //將存儲(chǔ)器地址為 R1+R2 的字?jǐn)?shù)據(jù)讀入寄存器 R0 LDR R0,[R1,#8] //將存儲(chǔ)器地址為 R1+8 的字?jǐn)?shù)據(jù)讀入寄存器 R0 LDR R0,[R1],R2 //將存儲(chǔ)器地址為 R1 的字?jǐn)?shù)據(jù)讀入寄存器 R0,幵將新地址 R1 +R2 寫入 R1 8.2.5 STR 指令 STR 是將數(shù)據(jù)寫入到存儲(chǔ)器中,示例代碼如下: STR R1, [R0] //將 R1 中的值寫入到 R0 中所保存的地址中 8.2.6 入棧,出棧指令 棧被定義為一種先進(jìn)后出的數(shù)據(jù)結(jié)構(gòu),即最后進(jìn)棧的元素將被最先彈出來.這很像許多人進(jìn)入一條窄得只能 容納一個(gè)人通過的小道,如果要從這條道往回退出來的話,那么最先退出來的人是最后一個(gè)進(jìn)入小道的人.所以棧具有后進(jìn)先出的性質(zhì)(LIFO)。我們使用 push 指令實(shí)現(xiàn)入棧,示例代碼如下: PUSH {R0~R3, R12} @將 R0~R3 和 R12 壓棧 出棧指令使用 POP,示例代碼如下: POP {R0~R3,R12} @在恢復(fù) R0~R3,R12 8.2.7 跳轉(zhuǎn)指令 跳轉(zhuǎn)指令用于實(shí)現(xiàn)程序流程的跳轉(zhuǎn),在 ARM 程序中有兩種方法可以實(shí)現(xiàn)程序流程的跳轉(zhuǎn): 1. 使用專門的跳轉(zhuǎn)指令(B B 、 BL 、 BX 、 BLX ) 2. 器 直接向程序計(jì)數(shù)器 C PC 寫入跳轉(zhuǎn)地址值 一般我們常用專門跳轉(zhuǎn)指令實(shí)現(xiàn)程序的跳轉(zhuǎn),下面我們來看下跳轉(zhuǎn)指令的使用: B 指令 B 指令是最簡(jiǎn)單的跳轉(zhuǎn)指令。一旦遇到一個(gè) B 指令,ARM 處理器將立即跳轉(zhuǎn)到給定的目標(biāo)地址,從那里繼續(xù)執(zhí)行。注意存儲(chǔ)在跳轉(zhuǎn)指令中的實(shí)際值是相對(duì)當(dāng)前 PC 值的一個(gè)偏移量,而不是一個(gè)絕對(duì)地址,它的值由匯編器來計(jì)算(參考尋址方式中的相對(duì)尋址)。它是 24 位有符號(hào)數(shù),左移兩位后有符號(hào)擴(kuò)展為 32 位,表示的有效偏移為 26 位(前后 32MB 的地址空間)。以下指令: B Label ;程序無條件跳轉(zhuǎn)到標(biāo)號(hào) Label 處執(zhí)行 BL 指令 跳轉(zhuǎn)之前,會(huì)在寄存器 R14 中保存 PC 的當(dāng)前內(nèi)容,因此,可以通過將 R14 的內(nèi)容重新加載到 PC 中,來返回到跳轉(zhuǎn)指令之后的那個(gè)指令處執(zhí)行。該指令是實(shí)現(xiàn)子程序調(diào)用的一個(gè)基本但常用的手段。以下指令: BL Label ;當(dāng)程序無條件跳轉(zhuǎn)到標(biāo)號(hào) Label 處執(zhí)行時(shí),同時(shí)將當(dāng)前的 PC 值保存到 R14 中 BLX 指令 從 ARM 指令集跳轉(zhuǎn)到指令中所指定的目標(biāo)地址,并將處理器的工作狀態(tài)由 ARM 狀態(tài)切換到 Thumb 狀態(tài),該指令同時(shí)將 PC 的當(dāng)前內(nèi)容保存到寄存器 R14 中。因此,當(dāng)子程序使用 Thumb 指令集,而調(diào)用者使用 ARM 指令集時(shí),可以通過 BLX 指令實(shí)現(xiàn)子程序的調(diào)用和處理器工作狀態(tài)的切換。同時(shí),子程序的返回可以通過將寄存器 R14 值復(fù)制到 PC 中來完成。指令格式如下: BLX 目標(biāo)地址 X BX 指令 跳轉(zhuǎn)到指令中所指定的目標(biāo)地址,目標(biāo)地址處的指令既可以是 ARM 指令,也可以是 Thum 指令,指令格式如下: BX{條件} 目標(biāo)地址 8.2.8 邏輯運(yùn)算指令 常用的邏輯運(yùn)算符如下: AND a, b //a = a & b 按位與 AND a, b, #c //a = b & #c 按位與 AND a, b, c //a = b & c 按位與 ORR a, b //a = a | b 按位或 ORR a, b, #c //a = b | #c 按位或 ORR a, b, c //a = b | c 按位或 BIC a, b //a = a & (~b) 位清除 BIC a, b, #c //a = b & (~#c) 位清除 BIC a, b, c //a = b & (~c) 位清除 ORN a, b //a = a | (b) 按位或非 ORN a, b, #c //a = b |(#c) 按位或非 ORN a, b, c //a = b |(c) 按位或非 EOR a, b //a = a ^b 按位異或 EOR a, b, #c //a = b ^ #c 按位異或 EOR a, b, c //a = b ^ c 按位異或 8.2.9 算數(shù)運(yùn)算符 ADD, a, #b // a = a + #b 加法運(yùn)算 ADD a, b, c //a = b + c 加法運(yùn)算 ADD a, b, #c //a = b + #c 加法運(yùn)算 SUB a, #b //a = a - #b 減法運(yùn)算 SUB a, b, c //a = b - c 減法運(yùn)算 SUB a, b, #c //a = b - #c 減法運(yùn)算 MUL a, b, c //a = b * c 懲罰運(yùn)行算(32 位) UDIV a, b, c //a = b/c 無符號(hào)除法 SDIV a, b, c //a = b/c 有符號(hào)除法 本節(jié)主要講解了一些最常用的指令,還有很多不常用的指令沒有講解,但是夠我們后續(xù)學(xué)習(xí)用了。要想詳細(xì)的學(xué)習(xí) ARM 的所有指令請(qǐng)參考《 ARM ArchitectureReference Manual ARMv7-A and ARMv7-Redition.pdf》和《ARM Cortex-A(armV7)編程手冊(cè) V4.0.pdf》這兩份文檔,他們分別在光盤資料的目錄:i.MX6UL 終結(jié)者光盤資料\10_其它參考資料。
|