msr -->>将普通寄存器中的数据写到特殊寄存器中
mrs -->>将特殊寄存器中的数据写到普通寄存器中
注:特殊寄存器 cpsr 的读写访问只能使用 msr 和 mrs 指令
.text /* 代码段 */
/* 将_start 声明为一个全局的函数, _start表示汇编程序的入口 */
.globl _start /* globl 与 global 效果一样 */
_start: /* 标签 : 类似于C语言的函数的名字, 表示函数的入口地址 */
/*
格式
msr cpsr, 普通寄存器/立即数 @ cpsr = 普通寄存器/立即数
mrs Rd, cpsr @ Rd = cpsr
*/
@ 系统上电默认是svc 模式
@ 修改系统模式到用户模式
@ svc模式(10011) -->> 用户模式(10000)
@ 1101 0011 -->> 1101 0000
@ 1.通过 msr 直接修改
@msr cpsr, #0xd0
@ 2.通过位运算的方式
mrs r0, cpsr
bic r0, r0, #0x1f @将[4:0]位清零
orr r0, r0, #0x10
msr cpsr, r0
stop:
b stop
.end
ldr -->>将内存中的数据读到普通寄存器中,读4个字节的大小
str -->>将普通寄存器中的数据写到内存中,写4个字节的大小
ldrh -->>将内存中的数据读到普通寄存器中,读2个字节的大小
strh -->>将普通寄存器中的数据写到内存中,写2个字节的大小
ldrb -->>将内存中的数据读到普通寄存器中,读1个字节的大小
strb -->>将普通寄存器中的数据写到内存中,写1个字节的大小
.text /* 代码段 */
/* 将_start 声明为一个全局的函数, _start表示汇编程序的入口 */
.globl _start /* globl 与 global 效果一样 */
_start: /* 标签 : 类似于C语言的函数的名字, 表示函数的入口地址 */
/*
其他用法:
@ 将[Rm + offset]地址中的内容读到Rd寄存器中,Rm中的值不变
ldr/ldrh/ldrb Rd, [Rm, #offset]
@ 将[Rm]地址中的内容读到Rd寄存器中, 同时更新Rm中的地址:Rm=Rm+offset
ldr/ldrh/ldrb Rd, [Rm], #offset
@ 将[Rm + offset]地址中的内容读到Rd寄存器中,同时更新Rm中的地址:Rm=Rm+offset
@ ! : 更新地址
ldr/ldrh/ldrb Rd, [Rm, #offset]!
@ 将Rn寄存器中的数据写到[Rm + offset]地址中,Rm中的值不变
str/strh/strb Rn, [Rm, #offset]
@ 将Rn寄存器中的数据写到[Rm]地址中,同时更新Rm中的地址:Rm=Rm+offset
str/strh/strb Rn, [Rm], #offset
@ 将Rn寄存器中的数据写到[Rm + offset]地址中,同时更新Rm中的地址:Rm=Rm+offset
str/strh/strb Rn, [Rm, #offset]!
offset : 偏移地址,偏移地址的大小是Load/Store指令可访问空间大小的整数倍。
*/
.if 0
ldr r0, =0x12345678
ldr r1, =0x40000800
@ 将 r0 中的数据写到 [r1] 指向的地址空间
str r0, [r1]
@ 将 [r1] 指向的地址空间的数据读到r2寄存器中
ldr r2, [r1]
.endif
ldr r0, =0x40000800
ldr r1, =0x11111111
ldr r2, =0x22222222
ldr r3, =0x33333333
@ 将r1中的数据写到[r0+4]地址中,r0中的值不变
@ [0x40000804] = 0x11111111 , r0 = 0x40000800
str r1, [r0, #4]
@ 将r2中的数据写到[r0]地址中,同时更新r0中的地址,r0=r0+4
@ [0x40000800] = 0x22222222 , r0 = 0x40000804
str r2, [r0], #4
@ 将r1中的数据写到[r0+4]地址中,同时更新r0中的地址,r0=r0+4
@ [0x40000808] = 0x33333333 , r0 = 0x40000808
str r3, [r0, #4]!
stop:
b stop
.end
执行结果:
.text /* 代码段 */
/* 将_start 声明为一个全局的函数, _start表示汇编程序的入口 */
.globl _start /* globl 与 global 效果一样 */
_start: /* 标签 : 类似于C语言的函数的名字, 表示函数的入口地址 */
/*
ldm Rm, {寄存器列表}
将Rm指向的连续的地址空间中的数据读到寄存器列表的每个寄存器中
stm Rm, {寄存器列表}
将寄存器列表中的每个寄存器中的数据写到Rm指向的连续的地址空间中
使用注意:
1. Rm寄存器中的数据被当成一个地址看待
2. 寄存器列表中的寄存器如果是连续的则使用"-"隔开;
3. 寄存器列表中的寄存器如果不连续则使用","隔开;
4. 寄存器列表中的寄存器要求从小到大依次书写,
如果从大到小书写,要求依次用逗号隔开书写,但编译器会报警告。
*/
@ 不管寄存器列表中的寄存器的顺序如何书写,
@ 永远都是小编号的寄存器对应低地址;
@ 大编号的寄存器对应高地址
ldr r0, =0x40000800
ldr r1, =0x11111111
ldr r2, =0x22222222
ldr r3, =0x33333333
ldr r4, =0x44444444
@ 将r1-r4寄存器中的数据写到r0指向的连续的16字节空间中
stm r0, {r1-r4}
@ 将r0指向的连续的16字节空间的数据读到r5-r8中
ldm r0, {r5-r7,r8}
stop:
b stop
.end
执行结果:
1> 增栈: 压栈之后,栈指针向高地址方向移动。
2> 减栈: 压栈之后,栈指针向低地址方向移动。
3> 空栈: 当前栈指针指向的空间没有有效的数据,因此可以先压栈,压栈之后栈指针指向的空间就有有效的数据,因此需要移动栈指针,让栈指针指向一个没有有效数据的空间。
4> 满栈: 栈指针指向的空间有有效的数据,需要先移动栈指针,让栈指针指向一个空的位置,在进行压栈的操作,压制之后此时栈指针指向的空间又有有效的数据。
栈的操作方式都是组合来使用的
满增栈 : Full Ascending -----> stmfa/ldmfs
满减栈 : Full Descending -----> stmfd/ldmfd
空增栈 : Empty Ascending -----> stmea/ldmea
空减栈 : Empty Descending -----> stmed/ldmed
ARM处理器默认采用的是满减栈,ARM指令集本身是支持以上4种栈的操作方式的所有的指令。
.text /* 代码段 */
/* 将_start 声明为一个全局的函数, _start表示汇编程序的入口 */
.globl _start /* globl 与 global 效果一样 */
_start: /* 标签 : 类似于C语言的函数的名字, 表示函数的入口地址 */
/*
指令格式
ldmfd sp!, {寄存器列表}
将sp指向的栈空间中的数据读到寄存器列表的每个寄存器中
stmfd sp!, {寄存器列表}
将寄存器列表中的每个寄存器中的数据写到sp指向的栈空间中
使用注意:
0. ! : 压栈和出栈之后都需要更新栈指针
1. sp寄存器中的数据被当成一个栈空间的地址看待
2. 寄存器列表中的寄存器如果是连续的则使用"-"隔开;
3. 寄存器列表中的寄存器如果不连续则使用","隔开;
4. 寄存器列表中的寄存器要求从小到大依次书写,
如果从大到小书写,要求依次用逗号隔开书写,编译器会报警告。
*/
ldr sp, =0x40000800 @ 初始化sp的地址
mov r0, #1
mov r1, #2
bl add_func1
add r2, r0, r1
nop
b stop
add_func1:
stmfd sp!, {r0-r1, lr}
mov r0, #3
mov r1, #4
bl add_func
add r3, r0, r1
ldmfd sp!, {r0-r1, lr} @ 也可以写成ldmfd sp!, {r0-r1, pc}
mov pc, lr
add_func:
stmfd sp!, {r0,r1}
mov r0, #5
mov r1, #6
add r4, r0, r1
ldmfd sp!, {r0,r1}
mov pc, lr
stop:
b stop
.end
执行结果;