day09 ARM汇编指令

来源:互联网 发布:交易宝行情软件 编辑:程序博客网 时间:2024/06/05 05:52
回顾:
面试题:谈谈对ARM处理器的认识
1.ARM编程基础
    1.1.谈谈常见的处理器
    x86/powerpc/dsp/fpga/mips(北京君正)/arm/单片机
    1.2.ARM的定义
    一种处理器架构
    ARM公司设计CPU核(ARM核),IP授权
    不生产处理器
    1.3.ARM的版本(ARM核的版本)划分
    大版本:
    小版本:
    1.4.ARM的三级指令流水线和五级指令流水线
    ARM7(小版本):采用三级指令流水线
    F->D->E
    特例:
    ldr:F->D->E->M->W
    bl:F->D->E->L->A
    中断:DI->EI->L->A
    CPU核执行完指令以后才会去处理中断
 
    ARM9(小版本以后):采用五级流水线F->D->E->M->W,除了ldr有M过程,其余指令一律没有M动作
            画出三级或者五级流水线的处理示意图
    1.5.ARM核的7种工作方式
         SVC管理/FIQ/IRQ/Abort/Undef/System/User
          明确:CPU核切换到对应模式的场景   
    1.6.ARM核的2种工作状态
     ARM状态
     ARM指令/32位
        
     Thumb状态              
     Thumb指令/16位
    1.7.ARM核的37个寄存器
     画出37个寄存器在7种工作模式下的分布图
                                                                     r0...r13(sp),r14(lr),r15(pc),cpsr(nzcviftmode),spsr                                        
    1.8.展示ARM汇编的编程格式
        .text
        .code 32 /.code 16
        .global start
         start:
          一堆的指令 
       .end   

    1.9.ARM编程涉及的条件助记符
         eq/ne/cs/cc  
      影响cpsr的nzcv的两种情形:指令后面加s。cmp指令不用加也影响 。
 
