GNU ARM 汇编基础笔记

文章目录

  • 目的
  • 模拟器 VisUAL
  • 常用指令
    • 内存访问指令
    • 通用数据处理指令
    • 分支和控制指令
    • 其它指令
  • GNU汇编语法
    • 伪指令
    • 分段
    • 标号
    • 注释
  • 与C语言混合使用
    • 汇编程序中调用C程序
    • C程序中调用汇编程序
    • C程序中内嵌汇编代码
  • 总结

目的

汇编的核心就是使用各种指令来编码完成需求,而指令这个东西其实就是最底层二进制的机器码的基础上做了一层语义化的替换,比如用 ADD 代表数字逻辑上的加减,用 MOV 代表数据传递等等,方便程序员进行阅读和编写。不过对于现在的各种高级语言而言汇编已经不怎么方便了。现在还在用汇编的主要是底层开发中一些特定需求的实现必须用到汇编。对于嵌入式开发而言了解汇编还是有一定需求的。

汇编主要是用各种指令来完成功能的编写,不同的处理器而言其架构和使用的指令集不同,不同的编译器下具体的语法差异也蛮大,但基础的语法思路和常用的指令基本上大家差异都不大。这篇文章将以 GNU ARM 汇编 为基础进行介绍。

模拟器 VisUAL

GNU ARM 汇编基础笔记_第1张图片

VisUAL has been developed as a cross-platform tool to make learning ARM Assembly language easier. In addition to emulating a subset of the ARM UAL instruction set, it provides visualisations of key concepts unique to assembly language programming and therefore helps make programming ARM assembly more accessible.

VisUAL是一个跨平台的可视化ARM汇编模拟器,可以方便的学习和演示使用ARM汇编指令。官方主页如下:
https://salmanarif.bitbucket.io/visual/index.html

VisUAL中用户可以访问的主要是 R0 ~ R13SPLRPC 这几个寄存器,以及内存地址 0x00010000 ~ 0xFFFFFFFC 这些区域。对于ARM来说 SP 是堆栈指针、 LR 用来存放返回地址、 PC 存放正在取的指令。
在这里插入图片描述
VisUAL模拟支持了部分常用的指令指令列表可以在下面找到:
https://salmanarif.bitbucket.io/visual/supported_instructions.html
VisUAL中可以使用 Ctrl + Space 显示当前行指令的详细语法说明:

常用指令

每种架构下其指令可能有几十条到上百条,这里只介绍些常用的指令。

内存访问指令

指令 说明
ADR 取相对于PC的地址到寄存器
LDR 取数据到寄存器
LDM 批量取数据到寄存器
STR 将寄存器上的数据保存到内存
STM 批量到寄存器数据内存
PUSH 数据入栈
POP 数据出栈


GNU ARM 汇编基础笔记_第2张图片

GNU ARM 汇编基础笔记_第3张图片

通用数据处理指令

指令 功能
ADD 加法
SUB 减法
AND 按位与
ORR 按位或
EOR 按位异或
BIC 按位清零
LSL 逻辑左移
LSR 逻辑右移
CMP 比较
CMN 源数据取反后比较
TST 测试
MOV 移动数据
MVN 源数据取反后移动

GNU ARM 汇编基础笔记_第4张图片
GNU ARM 汇编基础笔记_第5张图片
GNU ARM 汇编基础笔记_第6张图片
GNU ARM 汇编基础笔记_第7张图片
GNU ARM 汇编基础笔记_第8张图片

分支和控制指令

指令 功能
B 跳转
BL 跳转并将返回地址保存到LR寄存器中

GNU ARM 汇编基础笔记_第9张图片
GNU ARM 汇编基础笔记_第10张图片

其它指令

指令 功能
DCD 数据定义
END 程序结束

GNU汇编语法

VisUAL只能用来演示指令,下面的一些内容在VisUAL中可能无法正常使用。

伪指令

在汇编代码中经常可以见到以 . 开头的字段,这些字段都是伪指令。伪指令只是一些标识信息。下面是一部分常见的伪指令:

