当前位置: 首页 > article >正文

ARM 汇编基础总结

GNU 汇编语法

编写汇编的过程中,其指令、寄存器名等可以全部使用大写,也可以全部使用小写,但是不能大小写混用。

1. 汇编语句的格式

label: instruction @ comment

  • label即标号,表示地址位置,有些指令前面可能会有标号,这样就可以通过这个标号得到指令的地址,标号也可以用来表示数据地址。注意 label 后面的" : ",任何以" : "结尾的标识符都会被识别为一个标号。在汇编语言中,_start是一个特殊的标号(label),_start是程序执行的入口点,程序执行的起始地址,当程序加载到内存中时,处理器会跳转到_start标签所在的地址,从这里开始执行代码。_start主要作用是初始化程序运行环境,比如栈、全局变量、动态链接环境,然后跳转到用户定义的main函数执行。
  • instruction即指令,也就是汇编指令或伪指令
  • @表示后面的是注释,跟 C 语言的“/*”和“*/”一样,在 GNU 汇编文件中也可以使用“/*”和“*/”来注释
  • comment就是注释内容
2. 预定义段名

.section 伪操作,用于指定汇编代码或数据应当放入哪个段, 或者定义自己的自定义段,每个段以段名开始,以下一段名或者文件结尾结束

.text 表示代码段

.data 初始化的数据段

.bss 未初始化的数据段

.rodata 只读数据段

.byte 定义单字节数据,比如.byte 0x12

.equ 赋值语句,比如.equ num,0x12,表示num=0x12

.short 定义双字节数据,比如.short 0x1234

.long 定义一个 4字节数据,比如.long 0x12345678

.align 数据字节对齐,比如.align4表示4字节对齐

.end 表示源文件结束

.global 定义一个全局符号,比如.global _start

.section .data         // 数据段
message:
    .asciz "Hello, World!"  // 字符串常量

.section .text         // 代码段
.global _start         // 声明程序入口
_start:
    mov eax, 4         // 系统调用号,写操作
    mov ebx, 1         // 文件描述符(1是标准输出)
    lea ecx, [message] // 将字符串地址加载到 ecx
    mov edx, 13        // 要写入的字节数
    int 0x80           // 调用内核

    mov eax, 1         // 系统调用号,退出程序
    xor ebx, ebx       // 返回值为0
    int 0x80           // 调用内核

Cortex-A7 常用汇编指令

处理器内部数据传输指令

数据传输常用的指令有三个: MOV、 MRS 和 MSR。

指令

目标寄存器

源寄存器

功能

MOV R1, R0

R0

R1

将 R1 里面的数据复制到 R0 中

MRS R0, CPSR

R0

CPSR

将特殊寄存器 CPSR 里的数据复制到 R0

MSR CPSR, R1

CPSR

R1

将 R1 里面的数据复制到特殊寄存器 CPSR

1. MOV(Move)

MOV 指令将一个立即数或一个寄存器的值赋值到另一个寄存器中,只能操作通用寄存器,不能直接访问系统状态寄存器(CPSR)。

MOV R0, #0x1234 //将立即数 0x1234 赋值到寄存器 R0, 即R0 = 0X1234
MOV R1, R0 		//将寄存器 R0 的值复制到寄存器 R1, 即R1 = R0
2. MRS(Move Register from System)

MRS 指令将系统状态寄存器中的值读取到通用寄存器中。

MRS R0, CPSR //将系统状态寄存器 (CPSR) 的值存入 R0, 即R0=CPSR
MRS R1, SPSR //将系统状态寄存器 (SPSR) 的值存入 R1, 即R0=SPSR
3. MSR(Move System Register)

MSR 指令将系统状态寄存器中的值读取到通用寄存器中。

MSR SPSR, R0    //将 R0 的值写入 SPSR 系统寄存器中, 即SPSR=R0

存储器访问指令

常用的存储器访问指令有两种: LDR 和 STR 在编写汇编驱动的时候最常用这两个指令。在 32 位处理器中 LDR 和 STR 都是操作 4 个字节的地址,也就是 32 位数据。

指令

功能

