ARM指令
搬移指令
mov r13,#3
mov r0,r1
mov r0,r1,LSL#2 @LSL#2表示邏輯左移兩位,將r1左移兩位之後的數值賦給寄存器r0
mov r0,r1,LSR#2 @LSR#2表示邏輯右移兩位,將r1右移兩位之後的數值賦給寄存器r0
mrs r0,cpsr @讀取cpsr寄存器的值,賦給r0。注意特殊寄存器的讀取使用mrs指令
msr cpsr,r0 @將r0的值賦給cpsr。注意特殊寄存器的寫入使用msr指令
條件執行
if (a==0) x=0;
if (a>0) x=x+3;
cmp r0,#0 @比較r0與0是否相等
moveq r1,#0 @mov是上面講過的搬移指令,eq判斷上一條指令執行的結果是否相等,如果相等,則將0賦值給r1
addgt r1,r1,#3 @add是加法運算,gt判斷cmp指令r0是否大於0,如果大於零,則將r1+3的值賦給r1
指令是如何存儲的,如何被解析的?
寫c代碼--------------------------------->生成彙編代碼------------------------->FLASH中最終存儲機器指令(二進制)
指令機器碼(FLASH中最終存儲機器指令(二進制)進行解析)
當賦給寄存器的值過大時(一般大於255),就要使用僞指令對其賦值:ldr r0,=0x123456
邏輯指令
and r0,r1,#0xFF // r0 = r1&0xFF(與運算)
orr r3,r0,#0x0F // r3 = r0|0x0F (或運算)
bic r0,r0,#0x03 // 清除r0中的0號位和1號位
tst r0,#0x20 //測試第6位是否爲0 ,爲0則Z標誌置1
cmp r1,r0 //將R1與R0相減做比較,並根據結果設置CPSR的標誌位
例:
• 使能中斷和快速中斷?
mrs r0,cpsr
bic r0,r0,#0xc0 (清零F位和I位)
msr cpsr,r0
• 判斷當前工作狀態是否是ARM狀態,是則切換到user 工作模式?
mrs r0,cpsr
tst r0,#0x20
andeq r0,r0,#0xFFFFFFE0 (低5位清零)
orreq r0,r0,#0x10
msreq cpsr,r0
算術指令
add r0,r1,r2 //r0=r1+r2
sub r0,r1,#3 //r0= r1 - 3
sub r0,r1,r2,LSL#1 //r0=r1-(r2<<1)
mul r1,r2,r3 //r1=r2*r3
跳轉指令
b main //跳轉到標號爲main地代碼處
bl func //保存下一條要執行的指令的位置到 LR寄存器,跳轉函數func
//當跳轉代碼結束後,用MOV PC,LR指令跳回來
beq addr //當CPSR寄存器中的Z條件碼置位時,跳轉到該地址處
bne addr //當不等時,跳轉到地址addr
例:
用匯編實現下面功能:
void main(void)
{
int ret=0;
func1(2);
while(1) {};
}
func1(int a)
{
if(a==2)
return func2(a);
else
return func3(a);
}
func2(int a)
{
return a+3;
}
func3(int a)
{
return a-1;
}
實現 延時1秒函數:
@delay fos 1 second
delay1s:
ldr r4,=0x3FFFF
loop_delay1s:
sub r4,r4,#1
cmp r4,#0
bne loop_delay1s
delay1s_end:
mov pc,lr
測驗:
用匯編實現求最大公約數?(如9 15 值是3)
int GCD(int a,int b)
{
while(1)
{
if(a==b)
break;
if(a>b){
a=a-b;
}else{
b=b-a;
}
}
return a;
}
Load/Store 指令
注:load/store架構規定,存儲器之間不能直接拷貝,需通過寄存器做中轉 (比如講FLASH中的指令傳入到內存中就必須使用CPU寄存器做中轉)
ldr r0,[r1] (load) //r0=*r1 r1裏存放的是地址,把該地址裏存放的內容讀入到r0中
//LDRB(byte) LDRH(half word)
ldr r0,[r1,#8] //r0=*(r1+8) 存儲器地址爲r1+8的字數據讀入寄存器0。
ldr pc,_irq // pc = *(_irq) 將標號中的內容放入pc中
str r0,[r1] (store) // *r1 = r0 將寄存器r0中值寫入到存儲器地址爲r1的空間中
str r0,[r1],#4 // r0=*r1, r1=r1+4 將r0 中的字數據寫入以r1爲地址的內存中,並將新地址r1+4 寫入r1
str r0,[r1,#4] //*(r1+4)=r0 將r0 中的字數據寫入以r1+4 爲地址的內存中
Pre or Post Indexed 尋址
Pre-indexed: STR r0,[r1,#12]
例:
拷貝srcBuf裏內容 到destBuf中:
.text
ldr r0,=srcBuf
ldrb r1,[r0]
ldr r0,=destBuf
strb r1,[r0]
srcBuf:
.byte 0x01,02,0x03,0x04
.data
destBuf:
.space 8
.end
測驗
用匯編實現下面功能 :
main()
{
int i=0;
const char buf[]={1,2,3};
char destBuf[8];
for(i=0,i<3,i++)
{
destBuf[i] = buf[i];
}
}
GNU 彙編僞指令
.text 將定義符開始的代碼編譯到代碼段
.data 將定義符開始的代碼編譯到數據段
.end 文件結束
.equ GPG3CON, 0XE03001C0 定義宏(即用GPG3CON代替 0XE03001C0)
.byte 定義變量 1字節
.byte 0x11,'a',0 定義字節數組
.word 定義word變量 (4字節 32位機)
.word 0x12344578,0x33445566
.string 定義字符串 .string "abcd\0"
ldr r0,=0xE0028008 載入大常數0xE0028008 到r0中
.global _start 聲明_start 爲全局符號
例 拷貝ROM中字符串到RAM中:
.text
start:
ldr r5,=srcBuf
ldr r6,=destBuf
loop:
ldrb r4,[r5]
cmp r4,#0
beq main_end
ldrb r0,[r5],#1
strb r0,[r6],#1
b loop
main_end:
b main_end
srcBuf:
.string "abcdefg\0"
.data
destBuf:
.space 8
.end
批量操作指令
• 批量操作指令 (ia-Increment After ib-Increment Before da-Dec After db-Dec Before)
ldmia r0!, {r3 - r10} //r0裏地址指向的內容批量,load 到r3~r10寄存器中, r0裏地址會自動加4
stmia r0!, {r3 - r10} //把r3~r10寄存器中內容,store 到r0裏地址執行空間中,r0裏地址會自動加4
• 例:實現塊數據批量拷貝
r12指向源數據起始地址
r14指向源數據尾地址
r13指向目的數據起始地址
例 批量拷貝數據:
.text
ldr r12,=srcBuf
ldr r13,=dstBuf
ldmia r12!,{r0 - r11}
stmia r13!,{r0 - r11}
.data
srcBuf:
.string "abdfasdf13535dfksjdlfkjlksldkjflkl\0"
srcBuf_end:
dstBuf:
.space 12*4
.end
堆棧操作指令
stmfd sp!,{r0-r12,lr} 將寄存器r0~r12 lr中的值存入棧中
常用於中斷保護現場,! 表示會自動偏移
ldmfd sp!,{r0-r12,pc}^ 將棧中值逐個彈出到寄存器r0~r12 pc中
常用於中恢復斷現場,^表示會恢復spsr到cpsr
ARM 指令 高級
• 軟中斷指令
swi 0x02 產生軟中斷, 軟中斷號爲2