ARM 汇编基础知识

1.为什么学习汇编?

        我们在进行嵌入式 Linux 开发的时候是绝对要掌握基本的 ARM 汇编,因为 Cortex-A 芯片一
上电 SP 指针还没初始化, C 环境还没准备好,所以肯定不能运行 C 代码,必须先用汇编语言设置好 C 环境,比如初始化 DDR 、设置 SP 指针等等,当汇编把 C 环境设置好了以后才可以运行 C 代码。
        我们要编写的是 ARM 汇编,编译使用的 GCC 交叉编译器,所以我们的汇编代码要符合 GNU 语法。

 2.GNU 语法

1.语法结构

        GNU 汇编语法适用于所有的架构,并不是 ARM 独享的, GNU 汇编由一系列的语句组成,
每行一条语句,每条语句有三个可选部分:
        
label:instruction @ comment
  • label 即标号,表示地址位置,有些指令前面可能会有标号,这样就可以通过这个标号得到指令的地址,标号也可以用来表示数据地址。注意 label 后面的“:”,任何以“:”结尾的标识 符都会被识别为一个标号。
  • instruction 即指令,也就是汇编指令或伪指令。@符号,表示后面的是注释,就跟 C 语言里面的“/*”和“*/”一样,其实在 GNU 汇编文件中我们也可以使用“/*”和“*/”来注释。
  • comment 就是注释内容。

for example:

add:           @标号
MOVS R0, #0X12 @指令:设置 R0=0X12
        注意!ARM 中的指令、伪指令、伪操作、寄存器名等可以全部使用大写,也可以全部使用
小写,但是不能大小写混用

2.section 伪操作

用户可以使用 .section 伪操作来定义一个段,汇编系统预定义了一些段名:
.text 表示代码段。
.data 初始化的数据段。
.bss 未初始化的数据段。
.rodata 只读数据段。
也可以自己定义段,每个段以段名开始,以下一段名或者文件结尾结束
.section .testsection   @定义一个 testsetcion 段

3.汇编程序入口

        汇编程序的默认入口标号是_start ,不过我们也可以在链接脚本中使用 ENTRY 来指明其它
的入口点,下面的代码就是使用 _start 作为入口标号:
/*.global是伪操作,表示_start是全局标号*/
.global _start   
_start:
ldr r0, =0x12 @r0=0x12

类似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

GNU 汇编同样也支持函数,函数格式如下:

函数名 :
        函数体
        返回语句  @GNU 汇编函数返回语句不是必须的

 for example:

/* SVC 中断 */
SVC_Handler:           @函数名ldr r0, =SVC_Handler  @函数体  bx r0                 @返回语句

3.Cortex-A7 常用汇编指令

3.1 处理器内部数据传输指令

常用:MOVMRS MSR 

1.MOV指令

将数据从一个寄存器拷贝到另外一个寄存器,或者将一个立即数传递到寄 存器里面
MOV R0,R1
@将寄存器 R1 中的数据传递给 R0,即 R0=R1
MOV R0, #0X12
@将立即数 0X12 传递给 R0 寄存器,即 R0=0X12

2.MRS指令

将特殊寄存器 ( CPSR SPSR) 中的数据传递给通用寄存器,要读取特殊寄存器的数据只能使用 MRS 指令!
MRS R0, CPSR @将特殊寄存器 CPSR 里面的数据传递给 R0,即 R0=CPSR

3.MSR指令

MSR 指令和 MRS 刚好相反, MSR 指令用来将普通寄存器的数据传递给特殊寄存器,也就
是写特殊寄存器,写特殊寄存器只能使用 MSR。
MSR CPSR, R0 @将 R0 中的数据复制到 CPSR 中,即 CPSR=R0

3.2 存储器访问指令

        ARM 不能直接访问存储器,比如 RAM 中的数据, I.MX6UL 中的寄存器就是 RAM 类型 的,我用汇编来配置 I.MX6UL 寄存器的时候需要借助存储器访问指令,一般先将要配置的值写入到 Rx(x=0~12) 寄存器中,然后借助存储器访问指令将 Rx 中的数据写入到 I.MX6UL 寄存器。读取 I.MX6UL 寄存器也是一样的,只是过程相反。
        常用的存储器访问指令有两种:LDR 和 STR。
