匯編指令:邏輯指令、算術指令、跳轉指令
.text /* 邏輯指令 */ mov r7,#0x88 @ and r0,r1,#0xFF //r0=r1&0xFF @ orr r7,r7,#0xffff77 //r7=r7&0x88,清除7號3號位,但立即數太大,報錯 bic r7,r7,#0x88 //清除r7中7號3號 @ tst r0,#0x20 //測試5號位是否為0,為0則Z(30號位)標志置1 @ cmp r1,r0 //將R1與R0相減做比較,並根據結果設置CPSR的標志位 @ eg: 使能中斷和快速中斷 @ mrs r0,cpsr //對cpsr操作需要以寄存器為中介 @ bic r0,r0,#0xc0 //I-6,F-7,使能,將6/7位清零 @ msr cpsr,r0 @ eg: 判斷當前工作狀態位ARM狀態,是則切換到user工作模式? @ mrs r0,cpsr @ tst r0,#0x20 //5號位-1-Thumb狀態,0-ARM狀態 @ andeq r0,#0xffffffe0 //1110 0000 ,先把M[4:0]清零 @ orreq r0,#0x10 //將4號位置1 @ mrseq cpsr,r0 //上一判斷為真,則執行 /* 算術指令 */ @ add r0,r1,r2 //ro=r1+r2 @ sub r0,r1,#3 //ro=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 .end
用匯編實現以下功能:
1 void main(void) 2 { 3 int ret=0; 4 func1(2); 5 while(1) {} 6 } 7 8 func1(int a) 9 { 10 if(a=2) 11 return func2(a); 12 else 13 return func3(a); 14 } 15 16 func2(int a) 17 { 18 return a+3; 19 } 20 21 func3(int a) 22 { 23 return a-1; 24 }
示例代碼(1)
1 .text 2 3 main: 4 mov r5,#0 //0x00 5 mov r0,#2 //0x04 6 bl func1 //PC:0x08 LR:0x0C(12) 7 8 main_end: 9 b main_end @ while(1) {}; 死循環 10 11 func1: 12 cmp r0,#2 //PC:0x10(16) LR:0x0C 13 bleq func2 //PC:0x14(20) LR:0x18(24) 注意此處LR被跳轉指令里面嵌套的跳轉指令覆蓋了,導致無法跳回第一次跳轉指令的下一指令 14 blne func3 //跳轉回來時,PC:0x18 LR:0x18 往下走到0x1C, 15 func1_end: 16 mov pc,lr //PC:0x1C(28) LR:0x18,又將跳回0x18,成死循環,跳不出func1 17 18 func2: 19 add r0,#3 //PC:0x20(32) LR:0x18 20 mov pc,lr //PC:0x24 LR:0x18 21 22 func3: 23 sub r0,r0,#1 @或者寫成sub r0,#1 24 func2_end: 25 26 .end 27
為了避免跳轉指令嵌套導致LR被覆蓋的問題,可以在嵌套調用的函數里另設寄存器R儲存會被覆蓋的LR值,在跳轉時,將R賦PC就可以
1 .text 2 3 main: 4 mov r5,#0 //0x00 5 mov r0,#2 //0x04 6 bl func1 //PC:0x08 LR:0x0C(12) 7 8 main_end: 9 b main_end @ while(1) {}; 死循環 10 11 func1: 12 mov r12,lr //保存LR:0x0C,避免被覆蓋 13 cmp r0,#2 14 bleq func2 // 注意此處LR被跳轉指令里面嵌套的跳轉指令覆蓋了,導致無法跳回第一次跳轉指令的下一指令 15 blne func3 16 func1_end: 17 mov pc,r12 18 19 func2: 20 add r0,#3 21 mov pc,lr 22 23 func3: 24 sub r0,r0,#1 @或者寫成sub r0,#1 25 func2_end: 26 27 .end 28
1 .text 2 3 //load/store架構規定,存儲器之間不能直接拷貝,需要通過寄存器做中轉 4 ldr r0,[r1] //r0=*r1,r1里面存放的是地址,把該地址存放的內容讀入到r0 5 //LDRB(byte) LDRH(half word) 6 ldr r0,[r1,#8] //r0= *(r1+8) 存儲器地址為r1+8的字數據讀入寄存器0 7 ldr pc,_irq //pc= *(_irq) 將標號中的內容放入PC中 8 9 str r0,[r1] // *r1=r0 將r0中的值寫入存儲器地址為r1的空間中,並將r1+4寫入r1 10 11 str r0,[r1],#4 //r0=*r1,r1=r1+4 ,將r0中的值寫入存儲器地址為r1的空間中,並將r1+4寫入r1 12 13 str r0,[r1,#4] //*r0=(r1+4) 將r0中的字數據寫入以r1+4為地址的內存中 14 15 .end

示例:拷貝srcBuf里的內容到destBuf中
.text ldr r0,=srcBuf @r0存放src的地址 ldrb r1,[r0] @將r0里地址(src)里的(1byte)數據存入r1 ldr r0,=destBuf @r0存放dest地址 strb r1,[r0] @將r1里的數據存入r0里的地址的空間 scrBuf: .byte 0x01,0x02,0x03,0x04
.data destBuf: .space 8 .end
示例2:用匯編實現以下功能:
main() { int i=0; const char buf[]={1,2,3}; char destbuf[8]; for(i=0;i<3;i++) { destbuf[i] = buf[i]; } }
main: mov r5,#0 @用於for循環計數 ldr r7,=buf ldr r8,=destbuf loop: cmp r5,#3 beq main_end add r5,#1 ldrb r0,[r7],#1 @將r7里的地址buf里的1byte數據存入r0后,r7=r7+1 strb r0,[r8],#1 @將r0的值賦給r8里的地址dest空間后,r8=r8+1 b loop main_end: b main_end buf: .byte 1,2,3 @定義在代碼段僅可讀,在數據段可讀可寫 .data destbuf: .space 8 @定義空間大小為8個字節 .end

GNU匯編偽指令
.text // 將定義符開始的代碼編譯到代碼段 .data // 將定義符開始的代碼編譯到數據段 .end //文件結束 .equ GPG3, 0xFFFF //定義宏 .byte //定義變量1字節 .word //定義word變量(4字節 32位機) .string //定義字符串 .string "abc\0" .global _start //聲明_start為去全局符號
批量操作指令
ia --- Increment After
ib --- Increment Before
da --- Decrement After
db --- Decrement Befor
stmdb和ldmia指令一般配對使用,stmdb用於將寄存器壓棧,ldmia用於將寄存器彈出棧,作用是保存使用到的寄存器。
詳見:https://blog.csdn.net/minsophia/article/details/53080183


指令:stmdb sp!,{r0-r12,lr}
含義:sp = sp - 4,先壓lr,sp = lr(即將lr中的內容放入sp所指的內存地址)。sp = sp - 4,再壓r12,sp = r12。sp = sp - 4,再壓r11,sp = r11......sp = sp - 4,最后壓r0,sp = r0。
如果想要將r0-r12和lr彈出,可以用ldmia指令:
指令:ldmia sp!,{r0-r12,lr}
