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

news/2025/1/29 7:23:46/

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.ppmy.cn/news/1377641.html

相关文章

Unity性能优化篇(八) 导入的模型网格优化设置

模型导入Unity后&#xff0c;可以选中这个模型&#xff0c;在Inspector窗口设置它的属性。下面说的都是可自定义选择优化的地方 Model选择卡: 1.在Model选项卡&#xff0c;启用Mesh Compression可以压缩模型&#xff0c;压缩程度越高&#xff0c;模型精度越低&#xff0c;但是…

React改变数据【案例】

State传统方式 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>React Demo</title> <!--…

企业内部培训考试系统首页自定义版块说明

企业内部培训考试系统首页自定义版块说明&#xff0c;企业内部培训考试系统手机端首页设计太灵活。 1、整站主题色自定义&#xff0c;更换主题色后&#xff0c;重要的文字和按钮颜色都自动使用主题色渲染&#xff0c;相当于一键换皮肤。 2、首页背景图自定义&#xff0c;想换…

umi4 项目使用 keepalive 缓存页面(umi-plugin-keep-alive、react-activation)

umi4使用keepalive 配置文件config\config.ts export default defineConfig({plugins: [umi-plugin-keep-alive], });安装add umi-plugin-keep-alive yarn add umi-plugin-keep-alive页面 A import { KeepAlive, history, useAliveController } from umijs/max; const Page…

【杂记】IDEA和Eclipse如何查看GC日志

1.Eclipse查看GC日志 1.1 右击代码编辑区 -> Run As -> Run Configurations 1.2 点击Arguments栏 -> VM arguments:区域填写XX参数 -> Run 1.3 控制台输出GC详细日志 2.IDEA查看GC日志 2.1 鼠标右击代码编辑器空白区域&#xff0c;选择Edit 项目名.main()... 2.…

c++中string的模拟实现(超详细!!!)

1.string的成员变量、&#xff08;拷贝&#xff09;构造、析构函数 1.1.成员变量 private:char* _str;size_t _size; //string中有效字符个数size_t _capacity; //string中能存储有效字符个数的大小 1.2&#xff08;拷贝&#xff09;构造函数 //构造函数string(const char* …

retrofit2中,响应参数的int类型会被转为double小数点的解决办法。

背景 retrofit2版本&#xff1a;2.3.0 我使用retrofit2作为接口请求框架。 但是我发现在响应时&#xff0c;解析的响应内容总会有问题。 例如&#xff1a; {"msg":"","code":0.0,"data":"123"}其中的"code"应该…

2024 年中国高校大数据挑战赛赛题 C:用户对博物馆评论的情感分析完整思路以及源代码分享

博物馆是公共文化服务体系的重要组成部分。国家文物局发布&#xff0c; 2021 年我国新增备案博物馆 395 家&#xff0c;备案博物馆总数达 6183 家&#xff0c;排 名全球前列&#xff1b;5605 家博物馆实现免费开放&#xff0c;占比达 90%以上&#xff1b;全国 博物馆举办展览 3…