MOV/MVN ADR和ADRL伪指令

1342阅读 0评论2010-03-30 CUHH
分类:嵌入式

读第五节和第六节花的时间较长,主要是看得有点不太懂,但自己多动手实践了下,还是感觉基本上懂了的。小结如下:
  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,这样可以看出二者的差别来:
     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
上一篇:C 宏定义的简单总结
下一篇:ARM体系架构的寄存器地址是如何定义的?