首页 > 编程 > ASM > 正文

ARM:单和多寄存器加载存储、状态寄存器、协处理、软中断、乘法、交换等汇编指令

2019-11-08 02:13:14
字体:
来源:转载
供稿:网友
ARM汇编指令大多数都可以条件执行。条件存储在NZCV位。1. 分支跳转指令    b{l}{cond} <target>   // 相对跳转指令,跳转范围±32M2. 数据处理指令    '移位操作    LSL / LSR/ ASR/ ROR / RRX    '数据传输指令    MOV{cond} {s} <Rd>, <Operand>        cond, 可以条件执行        s, 操作结果可以影响N/Z/C        Rd,目标寄存器        operand,三种状态:立即数、寄存器、寄存器移位后的值。    '算术运算指令    add/adc{cond} {s} <Rd>, <Rm>, <operand>        s,影响NZCV位的取值        adc r0, r1, r2   @ r0=r1+r2+C    sub/sbc/rsb{cond} {s} <Rd>, <Rm>, <operand>        s,not,最高位没有借位 C=1        sbc r0, r1, r2   @ r0=r1-r2-not(C)        rsb r0, r0, #0   @ r0=0-r0    '位操作指令    and/orr/eor/bic{cond} {s} <Rd>, <Rm>, <operand>        mov r0, #1        eor r0, r0, r1, lsl #10   @ 将r0寄存器的bit10位取反        bic r0, r0, r1, lsl #10    @ 将r0寄存器的bit10位清0     '比较测试指令     1)不用加s,默认影响cpsr NZCV位     2)没有目标寄存器     cmp{cond} <Rm>, <operand>            cmp r0, r1  @ alu_out=r0-r1     tst{cond} <Rm>, <operand>            tst r0, #0x80   @ 测试r0的bit7位是否为0     teq{cond} <Rm>, <operand>            teq r0, #0x80   @ 测试两个数据是否相等                                   @ alu_out=r0^0x803、加载存储指令arm中所有的运算都必须在通用寄存器(r0-r15)中完成。    加载:从RAM取数据到通用寄存器;    存储:将通用寄存器中的数据保存到RAM;3.1 单寄存器加载存储指令    【加载】    mov r0, #0x40000000   ldr r1, [r0]     @// 将0x40000000开始的4字节数据加载到r1   ldrb r1, [r0]   @// 将0x40000000开始的1字节数据加载到r1,高3字节存储0    ldrsb r1, [r0]  @// 将0x40000000开始的1字节数据加载到r1,高3字节存储符号位    ldrh r1, [r0]    @// 将0x40000000开始的2字节数据加载到r1,高2字节存储0    ldrsh r1, [r0]  @// 将0x40000000开始的2字节数据加载到r1,高2字节存储符号位    ldrgtsh  LDRGTSH  '单寄存器加载存储指令10种情况'    ldr r1, [r0]    @//[r0]--->r1    --------r0:基址寄存器-------------------------------    ldr r1, [r0, #0x08]     @//[r0+0x08]--->r1    ldr r1, [r0, r2]           @//[r0+r2]--->r1    ldr r1, [r0, r2, lsl #2]  @//[r0+r2*4]--->r1    --------!:更新基址-----------------------------------    ldr r1, [r0, #0x08]!     @//[r0+0x08]--->r1    r0=r0+0x08    ldr r1, [r0, r2]!           @//[r0+r2]--->r1        r0=r0+r2    ldr r1, [r0, r2, lsl #2]!  @//[r0+r2*4]--->r1    r0=r0+r2*4    -------------------------------------------------------    ldr r1, [r0], #0x08     @//[r0]--->r1    r0=r0+0x08    ldr r1, [r0], r2           @//[r0]--->r1    r0=r0+r2    ldr r1, [r0], r2, lsl #2  @//[r0]--->r1    r0=r0+r2*4    【存储】    mov r0, #0x40000000   strr1, [r0]    @//r1--->[r0]    strb r1, [r0]   @//将r1最低的1个字节存储到0x40000000开始的内存中    strh r1, [r0]   @//将r1最低的2个字节存储到0x40000000开始的2字节内存空间中    'STR{cond} {b/h} <Rn>, <address_mode>    str r1, [r0]    -------------------------------------------------------    str r1, [r0, #0x08]       @//r1--->[r0+0x08]    str r1, [r0, r2]             @//r1--->[r0+r2]    str r1, [r0, r2, lsl #2]    @//r1--->[r0+r2*4]    -------------------------------------------------------    str r1, [r0, #0x08]!       @//r1--->[r0+0x08]    str r1, [r0, r2]!             @//r1--->[r0+r2]    str r1, [r0, r2, lsl #2]!    @//r1--->[r0+r2*4]    -------------------------------------------------------    str r1, [r0], #0x08       @//r1--->[r0]    r0=r0+0x08    str r1, [r0], r2             @//r1--->[r0]    r0=r0+r2    str r1, [r0], r2, lsl #2    @//r1--->[r0]    r0=r0+r2*4练习:    实现memcpy的汇编代码。    将0x8e00开始的16个字节拷贝到0x8f00开始的内存空间中去。
/** 代码演示  - memcpy.s **/.text.global _start.global memcpy.code 32_start:@ 将0x00008e00开始的16个字初始化成1-16mov  r0, #0x00008e00mov  r1, #1mov  r2, #16set_loop:str  r1, [r0], #4add  r1, r1, #1subs r2, r2, #1bne  set_loop@开始复制数据memcpy:mov  r0, #0x00008e00mov  r1, #0x00008f00mov  r2, #16cpy_loop:ldr  r3, [r0], #4str  r3, [r1], #4subs r2, r2, #1bne  cpy_loopmemcpy_ok:b    .@在数据段分配1K的空间.data                       .space 1024                .end/*-----------------------代码演示结束------------------------*/【注意】如果要仿真调试程序    1).data         .space 1024     2)指定数据段的地址        arm-...-as memcpy.s -o memcpy.o        arm-...-ld -T.data=0x8e00 memcpy.o -o memcpy    3)仿真调试        qemu-arm -g 1234 memcpy        另开窗口        arm-...-gdb memcpy        (gdb) target remote localhostip:1234        (gdb) b 10        (gdb) c        (gdb) x/16uw 0x8e00        (gdb) b 30    4)查看内存的方式        x/nfu address        n,查看内存单元个数        f,format显示内存单元的格式,x-16进制,d-10进制        u,unit单元大小,b-字节,h-半字,w-字3.2 多寄存器加载存储指令    【加载】    'LDM{cond} <address_mode> <Rb>{!}, <Reglist>{^}        !,更新地址        reglist,r0,r1,r2,r3,r4,r5(r0-r5)        address_mode,            ia:inc after // i增大,r0/r1/r5/r6地址分别为0x...0/4/8/C            ib:inc before  // i增大,r0/r1/r5/r6地址分别为0x...4/8/C/0            da:dec after // d减小,r6/r5/r1/r0地址分别为0x...0/C/8/4            db:dec before // d减小,r6/r5/r1/r0地址分别为0x...C/8/4/0        Rb,基址寄存器        ^,两种作用,特权模式下使用用户模式下的寄存器。        // 编号小的基地址存在低地址上。            mov r10, #0x40000000            ldmxx r10, {r0, r1, r5, r6}  @// xx 为地址模式    【存储】    'STM{cond} <address_mode> <Rb>{!}, <Reglist>{^}    (同上)练习二:    使用多寄存器加载存储指令,实现memcpy,把0x8e00开始的16个字初始化。
        mov r0, #0x8e00        mov r1, #0x8f00        mov r2, #4        loop:            ldmia r0!, {r3, r4, r5, r6}            stmia r1!, {r3, r4, r5, r6}            sub r2, r2, #1            cmp r2, #0            bne loop        memcpy_ok:            b .3.3 栈操作指令    【加载】    'LDMxx  sp!, {reglist r5-r9}    【存储】    'STMxx  sp!, {reglist r5-r9}    xx,取值:        FD==ia,full descend<下降>        FA==ib,full ascend<上升>        ED==da,empty descend        EA==db,empty ascend    push {fp}  <==> stmfd sp!, {r11}   // fp是r11的别名    pop {fp, pc}  <==>  ldm sp!, {fp, pc}    栈 - C语言中的作用:        1)存储局部变量        2)缓存LR4、状态寄存器访问指令    'MRS(mov reg cpsr)        msr r0, cpsr    @//r0=cpsr    'MSR(mov cpsr reg)        msr cpsr_c, r0      @//cpsr[ 7 : 0 ]=r0[ 7 : 0 ]        msr cpsr_fsxc, r0  @//cpsr=r05、协处理器指令    在ARM中最多可以有16个协处理器,命名为p0 p1 p2...p15    // p15协处理器比较有名,其中也有寄存器c0 c1 c2    // p15实现大小端的设置;    // DDI0500D_cortex_a53_r0p2_trm.pdf  P113. 搜索big-endian [25]     'MCR(mov cooperation reg)    'MRC(mov reg cooperation)6、软中断指令    'SWI/SVC  n(n为立即数)    该指令会导致软中断异常产生,该异常的产生硬件会做4件事,ARM核会切换为SVC模式。7、乘法指令    'MUL

