ARM汇编简介

寄存器资源

    R0-R10:通用寄存器
    R11(fp:frame-pointer):用来记录一个栈空间的开始地址
    Rl2(ip:The Intra-Procedure-call scratch register):用来临时存储sp
    R13(sp:stack pointer):栈指针寄存器
    R14(lr:link register):在发生跳转的时候,用来保存PC寄存器的值
    R15(pc:program counter):用来存放CPU需要执行的指令所在内存的地址
    CPSR:

        Mode[4:0] :记录当前ARM核所在的模式
        T[5] : ARM状态(执行32bit指令),Thumb状态(执行16bit指令)
        F[6] : FIO禁止(1)/使能(0)
        I[7] : IRQ禁止(1)/使能(0)
        N[31]: 计算结果为负数,置1
        Z[30]: 计算结果为零,置1
        C[29]: 产生进位则置1,产生借位则置0
        V[28]: 超出一个类型范围溢出
    SPSR: 异常产生的时候,用来保存CPSR的值

指令格式

<opcode>{<cond>}{s}   <Rd>, <Rn>, {,<operand2>}
        opcode:助记符
        cond: 条件 {NE 不相等、EQ 相等、GT 大于、LT 小于、GE 大于等于、LE 小于等于}
        s:会影响CPSR的NZCV

        Rd: 目标寄存器 R0 ~ R15
        Rn: 操作数一 必须是寄存器
        operand2: 操作数二 可以是立即数、寄存器、寄存器移位

一、数据传送指令

1. mov 目标寄存器, 操作数二(立即数、寄存器、寄存器移位)
        @将操作数二的值赋值给目标寄存器
        mov r0, #1               //立即数
        mov r1, r0                //寄存器
        mov r2, r1, lsl #2      //寄存器移位(lsl逻辑左移、lsr逻辑右移、asr算数右移)

2. mvn 目标寄存器, 操作数二
        @将操作数二取反的值赋值给目标寄存器
        mov r0, #0
3. LDR 目标寄存器, = 数据
        @将任意数据放到目标寄存器中
        ldr r0, =0x12345678

二、数据计算指令

    1. add 目标寄存器, 操作数一(寄存器), 操作数二(立即数、寄存器、寄存器移位)
        @将操作数一加上操作数二的结果给目标寄存器
        add r0, r1, #0xff
        add r1, r0, r1
        add r0, r1, r1, lsl #8
    2. sub目标寄存器, 操作数一, 操作数二
        @将操作数一减去操作数二的结果给目标寄存器
        @指令后加s,会影响CPSR寄存器的NZCV, eg: subs
    3. mul目标寄存器, 操作数一, 操作数二
        @操作数一和操作数二必须是寄存器,且操作数一和目标寄存器不可以一样

三、位运算指令

    1. and 目标寄存器, 操作数一, 操作数二
        @将操作数一按位与操作数二的结果给目标寄存器
    2. orr 目标寄存器, 操作数一, 操作数二
        @将操作数一按位或操作数二的结果给目标寄存器
    3. eor 目标寄存器, 操作数一, 操作数二
        @将操作数一按位异或操作数二的结果给目标寄存器
    4. bic 目标寄存器, 操作数一, 操作数二
        @将操作数一按位与操作数二取反的结果给目标寄存器
        @把操作数一中对应的操作数二中为1的位清除为0

四、比较指令

    1. cmp 寄存器, 操作数二
        @将寄存器的值与操作二比较,比较的结果会自动影响CPSR的NZCV

五、跳转指令

    1. B/BL 标签
          @跳到一个指定的标签,BL跳转之前,将跳转前的PC的值保存在LR,跳转范围+/-32M
    2. 给pc赋值 ldr pc, =标签名

六、内存访问指令

