基本指令学习
在博文:keil下ARM汇编程序建立与调试简介中学习建立ARM汇编程序工程。
本博文开始学习一步一步写ARM汇编程序。
一、重要概念理解
1. 立即数
1)把数据转换成二进制形式,从低到高写成 4位1组的形式,最高位一组不够4位的前面补0
2)数1的个数,如果大于8个【可能也是立即数,取反】不是立即数,如果小于等于8个 进行下面步骤
3)如果数据中间有连续的大于等于24个0,循环左移2的倍数,使高位全为0
4)找到最高位的1,去掉前面的最大偶数个0
5)找到最低位的1,去掉后面偶数个0
6)数剩下的位数,如果小于等于8位,那么这个数就是个立即数,反之就不是立即数
举例:判断0x80000001是不是立即数
1)二进制形式:1000 0000 00000000 0000 0000 0000 0001
2)1的个数小于8个,进入第三步
3)循环左移2位,这步结果为:00 0000 0000 0000 0000 0000 0000 000110
4)这步结果为:0110
5)这步结果为:0110
6)满足条件,所以0x80000001是立即数
判断一个数是否是立即数的目的是:在汇编操作中,有些指令操作的数只能是立即数,比如movr0,#0xnum ; num就必须是立即数。
2. 条件码,
条件码:本条指令的执行,依赖于上一个指令的执行结果举例理解,
比如寄存器中r0和r1分别保存两个数,如果r0小于r1,将r1值传给r0: cmp r0,r1 ;比较r0和r1,会有某个标志位记下它们比较的结果movlt r0,r1 ;movlt = mov + lt ,查上表知为lt为带符号数小于,也就是说mov在r0< r1时,才执行mov操作。如何查看条件是否成立呢?cmpr0,r1执行后,cpu就查看cpsr中N是否等于V, 不等于是说明r0< r1成立,执行mov操作。
3. cpsr中各个标志位具体含义 标志位 含 义 N 当用两个补码表示的带符号数进行运算时,N=1表示运算的结果为负数;N=0表示运算的结果为正数或零 Z Z=1表示运算的结果为零,Z=0表示运算的结果非零。 C 可以有4种方法设置C的值: -加法运算(包括CMP):当运算结果产生了进位时(无符号数溢出),C=1,否则C=0。 -减法运算(包括CMP):当运算时产生了借位时(无符号数溢出),C=0,否则C=1。 -对于包含移位操作的非加/减运算指令,C为移出值的最后一位。 -对于其它的非加/减运算指令,C的值通常不会改变。 V 可以有2种方法设置V的值: -对于加减法运算指令,当操作数和运算结果为二进制的补码表示的带符号数时,V=1表示符号位溢出 -对于其它的非加/减运算指令,V的值通常不会改变。 Q 在ARM V5及以上版本的E系列处理器中,用Q标志位指示增强的DSP运算指令是否发生了溢出。在其它版本的处理器中,Q标志位无定义
二、基本指令练习
1. 两个寄存器值相加减,
示例代码如下: areaexample,code,readonly entry start ;r0+ r1 -> r2(注:前面带有; 表示注解) mov r0,#0x2 movr1,#0x1 addr2, r0, r1 ;r0- r1 -> r2 mov r0,#0x2 movr1,#0x1 subr2, r0, r1 end
2. 两个寄存器值相与或异或,示例代码如下 areaexample,code,readonly entry start ;r0and r1 movr0,#0x1 movr1,#0x3 andr0,r0,r1 ;r0orr r1 movr0,#0x2 movr1,#0x1 orrr0,r0,r1 end 3.将特定的第几位清0,示例代码如下: areaexample,code,readonly entry start ;bic将特定的位清0 mov r0,#0xff movr1,#0x04 ;(0000 0100,将第3位清0) bicr2,r0,r1 end
3. 逻辑左移或逻辑右移,示例代码如下 areaexample,code,readonly entry start ;逻辑左移低位补0,高位丢弃 movr0,#0x1 movr1,r0,lsl #1; r1 = r0 << 0 ;逻辑右移低位丢弃,高位补0 movr0,#0xf0000007 movr1,r0,lsr #1 end
4. 算数左移或算数右移,示例代码如下 areaexample,code,readonly entry start ;注:算数左移与逻辑左移是同一个指令都是lsl ;算数右移低位丢弃,高位补符号位 movr0,#0xf0000007 movr1,r0,asr #1 end
5. 循环右移,示例代码如下 areaexample,code,readonly entry start ;循环右移 ror ;左端填充右端移出去的位 ;11110000 0000 0000 0000 0000 0000 0111循环右移结果:1111 1000 00000000 0000 0000 0000 0011 movr0, #0xf0000007 movr1,r0,ror #1 end
6. 带扩展位的循环右移,一次只能移一位,示例代码如下: areaexample,code,readonly entry start ;带扩展位的循环右移唯一的一个不需要指定移位位数的指令 ;移除去的位,移到 cpsr的 c位中 movr0, #0xf0000007 movsr1,r0,rrx ;注意带有s end
调试窗口如下:
注1:r0 为 1111 0000 0000 0000 0000 0000 0000 0111
注2:r1 为 0111 1000 0000 0000 0000 0000 0000 0011,最右面的1移到CPSR寄存器中的C中注3:r15(pc)保存cpu要执行的下一条指令的地址,这个地址保存的内容可以在Disassembly窗口中看到。
三、提高练习
1. 64 位加减,示例代码如下: areaexample,code,readonly entry start ;64位加法,一个64位保存在r0和r1中,一个64位保存在r2和r3中 movr0,#0xffffffff movr1,#0x1 movr2,#0x1 movr3,#0x1 addsr0,r0,r2 adc r4,r1,r3 ;64位减法,一个64位保存在r0和r1中,一个64位保存在r2和r3中 movr0,#0x0 movr1,#0x2 movr2,#0x1 movr3,#0x1 subsr0,r0,r2 ;注意c借位为0,否则为1 sbc r4,r1,r3 end
2. 求两个数差的绝对值,示例代码如下: areaexample,code,readonly entry start ;取两个数差的绝对值 movr0,#5 movr1,#4 cmpr0,r1 beqover subgtr3,r0,r1 subltr3,r1,r0 over bover end
3. 求3个数的最大数 areaexample,code,readonly entry start ;求3个数的最大数,将最大值保存在r0中 mov r0,#3 mov r1,#4 mov r2,#5 cmp r0,r1 movlt r0,r1 ;如果r0 < r1, 将 r1 -> r0 cmpr0,r2 movltr0,r2 ;如果r0< r2, 将 r2 -> r0 end
b与blb
无条件跳转,不考虑回来的问题bl 在跳转时会把下一条指令的地址装载到lr寄存器中,以方便cpu最终能回来 示例:通过bl跳到func,通过b跳到over,通过调试查看lr值,示例代码如下, area example,code,readonly entry start ;bl和b mov r0,#0x1 bl func mov r2,#0x3 b over func mov r1,#0x2 bx lr over b over end 提高:程序在执行时,先跳到func1, 再跳到func2,再跳到func3,然后再返回主程序继续执行。示例代码如下, area example,code,readonly entry start mov r0,#0x1 bl func1 mov r0,#0x2 b over func1 mov r1,lr bl func2 mov r0,#0x3 mov pc,r1 func2 mov r2,lr bl func3 mov r0,#0x04 mov pc,r2 func3 mov r0,#0x05 bx lr over b over end 注1:bl指令的偏移量不是相对于正在执行的那条指令,而是相对于预取指令-三级流水线注2:bl命令,最大寻址空间为±32M,访问其他内存通过ldr指令注3:进入子函数,通过栈保存返回地址+通用寄存器的值
通过读写cpsr spsr设置arm cpu工作模式
注1:需要通过mrs读取cpsr或spsr寄存器值 注2:cpsr_c指令中各个位含义[31:24] 条件标志位 f[23:16] 状态位域 s[15:8] 扩展位域 x[7:0] 控制位域 c示例:通过cpsr spsr设置arm cpu工作模式,代码如下: areaexample,code,readonly entry start bl enable_irq bl disable_irq bl stack_init b over enable_irq ;读取cpsr的值到r0 mrs r0, cpsr ; 将中断标志位置0,使能中断 bicr0,r0,#0x80 msr cpsr_c,r0 bx lr disable_irq mrs r0,cpsr; 将中断标志位置1,禁止中断 orr r0,r0,#0x80 msr cpsr_c,r0 bx lr stack_init ;当前cpu工作模式为svc模式,将堆栈地址初始化为33c00000 ldr sp , =0x33c00000 ; 将cpu工作模式为undef模式,将堆栈地址初始化为0x34000000 msr cpsr_c, #0xdb mov sp , #0x34000000; 将cpu工作模式为abort模式,将堆栈地址初始化为0x33f00000 ;abort msr cpsr_c, #0xd7 ldr sp , =0x33f00000; 将cpu工作模式为irq模式,将堆栈地址初始化为0x33e000000 ;irq msr cpsr_c, #0xd2 ldr sp , =0x33e00000; 将cpu工作模式为usr模式,将堆栈地址初始化为0x33d000000,注意在这种模式下,不可再设置cpsr寄存器,因为usr模式访问资源有限。 ;usr ;msr cpsr_c, 0xd0 ;ldr sp , =0x33d00000;cpu工作模式为返回svc模式 ;svc msr cpsr_c, #0xd3 bx lrover bover end svc模式调试界面如下 undef模式调试界面如下 注1:切换模式 必须通过msr这个指令注2:切换模式后,看到的 sp lr spsr 都是新的模式下的寄存器。注3: usr模式下,不能修改cpsr-----usr是非特权模式
ARM处理器的八种寻址
1. 立即数寻址add,r0,r0,#1
2. 寄存器寻址 add r0, r1, r2
3. 寄存器间接寻址:以寄存器中的值作为操作数的地址 Ldr r0, [r1]
4. 寄存器移位寻址 mov r1,#7 mov r2,#1 add r0, r1, r2 ,lsl #2
5. 基址变址寻址: 将基址寄存器的内容与指令中给出的偏移量相加,得到一个有效的操作地址。通常用于访问连续的地址空间。三种索引方式:前索引 ldr r0, [r1, #4];将r0中的值传递给r1中的值+4的地址,r1中的值保持不变,示例代码如下: ldr r1,=0x40000000 mov r2,#0x1 str r2,[r1,#4]自动索引 ldr r0, [r1, #4]!;将r0中的值传递给r1中的值+4的地址,r1中的值变为r1中的值+4,示例代码如下: ldr r1,=0x40000000 mov r2,#0x1 str r2,[r1,#4]!后索引 ldr r0, [r1],#4;将r0中的值传递给r1中的值的地址,r1中的值变为r1中的值+4,示例代码如下: ldr r1,=0x40000000 mov r2,#0x1 str r2,[r1],#4
6. 多寄存器寻址:可以实现一条指令完成多个寄存器值的传送,最多一次传送16个通用寄存器的值,连续的寄存器用‘-’连接,否则用逗号分隔。Xx可以是ia,ib,da,dbia 每次传递后地址+1ib 每次传递前地址+1da 每次传递后地址-1db 每次传递前地址-1示例代码如下: ldr r1,=0x40000000 mov r2,#0x1 mov r3,#0x2 mov r4,#0x3 mov r5,#0x4 stmia r1!,{r2-r5}
7. 相对寻址,如下代码: blablelable:
8. 堆栈寻址: 堆栈寻址是一种数据结构,按先进先出的方式操作,r13(sp)寄存器指示当前的栈顶位置,arm支持4种堆栈操作方式ia,ib,da,db。入栈:stmfd sp!, {r0-r12}出栈:ldmfd sp!,{r0-r12}示例代码如下: ldr sp,=0x40001000 movr1,#0x1 movr2,#0x2 movr3,#0x3 movr4,#0x4 stmfd sp!,{r1-r4} 示例:使用堆栈寻址实现保存调用函数时的返回地址和通用寄存器值。 area example,code,readonly entry start ;初始化堆栈值 ldr sp,=0x40001000 movr1,#0x1 movr2,#0x2 movr3,#0x3 movr4,#0x4 bl func1 b over func1 ;保存返回地址和通用寄存器值到栈种 stmfd sp!,{r1-r4,lr} mov r1,#0xf mov r2,#0xf mov r3,#0xf mov r4,#0xf bl func2 ;将栈中值弹出到通用寄存器中,而且将lr值直接放在pc中,实现返回 ldmfd sp!,{r1-r4,pc} func2 ;如上 stmfdsp!,{r1-r4,lr} mov r1,#0xe mov r2,#0xe mov r3,#0xe mov r4,#0xe ;如上 ldmfd sp!,{r1-r4,pc} over bover end
软中断SWI
在Linux程序中,用户程序是不能访问调用系统资源,但是可以通过系统调用或异常访问系统资源。那usr模式下是如何实现系统调用呢? 答案是通过SWI + 中断号。每一个系统调用对应一个SWI + 中断号,在arch/arm/kernel/entry-common.S下保存所有的SWI+ 中断号对应的系统调用。在usr模式下,调用swi时cpu会跳到Supervisor工作模式下,在这个过程中会执行异常处理的所有操作,这个过程参考博文:arm异常处理流程。 示例代码如下: areafirst, code, readonly code32 entry; 定义的异常向量表vector breset_handler ; 跳转到 reset_handler nop bswi_handler ; SWI 指令异常跳转的地址 nop nop nop nop nop swi_handler ;swi handler code ;异常处理首先要压栈保存处理器现场 mrsr0, cpsr movr0, #1 mov r1, #2 movspc, lr ; lr > pc 且 spsr -> cpsr返回 SVC -> USER reset_handler ;初始化 SVC 模式堆栈 ldrsp, =0x40002000 ;修改当前的模式从SVC模式改变为USER模式 mrsr0, cpsr bicr0, r0, #0x1f orrr0, r0, #0x10 msrcpsr_c, r0 ;初始化 USER 模式堆栈 ldrsp, =0x40001000 movr0, #1 ;USER SWI swi5 ; open APP USER 这条语句由用户程序自己出发异常 ;观察并记录对比指令执行前后的 PC LR CPSR SPSR SP的变化 addr1, r0, r0stop bstop end cpu在swi 5 后异常处理流程由硬件自己完成,这个过程如下图:注1:如果同时返回到异常发生的位置,同时又能切换模式只能使用如下指令: movs pc,lr注2:进入swi异常,如何得到 swi 指令后面跟的那个参数?所有的系统调用,都是依赖swi指令,操作系统提前给所有的系统调用编号思路:swi异常发生的时候,会把该指令的下一条指令的地址给 svc模式的lr,通过lr可以找到swi的地址,ldr r0, [lr,#-4] ; 获得SWI指令的机器码bic r0, r0,#0xff000000 ; 通过机器码获得SWI NUMBER
伪指令是用于告诉汇编程序如何进行汇编的指令。这里指的是MDK下的伪指令与GNU下的伪指令不一样。
一、常见伪指令【area】定义一个代码段或者数据段格式:area 段名 属性1,属性2……段名:code:代码段、data:数据段属性:readonly:只读、readwrite、align[4] 4字节对齐、code32 4字节、code16【entry】汇编程序的入口点,一个完整的汇编程序至少要有一个entry,一个文件中最多有一个entry,也可以没有,如果有多个entry,由链接器指定哪个是程序的入口点【end】程序的结尾【EQU】定义一个变量,相当于c语言中的define【export】声明一个全局的标号【import】通知编译器,要使用的标号在其他的源文件中定义,相当于c语言中的extern
二、符号定义伪指令gbla 、gbll 、gbls、GBLA伪操作声明一个全局的算术变量,并将其初始化为0。GBLL伪操作声明一个全局的逻辑变量,并将其初始化成{FALSE}。GBLS伪操作声明一个全局的串变量,并将其初始化为空串""。 lcla、lcll 、lclsGBLA伪操作声明一个局部的算术变量,并将其初始化为0。GBLL伪操作声明一个局部的逻辑变量,并将其初始化成{FALSE}。GBLS伪操作声明一个局部的串变量,并将其初始化为空串""。seta 、setl 、sets设置变量具体值示例代码如下:gbla test1test1 seta 0xaagbll test2test2 setl {true}gbls test3test3 sets "testing" dcb 、dcw 、dcd 、dcfd 、dcfs 、dcq 、space 、map fileddcb 类似于定义一个char数组,用于分配一片连续的字节存储单元,space 类似于mallocfen分配一片连续的内存区域并初始化为0filed 类似于一个结构体其他暂时不必了解。
三、控制伪指令【IF】if 逻辑表达式指令序列else指令序列Endif 【while】while指令语句wend