一个汇编文件中包含以下部分:
1, 汇编指令, 被编译成一条一条的汇编指令存放在内存中,
2, 伪指令,在编译的时候会被编译器替换成多条可以识别的arm汇编指令
3, 伪操作,引导编译器进行相关的编译工作,不会被编译成汇编指令,也不占内存空间
4, 标号,起标识某一个地址的作用,不占内存空间
汇编指令:
1. 数据处理指令 -- 对数据进行算术运算或者逻辑运算或者其他处理方法
2. 跳转指令 -- 可以实现程序的跳转
3. load/store架构指令 -- 对处理器的内存进行操作的指令
4. PSR数据传送指令 -- 用来操作程序状态寄存器
5. 协处理器指令 -- 用来操作协处理器
6. 软中断指令 -- 用来触发一个软中断异常
【1】数据处理指令
语法:
<指令>{<cond>}{S} Rd, Rn, Operand2
<指令>:指令的名字
{<cond>}:条件码 如果本指令加了条件码,必须条件满足这条指令才会执行, 不加条件码表示无条件执行
{S}:状态标记,只有当前指令加了S,那么运算结果才会影响CPSR寄存器的条件位
Rd:目的寄存器(比较指令没有目的寄存器)
Rn:第一操作寄存器(数据搬移指令没有第一操作寄存器)
Operand2:第二操作数(立即数,前面加#标识,16进制或者10进制),如果是寄存器的话,称之为第二操作寄存器
.text @指定一个代码段
.global _start @把_start标号定义成全局的
_start: @定义一个标号_start, 是汇编程序的默认入口
mov r0, #0xff @汇编指令,将0xff这个数据搬移到r0寄存器中
stop: @标号
b stop @跳转指令,相当于while(1), 防止程序跑飞
.end @伪操作,表示汇编程序的结束
【1】数据搬移: MOV MVN
mov r0, #0xff @r0 = 0xff
mvn r1, #0xff @r1 = ~0xff
mov r2, r1 @r2 = r1
【2】算术指令: ADD ADC SUB SBC RSB RSC
mov r0, #4
add r1, r0, #1 @r1 = r0 + 1
add r2, r1, r0 @r2 = r1 + r0
add r2, #1 @r2 = r2 + 1
@add r3, r0, #0xffffffff 不会改变C位
adds r3, r0, #0xffffffff @C = 1 N = 0 Z = 0
adc r4, r0, #1 @带进位的加法指令 r4 = r0 + 1 + C
@两个64位的数据进行加法运算,第一个64位数据的高32位放在r0中,低32位放在r1中,另一个数的高32位放在r2中,低32位放在r3中
mov r0, #0x1
mov r1, #0xffffffff
mov r2, #0x4
mov r3, #0x5
adds r4, r1, r3 @产生进位 C = 1
adc r5, r0, r2 @r5 = r0 + r2 + C
mov r0, #4
sub r1, r0, #1 @r1 = r0 - 1
sub r2, r0, r1 @r2 = r0 - r1
sub r0, #1 @r0 = r0 - 1
subs r4, r0, #5 @N = 1 C = 0 Z = 0
sbc r0, #1 @带借位的减法,r0 = r0 - 1 - !C
32位乘法
MUL{<cond>}{S} R0, R1, R2 R0 = R1 * R2
MLA{<cond>}{S} R0,R1,R2,R3 R0 = (R1 * R2) + R3 乘加指令
mul r4, r0, r1 @r4 = r0 * r1
@mul r4, r0, #4 报错,乘法指令不能使用操作数
mla r5, r0, r1, r3 @r5 = r0 * r1 + r3
【2】比较指令 cmp
mov r0, #4
mov r1, #5
cmp r0, #4 @cmp指令本质进行的是减法指令,没有目的寄存器 r0 - 4 = 0 Z = 1 C = 1
注意:所有的指令只有比较指令不加S,运算结果会影响CPSR寄存器,其他指令必须加S才会影响
【3】逻辑指令: AND ORR EOR BIC
mov r0, #0xff
and r1, r0, #0xfffffff0 @r1 = r0 & 0xfffffff0
orr r2, r0, #0xf00 @r2 = r0 | 0xf00
eor r3, r0, #0xf @r3 = r0 ^ 0xf (取反)
bic r4, r0, #0xf @位清零指令 哪些位为1就将哪些位清零 r4 = r0 & (~(0xf))
bic r5, r0, #0x50 @第4位和第6位清零
【4】跳转指令(分支指令)
Branch : B{<cond>} label
Branch with Link : BL{<cond>} subroutine_label //带返回的跳转,本质会保存返回地址
跳转范围:± 32 Mbyte
如何执行长跳转?
ldr pc, =lable
需求:将r0和r1中的数求和 然后求差, 最后相加
_start:
mov r0, #5
mov r1, #3
bl ad
bl sb
add r4, r2, r3
stop:
b stop
ad:
add r2, r1, r0
mov pc, lr
sb:
sub r3, r0, r1
mov pc, lr
【5】条件码
EQ: 相等 Z = 1
NE: 不相等 Z = 0
GT: 大于 N = 0 且 Z = 0
GE: 大于等于 N = 0
LT: 小于 N = 1
LE: 小于等于 N = 1 或 Z = 1
mov r0, #4
mov r1, #3
cmp r0, r1
bleq stop
addgt r3, r0, r1
指令练习:
if (a==0) func(1);
cmp r0, #0
mov r0, #1 //传递参数
bleq func
if (a==0) x=0;
if (a>0) x=1;
cmp r0, #0
moveq r1, #0
movgt r1, #1
if (a==4 || a==10) x=0;
cmp r0, #4
cmpne r0, #10
moveq r1, #0
【6】桶型移位器
LSL: 逻辑左移 高位丢弃,低位补0
LSR: 逻辑右移 低位丢弃,高位补0
ASR: 算术右移 低位丢弃,高位补符号位 如果移动多个位,高位全部补符号位
ROR: 循环右移 低位补高位
mov r0, #1
mov r1, r0, lsl #1 @r1 = r0 << 1
add r1, r0, lsl #1 @r1 = r1 + (r0 << 1)
mov r0, #0xf
mov r1, r0, ror #4
mov r0, #0xf0000000
mov r1, r0, asr #4
【7】立即数和有效数
立即数:如果一个数可以通过一个8位的数(0-255)ROR偶数位得到,那么这个数就是一个立即数
如何判断一个数是否是立即数:
1. 展开成二进制,如果1的个数大于8,肯定不是一个立即数
0x3fc00000 是一个立即数
0011 1111 1100 ...
0x1fe00000 不是一个立即数
0001 1111 1110 0000 ...
0x3e800000 是
0011 1110 1000 ....
0x30100000 不是
0011 0000 0001 0000 ....
有效数:
如果一个数是立即数,那么这个数也是有效数
如果一个数安位取反之后得到的数是立即数,那么这个数就是有效数
ldr r0, =0x12345678 //ldr伪指令,将任意数据搬移到一个通用寄存器中
GCD练习:
mov r0, #9
mov r1, #15
loop:
cmp r0, r1
beq stop
subgt r0, r1
sublt r1, r0
b loop
load/store架构指令
1. 单寄存器指令
2. 多寄存器指令
3. 数据交换指令
1. 单寄存器指令
语法:
LDR{<cond>}{<size>} Rd, <address> //内存 ==> 寄存器
STR{<cond>}{<size>} Rd, <address> //寄存器 ==> 内存
{<size>}: B H 不写(W)
Rd:目的寄存器
<address>:地址有三种形式
1) 标号
ldr r0, lable
2)寄存器间接寻址
str r0, [r1]
3)基址变址寻址
a. 前索引
str r0, [r1, #4]
b. 自动索引
str r0, [r1, #4]!
c. 后索引
str r0, [r1], #4
============= 标号 =============================
ldr r1, label @将label这个标号下面的数据读取到r1寄存器中
ldrb r2, label
stop:
b stop
label:
.word 0x12345678
================= 寄存器间接寻址
mov r0, #0x41000000 @0x40000000,0x42000000
mov r1, #0xff
str r1, [r0] @将r1里面的数据存储到r0指向的内存地址 *r0 = r1
======================= 基址变址寻址
====== 前索引
mov r0, #0x41000000 @0x40000000,0x42000000
mov r1, #0xff
str r1, [r0, #4] @将r1里面的数据存储到r0+4指向的内存地址 *(r0+4) = r1
========= 自动索引
mov r0, #0x41000000 @0x40000000,0x42000000
mov r1, #0xff
str r1, [r0, #4]! @将r1里面的数据存储到r0+4指向的内存地址,然后r0自动加4 *(r0+4) = r1 r0 = r0+4
========= 后索引
mov r0, #0x41000000 @0x40000000,0x42000000
mov r1, #0xff
str r1, [r0], #4 @将r1里面的数据存储到r0指向的内存地址,然后r0自动加4 *r0 = r1 r0 = r0+4
数组求和:
ldr r0, =myarray
mov r4, #0
loop:
ldr r1, [r0], #4
cmp r1, #0
beq stop
add r4, r1
b loop
用汇编实现如下功能:
main()
{
int i = 0;
char src_buf[] = {1, 2, 3};
char dest_buf[8];
for(i = 0; i < 3; i++)
{
dest_buf[i] = src_buf[i];
}
}
.
text
.global _start
_start:
ldr r0, =src_buf
ldr r1, =dest_buf
mov r4, #0
loop:
cmp r4, #3
beq stop
add r4, #1
ldrb r2, [r0], #1
strb r2, [r1], #1
b loop
stop:
b stop
src_buf:
.byte 1, 2, 3
dest_buf:
.space 8
.end