數(shù)據(jù)處理指令:
-
數(shù)據(jù)處理指令只可用于寄存器之間或寄存器與立即數(shù)之間
移位指令
- Rotate left(ROL) 可以通過(guò) rotate right (ROR)(32-number)來(lái)實(shí)現(xiàn), e.g. ROL 10 相當(dāng)于 ROR 22.
- 立即數(shù):只要該數(shù)嘱支,可以通過(guò)0x00-0xFF中某個(gè)數(shù),循環(huán)右移偶數(shù)位而產(chǎn)生挣饥,就是合法的mov的操作數(shù)除师,否則就是非法的mov的操作數(shù)。
mov r0, #256 ; mov r0, #0x100 mov r1, #0x40, 30 ; mov r1, #0x100
偽指令LDR可以用來(lái)MOV任意32位的常數(shù)到寄存器中
- 例如:
SUB r0,r1,#5 ;r1-5->r0 (立即尋址)
ADD r2,r3,r3,LSL #2 ;R3x4+r3->r2
ANDS r4,r4,#0x20 ;r4+0x20->r4,更新條件碼標(biāo)志位
ADDEQ r5,r5,r6 ;r5+r6->r5(條件-相等)(寄存器尋址)
存儲(chǔ)器存取指令:
-
存儲(chǔ)器存取指令用于通用寄存器與內(nèi)存單元之間
- 例如
LDR r0,[r1],#4 ;r1+4->r0(基址變址尋址)
STRNEB r2,[r3,r4] ;NE符合-將r2低8位數(shù)寫到[r3+r4]內(nèi)存單元(寄存器間接尋址)
LDRSH r5,[r6,#8]! ;[r6+8]->r5(半字節(jié))扔枫,r5中高16位設(shè)置成該字節(jié)的符號(hào)位
STMFD sp!,{r0,r2-r7,r10} ;出棧(多寄存器尋址)
- STR :從寄存器中將32bits字?jǐn)?shù)據(jù)存儲(chǔ)到內(nèi)存中
-
LDM/STM(出棧/入棧) 用于基址寄存器所指的一片連續(xù)內(nèi)存到寄存器列表中的多個(gè)寄存器之間的數(shù)據(jù)傳送汛聚。
PSR 讀寫指令
- MRS 將CPSR/SPSR的內(nèi)容讀取到通用寄存器中
- MSR 將通用寄存器的值寫到CPSR/SPSR的特定位域(f,c)中
- 例如
MRS R0, CPSR ;R0 = CPSR
MSR CPSR_c, R0 ;CPSR [c] = R0
ARM 跳轉(zhuǎn)分支指令
-
B <label>
PC 相對(duì)尋址 -
BL <子程序>
保存返回地址到 LR
返回時(shí)從 LR 恢復(fù) PC
對(duì)于 non-leaf 函數(shù)(func1), LR 必須壓棧保存
ARM條件執(zhí)行與標(biāo)志位
- ARM指令可以通過(guò)添加合適的條件碼后綴的方式使其條件執(zhí)行
- 通常情況下茧吊,數(shù)據(jù)處理指令不影響條件碼標(biāo)志位贞岭,但可以添加"S"后綴來(lái)改變標(biāo)志位狀態(tài)。
常用條件碼如下:
- 例如:
- if (a==0) func(1);
CMP r0,#0?
MOVEQ r0,#1
?BLEQ func
- if (a==0) x=0;?
if (a>0) x=1;
CMP r0,#0
?MOVEQ r1,#0?
MOVGT r1,#1?
- if (a==4 || a==10) x=0;
CMP r0,#4
?CMPNE r0,#10?
MOVEQ r1,#0
軟件中斷指令SWI
- SWI 指令用于產(chǎn)生軟件中斷搓侄,以便用戶程序能調(diào)用操作系統(tǒng)的系統(tǒng)例
程。 - 操作系統(tǒng)在SWI 的異常處理程序中提供相應(yīng)的系統(tǒng)服務(wù)话速,指令中
24 位的立即數(shù)指定用戶程序調(diào)用系統(tǒng)例程的類型讶踪,相關(guān)參數(shù)通過(guò)通用
寄存器傳遞。 - 當(dāng)指令中24 位的立即數(shù)被忽略時(shí)泊交,用戶程序調(diào)用系統(tǒng)例
程的類型由通用寄存器R0 的內(nèi)容決定乳讥。
ARM 偽指令
1. 符號(hào)定義偽指令
符號(hào)定義偽指令用于定義ARM 匯編程序中的變量、對(duì)變量賦值以及定
義寄存器的別名等操作
- GBLA廓俭、GBLL 和GBLS 偽指令用于定義一個(gè)ARM 程序中的全局變量云石,
并將其初始化。GBLA Number 研乒;定義一個(gè)全局的數(shù)字變量并初始化為0汹忠,變量名為Number Number SETA 0xaa ;將該變量賦值為0xaa GBLL Logical 雹熬;定義一個(gè)全局的邏輯變量并初始化為F(False)宽菜,變量名為L(zhǎng)ogical Logical SETL {TRUE} ;將該變量賦值為真 GBLS Str 竿报;定義一個(gè)全局的字符串變量并初始化為空铅乡,變量名為Str Str SETS “Testing” ;將該變量賦值為“Testing”
- LCLA烈菌、LCLL 和LCLS 偽指令用于定義一個(gè)ARM 程序中的局部變量阵幸,
并將其初始化花履。 - SETA、SETL挚赊、SETS 偽指令用于給一個(gè)已經(jīng)定義的全局變量或局部變
量賦值诡壁。 - RLIST 偽指令可用于對(duì)一個(gè)通用寄存器列表定義名稱,使用該偽指令定
義的名稱可在ARM 指令LDM/STM 中使用咬腕。LDM/STM 指令中欢峰,列
表中的寄存器訪問(wèn)次序?yàn)楦鶕?jù)寄存器的編號(hào)由低到高,而與列表中的寄
存器排列次序無(wú)關(guān)涨共。RegList RLIST {R0-R5纽帖,R8,R10} 举反;將寄存器列表名稱定義為RegList懊直,可在ARM 指令LDM/STM 中通過(guò)該名稱訪問(wèn)寄存器列表。
2. 數(shù)據(jù)定義偽指令
數(shù)據(jù)定義偽指令一般用于為特定的數(shù)據(jù)分配存儲(chǔ)單元火鼻,同時(shí)可完成已分
配存儲(chǔ)單元的初始化室囊。
- DCB 偽指令用于分配一片連續(xù)的字節(jié)存儲(chǔ)單元并用偽指令中指定的表
達(dá)式初始化。Str DCB “This is a test魁索!” 融撞;為Str分配一片連續(xù)的字節(jié)存儲(chǔ)單元并初始化。 Str = “This is a test粗蔚!”
- SPACE 偽指令用于分配一片連續(xù)的存儲(chǔ)區(qū)域并初始化為0尝偎。
DataSpace SPACE 100;分配連續(xù)100 字節(jié)的存儲(chǔ)單元并初始化為0鹏控。 DataSpace % 100
- MAP 偽指令用于定義一個(gè)結(jié)構(gòu)化的內(nèi)存表的首地址致扯。MAP 也可用“^”
代替 - FIELD 偽指令用于定義一個(gè)結(jié)構(gòu)化內(nèi)存表中的數(shù)據(jù)域。FILED 也可用
“#”代替当辐。MAP 0x100, R0 抖僵;定義結(jié)構(gòu)化內(nèi)存表首地址的值為0x100+R0。 A FIELD 16 缘揪;定義A 的長(zhǎng)度為16 字節(jié)耍群,位置為0x100+R0 B FIELD 32 ;定義B 的長(zhǎng)度為32 字節(jié)寺晌,位置為0x110+R0 S FIELD 256 世吨;定義S 的長(zhǎng)度為256 字節(jié),位置為0x130+R0
3. 匯編控制偽指令
匯編控制偽指令用于控制匯編程序的執(zhí)行流程呻征。
- IF耘婚、ELSE、ENDIF 偽指令能根據(jù)條件的成立與否決定是否執(zhí)行某個(gè)指
令序列陆赋。GBLL Test 沐祷;聲明一個(gè)全局的邏輯變量嚷闭,變量名為Test IF Test = TRUE 指令序列1 ELSE 指令序列2 ENDIF
- WHILE、WEND 偽指令能根據(jù)條件的成立與否決定是否循環(huán)執(zhí)行某個(gè)
指令序列赖临。GBLA Counter 胞锰;聲明一個(gè)全局的數(shù)學(xué)變量,變量名為Counter Counter SETA 3 兢榨;由變量Counter 控制循環(huán)次數(shù) …… WHILE Counter < 10 指令序列 WEND
- MACRO嗅榕、MEND 偽指令可以將一段代碼定義為一個(gè)整體,稱為宏指令吵聪,然后就可以在程序中通過(guò)宏指令多次調(diào)用該段代碼凌那。MEXIT 用于從宏定義中跳轉(zhuǎn)出去。
MACRO $HandlerLabel HANDLER $Handlerpara吟逝;宏的名稱為HANDLER帽蝶,有1 個(gè)參數(shù)$Handlerpara, $HandlerLabel為標(biāo)號(hào),在宏指令被展開(kāi)時(shí)會(huì)被替換為用戶定義的符號(hào)块攒。 sub sp,sp,#4 ;decrement sp(to store jump address) stmfd sp!,{r0} ;PUSH the work register to stack(lr does not push because it return to original address) ldr r0,=$Handlerpara;load the address of HandleXXX to r0 ldr r0,[r0] ;load the contents(service routine start address) of HandleXXX str r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stack ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR) MEND
4. 其他常用偽指令
- AREA 偽指令用于定義一個(gè)代碼段或數(shù)據(jù)段励稳。
- ALIGN 偽指令可通過(guò)添加填充字節(jié)的方式,使當(dāng)前位置滿足一定的對(duì)
其方式囱井。 - CODE16 偽指令通知編譯器其后的指令序列為16 位的Thumb 指令驹尼,
- CODE32 偽指令通知編譯器其后的指令序列為32 位的ARM 指令。
- ENTRY 偽指令用于指定匯編程序的入口點(diǎn)
- END 偽指令用于通知編譯器已經(jīng)到了源程序的結(jié)尾庞呕。
- EQU 偽指令用于為程序中的常量扶欣、標(biāo)號(hào)等定義一個(gè)等效的字符名稱,
類似于C 語(yǔ)言中的#define千扶。其中EQU 可用“*”代替。 - EXPORT 偽指令用于在程序中聲明一個(gè)全局的標(biāo)號(hào)骆捧,該標(biāo)號(hào)可在其他的
文件中引用澎羞。EXPORT 可用GLOBAL 代替。 - IMPORT 偽指令用于通知編譯器要使用的標(biāo)號(hào)在其他的源文件中定義敛苇,
但要在當(dāng)前源文件中引用妆绞,而且無(wú)論當(dāng)前源文件是否引用該標(biāo)號(hào),該標(biāo)
號(hào)均會(huì)被加入到當(dāng)前源文件的符號(hào)表中枫攀。 - EXTERN 偽指令用于通知編譯器要使用的標(biāo)號(hào)在其他的源文件中定義括饶,
但要在當(dāng)前源文件中引用,如果當(dāng)前源文件實(shí)際并未引用該標(biāo)號(hào)来涨,該標(biāo)
號(hào)就不會(huì)被加入到當(dāng)前源文件的符號(hào)表中图焰。 - GET 偽指令用于將一個(gè)源文件包含到當(dāng)前的源文件中,并將被包含的
源文件在當(dāng)前位置進(jìn)行匯編處理蹦掐〖几幔可以使用INCLUDE 代替GET僵闯。 - INCBIN 偽指令用于將一個(gè)目標(biāo)文件或數(shù)據(jù)文件包含到當(dāng)前的源文件
中。 - RN 偽指令用于給一個(gè)寄存器定義一個(gè)別名
AREA Init藤滥,CODE鳖粟,READONLY,ALIGN=3 拙绊;該偽指令定義了一個(gè)代碼段向图,段名為Init,屬性為只讀标沪, 并指定后面的指令為8 字節(jié)對(duì)齊榄攀。
ENTRY ;指定應(yīng)用程序的入口點(diǎn)
Temp RN R0 谨娜;將R0 定義一個(gè)別名Temp
GET a1.s 航攒;通知編譯器當(dāng)前源文件包含源文件a1.s
GET C:\a2.s ;通知編譯器當(dāng)前源文件包含源文件C:\ a2.s
INCBIN a1.bin趴梢;通知編譯器當(dāng)前源文件包含文件a1.bin
EXPORT Stest 漠畜;聲明一個(gè)可全局引用的標(biāo)號(hào)Stest
IMPORT Main ;通知編譯器當(dāng)前文件要引用標(biāo)號(hào)Main坞靶,但Main 在其他源文件中定義
Test EQU 50 憔狞;定義標(biāo)號(hào)Test 的值為50
CODE32 ;通知編譯器其后的指令為32 位的ARM 指令
LDR R0彰阴,=NEXT+1 瘾敢;將跳轉(zhuǎn)地址放入寄存器R0
BX R0 ;程序跳轉(zhuǎn)到新的位置執(zhí)行尿这,并將處理器切換到Thumb 工作狀態(tài)
……
CODE16 簇抵;通知編譯器其后的指令為16 位的Thumb 指令
NEXT LDR R3,=0x3FF
……
END 射众;指定應(yīng)用程序的結(jié)尾
C與ARM匯編混合編程
1. C/C++中嵌入?yún)R編程序
void enable_IRQ(void) //使能中斷程序
{
int tmp; //定義臨時(shí)變量碟摆,后面使用
__asm //內(nèi)嵌匯編程序的關(guān)鍵詞
{
MRS tmp, CPSR //把狀態(tài)寄存器加載給tmp
BIC tmp, tmp, #80 //將IRQ控制位清0
MSR CPSR_c, tmp //加載程序狀態(tài)寄存器
}
}
void disable_IRQ(void) //禁止中斷程序
{
int tmp; //定義臨時(shí)變量,后面使用
__asm //內(nèi)嵌匯編程序的關(guān)鍵詞
{
MRS tmp, CPSR //把狀態(tài)寄存器加載給tmp
ORR tmp, tmp, #80 //將IRQ控制位置1
MSR CPSR_c, tmp //加載程序狀態(tài)寄存器
}
}
2. 匯編與C/C++程序的變量相互訪問(wèn)
- 在C/C++程序中聲明的全局變量可以被匯編程序通過(guò)地址間接訪問(wèn)叨橱。
AREA Example, CODE, READONLY EXPORT AsmAdd IMPORT g_cVal @聲明外部變量g_cVal,在C中定義的全局變量 Add LDR R1, =g_cVal @裝載變量地址 LDR R0, [R1] @從地址中讀取數(shù)據(jù)到R0 ADD R0, R0, #1 @加1操作 STR R0, [R1] @保存變量值 MOV PC, LR @程序返回 END
- 在匯編程序中聲明的數(shù)據(jù)可以被C/C++程序所訪問(wèn)典蜕。在匯編程序中用EXPORT/GLOBAL偽指令聲明該符號(hào)為全局標(biāo)號(hào),C/C++程序中定義相應(yīng)數(shù)據(jù)類型的指針變量
匯編:
C/C++:EXPORT Message @聲明全局標(biāo)號(hào) Message DCB "HELLO$" @定義了5個(gè)有效字符,$為結(jié)束符
extern char* Message; int MessageLength() { int Length = 0; char *pMessage; //定義字符指針變量 pMessage = Message; //指針指向Message 內(nèi)存塊的首地址 /*while循環(huán)罗洗,統(tǒng)計(jì)字符串的長(zhǎng)度*/ while(*pMessage != '$') //$為字符串的結(jié)束符 { Length++; pMessage++; } return(Length); //返回字符串的長(zhǎng)度 }
3. 匯編與C/C++程序的函數(shù)相互調(diào)用(APTCS規(guī)則)
匯編程序設(shè)置要遵循APTCS規(guī)則愉舔,保證程序調(diào)用時(shí)參數(shù)的正確傳遞。
-
C程序調(diào)用匯編程序伙菜。
匯編程序中使用EXPORT偽指令聲明本子程序可外部使用轩缤,使其他程序可調(diào)用該子程序;在C語(yǔ)言程序中使用extern關(guān)鍵字聲明外部函數(shù)(聲明要調(diào)用的匯編子程序),才可調(diào)用此匯編的子程序典奉。
strcopy實(shí)現(xiàn)匯編代碼:AREA Example, CODE, READONLY @聲明代碼段Example EXPORT strcopy @聲明strcopy躺翻,以便外部函數(shù)調(diào)用 strcopy @ R0為目標(biāo)字符串的地址, R1為源字符串的地址 LDRB R2, [R1], #1 @讀取字節(jié)數(shù)據(jù),源地址加1 STRB R2, [R0], #1 @保存讀取的1字節(jié)數(shù)據(jù)卫玖,目標(biāo)地址加1 CMP R2, #0 @判斷字符是否復(fù)制完畢 BNE strcopy @沒(méi)有復(fù)制完公你,繼續(xù)循環(huán)復(fù)制 MOV PC, LR END
C/C++:
#include <stdio.h> extern void strcopy(char *d, const char *s); //聲明外部函數(shù),即要調(diào)用的匯編子程序 int main(void) { const char *srcstr = "First ource"; //定義字符串常量 char dststr[] = "Second string-destination"; //定義字符串變量 printf("Before copying: \n"); printf("src=%s, dst=%s\n", srcstr, dststr); //顯示源字符串和目標(biāo)字符串的內(nèi)容 strcopy(dststr, srcstr); //調(diào)用匯編子程序R0=dststr, R1=srcstr printf("After copying: \n"); printf("src=%s, dst=%s\n", srcstr, dststr); //顯示復(fù)制后的結(jié)果 return(0); }
-
匯編程序調(diào)用C程序
在匯編程序中使用IMPORT偽指令聲明將要調(diào)用的C程序函數(shù)假瞬。在調(diào)用C程序時(shí)陕靠,要正確設(shè)置入口參數(shù),然后使用BL指令調(diào)用脱茉。
C/C++ sum函數(shù):int sum(int a, int b, int c, int d, int e) { return(a+b+c+d+e); //返回5個(gè)變量的和 }
匯編調(diào)用sum函數(shù):
AREA Example, CODE, READONLY IMPORT sum @ 聲明外部標(biāo)號(hào)sum剪芥,即C函數(shù)sum() EXPORT CALLSUM UM STMFD SP!, {LR} @LR寄存器入棧 MOV R0, #1 @設(shè)置sum函數(shù)入口參數(shù),R0為參數(shù)a MOV R1, #2 @R1為參數(shù)b MOV R2, #3 @R2為參數(shù)c MOV R3, #5 @參數(shù) e=5琴许,保存到堆棧中 STR R3, {SP, #-4}! MOV R3, #4 @R3為參數(shù)d, d=4 BL sum @調(diào)用C程序中的sum函數(shù)税肪,結(jié)果放在R0中 ADD SP, SP, #4 @調(diào)整堆棧指針 LDMFD SP, {PC} @程序返回 END
寄存器最多傳遞4個(gè)參數(shù)(R0-R3),超出四個(gè)的需要用到堆棧榜田。
以上程序使用了5個(gè)參數(shù)鳞溉,分別使用寄存器R0存儲(chǔ)第1個(gè)參數(shù)砰左,R1存儲(chǔ)第2個(gè)參數(shù)枢劝,R2存儲(chǔ)第3個(gè)參數(shù)狸涌,R3存儲(chǔ)第4個(gè)參數(shù),第5個(gè)參數(shù)利用堆棧傳送辩块。
由于利用了堆棧傳遞參數(shù)蛔六,在程序調(diào)用結(jié)束后要調(diào)整堆棧指針。匯編程序中調(diào)用了C程序的sum子函數(shù)废亭,實(shí)現(xiàn)了1+2+3+4+5国章,最后相加結(jié)果保存在R0寄存器中。