汇编指令
汇编指令:逻辑指令、算术指令、跳转指令
.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}