zoukankan      html  css  js  c++  java
  • arm B和BL指令浅析

    arm B和BL指令浅析

    B或BL指令引起处理器转移到“子程序名”处开始执行。
    两者的不同之处在于:
    (1)BL指令在转移到子程序执行之前,将其下一条指令的地址拷贝到R14(LR,链接寄存器)。
          由于BL指令保存了下条指令的地址,因此使用指令“MOV PC ,LR”即可实现子程序的返回。
    (2)B指令则无法实现子程序的返回,只能实现单纯的跳转。用户在编程的时候,可根据具体应用选用合适的子程序调用语句。

    AREA Init,CODE,READONLY
    ;该伪指令定义了一个代码段,段名为Init,属性只读
    ENTRY ;程序的入口点标识
    .
    .
    bl delay ;调用延迟
    .
    .
    mov pc,lr ;返回
    -----------------------------------------------------------------------------
    ARM汇编指令的一些总结
    ARM汇编指令很多,但是真正常用的不是很多,而且需要认真琢磨的又更少了。
    比较有用的是MOV B BL LDR STR
    还是通过具体汇编代码来学习吧。
        @ disable watch dog timer
        mov r1, #0x53000000 //立即数寻址方式
        mov r2, #0x0
        str r2, [r1]
    立即数寻址方式,立即数要求以“#”作前缀,对于十六进制的数,还要求在#后面加上0x或者&。
    STR是比较重要的指令了,跟它对应的是LDR。
    ARM指令集是加载/存储型的,也就是说它只处理在寄存器中的数据。那么对于系统存储器的访问就经常用到STR和LDR了。

    STR是把寄存器上的数据传输到指定地址的存储器上。它的格式我个人认为很特殊:
    STR(条件) 源寄存器,<存储器地址>
    比如 STR R0, [R1] ,意思是R0-> [R1],它把源寄存器写在前面,跟MOV、LDR都相反。

    LDR应该是非常常见了。LDR就是把数据从存储器传输到寄存器上。而且有个伪指令也是LDR,因此我有个百思不得其解的问题。
    看这段代码:
        mov r1, #GPIO_CTL_BASE
        add r1, r1, #oGPIO_F
        ldr r2,=0x55aa // 0x55aa是个立即数啊,前面加个=干什么?
    对于当中的ldr 那句,我就不明白了,如果你把=去掉,是不能通过编译的。
    我查了一些资料,个人感觉知道了原因:这个=应该表示LDR不是ARM指令,而是伪指令。
    作为伪指令的时候,LDR的格式如下:
    LDR 寄存器, =数字常量/Label
    它的作用是把一个32位的地址或者常量调入寄存器。

    那大家可能会问,“MOV r2,#0x55aa”也可以啊。应该是这样的。不过,LDR是伪指令啊,也就是说编译时编译器会处理它的。怎么处理的呢?
    规则如下:如果该数字常量在MOV指令范围内,汇编器会把这个指令作为MOV。
    如果不在MOV范围中,汇编器把该常量放在程序后面,用LDR来读取,PC和该常量的偏移量不能超过4KB。
    然后说一下跳转指令。ARM有两种跳转方式。
    (1) mov pc <跳转地址〉
        这种向程序计数器PC直接写跳转地址,能在4GB连续空间内任意跳转。
    (2) 通过 B BL BLX BX 可以完成在当前指令向前或者向后32MB的地址空间的跳转(为什么是32MB呢?

    寄存器是32位的,此时的值是24位有符号数,所以32MB)。
    B是最简单的跳转指令。要注意的是,跳转指令的实际值不是绝对地址,而是相对地址——是相对当前

    PC值的一个偏移量,它的值由汇编器计算得出。
    BL非常常用。它在跳转之前会在寄存器LR(R14)中保存PC的当前内容。BL的经典用法如下:
    bl NEXT ; 跳转到NEXT
    ……
    NEXT
    ……
    mov pc, lr ; 从子程序返回。
    ------------------------------------------------------------------------------

    ------------------------------------------------------------------------------
    ARM体系结构还支持16位的Thumb指令集。Thumb指令集是ARM指令集的子集,它
    保留了32位代码优势的同时还大大节省了存储空间。由于Thumb指令集的长度只有16位,所以它的指令
    比较多。它和ARM各有自己的应用场合。对于系统性能有较高要求,应使用32位存储系统和ARM指令集;
    对于系统成本和功耗有较高要求,应使用16位存储系统和ARM指令集。
    --------------------------------------------------------------------------------

    --------------------------------------------------------------------------------
    1.对ARM异常(Exceptions)的理解
    所有的系统引导程序前面中会有一段类似的代码,如下:
    .globl _start ;系统复位位置
    _start: b reset ;各个异常向量对应的跳转代码
    ldr pc, _undefined_instruction ;未定义的指令异常
    ldr pc, _software_interrupt ;软件中断异常
    ldr pc, _prefetch_abort ;内存操作异常
    ldr pc, _data_abort ;数据异常
    ldr pc, _not_used ;未使用
    ldr pc, _irq ;慢速中断异常
    ldr pc, _fiq ;快速中断异常

    从中我们可以看出,ARM支持7种异常。问题时发生了异常后ARM是如何响应的呢?
    第一个复位异常很好理解,它放在0x0的位置,一上电就执行它,而且我们的程序总是从复位异常处理程序开始执行的,因此复位异常处理程序不需要返回。
    那么怎么会执行到后面几个异常处理函数呢?
    看看书后,明白了ARM对异常的响应过程,于是就能够回答以前的这个疑问。

    当一个异常出现以后,ARM会自动执行以下几个步骤:
    (1)把下一条指令的地址放到连接寄存器LR(通常是R14),这样就能够在处理异常返回时从正确的位置继续执行。
    (2)将相应的CPSR(当前程序状态寄存器)复制到SPSR(备份的程序状态寄存器)中。从异常退出的时候,就可以由SPSR来恢复CPSR。
    (3) 根据异常类型,强制设置CPSR的运行模式位。
    (4)强制PC(程序计数器)从相关异常向量地址取出下一条指令执行,从而跳转到相应的异常处理程序中。
    至于这些异常类型各代表什么,我也没有深究。因为平常就关心reset了,也没有必要弄清楚。
    ARM规定了异常向量的地址:
    b reset ; 复位 0x0
    ldr pc, _undefined_instruction ;未定义的指令异常 0x4
    ldr pc, _software_interrupt ;软件中断异常 0x8
    ldr pc, _prefetch_abort ;预取指令 0xc
    ldr pc, _data_abort ;数据 0x10
    ldr pc, _not_used ;未使用 0x14
    ldr pc, _irq ;慢速中断异常 0x18
    ldr pc, _fiq ;快速中断异常 0x1c
    这样理解这段代码就非常简单了。
    碰到异常时,PC会被强制设置为对应的异常向量,从而跳转到相应的处理程序,然后再返回到主程序继续执行。
    这些引导程序的中断向量,是仅供引导程序自己使用的,一旦引导程序引导Linux内核完毕后,会使用自己的中断向量。
    这又有问题了。比如,ARM发生中断(irq)的时候,总是会跑到0x18上执行啊。那Linux内核又怎么能使用自己的中断向量呢?
    原因在于Linux内核采用页式存储管理。开通MMU的页面映射以后,CPU所发出的地址就是虚拟地址而不是物理地址。
    就Linux内核而言,虚拟地址0x18经过映射以后的物理地址就是0xc000 0018。所以Linux把中断向量放到0xc000 0018就可以了。
    MMU的两个主要作用:
    (1)安全性:规定访问权限
    (2) 提供地址空间:把不连续的空间转换成连续的。
    第2点是不是实现页式存储的意思?

    .globl _start ;系统复位位置
    _start: b reset ;各个异常向量对应的跳转代码
    ldr pc, _undefined_instruction ;未定义的指令异常

    ……

    _undefined_instruction :
    .word undefined_instruction
    -----------------------------------------------------------------------------------

    -----------------------------------------------------------------------------------
    也许有人会有疑问,同样是跳转指令,为什么第一句用的是 b reset;而后面的几个都是用ldr?
    为了理解这个问题,我们以未定义的指令异常为例。
    当发生了这个异常后,CPU总是跳转到0x4,这个地址是虚拟地址,它映射到哪个物理地址
    取决于具体的映射。
    ldr pc, _undefined_instruction
    相对寻址,跳转到标号_undefined_instruction,然而真正的跳转地址其实是_undefined_instruction
    的内容——undefined_instruction。那句.word的相当于:
    _undefined_instruction dw undefined_instruction (详见毕设笔记3)。
    这个地址undefined_instruction到底有多远就难说了,也许和标号_undefined_instruction在同一个
    页面,也许在很远的地方。
    不过除了reset,其他的异常是MMU开始工作之后才可能发生的,因此undefined_instruction 的地址也经过了MMU的映射。
    在刚加电的时候,CPU从0x0开始执行,MMU还没有开始工作,此时的虚拟地址和物理地址相同;
    另一方面,重启在MMU开始工作后也有可能发生,如果reset也用ldr就有问题了,因为这时候虚拟地址和物理地址完全不同。
    因此,之所以reset用b,就是因为reset在MMU建立前后都有可能发生,而其他的异常只有在MMU建立之
    后才会发生。用b reset,reset子程序与reset向量在同一页面,这样就不会有问题(b是相对跳转的)。
    如果二者相距太远,那么编译器会报错的

  • 相关阅读:
    Oracle函数如何把符串装换为小写的格式
    Oralce中的synonym同义词
    JS中getYear()的兼容问题
    How to do SSH Tunneling (Port Forwarding)
    所谓深度链接(Deep linking)
    upload size of asp.net
    发一个自动刷网站PV流量的小工具
    解决Visual Studio 2008 下,打开.dbml(LINQ) 文件时,提示"The operation could not be completed." 的问题。
    在资源管理器中使鼠标右键增加一个命令,运行cmd,同时使得当前路径为资源管理器当前的目录
    使用SQL语句获取Sql Server数据库的版本
  • 原文地址:https://www.cnblogs.com/liulipeng/p/3373620.html
Copyright © 2011-2022 走看看