在线咨询
eetop公众号 创芯大讲堂 创芯人才网
切换到宽版

EETOP 创芯网论坛 (原名:电子顶级开发网)

手机号码,快捷登录

手机号码,快捷登录

找回密码

  登录   注册  

快捷导航
搜帖子
查看: 4507|回复: 0

[原创] SWI指令及半主机略解

[复制链接]
发表于 2010-8-13 20:43:35 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?注册

x
本帖最后由 zslhutu 于 2010-8-13 20:44 编辑

最近调试时遇到了点问题,就是SWi这个指令,略解了一二,希望跟大家分享一下,以下均为ARM指令,首先给出一个简单例子:

(0x00000008)  BAL   SWI_DO  ;在异常向量表0x00000008处


;----------------------------------
;BEGIN
;----------------------------------

SWI_DO
;---------------------------------------
;入栈
;--------------------------------------
    STMFD sp!, {r0 - r3, r12, lr}
    MRS r0, spsr                 ;为了防止是从SVC模式进来的,所以要保存SPSR
    STMFD sp!, {r0}


;-----------------------------------------
; 提取立即数
;-----------------------------------------   
    TST r0, #0x20
    LDR r0, [lr, #-4]            ;在指令“SWI #..”指令中提取立即数,立即数是软件
    BIC r0, r0, #0xff000000 ;提取的  

;---------------------------------------------
;进入功能代码,跳转
;--------------------------------------------
    CMP r0, #0x1000000
    LDRLS pc, [pc, r0, LSL #2]   ;跳到哪了?怎么跳的?在后边详解!
    B SWIOutOfRange   

;-----------------------------------------------
;SWI功能表   类似向量表
;------------------------------------------   
Switable
    DCD do_swi_0
    DCD do_swi_1
    nop
    nop
do_swi_0   
;----------------------------------------
    nop
   ;我要实现的功能代码,写在此处。。。。
   nop

;------------------------------------------   
;出栈返回
;-----------------------------------------
    LDMFD sp!, {r0}
    MSR spsr_cf, r0
    LDMFD sp!, {r0 - r3, r12, pc}^

do_swi_1
    B    do_swi_1    ;假设我这个子功能什么都不做
SWIOutOfRange
    B    SWIOutOfRange  
;---------------------------------------------------
;END!!!!!!!!!!!!1
;----------------------------------------------


上边相当于底层代码了,接下了就是我们再应用程序中调用它了:
int main(void)
{
     __asm   //C中调用汇编
    {
         SWI   0
    }
    return ;
}
这段程序中SWI语句的执行,ARM就会产生异常,从而PC指针跳到0x00000008这个地址(ARM公司做死的),然后执行BAL  SWI_DO。接下来就该执行我们写的SWI_DO后边的代码了。在那段代码我想重点解释两个语句;
  第一: 提取立即数时 LDR r0, [lr, #-4]   这个代码重点在于lr中是什么?由于SWI异常存的是PC-4,即“SWI 0”这个语句的下一条代码,因此 lr-#4就是"SWI 0"这条指令的地址!因此LDR r0, [lr, #-4]  就是将“SWI 0”指令的二进制编码格式放到了R0,因此只要看官一看SWi编码格式就知道怎么样把立即数提出来的了。
第二:进入功能代码,跳转时,LDRLS pc, [pc, r0, LSL #2]    这个语句困扰了我好几天,一直想不明白,后来终于破解了。在这里R0是#0,但我想以“R0 = 1#”为例(其他的数是一样的),首先假设LDRLS pc, [pc, r0, LSL #2] 这条指令在0x40000100这个地方,再假设do_swi_1标号地址为0x40000150,  那么“DCD do_swi_1”这个伪操作会让0x4000010C这个“字”地址存放值0x40000150。当R0=1时,首先在执行这条指令时PC值是多少呢?根据流水线预取指理论,我们知道在执行这条指令时,其实PC = 0x40000100 + 0x8 = 0x40000108; 因此这条指令被翻译为 pc = [0x40000108 + 0x4] = [0x4000010C] = 0x40000150;(注意0x4000010C可是加方括号的,你懂得~) 你说它接下来怎么做?自然是跳到0x40000150处执行代码。
    在第二个语句解释中,需要注意两点,第一左移两位是为了ARM指令的自对齐(伪操作DCD 和B SWIOutOfRange);第二就是DCD表和跳转指令中间隔了一个指令B SWIOutOfRange,因此在考虑PC预取时,刚好抵消了。所以如果中间的指令不是一条,而是多条或没有,则这条跳转指令就要修改了,所以这不是一般的跳转指令而是特殊的跳转指令。
    在ARM中存在这样的伪操作"__SWI()"用法如下:
    在代码开头声明  __SWI(0)  void my_function0(void);
                         __SWI(1)  void my_function0(void);
有了这个声明我可以不采用C中嵌入汇编,我可以这样
int main(void)
{
    my_function0();
}
只要这样写就可实现上边main里功能,这回__SWI()这个伪操作会用了吧!

   上边写了这么多,问个简单的问题,为什么要用SWI这个东东,如果我想执行do_swi_0(假设已被EXPORT),可以直接在main这样做
int main(void)
{
    do_swi_0();
}
这样一样会执行那段代码,不过是直接跳到这个标号执行,没有经过异常。问题就在这里了,就是因为SWI指令能够产生异常,变为系统模式,可以操作USER模式下不能访问的存储器和操作。
   说到SWi,大家很容易想到半主机模式,这个问题我没有彻底解决,也没有深究,仅是做了个实验,因为暂时时间比较紧。实验代码如下:
int main(void)
{
   char * data1;
   data1 = 0x61;

    /*注意要在头文件声明它***/
    _WriteC(3, data1);  //这个函数是printf函数执行时会调用的函数,
                             //而在retarget.c里有这样的声明
                            //__swi(0x123456) void _WriteC(unsigned op, char *c);


    __asm
    {
        SWI 0      //这个是刚刚咱们自己写的功能代码
    }
    return ;
}
      如果你在RVDS中运行这个代码,采用ICE来调试,如果不单不调试,就会在RVDS上打印“b” ,同时会执行咱们自己定义的0号功能。这说明什么?我没有定义SWI(0x123456)啊?在执行_WriteC时调用SWI,ARMulator会正确终止你的程序,执行SWI指令来通知仿真器,仿真器可以捕获到该SWI指令,仿真器在地址0x8处设置断点。根据SWI的number来判断这个SWI是不是SEMIHOSTING请求,如果是,再根据具体的semihosting number响应用户的semihosting请求,完成用户的semihosting请求后,返回到SWI的后面一条指令,继续执行,就当没有执行过SWI指令。由于0号功能仿真器不认,所以执行了咱们自己定义的功能代码。
     但是,如果你单步调试就会发现问题,它会一步一步的调用,最后会调用SWI 0x123456,这时仿真器不认帐了,它并不接手semihosting请求,而是跳到我们0x08地址执行指令,因为我们我没有做他的服务例程,所以执行服务例程跳转时,跳到了一个“未知”的地方。当然了,单步运行SWI 0,跟之前是一样的了。由于半主机的东西我没怎么看过,时间紧没办法,所以还期望有兴趣者自己探讨。
    SWI指令还有一个就是传参的问题,这个问题比较简单,跟函数调用传参原理类似,就是按规定放到特定的寄存器就OK了,一般是R0-R3,同时注意当传参是4个时,需要特别的定义。就不过多说了。
   望高手能够指出不对之处,及指点一下相关内容!!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

站长推荐 上一条 /1 下一条

X

小黑屋| 手机版| 关于我们| 联系我们| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2025-6-28 10:49 , Processed in 0.014623 second(s), 10 queries , Gzip On, MemCached On.

eetop公众号 创芯大讲堂 创芯人才网
快速回复 返回顶部 返回列表