1、为什么要学习汇编语言?
1)硬件上电的初始化代码是用汇编代码编写;
2)调试代码时,问题的解决有可能看汇编代码;
3)系统调优(提高产品整体运行效率);
memcpy:C语言函数原型
:汇编代码 - 效率更高
4)有些逻辑,拿C语言无法实现
例如,用c访问r5寄存器
register int a;
2、学习目标
1)使用汇编控制LED灯;
2)可以阅读汇编代码。
3、学ARM汇编要学的内容
汇编指令
伪指令
伪操作
/** 代码演示 - 最简单的汇编程序 **/
.text @代表接下来的内容放入代码段
.code 32 @代表接下来的内容翻译成ARM指令集
.global start @声明全局start标号
start:
mov r0, #10 @ r0=10
b . @ goto 当前位置执行,构成执行死循环
.end @ 代表整个汇编文件的结束
$: arm-cortex_a9-linux-gnueabi-as test.s -o test.o // 编译汇编代码
$: arm-cortex_a9-linux-gnueabi-objdump -S test.o > 1.asm// 反汇编
4、 ARM汇编的特点
汇编语言,又叫助记符语言。
1)大多数指令都是单周期指令
2)大多数指令都是可以条件执行的。
--------------------【条件码】--------------------
CMP: 比较
EQ: 相等
NE: 不等
ADD: 加法
CS: 无符号数大于或等于
CC: 无符号数小于
5、ARM汇编指令
5.1 分支跳转指令
'B{cond} <target_address> '分支指令 - 类似于goto
{} 代表可由可无,<>代表缺少就报错,cond为指令执行的条件码,target_address为指令跳转的目标地址。
'BL{cond} <target_address> '相对跳转指令 - 类似于函数调用
跳转范围限制±32M,PC=PC±32M
【L】决定由硬件自动保存下调指令的返回地址到lr,用于函数调用
// B和BL指令均可跳转到指令中的目标地址。-地址相关码/地址无关码
'BX{cond} <Rm>' 绝对跳转,4G范围
BLX
【X】带状态切换的分支跳转
5.2 数据处理指令
0)移位操作
lsl:逻辑左移 logical shift left,最低位补0
lsr:逻辑右移 logical shift right,最高位补0
asr:算术右移 arithmetic shift right,最高位补符号位,最低位舍弃
ror:循环右移 ,最低位变成新的最高位
rrx:带扩展位的循环右移,最高位扩展cpsr的C位
1)数据传输指令
'MOV{cond} {s} <Rd>, <operand>
cond:可以条件执行
s:操作结果影响cpsr N/Z/C位
Rd:目标寄存器
operand:目标操作数
立即数:mov r0, #1 /* 注意立即数的合法性问题,该立即数可以通过1个8bit位数循环右移得到*/
寄存器:mov r0, r1 @ r0=r1
寄存器移位之后的值:
mov r0, r1, LSL #2 @ r0=r1*4
mov r0, #0 @ 把数字0放到r0寄存器
movs r0, #0 @ r0=0 N=0 Z=1 C=0
movcss r0, r2
'MVN r0, #1 @ r0= ~(1)
2)算术运算指令
'ADD{cond} {s} <Rd>, <Rm> <shifter_operand> 加法
cond:条件码
s:操作结果影响cpsr N/Z/C/V位
Rd:目标寄存器,r0~r15其中任意一个
Rm:第一个操作数,r0~r15其中任意一个
operand:第二个操作数
立即数:add r0, r1, #8 @ r0=r1+8
// 错误示范:add r0, #8, r1
寄存器:add r0, r1, r2 @ r0=r1+r2
寄存器移位之后的值:
add r0, r1, r2, lsl #2 @ r0=r1+r2*4
add r0, r1, r2 @ r0=r1+r2
adds r0, r1, r2 @ r0=r1+r2 操作结果r0影响N/Z/C/V位
'ADC r0, r1, r2 @ r0=r1+r2+C 带进位的加法指令
64位加法运算:
高 低
加数 r0 r1
被加数 r2 r3
和 r0 r1
adds r1, r1, r3 @ 改变C
adc r0, r0, r2 @r0=r0+r2+C
'SUB/SBC/RSB{cond} {s} <Rd>, <Rm> <shifter_operand> 减法
cond:条件执行
s:操作结果影响cpsr N/Z/C/V
// 注意对C位的影响:
// 如果最高位没有借位 - C=1 - 如 100-20
// 如果最高位有借位 - C=0 - 如 20-100
Rd:目标寄存器,r0~r15其中任意一个
Rm:第一个操作数,r0~r15其中任意一个
operand:第二个操作数(同ADD)
立即数
寄存器
寄存器移位后的值
sub r0, r1, r2 @ r0=r1-r2
sub r0, r1, #8 @ r0=r1-8
sub r0, r1, r2, lsl #1 @r0=r1-r2*2
'SBC r0, r1, r2 @ r0=r1-r2-NOT(C)
rsb r0, r1, r2 @r0=r2-r1
rsb r0, r0, #0 @r0=0-r0
64bit减法运算:
高 低
被减数 r0 r1
减数 r2 r3
差 r0 r1
subs r1, r1, r3 @ r1=r1-r3 if (r1>r3) {C=1} if (r1<r3) {C=0}
sbc r0, r0, r2 @ r0=r0-r2-NOT(C)
练习一:
使用汇编语言求1-10的累加和,结果保存到r0中。
/** 代码演示 - **/
.text
.code 32
.global _start
_start:
mov r0, #0 @// 将0放入r0
mov r1, #10 @// 将10放入r1
sub_loop:
add r0, r0, r1 @// r0=r0+r1;
sub r1, r1, #1 @// r1=r1-1;
cmp r1, #0 @// 判断:r1 ?= 0
bne sub_loop @// ne不相等 b跳转到
b .
.end
/** ------------------------------------------------------- **/
$: arm-cortex_a9-linux-gnueabi-as sum.s -o sum.o
$: arm-cortex_a9-linux-gnueabi-ld sum.o -o sum
// 可以通过仿真软件qemu,在PC机上模拟arm core的执行过程
安装qemu:
联网安装:$: sudo apt-get install qemu...
非联网安装:$: cd ~/Downloads/qemu
$: sudo dpkg -i *.deb
重新编译sum程序:
$: cd -
$: arm-cortex_a9-linux-gnueabi-as sum.s -o sum.o -g
$: arm-cortex_a9-linux-gnueabi-ld sum.o -o sum
调试运行程序:
$: qemu-arm -g 1234 sum // 1234为端口
另开一个shell窗口,cd进到程序文件目录
$: arm-cortex_a9-linux-gnueabi-gdb sum
(gdb) target remote 192.168.1.8:1234 // localhost(ip)
(gdb) b 8
(gdb) c
(gdb) info reg r1 // 查看r1寄存器里面的值,r1 = 10
(gdb) n
(gdb) info reg r1 // r1 = 9
(gdb) b 16
(gdb) c
(gdb) info reg r0 // r0 = 19
注意:
$: arm-cortex_a9-linux-gnueabi-gdb sum
arm-cortex_a9-linux-gnueabi-gdb: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.16` not found (required by arm-cortex_a9-linux-gnueabi-gdb)
此时需要做的是安装libc6的库:
$: cd ~/Downloads/
$: sudo dpkg -i *.deb
3)逻辑运算指令
'AND/ORR/EOR{cond} {s} <Rd>, <Rm>, <operand>'
and r0, r1, #0x80 @ r0=r1&0x80
orr r5, r8, r7 @ r5=r8|r7
eor r5, r6, r7, lsl #2 @ r5=r6^(r7*4)
将r0中的bit15位取反?
mov r1, #1
eor r0, r0, r1, lsl #15
'BIC{cond} {s} <Rd>, <Rm>, <operand>'
bic r0, r0, #0x08 @ 将r0的bit3位清0,其他bit位保持不变
bic r0, r0, #0xff @ 将r0的低8位全部清0
将r0的bit7位清0,其他位保持不变?
move r1, #1
bic r0, r0, r1 lsl #7
4)比较测试指令
该类指令不用加s,默认就能影响cpsr的N/Z/C/V位
运算结果不保存。
'CMP/CMN{cond} <Rm>, <operand> '
cmp r0, #0x08 @alu_out=r0-0x08 // alu_out算逻单元
@ if (r0<0x08) N=1 反之N=0
@ if (r0==0x08) Z=1 反之Z=0
@ if (r0>0x08) C=0 反之C=1
cmp r1, r2
cmp r1, r2, lsl #1
'TST{cond} <Rn>, <operand>
tst r1, #0x08 @ alu_out=r1&0x08
@ 根据alu_out取值去影响cpsr N/Z/C/V
@ 测试r1 bit3是否为0
如果r1 bit3为0,Z=1
'TEQ{cond} <Rn>, <operand>
teq r1, r2 @ alu_out=r1^r2
如果两个数据相等alu_out=0,Z=1
练习二:
求两个数据的最大公约数。
r0 r1
eg: 120 48 // 每次相减,把结果与运算的数对比,取小的放有侧
72 48
24 24 // r0,r1两个寄存器的数相等的时候,即得结果
r0 r1
/** 代码演示 - gcd.s **/
.text
.global _start
.global gcd
.code 32
_start:
mov r0, #20 @存放参数1
mov r1, #12 @存放参数2
gcd:
cmp r0, r1
beq gcd_ok
subcs r0, r0, r1
subcc r1, r1, r0
b gcd
gcd_ok:
@ R0,R1相等为最大公约数。
b .
.end