购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.4 GNU汇编语言的基本语法

能够在MCU内直接运行的指令序列是机器语言,用助记符号来表示机器指令便于记忆,这就形成了汇编语言。由于采用汇编语言编写的程序不能直接在MCU的程序存储器中运行,所以必须先将程序转为机器语言。把用汇编语言编写的源程序“翻译”成机器语言的工具称为汇编程序或汇编器(Assembler),以下统一称为汇编器。

本书给出的所有样例程序均是在所使用的开发环境下实现的,默认使用 GNU 汇编器(Cross ARM GNU Assembler),汇编语言格式满足GNU汇编语法,下面简称ARM-GUN汇编 。为了有助于解释涉及的汇编指令,下面将介绍一些汇编语言的基本内容。

2.4.1 ARM-GUN汇编语言格式

汇编语言源程序可以用通用的文本或软件进行编辑,并以ASCII码的形式保存。不同的汇编器对汇编语言源程序的格式有一定的要求,同时,汇编器除识别 MCU 的指令系统外,为了能够正确地产生目标代码以及方便汇编语言程序的编写,汇编器还提供了一些在汇编时使用的命令、操作符号,在编写汇语言编程序时,也必须正确使用它们。由于汇编器提供的指令仅是为了更好地完成“翻译”工作,并不产生具体的机器指令,因此这些指令称为伪指令(Pseudo Instruction)。例如,伪指令会告诉编译器从哪里开始编译,到何处结束,汇编后的程序如何放置等相关信息。当然,这些相关信息必须包含在汇编语言源程序中,否则编译器就难以编译好源程序,难以生成正确的目标代码。

汇编语言源程序以行为单位进行编写,每一行最多可以包含以下几个部分:

标号:操作码 操作数1,操作数2… 注释

1.标号(Label)

标号可以确定代码当前位置的程序计数器(PC)的值,标号有以下要求及说明:

(1)如果一个语句有标号,则标号必须在汇编语句的开头部分。

(2)标号可以由任何有效字符和冒号组成。有效字符包含:字母 A~Z、字母 a~z、数字0~9、下画线“_”、符号“$”,但开头的第一个字符不能是数字和“$”。

(3)汇编器对标号中字母的大小写敏感,但不区分指令中字母的大小写。

(4)标号长度基本上不受限制,但实际使用时通常不要超过20个字符。若希望更多的汇编器能够识别,建议标号(或变量名)的长度小于8个字符。

(5)标号后必须带冒号“:”。

(6)一个标号在一个文件(程序)中只能定义一次,重复定义会导致无法通过编译。

(7)一行语句只能有一个标号,汇编器会将当前程序计数器的值赋给该标号。

2.操作码(Opcode)

操作码包括指令码或伪指令,其中伪指令是指汇编器可以识别的伪指令。对于有标号的行,必须用至少一个空格或制表符(TAB)将标号与操作码隔开。对于没有标号的行,不能从第一列开始写操作码,应以空格或制表符(TAB)开头。汇编器不区分操作码中字母的大小写。

3.操作数(Operand)

操作数可以是地址、标号或指令码定义的常数,也可以是由伪运算符构成的表达式,操作数还可以是CM0+内部寄存器或者另一条指令的特定参数。若一条指令或伪指令有操作数,则操作数与操作码之间必须用空格隔开书写。操作数多于一个时,操作数之间用逗号“,”分隔。操作数中一般都有一个存放结果的寄存器,这个寄存器在操作数的最前面。

(1)常数标识。汇编器识别的常数有十进制(默认不需要前缀标识)、十六进制(前缀标识为0x)、二进制(前缀标识为0b)。

(2)“#”表示立即数。一个常数前添加“#”表示一个立即数,不加“#”时,表示一个地址。

特别说明:初学者常常会将立即数前的“#”遗漏,如果该操作数只能是立即数时,汇编器会提示错误,例如:

mov  r3,1  @给寄存器r3赋值为1(这个语句不对)

编译时会提示“immediate expression requires a # prefix – 'mov r3,1'”,应该改为:

mov  r3,#1  @给寄存器r3赋值为1(这个语句对)

(3)圆点“.”。若圆点“.”单独出现在语句的操作码之后的操作数位置上,则表示当前程序计数器的值被放置在圆点的位置。例如,“b.”语句代表转向本身,相当于永久循环,在调试中希望程序停留在某个地方可以添加这种语句,调试之后应删除。

(4)伪运算符。表2-21列出了汇编器识别的伪运算符。

表2-21 汇编器识别的伪运算符

4.注释(Comment)

注释即说明文字,多行注释以“/*”开始,以“*/”结束。注释可以包含多行,也可以独占一行。在GNU汇编器的汇编语言中,单行注释以“@”引导或者用“#”引导,用“@”引导类似于 C 语言的“//”;用“#”引导时,“#”必须为单行的第一个字符。不同开发环境有所区别,只要注意寻找对应方式即可,读者可参见具体的工程样例。

