LoadStore内存读写指令
单寄存器读写指令
c
复制代码
指令码
----------------
ldr : 从内存地址中读取数据到寄存器中,读4个字节的数据
str : 将寄存器中的数据写到内存地址中,写4个字节的数据
------------------------------------------------------
ldrh : 从内存地址中读取数据到寄存器中,读2个字节的数据
strh : 将寄存器中的数据写到内存地址中,写2个字节的数据
---------------------------------------------------------
ldrb : 从内存地址中读取数据到寄存器中,读1个字节的数据
strb : 将寄存器中的数据写到内存地址中,写1个字节的数据
----------------------------------------------------------
ld : Load st : Store r : Register h : half b : byte
---------------------------------------------------------------
指令格式
---------------
ldr/ldrh/ldrb Rd, [Rm]
[Rm] : 将Rm寄存器中的数据当成内存的地址
功能:将[Rm]指向的地址空间的数据读到Rd寄存器中
int a = 100;
int *p = &a;
int b = *p;
[Rm] <==> p
ldr Rd, [Rm] <==> b = *p;
str/strh/strb Rn, [Rm]
[Rm] : 将Rm寄存器中的数据当成内存的地址
功能:将Rn寄存器中的值写到[Rm]指向的地址空间中
int a = 100, b = 200;
int *p = &a;
*p = b;
-----------------------------------
指令测试代码
-----------------------
/* 7. 单寄存器操作指令 */
ldr r0, =0x40000800 @ 准备地址
ldr r1, =0x12345678 @ 准备数据
@ 将r1寄存器中的值写到[r0]指向的地址空间中
str r1, [r0]
@ 将[r0]指向的地址空间中的数据读到R2寄存器中
ldr r2, [r0]
------------------------------------------
单寄存器操作指令的其他用法
---------------------------------
ldr/ldrh/ldrb Rn, [Rm, #offset_addr]
将[Rm + offset_addr]指向的地址空间的数据读到Rn寄存器中,
Rm寄存器中存储的内存地址不变
ldr/ldrh/ldrb Rn, [Rm], #offset_addr
---------------------------------------------------
将[Rm]指向的地址空间的数据读到Rn寄存器中,
同时更新Rm寄存器中的地址,Rm = Rm + offset_addr
ldr/ldrh/ldrb Rn, [Rm, #offset_addr]!
--------------------------------------------------------------
将[Rm + offset_addr]指向的地址空间的数据读到Rn寄存器中,
同时更新Rm寄存器中的地址,Rm = Rm + offset_addr
! : 更新地址
以上三种不同的指令格式同样适用于str/strh/strb指令。
-------------------------------------------------------------
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寄存器的地址
@ [0x40000800] = 0x22222222 r0 = 0x40000804
str r2, [r0], #4
@ 将r3寄存器中的数据写到[R0+4]指向的地址空间中
@ 同时更新r0寄存器的地址
@ [0x40000808] = 0x33333333 r0 = 0x40000808
str r3, [r0, #4]!
----------------------------------------------------------
练习题:
将0x12345678数据写到内存的0x40000800地址中,
使用ldrb指令读取0x40000800地址中的数据,每次读1个字节,读取4次。
将读取的4个字节的数据使用位运算的方式拼接得到0x12345678
ldr r0, =0x40000800
ldr r1, =0x12345678
str r1, [r0]
@ 使用ldrb方式读取单字节的数据
@ r2 = 0x78 r3 = 0x56 r4 = 0x34 r5 = 0x12
/*
ldrb r2, [r0, #0]
ldrb r3, [r0, #1]
ldrb r4, [r0, #2]
ldrb r5, [r0, #3]
*/
/*
ldrb r2, [r0], #1
ldrb r3, [r0], #1
ldrb r4, [r0], #1
ldrb r5, [r0]
*/
ldrb r2, [r0, #0]!
ldrb r3, [r0, #1]!
ldrb r4, [r0, #1]!
ldrb r5, [r0, #1]!
@ 使用位运算进行数据的拼接
/*
mov r6, r5 @ r6 = 0x12
orr r6, r4, r6, lsl #8
orr r6, r3, r6, lsl #8
orr r6, r2, r6, lsl #8
*/
/*
mov r6, r5, lsl #24
orr r6, r6, r4, lsl #16
orr r6, r6, r3, lsl #8
orr r6, r6, r2
*/
mov r6, r5, lsl #24
lsl r4, r4, #16
add r6, r6, r4
lsl r3, r3, #8
add r6, r6, r3
lsl r2, r2, #0
add r6, r6, r2
多寄存器读写指令
c
复制代码
指令码
-----------
stm : 向内存地址中同时写入多个寄存器中的数据
ldm : 将内存地址中的数据同时读到多个寄存器中
st : Store ld : Load m : multi
-----------------------------------------------------------
指令格式
--------------
stm Rm, {寄存器列表}
Rm : Rm寄存器中的数据被当成一个内存地址看待
功能:将寄存器列表中的所有寄存器中的数据写到Rm指向的内存地址的连续空间中
ldm Rm, {寄存器列表}
Rm : Rm寄存器中的数据被当成一个内存地址看待
功能:将Rm指向的内存地址空间中连续的数据读到寄存器列表的每个寄存器中
寄存器列表的书写格式:
1. 如果寄存器列表中的寄存器编号连续使用"-"隔开;
比如:{r1-r5}
2. 如果寄存器列表中的寄存器编号不连续使用","隔开;
比如:{r1,r3,r5} {r1-r4,r6}
3. 寄存器列表中的寄存器编号要求从小到大进行书写,不要从大到小书写
比如:
{r1-r5,r7} ---> OK
{r5-r1, r0} ---> Error, 编译报错
{r5,r4,r3,r2,r1} ---> Ok, 但是编译器报警告
--------------------------------------------------------
指令测试代码
------------
ldr r0, =0x40000800 @ 准备地址
ldr r1, =0x11111111 @ 准备数据
ldr r2, =0x22222222
ldr r3, =0x33333333
ldr r4, =0x44444444
ldr r5, =0x55555555
@ 将r1-r5寄存器中的数据写到
@ r0指向的连续的20字节内存空间中
@ stm r0, {r1-r5}
stm r0, {r5,r4,r3,r2,r1}
@ 将r0指向的连续的20字节内存空间
@ 的数据读到r6-r10寄存器中
@ ldm r0, {r6-r9, r10}
ldm r0, {r10,r9,r8,r7,r6}
--------------------------------------------
不管寄存器列表中的寄存器的顺序如何书写,
永远都是小编号的寄存器对应着低地址,
大编号的寄存器对应着高地址。
---------------------------------------------------
{r5-r1, r0} ---> Error, 编译报错
{r5,r4,r3,r2,r1} ---> Ok, 但是编译器报警告
特殊功能寄存器读写指令
c
复制代码
指令码
--------------
对cpsr寄存器进行读写操作的指令
msr : 将普通寄存器中的数据写到特殊功能寄存器cpsr中
mrs : 将特殊功能寄存器cpsr中的数据写到普通寄存器中
m : move
s : special
r : register
--------------------------------------------------------------
指令格式
----------------
msr cpsr, Rn
mrs Rd, cpsr
-----------------------
指令测试代码
--------------
/* 系统上电,处理器默认工作再SVC模式下,
修改CPSR寄存器的M[4:0]位,切换到user模式下,
SVC模式(0b10011) ---> User模式(0b10000)
修改模式位的同时,要保证其他位不变
修改CPSR的值: 1101 0011 ---> 1101 0000*/
@ 方式1:之间对CPSR寄存器进行赋值操作
@ msr cpsr, #0xD0
--------------------------------------------------------
@ 方式2:使用位运算
@ 1. 先将cpsr中的值读到普通寄存器中
mrs r0, cpsr
@ 2. 将普通寄存器中的[4:0]位清0
bic r0, r0, #0x1F @ and r0, r0, #(~0x1F)
@ 3. 将普通寄存器中的[4:0]位写成0x10
orr r0, r0, #0x10
@ 4. 将普通寄存器中的值写到CPSR中
msr cpsr, r0
-----------------------------------------------------------
栈操作指令
c
复制代码
栈的种类
--------------
增栈:压栈之后栈指针向高地址方向移动。
------------------------------------
减栈:压栈之后栈指针向低地址方向移动。
-------------------------------------
满栈:当前栈指针指向的栈空间中有有效的数据,要想在栈空间中压入数据,
需要向移动栈指针指向一个空的空间,然后再压入数据。
压入数据之后,栈指针指向的空间又有有效的数据。
----------------------------------------------------
空栈:当前栈指针指向的栈空间中没有有效的数据,可以先压入数据,
需要移动栈指针指向一个空的空间。此时栈指针指向的又是一个空的空间。
-----------------------------------------------------------------
栈空间的操作方式
-----------------
栈空间的读写方式有4种:
满增栈 :Full Ascending Stack
满减栈 :Full Descending Stack
空增栈 :Empty Ascending Stack
空减栈 :Empty Descending Stack
--------------------------------------
栈空间操作指令
---------------
满增栈操作指令:stmfa/ldmfa
满减栈操作指令:stmfd/ldmfd
空增栈操作指令:stmea/ldmea
空减栈操作指令:stmed/ldmed
----------------------------------------------
ARM处理器中默认采用的是满减栈。
--------------------------------------------
栈空间操作指令的语法格式
-----------------------------
stmfd sp!, {寄存器列表}
sp : sp中存放的是指向的栈空间的地址
! : 压栈之后更新栈指针的地址
功能:将寄存器列表中的每个寄存器中的数据压到SP栈指针指向的连续的栈空间。
ldmfd sp!, {寄存器列表}
sp : sp中存放的是指向的栈空间的地址
! : 压栈之后更新栈指针的地址
功能:将SP栈指针指向的连续的栈空间中的数据出栈到寄存器列表的每个寄存器中。
寄存器列表的书写格式:
1. 如果寄存器列表中的寄存器编号连续使用"-"隔开;
比如:{r1-r5}
2. 如果寄存器列表中的寄存器编号不连续使用","隔开;
比如:{r1,r3,r5} {r1-r4,r6}
3. 寄存器列表中的寄存器编号要求从小到大进行书写,不要从大到小书写
比如:
{r1-r5,r7} ---> OK
{r5-r1, r0} ---> Error, 编译报错
{r5,r4,r3,r2,r1} ---> Ok, 但是编译器报警告
-------------------------------------------------------------
指令测试代码
-----------------
ldr sp, =0x40000820 @ 准备地址
ldr r1, =0x11111111 @ 准备数据
ldr r2, =0x22222222
ldr r3, =0x33333333
ldr r4, =0x44444444
ldr r5, =0x55555555
@ 压栈
@ 将r1-r5寄存器中的数据压倒sp指向的连续的栈空间中,
@ 同时更新sp中的地址 , sp = sp - 20
stmfd sp!, {r1-r5}
@ 出栈
@ 将sp指向的栈空间的数据出栈到r6-r10寄存器中,
@ 同时更新sp中的地址, sp = sp + 20
ldmfd sp!, {r6-r10}
------------------------------------------------------
#include <stdio.h>
void add_func()
{
int a = 300, b = 400;
int sum = a + b; // 700
}
int main(int argc, const char *argv[])
{
int a = 100, b = 200;
add_func();
int sum = a + b; // 300
return 0;
}
-------------------------------------------
ldr sp, =0x40000820 @ 初始化栈指针
mov r0, #3
mov r1, #4
bl add_func
add r2, r0, r1 @ r2 = r0 + r1 = 0x7
b stop
add_func:
stmfd sp!, {r0-r1, lr} @ 压栈保存现场
mov r0, #5
mov r1, #6
bl sub_func
add r3, r0, r1 @ r3 = r0 + r1 = 0xB
ldmfd sp!, {r0-r1, pc} @ 出栈恢复现场
@ mov pc, lr
sub_func:
stmfd sp!, {r0-r1} @ 压栈保存现场
mov r0, #10
mov r1, #2
sub r4, r0, r1 @ r4 = r0 - r1 = 0x8
ldmfd sp!, {r0-r1} @ 出栈恢复现场
mov pc, lr
跳转指令
c
复制代码
指令码:
----------------
b : 不带返回值的跳转指令
发生跳转时,不会自动的保存返回地址到LR寄存器中
----------------------------------------------------------
bl : 带返回值的跳转指令
发生跳转时,会自动保存返回地址到LR寄存器中
-------------------------------------------------------------
指令格式
----------------------------------------------------------
b/bl{cond} Label(标签) @ 程序跳转到label标签下的第一条指令
Label: @ 可以理解为C语言的函数名
asm code
注:
1. label标签可以看成函数名,表示函数的入口地址,
及函数内的第一条汇编指令的地址
2. 跳转指令的本质是修改PC寄存器的值, PC = Label
3. b跳转指令的使用:有去无回就用b;
比如:
stop:
@ asm code
b stop
4. bl跳转指令的使用:有去有回就用bl;
比如:函数的调用
-------------------------------------------------------
指令测试代码
-------------------
/* 6. 跳转指令 */
@ 定义一个交换函数,交换两个寄存器的值
mov r0, #15
mov r1, #9
@ 使用bl指令调用swap_func函数
@ 1. 查看PC寄存器是否执行函数的第一条指令
@ 2. 查看LR寄存器中是否保存返回地址
bl swap_func
nop
nop @ 空指令,没有任何的意义,只是占位
@ b stop
ldr pc, stop
swap_func:
eor r0, r0, r1
eor r1, r0, r1
eor r0, r0, r1
mov pc, lr @ 函数的返回
stop:
b stop
----------------------------------------------------------
跳转指令的实现的其他的方式
----------------------------------
1. mov pc, lr @ 一般用于程序的返回
2. ldr pc, =Label @ pc = Label 等价于 b Label
3. mov pc, #label @ 不一定,Label不一定是一个立即数,一般不用
-----------------------------------------------------------------
练习题:使用跳转指令,比较指令,条件码,减法指令求两个数的最大公约数
---------------------------------------------------------------------
.text
.global _start
_start:
mov r0, #9
mov r1, #15
loop:
cmp r0, r1
beq stop
subhi r0, r0, r1
subcc r1, r1, r0
b loop
stop:
b stop
.end