MOV MVN 范围问题

读第五节和第六节花的时间较长,主要是看得有点不太懂,但自己多动手实践了下,还是感觉基本上懂了的。小结如下:

   1. MOV/MVN

   可以直接装载一些特定范围的32位值到寄存器中,这些值包括:

   (1) 8位常量,即0--255

   (2) 8位常量右移偶数位

   (3) MVN可以处理(1)(2)中值的按位取反值

   如果MOV/MVN指令中给出的立即数常量不在上述范围内,则汇编器会报错。

   2. LDR Rd,=字面数值常量

   可以装载任何32位值到寄存器中,汇编器先试图用MOV/MVN指令来处理给出的字面数值常量;如果不能处理,再用文字池来处理,即把常量放在文字池中,然后从文字池中取得所需的常量

   3. ADR 指令计算给定的PC相关的表达式相对于PC的偏移量,然后试图生成一条指令来进行地址装载,可以处理的地址范围是255字节(非字对齐的地址)或者1020字节(字对齐的地址,255字)以内。如果给定的表达式表示的地址相对于PC的偏移量不在这个范围内,则汇编器会报错。

   4. ADRL 指令计算给定的PC相关的表达式相对于PC的偏移量,然后试图生成两条指令来进行地址装载,可以处理的地址范围是64K字节(非字对齐的地址,255*255)或者256K字节(字对齐的地址,64K*4)以内。如果给定的表达式表示的地址相对于PC的偏移量不在这个范围内,则汇编器会报错。

   5. LDR Rd,=相对于PC的表达式

   指令会计算表达式表示的地址,然后在文字池中放入这个地址值,从文字池中加载地址。注意:汇编后的代码总是从文字池加载地址,不会用相对于PC的偏移量来表示地址。这样执行时间会较长(需要访问文字池),所以应该尽量使用ADR或者ADRL,只有在不能使用ADR和ADRL时,才使用LDR。

   6. 关于调试

   发现文档中说的用armsd进行调试的方法,对本章的只有汇编代码的程序不管用,大概是armsd的版本问题。但这一章涉及的程序都可以用ADS 1.2版本中的AXD调试器进行调试,还是图形界面的,比较好用。如果要在调试器中看到源代码,需要的armcc或者armasm的命令行中加入-g参数,表示需要调试信息。

 

   5 装载常量到寄存器

   5.1 为什么装载常量是个问题?

   因为所有的ARM指令都是32位长的,而且不把指令流作为数据使用,所以单个指令是没有办法在不从内存加载数据的情况下,把任何32位立即数常量装载到寄存器中的。

   虽然数据装载可以把任何32位值装入寄存器中,但还有更直接,也更有效的方法来装载很多常用的常量。

  

   5.2 使用MOV/MVN直接装载

   MOV指令可以直接把8位常量(0--255)装载到寄存器中;MVN可以把这些值的按位取反值(0xFFFFFF00到0xFFFFFFFF)装载到寄存器中。

   把MOV和MVN与桶形移位器结合使用可以构造更多的常量。可以构造的常量都是8位值循环右移偶数位的结果。例如:

   0--255                               0--0xFF 不移位

   256,260,264,...,1016,1020            0x100--0x3FC之间步进4,循环右移30位

   1024,1040,1056,...4080               0x400--0xFF0之间步进16,循环右移28位

   等等,以及这些值的按位取反值。可以直接使用指令把这些常量装载到寄存器中:

   MOV r0,#0xFF           ;r0=255

   MOV r0,#0x1,30         ;r0=1020

   MOV r0,#0xFF,28        ;r0=4080

   然而,把常量转化成这种形式是不太容易的。汇编器会试图进行这种转化,如果转化不能进行,汇编器会报错。

   下面的例子展示了汇编器是怎样进行这种转化的。左边的列是用户输入的ARM指令,右边是汇编器试图进行的转换。

    MOV r0,#0                ========>    MOV r0,#0

    MOV r1,#0xFF000000       ========>    MOV r1,#0xFF000000

    MOV r2,#0xFFFFFFFF       ========>    MVN r2,#0

    MVN r0,#1                ========>    MVN r0,#1

    MOV r1,#0xFC000003       ========>    MOV r1,#0xFF,6

    MOV r2,#0x03FFFFFC       ========>    MVN r2,#0xFF,6

 

    5.3 使用LDR Rd,=字面数常量进行直接装载

    汇编器提供不同于MOV和MVN的,不需要数据处理操作的,可构造任何32位数值常量的方法,这就是LDR Rd,=指令。

    如果LDR Rd,=指令中给出的常量可以用MOV或者MVN构造,汇编器将使用MOV或者MVN,否则将生成一个带相对PC地址的LDR指令来从文字池中读取所需的常量。文字池是一块为常量分配的内存。通常,在每个END伪指令后面会有一个文字池。然而在较大的程序中,它却可能是不能访问到的(因为LDR指令中的偏移量是12位值,只能表示4KB的范围)。这时可以使用LTORG伪指令。

    当LDR Rd,=指令要访问文字池中的常量时,汇编器先检查在当前文字池是否可以访问到,池中是否有所需要的常量。如果是,则对已存在的常量进行编址,否则试图把常量放到下一个文字池中。如果下一个文字池不可访问(因为不存在或者到它的距离超过4KB),则汇编器会报错,这时应该在LDR Rd,=指令后较近的地方放置一个LTORG伪指令。

    请看下面的例子(作为注释列出的指令是汇编器产生的):

            AREA Loadcon2,CODE

            ENTRY

            BL  func1

            BL  func2

            SWI 0x11

      func1

            LDR  r0,=42                     ======>  MOV r0,#42

            LDR  r1,=0x55555555             ======>  LDR r1,[pc,#offset 到文字池1]

            LDR  r2,=0xFFFFFFFF             ======>  MVN r2,#0

            MOV  pc,lr

            LTORG                           ======> 文字池1包含字面常量0x55555555

       func2

            LDR  r3,=0x55555555             ======> LDR r3,[pc,#offset 到文字池1]

            ;LDR r4,=0x66666666 ;注释掉这一句会发生错误,因为文字池2不可访问(距离超过4KB)

            MOV  pc,lr

       LargeTable % 4200 ;从当前位置清除4200字节的内存,从而让END后面的文字池距离超过4KB

            END

        注意文字池必须放置在代码段外面,否则处理器会把它当作指令来执行。

 

      6 装载地址到寄存器

      装载某个地址到寄存器的操作是很常见的,例如装载代码段中的字符串常量或者跳转表的起始地址到寄存器中。然而由于ARM代码是可重定位的,以及可以直接装载到寄存器中的值是有限的,这时不能使用绝对地址。这时应该用相对于当前PC的偏移量来表示地址,可以直接用当前PC和合适的偏移量来表示,或者可以从文字池中装载。

      6.1 ADR和ADRL伪指令

      从效率方面考虑,不需要内存访问的地址装载是很重要的,为此汇编器提供了ADR和ADRL伪指令。ADR和ADRL接受一个PC相关的表达式(同一代码段中的标签)并计算到达指定地方的偏移量。

      ADR试图用与LDR Rd,=指令相同的机制来生成单个指令进行地址装载操作。如果指定地址不能在单个指令中构造,汇编器会报错。通常对于非字对齐的地址,偏移量范围是255字节以内;对于字对齐的地址,偏移量范围是1020字节(255字)。

      ADRL试图用两条数据处理指令进行地址装载操作。即使可以用单个指令完成操作,第二条冗余的指令也还是会生成。如果不能用两条指令完成操作,汇编器会报错,这时LDR Rd,=可能是最好的选择。通常对于非字对齐的地址,ADRL可以处理的偏移量范围是64K字节以内;对于字对齐的地址可以处理的范围是256K字节。

    请看下面的例子:

    AREA Loadcon3,CODE
    ENTRY

Start
    ADR r0,Start              ;=====> SUB r0,pc,#pc到Start的偏移量(8)
    ADR r1,DataArea           ;=====> ADD r1,pc,#pc到DataArea的偏移量(8)
    ;ADR r2,DataArea + 4300   ;=====> 偏移量不能用ADD的第二个操作数表示,会报错
    ADRL r3,DataArea + 4300   ;=====> ADD r2,pc,#pc offset1(4096)
                              ;       ADD r2,pc,#pc offset2(208)
    SWI  0x11

DataArea % 8000
    END

   分析:

   (1) ADR r0,Start    =====> SUB r0,pc,#8

       ADR r1,DataArea =====> ADD r1,pc,#8

   ARM采用流水线机制,当前指令地址等于PC-8

   (2) ADRL r3,DataArea + 4300

   偏移地址为4304,因为ADRL是要被替换成两天指令的,在执行第一条指令的时候,PC指向SWI 0x11指令,则DataArea相对于PC的偏移为+4,再加上4300,最终偏移地址为4304,可以表示成1024*4+208,所以ADRL分解成两条指令后的偏移地址分别为4096和208.

 

     6.2 LDR Rd,=相对于PC的表达式

     与数常量一样,LDR Rd,=也可以处理相对于PC的表达式,如标签。即使可以用ADD或者SUB来构造所需的地址,也还是会生成LDR指令来装载相对于PC的表达式。

    AREA Loadcon3,CODE
    ENTRY

Start
    LDR r0,=Start             ;=====> LDR r0,[文字池中表示Start地址的常量的地址]
    LDR r1,=DataArea          ;=====> LDR r1,[文字池中表示DataArea地址的常量的地址]
    LDR r3,=DataArea + 4300   ;=====> LDR r1,[文字池中表示DataArea地址的常量的地址]
    SWI  0x11
    LTORG
DataArea % 8000
    END

    分析:这段代码与6.1节中的不同之处在于用LDR替换ADR或者ADRL,这样可以看出二者的差别来:

  • ADR和ADRL用相对于PC的偏移量表达式来装载地址,处理得地址范围有限,但可以节省运行时间;
  • LDR总是在文字池中存储所需的地址值,然后从文字池中装载地址,需要进行内存访问,运行时间较长 

     6.3 装载地址到寄存器中的示例

     下面的程序含有函数strcpy,它把字符串从一个内存地址复制到另一个地址。传入函数的两个参数是:源字符串的地址和目标地址。表示字符串结束的空字符也会被复制。     
    AREA StrCopy,CODE
    ENTRY
main
    ADR r1,srcstr
    ADR r0,dststr
    BL  strcopy
    SWI 0x11

srcstr DCB "This is my first(source) string",0
dststr DCB "This is my second(destination) string",0
    ALIGN

strcopy
    LDRB r2,[r1],#1
    STRB r2,[r0],#1
    CMP  r2,#0
    BNE  strcopy
    MOV  pc,lr

    END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值