目的 认识汇编, 从而更好的进行C语言编程
RAM指令格式: 了解
4字节宽度 地址4字节对齐 方便寻址
condition: 高4bit[31:28] 条件码 0-15 (16个值 )
条件码: 用于指令的 条件执行 , ARM指定绝大部分 都可以条件执行
可以让程序 不必要跳转 而执行分支结构 不打断流水线 效率更高
指令操作码: [24:21] 指令本身的 编号 可以有16种指令
Rn: 第一操作寄存器 第一个操作对象寄存器
Rd: 目标寄存器 用于存放输出结果的寄存器
Rm: 第二操作寄存器 或 立即数 或 立即数移位
S位: 用于指定 该指令 是否记录其状态到 CPSR中
1. 汇编指令 编译后将 生成一条对应值 指令码
2. 汇编伪指令 编译后将 生成一条或多条指令码
3. 汇编操作(标志符号) .text .end ...
.text 汇编代码段开始 属于汇编操作
mov r0,#1 @ 汇编指令
.data 汇编数据段开始
.end 汇编代码结束
ARM 约定:
Byte: 8bits(1byte) 字节 char
Halfword: 16bits(2byte) 半字 short
Word: 32bits(4byte) 字 int
Doubleword: 64bits (cortex-a)
mov mvn
使用12bit 按规则可以存放的数 称立即数
0 - 2^12-1 即最大2048
12bit分两部分 高4bit 存放 循环右移的偶数次
低8bit 存放 右移的数本身
立即数定义: 一个数 可以通过一个8bit(最大255)数 循环右移偶数位得到, 该数即立即数 目的 扩大了 可以输入的值的 值域范围 即 0-2^32 但不连续
指令 mov r0,#0xff000
汇编如下
0x00000000 E3A00AFF MOV R0,#0x000FF000
立即数0xff000二进制如下
0000 0000 0000 1111 1111 0000 0000 0000
靠1111 1111 循环右移 10*2 = 20位得到
0000 0000 0000 1111 1111 0000 0000 0000
小知识 :
循环右移: 桶型移位器 硬件设备
(1) 100(十进制) 是 (因为100 8个bit就能装下,移不移位不影响的)
(2) 0x F000000F 是 0xff 循环右移 4位 是立即数
解释,汇编如下
0x00000000 E3A002FF MOV R0,#0xF000000F
靠000000FF 右移4位 得到立即数 -----> F000000F
二进制的写法
0000 0000 0000 0000 0000 0000 1111 1111
右移4位 -----> 1111 0000 0000 0000 0000 0000 0000 1111
(3) 0X 0F0F0F0F 不是立即数 (因为中间的0不可能靠移位能够得到)
(4) 0X 12300 不是立即数 (因为右移的本身数部分8个bit怎么都装不下)
解释
0X 12300 二进制如下
0000 0000 0000 0001 0010 0011 0000 0000
黄色部分8个bit装不下,至少9个bit了,不符合立即数定义
(5) 0x 12c00 是
0000 0000 0000 0001 0010 1100 0000 0000
黄色部分7个bit可以装下,符合定义
本身数0100 1011 = 4b开始循环右移,写满32位如下
0000 0000 0000 0000 0000 0000 0100 1011 右移 4 如下
1011 0000 0000 0000 0000 0000 0000 0100 右移 8 如下
0100 1011 0000 0000 0000 0000 0000 0000 右移 16 如下
0000 0000 0100 1011 0000 0000 0000 0000 右移22 如下
00 00|00 00|00 00|00 01|00 10|11 00|00 00|00 00
规整
0 0 0 1 2 C 0 0
看看汇编如下,双数分析正确
0x00000000 E3A00B4B MOV R0,#0x00012C00
(6) 0X 1060 不是
0000 0000 0000 0000 0001 0000 0110 0000
虽然红字刚好8个bit可以装下,但是前面的0(黄色部分的个数是奇数个,不符合立即数规定)
mov r0,#0xffffffff (这里的0xffffffff就是有效数)
汇编如下
0x00000000 E3E00000 MVN R0,#0x00000000
可以看到 mov 编程mvn 了
mvn 将数 进行取反后在进行装载
有效数: 一个数取反后是立即数
这个程序直接报错,因为无论是立即数还是有效数,都无法装载
但是 使用伪指令可以装载
暂时了解即可
ldr r0,=0x12345678
汇编如下
0x00000004 E51F0004 LDR R0,[PC,#-0x0004]
偏移
lsl 逻辑左移: 对无符号数操作 << 高位移出丢弃 低位补0
lsr 逻辑右移: 对无符号数操作 >> 低位移出丢弃 高位补0
asr 算数右移: 对有符号数操作 >> 低位移出丢弃 高位补符号位
ror 循环右移: 低位移出 补充到高位
@移位操作
mov r0,#1
lsl r1,r0,#2 @相当于c语言的r1 = r0<<2; 左移相当于做乘法 R0 * 2^2
lsr r2,r1,#1 @相当于c语言的r2 = r1>>2; 右移相当于做除法 R1 / 2^1
unsigned int ROR(unsigned int a, char cnt)
{
int f = 0;
while(cnt--)
{
f = a & 1; //取出低位
a >> 1; // 右移
a = a | (f << 31); // 补充高位
}
}
@算数右移
ldr r3,=-5
asr r4,r3,#1 @ 算数右移1位 R3 / 2^1
@ 算数运算
MOV R0, #5
MOV R1, #2
ADD R1,R0 @相当于c语言的r1 += r0
a = 0x12345678 87654321
b = 0x12345678 88888888
r0 高32bit r1 低32bit
@32位寄存器 实现64位加法: a = 0x12345678 87654321 b = 0x12345678 88888888
ldr r0,=0x12345678 @使用伪指令分开装
ldr r1,=0x87654321
ldr r2,=0x12345678
ldr r3,=0x88888888
adds r5,r1,r3 @ 计算低32位 s 表示cpsr要存储进位标志
adc r4,r0,r2 @ 计算高32位 R4 = R0+R2+进位标志
@ 练习实现 a-b
subs r5,r1,r3 @ 计算低32位 R5=R1-R3
sbc r4,r0,r2 @ 计算高32位
@逆向减法指令
mov r0,#10
@sub r1,r0,#5 @ r1=r0-5; @按规定立即数必须在最后面 比如这样sub r1,r0,#5 不能写成sub r1,#5,r0
rsb r2,r0,#5 @ r1 = 5-r0
FFFF FFFB 补码
FFFF FFFA 反码
0000 0005 源码 即 -5
@乘法指令
mov r0,#10
mov r1,#5
mul r3,r0,r1 @ r0=r0*r1 这里r3等于 50
位运算 C语言: & 按位与
| 按位或
^ 按位异或
~ 按位取反 ( mvn在汇编中既可以搬运数据也可以取反)
and orr eor
逻辑运算: !非 &&逻辑与 ||逻辑或
and & 按位操作 与0得0 与1不变
@与运算
mov r0,#0xff
and r1,r0,#0xf
orr | 按位操作 或1得1 或0不变
@或运算
mov r0,#0xff
orr r1,r0,#0xf
eor ^ 按位操作 相同为0 不同为1
@异或运算
mov r0,#0xff
eor r1,r0,#0xf
a = **** **** **** **** **** **** [****] ****
a & 1111 1111 1111 1111 1111 1111 0000 1111 ----> 即 ~ 1111 0000
a & ~ 1111 0000
a = a & ~ (0xf << 4);
= **** **** **** **** **** **** [0000] ****
a = **** **** **** **** **** **** [****] ****
a & 1111 1111 1111 1111 1111 1111 0000 1111
= **** **** **** **** **** **** [0000] ****
a | 0000 0000 0000 0000 0000 0000 0111 0000
a | (0x7 << 4)
= **** **** **** **** **** **** [0111] ****
a = (a & ~ (0xf << 4)) | (0x7 << 4);
a = **** **** **** *[*** **** ***]* **** ****
& ~(0x3ff << 9);
a = **** **** **** *[000 0000 000]* **** ****
| (0x123 << 9)
a = (a & ~ (0x3ff << 9)) | (0x123 << 9);
有一个数 a [31:0] 希望 其中 [m:n] = x 其他bit不变
a = (a & ~ ( P << n)) | (x << n); 其中 P= 2^(m-n+1) - 1; P本质是 位宽这么多个1的值
bic 位清除指令 用于清除指定的位
bic 位清除指令 用于清除指定的位
bic的规律是 遇见 0 不变 遇见1 清0
@位操作指令bic
@清除4-7位为0
mov r0,#0xff
bic r1,r0,#0xf0 @ 类似于 R1=R0 & ~(0XF0)
if(a > 0)
a= 5;
else // <= 0
a= 6;
将上述代码编译成汇编代码
@比较指令 与 条件指令
@ if(a > 0)
@ a= 5;
@ else // <= 0
@ a= 6;
@将上述代码改写为汇编
mov r0,#2
cmp r0,#0 @比较r0 与 0的关系
movgt r0,#5 @意思是如果r0>0 就将r0赋值为5
movle r0,#6 @意思是如果r0<=0 就将r0赋值为6
NOP @ 空指令 MOV R0,R0 浪费时间
b : 不带链接的跳转 类似 goto
bl : 带链接的跳转 就是 跳转时 会 将PC指针备份到 lr寄存器中
这段代码的意思就是 在开始设置了一个loop地址标记,在最后 b loop 在此跳转到地址标记处执行代码
loop: @地址标记
mov r0,#2
cmp r0,#0 @比较r0 与 0的关系
movgt r0,#5 @意思是如果r0>0 就将r0赋值为5
movle r0,#6 @意思是如果r0<=0 就将r0赋值为6
NOP @ 空指令 MOV R0,R0 浪费时间
b loop @ 跳转到标记loop出开始执行
c实现
for(int i =1, sum = 0; i<= 100; i++)
sum += i;
汇编如下
@汇编实现求 1+2+3 +.. 100 = ???
@ for(int R0 =1, R1 = 0; R0 <= 100; R0++) R1 += R0;
mov r0,#1 @给r0 为1
mov r1,#0 @给r1 为0 用来存储结果
loop: @地址标记
cmp r0,#100 @比较r0 和 100 的大小
addle r1,r0 @如果r0 <= 100 就执行 r1 = r1+r0
addle r0,#1 @如果r0 <=100 就执行 r0 = r0+1
ble loop @ 只有条件满足时(r0 <= 100 )才会 跳转到标记loop出开始执行(不满足则跳出即r0 > 100)
NOP
@汇编实现求 1+2+3 +.. 100 = ???
@ for(int R0 =1, R1 = 0; R0 <= 100; R0++) R1 += R0;
mov r0,#1 @给r0 为1
mov r1,#0 @给r1 为0 用来存储结果
loop: @地址标记
cmp r0,#100 @比较r0 和 100 的大小
addle r1,r0 @如果r0 <= 100 就执行 r1 = r1+r0
addle r0,#1 @如果r0 <=100 就执行 r0 = r0+1
ble loop @ 只有条件满足时(r0 <= 100 )才会 跳转到标记loop出开始执行(不满足则跳出即r0 > 100)
NOP
编译如下C代码为汇编指令:
switch(a)
{
case 1: a=5; break;
case 2: a=6;
case 3: a=7; break;
default: a=10;
}
mov r0,#1
teq r0,#1 @ case 1:
moveq r0,#5 @相等时执行
beq _end @break
teqne r0,#2 @ 如果case 1未命中(r0不等于1),则继续比较r0中的值是否等于2。
moveq r0,#6 @ a = 6 如果比较结果相等(teqne指令执行后,r0等于2),则执行这行指令
teqne r0,#3 @ 如果case 2未命中(r0不等于2),则继续比较r0中的值是否等于3。
moveq r0,#7 @ 如果比较结果相等(teqne指令执行后,r0等于3),则执行这行指令。
beq _end @break @ 如果在case 3中执行了moveq指令(r0被设置为7),则跳转到标签_end处。
movne R0,#10 @default @ 如果前面的case都未命中(r0不等于1、2或3),则执行这行指令。
@ movne指令在不相等(not equal)条件下,将立即数10(#10)移动到r0寄存器中
_end:
nop
主要作用于函数调用
c语言
int addNum(int a,int b, int c)
{
return a+b+c;
}
a = addNum(1,2,3);
汇编实现
C语言标准规范:
规定 函数的调用 参数 前4个参数使用 r0-r3传递 ,超过4个的参数 使用栈传递
返回值 通过R0传递
@bl指令实现 定义函数
@int addNum(int a,int b, int c)
@{
@ return a+b+c;
@}
@a = addNum(1,2,3);
@准备参数
mov r0,#1
mov r1,#2
mov r2,#3
@调用函数
bl addNum
nop
nop
@定义函数
addNum: @r0,r1,r2 3个参数
add r0,r1
add r0,r2
mov pc,lr @相当于return 这里返回到nop了就是bl addNum 调用函数的下一条指令
1.b/bl指令 是一个相对跳转指令 相对当前取址的指令位置 向前或先后 跳转 指定的指令条数
2.b/bl指令 最大可以跳转的 地址空间 为 +- 32M
指令除去 8BIT的条件码与指令码 剩余24bit 用于存储跳转的 指令条数
Linux程序地址空间 0-4G
b/bl属于短跳转 +- 32M
长跳转/绝对跳转 直接将要跳转的内存地址 加载到PC寄存器中