8、交换指令    'SWP

----------------------------------------------------------------------------------------------------

一、ARM指令格式【ARM采用的是32位架构】    Byte:字节,8bits    HalfWord:半字,16bits    Word:字,32bits    // 大部分ARM core提供:ARM-32bit指令集,Thumb-16bit指令集。【ARM指令的特点】    1)没每条指令的多功能;    2)指令都带有条件;    3)灵活的第2操作数。【ARM指令的基本格式】        "<opcode>{<cond>} {s} <Rd> , <Rn>{, <operand2>}        cond:条件码。        如果不指定cond,则指令无条件执行。        <>:必须项。        { }:可选项。        opcode:指令助记符。        cond:执行条件。        S:是否影响CPSR状态寄存器的值。        Rd:目标寄存器。        Rn:第1个操作数的寄存器。        // 例如: SUBNES R2, R1, #0x20    @减法运算,条件NE,结果影响CPSR        operand2:第2操作数,能够提高代码效率。        有如下形式:                #immed_8r       常数表达式(立即数)                Rm                   寄存器的数值                Rm,shift            寄存器移位后的数值        /* 常数表达式 - 立即数:合法性 */        一个8位的常数通过循环右移偶数位得到,即为合法常量。        // 例如:合法常数:0xFF, 0x104, 0xFF0, 0xFF000, 0xFF000000, 0xF000000F        // 例如:非法常数:0x101, 0x102, 0xFF1, 0xFF04, 0xFF003, 0xFFFFFFFF        Rm:寄存器的数值        Rm,shift:寄存器移位后的值            LSL #n:逻辑左移n位            LSR #n:逻辑右移n位            ASL #n:算术左移n位            ASR #n:算术右移n位            ROR #n:循环右移n位            RRX:带扩展的循环右移1位            // n位为2的n次方。二、ARM数据操作指令【数据处理指令】    1)数据传送指令    2)算术运算指令    3)逻辑运算指令    4)比较指令    5)测试指令    6)乘法指令《opcode操作码功能表》AND/EOR/SUB/RSB/ADD/ADC/SBC/RSC/TST/TEQ/CMP/CMN/ORR/MOV/BIC/MVN【数据传送指令】 mov/mvn    ' mov{cond}{s}  Rd, operand2    将8位立即数或寄存器传送到目标寄存器Rd中。    // 用于移位运算等操作。    ' mvn{cond}{s}  Rd, operand2    将8位立即数或寄存器按位取反后传送到目标寄存器Rd中。    // 取反功能,可使寄存器装载范围更广的立即数。【算术运算指令】add/sub/rsb/adc/sbc/rsc    add:加法    sub:减法    rsb:逆向减法    adc:带进位加法    sbc:带进位减法    rsc:带进位逆向减法    ' add{cond}{s}  Rd, Rn, operand2    将operand2的值与Rn的值相加,结果保存到Rd寄存器。    // 其他算术运算指令等同于add。【逻辑运算指令】and/orr/eor/bic    and:逻辑与    orr:逻辑或    eor:逻辑异或    bic:位清除(按位清0,其他位不变)    ' and{cond}{s}  Rd, Rn, operand2    将operand2的值与寄存器Rn的值按位作逻辑"与&"操作,结果保存到Rd中。    // 其他逻辑运算指令等同于and。---> 嵌入式控制寄存器的移位操作常用。【比较指令】cmp/cmn/tst/teq - 用的比较多!    cmp:比较(本质:相减,影响cpsr标志位)    cmn:负数比较(本质:相加,影响cpsr标志位)    tst:位测试(本质:逻辑与,影响cpsr标志位)    teq:相等测试(本质:逻辑异或,影响cpsr标志位)    ' cmp{cond}  Rn, operand2    将寄存器Rn的值减去operand2的值,根据操作结果更新CPSR中的响应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。    // 【注意】比较指令的本质是做算术运算,但唯一区别是并不保存运算结果。    // 【错例】cmp  r2, r15, asr  r0   @r15不允许与被控制移位的寄存器一起出现,在有移位操作的情况下,不能将r15用于任何操作数。【乘法指令】mul/mla/umull/umlal/smull/smlal    mul:32bit乘法    mla:32bit乘加    ' mul{cond}{s}  Rd, Rm, Rs    将Rm和Rs中的值相乘,结果的低32位保存到Rd中。    ' mla{cond}{s}  Rd, Rm, Rs, Rn    将Rm和Rs中的值相乘,再将结果加上第3个操作数Rn,结果的低32位保存到Rd中。    // 【注意】r15不能用作rd, rm, rs, rn,且rd不能与rm相同。    umull/umlal/smull/smlal  均为64位指令。    umull:64bit无符号乘法    umlal:64bit无符号乘加    smull:64bit有符号乘法    smlal:64bit有符号乘加【分支指令】b/bl/bx    b:分支指令branch    bl:带链接的分支指令branch with link    bx:带状态切换的分支指令branch and exchange(bx{cond}  Rm)    ' b{cond}  label    跳转范围限制±32M字节地址内(ARM为字对齐,最低2位地址固定为0)    // 【注意】bl指令需要与 mov pc, lr 一起使用,实现返回指定地址。【软中断指令】swi    swi:软中断    ' swi{cond}  immed_24    实现从用户模式变换到管理模式。三、ARM指令的寻址方式    ARM处理器具有 7 种基本的寻址方式:    1)立即寻址;        // 立即寻址就是立即数寻址,操作数直接通过指令给出。    2)寄存器寻址;        // 指令中的地址码是寄存器编号,执行时直接取寄存器的值来操作。    3)寄存器间接寻址;        // 通过load/store完成对数据的传送操作。利用一个寄存器的值作为存储器地址,在指定的寄存器中存放有效地址,而操作数则放在存储单元中。    4)基址寻址;        // 将基址寄存器的内容与指令中给出的偏移量相加,形成操作数的有效地址。    5)相对寻址;        // 由程序计数器PC提供基准地址,指令中的地址码字段作为偏移量,两者相加后得到的地址为操作数的有效地址。    6)堆栈寻址;        // 后进先出。使用一个专门的寄存器(堆栈指针)指向一块存储区域(堆栈),指针所指向的存储单元即是堆栈的栈顶。向上-递增堆栈,向下-递减堆栈。    7)块拷贝寻址;        // 将寄存器内容复制到寄存器的地址所指示的存储器中,需要注意的是在存储第一个值之后存储器地址是增加还是减少。        ia - 传送后,地址+4        ib - 传送钱,地址=4        da - 传送后,地址-4        db - 传送后,地址-4四、存储器访问指令    ARM处理器是load/store型的,即它对数据的操作是通过将数据从存储器加载到片内寄存器中进行处理,处理完成后的结果经过寄存器存回到存储器中,以加快对片外存储器进行数据处理的执行速度。【单寄存器加载】ldr    ldr:加载字数据    ldrb:加载无符号字节数据    ldrt:以用户模式加载字数据    ldrbt:以用户模式加载无符号字节数据    ldrh:加载无符号半字数据    ldrsb:加载有符号字节数据    ldrsh:加载有符号半字数据【单寄存器存储】str    str:存储字数据    strb:存储字节数据    strt:以用户模式存储字数据    strbt:以用户模式存储字节数据    strh:存储半字数据【字和无符号字节的加载/存储指令】    ' ldr{cond}{t}  Rd, <地址>    将制定地址上的字数据读入Rd。    ' str{cond}{t}  Rd, <地址>    将Rd中的字数据存入制定地址。    ' ldr{cond}b{t}  Rd, <地址>    将制定地址上的字节数据读入Rd。    ' str{cond}b{t}  Rd, <地址>    将Rd中的字节数据存入制定地址。    // t 代表处理器是在特权模式下,存储系统也将访问看成是在用户模式下进行的。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表

图片精选