嵌入式养成计划-51----ARM--ARM汇编指令--内存读写指令--程序状态寄存器传输指令--软中断指令--混合编程

一百二十七、内存读写指令

  • 通过内存读写指令可以实现向内存中写入指定数据或者读取指定内存地址的数据

127.1 单寄存器内存读写指令

  • 将一个寄存器中的数值写入到内存,或者从内存中读取数据放在某一个指定寄存器中

127.1.1 指令码和功能

1. 向内存中写:
str{条件码} 目标寄存器,[目标地址]	:	将目标寄存器的4字节数值写入到目标地址为首地址的空间中
strh{条件码} 目标寄存器,[目标地址]	:	将目标寄存器的2字节数值写入到目标地址为首地址的空间中
strb{条件码} 目标寄存器,[目标地址]	:	将目标寄存器的1字节数值写入到目标地址为首地址的空间中2. 从内存中读:
ldr{条件码} 目标寄存器,[目标地址]	:	从目标地址为首地址的空间中读取4字节数据存放在目标寄存器中
ldrh{条件码} 目标寄存器,[目标地址]	:	从目标地址为首地址的空间中读取2字节数据存放在目标寄存器中
ldrb{条件码} 目标寄存器,[目标地址]	:	从目标地址为首地址的空间中读取1字节数据存放在目标寄存器中

127.1.2 示例

.text  
.global start 
_start:mov r0,#0XFFFFFFFEmov r1,#0X40000000  @R1保存内存地址@将r0的值写入到r1对应的地址空间str r0,[r1]@从r1对应的地址空间中读取数据保存在r2中ldr r2,[r1]
wh: b wh  
.end 

在这里插入图片描述

