美文网首页程序员ARM硬件架构
ARM汇编之内存处理一网打尽

ARM汇编之内存处理一网打尽

作者: 赵国开 | 来源:发表于2018-07-24 20:07 被阅读9次

    RISC架构可以认为是加载/存储的架构,因为所有存储在外部数据都需要通过指令加载到处理器进行处理。

    加载/存储的指令很多,常用的如下:

    加载 存储 长度和类型
    LDR STR 32位的字(默认)
    LDRB STRB 8位的无符号字节
    LDRH STRH 16位无符号的半字
    LDRSB 8位的有符号字节
    LDRSH 16位的有符号半字
    LDM STM 多个字的处理

    • 加载/存储的单指令处理的格式如下:
      LDR|STR{<size>}{<cond>} <Rd>, <addressing_mode>
      LDR表示从内存中加载数据并写到通用寄存器,STR表示从通用寄存器读取数据并把它存储到内存中。其中size是可选的如上表中的B/SB/H/SH等,addressing_mode表示寻址模式就是前面的文章《ARM汇编之内存寻址模式》介绍的,总共3种。
      下面这种格式不加!是偏移寻址模式,加!是前变址寻址模式
      LDR|STR{<size>}{<cond>} <Rd>, [<Rn>, <offset>]{!}
      下面这种格式是后变址寻址模式
      LDR|STR{<size>}{<cond>} <Rd>, [<Rn>], <offset>
      例子:
    AREA load_store, CODE, READONLY
    
    ARM
    
    ENTRY
    
    start
    
    MOV R1,#0x40000000
    MOV R2,#2
    
    LDR R3,[R1,R2,LSL #2];偏移寻址模式
    ;LDR R3,[R1,R2,LSL#2]!;前变址寻址模式
    ;LDR R3,[R1],R2,LSL#2;后变址寻址模式
    
    STR R2,[R1]
    STR R2,[R1,#4]!
    MOV R2,#0XFF
    STR R2,[R1],#8
    
    stop
    
    MOV r0, #0x18 ; angel_SWIreason_ReportException
    LDR r1, =0x20026 ; ADP_Stopped_ApplicationExit
    SVC #0x123456 ; A32 semihosting (formerly SWI)
    
    END
    
    • 加载/存储多个寄存器的指令

    LDM的语法格式如下:
    LDM{addr_mode}{cond} Rn{!}, reglist{^}
    其中<addr_mode>告诉汇编指令寻址的模式有下面四种:
    IA,在每次传输完之后递增地址(默认模式,可省略) 。
    IB,在每次传输完之前递增地址 (仅A32支持)。
    DA,在每次传输完之后递减地址 (仅A32支持)。
    DB,在每次传输完之前递减地址。

    cond是可选的,表示条件码。
    Rn是基址寄存器。
    感叹号!是可选的,如果有表示最后的地址要写回Rn寄存器。
    reglist是用来加载数据的寄存器列表(包含一个或多个寄存器,多个的话可用逗号进行分隔,如果是多个连续的寄存器也可用一个范围表示,比如R0-R4),reglist需要用花括号括起来,对A32来说寄存器从R0到R15都可用,但T32只有部分寄存器可用。
    reglist之后的^是可选的,仅A32支持,他不能用在用户模式或者系统模式( User mode/System mode),他是为如下场景设计的:

    • 当reglist中包含PC寄存器时,不仅将数据加载到寄存器列表中指定的寄存器,同时也顺便把SPSR复制到CPSR,这发生在异常模式下中断处理程序还回时。
    • 否则数据将被传输进/出用户模式的寄存器,而不是当前模式的寄存器

    备注:因为有多个寄存器,那么怎么考虑寄存器列表里面的寄存器和内存之间的映射,拿存储来说,是最低序号的寄存器存储到最低内存地址中,最高序号的寄存器存储到最高的内存地址中,加载也是按这种方式进行映射的,这个是跟寄存器列表中寄存器的存放顺序没有关系的。

    STM的语法格式如下:
    STM{addr_mode}{cond} Rn{!}, reglist{^}

    STM和LDM所带选项基本一样,释义也基本一样,对LDM来说Rn存储的是内存中的基地址,只是STM要从该地址往内存中写数据,而LDM是从该地址从内存中读数据。reglist之后的^对STM来说,同样也不能在用户模式或者系统模式,只是出现该符号时数据将被传输进/出用户模式的寄存器,而不是当前模式的寄存器。

    例子1:

    AREA load_store, CODE, READONLY
    ARM
    
    ENTRY
    
    start
    
    MOV R9,#0x40000000
    ADD R10,R9,#32
    LDMIA R9, {R5, R0, R3};低地址的数据先加载到r0,在加载到r3,最后加载到r5,
                          ;加载顺序跟放置在列表中的位置无关,只跟寄存器的编号有关
    LDMDB R10!,{R0-R1};最终地址会被写回R10
    
    stop
    
    MOV r0, #0x18 ; angel_SWIreason_ReportException
    LDR r1, =0x20026 ; ADP_Stopped_ApplicationExit
    SVC #0x123456 ; A32 semihosting (formerly SWI)
    
    END
    

    例子2:

    AREA load_store, CODE, READONLY
    
    ARM
    
    ENTRY
    
    start
    
    MOV R9,#0x40000000
    ADD R10,R9,#32
    MOV R2,#2
    MOV R1,#1
    STMIA R9,{R2,R1};注意最低地址存储R1,在接着存储R2,存储顺序跟放置在列表中的位置无关
    
    STMIB R9,{R1,R2}
    STMDA R10,{R2,R1}
    STMDB R10!,{R1,R2}
    
    stop
    
    MOV r0, #0x18 ; angel_SWIreason_ReportException
    LDR r1, =0x20026 ; ADP_Stopped_ApplicationExit
    SVC #0x123456 ; A32 semihosting (formerly SWI)
    
    END
    
    • PUSH和POP

    主要是针对栈的操作,PUSH其实就是 STMDB sp!,POP其实就是 LDMIA sp!
    语法格式如下:
    PUSH{<cond>} <reglist>
    POP{<cond>} <reglist>
    其中reglist可以参照前面的LDM/STMD的说明

    例子:

    AREA push_pop, CODE, READONLY
    
    ARM
    
    ENTRY
    
    start
    
    mov sp,#0x40000010
    push {r1,r0}
    pop {r3,r4}
    
    stop
    
    MOV r0, #0x18 ; angel_SWIreason_ReportException
    LDR r1, =0x20026 ; ADP_Stopped_ApplicationExit
    SVC #0x123456 ; A32 semihosting (formerly SWI)
    
    END
    
    • 使用 LDM和STM来实现栈

    使用栈时需要注意两点:

    • 栈的增长方向,向上增长(ascending)或者向下增长(Descending)
    • 栈指针的位置,指向空的位置(empty,栈中下一个空的位置),或者满的位置(Full,最后一个栈成员的位置)

    因此就可以组合出四种栈:
    FD(Full Descending stack)
    FA(Full Ascending stack)
    ED(Empty Descending stack)
    EA(Empty Ascending stack)
    当然这四种操作和LDM/STM中的addr_mode是等价的可以互相替换的,编程时如果你觉得基于这种栈方式的操作更容易理解,可以用这种助记符来进行编写程序,汇编器会自动帮你把这些转换为基于addr_mode的合适指令。
    下面是一张等价图

    image.png

    如果你是基于FD的栈来进行开发,如果没用FD助记符,那么入栈是你需要使用STMDB,而出栈是需要使用LDMIA,如果使用FD的话入栈STMFD,出栈LDMFD就行了剩下的交个汇编器去解释,这种就很容易记忆了。下面是四种栈入栈/出栈的指令及等效的addr_mode模式下的原始指令


    image.png

    例子:

    AREA push_pop, CODE, READONLY
    
    ARM
    
    ENTRY
    
    start
    
    mov sp,#0x40000010
    stmfd sp!,{r0,r1};入栈
    ldmfd sp!,{r2,r3};出栈
    
    stop
    
    MOV r0, #0x18 ; angel_SWIreason_ReportException
    LDR r1, =0x20026 ; ADP_Stopped_ApplicationExit
    SVC #0x123456 ; A32 semihosting (formerly SWI)
    
    END
    

    参考文献

    【1】DUI0801I_armasm_user_guide

    相关文章

      网友评论

        本文标题:ARM汇编之内存处理一网打尽

        本文链接:https://www.haomeiwen.com/subject/reiymftx.html