首页 > 编程语言 >arm汇编

arm汇编

时间:2023-09-16 14:34:25浏览次数:48  
标签:汇编 R0 SP 地址 指令 寄存器 r0 arm

1 GNU 汇编格式

label:instruction @ comment

label 即标号,表示地址位置,有些指令前面可能会有标号,这样就可以通过这个标号得到指令的地址,标号也可以用来表示数据地址。注意 label 后面的“:”,任何以“:”结尾的标识符都会被识别为一个标号。
instruction 即指令,也就是汇编指令或伪指令。
@符号,表示后面的是注释,就跟 C 语言里面的“/”和“/”一样,其实在 GNU 汇编文件中我们也可以使用“/*”和“*/”来注释。
comment 就是注释内容。

add:
MOVS R0, #0X12 @设置 R0=0X12

注意: ARM 中的指令、伪指令、伪操作、寄存器名等可以全部使用大写,也可以全部使用小写,但是不能大小写混用

1.1伪操作

1.1.1 .section

来定义一个段,汇编系统预定义了一些段名:

.text 表示代码段。
.data 初始化的数据段。
.bss 未初始化的数据段。
.rodata 只读数据段。

定义一个 testsetcion 段

.section .testsection

汇编程序的默认入口标号是_start,不过我们也可以在链接脚本中使用 ENTRY 来指明其它的入口点。

1.1.1 .global

.global _start
_start:
ldr r0, =0x12 @r0=0x12

.global 是伪操作,表示_start 是一个全局标号,类似 C 语言里面的全局变量一样,常见的伪操作有:

.byte 定义单字节数据,比如.byte 0x12。
.short 定义双字节数据,比如.short 0x1234。
.long 定义一个 4 字节数据,比如.long 0x12345678。
.equ 赋值语句,格式为:.equ 变量名,表达式,比如.equ num, 0x12,表示 num=0x12。
.align 数据字节对齐,比如:.align 4 表示 4 字节对齐。
.end 表示源文件结束。
.global 定义一个全局符号,格式为:.global symbol,比如:.global _start。

1.2 函数定义

函数名:
	函数体
	返回语句

GNU 汇编函数返回语句不是必须的,如下代码就是用汇编写的Cortex-A7 中断服务函数:

/* 未定义中断 */
Undefined_Handler:
	ldr r0, =Undefined_Handler
	bx r0
/* SVC 中断 */
SVC_Handler:
	ldr r0, =SVC_Handler
	bx r0
/* 预取终止中断 */
PrefAbort_Handler:
	ldr r0, =PrefAbort_Handler 
	bx r0

以函数 Undefined_Handler 为例我们来看一下汇编函数组成,“Undefined_Handler”就是函数名,“ldr r0, =Undefined_Handler”是函数体,“bx r0”是函数返回语句,“bx”指令是返回指令,函数返回语句不是必须的.

2 ARMv7汇编指令

2.1 数据移动指令

数据移动指令都是cpu内部寄存器之间的数据拷贝。

2.1.1 MOV

MOV R0,R1 @将寄存器 R1 中的数据传递给 R0,即 R0=R1
MOV R0, #0X12 @将立即数 0X12 传递给 R0 寄存器,即 R0=0X12

2.1.2 MRS

读取特殊寄存器的数据只能使用 MRS 指令:

MRS R0, CPSR @将 CPSR 里面的数据传递给 R0,即 R0=CPSR

2.1.3 MSR

和 MRS 刚好相反,通用寄存器写入到特殊寄存器

MSR CPSR, R0 @将 R0 中的数据复制到 CPSR 中,即 CPSR=R0

2.2 数据存取指令(访问存储器RAM)

2.2.1 LDR

数据加载指令,从指定地址读取到cpu寄存器。

LDR R0, =0X0209C004 @将寄存器地址 0X0209C004 加载到 R0 中,即 R0=0X0209C004
LDR R1, [R0] @读取地址 0X0209C004 中的数据到 R1 寄存器中

2.2.2 STR

数据存放指令,从cpu寄存器写入指定地址。

LDR R0, =0X0209C004 @将寄存器地址 0X0209C004 加载到 R0 中,即 R0=0X0209C004
LDR R1, =0X20000002 @R1 保存要写入到寄存器的值,即 R1=0X20000002
STR R1, [R0] @将 R1 中的值写入到 R0 中所保存的地址中