2.4.2 常用伪指令简介

不同集成开发环境下的伪指令稍有不同,伪指令书写格式与所使用的开发环境有关,读者可参照具体的工程样例“照葫芦画瓢”。

伪指令主要包括用于常量及宏的定义、条件判断、文件包含等伪指令。在 KDS3.0开发环境下,所有的汇编命令都是以“.”开头的。这里以本书使用的开发环境(S32DS)为例介绍有关汇编伪指令。

1.系统预定义的段

C语言程序在经过gcc编译器后最终生成.elf格式的可执行文件。.elf文件是以段为单位来组织的,通常可分为.text、.data和.bss三个段,其中,.text是只读的代码区,.data是可读可写的数据区,而.bss 则是可读可写且没有初始化的数据区。.text 段开始地址为0x0,接着分别是.data段和.bss段。

.text       @表明以下代码在.text段

.data       @表明以下代码在.data段

.bss       @表明以下代码在.bss段

2.常量的定义

汇编代码常用的功能之一是常量的定义。使用常量定义,能够提高程序代码的可读性,并且使代码维护更加简单。常量的定义可以使用.equ 汇编指令,下面是 GNU 汇编器的一个常量定义的例子。

.equ     _NVIC_ICER, 0xE000E180

……

LDR     R0,=_NVIC_ICER  @将0xE000E180放到R0中

常量的定义还可以使用.set汇编指令,其语法结构与.equ相同。

.set ROM_size,128*1024     @ROM大小为131072字节(128KB)

.set start_ROM,0xE0000000

.set end_ROM,start_ROM+ROMsize  @ROM结束地址为0xE0020000

3.程序中插入常量

对于大多数汇编工具来说,一个典型的特性是可以在程序中插入数据。GNU汇编器语法可以写成

LDR R3,=NUMNER     @得到NUMNER的存储地址

LDR R4,[R3]        @将0x123456789读到R4

……

LDR R0,=HELLO_TEXT  @得到HELLO_TEXT的起始地址

BL PrintText      @调用PrintText函数显示字符串

……

ALIGN 4

NUMNER:

.word 0x123456789

HELLO_TEXT:

.asciz“hello\n”      @以'\0'结束的字符

为了在程序中插入不同类型的常量,GNU 汇编器中包含许多不同的伪指令,如表2-22所示。

表2-22 用于在程序中插入不同类型常量的常用伪指令

4.条件伪指令

.if条件伪指令后面紧跟着一个恒定的表达式(即该表达式的值为真),并且最后要以.endif结尾。如果中间有其他条件,可以用.else编写汇编语句。

.ifdef标号,表示如果标号被定义,则执行标号下面的代码。

5.文件包含伪指令

.include "filename"

.include是一个附加文件的链接指示命令,利用它可以把另一个源文件插入当前的源文件中并一起汇编,成为一个完整的源程序。filename 是一个文件名,可以包含文件的绝对路径或相对路径,但建议同一个工程的相关文件放到同一个文件夹中,所以更多的时候使用相对路径,可参见第3章中的第一个汇编实例程序。

6.其他常用伪指令

除了上述的伪指令,GNU汇编器还有其他常用伪指令。

(1).section伪指令:用户可以通过.section伪指令来自定义一个段。例如:

.section .isr_vector, "a" @定义一个.isr_vector段,"a"表示允许段

(2).global伪指令:.global伪指令可以用来定义一个全局符号。例如:

.global symbol @定义一个全局符号symbol

(3).extern伪指令:.extern伪指令的语法为“.extern symbol”,声明symbol为外部函数,在调用的时候可以遍访所有文件找到该函数并且使用它。例如:

.extern main     @声明main为外部函数

bl main       @进入main函数

(4).align伪指令:.align伪指令可以通过添加填充字节来使当前位置满足一定的对齐方式。语法结构为“.align [exp[, fill]]”,其中,exp为0~16之间的数字,表示下一条指令对齐至2 exp 位置,若未指定,则将当前位置对齐到下一个字的位置;fill表示为对齐而填充的字节值,可省略,默认为0x00。例如,

.align 3      @把当前位置计数器值增加到2 3 的倍数上,若已是2 3 的倍数,不做改变

(5).end伪指令:.end伪指令声明汇编文件的结束。

还有有限循环伪指令、宏定义和宏调用伪指令等,读者可参见《GNU汇编语法》。 hxgSPn4+7IkDp8M4pbbCG/YqvncKc96lCs27yo7S+ISYMDqQyBK3JPn+iLy4/oAl

点击中间区域
呼出菜单
上一章
目录
下一章
×