127.1.3 单寄存器内存索引方式

  • 前索引方式
    str{条件码} 目标寄存器,[目标地址,#立即数]//将目标寄存器的数据保存在目标地址+立即数为起始地址的内存中
    ldr{条件码} 目标寄存器,[目标地址,#立即数]//从目标地址+立即数为起始地址的内存中读取数据保存在目标寄存器
    
    在这里插入图片描述
  • 后索引方式
    str{条件码} 目标寄存器,[目标地址],#立即数//将目标寄存器的数据保存在目标地址为起始地址的内存中,接着目标地址自加立即数大小
    ldr{条件码} 目标寄存器,[目标地址],#立即数//从目标地址为起始地址的内存中读取数据保存在目标寄存器,接着目标地址自加立即数大小
    
    在这里插入图片描述
  • 自动索引方式
    str{条件码} 目标寄存器,[目标地址,#立即数]!//将目标寄存器的数据保存在目标地址+立即数为起始地址的内存中,接着目标地址自加立即数大小
    ldr{条件码} 目标寄存器,[目标地址,#立即数]!//从目标地址+立即数大小为起始地址的内存中读取数据保存在目标寄存器,接着目标地址自加立即数大小
    
    在这里插入图片描述

127.2 批量寄存器的内存读写方式

  • 将多个寄存器的数据存放在内存中以及从内存中取出数据保存在多个寄存器中

127.2.1 指令码以及格式

向内存写:stm 目标地址,{目标寄存器列表}将列表中各个寄存器的数值保存在目标地址对应的地址空间中 
从内存读:ldm 目标地址,{目标寄存器列表}从目标地址对应的地址空间中拿数据保存到寄存器列表中各个寄存器中注意:
1. 寄存器列表中每一个寄存器之间用','分隔,如果寄存器列表中寄存器的编号连续,那么可以用-表示一定范围内的寄存器,比如 {r1-r5}
2. 无论寄存器列表中的寄存器表现形式如何,在存取数据时始终是小编号寄存器对应低地址

127.2.2 示例

.text  
.global start 
_start:mov r0,#0X40000000mov r1,#1mov r2,#2mov r3,#3mov r4,#4mov r5,#5@向内存中写@stm r0,{r2,r1,r4,r3,r5}stm r0,{r1-r5}@从内存中读取数据ldm r0,{r6-r10}
wh: b wh  
.end 

在这里插入图片描述

127.2.3 批量寄存器的地址增长方式

  • 每次向寄存器保存的地址对应的地址空间中写入一个数据,这个寄存器保存的地址会发生相应的增长变化,这就是批量寄存器的地址增长方式
    .text  
    .global start 
    _start:mov r0,#0X40000000mov r1,#1mov r2,#2mov r3,#3mov r4,#4mov r5,#5@向内存中写stm r0!,{r1-r5}
    wh: b wh  
    .end 
    
    在这里插入图片描述
  • 内存读写命令后加ia后缀
    先向 R0 寄存器存放的地址指向的内存空间中保存一个数据,然后 R0 寄存器指向的地址往高地址方向增长
  • 内存读写命令后加ib后缀
    先 R0 寄存器指向的地址往高地址方向增长,然后向 R0 寄存器存放的地址指向的内存空间中保存一个数据
  • 内存读写命令后加da后缀
    先向 R0 寄存器存放的地址指向的内存空间中保存一个数据,然后 R0 寄存器指向的地址往低地址方向增长
  • 内存读写命令后加db后缀
    先 R0 寄存器指向的地址往低地址方向增长,然后向 R0 寄存器存放的地址指向的内存空间中保存一个数据

127.3 栈内存的读写

  • 栈指针寄存器:SP/R13 保存栈顶的地址
  • 栈:本质上就是一段内存。在内存中选取一段内存作为栈内存,可以用于保存临时数据。

127.3.1 栈内存的读写

增栈	:	每次压栈结束,SP保存的栈顶地址往高地址方向增栈
减栈	:	每次压栈结束,SP保存的栈顶地址往低地址方向增栈空栈	:	压栈结束后,SP保存的栈顶空间中没有有效数据
满栈	:	压栈结束后,SP保存的栈顶空间中有有效数据其本质就是先改变指向还是先存放数据空增栈(EA)/空减栈(ED)/满增栈(FA)/满减栈(FD)
E	:	empty 空			F	:	full 满
A	:	addition 增加		D	:	delete 减少当前ARM处理器使用的是哪种栈?满减栈

127.3.2 满减栈压栈出栈操作

  1. push {寄存器列表}@压栈
    pop {寄存器列表}@出栈
  2. .text  
    .global start 
    _start:@初始化栈ldr SP,=0X40000020mov r1,#1mov r2,#2mov r3,#3mov r4,#4mov r5,#5@压栈stmdb sp!,{r1-r5}@出栈ldmia sp!,{r6-r10}
    wh: b wh  
    .end 
    
  3. .text  
    .global start 
    _start:@初始化栈ldr SP,=0X40000020mov r1,#1mov r2,#2mov r3,#3mov r4,#4mov r5,#5@压栈stmfd sp!,{r1-r5}@出栈ldmfd sp!,{r6-r10}
    wh: b wh
    .end 
    

127.3.3 栈的应用实例—叶子函数调用过程

  • 当我们在主函数中调用一个函数,被调用的这个函数中没有别的函数调用,那么 这个函数就叫做叶子函数

如:

.text  
.global _start 
_start:@初始化栈ldr SP,=0X40000020b main
main:mov r1,#3mov r2,#4bl fun1add r3,r1,r2b main
fun1:@压栈保护现场stmfd sp!,{r1,r2}mov r1,#7mov r2,#9sub r4,r2,r1@出栈恢复现场ldmfd sp!,{r1,r2}mov pc,lr   @程序返回
.end 

127.3.4 栈的应用实例—非叶子函数调用过程

  • 当我们在主函数中调用一个函数,被调用的这个函数中存在别的函数调用,那么 这个函数就叫做非叶子函数
.text  
.global _start 
_start:@初始化栈ldr SP,=0X40000020b main
main:mov r1,#3mov r2,#4bl fun1add r3,r1,r2b main
fun1:@压栈保护现场stmfd sp!,{r1,r2,lr}mov r1,#7mov r2,#9bl fun2sub r4,r2,r1@出栈恢复现场ldmfd sp!,{r1,r2,lr}mov pc,lr   @程序返回
fun2:stmfd sp!,{r1,r2}mov r1,#4mov r2,#8mul r4,r2,r1@出栈恢复现场ldmfd sp!,{r1,r2}mov pc,lr   @程序返回
.end

一百二十八、程序状态寄存器传输指令

  • 指令的作用实现CPSR寄存器数值的读取以及数值的修改

128.1 指令码以及格式

格式:
msr CPSR,第一操作数将第一操作数的数值写入到CPSR寄存器中
mrs 目标寄存器,CPSR读取CPSR数值保存到目标寄存器中

128.2 实例

.text  
.global _start 
_start:mrs r1,CPSR  @读取CPSR数值@切换到USR模式,取消FIQ和IRQ禁止msr CPSR,#0x10
.end 注意:
user模式是ARM处理器工作模式中唯一的非特权模式,
这种模式下无法通过手动修改CPSR数值切换到特权模式,
只有发生对应的异常后才可以切换到异常模式

一百二十九、软中断指令

129.1 软中断概念

  • 软中断是从软件层次上模拟的硬件中断,原理和硬件中断一样。
  • 软中断触发之后CPU进行异常模式的切换(SVC),紧接着执行软中断对应的异常处理程序。

129.2 软中断指令码以及使用

  • swi 中断号
  • 注意:中断号是一个由24位二进制数组成的一个整数,用于区分不同的中断

129.3 异常处理过程分析

129.3.1 异常模式和异常源的对应关系

  • 5种异常模式对应7种异常源

    异常模式异常源解释
    FIQFIQ类型异常源一些硬件发生了FIQ异常事件进入FIQ模型
    IRQIRQ类型异常源一些硬件发生了IRQ异常事件进入IRQ模型
    SVC复位信号按键复位/上电复位时产生
    swi软中断指令执行swi指令
    undef未定义异常源译码器在翻译指令时,遇到无法翻译的指令,指令未定义
    abortdata abort取数据发生异常时
    prefetch abort取指令发生异常时

129.3.2 异常的处理过程分析(面试重点)

  • 异常的处理过程
    当一个异常源产生之后CPU会进行一些工作用于程序的跳转以及异常模式的切换,这个过程分为四大步三小步
    1. 保存发生异常之前的CPSR的值到对应异常模式下的SPSR寄存器中
    2. 修改CPSR的数值2.1	根据实际情况设置FIQ和IRQ中断禁止  CPSR[7:6]2.2 修改处理器工作状态为ARM状态  CPSR[5]2.3 修改处理器的工作模式为对应的异常模式 CPSR[4:0]
    3. 保存主程序的返回地址到对应模式下的LR寄存器中
    4. PC的值到对应异常模式下的异常向量表中
    
  • 处理完异常之后现场的恢复过程
    1. 恢复CPSR寄存器的值为未发生异常之前的状态
    2. 修改PC的值为未发生异常之前的下一条指令地址   PC=LR
    
    在这里插入图片描述

129.3.3 异常向量表

  1. 异常向量表是内存空间中的一段内存。
    这段内存占据了32字节,被平分为8等份,一份是4字节。
    每一份内存对应一种异常源,有一份保留,在异常向量表内存里存放的是当前异常源对应的异常处理程序的跳转指令。
    当发生异常之后,CPU会修改PC的值为对应异常源在异常向量中的位置,执行这个位置中的跳转指令,去处理异常处理程序。
  2. 每一种异常源在异常向量表中的位置是固定,不能随便修改。
  3. 只要设置了异常向量表的基地址,就可以根据不同异常在一场向量表中的位置找到对应异常的跳转指令。
    在这里插入图片描述

129.4 软中断异常处理实例

.text  
.global _start 
_start:@初始化异常向量表b mainb .b do_swib .b .b .b .b .
main:@初始化栈mov sp,#0X40000020@切换到USER模式MSR CPSR,#0X10MOV R1,#1MOV R2,#2@触发软中断SWI 1add r3,r1,r2b main@异常处理
do_swi:	@保护现场stmfd sp!,{r1,r2,lr}mov r1,#3mov r2,#4mul r4,r1,r2@恢复现场ldmfd sp!,{r1,r2,pc}^  @  ^的作用是修改PC的值的同时将SPSR的值赋值给CPSR
.end

一百三十、混合编程

130.1 混合编程的意义

  • 所谓的混合编程就是c语言资源和汇编资源的相互调用
    • 一般工程会有汇编启动程序,启动程序完成堆栈的相关初始化,完毕之后才跳转到c语言的main函数
    • c语言中几乎不可以直接操作寄存器,但是有些特定场景下需要c中操作寄存器,这时候就需要c语言中嵌套汇编的语法

130.2 混合编程概述

  • 要想实现C和汇编的混合编程必须遵循ATPCS规范。

  • ATPCS : ARM-Thumb Procedure Call Standard

    int add(int i,int j)
    {return i+j;
    }
    
  • 将汇编的标签当作C语言的函数使用

  • 将C语言的函数当作汇编的标签使用

  • 函数参数的传递采用R0-R3进行传递,如果参数的个数大于4个通过压栈的方式进行传递

  • 函数的返回值通过R0返回,如果函数的返回值大于4个字节通过r0-r1返回。

  • ATPCS规范中规定ARM采用满减栈。

130.3 汇编调用C语言的函数

  • 将C语言的函数当作汇编的标签使用

示例:

  • 汇编文件
    .text    
    .global _start  
    _start: @ 1. 初始化栈指针,C代码运行必须有栈ldr sp, =0x40000820@ 2. 汇编调用c函数 @ 2.1 给C的函数传递实参值mov r0, #3   @ a = 3mov r1, #4   @ b = 4mov r2, #5   @ c = 5mov r3, #6   @ d = 6@ 2.2 汇编调用c的函数bl add_func@ 2.3 函数的返回通过r0返回,查看r0寄存器中的值
    loop:   b loop  
    .end
    
  • c文件
    // c代码的函数是一个全局的函数
    int add_func(int a, int b, int c, int d) 
    {return (a+b+c+d);
    }
    

130.4 c语言调用汇编标签

  • 将汇编的标签当作c语言的函数

示例:

  • 起始汇编文件
    .text    
    .globl _start  
    _start: @ 1. 初始化栈指针,C代码运行必须有栈ldr sp, =0x40000820@ 2. 汇编调用c,跳转到main函数b main
    .end
    
  • c文件
    // 使用extern对函数进行声明
    extern int add_func(int a, int b, int c, int d);int sum = 0;
    int main()
    {// 在c代码中调用汇编代码sum = add_func(1,2,3,4);while(1);return 0;
    }
    
  • 汇编文件
    .text 
    .global add_func  	@ 将add_func函数声明为全局add_func:add r0, r0, r1add r0, r0, r2add r0, r0, r3mov pc, lr
    .end
    

130.5 c语言内联汇编

  • 在某一些特定的场景下需要在c语言中直接使用汇编的语法,此时需要内联汇编。内联汇编的实现需要通过asm关键字进行修饰

130.5.1 格式

asm volatile("汇编指令模板\n\t"     //"\n\t"表示一条指令的结束.....:输出列表  //指令结果的输出值:输入列表  //指令的数据输入:破坏列表  //破坏列表指定我们当前可用的寄存器
);

130.5.2 实例

  • 汇编启动文件
    .text    
    .globl _start  
    _start: @ 1. 初始化栈指针,C代码运行必须有栈ldr sp, =0x40000820@ 2. 汇编调用c,跳转到main函数b main
    .end
    
  • c语言文件
    // 内联汇编 
    int add_func2(int a, int b, int c, int d)
    {int sum = 0;// 使用汇编实现求和asm volatile(  "add r0, r0, r1\n\t"  "add r0, r0, r2\n\t"  "add r0, r0, r3\n\t"  :"=r"(sum)  :"r"(a),"r"(b),"r"(c),"r"(d)  :"memory" );return sum;
    }//"=r"(sum)表示输出从寄存器中放到变量sum中// "r"(a) 指定输入从变量a中获取放到通用寄存器//"memory"声明使用内存// 使用extern对函数进行声明
    extern int add_func(int a, int b, int c, int d);int sum = 0;int main()
    {// 调用内联汇编的函数 sum = add_func2(5,6,7,8);// 在c代码中调用汇编代码sum = add_func(1,2,3,4);while(1);return 0;
    }
    
  • 汇编文件
    .text 
    .global add_func  @ 将add_func函数声明为全局add_func:add r0, r0, r1add r0, r0, r2add r0, r0, r3mov pc, lr
    .end
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/168425.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

音乐免费下载mp3格式+音频格式转换+剪辑音频+合并音频教程

1.在qq音乐网页版搜索想要的歌曲 qq音乐网站:https://y.qq.com/ 如果你是vip可以直接下载vip的歌曲,如果不是选择不是vip的歌曲进行第一步的操作 2.点击播放进入页面后F12拿到音频地址 然后双击src里面的音频地址复制 网页新标签打开赋值的这个链接&a…

51单片机的篮球计分器液晶LCD1602显示( proteus仿真+程序+原理图+PCB+设计报告+讲解视频)

51单片机的篮球计分器液晶LCD1602显示 📑1.主要功能:📑讲解视频:📑2.仿真📑3. 程序代码📑4. 原理图📑5. PCB图📑6. 设计报告📑7. 设计资料内容清单&&…

自动驾驶学习笔记(七)——感知融合

#Apollo开发者# 学习课程的传送门如下,当您也准备学习自动驾驶时,可以和我一同前往: 《自动驾驶新人之旅》免费课程—> 传送门 《Apollo Beta宣讲和线下沙龙》免费报名—>传送门 文章目录 前言 感知融合 卡尔曼滤波 融合策略 实…

【操作系统】考研真题攻克与重点知识点剖析 - 第 2 篇:进程与线程

前言 本文基础知识部分来自于b站:分享笔记的好人儿的思维导图与王道考研课程,感谢大佬的开源精神,习题来自老师划的重点以及考研真题。此前我尝试了完全使用Python或是结合大语言模型对考研真题进行数据清洗与可视化分析,本人技术…

ARM 基础学习记录 / 异常与GIC介绍

GIC概念 念课本(以下内容都是针对"通用中断控制器(GIC)"而言,直接摘录的,有的地方可能不符人类的理解方式): 通用中断控制器(GIC)架构提供了严格的规范&…

MySQL时间类型注意事项

MySQL常见的时间类型有YEAR、DATE、TIME、DATETIME、TIMESTAMP,绝大多数业务都是精确到秒的,所以通常用后两种。并且MySQL5.6以后后两种支持精度到毫秒(最多小数点后6位) DATETIME占8字节,不论要不要毫秒 TIMESTAMP占4…

物业管理服务预约小程序的效果如何

物业所涵盖的场景比较多,如小区住宅、办公楼、医院、度假区等,而所涵盖的业务也非常广,而在实际管理中,无论对外还是对内也存在一定难题: 1、品牌展示难、内部管理难 物业需求度比较广,设置跨区域也可以&…

Python之函数-作用域和嵌套函数

Python之函数-作用域和嵌套函数 作用域 一个标识符的可见范围,这就是标识符的作用域。一般常说的是变量的作用域每一个函数都会开辟一个作用域 作用域分类 全局作用域 在整个程序运行环境中都可见全局作用域中的变量称为全局变量global 局部作用域 在函数、类等…

【Python 千题 —— 基础篇】成绩评级

题目描述 题目描述 期末考试结束,请根据同学的分数为该同学评级。 A:90 ~ 100B:80 ~ 89C:70 ~ 79D:60 ~ 69E:0 ~ 60 输入描述 输入同学的分数。 输出描述 输出该同学的等级。 示例 示例 ① 输入&…

张小泉的“老字号”快守不住了:限期整改,业绩和产品各有危机

撰稿|行星 来源|贝多财经 11月8日,商务部等5部门发布了中华老字号的复核结果。结果显示,全国有981家中华老字号企业通过了复核,73家中华老字号企业附条件通过复核,另有55家企业未能通过复核。 贝多财经发现,张小泉股…

Pycharm常用快捷键和替换正则表达式

原生快捷键的使用: 1.CtrlF:查找 2.CtrlZ:返回上一步 3.Alt 鼠标左键选择:多行同时编辑(上、下、左、右键能够移动光标) 按住Ctrl,左键点击,定位光标 编辑过程 URL常用的替换正则表达式&am…

Echarts多条折线图 y轴与实时值提示框数值不一致解决方法:

Echarts多条折线图 y轴与实时值提示框数值不一致解决方法: 删除stack属性即可 stack看官网就是数据堆叠 发现这个bug,特此记录