處理內部數據傳輸指令
處理器內進行數據傳遞,常見的操作有如下3種。
指令 | 目的 | 源 | 描述 |
---|---|---|---|
MOV | R0 | R1 | 將R1中的數據復制到R0中 |
MRS | R0 | CPSR | 將特殊寄存器中的數據復制R0中 |
MSR | CPSR | R1 | 將R1中的數據復制到特殊寄存器CPSR中 |
詳細地介紹如何使用這3個指令。 |
1. MOV指令
MOV指令用于獎數據從一個寄存器復制到另外一個寄存器,或才將一個立即數傳遞到寄存器中,使用如下代碼:
MOV R0,R1 @將寄存器R1的數據復制到R0,即R0=R1
MOV R0, #0x12 @將立即數0x12傳遞給R0寄存器,即R0=0x12
2. MRS指令
MRS 指令用于獎特殊寄存器(CPSR和SPSR)中的數據傳遞給通用寄存器,要讀取特殊寄存器的數據只能全用MRS,使用如下代碼:
MRS CPSR,R0 @將CPSP寄存器中和數據傳遞給R0 即:R0=CPSR
3. MSR指令
MSR指令和MRS指令剛好相反,MSR指令用來獎普通寄存器的數據傳遞給特殊寄存器,也就寫特殊寄存器,寫特殊寄存器只能用MSR,使用如下代碼。
MSR CPSR,R0 @將R0的數據復制到CPSR中,即CPSR=R0
【理解】: R 即讀,S即寫”
存儲器訪問指令
ARM不能直接訪問存儲器,比如RAM中的數據。1.MX6ULL中的寄存器就是RAM類型的,我們用匯編來配置I.MX6Ull寄存器時需要借助存儲器訪問指令,一般先將要配置的值寫入反Rx(x=0~12)寄存器中,然后借助存儲器訪問指令將Rx中的數據寫入到I.MX6UL
寄存器中,讀取I.MX6ULL寄存器也是一樣的,只是過程剛好相反。常用的存儲器訪問指令有兩種LDR和STR。用法如下表:
指 令 | 描 述 |
---|---|
LDR Rd,[Rn,# offset | 從存儲器Rn+offset的位置讀取數據存放到Rd中 |
STR Rd,[Rn,# offset | 將Rd中的數據寫入到存儲器中的Rn+offset位置 |
1. LDR 指令
LDR(載入指令)主要用于從存儲器加載數據到Rx中,LDR也可以將一個立即數加載到寄存器Rx中,LDR加載立即數時要使用“=”,而不是“#”。在嵌入式開發中,LDR最常用的就是讀取CPU的寄存器值,比如I.MX6ULL有個寄存器GPIO_GDIR,其地址為0X0209C004,我們現在讀取這個寄存器中的數據,如下所示:
LDR R0, = 0X0209C004 @將害存器址0X0209C004加載到R0中,R0=0X0209C004
LDR R1,[R0] @讀取地址0X0209C004中的數據到R1寄存器中
上面的代碼就是讀取GPIO1_GDIR中的值,讀取到的寄存器值保存在R1寄存器中,上面代碼中offset是0,沒有用到offset。
2. STR指令
LDR是從存儲器中讀取,STR就是將數據寫入到存儲器,同樣以I.MX6ULL寄存器GPIO1_GDIR為例,現在我們要配置寄存器GPIO1_GDIR的值為0X20000002:
LDR R0, = 0X0209C004 @將害存器址0X0209C004加載到R0中,
LDR R1,= 0X20000002 @R1保存要寫入寄存器的值
STR R1,[R0] @將R1的值寫入到R0 中所保存地址中。
【注】LDR指令和STR指令都是按字進行讀取和寫入的,也就是操作的32位數據。如果要按照字節、半字節進行操作的話可以在LDR指令后面加上B或者H,比如按字節操作指令就是LDRB和STRB,按半字節操作的指令就是LDRH和STRH。
壓棧與出棧指令
我們通常會在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~R4和R12壓入棧中:
PUSH {R0~R5,R12} @將R0~R5,R12壓棧
PUSH {LR}
出棧:
POP {LR}
PPOP {R0~R5,R12} @將R0~R5,R12壓棧
PUSH和POP的另外一種寫法是STMFD SP!和LDMFD SP! 代碼如下列所示:
STMFD SP!,{R0~R5,R12} @R0~R5,R12入棧
STMFD SP!,{LR} @LR入棧
LDMFD SP!, {LR} @LR入棧
LDMFD SP!, {R0~R5,R12} @R0~R5,R12入棧
STMFD與STR不同是一次可以操作多個寄存器
同理LDMFD與LDR不同也是一次可以操作多個寄存器。
跳轉指令
有多種跳轉操作:
直接使用跳轉指令B、BL、BX等。
直接向PC寄存器中寫入數據。
上述兩種方法都可以完成跳轉操作,但是一般常用的是B、BL或BX,用法如下表:| 指令 | 描述 || ---------- | --------------------------------------------------------------------------------------------------------------- |
| B label | 跳轉到label,如果跳轉滿園超過正確負2KB,可以指定的B.W label使用32位版本的跳轉指令,這樣可以得到較大范圍的跳轉 |
| BX Rm | 間接跳轉,跳轉到存放于Rm中的地址處,并且切換指令集 |
| BL label | 跳轉到標號址,并將返回地址保存在LR中 |
| BLX Rm | 結合BX和BL的特點,跳轉到Rm指定的地址,并將返回地址保存在LR中,切換指令集 |
下面重點學習一下B和BL指令,因為要在匯編中進行函數調用使用的就是B和BL指令。
- B指令
這是最簡單的跳轉指令,B指令會將PC寄存器的值設置為跳轉目標地址,一旦執行B指令,ARM處理器就會立即跳轉到指定的目標地址。如果要調用的函數不會返回到原來的執行處,那就可以用B指令。_start: ldr sp, = 0x8020000 @設置棧指針 b main @跳轉到main函數
上述代碼就是典型的在匯編中初始化語言支行環境,然后跳轉到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
上述代碼中第4行就是執行C語言的中斷處理函數,當處理完成以后就需要返回來繼續執行下面的程序,所以使用BL指令。
學習總結
學習了常用的匯編指令中的數據傳輸: MOV 、MRS、MSR。存儲器LDR、STR。壓棧和出棧指令:PUSH、POP、STMFD SP!LDMFD SP!。跳轉指令 B、BX、BL、BLX。了解這些指令后,在分析啟動文件中就可以看得懂匯編指令了。
-
寄存器
+關注
關注
31文章
5363瀏覽量
121193 -
C語言
+關注
關注
180文章
7614瀏覽量
137728 -
匯編指令
+關注
關注
0文章
38瀏覽量
11511 -
MRS
+關注
關注
0文章
7瀏覽量
7655 -
Cortex-A7
+關注
關注
0文章
34瀏覽量
16545
發布評論請先 登錄
相關推薦
評論