伪指令 描述
.arm 声明接下来的程序使用ARM指令集
.thumb 声明接下来的程序使用Thumb指令集
.arch 声明目标架构
.global 全局声明标志
.local 局部声明标志
.weak 弱声明相当于C语言中的__weak
.section 段标记
.ascii 定义一串字符串
.asciz .string 定义一串字符串,自动补充结束符
数据将在连续的空间上
.byte 声明一个字节变量
多个变量可以一次性声明,使用逗号分隔
变量将在连续的空间上
.hword 声明一个2字节变量
.word 声明一个4字节变量
.fill 重复填充数据
.set .equ 给符号(变量)复制
语法:.set/.equ symbol, expression
.type 设置一个符号的属性值
语法:.type name , description
description取值如下:
%function 表示该符号用来表示一个函数名
%object表示该符号用来表示一个数据对象
.balign 数据对齐指令
.align 数据对齐指令
.macro .endm 宏定义
.if .else .endif 条件编译

分段

使用.section伪指令可以对程序进行分段,比如下面这样:

.section .bss  @未初始化数据段
.section .data @已初始化数据段
.section .text @可执行代码段(程序段)

当然上面的 .bss .data .text 这些都是汇编中已经定义的段,使用的时候可以省略前面的 .section 。

标号

在汇编代码中以 : 结尾的字段是标号,标号有点类似于C语言中的函数名,可以使用标号来跳转调用等。

_start 标号是一个特殊的标号,是程序的入口点,相当于C语言中的main函数。

注释

在汇编中使用 @ 符号表示注释,其后面的内容为注释内容。有点编译器中你也可以使用 /* 注释内容 */ 的方式进行注释

与C语言混合使用

从这里开始的演示均在树莓派4B中进行,理论上只要CPU是ARM架构的电脑上装个主流的Linux发行版就可以进行测试。

汇编程序中调用C程序

准备下面程序保存为 main.c 文件:

#include 
#include  

void main(void)
{
     
	printf("Hello world!\n");
	exit(0);
}

准备下面程序保存为 start.s 文件:

.text
.global _start @ 使_start标号全局可见
_start:        @ _start标号是程序入口
	bl main    @ 跳转到main函数

GNU ARM 汇编基础笔记_第11张图片
汇编程序中调用C程序直接使用函数名就行,如果函数的传入参数不大于四个则依次把参数放入R0~R3寄存器即可(如果参数是double或是long类型的话一个参数会占用连续的两个寄存器):
GNU ARM 汇编基础笔记_第12张图片
如果函数的传入参数大于四个,那么多出来的那些参数需要放入堆栈中,可以编写C程序然后使用gcc -S编译成汇编文件查看具体的处理方式。

函数如果有返回值,返回值会放入R0寄存器(如果是double或是long类型的会放入R0和R1)。

C程序中调用汇编程序

准备下面程序保存为 fun.s 文件:

.text
.global fun        @ 使fun这个符号(函数名)外部可见
fun: 
	add r4, r0, r1 @ 将传入的两个参数相加
	mov r0, r4     @ 装载返回值
	mov pc, lr     @ 函数返回

准备下面程序保存为 main.c 文件:

#include 

extern int fun(int a, int b); // 声明fun函数,该函数功能在上面汇编代码中实现

void main(void)
{
     
	printf("%d\n", fun(200, 33));
}

GNU ARM 汇编基础笔记_第13张图片
汇编中的符号就相当于C语言的函数名,把这个符号使用 .global 声明为全局的就可以被其它文件使用了。

C程序中内嵌汇编代码

使用 __asm 指令可以在C程序中直接内嵌汇编代码,当然这样使用的汇编代码部分有很多功能限制。

总结

相比于很多高级语言来说,汇编的语法其实很简单,更多内容可以参考下面连接:

《GNU ARM Assembler Quick Reference》
链接: https://pan.baidu.com/s/1VS7HgKtDxH0OMmkiGPZBEA 提取码: grw3

《ARM and Thumb-2 Instruction Set Quick Reference Card》
链接: https://pan.baidu.com/s/1PcSYCf7–mlEtOlqGoRnUg 提取码: ksg3

《C语言与汇编语言之间的函数调用》 - 纫秋兰以为佩

你可能感兴趣的:(编程相关,arm,汇编,嵌入式,指令,语法)