1.LDR指令
        LDR 主要用于从存储加载数据到寄存器 Rx 中,LDR 也可以将一个立即数加载到寄存器 Rx 中,LDR 加载立即数的时候要使用“ = ”,而不是“ # ”。
        在嵌入式开发中,LDR 最常用的就是读 取 CPU 的寄存器值。
例如:如 I.MX6UL 有个寄存器 GPIO1_GDIR,其地址为 0X0209C004,我们现在要读取这个寄存器中的数据,代码: 
 LDR R0, =0X0209C004 @将寄存器地址 0X0209C004 加载到 R0 中,即 R0=0X0209C004LDR R1, [R0] @读取地址 0X0209C004 中的数据到 R1 寄存器中,offset为0
2.STR指令
        LDR 是从存储器读取数据, STR 就是将数据写入到存储器中,同样以 I.MX6UL 寄存器
GPIO1_GDIR 为例:
 LDR R0, =0X0209C004 @将寄存器地址 0X0209C004 加载到 R0 中,即 R0=0X0209C004LDR R1, =0X20000002 @R1 保存要写入到寄存器的值,即 R1=0X20000002STR R1, [R0] @将 R1 中的值写入到 R0 中所保存的地址中
LDR 和 STR 都是按照进行读取和写入的,也就是操作的 32 位数据,如果要按照字节、 半字进行操作的话可以在指令“LDR ”后面加上 B H ,比如按字节操作的指令就是 LDRB 和 STRB,按半字操作的指令就是 LDRH STRH

3.3压栈和出栈指令

        我们通常会在 A 函数中调用 B 函数,当 B 函数执行完以后再回到 A 函数继续执行。要想
再跳回 A 函数以后代码能够接着正常运行,那就必须在跳到 B 函数之前将当前处理器状态保存
起来 ( 就是保存 R0~R15 这些寄存器值 ) ,当 B 函数执行完成以后再用前面保存的寄存器值恢复
R0~R15 即可。保存 R0~R15 寄存器的操作就叫做现场保护恢复 R0~R15 寄存器的操作就叫做
恢复现场在进行现场保护的时候需要进行压栈 (入栈) 操作,恢复现场就要进行出栈操作压栈
的指令为 PUSH,出栈的指令为 POPPUSH POP 是一种多存储和多加载指令即可以一次
操作多个寄存器数据他们利用当前的栈指针 SP 来生成地址 PUSH POP 的用法如表:

 例如:将R0~R3、R12寄存器压栈操作当前的 SP 指针指向 0X80000000,处理器的堆栈是向下增长的,使用的汇编代码如下:

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

压栈完成以后的堆栈如图

 由于32位处理器,每个寄存器为32位,占用4个字节,这里5个寄存器占用20个字节,转换为十六进制是0x14,需要在堆栈上分配连续的地址空间长度为0x14,

        故SP指针的位置变化为:0x800000 - 0x14 = 0x7FFFFFEC

 LR 进行压栈完成以后的堆栈模型如图:

这里使用LR寄存器来存放程序的返回地址,同理, LR寄存器(备份寄存器R14)占4个字节,

SP变化:0x7FFFFFEC-0X04 = 0X7FFFFFE8

接下来作出栈操作:

POP {LR}                @先恢复 LR
POP {R0~R3,R12}  @ 在恢复 R0~R3,R12
 出栈的就是从栈顶,也就是 SP 当前执行的位置开始,地址依次减小来提取堆栈中的数据到要恢复的寄存器列表中。

        PUSH 和 POP 的另外一种写法是“STMFD SP!”和“LDMFD SP!”。STM LDM 就是多存储和多加载,可以连续的读写存储器中的多个连续数据。

3.4跳转指令

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

一般常用的还是 BBL 或 BX

如果要在汇编中进行函 数调用使用的就是 B 和 BL 指令

1 B 指令
        这是最简单的跳转指令,B 指令会将 PC 寄存器的值设置为跳转目标地址, 一旦执行 B 指 令,ARM 处理器就会立即跳转到指定的目标地址,如果要调用的函数不会再返回到原来的执行