2.ARM异常
  <<ARM体系结构与编程>>第二版,杜春雷
  2.1.何为ARM核(CPU核)的"跳转"?
      答:CPU核跳转是指CPU核到某个地址去"运行"(取指,解码,执行,(访存),写回)
  2.2.何为"异常"?
      答:异常不是预期的事情,具有随机性
  2.3.ARM核的7种异常
      问:一旦ARM的某种异常发生,ARM核要切换到什么模式
          ARM核跑到哪个地址去运行(F->D->E->M->W)
      答:通过一下表格来进行汇总
          只需画出一下表格即可:
          异常         工作模式        CPU核运行地址    场景
          复位异常    SVC管理模式       0x00         系统复位
          
          Undef未定义 Undef未定义       0x04         CPU核执行一个
          指令异常    指令模式                不认识的指令
          
          软中断异常  SVC管理模式       0x08         CPU核执行swi指令
          
          预取指令    Abort中止模式     0x0C         CPU核取指失败
          异常
          
          数据处理    Abort中止模式     0x10         CPU核访存失败
          异常
          
          IRQ中断     IRQ中断模式       0x18         外设给CPU核发送IRQ中断电信号
          异常  
          
          FIQ中断     FIQ快速中断模式   0x1C         外设给CPU核发送FIQ快速中断电信号
          异常
          
          注意:
          1.一旦异常发生,CPU核会毫无条件的必须的强制的跑到对应的
            入口地址(0x00/0x04/0x08/0x0c/0x10/0x18/0x1c)去运行
   
  2.4.问:ARM核一旦触发某个异常,ARM核如何处理异常呢?
      答:以UART控制器给CPU核发送IRQ中断电信号为例,
          理清IRQ异常的处理流程:
          1.当UART控制器准备好数据以后,首先会给
            中断控制器发送一个中断电信号
          2.中断控制器接收到这个中断电信号以后
            经过一番的判断之后,中断控制器最终
            以IRQ的形式给CPU核发送一个IRQ中断电信号
          3.CPU核一旦接收到IRQ中断电信号,就会触发
            一个IRQ异常,CPU核立马要处理这个IRQ异常
            3.1.CPU核硬件上自动完成以下工作:
                1.把当前程序对应的cpsr的值保存到IRQ中断模式下的spsr_irq
                  问:为什么保存cpsr到spsr_irq
                  答:为了将来IRQ异常处理完毕返回到原先
                      被打断的地方
                
                2.设置cpsr,将cpsr的:
                  bit[4:0]=IRQ工作模式对应的值,让CPU核
                           切换到IRQ中断模式
                  bit[5]=0,让ARM核切换到ARM状态下
                  bit[7:6]=11,禁止ARM核响应IRQ和FIQ中断
                   
                3.保存返回地址:lr=pc-4  
                  问:为什么是lr=pc-4呢?    
                  答:例如:
                      内存地址     指令
                      0x8000       add
                      0x8004       sub
                      0x8008       orr
                      当add指令执行的时候:pc=0x8008
                      当add指令执行的时候,触发IRQ异常
                      将来IRQ异常处理完毕,CPU核返回到0x8004
                      去运行,所以lr=pc-4=0x8008-4=0x8004
               
              4.设置pc=0x18
                问:为什么0x18呢?
                答:因为IRQ异常处理的入口地址为0x18
                问:pc=0x18代表什么含义呢?
                答:本质就是让CPU核跑到0x18地址去
                    运行(F->D->E->M->W)
                举一反三:
                        pc=0x48000000:让CPU核跑到0x48000000去运行
                        pc=uart_init:让CPU核跑到uart_init函数对应的
                                     地址去运行
                问:0x18地址放什么东西?
                答:0x18势必放自己编写的软件代码
                    例如:add指令可以放到0x18地址
                     
                切记:只要给pc赋值(地址),那么CPU核就会
                      跑到对应的地址去运行
                  
                结论:一旦ARM核做pc=0x18,让CPU核跑到0x18
                地址去运行,至此正式开启了软件进一步
                处理IRQ异常的流程
              
             3.2.软件处理IRQ异常的流程:
                      软件如何处理IRQ异常完全由程序员自行决定
                      软件处理IRQ异常结束以后,最后让CPU
                      核返回到原先被打断的地方(某个程序)
                      继续执行,软件只需完成一下两个动作即可
                      实现返回:
                         cpsr=spsr_irq:将IRQ模式下的spsr的值归还给cpsr
                         spsr_irq保存原先被打断
                                                          的程序运行的状态
                              pc=lr:lr保存的返回地址,让CPU核跑到
                                     原先被打断的地方继续执行
             
            至此:IRQ异常处理完毕!
    2.5.切记切记:ARM核跳转的方式有三种
        1.ARM核发生异常,一旦发生异常,ARM核跑到
          对应的异常入口地址去
           
        2.ARM核执行b或者bl跳转指令
         
        3.直接向pc赋值
          此法最给力!
 
