ARM8 汇编指令 电脑版发表于:2024/7/10 16:08 ![](https://img.tnblog.net/arcimg/hb/6930d1439f4b43e785a433685b813262.png) >#ARM8 汇编指令 [TOC] ARM8 汇编指令参考 ------------ tn2>关于ARM8汇编指令可以参考线上链接:https://developer.arm.com/documentation/ddi0596/2021-09/Base-Instructions ### 常用命令如下 | 汇编代码 | 注释 | |-------------------------|----------------------------------------------------------------------| | `b.ne label` | 不等时跳转到label | | `cbz w10, 1f` | w10值等于0时跳转到1f | | `ret` | 子程序返回指令,返回地址默认保存在LR(X30),代替了mov pc, lr | | `ldr x0, =__main` | 大范围的地址读取:把标号__main(地址)读入x0 | | `adr x0, vector` | 小范围的地址读取:把标号vector(地址)读入x0,标号距当前指令PC的偏移小于1M | | `stp x29, x30, [sp, #-16]!` | 入栈:把x29, x30存储到sp-16指向的空间后,sp自减16 | | `ldp x29, x30, [sp], #16` | 出栈:把sp指向的空间内容载入到x29, x30后,sp加16 | | `mrs x0, sctlr_el1` | 读sctlr_el1内容到x0(系统寄存器,都通过mrs msr来操作) | | `msr sctlr_el1, x0` | 写x0内容到sctlr_el1 | | `svc #2` | 系统调用指令(触发一个同步异常,CPU则会陷入EL1) | | `.global _start` | 声明`_start`为全局符号(让链接脚本能看到) | | `.quad 0x3FA0` | 在存储器中分配8个字节,初值设为0x3FA0 | | `.align 4` | 2^4 =16字节对齐 | | `.macro myAdd, x, y` | 宏函数,类似myAdd(x, y) | | `add \x, \x, \y` | 宏函数内容,执行add指令 | | `.endm` | 宏定义结束 | | `myAdd x0, x2` | 调用宏函数myAdd,等价于add x0, x0, x2 | ### 延时函数举例 ```bash .globl _start // 声明_start为全局符号 _start: // 程序入口 mov x0, #3 // 将立即数3赋值给寄存器x0 bl delay // 调用延时函数delay reset_end: // 无限循环标签reset_end b reset_end // 无条件跳转到reset_end delay: // 延时函数开始 ldr x4, =0x03 // 将立即数0x03加载到寄存器x4 loop_delay: // 循环延时标签loop_delay sub x4, x4, #1 // 将寄存器x4的值减1 cmp x4, #0 // 比较寄存器x4与0 cbz x4, delay_end // 如果x4等于0则跳转到delay_end b.ne loop_delay // 如果x4不等于0则跳转到loop_delay继续循环 delay_end: // 延时结束标签delay_end ret // 返回到调用者 ``` ```bash # 编译 make # 启动 qemu qemu-system-aarch64 -machine virt -cpu cortex-a57 -nographic -kernel start -S -s ``` tn2>给`x4`赋值`3` ![](https://img.tnblog.net/arcimg/hb/890db0a79e94444294c22336a54b7cab.png) ![](https://img.tnblog.net/arcimg/hb/653d7dded37548cd9f22609667b8d2f6.png) tn2>然后每次递减1判断为0时,通过`cbz`跳转到`delay_end`函数,否则还是通过`b.ne`循环递减。 ![](https://img.tnblog.net/arcimg/hb/78487419b89242d88d9059303e1e5c5f.png) ![](https://img.tnblog.net/arcimg/hb/23aa38cc36c04d99a54e694f09b0b699.png) ### 宏函数(macro) tn2>宏函数(macro)是在汇编代码中定义的一组指令片段,可以通过宏的名称多次调用,以避免重复代码。 下面写一个简单相加的宏函数简单举例。 ```bash .macro myAdd, x, y add \x, \x, \y .endm .globl _start _start: // 程序入口 mov x2, #1 mov x0, #2 myAdd x0, x2 // 调用myAdd宏函数 mov x0, xzr ``` ![](https://img.tnblog.net/arcimg/hb/f2a8604360184962aa0fd73b76b92eaa.png) ### 异常级别的宏函数处理 | 异常状态 | 描述 |值 | | ------------ | ------------ | ------------ | |EL0: 用户态(User Mode)|普通应用程序运行的模式,没有特权。| `0x0` | |EL1: 内核态(Kernel Mode)|操作系统内核运行的模式,具有较高特权。|`0x4`| |EL2: Hypervisor Mode|虚拟化层运行的模式,用于管理虚拟机,有最高的特权级别之一。|`0x8`| |EL3: Secure Monitor Mode|安全监控模式,通常用于安全操作,例如信任区(TrustZone)中的安全监控。|`0xC`| ```bash .macro switch_el, xreg, el1_label, el2_label // 宏函数定义,接收两个参数:寄存器xreg和标号el1_label mrs \xreg, CurrentEL // 读取当前异常级别(CurrentEL)到寄存器xreg cmp \xreg, 0x4 // 比较寄存器xreg的值是否等于0x4(EL1) b.eq \el1_label // 如果xreg等于0x4,则跳转到标号el1_label cmp \xreg, 0x8 // 比较寄存器xreg的值是否等于0x4(EL2) b.eq \el2_label // 如果xreg等于0x8,则跳转到标号el2_label .endm // 宏函数结束 .globl _start // 声明_start为全局符号 _start: // 程序入口 mov x0, #0x4 // 将立即数0x4赋值给寄存器x0 switch_el x0, 1f, 2f // 调用宏函数switch_el,判断当前EL并跳转 mov x2, #2 // 如果没有跳转,继续执行,将立即数2赋值给寄存器x2 reset_end: // 无限循环标签reset_end b reset_end // 无条件跳转到reset_end 1: // 标号1 mov x1, #0x30 // 将立即数0x30赋值给寄存器x1 b reset_end // 跳转到reset_end,进入无限循环 2: // 标号2 mov x2, #0x30 // 将立即数0x30赋值给寄存器x2 b reset_end // 跳转到reset_end,进入无限循环 ``` ![](https://img.tnblog.net/arcimg/hb/7223de6afe9d42d392ff2856bb8874ef.png) ![](https://img.tnblog.net/arcimg/hb/1751300eed794edf85e677d3d188d05f.png) tn2>虽然不能在宏函数中执行,但是我们可以通过反汇编查看它接下来需要执行的代码是宏函数里面需要执行的汇编。 ![](https://img.tnblog.net/arcimg/hb/14e75ef4e0474f139d76cf11c9ec0e81.png)