ARM指令和异常向量表20220911
1、在ARM开发中,异常向量表(或者称为中断向量表)处在一个关键的位置,因为它控制了ARM芯片复位、异常、IIRQ中断/FIQ中断 等 时的跳转地址,也即是调到哪里去执行启动代码。一般来说,异常向量表的形式如下
Vector: ; All default exception handlers (except reset) are ; defined as weak symbol definitions. ; If a handler is defined by the application it will take precedence. LDR pc, =resetHandler ; Reset LDR pc, Undefined_Addr ; Undefined instructions LDR pc, SWI_Addr ; Software interrupt (SWI/SYS) LDR pc, Prefetch_Addr ; Prefetch abort LDR pc, Abort_Addr ; Data abort B . ; RESERVED LDR pc, IRQ_Addr ; IRQ LDR pc, FIQ_Addr ; FIQ</p><p>Undefined_Addr: DCD Undefined_Handler SWI_Addr: DCD SWI_Handler ;DCD是伪指令,分配4字节空间 Prefetch_Addr: DCD Prefetch_Handler Abort_Addr: DCD Abort_Handler FIQ_Addr: DCD FIQ_Handler IRQ_Addr DCD IRQ_Handler ———————————————— 版权声明:本文为CSDN博主「a747lulu747」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/a747lulu747/article/details/9699323
在编译链接时,需要将此异常向量表安排到地址0处【arm9 arm10以后新品也可以在其他固定的地址】,芯片复位后PC == 0,便执行 LDR pc, =resetHandler 这条跳转指令,转而执行启动代码。若发生其他异常或者中断时,PC被硬件强制赋值为 0x0000 00XX,也是取异常向量表中的跳转指令执行,转而执行对应的异常或者中断处理例程。
但仔细观察会发现,复位异常和其他的异常不同。复位异常使用伪指令 LDR pc, =resetHandler,而其他异常则使用LDR指令。为什么?
2、关于ARM伪指令
ARM伪指令不是真正的指令,伪指令在真正编译链接后,会被一条真实的ARM指令代替。
3、LDR指令和LDR伪指令的区别
1)LDR指令是将内存中的一个字加载到目标寄存器中,如
LDR R0,[R1] ;将R1指向内存的32bit加载到R0中
LDR R4,lable ;将地址label处的内存中的一个32bit的字加载到R4中,如果地址为label处的内容是0xFFFF 5555,则此指令执行后,R4 = 0xFFFF5555
如果是LDR RX, label的形式,按照ARM官方文档【DDI0406B_arm_architecture_reference_manual_errata_markup_10_0.pdf】的说法,LDR指令的内存操作地址是在PC的基础上加上指令中的立即数偏移得到,如果要访问的内存离当前PC太远,则LDR指令就无能为力了,因为LDR指令中只有12bit来存放偏移地址,太远或者不适合移位形成的偏移量不能用LDR指令。
2)LDR伪指令则是将一个数赋给寄存器。LDR伪指令是将一个寄存器等于某个值,这个值可以来源于指令中包含的立即数,也可以来源于一个内存位置存放的内容。当要赋予的值可以用ARM指令的立即数表示时,LDR伪指令用MOV指令代替,否则LDR伪指令用LDR指令代替。LDR伪指令的一般形式:
LDR R0,=0x01
LDR PC,=label
LDR R0,=0x01这条伪指令可以用MOV指令代替,因为要赋予的常数0x01可以包含在MOV指令的立即数中。而LDR PC, =label则不一定是用MOV指令来实现,因为label是一个地址常数,通过指令中12bit立即数移位的方式不一定能形成。若MOV指令不能实现,则用LDR指令来实现,实现的过程如下图
:即先使用PC+偏移量作为地址访问内存,将此地址的内存加载到目标寄存器中(PC寄存器)。当然,指定地址处的内存必须包含正确的值,编译器自动在这个地址中写入正确的label值。
所以,LDR伪指令可以实现将任何32bit的数装入目标寄存器,并且在伪指令LDR PC, =label中执行之后,PC的值就是label的值。
4、回到异常向量表
LDR PC, =resetHandler 是将标号resetHandler的值(编译器编译时分配的地址值)载入PC,发生跳转,即跳转到resetHandler处执行;
LDR PC, XXXX_Addr 是将XXX_Addr处的内容载入PC,发生跳转,必须访问XXX_Addr地址的内存将跳转值取出。
5、总结:
LDR PC, =label 是伪指令,可以实现任何地址的跳转,执行后,直接跳转到label处执行【4Kb范围内】;
LDR PC, label 是指令,执行后是将label地址处的内容(跳转地址)装入PC,所以LDR指令常常配合DCD使用。但也可以实现任何地址的跳转【参考后面的问答】。
问:为什么在中断向量表中不直接LDR PC ,"异常地址"。而是使用一个标号,然有再在后面使用 DCD定义这个标号?
答:因为LDR指令只能是跳到当前PC 4kB[2^12=4KB]范围内,而B指令能跳转到32MB范围,而现在这样在 LDR PC ,"xxxx"这条指令不远处用"xxxx" DCD 定义一个字,而这个字里面存放最终异常服务程序 的地址,这样可以实现4GB全范围跳转。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构