嵌入式学习笔记(四)

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 
相关推荐
EterNity_TiMe_40 分钟前
【论文复现】(CLIP)文本也能和图像配对
python·学习·算法·性能优化·数据分析·clip
sanguine__43 分钟前
java学习-集合
学习
lxlyhwl43 分钟前
【STK学习】part2-星座-目标可见性与覆盖性分析
学习
nbsaas-boot44 分钟前
如何利用ChatGPT加速开发与学习:以BPMN编辑器为例
学习·chatgpt·编辑器
dr李四维1 小时前
iOS构建版本以及Hbuilder打iOS的ipa包全流程
前端·笔记·ios·产品运营·产品经理·xcode
CV学术叫叫兽1 小时前
一站式学习:害虫识别与分类图像分割
学习·分类·数据挖掘
我们的五年2 小时前
【Linux课程学习】:进程程序替换,execl,execv,execlp,execvp,execve,execle,execvpe函数
linux·c++·学习
一棵开花的树,枝芽无限靠近你2 小时前
【PPTist】添加PPT模版
前端·学习·编辑器·html
VertexGeek2 小时前
Rust学习(八):异常处理和宏编程:
学习·算法·rust
二进制_博客3 小时前
Flink学习连载文章4-flink中的各种转换操作
大数据·学习·flink