LoadStore内存读写指令
单寄存器读写指令
指令码
-- -- -- -- -- -- -- -- 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] <= = > pldr Rd, [ Rm] <= = > b = * p; str/ strh/ strb Rn, [ Rm] [ Rm] : 将Rm寄存器中的数据当成内存的地址功能:将Rn寄存器中的值写到[ Rm] 指向的地址空间中int a = 100 , b = 200 ; int * p = & a; * p = b;
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - 指令测试代码
-- -- -- -- -- -- -- -- -- -- -- - 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_addrldr/ 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, #1 ] ! ldrb r5, [ r0, #1 ] ! @ 使用位运算进行数据的拼接mov r6, r5, lsl #24 lsl r4, r4, #16 add r6, r6, r4lsl r3, r3, #8 add r6, r6, r3lsl r2, r2, #0 add r6, r6, r2
多寄存器读写指令
指令码
-- -- -- -- -- - 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, 但是编译器报警告
特殊功能寄存器读写指令
指令码
-- -- -- -- -- -- -- 对cpsr寄存器进行读写操作的指令msr : 将普通寄存器中的数据写到特殊功能寄存器cpsr中mrs : 将特殊功能寄存器cpsr中的数据写到普通寄存器中m : move s : special r : register
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 指令格式
-- -- -- -- -- -- -- -- msr cpsr, Rnmrs Rd, cpsr-- -- -- -- -- -- -- -- -- -- -- - 指令测试代码
-- -- -- -- -- -- --
@ 方式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
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
栈操作指令
栈的种类
-- -- -- -- -- -- -- 增栈:压栈之后栈指针向高地址方向移动。
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 减栈:压栈之后栈指针向低地址方向移动。
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - 满栈:当前栈指针指向的栈空间中有有效的数据,要想在栈空间中压入数据,需要向移动栈指针指向一个空的空间,然后再压入数据。压入数据之后,栈指针指向的空间又有有效的数据。
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 空栈:当前栈指针指向的栈空间中没有有效的数据,可以先压入数据,需要移动栈指针指向一个空的空间。此时栈指针指向的又是一个空的空间。
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - 栈空间的操作方式
-- -- -- -- -- -- -- -- -
栈空间的读写方式有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;
} int main ( int argc, const char * argv[ ] )
{ int a = 100 , b = 200 ; add_func ( ) ; int sum = a + b; return 0 ;
}
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
ldr sp, = 0x40000820 @ 初始化栈指针mov r0, #3 mov r1, #4 bl add_funcadd r2, r0, r1 @ r2 = r0 + r1 = 0x7 b stopadd_func: stmfd sp! , { r0- r1, lr} @ 压栈保存现场mov r0, #5 mov r1, #6 bl sub_funcadd r3, r0, r1 @ r3 = r0 + r1 = 0xB ldmfd sp! , { r0- r1, pc} @ 出栈恢复现场@ mov pc, lrsub_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
跳转指令
指令码:
-- -- -- -- -- -- -- -- b : 不带返回值的跳转指令发生跳转时,不会自动的保存返回地址到LR寄存器中-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- bl : 带返回值的跳转指令发生跳转时,会自动保存返回地址到LR寄存器中
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - 指令格式
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
b/ bl{ cond} Label ( 标签) @ 程序跳转到label标签下的第一条指令Label: @ 可以理解为C语言的函数名asm code注:1. label标签可以看成函数名,表示函数的入口地址,及函数内的第一条汇编指令的地址2. 跳转指令的本质是修改PC寄存器的值, PC = Label3. b跳转指令的使用:有去无回就用b;比如:stop: @ asm codeb stop4. bl跳转指令的使用:有去有回就用bl;比如:函数的调用-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - 指令测试代码
-- -- -- -- -- -- -- -- -- - @ 定义一个交换函数,交换两个寄存器的值mov r0, #15 mov r1, #9 @ 使用bl指令调用swap_func函数@ 1. 查看PC寄存器是否执行函数的第一条指令@ 2. 查看LR寄存器中是否保存返回地址bl swap_funcnopnop @ 空指令,没有任何的意义,只是占位@ b stopldr pc, stopswap_func: eor r0, r0, r1eor r1, r0, r1eor r0, r0, r1mov pc, lr @ 函数的返回stop: b stop
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 跳转指令的实现的其他的方式
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 1. mov pc, lr @ 一般用于程序的返回2. ldr pc, = Label @ pc = Label 等价于 b Label3. mov pc, #label @ 不一定,Label不一定是一个立即数,一般不用
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - 练习题:使用跳转指令,比较指令,条件码,减法指令求两个数的最大公约数
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - . text. global _start_start: mov r0, #9 mov r1, #15 loop: cmp r0, r1beq stopsubhi r0, r0, r1subcc r1, r1, r0b loopstop: b stop . end