ARM-v7 程序计数器PC的相关指令与应用

1. 前言

        如图1所示,R14是连接寄存器(Link Register),在汇编指令中通常也写为LR,用于存储函数调用和异常等的返回信息,复位时,默认值为0xFFFFFFFF;

图1 Core register 

        R15是程序计数器(PC,Program Counter),复位后初始值为Vector Table(中断向量表)的首地址加上0x04(Reset向量),其bit[0的值为必须1,并会加载到EPSR(Execution Program Status Register)的T字段(Thumb state bit),表示处于Thumb状态。ARM-v7只支持在Thumb下执行指令,在T字段为0的情况下,执行任何指令都将导致错误或锁定。

        如下所示,向量表的首地址存放的是MSP的初始地址,偏移四字节后即是Reset_Handler,也就是说代码复位后是从Reset_Handler开始运行的。

       const pFunc __VECTOR_TABLE[256] __VECTOR_TABLE_ATTRIBUTE = {(pFunc)(&__INITIAL_SP),                   /*     Initial Stack Pointer */Reset_Handler,                            /*     Reset Handler */NMI_Handler,                              /* -14 NMI Handler */HardFault_Handler,                        /* -13 Hard Fault Handler */MemManage_Handler,                        /* -12 MPU Fault Handler */BusFault_Handler,                         /* -11 Bus Fault Handler */UsageFault_Handler,                       /* -10 Usage Fault Handler */0,                                        /*     Reserved */0,                                        /*     Reserved */0,                                        /*     Reserved */0,                                        /*     Reserved */SVC_Handler,                              /*  -5 SVCall Handler */DebugMon_Handler,                         /*  -4 Debug Monitor Handler */0,                                        /*     Reserved */PendSV_Handler,                           /*  -2 PendSV Handler */SysTick_Handler,                          /*  -1 SysTick Handler *//* Interrupts */Interrupt0_Handler,                       /*   0 Interrupt 0 */Interrupt1_Handler,                       /*   1 Interrupt 1 */Interrupt2_Handler,                       /*   2 Interrupt 2 */Interrupt3_Handler,                       /*   3 Interrupt 3 */Interrupt4_Handler,                       /*   4 Interrupt 4 */Interrupt5_Handler,                       /*   5 Interrupt 5 */Interrupt6_Handler,                       /*   6 Interrupt 6 */Interrupt7_Handler,                       /*   7 Interrupt 7 */Interrupt8_Handler,                       /*   8 Interrupt 8 */Interrupt9_Handler                        /*   9 Interrupt 9 *//* Interrupts 10 .. 255 are left out */
};

        对于PC来说,其相关的表达式或标签(label)指示着一条指令或数据的地址(目标位置),如果PC当前位置到目标位置的偏移量大的过分,编译器会报错。由于ARM-v7采用了指令流水线技术,所以读PC的返回值是当前指令地址+4,且返回值的LSB为0(Thumb指令至少半字对齐),例如:

0x1000: MOV R0, PC ; R0 = 0x1004

        具体来说:

        ①对于B、BL、CBNZ和CBZ指令,PC的值是当前指令地址加上4字节; 

        ②对于其他使用label的指令来说,PC的值是当前指令地址加上4字节,且指令执行后PC的值的bit[1]会被强制清零,以保证其值按字长(word)对齐。       

        此外,向PC中写数据,就会引起一次程序分支(不更新LR寄存器),但无论是直接写PC还是使用分支转移指令,都必须保证加载到PC的值的LSB为1,即bit[0]为1,用以表明是在Thumb状态下执行;

2.相关汇编指令

2.1 PUSH/POP

        PUSH和POP指令适用于寄存器的压栈和出栈,且必须是满减栈(full descending stack):

表1 PUSH/POP指令
PUSH <condition>  {reglist}reglist中不可包含PC(独一无二的PC不允许有影子的存在,说一不二)
POP <condition>  {reglist}reglist中不可同时包含PC和LR(既生瑜何生亮)

        其中:

        ①conditon为条件码,可选;

        ②reglist为非空寄存器列表,列表元素可以是寄存器或寄存器子列表(range,如"R0-R2"表示R0,R1,R2),如果包含多个寄存器或寄存器子列表,则以逗号分隔;

        ③reglist不可包含SP(医者不能自医啊);       

        ④当 reglist中存在PC时,则在POP指令完成时就会跳转到PC所对应的地址执行(该地址必须半字对齐);同时,PC对应出栈值的bit[0]会用来更新APSR的T字段(T-bit),且该bit的值必须为1,以指示Thumb状态;此外,如果该POP指令带有条件码,则必须是IT指令块的最后一条指令。

        通常来说,PUSH和POP会成对使用,且 在PUSH和POP的过程中,SP的值会按堆栈的使用规则自动调整。例如,如满减栈情况下,PUSH的同时SP自减,POP的同时SP递增;

        注意:在寄存器列表中,不管寄存器序列如何,汇编器都将把它们升序排序,优先 PUSH序号大的寄存器,优先 POP序号小的寄存器,例如:

PUSH {R0,R4-R7} ; Push R0,R4,R5,R6,R7 onto the stack
PUSH {R2,LR}    ; Push R2 and the link-register onto the stack
POP {R0,R6,PC}  ; Pop r0,r6 and PC from the stack, then branch to the new PC.

         这样就意味着,R0最后入栈,最先出栈,这应该也利于R0的频繁使用吧。

        值得一提的是,STMDB和LDMIA在以R13(SP)为目的寄存器时,可以达到与PUSH/POP相同的效果:

STMDB SP!, {R0-R3, LR} ;等效于 PUSH {R0-R3, LR}
LDMIA SP!, {R0-R3, PC} ;等效于 PUSH {R0-R3, PC}

 2.2 分支(branch)指令

表2 分支指令(branch instrctions)
指令跳转范围说明
B label-16MB ~ +16MB立即跳转(通过立即数或表达式)
B<cond> lable (IT指令块外)-1MB ~ +1MB立即跳转
B<cond> lable (IT指令块内)-16MB ~ +16MB立即跳转
BL{cond} label-16MB ~ +16MB立即跳转,同时将返回地址存储到LR
BX{cond} RmRm中的任意值通过寄存器间接跳转
BLX{cond} RmRm中的任意值通过寄存器间接跳转,同时将返回地址存储到LR

        其中: 

        ①由于PC的值为当前指令地址+4,着也就意味着向前跳转的范围多了4个字节;

        ②label是一个PC相关的表达式,表示要跳转到的地址;

        ③ BX 和 BLX中,Rm寄存器的值为跳转的目的地址,bit[0]指示跳转后CPU要进入的状态,且如前文所述,该值的bit[0]必须为1,生成地址时会忽略该bit(置0),如果BL和BLX指令中Rm的bit[0]不为1,则会产生一个用法错误异常(UsageFault exception);

        ④BL和BLX指令会将当前下一条指令的地址存储到LR中,以提供返回信息;

        ⑤B<cond> lable是唯一在IT指令块内外都可以使用的条件分支指令,对于其余的分支指令,在IT指令块内部必须是带条件的(IT指令块内部的指令都是条件指令),在IT指令块外则必须是无条件的;

        ⑥在IT指令块内部使用分支指令时,则该分支指令必须时IT指令块的最后一条指令;

        ⑦BLX指令中不可使用PC;

        ⑧使用 .W后缀可以拓展分支跳转范围;

3.通过PC控制程序执行

3.1通过 MOV指令

MOV PC, Rn ; branch to the address indicated in Rn

         当使用MOV指令将Rn中存储的值赋给PC时,该值的bit[0]将会被忽略,并跳转到Rn给出的地址中。此外,使用MOV指令对PC进行赋值时,MOV后不可使用S条件后缀,且Rn必须是一个没有移位的寄存器。虽然MOV指令可实现分支跳转,但BX或BLX指令更专业,移植性也更好。

3.2 通过分支指令       

B loopA     ; Branch to loopA
BLE ng      ; Conditionally branch to label ng
B.W target  ; Branch to target within 16MB range
BEQ target  ; Conditionally branch to target
BEQ.W target; Conditionally branch to target within 1MB
BL funC     ; Branch with link (Call) to function funC, return address stored in LR
BX LR       ; Return from function call
BXNE R0     ; Conditionally branch to address stored in R0
BLX R0      ; Branch with link and exchange (Call) to a address stored in R0.

3.3 通过LDR指令        

LDR PC, [Rn] ;转移地址存储在 Rn 所指向的存储器中

3.4 通过POP指令

        既然LR在子程序调用过程中的唯一用处就是存储返回地址,那就直接绕过LR,将返回地址传给PC,返回子程序调用处,例如:

push {r0-r3, lr}    ;子程序入口
pop  {r0-r3, pc}    ;子程序出口

4. PC跳转的应用

 4.1 程序加载后跳转到resetHandler

       /**************************************************************************************************Local Functions**************************************************************************************************/uint32 resetHandlerAddr;void Device_Deinit(void);status_t bootUpCurrentCore(uint32_t entryPoint){/* entryPoint为中断向量表(vectortable)的首地址, vectortable[1]的地址为resetHandlerAddr */resetHandlerAddr =*((uint32_t*)entryPoint+1);         Device_Deinit();S32_SysTick->CSRr = S32_SysTick_CSR_ENABLE(0u);__asm("ldr r0, =resetHandlerAddr");        __asm("ldr r1, [r0]");  /* r1 = *resetHandlerAddr; 即r1 = resetHandler */__asm("mov pc, r1");    /* pc = resetHandler, 即跳转到resetHandler函数 */return STATUS_SUCCESS;}

4.2 Reset_Handler函数完成系统初始化

/*----------------------------------------------------------------------------Reset Handler called on controller reset
*----------------------------------------------------------------------------*/
void __attribute__((naked,__noreturn__)) Reset_Handler(void)
{__EARLY_INIT();/* Stack pointer initialisation */__set_CONTROL(0);                       /* MSP with privilege mode*/__set_PSP(0);__set_BASEPRI(0);__set_MSP((uint32_t)&__INITIAL_SP);SystemInit();                             /* CMSIS System Initialization */__PROGRAM_START();                        /* Enter PreMain (C library entry point) */
}

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

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

相关文章

vscode 配置 c 语言 问题解决

1.VS code调试时显示Unable to start debugging.The value of miDebuggerPath is invalid。 解决方法: VS code调试时显示Unable to start debugging.The value of miDebuggerPath is invalid_unable to start debugging. the value of midebugger-CSDN博客 2.VSCode运行C终端…

分类算法(Classification algorithms)

逻辑回归(logical regression&#xff09;&#xff1a; 逻辑回归这个名字听上去好像应该是回归算法的&#xff0c;但其实这个名字只是在历史上取名有点区别&#xff0c;但实际上它是一个完全属于是分类算法的。 我们为什么要学习它呢&#xff1f;在用我们的线性回归时会遇到一…

前端框架的发展历史介绍

前端框架的发展历史是Web技术进步的一个重要方面。从最初的简单HTML页面到现在的复杂单页应用程序&#xff08;SPA&#xff09;&#xff0c;前端框架和库的发展极大地推动了Web应用程序的构建方式。以下是一些关键的前端框架和库&#xff0c;以及它们的发布年份、创建者和主要特…

java-ssm-jsp的问卷调查系统的设计与实现

java-ssm-jsp的问卷调查系统的设计与实现 获取源码——》公主号&#xff1a;计算机专业毕设大全

第十五章垃圾回收相关算法

第十五章垃圾回收相关算法 文章目录 第十五章垃圾回收相关算法1. 垃圾标记阶段的算法之引用计数算法1.1 垃圾标记阶段&#xff1a;对象存货判断1.2 方式一&#xff1a;引用计数算法循环引用 1.3 小结 2. 垃圾标记阶段的算法之可达性分析算法2.1 方式二&#xff1a;可达性分析&a…

Qt 定时器事件

文章目录 1 定时器事件1.1 界面布局1.2 关联信号槽1.3 重写timerEvent1.4 实现槽函数 启动定时器 2 定时器类 项目完整的源代码 QT中使用定时器&#xff0c;有两种方式&#xff1a; 定时器类&#xff1a;QTimer定时器事件&#xff1a;QEvent::Timer&#xff0c;对应的子类是QTi…

HttpURLConnection详解及使用

HttpURLConnection 请求响应流程 设置连接参数的方法 setAllowUserInteractionsetDoInputsetDoOutputsetIfModifiedSincesetUseCachessetDefaultAllowUserInteractionsetDefaultUseCaches 发送URL请求 建立实际连接之后&#xff0c;就是发送请求&#xff0c;把请求参数传到…

docker-swarm集群搭建

目录 一、docker swarm介绍 二、部署docker 三、搭建集群 3.1 工作模式 3.2 将当前主机作为leader 3.3 将第二个节点slave1加入到worker 3.4 将第三个节点slave2也加入到worker 3.5 将第四个节点(slave3)加入到manager 四、总结 一、docker swarm介绍 Docker Swarm…

java中{ }对变量可用scope的限制

改变变量定义位置可解决 y定义在{}内&#xff0c;属于局部变量&#xff0c;只能再在{}其中有效使用&#xff0c;因此第二个输出报错。 将int y6放在main()后&#xff0c;{}前可解决

浅析开源内存数据库Fastdb

介绍&#xff1a; Fastdb是免费开源内存数据库&#xff0c;其优秀的性能&#xff0c;和简洁的C代码&#xff0c;让我学习使用过程中收益颇多&#xff0c;但是国内中文相关研究的文章相当稀少&#xff0c;外文我查询相当不便。有兴趣的朋友可以通过以下网站访问&#xff1a;Mai…

《vtk9 book》 官方web版 第3章 - 计算机图形基础 (3 / 6)

3.8 演员几何 我们已经看到了光照属性如何控制演员的外观&#xff0c;以及相机如何结合变换矩阵将演员投影到图像平面上。剩下的是定义演员的几何形状&#xff0c;以及如何将其定位在世界坐标系中。 建模 计算机图形学研究中的一个重要主题是建模或表示物体的几何形状。…

一劳永逸的方法解决:LNK1168无法打开 xxx.exe 进行写入 报错问题

这种错误的产生原因&#xff1a; 运行程序退出不是按正常流退出&#xff0c;是按窗口右上角的 “X” 来关闭程序&#xff0c;但是后台的xxx.exe控制台程序还在运行&#xff1b;修改程序的代码后再运行&#xff0c;就会报LNK1168的错误&#xff1b; 报错示例&#xff1a; 解决方…

文本生成视频:从 Write-a-video到 Sora

2024年2月15日&#xff0c;OpenAI 推出了其最新的文本生成视频模型——Sora。Sora 能够根据用户的指令生成一分钟长度的高质量视频内容。这一创新的发布迅速在社会各界引发了广泛关注与深入讨论。本文将围绕本实验室发表于SIGGRAPH AISA 的 Write-a-video和 Sora 展开&#xff…

8核16G服务器多少钱?2024年腾讯云8核16G轻量服务器性能测评

腾讯云8核16G轻量服务器CPU性能如何&#xff1f;18M带宽支持多少人在线&#xff1f;轻量应用服务器具有100%CPU性能&#xff0c;18M带宽下载速度2304KB/秒&#xff0c;折合2.25M/s&#xff0c;系统盘为270GB SSD盘&#xff0c;月流量3500GB&#xff0c;折合每天116.6GB流量&…

真实案例分享:MOS管电源开关电路,遇到上电冲击电流超标

做硬件&#xff0c;堆经验。 分享一个案例&#xff1a;MOS管电源开关电路&#xff0c;遇到上电冲击电流超标&#xff0c;怎么解决的呢&#xff1f; 下面是正文部分。 —— 正文 —— 最近有一颗用了挺久的MOSFET发了停产通知&#xff0c;供应链部门找到我们研发部门&#xff0c…

Pycharm与Anaconda安装

网址&#xff1a; Pycharm&#xff1a;https://www.jetbrains.com/pycharm/ Anaconda&#xff1a;https://www.anaconda.com/download/ 官网下载速度太慢可以选择到清华源下载&#xff1a;https://repo.anaconda.com/archive/ 一&#xff1a;Anaconda安装 安装&#xff1a; …

Building Systems with the ChatGPT API

Building Systems with the ChatGPT API 本文是 https://www.deeplearning.ai/short-courses/building-systems-with-chatgpt/ 这门课程的学习笔记。 文章目录 Building Systems with the ChatGPT APIWhat you’ll learn in this course Language Models, the Chat Format and…

【C++ vector 类】

1. 标准库中的vector类 vector 类 的介绍&#xff1a; 注意&#xff1a; 1. vector是表示可变大小数组的序列容器。 2. 就像数组一样&#xff0c;vector 也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问&#xff0c;和数组一样高效。但是…

长度为n的数组a初始值全为0,目标是把数组a变为数组b(1<=bi<=n), 可以进行任意次操作:选择长度为k的数组c,(1<=ci<=n且两两不同)

对于1<i<k, 把 a[c[i]] 改为c[i % k 1]。给定n&#xff0c;k和数组b&#xff0c;判断能否得到数组b。 题目 思路&#xff1a; #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #d…

消息服务--Kafka的简介和使用

消息服务--Kafka的简介和使用 前言异步解耦削峰缓存1、消息队列2、kafka工作原理3、springBoot KafKa整合3.1 添加插件3.2 kafKa的自动配置类3.21 配置kafka地址3.22 如果需要发送对象配置kafka值的序列化器3.3 测试发送消息3.31 在发送测试消息的时候由于是开发环境中会遇到的…