LDR Rd, [Rn, #offset]

从 Rn+offset 的地址读取数据存到 Rd 中

STR Rd, [Rn, #offset]

将 Rd 寄存器中的数据写入到 Rn+offset 地址

1. LDR(Load Register)

LDR 指令用于从内存中加载一个值到寄存器,[ R1 ] 表示以 R1 存储的数字为地址。

LDR R0, = 0x20000000 //将立即数 0x20000000 加载到寄存器 R0 中

//如果地址 0x20000000 存储的值是 0x1234, 则执行后 R0 = 0x1234
LDR R0, [R1]     //将内存地址 R1 指向的数据加载到寄存器 R0 中

//带偏移量的加载, 如果地址 0x20000004 存储的值是 0x6789, 则执行后 R0 = 0x6789
LDR R0, [R1, #4] //将 R1 + 4 的地址处的数据加载到 R0 中
2. STR(Store Register)

STR 指令用于将寄存器中的值存储到内存地址中。

STR R0, [R1]     //将 R0 的值存储到内存地址 R1 指向的地址
STR R0, [R1, #4] //将 R0 的值存储到 R1 + 4 的地址处

栈指针

SP 指针(Stack Pointer,栈指针)是计算机体系结构中一个重要的寄存器 ,在不同的处理器架构中,SP寄存器是硬件层面预定义的,处理器会专门保留一个寄存器用来表示栈指针。在 ARM 架构中 R13 是专门的寄存器,SP是它的别名。SP指向栈的当前顶部或下一条可用地址。

1. 栈的应用

在程序执行过程中,栈通常用于管理函数调用、局部变量和保存寄存器内容等任务。当函数被调用时,局部变量、返回地址、保存的寄存器等会被压入栈中,SP指针会自动调整,指向栈的最新位置,当函数返回时,这些数据会被弹出,SP指针恢复到调用前的位置。

2. 栈指针位置

SP设置栈指针(C 语言运行的时候需要出栈和入栈,所以需要栈内存),SP指针可以指向内部 RAM 也可以指向 DDR,以指向 DDR 为例,假设 DDR 的大小是 512MB,起始地址为 0x80000000,那么 DDR 的地址范围是 0x8000_0000~0x9FFF_FFFF,设置栈大小为 2MB=0x0020_0000(对于裸机运行已经够大了),栈的增长方式是向下增长,即高地址向低地址增长,所以设置栈地址为 0x8020_0000,保证栈不会溢出。

.global _start          // 声明程序入口
_start:
    ldr sp, =0X80200000  // 设置栈指针的起始地址
b main 				          //跳转到main函数

压栈和出栈指令

常用的栈操作指令是 PUSH 和 POP 。压栈和出栈指令通常用于保存和恢复寄存器的值,或者用于函数调用的返回地址。

1. PUSH(压栈)

PUSH 指令将寄存器的值压入栈中,并自动更新栈指针。它是对栈操作的简化形式,常用于保存寄存器的值。例如: PUSH {R0, R1, R2, R3}

2. POP(出栈)

POP 指令从栈中弹出寄存器的值,恢复寄存器的内容,并自动更新栈指针。例如:POP {R0, R1, R2, R3}

在压栈出栈的过程中,要保证先进后出的特性,最后压入栈中的寄存器最先出栈。例如,压栈的顺序为R0 -> R1 -> R2 -> R3,那么出栈的顺序为R3 -> R2 -> R1 -> R0。下面以中断场景举例压栈和出栈,在中断服务例程中,保存和恢复多个寄存器的值也非常常见。由于中断会打断程序的正常执行流程,所以需要保存现场,确保中断处理完成后能够正确地恢复执行。

ISR_Handler:
  PUSH {R0, R1, R2, R3}   // 保存现场, 保存 R4, R5, R6, R7 到栈中
  //中断处理逻辑
  POP {R0, R1, R2, R3}    // 恢复现场, 恢复 R4, R5, R6, R7
  BX LR                   // 返回到调用者

跳转指令

常用的跳转指令有 B 和 BK,其中 B 指令跳转后不会回到原来位置,BK 跳转后待函数执行完还会回到原来位置。

1. B(Branch)

B 指令这是最简单的跳转指令, B 指令会将 PC 寄存器的值设置为跳转目标地址, 一旦执行 B 指令, ARM 处理器就会立即跳转到指定的目标地址。如果要调用的函数不会再返回到原来的执行处,那就可以用 B 指令,如下示例

_start:
ldr sp, =0X80200000 //设置栈指针
b main             //跳转到 main 函数

//用 B 指令实现 loop 死循环
loop:
  b loop
2. BL(Branch with Link )

BL 指令相比 B 指令的区别是 BL 会跳转到指定函数,待函数执行完再返回原来位置继续执行,因为在跳转之前会在寄存器 LR(R14)中保存当前 PC 寄存器值,所以可以通过将 LR 寄存器中的值重新加载到 PC 中来继续从跳转之前的代码处运行,这是子程序调用一个基本但常用的手段。比如 Cortex-A 处理器的 irq 中断服务函数都是汇编写的,主要用汇编来实现现场的保护和恢复、获取中断号等。但是具体的中断处理过程都是 C 函数,所以就会存在汇编中调用 C 函数的问题。

PUSH {r0, r1}        //保存 r0,r1
CPS #0x13            //进入 SVC 模式,允许其他中断再次进去
BL system_irqhandler //加载 C 语言中断处理函数到 r2 寄存器中
CPS #0x12            //进入 IRQ 模式
POP {r0, r1}
STR r0, [r1, #0X10]  //中断执行完成,写 EOIR

算术运算指令

常用加法、减法,乘除很少用

指令

计算公式

说明

ADD Rd, Rn, Rm

Rd = Rn + Rm

加法运算,指令为 ADD

ADD Rd, Rn, #immed

Rd = Rn + #immed

ADC Rd, Rn, Rm

Rd = Rn + Rm + 进位

带进位的加法运算,指令为 ADC

ADC Rd, Rn, #immed

Rd = Rn + #immed +进位

SUB Rd, Rn, Rm

Rd = Rn – Rm

减法

SUB Rd, #immed

Rd = Rd - #immed

SUB Rd, Rn, #immed

Rd = Rn - #immed

SBC Rd, Rn, #immed

Rd = Rn - #immed – 借位

带借位的减法

SBC Rd, Rn ,Rm

Rd = Rn – Rm – 借位

MUL Rd, Rn, Rm

Rd = Rn * Rm

乘法(32 位)

UDIV Rd, Rn, Rm

Rd = Rn / Rm

无符号除法

SDIV Rd, Rn, Rm

Rd = Rn / Rm

有符号除法

在 CPU 运行过程中,如果想执行运算操作,必须要先将数据加载到寄存器中,然后才能执行运算操作!

// 实现 DDR 两个地址数据相加
LDR R0, [#0x20000000]   // 从地址 0x20000000 加载数据到 R0
LDR R1, [#0x20000004]   // 从地址 0x20000004 加载数据到 R1
ADD R2, R0, R1          // 将 R0 和 R1 的值相加,结果存入 R2

// 实现两个立即数相加
MOV R0, #0x1          // 将立即数 0x1 加载到 R0
MOV R1, #0x2          // 将立即数 0x2 加载到 R1
ADD R2, R0, R1        // 将 R0 和 R1 的值相加,结果存入 R2

逻辑运算指令

指令

计算公式

说明

AND Rd, Rn

Rd = Rd &Rn

按位与

AND Rd, Rn, #immed

Rd = Rn &#immed

AND Rd, Rn, Rm

Rd = Rn & Rm

ORR Rd, Rn

Rd = Rd | Rn

按位或

ORR Rd, Rn, #immed

Rd = Rn | #immed

ORR Rd, Rn, Rm

Rd = Rn | Rm

BIC Rd, Rn

Rd = Rd & (~Rn)

位清除

BIC Rd, Rn, #immed

Rd = Rn & (~#immed)

BIC Rd, Rn , Rm

Rd = Rn & (~Rm)

ORN Rd, Rn, #immed

Rd = Rn | (#immed)

按位或非

ORN Rd, Rn, Rm

Rd = Rn | (Rm)

EOR Rd, Rn

Rd = Rd ^ Rn

按位异或

EOR Rd, Rn, #immed

Rd = Rn ^ #immed

EOR Rd, Rn, Rm

Rd = Rn ^ Rm


http://www.kler.cn/a/467553.html

相关文章:

  • 鸿蒙NEXT使用request模块实现本地文件上传
  • Cyber Security 101-Web Hacking-Burp Suite: The Basics(Burp Suite:基础知识)
  • 《数据结构》期末考试测试题【中】
  • JavaScript系列(8)-- Array高级操作
  • CANFD芯片在商业航天的应用
  • Android中创建ViewModel的几种方法
  • 语义搜索、语义数据搜索
  • 折叠手机市场出现崩塌迹象,三星做了努力,将推低价折叠手机
  • GO语言学习(一:背景与代码初始)
  • Nginx:SSL/TLS 配置
  • HTML5 动画效果:淡入淡出(Fade In/Out)详解
  • AI 热潮背后的沉重:解读 DeepMind 天才科学家 Felix Hill 的心声
  • WebGL 实践(一)开发环境搭建
  • Json与jsoncpp
  • 使用 Jupyter Notebook:安装与应用指南
  • 深度学习中的步数指的是什么
  • 【含开题报告+文档+PPT+源码】基于SpringBoot的线上动物园售票系统设计
  • Python 基于 opencv 的人脸识别监控打卡系统(源码+部署)
  • RocketMQ消费者如何消费消息以及ack
  • 继承(3)
  • Xilinx Vivado环境下载bit后自动触发ILA采集
  • TradingView功能
  • Chain of Agents(COA):大型语言模型在长文本任务中的协作新范式
  • 【Go学习】-01-5-网络编程
  • 前端网站部署遇到的问题
  • 牛客网刷题 ——C语言初阶(5操作符)——BC111 小乐乐与进制转换