利用汇编代码编写启动文件,实现main.c文件跳转(6.19-6.21)

发布于:2025-07-01 ⋅ 阅读:(23) ⋅ 点赞:(0)

一、名词大全:

  1. 中央处理器(Central Processing Unit,CPU)是一台计算机的运算核心和控制核心,有几个内核就是几核CPU。
  2. 内核kernel基本组成:CU:控制器、ALU :算数逻辑单元、REG:寄存器(通用寄存器、PSR:状态指示寄存器等)、MMU:内存管理单元,实现虚拟内存到物理内存映射,编辑外设需要关闭。裸机默认关闭,linux操作系统会打开。cache:缓存:加速处理器对内存访问,icache指令缓存、dcache数据缓存(为编辑外设,需要关闭,C 语言中,外设寄存器应声明为 volatile,告诉编译器 禁止优化(每次读写必须访问实际地址,不依赖缓存或寄存器暂存))
  3. 内核指令集:用指令对计算机实现控制和计算的指令集合。RISC:精简指令集,arm在用,手机等、CISC:复杂指令集,x86在用,电脑等
  4. 常用内核寄存器(位于内核内部,不同于内核外部RAM,不可被寻址):PC: 程序计数:R15,存储下一条要执行的指令地址,指令在flash中、 LR: 链接返回:R14,存储子程序或异常返回地址。、SP: R13,栈指针,由于C语言各种变量函数名保存在外部ram,而内核不知道起始地址,所以需要汇编编写启动程序让内核sp指向它。
  5. 哈佛结构:指令跟数据分开存储,arm采用、       冯诺依曼结构:指令与数据放一起。
  6. SOC:片上系统(从狭义角度讲,它是信息系统核心的芯片集成,是将系统关键部件集成在一块芯片上;从广义角度讲, SoC是一个微小型系统,如果说中央处理器(CPU)是大脑,那么SoC,就是包括大脑、心脏、眼睛和手的系统。)

  1. 机器语言:二进制机器码
  2. 汇编语言:机器码的助记符号。
  3. 高级语言:如C语言
  4. 编译:从源代码(通常为高级语言)到能直接被计算机或虚拟机执行的目标代码(通常为低级语言或机器语言)的翻译过程。
  5. 编译流程:源码(.c)    1预处理-->     (.i)    2编译-->      汇编文件(.s)    3汇编-->     目标代码(.o)        4链接-->     可执行文件
  6. 最小系统:系统能够正常工作的最少器件构成的系统 。面向应用的ARM最小系统:flash(非易失性存储器):存程序代码,memory:通常为ram(易失性存储器)存储变量、函数名等。时钟:晶振

  7. 二极管:,正极接高电平,负极接低电平才能导痛。
  8. 芯片封装方式:DIP:双列直插封装、SOP:标贴封装、QFP:引脚扁平封装
  9. 计算机系统:硬件+软件
  10. 硬件:输入输出设备、存储器、运算器、控制器、总线
  11. 存储器:主存储器:ram,可由CPU直接访问,用来存放当前正在执行的程序和数据。辅助存储器:rom设置在主机外部,CPU不能直接访问,用来存放暂时不参与运行的程序和数据,需要时再传送到主存。高速缓冲存储器(Cache):CPU可以直接访问,用来存放当前正在执行的程序中的活跃部分,以便快速地向CPU提供指令和数据。      nor flash:cpu可寻址,存启动代码,、nand flash:不可寻址,存文件,如sd卡就是nand  flash。

  12. 运算器:对信息进行处理和运算的部件,经常进行的运算是算术运算和逻辑运算,因此运算器的核心是算术逻辑运算部件ALU。运算器中有若干个寄存器(如累加寄存器、暂存器等)。
  13. 控制器:计算机指挥中心。控制器中主要包括时序控制信号形成部件和一些专用的寄存器。

  14. 总线:总线是一组能为多个部件服务的公共信息传送线路,它能分时地发送与接收各部件的信息。
  15. AHB:先进高速总线,主存总线,连接高速设备; APB:先进外设总线,连接低速设备。DMA:直接内存访问总线
  16. arm处理器七种工作模式:1个非特权模式:User:用户模式,大部分任务执行的模式、6个特权模式:FIQ:快速中断模式,当一个高优先级中断产生时进入的模式、IRQ:中断请求模式,当一个低优先级中断产生进入的模式、SVC:管理模式:复位或软中断指令执行后进入的模式、Abort:中止模式,存取异常进入的模式、Undef:未定义模式,当执行未定义指令进入的模式、System:系统模式,使用和User模式相同寄存器集的模式。{补充(不常用):cortex-A特有模式、Monitor:监控模式,特权模式}
  17. ARM处理器的最新发展:架构版本:arm11后,出现arm cortex系列  a:应用领域    M:微控制器领域   R:实时处理领域         指令集版本:arm v4  v5   v7    v8                                                芯片生产厂家:NXP:恩智浦、   ST:意法半导体   Atmel公式、 Infineon、  samsung:三星、           
  18. 不同模式寄存器:黑色表示共用寄存器,其他颜色表示不共用部分。

  19. ARM 有37个32-Bits长的寄存器.
    1个用作CPSR(current program status register):当前程序状态寄存器
    5个用作SPSR(saved program status registers):保存程序状态寄存器
    31 个通用寄存器(r0 - r15  加上其他模式专用寄存器)
    Cortex体系结构下有40个32-Bits长的寄存器
    Cortex-A多出3个寄存器,Monitor 模式 r13_mon , r14_mon, spsr_mon
    当前处理器的模式决定着哪组寄存器可操作. 任何模式都可以存取:
    相应的r0-r12子集
    相应的 r13 (the stack pointer, sp) and r14 (the link register, lr)
    相应的 r15 ( the program counter, pc)
    相应的CPSR(current program status register, cpsr)
    特权模式 (除system模式) 还可以存取;
    相应的 spsr (saved program status register)
  20. 程序状态寄存器PSR(32位):               

    条件标志位:N (Negative): ALU运算结果为负时置1、Z (Zero): ALU运算结果为零时置1、C (Carry): ALU运算产生进位或借位时置1              V (Overflow): ALU运算发生溢出时置1

    特殊功能位:Q: 仅ARM vSTEJ架构支持,指示饱和状态、J: 仅ARM vSTEJ架构支持,与T位组合表示处理器状态、 DNM: 禁止修改位

    控制位:GE[3:0]: SIMD指令执行时的大于或等于标志、IT[7:2]: IFTHEN指令执行状态、 E: 大小端控制(1=大端,0=小端)、 A: 禁止不精确数据异常(1=禁止)、 I: 中断禁止(1=禁止IRQ)、 F: 快速中断禁止(1=禁止FIQ)

    处理器状态位(T位):T=0,J=0: ARM状态、T=1,J=0: Thumb状态、T=1,J=1: ThumbEE状态、T=0,J=1: Jazelle状态

     处理器模式位:M[4:0]: 10000: 用户模式(User)、 10001: FIQ模式、 10010: IRQ模式、 10011: 管理模式(SVC)、 10110: 监控模式(Monitor)、 10111: 中止模式(Abort)、11011: 未定义模式(Undefined)、11111: 系统模式(System)

      
  21. 程序计数器PC:
    当处理器执行在 ARM状态 : 所有指令 32 bits 宽 、所有指令必须 word(4字节)对齐、所以 pc值 bits [31:2] 决定 , bits [1:0] 未定义 ( 所以指令不能 halfword / byte对齐 ).
    当处理器执行在 Thumb状态 : 所有指令 16 bits 宽 、所有指令必须 halfword 对齐 、所以 pc值 bits [31:1] 决定 , bits [0] 未定义 ( 所以指令不能 byte 对齐 ).
    当处理器执行在Jazelle 状态 : 所有指令 8 bits 宽 、处理器执行 word 存取一次取 4 条指令
  22. 异常向量表:位于特定内存地址,包含8种异常类型的跳转地址:

    | 偏移量 | 异常类型 | 典型处理方式 |

    | 0x00   | Reset    | 系统初始化代码 |

    | 0x04   | Undefined Instruction | 模拟指令或报错 |

    | 0x08   | Software Interrupt (SWI) | 系统调用处理 |

    | 0x0C   | Prefetch Abort | 指令获取错误处理 |

    | 0x10   | Data Abort | 数据访问错误处理 |

    | 0x14   | Reserved | 保留 |

    | 0x18   | IRQ | 普通中断处理 |

    | 0x1C   | FIQ | 快速中断处理 |

    1. arm异常处理机制:

      1. 状态保存:

          将当前程序状态寄存器`CPSR`拷贝到对应异常模式的`SPSR_<mode>`

          保存返回地址到对应异常模式的链接寄存器`LR_<mode>`

      2. 状态切换:

          强制进入ARM状态(无论之前处于ARM/Thumb状态)

          切换到对应的异常模式(如FIQ/IRQ/Abort等)

          根据需要设置中断禁止位(如进入IRQ时禁止IRQ中断)

      3. 跳转执行:

          将PC设置为异常向量表中对应的地址

       关键寄存器操作

      | CPSR → SPSR_<mode> | 保存处理器状态 |

      | 设置CPSR[4:0] | 切换处理器模式 |

      | 设置CPSR[5] (T位) | 强制ARM状态(T=0) |

      | 设置CPSR[7]/[6] | 禁止IRQ/FIQ中断 |

      | PC → LR_<mode> | 保存返回地址 |

      注意:

       在高版本ARM核(如ARM9/10)中,向量表可重定位到`0xFFFF0000`

       所有异常处理必须在ARM状态下执行

      4. 异常返回

      异常处理完成后需执行以下操作恢复现场:

              1. 从`SPSR_<mode>`恢复`CPSR`

              2. 从`LR_<mode>`恢复`PC`

              3. 使用特定指令返回(如`SUBS PC, LR, 4`)

      小测试:
      1) ARM可以工作的模式名字
      ARM处理器支持以下7种特权模式和1种非特权模式:
      用户模式(User Mode):非特权模式,运行普通应用程序。
      FIQ模式(FIQ Mode):快速中断处理。
      IRQ模式(IRQ Mode):普通中断处理。
      管理模式(Supervisor Mode, SVC):操作系统内核和复位初始状态。
      中止模式(Abort Mode):内存访问异常处理。
      未定义模式(Undefined Mode):处理未定义指令异常。
      系统模式(System Mode):特权模式,与用户模式共用寄存器。
      监控模式(Monitor Mode)(仅ARMv7安全扩展):安全与非安全模式切换。
      
      2) ARM核有多少个寄存器?
      37个寄存器(ARMv7-A/R架构):
      31个通用寄存器(R0-R15 + 其他模式专用寄存器)。
      6个状态寄存器(CPSR + 5个SPSR,每种异常模式一个SPSR)。
      注意:不同模式下可见的寄存器不同(如FIQ模式有专用的R8-R14)。
      
      3) 什么寄存器用于存储PC和LR寄存器?
      PC(程序计数器):R15,存储下一条要执行的指令地址。
      LR(链接寄存器):R14,存储子程序或异常返回地址。
      
      4) R13通常用来存储什么?
      R13 通常用作 栈指针(SP),不同模式有独立的SP(如SP_irq、SP_svc)。
      
      5) 哪种模式使用的寄存器最少?
      用户模式(User Mode),仅使用 R0-R15和CPSR,不占用额外的模式专用寄存器。
      
      6) 在Thumb指令集中,哪些寄存器处于Low group?
      Low寄存器:R0-R7,所有Thumb指令均可直接访问。
      High寄存器:R8-R15,部分Thumb-2指令支持访问。
      
      7) CPSR的哪一位反映了处理器的状态?
      T位(CPSR[5]):
      T=0:ARM状态(32位指令)。
      T=1:Thumb/ThumbEE状态(16/32位混合指令)。
      J位(CPSR[24])(仅ARMv5+):与T位组合表示Jazelle状态。
      
      8) 所有的Thumb指令采取什么对齐方式?
      半字对齐(2字节对齐),即地址最低位必须为0(如0x1000、0x1002)。
      Thumb-2指令:可能是2字节或4字节对齐。
      
      9) ARM有哪几个异常类型?
      ARM的异常类型及对应向量表偏移:
      偏移地址	异常类型	触发条件
      0x00	Reset	处理器复位
      0x04	Undefined Instruction	遇到未定义指令
      0x08	Software Interrupt (SWI/SVC)	执行SVC指令
      0x0C	Prefetch Abort	指令预取失败
      0x10	Data Abort	数据访问失败
      0x14	Reserved	保留
      0x18	IRQ	普通中断请求
      0x1C	FIQ	快速中断请求
      10) 为什么FIQ的服务程序地址要位于0x1C?
      效率优化:
      FIQ向量在向量表末尾(0x1C),允许将FIQ处理代码直接放在向量表之后,无需跳转指令,减少延迟。
      FIQ模式有专用寄存器(R8-R14),无需保存上下文,进一步加速响应。
      
      11) 在复位后,ARM处理器处于何种模式、何种状态?
      模式:管理模式(Supervisor Mode, SVC)(CPSR[4:0]=10011)。
      状态:ARM状态(CPSR[5]=0,T位清零)。
      PC:从0x00000000(或高端向量0xFFFF0000)开始执行。

      二、ARM汇编:

    2. 学习arm汇编的主要目的是为了编写arm启动代码,启动代码启动以后,引导程序到c语言环境下运行。换句话说启动代码的目的是为了在处理器复位以后搭建c语言最基本的需求。因此启动代码的主要任务有:
      1、    初始化异常向量表;
      2、    初始化各工作模式的栈指针寄存器;
      3、    开启arm内核中断允许;
      4、    将工作模式设置为user模式;
      5、    完成上述工作后,引导程序进入c语言主函数执行;

    3. ARM公司定义了6种主要的指令集体系结构版本。V1-V6。(所以上面提到的ARMv6是指指令集版本号)。 即:ARM architecture
      ARMv1:
      该版本的原型机是ARM1,没有用于商业产品。
      ARMv2:
       对V1版进行了扩展,包含了对32位结果的乘法指令和协处理器指令的支持。
      ARMv3:
       ARM公司第一个微处理器ARM6核心是版本3的,它作为IP核、独立的处理器、具有片上高速缓存、MMU和写缓冲的集成CPU。
      ARMv4:
      当前应用最广泛的ARM指令集版本。
      ARM7TDMI、ARM720T、ARM9TDMI、ARM940T、ARM920T、Intel的StrongARM等是基于ARMv4T版本。
      ARMv5:
      ARM9E-S、ARM966E-S、ARM1020E、ARM 1022E以及XScale是ARMv5TE的。
      ARM9EJ-S、ARM926EJ-S、ARM7EJ-S、ARM1026EJ-S是基于ARMv5EJ的。
      ARM10也采用。
      其中后缀意义如下:
      E:增强型DSP指令集。包括全部算法和16位乘法操作。
      J:支持新的Java。
      ARMv6:
      采用ARMv6核的处理器是ARM11系列。
      ARM1136J(F)-S基于ARMv6主要特性有SIMD、Thumb、Jazelle、DBX、(VFP)、MMU。
      ARM1156T2(F)-S基于ARMv6T2 主要特性有SIMD、Thumb-2、(VFP)、MPU。
      ARM1176JZ(F)-S基于ARMv6KZ 在 ARM1136EJ(F)-S 基础上增加MMU、TrustZone。
      ARM11 MPCore基于ARMv6K 在ARM1136EJ(F)-S基础上可以包括1-4 核SMP、MMU。
      ARMv7-A:
      ARM处理器核:
      ARM公司开发了很多ARM处理器核,最新版位ARM11。 Cortex A7等。
       

    4. arm汇编指令集特征所有ARM指令均为32-bits长 、大部分为单周期指令 、所有指令都可以条件执行 、采用 Load/Store 架构

      ARM 是一种典型的 Load/Store 架构(加载/存储架构),这意味着:

      1、运算指令(ALU操作)不能直接访问内存

      2、必须先用 LDR(加载)指令将数据从内存读到寄存器,运算后再用 STR(存储)指令写回内存。

      3、所有计算仅在寄存器之间进行

    5. mov指令:mov指令:加载12位立即数到寄存器或转移一个寄存器的值到另外一个寄存器
      mov r0, #2 ;加载立即数2到寄存器r0,MOV{S}<c> <Rd>, #<const>
      mov r1, r0 ;将r0寄存器的值加载到r1,MOV{S}<c> <Rd>, <Rm>

      {S}:可选,表示是否更新状态标志(如 MOVS 会更新 CPSR)。<c>:可选条件码(如 MOVEQ 仅在相等时执行)。<Rd>:目标寄存器(这里是 r0)。#<const>:立即数(这里是 2)

    6. 偏移指令:ASR算数右移(高位补符号位)、LSL逻辑左移、LSR逻辑右移(高位补0)、ROR循环右移。

    7. MVN 操作数按位取反给寄存器

    8. 加法: add指令
      ADD{S}<c> <Rd>, <Rn>, #<const>
      ADDS}<c> <Rd>, <Rn>, <Rm>{, <shift>}
      减法:sub指令
      SUB{S}<c> <Rd>, <Rn>, #<const>
      SUB{S}<c> <Rd>, <Rn>, <Rm>{, <shift>}

    9. 以上四条指令都有立即数作为第二操作数的情况,那么是什么立即数呢?准确的说这里所指的是12位立即数imm12。先说怎么判断某数是不是12位立即数,12位立即数的条件是:
      1.    如果某个数的数值范围是0~0xFF之间,那么这个数一定是立即数;
      2.    把某个数展开成2进制,这个数的最高位1至最低位1之间的二进制数序列的位数不能超过8位;
      3.    这个数的二进制序列凑够8位之后的的右边必须为偶数个连续的 0
      例如:0x234 = 0000 0000 0000 0000 0000 0010 0011 0100
      最高位1至最低位1之间的二进制数序列:1000 1101没有超过8位
      末尾1的右边有2个0,所以0x234是立即数

      0x3f4 = 0000 0000 0000 0000 0000 0011 1111 0100
      最高位1至最低位1之间的二进制数序列:1111 1101 从第一个1开始到最后一个1之间没有超过8位
      末尾1的右边有2个0,所以0x3f4是立即数

      0x132 = 0000 0000 0000 0000 0000 0001 0011 0010
      最高位1至最低位1之间的二进制数序列:1001 1001 从第一个1开始到最后一个1之间没有超过8位
      末尾1的右边有1个0,不满足第二条,所以0x132不是立即数

      0x7f8 = 0000 0000 0000 0000 0000 0111 1111 1000
      最高位1至最低位1之间的二进制数序列:1111 1111从第一个1开始到最后一个1之间没有超过8位 
      末尾1的右边有3个0,不满足第二条,所以0x7f8不是立即数

      0xfab4 = 0000 0000 0000 0000 1111 1010 1011 0100
      最高位1至最低位1之间的二进制数序列:0011 1110 1010 1101 从第一个1开始到最后一个1之间超过8位,不满足条件1,所以这个数不是立即数
      这是因为ARM中将这 12bits 分为 8bit 常数(0~255)和 4bit 循环右移位值(0~15)
      8bit 常数范围(0~255),位移的步进值是以2为单位(即实际位移 2 * rotate 位),可以表示循环有以(0~30)偶数位: 0、2、4、6、8、10、12、14、16、18、20、22、24、26、28、30。在实际存储这个数值的时候,要想办法把这个数压缩到这12位中去。压缩的方法就是找一个数,这个数必须是一个8bit数,之后循环右移2 * rotate位。如果能找打这个数,那么待保存的数就是立即数,否则就不是。
       

    10. 非立即数存进rn,又该怎么做呢?
        

      LDR(Load Register)指令
      LDR 用于从内存(通常是 RAM)加载 32 位数据到寄存器。它有多种寻址模式:

      (1) 直接加载立即数(伪指令)
      arm
      LDR{<c>}{<q>} <Rt>, =<imm32>  ; 伪指令,编译器自动处理
      示例:
      ldr r0, =0x2FAB4  ; 将 0x2FAB4 加载到 r0

      (2) 基址寻址(Offset Mode)
      arm
      LDR<c> <Rt>, [<Rn>{, #+/-<imm12>}]
      示例:
      ldr r0, [r1, #4]  ; 从地址 r1 + 4 加载数据到 r0
      说明:
      Rn 是基址寄存器(如 r1)。
      #+/-<imm12> 是 12 位偏移(可选,范围 0-4095)。

      (3) 后变址寻址(Post-indexed Mode)
      LDR<c> <Rt>, [<Rn>], #+/-<imm12>
      示例:
      ldr r0, [r1], #8  ; 从 r1 加载数据到 r0,然后 r1 += 8
      说明:先访问内存,再更新基址寄存器。

      (4) 前变址寻址(Pre-indexed Mode)
      arm
      LDR<c> <Rt>, [<Rn>, #+/-<imm12>]!
      示例:
      ldr r0, [r1, #8]!  ; 从 r1 + 8 加载数据到 r0,然后 r1 += 8
      说明:先计算新地址,再访问内存并更新基址寄存器。

      2. STR(Store Register)指令
      STR 用于将寄存器中的 32 位数据存储到内存,语法与 LDR 类似:

      (1) 基址寻址
      STR<c> <Rt>, [<Rn>{, #+/-<imm12>}]
      示例:
      str r0, [r1, #4]  ; 将 r0 的值存储到地址 r1 + 4
      (2) 后变址寻址
      arm
      STR<c> <Rt>, [<Rn>], #+/-<imm12>
      示例:
      str r0, [r1], #8  ; 将 r0 存储到 r1,然后 r1 += 8
      (3) 前变址寻址
      STR<c> <Rt>, [<Rn>, #+/-<imm12>]!
      示例:
      str r0, [r1, #8]!  ; 将 r0 存储到 r1 + 8,然后 r1 += 8

    11. bic指定位清0指令:
      BIC{S}<c> <Rd>, <Rn>, #<const>;

      示例
      mov r0, #0xFF    ; r0 = 0b11111111
      bic r1, r0, #0x0F ; r1 = r0 & ~0x0F = 0b11110000

      orr指定位置1指令:
      ORR{S}<c> <Rd>, <Rn>, #<const>

      示例
      mov r0, #0x0F    ; r0 = 0b00001111
      orr r1, r0, #0xF0 ; r1 = r0 | 0xF0 = 0b11111111
    12. 汇编指令的s后缀,几乎所有的汇编指令都可以在指令后面加上s后缀,s后缀的含义是在指令执行过程中会更新cpsr寄存器的N,V,C,Z位
      N:在结果是有符号的二进制补码情况下,如果结果为负数,则N=1;如果结果为非负数,则N=0
      Z:如果结果为0,则Z=1;如果结果为非零,否则Z=0
      C:是针对无符号数最高有效位向更高位进位时C=1;减法中运算结果的最高有效位从更高位借位时C=0
      V:该位是针对有符号数的操作,会在下面两种情形变为1,两个最高有效位均为0的数相加,得到的结果最高有效位为1;两个最高有效位均为1的数相加,得到的结果最高有效位为0;除了这两种情况以外V位为0
          例如:
              mov r0, #0xFFFFFFFF
              adds r1, r0, #1
              上面的操作会导致Z,C置位,这是因为结果为0,并且从无符号数角度来看,已经从最高位向更高位进位了
              而 
              mov r0. #0x7FFFFFFF
              adds r1, r0, #1
              会造成N位和C位置位,这是因为计算结果0x80000000最为位为1,代表负数,并且 从有符号角度来看,把一个整数加成了负数。

    13. 常用条件码:

      EQ (Equal, Z == 1)
      用途:检查两个值是否相等。

      示例:

      cmp r0, r1      ; 比较 r0 和 r1
      moveq r2, #100  ; 若 r0 == r1,则 r2 = 100
      NE (Not Equal, Z == 0)
      用途:检查两个值是否不相等。

      示例:

      cmp r0, r1
      bne loop       ; 若 r0 != r1,跳转到 loop
      GT (Signed Greater Than, Z == 0 && N == V)
      用途:有符号数的大于比较。

      示例:

      cmp r0, r1
      bgt label      ; 若 r0 > r1(有符号),跳转到 label
      LT (Signed Less Than, N != V)
      用途:有符号数的小于比较。

      示例:

      cmp r0, r1
      blt label      ; 若 r0 < r1(有符号),跳转到 label
      HI (Unsigned Higher, C == 1 && Z == 0)
      用途:无符号数的大于比较。

      示例:

      cmp r0, r1
      bhi label      ; 若 r0 > r1(无符号),跳转到 label

      GE    有符号 ≥    循环边界检查(for i >= 0)
      LE    有符号 ≤    循环终止条件(while i <= 10)
      LS    无符号 ≤    缓冲区长度检查
      CS/HS    无符号 ≥(Carry Set)    加法进位检测
      CC/LO    无符号 <(Carry Clear)    借位检测(减法)                     从r0, r1代表的两个有符号数中找到较大值放入r2寄存器

    14. CMP比较指令用于比较两个寄存器的值或者比较一个寄存器和立即数的值,其原理是对待比较的两个数求差,看结果是否为0,这个指令会无条件修改N,V,C,Z位。
      如:
      mov r0, #100
      cmp r0, #100
      会导致Z置位,从条件码表可知,只要Z置位就是两数相等。
      1. arm跳转指令:b

        无函数调用的函数(leaf叶函数)

        B <label>

        最多跳到PC ±32 Mbyte .

                                配合之前的条件码,就可以实现一些较为复杂的操作,例如实现从0加到100的和                                    事实上,程序跳转工作更多的是为了实现类似函数的功能,此时lable就是函数的函数名,其实lable本身代表的就是待跳转那一行指令的地址,b指令本质上就是把待跳转那行的地址装入pc寄存器,但是函数在调用完毕之后要回到调用处的下一行指令处执行,为了能够回到调用的下一行,需要使用bl指令。bl和b之间的区别就在于bl会在lr寄存器中保存回来的地址。如:

        BL <子程序>

        保存返回地址到 LR

        返回时从 LR 恢复 PC

        bx lr相当于mov pc  lr。以上代码虽然实现了函数调用的程序流程,但是还存在两个问题:一是调用完毕以后r0, r1寄存器的值被max_of_two_numbers函数修改了,二是如果出现了函数的嵌套调用,那么lr寄存器的值就会被修改,而无法回到最开始的地方。要解决这个问题,就必须在每次函数调用前保护现场,在函数调用完毕以后恢复现场。而实现这个功能就需要使用栈这个数据结构。

        1. 数组栈:用一段连续的内存空间为栈提供间。                             入栈的方式有四种做法:
          1.    空增:先写入数据,再让栈指针自增;
          2.    空减:先写入数据,再让栈指针自减;
          3.    满增:先让栈指针自增,再写入数据;
          4.    满减:先让栈指针自减,再写入数据。
          arm体系采用的方案是满减。栈底设置为0x40001000,从地址0x40000000开始的0x1000这段内存空间对应的是一段ram,总共4k。实际能够使用的内存空间为[0x40000000~0x40000FFF],设置栈底指针寄存器: ldr sp =0x40001000

      2. 对于 non-leaf(非叶) 函数,及有函数嵌套调用的函数, LR 必须压栈保存。

        STMFD(Store Multiple Full Descending)
        将寄存器列表(含 LR)压栈(SP 指向栈顶,地址递减)。STMFD<c> <Rn>{!}, <registers>
        其中Rn表示栈底指针寄存器,< registers >表示需要入栈保护的寄存器,!表示入栈之后sp自动自减。如:
        stmfd sp!, {r0, r1, r2, r3-r12, lr}

        LDMFD(Load Multiple Full Descending)
        从栈中恢复寄存器,并将 LR 值直接赋给 PC(实现返回)。LDMFD<c> <Rn>{!}, <registers>
        中Rn表示栈底指针寄存器,< registers >表示需要入栈保护的寄存器,!表示出栈之后sp自动自增。如:
        ldmfd sp!, {r0, r1, r2, r3-r12, lr}

    15.  在汇编中调用c语言编写的函数
      设有c语言定义的函数void func_c(void);在汇编代码中调用该函数,只需用import声明函数名即可,之后就可以使用bl指令调用该函数,注意,既然是调函数,就一定要保护现场

    16.   向c函数传参
      向c函数传参的方法很简单,如果参数个数小于等于4个,就直接用r0~r3传参,c函数返回值通过r0寄存器返回
      设有c函数:
      int add_c(int a, int b, int c, int d)
      {
      return a + b + c + d;
      }如果参数个数大于4个,从第五个参数开始就需要通过栈来传参在c语言中调用汇编编写的函数类似,不过在汇编中用export声明函数,同时需要在c语言中用extern声明函数,按照标准,调用者负责保护现场和恢复现场

切换arm内核的工作模式
切换工作方式的思路很简单,由于内核的工作模式是由cpsr寄存器的低5位来设置的,那么就可以先把cpsr读出来,更改低5位之后再设置进去。这里读取cpsr使用mrs指令,写cpsr寄存器用msr指令,需要注意的是在keil环境下写cpsr需要写成:   msr cpsr_c r0;将r0的值写入到cpsr寄存器,需要注意的是在keil环境下写cpsr需要写成:   msr cpsr_c r0;将r0的值写入到cpsr寄存器

;学习了这些指令,最终就可以编写我们自己的启动代码了
	preserve8
	area reset, code, readonly
	code32
	entry


	b start
	nop
	nop
	nop
	nop
	nop
	b do_interrupt
	nop

start
	ldr sp, =0x40001000
	mrs r0, cpsr
	bic r0, r0, #0x1F
	orr r0, r0, #0x12
	bic r0, r0, #(0x01 << 7)
	msr cpsr_c, r0

	ldr sp, =0x40001000
	sub sp, sp, #1024
	mrs r0, cpsr
	bic r0, r0, #0x1F
	orr r0, r0, #0x10
	msr cpsr_c, r0

	ldr sp, =0x40001000
	sub sp, sp, #2048

	import main
	b main

do_interrput
	sub lr, lr, #4
	stmfd sp!, {r0-r12, lr}
	import interrupt_handle
	bl interrupt_handle
	ldmfd sp!, {r0-r12, pc}^
	end

1、软件中断:Software Interrupt

使用指令SWI:示例:

swi  立即数

三、开发板:IMX6ULL-Mini  核心板是一个soc,由恩智浦制造,中间核心板包含一个cortex a7内核,左侧是nand-flash,下面是ddrram 512mb,内核右边有一个晶振24MHz。,核心板下的底板就是正点原子搭建的各种外设。

编程使用请看下一节。


    网站公告

    今日签到

    点亮在社区的每一天
    去签到