ARM 汇编若干问题(一般中断问题与软中断SWI分析)

来源:互联网 发布:ping涉及到的网络协议 编辑:程序博客网 时间:2024/05/17 03:41

       一般中断问题分析

      ARM CPU 在上电启动之后会自动进入SVC模式,也是ARM上电后的默认工作模式,如果发生了中断,ARM会自动切换到外部中断模式(IRQ为例),如果是FIQ那么就切换到FIQ模式下面进行处理。

      在每一个模式下面都有一组可以访问的寄存器,SVC和IRQ模式下的R0~R12是共用的,这就涉及到寄存器Rx内容的保存:压栈,恢复:出栈操作。在IRQ处理中用到了哪些Rx就要进行相应的保护与恢复,否则当处理退出IRQ时就会出错误。

      发生中断时候,ARM是要首先完成当前正在执行的指令,然后再进行为IRQ的必要处理。具体的内容如下:

  • 将当前CPSR,保存到IRQ模式下的的SPSR_irq,进行备份。
  • 把PC-4所指向的地址放到LR。LR = PC-4。为中断返回是有个退出点。
  • 强制PC= 0X0000'0018,指向IRQ的中断向量的地址,通常地址0X0000'0018放置一条跳命令,跳转到IRQ的中断服务函数的入口地址。B   HANDLERIRQ.

充一点:ARM的正在执行的指令地址x,和取指令的地址PC,地址y,之间是差x=y-8.

 

                                    难点:中断服务函数退出地址的计算

    PC-8        --------------->|正在执行的指令的地址|0x120  如果此时发生中断

    PC-4        --------------->|真正的中断返回点地址|0x124

    PC          ---------------->|取指令的地址       |0x128      

通过上述分析可以得出:真正的中断返回点地址是0X124,真正应该存入LR的地址是0X124.但是实际情况是:0X128 ?????

分析:ARM 总是执行完当前的指令才会处理其他事情,问题就出在这里当ARM执行完当前的指令的时候PC已经更新了,如下

 PC = PC+4;PC = 0X128+4 = 0X12C,存放到LR=PC-4; LR = 0X12C-4=0X128;这个0X128不是真正的中断函数的返回点。真正的函数返回点是0X124.怎么办???? 

在给PC恢复数值是进行这样的处理下:LR = LR-4,PC=LR.   ==>SUBS PC,LR,#4.  并且自动恢复CPSR=SPSR_irq

 

SWI软中断问题分析

    软中断和一般的中断处理流程有不同的地方:CPU在执行指令时候,遇见一般的中断,先要执行完当前的指令,更新PC,然后把LR=PC-4.SPSR_irq=CPSR.CPSR=XXX.进入IRQ中断处理过程。真正的断点返回地址是:LR-4。在返回时需要进行地址的调整。 ==>SUBS PC,LR,#4.并且自动恢复CPSR=SPSR_irq。

   swi软中断还是很特别的玩意,软中断发生的时候,CPU切换到SVC工作模式,PC的数值不进行更新,即:不是等到把当前的指令执行完毕之后在执行其他的处理。而是立刻处理。CPU立即把软中断的返回地址写入LR中。LR=PC-4,这是真正的返回地址,PC的数据不更新。!!!!。SWI中断返回地址不需要调整就是正确的地址。

   然后就是软中断编号的计算,方法:发生软中断时那条指令地址里面数据的低24位放的就是软中断标号,从中取出来放到R0里面,R0是函数的第一个参数,也是存放函数返回结果的地方。

   HandlerSWI

    STMFD SP!,{R0-R3,R12,LR},LR存放的是swi真正的软中断要返回的中断地址点。

    LDR   R0,[LR,#-4];找到到底在哪一条指令时发生了软中断,很明显:就是LR真正断点返回地址的上面一条地址。LR-4或者PC-8。找个这个指令的地址,取出其中的内容。软中断的编号就是存放在发生软中断那条指令地址的低24位数据里面。

    BIC  R0,R0,#0xFF00'0000,低24位是软中断号

    BL  my_swi_handler;

    LDMFD SP! ,{R0-R3,R12,PC}^,其中^表示CPSR=SPSR_svc.只能手动恢复。

使用方法

 extern void my_swi_handler(unsigned int num);

      __swi(0x1)led_one(void);

     __swi(0x2)led_two(void);

 

main()

{

      led_one();

      delay(100ms);

      led_two();

}

void my_swi_handler(unsigned int num)

{

      case 0x1:{ do_some_thing}break;

      case 0x2:{do_another_thing}break;

      default:    break;

}


原创粉丝点击