处,那就可以用 B 指令,例如:
  _start :                        @入口标号
 ldr sp ,= 0X80200000 @ 设置栈指针
 b main                        @跳转到 main 函数  跳转到 C 文件以后再也不会回到汇编了。
2 BL 指令
        BL 指令相比 B 指令,在跳转之前会在寄存器 LR(R14)中保存当前 PC 寄存器值所以可以 通过将 LR 寄存器中的值重新加载到 PC 中来继续从跳转之前的代码处运行这是子程序调用的一个基本但常用的手段。比如 Cortex-A 处理器的 irq 中断服务函数都是汇编写的,主要用汇编来实现现场的保护和恢复、获取中断号等。但是具体的中断处理过程都是 C 函数,所以就会存在汇编中调用 C 函数的问题。而且当 C 语言版本的中断处理函数执行完成以后是需要返回到irq 汇编中断服务函数,因为还要处理其他的工作,一般是恢复现场。这个时候就不能直接使用B 指令了,因为 B 指令一旦跳转就再也不会回来了,这个时候要使用 BL 指令。

3.5算术运算指令

掌握加法运算。

3.6逻辑运算指令

具体案例再补充。

参考:

《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.6》

ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf》和《 ARM Cortex-A(armV7) 编程手册 V4.0.pdf

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/65780.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

七、Linux中一些符号的含义和宿主目录的介绍

1、Linux中一些符号的含义 在Linux命令行中,会看到如下一些符号,含义如下。 符号含义. 代表当前目录..代表上一层目录,当前目录的父目录-代表前一个目录,刚才从哪个目录cd过来~代表当前用户的宿主目录/代表根目录$普通用户的命…

两个线程同步执行:解决乱箭穿心(STL/Windows/Linux)

C自学精简教程 目录(必读) C并发编程入门 目录 多线程同步 线程之间同步是指线程等待其他线程执行完某个动作之后再执行(本文情况)。 线程同步还可以是像十字路口的红绿灯一样,只允许一个方向的车同行,其他方向的车等待。 本…

Mac 如何判断下载Mac with Intel Chip 还是 Mac with Apple Chip

如下图,当我们在 Mac系统 下载客户端时,有两种选择:Mac with Intel Chip 、 Mac with Apple Chip 如何判断要下载哪一种? 需要判断本机Mac是在Inter芯片还是Apple芯片上运行的。方法如下: 点击屏幕左上角Apple标志&a…

DHorse v1.3.2 发布,基于 k8s 的发布平台

版本说明 新增特性 构建版本、部署应用时的线程池可配置化; 优化特性 构建版本跳过单元测试; 解决问题 解决Vue应用详情页面报错的问题;解决Linux环境下脚本运行失败的问题;解决下载Maven安装文件失败的问题; 升…

Docker技术--Docker中的网络问题

1.docker中的网络通信 如果想要弄清楚docker中的网络通信问题,其实需要弄清楚这几个问题就可以:容器与容器之间的通信、容器与外部网络之间的通信、外部网络与容器之间的通信。 -a:容器与容器之间的通信,如下所示: 在默认情况下,docker使用网桥(Bridge模式)与NAT通信。这…

已解决module ‘pip‘ has no attribute ‘pep425tags‘报错问题(如何正确查看pip版本、支持、32位、64位方法汇总)

本文摘要:本文已解决module ‘pip‘ has no attribute ‘pep425tags‘的相关报错问题,并总结提出了几种可用解决方案。同时结合人工智能GPT排除可能得隐患及错误。并且最后说明了如何正确查看pip版本、支持、32位、64位方法汇总 😎 作者介绍&…

Lesson6---案例:人脸案例

学习目标 了解opencv进行人脸检测的流程了解Haar特征分类器的内容 1 基础 我们使用机器学习的方法完成人脸检测,首先需要大量的正样本图像(面部图像)和负样本图像(不含面部的图像)来训练分类器。我们需要从其中提取特…

清理docker镜像方法

