ARM-V7架構(二)

lethe1203發表於2024-03-16
本節主要介紹ARM指令:

GNU彙編語法:

GNU 彙編語法適用於所有的架構,並不是 ARM 獨享的,GNU 彙編由一系列的語句組成,
每行一條語句,每條語句有三個可選部分,如下:
label:instruction @ comment
label 即標號,表示地址位置,有些指令前面可能會有標號,這樣就可以透過這個標號得到指令的地址,標號也可以用來表示資料地址。注意 label 後面的“:”,任何以“:”結尾的識別符號都會被識別為一個標號。
instruction 即指令,也就是彙編指令或偽指令。
@符號,表示後面的是註釋,就跟 C 語言裡面的“/*”和“*/”一樣,其實在 GNU 彙編檔案中我們也可以使用“/*”和“*/”來註釋。
comment 就是註釋內容。
比如如下程式碼:
add: MOVS R0, #0X12 @設定 R0=0X12
上面程式碼中“add:”就是標號,“MOVS R0,#0X12”就是指令,最後的“@設定 R0=0X12”就是註釋。
使用者可以使用.section 偽操作來定義一個段,彙編系統預定義了一些段名:
.text 表示程式碼段。
.data 初始化的資料段。
.bss 未初始化的資料段。
.rodata 只讀資料段。
我們當然可以自己使用.section 來定義一個段,每個段以段名開始,以下一段名或者檔案結尾結束,比如:
.section .testsection @定義一個 testsetcion 段
彙編程式的預設入口標號是_start,不過我們也可以在連結指令碼中使用 ENTRY 來指明其它的入口點,下面的程式碼就是使用_start 作為入口標號:
.global _start _start: ldr r0, =0x12 @r0=0x12
上面程式碼中.global 是偽操作,表示_start 是一個全域性標號,類似 C 語言裡面的全域性變數一樣,常見的偽操作有:
.byte 定義單位元組資料,比如.byte 0x12。
.short 定義雙位元組資料,比如.short 0x1234。
.long 定義一個 4 位元組資料,比如.long 0x12345678。
.equ 賦值語句,格式為:.equ 變數名,表示式,比如.equ num, 0x12,表示 num=0x12。
.align 資料位元組對齊,比如:.align 4 表示 4 位元組對齊。
.end 表示原始檔結束。
.global 定義一個全域性符號,格式為:.global symbol,比如:.global _start。
GNU 彙編同樣也支援函式,函式格式如下:
函式名:
    函式體
    返回語句
GNU 彙編函式返回語句不是必須的,如下程式碼就是用匯編寫的 Cortex-A7 中斷服務函式:
/* 未定義中斷 */
Undefined_Handler:
    ldr r0, =Undefined_Handler
    bx r0
/* SVC 中斷 */
SVC_Handler:
    ldr r0, =SVC_Handler
    bx r0
/* 預取終止中斷 */
PrefAbort_Handler:
    ldr r0, =PrefAbort_Handler 
    bx r0
上述程式碼中定義了三個彙編函式:Undefined_Handler、SVC_Handler 和PrefAbort_Handler。以函式 Undefined_Handler 為例我們來看一下彙編函式組成,“Undefined_Handler”就是函式名,“ldr r0, =Undefined_Handler”是函式體,“bx r0”是函式返回語句,“bx”指令是返回指令,函式返回語句不是必須的。

ARM-V7常用匯編

處理器內部資料傳輸指令

MOV指令:
mov指令常見的三種用法:
  • 用於資料傳送可以傳送立即數,暫存器的值和移位操作產生的值
  • 當目標暫存器是PC指標的時候,可以實現程式的跳轉
  • 當目標暫存器是pc指標的時候並且s位被設定,那麼在執行跳轉操作指令的同時會把SPSR暫存器的值恢復到CPSR中,所用於異常返回
mov r0,r0    /*空指令,耗時操作*/
mov r0,r0,lsl#3    /*左移操作,相當於r0=r0*8*/
mov pc,lr        /*退出到呼叫者,用於普通函式返回,PC即是R15*/
movs pc,lr        /*退出到呼叫者並恢復標誌位,用於異常函式返回,加s表示影響cpsr,表示恢復到呼叫之前的位置*/
MVN指令:
mvn指令主要完成下面的功能:
  • 向暫存器中傳送一個負數
  • 生成位掩碼
  • 求一個數的反碼
mvn r0,#4    /*r0 = -5,立即數4取反為-5*/
mvn r0,#0    /*r0 = -1*/
MRS 指令:
MRS 指令用於將特殊暫存器(如 CPSR 和 SPSR)中的資料傳遞給通用暫存器,要讀取特殊
暫存器的資料只能使用 MRS 指令!使用示例如下:
MRS R0, CPSR  @將特殊暫存器 CPSR 裡面的資料傳遞給 R0,即 R0=CPSR
MSR 指令:
MSR 指令和 MRS 剛好相反,MSR 指令用來將普通暫存器的資料傳遞給特殊暫存器,也就
是寫特殊暫存器,寫特殊暫存器只能使用 MSR,使用示例如下:
MSR CPSR, R0  @將 R0 中的資料複製到 CPSR 中,即 CPSR=R0

