@ | 注释 |
# | 注释(放在行首表示注释一行) |
/*? ? */ | 注释 |
#+数字 | 立即数 |
: | 一种标号(比如main:? ? ? ? loop:) |
.text? ? ? ? .end+换行 | 固定格式 |
ARM指令构成 | ARM 指令包含操作码和一些其他的信息,只剩下8 位存放数据 具体来说,一个 ARM 指令通常由以下部分组成:
其中,操作码、条件码、寄存器索引和位移量总共占据了大部分的指令位数,因此,一个 ARM 指令中只有少部分(通常为 8 位)是用来表示立即数的。 例如,一个 ARM 指令可能是这样的格式: Opcode ? ?Condition ? Register1 ?Register2 ?Offset/Displacement? ?#0 |
一般指令格式 | 操作码 ?目的操作数 ? 源操作数 ? mov ??? ?r0, ??? ? ?#5 ?? |
立即数的优点 | 取指的时候,可以直接读到cpu ?不需要单独去内存读取,速度快 |
立即数的缺点 | 不能是任意的32位数字,有局限,且只能放在指令右边 |
转化为二进制编码,观察第一个1和最后一个1之间的位数(包含这两个1) 1、将数据转换为 二进制编码 ? 4 个一组? |
eg1:0xF000 000F 1111 ?0000 0000 0000 0000 0000 0000 ?1111 -- 1111 1111合法 |
eg2:0x123 ... 0000 ? ?0001 0010 0011 -- 01 0010 0011不合法 |
eg3:0x234 ....?? ? 0010 0011 0100合法 |
eg4:0x3F0 ... ? ? ?0011 1111 0000合法 |
伪指令不是指令
伪指令和指令的根本区别 | 编译后,是否生成机器码(伪指令不会生成) |
伪指令的意义 | 指导编译的过程(如果需要执行伪指令,必须先翻译成在指令) 比如.text? .end 也是伪指令 |
伪指令和编译器相关 | 我们用到是gnu工具链,因此我们学习的是gnu 环境下的汇编伪指令 |
常用伪指令 | ? ? ldr ?//大范围地址的加载指令 ? ? ldr (load register) 将内存内容加载进入通用寄存器? |
eg: ldr r0, =0x12345678 num : .word ? 0x15300000 ? ? //int ?num = 0x15300000? buf1: .byte 1, 2, 3, 4?? ??? ?//char ?buf1[4] = {1,2,3,4};? |
mov | 搬移 mov r2, r1 ? // r2 = r1 mov r0, #7 ? //r1 ?= 7? |
mvn | 取反搬移 mvn r0, r1 ? // r0 = ~r1 |
ldr | 向寄存器存入非立即数可以用伪指令 |
LSL | 左移位 |
LSR | 右移位 |
ASL | 算术左移位(位数不够补符号位) |
ASR | 算术右移位(位数不够补符号位) |
ROR | 循环移位 |
RRX | 带扩展的循环右移 |
偏移指令总结 | ?? ?mov r0,r2, ?lsr #3 ? //r0 = r2 ?>> 3? ? ? mov r1 ,r0,lsl #1 ? ?? ?//r1 = r0 << 1? ? ? add ? r2, r1,r0,lsr #2 ?// r2 = r1 + r0 >> 2? ? ? ? ?? ? ? ?? ?LSL '逻辑左移' (Logical ? shift ?left) ? ? ? ? LSR '逻辑右移'(Logical ? shift ?right) ? ? ? ? ASL '算术左移' (Arithmet ?shift ?left ) ? ? ? ? ASR '算术右移'(Arithmet ?shift right ) ? ? ? ? ROR '循环右移' ? ? ? ? RRX '带扩展的循环右移' |
重要例子 | tst ?r0, #0x8?? ?@判断某个数的某几位 是否为 0 tst ?r0, #0x8? ? 表示判断r0的第三位(8的二进制1000,也就是从右往左的第四位(0 1 2 3))是否为0 eq? ne表示相等/不相等 |
练习:
a) r0 = 16
b) r0 = r1/16
c) r1 = r2 *2
d) r0 = -4
e) r1 = r0/2
答:
mov r0, #16
mov r1, #32
mov r2,r1, LSR #4
mov r2, #4
mov r3, r2, LSL #1
mov r0, #-4
mov r1,r0, ASR #1
.text @基本格式的头
@搬移指令
mov r0, #3 @搬移指令: r0 = 3 ; #3 立即数
ldr r0, =0xfff00000 @ldr 伪指令 r0 = 0xfff
mov r1,r0 @搬移指令 r1 = r0
@移位
mov r0, #0x4
mov r1, r0, LSL #1 @r1=r0 << 1 , 逻辑左移: 高位移除部分舍去,低位不够补0
ldr r0, =0xffffffff
mov r2, r0, LSR #1 @r2=r0 >>1 逻辑右移,低位移除部分舍去, 高位部分不够补 0
mov r3, r0, ASR #1 @r3 = r0 >> 1 算术右移,低位移除部分舍去 高位不够 补 符号位
ldr r0, =0x7000000f
mov r4,r0, ROR #1 @r4 = r0 >>1 , 循环右移, 低位移除部分补到 高位
ldr r0, =0xff0000ff
mvn r5,r0 @r5 = ~r0
.end @基本格式的尾巴 通常 该行下边 加空行
and | 与 |
orr | 或 |
bic | 按位清零(比如说0x4 就表示第2位(从0开始数)清零) bic ?r0,r1,#0x4 ?//r0 ?= r1 &(~0x4 ?) |
eor | 异或 |
.text @基本格式的头
@逻辑指令
mov r0, #6
mov r1, #5
and r2, r0,r1 @r2 = r1 & r0
orr r3, r0,r1 @r3 = r1 | r0
eor r4, r0,r1 @r4 = r1 ^ r0
ldr r0,=0x12345678
bic r1, r0, #0xff
bic r2,r0, #0xf0000000
tst r0, #0x8 @判断某个数的某几位 是否为 0
@ movne r11, #1
moveq r11, #1 @当上一条判断结果相等时 执行 mov
movne r12, #2 @当上一条判断结果不相等时执行mov
@使能中断和快速中断(设置cpsr的值)
mrs r0, cpsr @ r0 = cpsr
bic r1, r0, #0xc0
msr cpsr, r1 @cpsr = r1
.end @基本格式的尾巴 通常 该行下边 加空行
用法注意:?? ????????cmp ????????moveq ????????movne |
cmp | cmp ? r1, r0 ? ????????//改变 cpsr 的 NZ? moveq ?r2, #0 ? ?//相当于 ?if (r1 == r0) ?r2 = 0(这是两行代码) |
cmn | |
关系符号 | eq == ge >= gt > le <= lt < ne != |
tst | 实质是做 与运算, 通常用于 测试某一位或几位是0 还是1 结果 CPSR ?'Z' ?位来判断,?Z 位位 1 表明结果为0? tst ?r0, ?#0x10 ?//测试 第4位是否为 0, ?if((r0 ?&(0x10)) == 0) |
teq | 实质是异或运算 ?测试两个 数是否相等,如果两个数相等或者异或结果 为 0 ?修改 cpsr 的z 位 判断 teq r0, r1 ? // if((r0 ^ r1 ) == 0)? |
.text @基本格式的头
@比较指令
/*
int a = 5;
if(a >= 5)
a = 0
else
a = 100
*/
/*
mov r0, #5
cmp r0, #5 @比较指令 , 比较 r0 和 5
movge r0 , #0 @当cmp的结果 >= 时, r0 = 0
movlt r0, #100 @当cmp的结果 是 < 时, r0 = 100
*/
.end @基本格式的尾巴 通常 该行下边 加空行
add | 相加 add r2, r1,#3 ? ? //r2 = r1 + 4? |
adds | 会改变条件位的相加(配合adc使用) adds ?r0, r1, r2 ? // ?r0 = r1 + r2 ?&& ?(cpsr ) v c? CPSR:进位 -- c置为1?? ?溢出 -- v置为1 补充--溢出:在两个正数相加时,如果结果超过了机器所能表示的最大正数,那么就发生了上溢。同样地,在两个负数相加时,如果结果小于机器所能表示的最小负数,那么就发生了下溢 |
adc | 带进位的加法(常用于64位加法,配合adds使用) adc ?r0, r1, r2 ?//r0 = r1 + r2 ?+ (cpsr) ?c |
sub | 相减 sub r1, r2, r3? |
subs | 影响cpsr的条件位? 没有借位 时 cpsr ?'c' 位 置1 ?当有借位 c = 0? |
sbc | sbc ?带借位的减法 |
rsb | rsb ? 逆向减法 rsb r1,r2, r3 ?//r1 ?= r3 - r2? |
mul | 乘法指令? mul r2, r0, r1 ?// r2 = r0 * r1? |
.text @基本格式的头
@算术指令
mov r0, #10
mov r1, #5
add r2, r0, r1 @r2 = r0 + r1
ldr r0, =0xff000000
ldr r1, =0xf0000000
@add r2, r0, r1 @(不带进位加法)
adds r2, r0, r1 @r2 = r0 + r1 , 如果有进位 那么会修改cpsr的进位值
mov r3, #1
mov r4, #1
adc r5,r3,r4 @r5 = r3 + r4 + 进位值
@64 bit 两个数的加法 r0, r1 表示被加数, r2, r3表示加数
@64 bit 两个数的减法
ldr r0, =0xffffffff @低32 bit
ldr r1, =0x1 @高32bit
ldr r2,=0x1 @低32bit
ldr r3,=0x5 @高32bit
adds r4, r0, r2
adc r5, r1, r3
subs r6, r2,r0
sbc r7, r3,r1
@add r2, r0, r1 @不带进位 不能计算 64 bit
.end @基本格式的尾巴 通常 该行下边 加空行
b 类似 goto? ? ? bl ?跳转之前 先保存 下一条指令的地址 ? ? bl: lr(r14) = pc (r15) -4 ? ?(由cpu 帮我们执行 ) |
int ?main(void ) func2(int a) |
.text @基本格式的头
@跳转练习
main:
mov r0, #0
mov r1, #2
bl func1
main_end:
b main_end @死循环
func1:
cmp r1, #2
bleq func2
blne func3
func1_end:
func2:
add r1, #3
b func2_end
func2_end:
mov pc, lr
func3:
@.....字节补充一下
func3_end:
.end @基本格式的尾巴 通常 该行下边 加空行
.text @基本格式的头
@跳转指令
/*
mov r0, #1
mov r1, #10
b t @跳转指令, 跳到 t 后执行
add r2, r0, r1
t:
sub r3, r0, r1
@b t
*/
@实现 1-100的累加 和
/*
for(int i=1; i<=100; i++)
{
static int sum += i;
}
*/
mov r0, #0 @类似 sum 累加和
mov r1, #1 @类似 i 循环变量
loop:
@循环计算
cmp r1, #100
bgt loop_end @这句 相当于 判断 i 大于 100 跳出循环
add r0,r1 @相当于 sum += i;
add r1, #1 @相当于 i++
b loop @继续下次循环
loop_end:
@结束
mov r12,r0 @把计算结果 放到 r12
.end @基本格式的尾巴 通常 该行下边 加空行
.text @基本格式的头
@跳转指令
@实现延时 1 s
ldr r0, = 0x1f1fffff @具体多少为 1秒 需要计算
loop:
cmp r0, #0
beq loop_end
sub r0,#1
b loop
loop_end:
mov r12, #0
.end @基本格式的尾巴 通常 该行下边 加空行
.text @基本格式的头
@判断 当前工作状态是否 是ARM 状态 ,如何切换到 user 模式或其他模式
mrs r0, cpsr @r0 = cpsr
mov r1, #0x20 @0010 0000 T 位
orr r1, r1, #0x1000000 @J 位, 结果r1把 T位和 J位置为1 ,其余为0
tst r0, r1 @判断 r0中的J和 T 是否为 0
biceq r0, #0x1f @把 mode 清0
orreq r0, #0x10 @把mode 变为 user模式: 10000
msr cpsr, r0 @cpsr = r0
.end