1. 单个数据访问
      LDR 将内存中的值加载到寄存器
      STR 将寄存器中的值写入内存
      @ 寄存器间接寻址:寄存器的值是一个地址
        LDR r0, [r1]  @ r0 = *r1
        STR r0, [r1]  @*r1 = r0
      @ 基址变址寻址:将基地址寄存器加上指令中给出的偏移量,得到数据存放的地址
        前索引
            STR r0, [r1, #4]  // *(r1 + 4) = r0
            LDR r0, [r1, #4]  // r0 = *(r1 + 4)
        后索引
            STR r0, [r1], #4 // *r1 = r0 && r1 = r1 + 4
            LDR r0, [r1], #4 // r0 = *r1 && r1 = r1 + 4
        自动索引
            STR r0, [r1, #4] // *(r1 + 4) = r0 && r1 = r1 + 4
            LDR r0, [r1, #4] // r0 = *(r1 + 4) && r1 = r1 + 4

2.多个数据访问
      @ LDM 将一块内存中的值加载到多个寄存器中   LDM{条件}{s}<MODE> 基址寄存器{!},{Reglist}^
      @ STM 将多个寄存器中的值,储存到一块内存中 STM{条件}{s}<MODE> 基址寄存器{!},{Reglist}^
      MODE: IA 后增加地址;IB 先增加地址;DA 后减小地址;DB 先减小地址
      基址寄存器:存放内存的起始地址
      !:最后更新基址寄存器的值
      Reglist: 多个寄存器,从小到大,中间用","隔开{r0, r2, r4}、{r0, r3-r7},寄存器号大的对应内存的高地址,寄存器号小的对应内存的低地址
      ^: 它存在,如果Reglist没有pc的时候,这个时候操作的寄存器是用户模式下的寄存器,在LDM指令中,有PC的时候,在数据传送的时候,会将SPSR的值拷贝到CPSR,用于异常的返回

3.栈操作指令
      进栈: stmfd sp!,{寄存器列表}
      出栈: ldmfd sp!,{寄存器列表}
      @ 在对栈操作之前,必须先设置sp的值,进栈和出栈的方式一样,ATPCS标准规定满减栈(满堆栈并且向下增长)
      @ 堆栈指针指向最后压入的堆栈的有效数据顶,称为满堆栈
      @ 堆栈指针指向下一个待压入数据的空位置,称为空堆栈

4. CPSR/SPSR操作指令
        MRS Rn, CPSR/SPSR 将状态寄存器的值读到通用寄存器中
        MSR CPSR/SPSR, Rn 将通用寄存器的值写到状态寄存器中

七、ARM常用伪指令分析

    1. LDR R0, =0x12345678
        ldr r1, =0x12345678
        翻译如下:
         0x00000004  E51F1000  LDR       R1,[PC]             @ 执行阶段
         0x00000008  EAFFFFFE  B         0x00000008         @ 译码阶段
         0x0000000C  12345678  EORNES    R5,R4,#0x07800000 @ 预取阶段,pc的值
    2. LDR R0, =Lable
        @指令表示将Label的值写入r0,Label的值由指定的代码段运行地址(-Ttext=地址值)来决定
        @ 首先根据指定的代码段开始的地址,算出Labe标签对应的地址值
        @ 然后将这个表示的地址值存放在一个位置
        @ 生成内存访问指令,根据pc+固定偏移量,找到标签对应值存放的位置
        @ ps: 当代码编译结束的时候,标签表示的地址值(根据指定的代码段地址)已经编译死存放在程序文件中了。
    3. LDR R0, Lable
        @ R0写入的事Lable地址所对应的数据
        例如:0x0000000c Lable: .word 0x12345678
                   R0读到的不是0x0000000c而是0x12345678

    4. ADR R0, Lable
        @ 也是将Label的值写入r0

        ```
        _start:
        mov r0, #1         @0x0000
        adr r1, Lable      @0x0004
        stop:        
           b stop             @0x0008
        Lable:
           .word 0x12345678 @0x0004

        ```
        翻译如下:
        0x00000000  E3A00001  MOV       R0,#0x00000001     
        0x00000004  E28F1000  ADD       R1,PC,#0x00000000
        0x00000008  EAFFFFFE  B         0x00000008
        0x0000000C  12345678  EORNES    R5,R4,#0x07800000
        ps: 指令表示根据当前的PC的值+/-偏移量,动态获取当前Label所表示的内存地址, 不会在编译阶段就写死
        所以通过 ADR R0, _start 可以获取在内存中的实际地址

八、ATPCS标准 -- ARM-Thumb程序调用标准

寄存器同义词特殊名规范作用
r15PC程序计数器
r14LR链接寄存器,发生跳转时用来保存pc
r13SP栈指针寄存器
r12IP保存SP
r11v8FPARM状态变量寄存器8 & 栈帧寄存器
r10v7SLARM状态变量寄存器7 & 堆栈限制指针寄存器
r9v6SBARM状态变量寄存器6
r8v5ARM状态变量寄存器5        
r7v4WR变量寄存器4 & Thumb状态工作寄存器
r6v3变量寄存器3
r5v2变量寄存器2
r4v1变量寄存器1
r3a4参数/结果/临时寄存器4
r2a3参数/结果/临时寄存器3
r1a2参数/结果/临时寄存器2
r0a1参数/结果/临时寄存器1

 @ v1-v8变量寄存器只有v1-v4可以在Thumb下使用
 @ Thumb状态下,只有r0-r7,SP,LR,PC普遍可用     
 @ ARM压栈的顺序很是规矩,依次为当前函数指针PC、返回指针LR、栈指针SP、栈基址FP、传入参数个数及指针、本地变量和临时变量。

九、汇编与c混合编程

1. 汇编调用c
        @ 需要在汇编代码中指定sp
        mov r0, #1
        mov r1, #2
        ldr sp, =0x4000fff0
        bl add

2. c内嵌汇编
        asm(
            "指令1\n"
            "指令2\n"

           ... ...
           :输出列表
           :输入列表
           :修改列表(通用的寄存器)
         );
        指    令:ARM汇编指令
        输出列表:将内嵌汇编中的寄存器值输出到c变量
        输入列表:将c变量输入到内联汇编中使用的寄存器
        修改列表:内联汇编中修改的寄存器

```
        int add(int a, int b){
            int c;
            asm(
                "add r0, %1, %2\n"
                "mov %0, r0\n"
                :"=r"(c)
                :"r"(a), "r"(b)
                :"r0"
            );
            return c;
        }

```
        ps : C变量的引用,从输出列表到输入列表开始编号:第一个C变量%0,第二个C变量%1,... ...
        
        修饰符                说明
         无                被修饰的操作符是只读的
         =                 被修饰的操作符只写
         +                 被修饰的操作符具有可读写的属性
         &                 被修饰的操作符只能作为输出

十、ARM核异常处理

(一)什么是异常

异常是处理器核在执行程序指令的过程中突然遇到了异常的事情,这些事件包括硬件中断、指令执行错误、用户程序请求服务、内存访问异常、取指令异常等,几乎每种处理器都支持特定的异常处理,中断也是异常的一种

(二)ARM异常源

异常偏移量异常源描述优先级模式
0x00Reset复位异常:在内核复位时执行1管理模式(SVC)
0x04

Undefined

instructions

未定义指令异常:流水线执行非法指令产生,该异常发生在流水线译码阶段,如果当前指令不能被识别为有效指令,将会出现此类异常。6未定义模式(UDF)
0x08SVC(Supervisor Call)、SWl(Software
Interrupt)
软中断异常:用于程序触发软件中断执行该异常是由应用程序自己调用时产生,该异常在管理模式(SVC)下运行。6管理模式(SVC)
0x0cPrefetch abort预取指令中止异常:当一条指令从内存中取出时由于某些原因失败,且如果它能到达执行状态将会触发此异常。5终止模式(ABT)
0x10Data abort数据访问中止异常:如果一个预取指令试图存取一个不存在或非法的内存单元时,将会触发此异常。5终止模式(ABT)
0x14Reserved保留保留保留
0x18IRQ一般中断异常4一般中断模式(IRQ)
0x1cFIQ快速中断模式3快速中断模式(FIQ)

FIQ较IRQ快的原因:
        1.FIQ在异常向量表位于最末
                a.所以可以直接把异常处理程序写在异常向量表之后,省去了跳转的过程。
                b.而IQ需要执行向量表对应的跳转指令之后,跳转到指定的中断处理程序。
        2.FIQ模式有5个私有寄存器(R8-R12)
                a.执行中断处理程序前无需压栈保存寄存器,可直接处理中断。
                b.而IRQ的R8-R12寄存器是与和其它模式共用的,在中断前需要保护现场,即把R8-R12的数据保存在栈中。
        3.FIQ的优先级高于IRQ
                a.两个中断同时发生时先响应FIQ
                b.FIO可以打断IRO,IRO不能打断FIO。

(三)ARM异常处理过程

异常向量表:一块内存存放的是跳转到异常处理函数入口地址的指令,当异常产生的时候
ARM核会到异常向量表的相应位置取指令执行,跳转到对应的异常处理函数。

1. 异常产生,ARM核自动做的事情:
        (1)拷贝CPSR的值到SPSR异常
        (2)修改CPSR
                [1]进入ARM状态
                [2]切换到异常模式
                [3]禁止中断
        (3)将当前PC的值保存到LR_异常
        (4)将印c设置到异常向量表相应位置

2. 从异常向量表跳转到异常处理函数 ==》

exception_handler:
        通用寄存器压栈保护        
        异常处理
        异常返回(恢复寄存器、CPSR、PC)

异常向量表的位置:

        1.0xFFFF0000 or 0x00000000 on ARM720T and on ARM9/10 family devices,Determined by cp15.c1寄存器
        2.Cortex-A Determined by cp15.c12存器

ps: cp0~cp15共16个协处理器,它们也有自己的寄存器,一般以c开头

(四) SVC/SWI异常处理

 指令:SVC/SWI{cond} #imm

1.软中断在操作系统中的应用

2. 实例演示

_start:
    b reset
    ldr pc, _undefined_instruction
    ldr pc, _software_interrupt
    ldr pc, _prefetch_abort
    ldr pc, _data_abort
    ldr pc, _not_used
    ldr pc, _irq
    ldr pc, _fiq
    
_undefined_instruction: .word _undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word _prefetch_abort
_data_abort: .word _data_abort
_not_used: .word _not_used
_irq: .word _irq
_fiq: .word _fiq
    
resest:
    @adr r0, _start
    @mcr p15, 0, r0, c12, c0, 0
    
    swi #8 @发生软中断
    
    mov r0, #1 @但是因为破坏了流水线,ARM核会把lr调整到这里
    mov r1, #2 @此时pc指向这里
    
stop:        
    b stop
    
software_interrupt:
    ldr sp!, =0x40000fff0 @设置栈指针位置
    stmfd sp!, {r0-r12, lr} @保护通用寄存器
    
    ldr r0, [lr, #-4]
    mov r1, #0xff
    bic r0, r0, r1, lsl #24 @以上三步可以获取软中断号
    
    ldmfd sp!, {r0-r12, pc}^ @^: 有PC的时候,在数据传送的时候,会将SPSR的值拷贝到CPSR 

  • 22
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

清墨书晚风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值