儲存器訪問指令:

LDR 指令:
LDR 主要用於從儲存載入資料到暫存器 Rx 中,LDR 也可以將一個立即數載入到暫存器 Rx中,LDR 載入立即數的時候要使用“=”,而不是“#”。
LDR R0, =0X0209C004 @將暫存器地址 0X0209C004 載入到 R0 中,即 R0=0X0209C004
LDR R1, [R0] @讀取地址 0X0209C004 中的資料到 R1 暫存器中
STR 指令:
LDR 是從儲存器讀取資料,STR 就是將資料寫入到儲存器中,
LDR R0, =0X0209C004 @將暫存器地址 0X0209C004 載入到 R0 中,即 R0=0X0209C004
LDR R1, =0X20000002 @R1 儲存要寫入到暫存器的值,即 R1=0X20000002
STR R1, [R0] @將 R1 中的值寫入到 R0 中所儲存的地址中

壓棧出棧指令:

在 A 函式中呼叫 B 函式,當 B 函式執行完以後再回到 A 函式繼續執行。要想再跳回 A 函式以後程式碼能夠接著正常執行,那就必須在跳到 B 函式之前將當前處理器狀態儲存起來(就是儲存 R0~R15 這些暫存器值),當 B 函式執行完成以後再用前面儲存的暫存器值恢復R0~R15 即可。儲存 R0~R15 暫存器的操作就叫做現場保護,恢復 R0~R15 暫存器的操作就叫做恢復現場。在進行現場保護的時候需要進行壓棧(入棧)操作,恢復現場就要進行出棧操作。壓棧的指令為 PUSH,出棧的指令為 POP,PUSH 和 POP 是一種多儲存和多載入指令,即可以一次操作多個暫存器資料,他們利用當前的棧指標 SP 來生成地址,PUSH 和 POP 的用法如下所示:
PUSH <reg list>   /*將暫存器列表存入棧中*/
POP <reg list>     /*從棧中恢復暫存器列表*/
舉例:
要將 R0~R3 和 R12 這 5 個暫存器壓棧
PUSH {R0~R3, R12} @將 R0~R3 和 R12 壓棧
push和pop的另外一種寫法便是stmfd sp!和ldmfd sp!

跳轉指令:

有多種跳轉操作,比如:
①、直接使用跳轉指令 B、BL、BX 等。
②、直接向 PC 暫存器裡面寫入資料。
B指令:
這是最簡單的跳轉指令,B 指令會將 PC 暫存器的值設定為跳轉目標地址, 一旦執行 B 指令,ARM 處理器就會立即跳轉到指定的目標地址。如果要呼叫的函式不會再返回到原來的執行處,那就可以用 B 指令,如下示例:
_start:
    ldr sp,=0X80200000 /*設定棧指標*/
    b main     /*跳轉到 main 函式*/
上述程式碼就是典型的在彙編中初始化 C 執行環境,然後跳轉到 C 檔案的 main 函式中執行,上述程式碼只是初始化了 SP 指標,有些處理器還需要做其他的初始化,比如初始化 DDR 等等。因為跳轉到 C 檔案以後再也不會回到彙編了,所以在第 4 行使用了 B 指令來完成跳轉。
BL指令:
BL 指令相比 B 指令,在跳轉之前會在暫存器 LR(R14)中儲存當前 PC 暫存器值,所以可以透過將 LR 暫存器中的值重新載入到 PC 中來繼續從跳轉之前的程式碼處執行,這是子程式呼叫一個基本但常用的手段。比如 Cortex-A 處理器的 irq 中斷服務函式都是彙編寫的,主要用匯編來實現現場的保護和恢復、獲取中斷號等。但是具體的中斷處理過程都是 C 函式,所以就會存在彙編中呼叫 C 函式的問題。而且當 C 語言版本的中斷處理函式執行完成以後是需要返回到irq 彙編中斷服務函式,因為還要處理其他的工作,一般是恢復現場。這個時候就不能直接使用B 指令了,因為 B 指令一旦跳轉就再也不會回來了,這個時候要使用 BL 指令,示例程式碼如下:
push {r0, r1}             /*儲存 r0,r1*/
cps #0x13                 /*進入 SVC 模式,允許其他中斷再次進去*/
bl system_irqhandler     /*載入 C 語言中斷處理函式到 r2 暫存器中*/
cps #0x12                 /*進入 IRQ 模式*/
pop {r0, r1}         
str r0, [r1, #0X10]     /*中斷執行完成,寫 EOIR*/

算術運算和邏輯運算指令:

ADD指令:
add r0,r0,#3    /*保留r0中的0位和1位,丟棄其他的位*/
add r2,r1,r3    /*r2 = r1&r3*/
ands r0,r0,#0x01    /*r0 = r0&0x1,取出最低位資料*/
ORR指令:
邏輯或
SUB指令:
sub r0,r1,r2        /*r0 = r1-r2*/
sub r0,r1,#256      /*r0 = r1-256*/
其他的不一一說明,具體邏輯運算指令如下:
0

相關文章