首先stop ps -a里的容器,然后rm容器,最后再rmi镜像 先停止容器 rm容器 docker rmi 镜像 删除后可以发现已经不存在

编写一个这样的程序,满足五日均线,十日均线,二十日均线,六十天六日均线调头向上的选股代码

编写一个这样的程序,满足五日均线,十日均线,二十日均线,六十天六日均线调头向上的选股代码 以下是一个用C语言编写的程序,可以读取股票数据并筛选出满足条件的股票。程序使用了一个假设的股票数据文件格式&#xff0c…

基于RabbitMQ的模拟消息队列之四——内存管理

文章目录 一、设计数据结构二、管理集合1.交换机2.队列3.绑定4.消息5.队列上的消息6.待确认消息7.恢复数据 一、设计数据结构 针对交换机、队列、绑定、消息、待确认消息设计数据结构。 交换机集合 exchangeMap 数据结构:ConcurrentHashMap key:交换机name value:交…

中心差分法-学习笔记《结构动力学-陈政清》

激励分段解析法仅仅对外载荷进行了离散,但对运动方程还是严格满足的,体系的运动在时间轴上依然是满足运动微分方程。然而,一般的时域逐步积分法进一步放松要求,不仅仅对外荷载进行离散化处理,也对体系的运动进行离散化…

Linux —— 进程信号

一,信号概念 信号是进程之间事件异步通知的一种方式,属于软中断; 系统定义的信号 每个信号都有一个编号和一个宏定义名称(可在signal.h查看);编号34以上的为实时信号; [wz192 Desktop]$ kill -…

vue的 ECMAScript 6的学习

一 ECMAScript 6 1.1 ECMAScript 6 ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现(另外的 ECMAScript 方言还有 Jscript 和 ActionScript)。 因此,ES6 既是一个历史名词,也…

windows 搭建 swoole开发环境(官网已支持)

第一步下载:swoole官网下载 swoole-cli-v5.0.3-cygwin-x64.zip 只支持 64 位的系统 第二步解压到指定文件夹:E:\phpstudy_pro\WWW\swoole-cli-v5.0.3-cygwin-x64 第三步设置环境变量:把解压后的文件夹下的 bin 目录路径配置到系统的 Path 环境…

MATLAB制图代码【第二版】

MATLAB制图代码【第二版】 文档描述 Code describtion: This code is version 2 used for processing the data from the simulation and experiment. Time : 2023.9.3 Author: PEZHANG 这是在第一版基础上,迭代出的第二版MATLAB制图代码,第二版的特点是…

文献阅读:Semantic Communications for Speech Signals

目录 论文简介动机:为什么作者想要解决这个问题?贡献:作者在这篇论文中完成了什么工作(创新点)?规划:他们如何完成工作?自己的看法(作者如何得到的创新思路) 论文简介 作者 Zhenzi Weng Zhijin Qin Geoffre…

2023 AZ900备考

文章目录 如何学习最近准备考AZ900考试,找了一圈文档,结果发现看那么多文档,不如直接看官方的教程https://learn.microsoft.com/zh-cn/certifications/exams/az-900/ ,简单直接,突然想到纳瓦尔宝典中提到多花时间进行思…

linux安装firefox

1.下载对应包 https://www.mozilla.org/en-US/firefox/all/#product-desktop-release 2. 挂载桌面链接(如果/usr/bin/firefox下有的话,先删除) ln -s /opt/firefox/firefox /usr/bin/firefox 3.执行以下命令,即可启动Firefox客户端: firefox

Windows 重新映射 CapsLock 大写锁定到 Ctrl

Windows 重新映射 CapsLock 大写锁定到 Ctrl 本要点中的这些方法适用于我的美国键盘布局。我不确定其他布局。如果出现问题,请恢复您的更改;删除您创建的注册表项(并重新启动)。 强烈推荐 方法5 ctrl2cap,因为不会影…

Linux 基础篇

很长时间,因为一些事情,没有更新我的文章这让我很惭愧,于是我将打算在今天更新下自己的文章,我发现一些事情,计算机并不是很难学到可以工作的水平,关键在于是否可以坚持下来,有很多时候我并不是…