.s文件中的符号
伪操作:指导编译器对代码进行编译的
不占用内存空间
指令:编译器将其编译成机器码,
存放到内存空间
伪指令:本身不是一条指令,
编译器在编译的时候,
将其编译成多条指令,
完成一条伪指令的功能
汇编的基本语法
“@” : 单行注释
“/* */” : 多行注释
一条指令占用一行
汇编中不区分大小写
汇编指令的基本格式
/*
<opcode><code>{s} Rd, Rn, {oprand2}
opcode:指令的名字
code:条件码
如果什么都不写代表无条件执行
s:状态位
指令的结果会影响cpsr的NZCV位
Rd:目标寄存器
Rn:第一个操作寄存器,只能是一个寄存器
如果目标寄存器和第一个操作寄存器的编号相同
可以合并成只写1个,类似于C语言中的双目运算符
oprand2:第二个操作数 */
可以是普通的寄存器
可以是立即数或者有效数
经过移位操作的寄存器
1. 数据操作指令
2. 跳转指令
3. load/store指令
4. 特殊功能寄存器操作指令
5. 软中断
.text 以下为代码段
.global _start 生命一个全局的函数
_start: 函数的入口
代码段
1. 数据操作指令
搬移指令 mov mvn
mov/mvn<code> Rd, oprand2
mov r0, #0xFF r0 = 0xFF
#号后边跟的是立即数或者有效数
mov r1, r0 r1 = r0
mvn r2, r0 r2 = ~r0
mov r3, #0xFFFFFF
伪指令 ldr
ldr r4, =0x12345678
移位操作指令 lsl lsr asr ror
lsl/lsr/asr/ror<code>{s} Rd, Rn, <oprand2>
lsl 无符号数的左移
lsr 无符号数的右移
asr 有符号数的右移
ror 循环右移
mov r0, #0xFF
lsl r0, #0x4
将r0中的值左移4位,赋值给R1
高位移除,低位补0
lsl r1, r0, #4
将r0中的值右移4位,赋值给R2
低位移除,高位补0
lsr r2, r0, #4
将r0中的值循环右移4位,赋值给R3
低位移除,补到高位
ror r3, r0, #4
将r0中的值右移4位,赋值给R4
低位移出,高位补符号位
asr r4, r0, #4
将r0中的值右移4位,赋值给R5
低位移出,高位补符号位
ldr r0, =0x800000FF
asr r5, r0, #4
mov r0, #0xFF
mov r1, r0, lsl #4
mov r2, #(0xFF << 4)
算数运算指令 add adc sub sbc mul(乘法)
add/adc/sub/sbc<code>{s} Rd, Rn, <oprand2>
假设2个64位的数相加
第一个64位的数,
R0存放低32位,R1存放高32位
第二个64位的数,
R2存放低32位,R3存放高32位
结果R4存放低32位,R5存放高32位
.if 0 汇编中使用条件语句进行注释
mov r0, #0xFFFFFFFF
mov r1, #3
mov r2, #4
mov r3, #5
adds r4, r0, r2 r4 = 0x3
adc r5, r1, r3 r5 = r1 + r3 + C = 0x9
.endif
减法指令 sub sbc
两个64位数相减
第一个64位的数,
R0存放低32位,R1存放高32位
第二个64位的数,
R3存放低32位,R4存放高32位
R4存放低[31:0]位,R5存放高[63:32]位
.if 0
mov r0, #3
mov r1, #9
mov r2, #4
mov r3, #5
subs r4, r0, r2
sbc r5, r1, r3 r5 = r1 - r3 - !C = 0x3
.endif
mul<code>{s} Rd, Rn, Rm
mul r0, r1, r2 r0 = r1 * r2
逻辑运算指令 and orr eor bic
and/orr/eor/bic<code>{s} Rd, Rn, <oprand2>
mov r0, #0xFF
R0[7:4]清零 --》 R1
and r1, r0, #0xFFFFFF0F r1 = 0xF
and r1, r0, #(~(0xF << 4))
R0[11:8]置一 --》 R2
orr r2, r0, #(0xF << 8) r2 = 0xFFF
orr r2, r0, #0xF00
R0[11:4]取反 --》 R3 r3 = 0xF0F
eor r3, r0, #(0xFF << 4)
eor r3, r0, #0xFF0
bic 位清除指令,想把哪位清零,就给哪位写1
R0[7:4]清零 --》r4
bic r4, r0, #0xF0
位操作指令 teq tst cmp
teq/tst/cmp<code> Rn, oprand2
最终影响的是cpsr的NZCV位,不需要加s
teq 测试两个数是否相等
mov r1, #5
teq r1, #5
teq r1, #6 本质:进行了异或运算
tst 测试某一位是否为1
mov r0, #0xF 0b1111
tst r0, #0x4 0b0100
tst r0, #0x10 0b0001 0000 本质:进行了与运算
tst r0, #0x18 0b0001 1000
cmp 比较指令
mov r0, #4
cmp r0, #5 本质:做减法运算
条件码
mov r0, #3
mov r1, #4
cmp r0, r1
subhi r0, r0, r1
subcc r1, r1, r0
跳转指令 b bl
b/bl<code> lable 跳转到标签下的第一条指令开始执行
通过标签可以找到函数入口的首地址
标签相当于C语言中函数的函数名
.if 0
mov r0, #3
mov r1, #4
b func1
b跳转指令,不会保存跳转指令的下一条指令的地址到lr
fo:
add r2, r0, r1
b loop
func1:
mov r0, #5
mov r1, #6
add r3, r0, r1
mov pc, #0xF 0b1100
b fo
.endif
bl 当执行bl跳转指令时,会将跳转指令的下一条指令的地址,
保存到lr寄存器中,保存返回地址是cpu自动完成
.if 0
mov r0, #3
mov r1, #4
bl func2
add r2, r0, r1
b loop
func2:
mov r3, #4
mov r4, #5
add r5, r3, r4
mov pc, lr
总结:跳转指令的本质:修改PC值
子函数的调用使用bl
.endif
.if 0
mov r0, #9
mov r1, #15
loop:
cmp r0, r1
beq stop
subhi r0, r0, r1
subcc r1, r1, r0
b loop
.endif
特殊功能寄存器传送指令 cpsr
msr cpsr, Rn 将Rn中的值赋值给cpsr
mrs Rn, cpsr 将cpsr中的值赋值个Rn
如何从svc模式切换到用户模式
M = 0x13(svc) M= 0x10(user)
0xD3 0b1101 0011
IFTM MMMM
mov r0, #0xD0
msr cpsr, r0
msr cpsr, #0xD0
mrs r0, cpsr
bic r0, r0, #0x1F
and r0, r0, #(~0x1F)
orr r0, r0, #0x10
msr cpsr, r0
stop: 死循环相当于while(1);
b stop
.end 结束
load/sotre指令 内存读写的操作指令
单寄存器操作指令 ldr str
r:寄存器
语法格式:ldr/str Rn,[Rm]
对一个字进行操作的指令
int * p; p-->Rm
*p -->[Rm]
ldr r0, =0x20008000
ldr r1, =0x12345678
str r1, [r0] 将r1中的值存储到r0指向的地址空间中
ldr r2 ,[r0] 将r0指向的地址空间的值写道r2寄存器中
如果过直接执行会报错,地址没有读写权限
解决办法:在工程文件中创建 map.ini文件 在文件中添加以下内容
map 0x20000000,0x2000ffff read write
在工程中的projiect--target 1右键选择
options for target --debug--initialization file添加map.ini文件
ldrh strh ldrb strb
h:half半字 b:byte一个字节
用法和ldr str完全一样
str r1,[r0, #4]
将r1中的值,存储到r0+4指向的地址空间中
r0保持不变
str r1, [r0], #4
将r1中的值,存储到r0指向的地址空间中
r0 = r0+4
str r1, [r0, #4]!
将r1中的地址存储到r0+4中
r0 = r0+4
要求是4字节对齐
多寄存器操作指令 ldm stm
m:mutil
语法格式:
ldm/stm Rn, {寄存器列表}
Rn :内存的地址
{寄存器列表}:要操作的寄存器列表
stm r0,{r1-r5}
如果寄存器编号连续
如果寄存器列表中既有连续又有不连续
连续的中间使用-隔开,不连续的使用,隔开
将r1-r5寄存器中的值存储到r0指向的地址空间的连续的20个字节空间中
寄存器列表中的寄存器的顺序,不管如何排列,永远都是低地址存储编号小的
读取的时候,低地址放到小编号寄存器中
如果stm和ldm什么后缀都不加,默认为后缀为IA
IA:increment after
增加:指向高地址方向存储数据
after:当前指向的地址中,没有数据
可以先存储,再向地址增加的方向依次存储
IB:before:当前指向的地址中,有数据
地址先增加4,再向地址增加的方向依次存储
DA:Dcrement after
Dcrement : 向低地址方向存储数据
after:当前指向的地址中,没有数据
可以先存储,再向地址递减的方向依次存储
DB:before:当前指向的地址中,有数据
地址先减4,再向地址递减的方向依次存储
!:更新r0的地址
stmia r0!,{r1-r5}
栈操作指令 sp
站的种类:
增栈:栈指针想高地址方向移动
减栈:向低地址方向移动
空栈:栈指针指向的当前位置为空,如果要存储数据需要先存数据,然后栈指针在移动到一个空的位置
满栈:栈指针指向的当前位置不为空,如果要存储数据,需要先移动栈指针到一个空的位置,在存储数据
栈的操作方式
满增栈 full ascending stmfa ldmfa
满减栈 full descending
空增栈 empy ascending
空减栈 empty descending
arm默认采用的是满减栈
语法格式:
stmfd/ldmfd sp!, {寄存器列表}
软中断指令 swi