3.ARM指令集之分支跳转指令:b/bl/bx

  1.b为不带返回的跳转指令
    跳转范围相对PC,+-32MB
    b loop 无条件跳转
    beq loop 有条件跳转
     
  2.bl为待返回的跳转指令
    跳转范围相对PC,+-32MB
    切记:当CPU核执行bl指令时,CPU核硬件上自动将
    下一条指令的地址保存在lr中
    所以将来软件返回的指令为:mov pc, lr
    例如:
    内存地址 指令
    0x8000   bl strcmp @当bl执行时,pc=0x8008,lr=0x8004
    0x8004   add r0, r0, r1
    0x8008   sub r0, r0, r1
     
    strcmp:
                一大堆指令
              mov pc, lr @pc=lr=0x8004,让CPU核跑到0x8004去运行
     
    案例:分析一下代码的漏洞
    init:
         bl main
         b .        
    main:
       stmfd   sp!, {lr} @将lr压栈
       bl          func1
       ldmfd   sp!, {pc} @出栈,pc=lr,实现返回   
    func1:
       bl        func2
       mov   pc,  lr
    func2:
       mov   pc,  lr
 
  3.bx带状态切换的指令(了解)
    bx跳转的地址的bit[0]=0:切换到ARM状态
    bx跳转的地址的bit[0]=1:切换到Thumb状态
    例如:
    ldr r0, =0x48000000
    bx r0 @让CPU核切换到ARM状态并且跳转到0x48000000去运行
     
    ldr r0, =0x48000001
    bx r0 @让CPU核切换到Thumb状态并且跳转到0x48000001去运行                 
   
  案例:编写ARM汇编代码,实现两个数的减法
  实施步骤:
  1.上位机(自己安装的纯系统)安装qemu模拟器,用此模拟器来模拟一个ARM核
    sudo apt-get install qemu
  2.达内虚拟机已经安装完毕,无需安装
  3.编写测试
    mkdir /opt/arm/day09/1.0 -p
    cd /opt/arm/day09/1.0
    vim do_sub.s 添加如下内容
    .text
    .code 32
    .global _start
    _start:
            mov r0, #0 @r0=0
            mov r1, #2 @r1=2
            mov r2, #9 @r2=9
            subs r0, r1, r2 @r0=r1-r2=2-9=-7
            
            b . @死循环
    .end  
    保存退出
    arm-cortex_a9-linux-gnueabi-as -g -o do_sub.o do_sub.s
    说明:
    arm...as:汇编器,将汇编文件编译生成目标文件
    -g:添加调试信息
     
    arm-cortex_a9-linux-gnueabi-ld -o do_sub do_sub.o
     
    在一个终端中执行:
    cd /opt/arm/day09/1.0
    qemu-arm -g 1234 do_sub //执行qemu-arm模拟器
                              将来用于运行do_sub
                              并且指定端口号为1234
    此时此刻,命令卡主不动!
     
    再打开一个新的终端执行:
    cd /opt/arm/day09/1.0
    arm-cortex_a9-linux-gnueabi-gdb do_sub //调试do_sub程序
    出现“(gdb)”命令提示符,输入一下命令调试do_sub
    (gdb)target remote localhost:1234 //让gdb连接qemu
    (gdb)l //查看代码
    (gdb)b 5 //在第五行设置一个断点
    (gdb)s //让CPU执行下一条指令,单步执行
    (gdb)info reg //查看ARM寄存器的值
             寄存器 寄存器值(10) 寄存器值(16)
             r0                0                     0
             r1      ...                   ...
             r2
             ...
             cpsr                          
    各种s,各种info reg
    最终查看r0是否为-7,cpsr的bit31是否为1
    (gdb)quit //退出gdb  
 
