arm汇编指令之数据块传输(LDM,STM)详见

http://blog.chinaunix.net/uid-28458801-id-3791987.html


数据块传输指令用于加载(LDM或者存储(STM当前有效寄存器的任意子集。
它们支持所有可能的堆栈模式,维持空或者满的堆栈,此堆栈可以向上或者向下,在保存或者恢复内容,
移动主存储器的大数据块是非常有效的。

1,指令格式:

{cond}     Rn{!},   {^}
    *{cond}
         条件代码
    * 指令类型
    *Rn            基址寄存器,其不可以为R15
    *
        寄存器列表,寄存器范围包含在{}(比如{R0,R2-R7,R10}),其可以是R0~R15的任意组合
                           由于R15是pc,对其操作可能会造成程序跳转,R15在最后一个被传输。
                            序号低的寄存器对应于存储器的低地址,不考虑{...}中的次序
    *{!} 
           为可选后缀,
            若选用该后缀,表示请求回写(W=1),则当数据传送完毕之后,将最后的地址写入到基址寄存器(Rn中,
            否则,W=0。表示请求不写回,基址寄存器的内容不改变
    *{^}            为可选后缀,
            当指令为LDM且寄存器列表中包含R15,选用该后缀时表示:除了正常的数据传送外,还将SPSR复制到CPSR中。
            同时,该后缀还表示传入或传出的是用户模式下的寄存器,而不是当前模式下的寄存器。

2,指令类型:



  
    当LDM/STM没有被用于堆栈,而只是简单地表示地址前向增加,后向增加,前向减少,后向减少时,由IA,IB,DA,DB控制。
        IA    ---->   Increment    After    每次传送地址加4
        IB    ---->   Increment    Before    每次传送地址加4
        DA    ---->   Decrement    After    每次传送地址减4
        DB    ---->   Decrement    Before    每次传送地址减4

    堆栈请求格式
,FD,ED,FA,EA定义了前/后向索引和上/下位,F,E表示堆栈满或者空。
    A 和D 定义堆栈是递增还是递减,如果递增,STM将向上,LDM向下,如果递减,则相反。
        FA    ---->   Full    Ascending        满递增堆栈          
        FD    ---->   Full    Descending        满递减堆栈 
        EA    ---->   Empty    Ascending    空递增堆栈
        ED    ---->   Empty    Descending    空递减堆栈



        示例:


        两段代码的执行结果是一样的,但是使用堆栈指令的压栈和出栈操作编程很简单(只有前后一致即可),
        而使用数据块指令进行压栈和出栈操作则需要考虑空与满,加与减对应的问题。



3,指令详解:
    (1)IA
            STMIA
 R0!,{R1,R2, R3,R14}
                    先传后增,寄存器→RAM
                    效果图:

                               


            LDMIA R0!,{R1,R2, R3,R14}

                    先传后增, RAM →寄存器

                    效果图:




    (2)IB
            STMIB
 R0!,{R1,R2, R3,R14}
                    先增后传,寄存器→RAM
                    效果图:

                   

            LDMIB R0!,{R1,R2, R3,R14}
        先增后传, RAM →寄存器
        效果图:



    (3)DA
            STMDA 
R0!,{R1,R2, R3,R14}
                    先传后减, 寄存器→ RAM
                    效果图:

 

            LDMDA R0!,{R1,R2, R3,R14}
                    先传后减, RAM → 寄存器
                    效果图:



    (4)DB
            STMDB 
R0!,{R1,R2, R3,R14}
                    先减后传,寄存器→ RAM
                    效果图:


                     
            LDMDB R0!,{R1,R2, R3,R14}
                    先减后传, RAM → 寄存器
                    效果图:

    (5)FA
            STMFA 
SP!,{R0,R1,R2,R14}
                    满递增入栈,R13为基址地址
                    效果图:

            LDMFA SP!,{R0,R1,R2,R14}
                    满递增出栈,R13为基址地址
                    效果图:

    
(6)FD
            STMFD 
SP!,{R0,R1,R2,R14}
                    满递减入栈,R13为基址地址
                    效果图:

            LDMFD SP!,{R0,R1,R2,R14}
                    满递减出栈,R13为基址地址
                    效果图:

    (7)EA
            STMEA 
SP!,{R0,R1,R2,R14}
                    空递增入栈,R13为基址地址
                    效果图:

            LDMEA SP!,{R0,R1,R2,R14}
                    空递增出栈,R13为基址地址
                    效果图:

    (8)ED
            STMED 
SP!,{R0,R1,R2,R14}
                    空递减入栈,R13为基址地址
                    效果图:
            
            LDMED SP!,{R0,R1,R2,R14}
                    空递减出栈,R13为基址地址
                    效果图:


///

注:非常感谢博主“希望之光”,文章转自他的博客:http://blog.chinaunix.net/uid-20379123-id-1956584.html

ARM的六大类指令集---LDR、LDRB、LDRH、STR、STRB、STRH

ARM微处理器支持加载/存储指令用于在寄存器和存储器之间传送数据,加载指令用于将存储器中的数据传送到寄存器,存储指令则完成相反的操作。常用的加载存储指令如下:

—  LDR     字数据加载指令

—       LDRB    字节数据加载指令

—  LDRH    半字数据加载指令

—  STR     字数据存储指令

—       STRB    字节数据存储指令

—  STRH    半字数据存储指令

1、LDR指令

LDR指令的格式为:

LDR{条件} 目的寄存器,<存储器地址>

LDR指令用于从存储器中将一个32位的字数据传送到目的寄存器中。该指令通常用于从存储器中读取32位的字数据到通用寄存器,然后对数据进行处理。当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当作目的地址,从而可以实现程序流程的跳转。该指令在程序设计中比较常用,且寻址方式灵活多样,请读者认真掌握。

指令示例:

LDR   R0[R1]                  ;将存储器地址为R1的字数据读入寄存器R0

LDR   R0[R1R2]             ;将存储器地址为R1+R2的字数据读入寄存器R0

LDR   R0[R1,#8]             ;将存储器地址为R1+8的字数据读入寄存器R0

LDR   R0[R1R2]            ;将存储器地址为R1+R2的字数据读入寄存器R0,并将新地址R1R2写入R1

LDR   R0[R1,#8]           ;将存储器地址为R1+8的字数据读入寄存器R0,并将新地址R18写入R1

LDR   R0[R1]R2              ;将存储器地址为R1的字数据读入寄存器R0,并将新地址R1R2写入R1

LDR   R0[R1R2LSL2]   ;将存储器地址为R1R2×4的字数据读入寄存器R0,并将新地址R1R2×4写入R1

LDR   R0[R1]R2LSL2     ;将存储器地址为R1的字数据读入寄存器R0,并将新地址R1R2×4写入R1

2、LDRB指令

LDRB指令的格式为:

LDR{条件}B 目的寄存器,<存储器地址>

LDRB指令用于从存储器中将一个8位的字节数据传送到目的寄存器中,同时将寄存器的高24位清零。该指令通常用于从存储器中读取8位的字节数据到通用寄存器,然后对数据进行处理。当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当作目的地址,从而可以实现程序流程的跳转。

指令示例:

LDRB R0[R1]         ;将存储器地址为R1的字节数据读入寄存器R0,并将R0的高24位清零。

LDRB R0[R1,#8]    ;将存储器地址为R18的字节数据读入寄存器R0,并将R0的高24位清零。

3、LDRH指令

LDRH指令的格式为:

LDR{条件}H 目的寄存器,<存储器地址>

LDRH指令用于从存储器中将一个16位的半字数据传送到目的寄存器中,同时将寄存器的高16位清零。该指令通常用于从存储器中读取16位的半字数据到通用寄存器,然后对数据进行处理。当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当作目的地址,从而可以实现程序流程的跳转。

指令示例:

LDRH R0[R1]         ;将存储器地址为R1的半字数据读入寄存器R0,并将R0的高16位清零。

LDRH R0[R1,#8]    ;将存储器地址为R18的半字数据读入寄存器R0,并将R0的高16位清零。

LDRH R0[R1R2]    ;将存储器地址为R1R2的半字数据读入寄存器R0,并将R0的高16位清零。

4、LDM指令

L的含义仍然是LOAD,即是Load from memory into register

虽然貌似是LDR的升级,但是,千万要注意,这个指令运行的方向和LDR是不一样的,是从左到右运行的。该指令是将内存中堆栈内的数据,批量的赋值给寄存器,即是出栈操作;其中堆栈指针一般对应于SP,注意SP是寄存器R13,实际用到的却是R13中的内存地址,只是该指令没有写为[R13],同时,LDM指令中寄存器和内存地址的位置相对于前面两条指令改变了,下面的例子:

LDMFD     SP! ,   {R0, R1, R2}

实际上可以理解为:    LDMFD     [SP]!,    {R0, R1, R2}

意思为:把sp指向的3个连续地址段(应该是3*4=12字节(因为为r0,r1,r2都是32位))中的数据拷贝到r0,r1,r2这3个寄存器中去。


5、STR指令

STR指令的格式为:

STR{条件} 源寄存器,<存储器地址>

STR指令用于从源寄存器中将一个32位的字数据传送到存储器中。该指令在程序设计中比较常用,且寻址方式灵活多样,使用方式可参考指令LDR

指令示例:

STR   R0[R1],#8    ;将R0中的字数据写入以R1为地址的存储器中,并将新地址R18写入R1

STR   R0[R1,#8]    ;将R0中的字数据写入以R18为地址的存储器中。

6、STRB指令

STRB指令的格式为:

STR{条件}B 源寄存器,<存储器地址>

STRB指令用于从源寄存器中将一个8位的字节数据传送到存储器中。该字节数据为源寄存器中的低8位。

指令示例:

STRB R0[R1]         ;将寄存器R0中的字节数据写入以R1为地址的存储器中。

STRB R0[R1,#8]    ;将寄存器R0中的字节数据写入以R18为地址的存储器中。

7、STRH指令

STRH指令的格式为:

STR{条件}H 源寄存器,<存储器地址>

STRH指令用于从源寄存器中将一个16位的半字数据传送到存储器中。该半字数据为源寄存器中的低16位。

指令示例:

STRH R0[R1]         ;将寄存器R0中的半字数据写入以R1为地址的存储器中。

STRH R0[R1,#8]    ;将寄存器R0中的半字数据写入以R18为地址的存储器中。

8、STM指令

S的含义仍然是STORE,与LDM是配对使用的,其指令格式上也相似,即区别于STR,是将堆栈指针写在左边,而把寄存器组写在右边。

   STMFD      SP!,   {R0}

同样的,该指令也可理解为:  STMFD      [SP]!,   {R0}

意思是:把R0保存到堆栈(sp指向的地址)中。



///

http://blog.csdn.net/u011449588/article/details/44945411

关于多寄存器加载存储指令

1.LDMIA指令、LDMIB指令、LDMDB指令、LDMDA指令

(1)LDMIA指令,IA表示每次传送后地址加4

 

 

 

(2)LDMIB指令,每次传送前地址加四

 

(3)LDMDB指令,每次传送前地址减4,这里还要注意程序中先给R5,还是先给R8,这里明显是先给R8

 

(4)LDMDA指令,每次传送后地址减4,这里也是先给R8,不是先给R5!!

 

 

 

2.下面来看看STMIA指令、STMIB指令、STMDB指令、STMDA指令

 

(1)      STMIA指令, STMIA R0,{R1,R2,R3,R4}  ;将R1—R4的数据存储到R0指向的地址上,R0的值不更新,IA传送后地址加4,所以这里内存当中的地址是从0x8004开始变化的

 

(2)      STMIB指令, STMIB R0,{R1,R2,R3,R4}  ;将R1—R4的数据存储到R0指向的地址上,R0的值不更新,IB每次传送前地址加4,所以内存中的值是从0x8008开始变化的

 

 

 

(3)      STMDB指令, STMDB R0,{R1,R2,R3,R4}  ;将R1—R4的数据存储到R0指向的地址上,R0的值不更新,DB每次传送后地址减4,所以内存中的值是从0x8010开始递减变化的,注意这里是先把表达式中的R4先给地址0x8010


(4)      STMDA与上面STMDB指令类似,DA是每次传送后地址减4,我就不截图了。

 

 

下面看一下最TMD烦的的是堆栈寻址方式,依次讲解STMFD、STMED、STMFA、STMEA

1.      STM加载指令

(1)      STMFD意思是满堆栈递减指令,堆栈向下增长。如下图就是解释堆栈向下增长,向下增长,栈顶指针在内存中向低地址处移动,叫做向下增长,这里我为什么要先讲STMFD指令,怎么不讲LDMFD等指令呢,因为这里涉及到堆栈,向内存中写入寄存器中所存储的值,更能体现出进栈的动作,看完下面的例子,你会知道STM的后缀为什么是FD了。

 

 

关键代码:STMFDSP!,{R1-R3,R4},可以这么看,先把R4,R3,R2,R1依次压栈,至于为什么不是R1,R2,R3,R4依次压栈,因为做出的实验就不是这样的,所以R4准备进栈的时候,栈指针SP先减4,然后先把R4寄存器里面的值放到内存地址为0x803c里面,这里为什么SP要先减4呢,因为这里是满堆栈,所以要先把栈指针做出响应的变化以后,才能进行存储,至于什么是满堆栈和空堆栈,我这里就不解释了。程序效果看下图即可:

 

(2)      看了上面STMFD以后,现在看STMFA就很简单了:不过这里需要特别注意,这里并不是先操作R4寄存器的,而是操作R1寄存器,至于为什么,我也不知道,只是实验总结出来的,这种东西并没有什么规律可言,实验是怎么样就怎么样吧。同理的是,先把SP做相应的改变,也就是这里先SP+4=0x8044,然后把R1寄存器的值放到内存地址的0x8044处,然后R2,R3,R4依次放下去,最后改变SP的值,因为代码中多了一个感叹号STMFA SP!,{R1-R3,R4}

 

(3)      STMED是空递减堆栈,可以看出堆栈指针一开始指在0x8040处,所以先把R4的寄存器的值放到内存单元0x8040中,这里其实也是先操作R4寄存器,至于为什么,只能说和STMFD对应的。


(4)      STMEA空递增堆栈,这里我只贴图不说话,哈哈,看不懂,回家种田去

 

 

下面看看和STMFD指令相对应的LDMFD等指令,我依次讲解LDMFD、LDMFA、LDMED、LDMEA指令,至于我为什么按这种顺序讲,为了和上面的STMFD等指令联系在一起。

(1)      还记得我上面那些STM那些指令先将的是什么,对了,第一个讲的是STMFD指令,把内存中的数据批量放到寄存器中。FD为满递减堆栈。

这里值得注意的是,FD不是满堆栈递减吗,为什么程序执行完以后SP是增加的,在没有执行MOVR9, SP这条指令之前,FD确实代表是递减的意思,这里因为sp栈顶指针实际上是增加,至于为什么不写LDMFA,只不过这样LDMFD能和STMFD指令对应,看起来顺眼点吧,其实这里我要说明的是STMDB并不是和LDMDB对应,而是和LDMIA对应的,这里注意一下就行了,以后程序编多了,直接就记住了,不过你只要原理懂了,管它怎么写呢。

 

 

 

(2)      那自然地下面就讲LDMFA指令了:同理的嘛!!

 

(3)      LDMED指令:依葫芦画瓢

 

 

(4)      LDMEA指令:就不写了,吃饭去了!!!

 

最后注意的一点就是这里的SP指针是我假定的一个值,如果你以后写arm代码,调用C函数的时候,用到栈指针,系统会自动分配,就不存在sp是否非法的问题,什么是非法问题呢?先举个例子,看下图,这里对应的SP是0x8020,并且是满递减,把寄存器的值写到内存当中,我明明写的是STMFDSP!,{R1-R3,R4},把4个寄存器的内容写到内存中,可是最后就存了两个,因为你0x8018的地址处之前可能代码段的内容就存入在那里,所以你就不能改了,获取你这时候把这个不能写入的内存地址是可以读取到寄存器中的,我没试过,有兴趣可以试试。所以在用STM指令的时候要注意这点了!!




  • 8
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值