LDR 和 STR 都是按照4 byte进行读取和写入的,也就是操作的 32 位数据,如果要按照字节、半字进行操作的话可以在指令“LDR”后面加上 B 或 H,比如按字节操作的指令就是 LDRB 和STRB,按半字(16位)操作的指令就是 LDRH 和 STRH。

2.3 入栈出栈指令

函调调用过程中离不开现场的保护和恢复。保存 R0~R15 寄存器的操作就叫做现场保护,恢复 R0~R15 寄存器的操作就叫做恢复现场。

2.3.1 PUSH

比如要将 R0~R3 和 R12 这 5 个寄存器压栈,当前的 SP (stack pointer)指针指向 0X80000000,我们知道栈空间的地址是向下增长的,堆空间地址向上增长。

PUSH {R0~R3, R12} @将 R0~R3 和 R12 压栈

那么压栈完成以后的堆栈如下:入栈保护现场完这5个寄存器后,SP指向0X7FFFFFEC(每压栈一个寄存器,SP地址减4)
image
再次保存LR寄存器,进行压栈:
image

PUSH {LR} @将 LR 进行压栈

2.3.2 POP

POP {LR} @先恢复 LR
POP {R0~R3,R12} @在恢复 R0~R3,R12

可以看出入栈出栈本质都是对SP指针进行加减,入栈减,出栈加,入栈把寄存器依次保存进SP指向的地址去,出栈从SP地址依次取出数据。

2.3.3 STMFD和LDMFD

入栈出栈的另外一种写法是“STMFD SP!”和“LDMFD SP!”。

STMFD SP!,{R0~R3, R12} @R0~R3,R12 入栈
STMFD SP!,{LR} @LR 入栈
bl xxx
LDMFD SP!, {LR} @先恢复 LR
LDMFD SP!, {R0~R3, R12} @再恢复 R0~R3, R12

STMFD 可以分为两部分:STM 和 FD,同理,LDMFD 也可以分为 LDM 和 FD。前面我们讲了 LDR 和 STR,这两个是数据加载和存储指令,但是每次只能读写存储器中的一个数据。STM 和 LDM 就是多存储和多加载,可以连续的读写存储器中的多个连续数据。
FD 是 Full Descending 的缩写,即满递减的意思。根据 ATPCS 规则,ARM 使用的 FD 类型的堆栈,SP 指向最后一个入栈的数值,堆栈是由高地址向下增长的,也就是前面说的向下增长的堆栈,因此最常用的指令就是 STMFD 和 LDMFD。STM 和 LDM 的指令寄存器列表中编号小的对应低地址,编号高的对应高地址.

2.4 跳转指令

2.4.1 B 指令

B 指令会将 PC 寄存器的值设置为跳转目标地址,如果要调用的函数不会再返回到原来的执行处,那就可以用 B 指令.

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

在汇编中初始化 C 运行环境,然后跳转到 C 文件的 main 函数中运行,上述代码只是初始化了 SP 指针,有些处理器还需要做其他的初始化,比如初始化 DDR 等等.

2.4.2 BL 指令

有返回的跳转,跳转之前会在寄存器 LR(R14)中保存当前 PC 寄存器值,所以可以通过将 LR 寄存器中的值重新加载到 PC 中来继续从跳转之前的代码处运行,这是子程序调用一个基本但常用的手段。
比如 Cortex-A 处理器的 irq 中断服务函数都是汇编写的,主要用汇编来实现现场的保护和恢复、获取中断号等。但是具体的中断处理过程都是 C 函数,所以就会存在汇编中调用 C 函数的问题。而且当 C 语言版本的中断处理函数执行完成以后是需要返回到irq 汇编中断服务函数,因为还要处理其他的工作,一般是恢复现场。

push {r0, r1} @保存 r0,r1
cps #0x13 @进入 SVC 模式,允许其他中断再次进去

bl system_irqhandler @加载 C 语言中断处理函数到 r2 寄存器中