4.ARM指令集之数据处理指令
  数据处理指令又分四类:
      数据传送指令
      算数运算指令
      位运算指令
      比较指令
  4.1.数据处理指令的移位操作符
      lsl(左移)/lsr/asr/ror/rrx
   
  4.2.数据处理指令之数据传送指令:mov/mvn
      注意:mov和mvn指令操作的立即数的范围为0x00~0xff
      例如:
               mov r0, #0xff  @合法,编译通过
               mov r0, #0x1ff @不合法,编译不通过
               mov r0, #0xffffffff @不合法
               mvn r0, #0x0 @合法,r0=0xffffffff
               moveqs    r0, r1
               说明:
               1.判断cpsr的z位是否为1
                 如果为1条件成立,执行mov
                 把r1的值给r0
               2.又由于指令后面加了s,根据运算结果r0
                 影响cpsr的nzc
                
               mvneqs    r0, r1
               说明:
               1.判断cpsr的z位是否为1
                 如果为1条件成立,执行mov
                 把r1的值取反给r0
               2.又由于指令后面加了s,根据运算结果r0
                 影响cpsr的nzc
    
   4.3.切记切记切记:影响cpsr的两种情形
          1.指令后面加s,并且目标寄存器是pc,将来的
            运算过程为:
            cpsr=spsr:实现程序状态的恢复
            pc=地址:实现CPU核的跳转
            问:何为目标寄存器呢?
            答:离指令最近的寄存器
            例如:
                    moveqs pc, lr
                    1.首先判断当前cpsr的z位是否为1
                      如果为1条件成立,执行mov
                      就是将lr的值给pc,即:pc=lr
                      本质实现返回,实现CPU核的跳转
                    2.又由于目标寄存器是pc,并且指令
                      后面加了s,最后还要实现把当前工作模式
                      下的spsr赋值给cpsr,即:cpsr=spsr_mode
                      本质实现程序状态的恢复
            
           2.指令后面加s(除了比较指令),但是目标寄存器不是pc
             运算的结果仅仅影响cpsr的nzcv位
             例如:
                     moveqs r0, r1
                    1.首先判断当前cpsr的z位是否为1
                      如果为1条件成立,执行mov
                      就是将r1的值给r0
                    2.指令后面加了s,根据运算结果r0影响
                      cpsr的nzc位
         
        4.3.数据处理指令之算数运算指令
            add:不带进位的加法指令
            adc:带进位的加法指令  
            例如:
            add r0, r0, r1 @r0=r0+r1
            adc r0, r0, r1 @r0=r0+r1+cpsr的c位的值
            例如:实现两个64位数的加法
                            低32位       高32位
            数据A   r0           r1
            数据B   r2           r3
            答案:
                        adds r0, r0, r2
                        adc  r1, r1, r3
             
            sub:不带借位的减法指令
            sbc:带借位的减法指令
            例如:
                        sub r0, r0, r1 @r0 = r0-r1
                        sbc r0, r0, r1 @r0 = r0-r1-cpsr的c
         
        4.4.数据处理指令之位运算指令:and/orr/eor/bic
            例如:biceqs r0, r0, #0x07 @r0=r0 & ~(0x07)
         
        4.5.数据传送指令之比较指令:cmp/cmn/tst/teq
            cmp本质做减法运算
                        cmp r0, r1 @运算结果=r0-r1
            cmn本质做加法运算
                        cmn r0, r1 @运算结果=r0+r1
            tst本质做位与运算
                        tst r0, r1 @运算结果=r0&r1
            teq本质做位异或运算
                 teq r0, r1 @运算结果=r0^r1
            比较指令后面不加s,运算结果同样影响cpsr的nzc位
         
5.案例:编写汇编代码,实现:10+9+8+0
  实施步骤:
  mkdir /opt/arm/day09/2.0  
  cd /opt/arm/day09/2.0
  vim sum.s 添加如下内容
       .text
       .code 32
       .global _start
       _start:
               mov r0, #0
               mov r1, #10
       _sum:
               add r0, r0, r1
               sub r1, r1, #1
               cmp r1, #0
               bne _sum               
               b .
       .end
       保存退出
       arm-cortex_a9-linux-gnueabi-as -g -o sum.o sum.s
       arm-cortex_a9-linux-gnueabi-ld -o sum sum.o
        
       一个终端输入:
       cd /opt/arm/day09/2.0
       qemu-arm -g 1234 sum
       另一个终端输入:
       arm-cortex_a9-linux-gnueabi-gdb sum
       (gdb)target remote localhost:1234
       (gdb)l
       (gdb)b ...
       (gdb)s  
       (gdb)info reg
       (gdb)s
       ...
       最终查看r0的值
        
6.案例: 编写汇编代码,实现求两个数的最大公约数
  求两个数的最大公约数的算法:
    20    12
                12    8
                            8    4
                                     4    4
                                               0
   实施步骤:
   mkdir /opt/arm/day09/3.0
   cd /opt/arm/day09/3.0
   vim gcd.s 添加如下内容
   .text
   .code 32
   .global _start
   _start:
       mov r0, #12
       mov r1, #20
   loop:
       cmp r0, r1
       beq loop_end
       subcs r0, r0, r1
       subcc r1, r1, r0
       b loop
       
   loop_end:
       b .         
   保存退出
   编译
   qemu-arm+gdb进行测试