汇编与c之间内容传递的分析理解及ldr指令与.word伪指令的简单介绍

 

注:因涉及个人理解,可能有错 。如欲转载 ,请注明出处。

 

一、ldr指令的两种不同用途

(1)作为arm汇编指令集中的ldr加载指令

        ·指令格式: 

  LDR{条件}    Rd, <地址>
  LDR{条件}B   Rd, <地址>

说明:该指令用于把<地址>指向的数据加载到Rd寄存器里,LDR是加载指向的一个字内容,而后面对了个后缀'B'表示只加载指向的一个字节的内容.

 

(2)作为编译器自己定义的ldr伪指令

        ·伪指令格式:

    LDR Rd,=expr

说明:可以直接理解为把expr值赋给Rd  .比如:

ldr R0 , =0x30000040  ;就是把0x30000040赋给R0

 

关于这部分的易错点,其实可以看一下uboot的向量表前面的编写.

        _start: b       start_code /*直接跳转到start_code处,不保存当前地址*/
         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
       
        _undefined_instruction: .word undefined_instruction
        _software_interrupt: .word software_interrupt
        _prefetch_abort: .word prefetch_abort
        _data_abort:  .word data_abort
        _not_used:  .word not_used
        _irq:   .word irq
        _fiq:   .word fiq

 

       对汇编指令不是很熟的人刚开始看到这段代码可能会有一个疑问(当然也包括我),这里为什么还要多加个.word把函数指针irq以word类型放在当前地址处 , 然后用_irq来标志这个当前地址 . 为什么不直接把irq直接使用ldr pc, irq就好,这样一旦中断来了就直接跳到irq指向的函数去指向?

 

       上面的理解其实是错的,ldr pc,irq中的irq是函数指针,那么这个语句是把irq指向的内容(就是函数的代码)的前个word赋给pc,而不是把函数指针赋给pc,所以写成这样是无法正常进入中断服务程序的.

 

        如果想要直接用irq来赋给pc,那就需要使用ldr的伪指令来完成 , 如 :  ldr pc , =irq .

否则就要像上面那样,把irq以word形式通过.word放在当前地址里,给这个地址做标号,在跳转指令里就可以直接通过ldr pc , _irq把该地址标号的内容(irq函数指针)赋给pc .

 

(3)而在c语言里对这些值的引用却大大不同 , 比如在汇编文件里有下面几条语句(中间省略了与该讨论无关的代码)  ,其中//后面的是反汇编出来对应的代价:

.global _start                   

 .global wsnboot_start     

.global bss_start          

.global _bss_start

 

_start:                                      //3ff80000 <_start>:

       b ResetInit                        //3ff80000:       ea000008

       ...

wanboot_start                       //3ff80020 <wsnboot_start>:
        .word   _start                   //3ff80020:       3ff80000        svccc   0x00f80000


bss_start:                                //3ff80024 <bss_start>:
        .word _bss_start             //3ff80024:       3ff80540        svccc   0x00f80540  (其中3ff80540是_bss_start的值)

        ...

        ldr     r0 , =_start             //ldr     r0, [pc, #116]  ; 3ff800dc  //  3ff800e0:   3ff80540  (最终结果就是r0 = 3ff80540)

        ldr     r1 , = _bss_start    //ldr     r1, [pc, #116]  ; 3ff800e0  //  3ff800dc:       3ff80000

在c语言里用了这样两条语句 :

       i = _bss_start  - _start ;   (本意是希望得到_bss_start地址到_start地址的距离 , 也就是希望是3ff80540-3ff80000)

ldr     r3, [pc, #52]   ; 3ff80128       //3ff80128:       3ff80540    //可以看出这里是把3ff80540赋给r3

ldr     r0, [r3]                                                                             //把3ff80540地址里的数据赋给r0

ldr     r3, [pc, #44]   ; 3ff8012c       //3ff8012c:       3ff80000    //把3ff80000赋给r3

ldr     r2, [r3]                                                                            //把3ff80000里的内容赋给r2

rsb     r2, r2, r0                                                                       //[3ff80540]里的内容  - [3ff80000]里的内容

         从上面的情况已经可以看得出,这明显不是我们想要的 , 在汇编里我们完全可以通过ldr伪指令直接把变量值直接赋给寄存器 , 而在c语言里对汇编里的变量都是当作指针并直接取其指向的内容进行操作 .

        具体为什么要这样我也不太明白 ,    个人的理解是(如理解有错,请大家矫正)  :  在c语言里不管是常量也好 , 变量也好 , 指针变量也好 ,都是有对应的地址来存储它们 .

         而在汇编里 , 其实我们上面引用的不应该理解为变量 , 只是汇编里的一个地址标号而已, 该地址标号只是用于编译器暂时使用 ,不会占用我们真正的内存 .  既然不会占用我们的内存 , 那在c语言里就不能对地址标号当作变量引用 , 因为c语言是通过地址来引用变量常量的 . 所以以上那种做法是错误的 . 那是企图引用汇编里的不占地址空间的地址标号 .  所以如果在c语言里要使用该值 , 就必须为该值开辟一个空间来放置它 , 然后c语言里通过访问该地址来取出该数据 . 可能这就是为什么c语言里只能把引用汇编里的东西都当作地址来取出里面的值 , 实现了汇编与c里值的传递 .  

以下就是正确的实现:         

 j = bss_start - wsnboot_start; (本意也是希望得到_bss_start到_start地址的距离 , 也就是希望是3ff80540-3ff80000 )

ldr     r3, [pc, #40]   ; 3ff80130       //3ff80130:       3ff80024   //在汇编里我们已经先把地址标号3ff80540放在3ff80024 里

                                                                                               //所以这里是把3ff80024这个地址放在r3里.

ldr     r1, [r3]                                                                            //取出3ff80024里的值 , 就是地址标号3ff80540 .

ldr     r3, [pc, #32]   ; 3ff80134       //3ff80134:       3ff80020   //在汇编里我们已经先把地址标号3ff80000放在3ff80020 里

ldr     r3, [r3]                                                                            //取出3ff80020 里的值 , 就是地址标号3ff80000

rsb     r3, r3, r1                                                                        //把3ff80540  -3ff80000   ,与预期相符

 

 

 

 

 

 

 

 

 这个是GNU ASM的网站,里面有对伪指令的一些介绍,很多我都看不懂,关键在于自己去理解.

 http://tigcc.ticalc.org/doc/gnuasm.html#SEC49

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值