cps #0x12 @进入 IRQ 模式
pop {r0, r1} 
str r0, [r1, #0X10] @中断执行完成,写 EOIR

跳转指令总结:
有多种跳转操作,比如:
①、直接使用跳转指令 B、BL、BX 等。
②、直接向 PC 寄存器里面写入数据。
image

2.5 算数运算指令

加减乘除,常用的运算指令用法:
image

2.6 逻辑运算指令

与或非指令用法:
image

3 参考链接

https://developer.arm.com/documentation/ddi0406/cd?lang=en
image

https://developer.arm.com/documentation/den0013/d/?lang=en
image

标签:汇编,R0,SP,地址,指令,寄存器,r0,arm
From: https://www.cnblogs.com/fuzidage/p/17703733.html

相关文章

  • 在Ubuntu20.0下搭建CUDA、cuDNN、Anaconda、pycharm
    其他链接1.buntu18.04下搭建CUDA、cuDNN、Anaconda、tensorflow1.15、Pycharm、ros、Cl2.pip使用中科大源、清华源或修改默认源为中科大源、清华源安装照着链接1的方法就可以下好cuda、cudnn和anaconda1.先装驱动,检验驱动nvidia-smi出现这个页面就成功了2.安装cuda,检验c......
  • Pycharm远程连接到服务器运行错误can‘t open file ‘tmp
    Pycharm远程连接到服务器运行错误can‘topenfile‘/tmp/.../a.py‘:[Errno2]Nosuchfileordirectory问题描述win11Pycharm+Linux服务器,运行代码后显示上述错误。即linux环境中没有xx文件。分析:python默认到/tmp/下去找代码并执行,但x.py文件根本不再tem路径下。即......
  • Python集成开发环境IDE-Pycharm 2023 win+mac版
    PyCharm2023是一种流行的集成开发环境(IDE),专门为Python开发人员设计.→→↓↓载Pycharm2023mac/winPyCharm2023版提供了强大的代码编辑器,支持智能代码完成、代码分析、代码重构等功能。它还可以自动检测错误并提供修复建议。PyCharm的调试器非常强大,可以帮助开发人员诊断和......
  • HarmonyOS应用开发Web组件基本属性应用和事件
    一、Web组件概述Web组件用于在应用程序中显示Web页面内容,为开发者提供页面加载、页面交互、页面调试等能力。● 页面加载:Web组件提供基础的前端页面加载的能力,包括加载网络页面、本地页面、Html格式文本数据。● 页面交互:Web组件提供丰富的页面交互的方式,包括:设置前端页面深色模......
  • HarmonyOS应用侧与前端页面数据通道建立
    一、 应用侧调用前端页面函数应用侧可以通过runJavaScript()方法调用前端页面的JavaScript相关函数。在下面的示例中,点击应用侧的“runJavaScript”按钮时,来触发前端页面的htmlTest()方法。● 前端页面代码。<!--index.html--><!DOCTYPEhtml><html><body><script>......
  • Qt For OpenHarmony
    本文转载自 OpenHarmony TSC 官方微信公众号《峰会回顾第29期 | Qt For OpenHarmony》演讲嘉宾 | 蔡万苍回顾整理 | 廖   涛排版校对 | 李萍萍嘉宾简介蔡万苍,13年C++/Qt开发相关工作经验,曾任职Qt公司,担任技术支持、Qt咨询师,协助国内多家Qt企业版用户完成基于Qt的软......
  • OpenHarmony系统能力SystemCapability使用指南
    一、概述1、系统能力与 APISysCap,全称SystemCapability,即系统能力,指操作系统中每一个相对独立的特性,如蓝牙,WIFI,NFC,摄像头等,都是系统能力之一。每个系统能力对应多个API,随着目标设备是否支持该系统能力共同存在或消失,也会随着DevEco Studio一起提供给开发者做联想。开发者可以在[S......
  • HarmonyOS/OpenHarmony应用开发-DevEco Studio新建项目的整体说明
    一、文件-新建-新建项目二、传统应用形态与IDE自带的模板可供选用与免安装的元服与IDE中自带模板的选择三、以元服务,远程模拟器为例说明IDE整体结构1区是工程目录结构,是最基本的配置与开发路径等的认知。2区是代码开发与修改区,是开发者主要的工作区域。3.是效果展示区,包括远程模拟......
  • Xines广州星嵌全新FPGA开发板—OMAPL138/C6748 DSP+ARM+FPGA
    1  开发板简介    XQ138F-EVM是一款基于广州星嵌TIOMAP-L138(浮点DSPC6748+ARM9)+XilinxSpartan-6FPGA核心板SOM-XQ138F设计的开发板,它为用户提供了SOM-XQ138F核心板的测试平台,用于快速评估SOM-XQ138F核心板的整体性能。 XQ138F-EVM底板采用沉金无铅工艺的四层板设......
  • openharmony应用开发HDC 常用命令
    1.查看hdc版本:hdc--version 2.查看设备sn号:hdclisttargets 3.进入shell:hdcshell 4.重启设备hdcshellreboot 5.设置RK开发板根目录的读写权限hdcshellmount-orw,remount/#此命令用于解决推包时提示只读路径,无法推送。例如:[Fail]Erroropeningfile:......