汇编语言(四)——编程语法入门

目录

0.第一个汇编程序

1.语言常量

(1)整数常量

(2)实数常量

(3)字符常量

2.保留字

3.标识符

4.伪指令

5.指令

(1)标号

(2) 指令助记符

(3)操作数

(4)注释

(5) NOP(空操作)指令

6.汇编器以及汇编流程

7.数据定义详解

(1)定义 BYTE 和 SBYTE 数据

(2)定义 WORD 和 SWORD 数据

(3)定义 DWORD 和 SDWORD 数据

(4)定义 QWORD 数据

(5)定义压缩 BCD(TBYTE)数据

(6)定义浮点类型

(7)等号(=)伪指令

8.数组和字符串长度

9.常用伪指令

(1)EQU伪指令

(2)TEXTEQU伪指令


0.第一个汇编程序

汇编程序几乎提供了全部信息的语言。程序员可以看到正在发生的所有事情,甚至包括 CPU 中的寄存器和标志!但是,在拥有这种能力的同时,程序员必须负责处理数据表示的细节和指令的格式。程序员工作在一个具有大量详细信息的层次。现在以一个简单的汇编语言程序为例,来了解其工作过程。

下面这个程序是执行两个数相加,并将结果保存在寄存器中的代码:(没运行起来)

.data                          ;此为数据区
sum DWORD 0                    ;定义名为sum的变量
.code                          ;此为代码区
main PROC
    mov eax,5                  ;将数字5送入而eax寄存器
    add eax,6                  ;eax寄存器加6
    mox sum,eax
    INVOKE ExitProcess,0       ;结束程序
main ENDP

程序员可能熟悉的类型相比它们没有那么具体,比如 int、double、float 等等。这些关键字只限制大小,并不检查变量中存放的内容。记住,程序员拥有完全控制权。那些被 .code 和 .data 伪指令标记的代码和数据区,被称为段。即,程序有代码段和数据段。

1.语言常量

这里使用 Microsoft 语法符号。方括号内的元素是可选的;大括号内的元素用 | 符号分隔,且必须要选择其中一个元素;斜体字标识的是有明确定义或说明的元素。

(1)整数常量

整数常量(integer literal)(又称为整型常量(integer constant))由一个可选前置符号、一个或多个数字,以及一个指明其基数的可选基数字符构成:

[{+|-}] digits [radix]

比如 26 就是一个有效的整数常量。它没有基数,所以假设其是十进制形式。如果想要表示十六进制数 26,就将其写为 26h。同样,数字 1101 可以被看做是十进制值,除非在其末尾添加“b”,使其成为 1101b (二进制)。下表列出了可能的基数值:
 

h 十六进制 r 编码实数
q/o 八进制 t 十进制(备用)
d 十进制 y 二进制(备用)
b 二进制     

以字母开头的十六进制数必须加个前置 0,以防汇编器将其解释为标识符。整型常量表达式 (constant integer expression) 是一种算术表达式,它包含了整数常量和算术运算符。每个表达式的计算结果必须是一个整数,并可用 32 位 (从 0 到 FFFFFFFFh) 来存放。下表列出了算术运算符,并按照从高 (1) 到低 (4) 的顺序给出了它们的优先级。对整型常量表达式而言很重要的是,要意识到它们只在汇编时计算。这里将它们简称为 整数表达式:
 

运算符 名称 优先级
() 圆括号 1
+,- 一元加、减 2
*, / 乘、除 3
MOD 取模 3
+, - 加、减 4

(2)实数常量

实数常量(real number literal)(又称为浮点数常量(floating-point literal))用于表示十进制实数和编码(十六进制)实数。十进制实数包含一个可选符号,其后跟随一个整数,一个十进制小数点,一个可选的表示小数部分的整数和一个可选的指数:

实数常量(real number literal)(又称为浮点数常量(floating-point literal))用于表示十进制实数和编码(十六进制)实数。十进制实数包含一个可选符号,其后跟随一个整数,一个十进制小数点,一个可选的表示小数部分的整数,和一个可选的指数:

[sign]integer.[integer] [exponent]

符号和指数的格式如下:

sign                {+,-}

exponent        E[{+,-}]integer

(3)字符常量

字符常量 (character literal) 是指,用单引号或双引号包含的一个字符。汇编器在内存中保存的是该字符二进制 ASCII 码的数值。例如:

'A'

"d"

字符串常量 (string literal) 是用单引号或双引号包含的一个字符 ( 含空格符 ) 序列,例如:

'ABC'

'X'

"Good night, Gracie"

嵌套引号也是被允许的,和字符常量以整数形式存放一样,字符串常量在内存中的保存形式为整数字节数值序列。

2.保留字

保留字(reserved words)有特殊意义并且只能在其正确的上下文中使用。默认情况下,保留字是没有大小写之分的。比如,MOV 与 mov、Mov 是相同的。保留字有不同的类型:

  • 指令助记符,如 MOV、ADD 和 MUL。
  • 寄存器名称。
  • 伪指令,告诉汇编器如何汇编程序。
  • 属性,提供变量和操作数的大小与使用信息。例如 BYTE 和 WORD。
  • 运算符,在常量表达式中使用。
  • 预定义符号,比如 @data,它在汇编时返回常量的整数值。

下表是常用的保留字列表。
 

$ PARITY? DWORD STDCALL
? PASCAL FAR SWORD
@B QWORD FAR16 SYSCALL
@F REAL4 FORTRAN TBYTE
ADDR REAL8 FWORD VARARG
BASIC REAL10 NEAR WORD
BYTE SBYTE NEAR16 ZERO?
C SDORD OVERFLOW?  
CARRY? SIGN?    

3.标识符

标识符(identifier)是由程序员选择的名称,它用于标识变量、常数、子程序和代码标签。
 

标识符的形成有一些规则:

  • 可以包含 1 到 247 个字符。
  • 不区分大小写。
  • 第一个字符必须为字母 (A---Z, a---z) A 下划线 (_)、@、? 或 $。其后的字符也可以是数字。
  • 标识符不能与汇编器保留字相同。

一般情况下,应避免用符号 @ 和下划线作为第一个字符,因为它们既用于汇编器,也用于高级语言编译器。

4.伪指令

伪指令 (directive) 是嵌入源代码中的命令,由汇编器识别和执行。伪指令不在运行时执行,但是它们可以定义变量、宏和子程序;为内存段分配名称,执行许多其他与汇编器相关的日常任务。默认情况下,伪指令不区分大小写。例如,.data,.DATA 和 .Data 是相同的。

汇编器伪指令的一个重要功能是定义程序区段,也称为段 (segment)。程序中的段具有不同的作用。段可以用于定义变量,并用 .DATA 伪指令进行标识;.CODE 伪指令标识的程序区段包含了可执行的指令;.STACK 伪指令标识的程序区段定义了运行时堆栈,并设置了其大小,例如:.stack 100h;

5.指令

指令(instruction)是一种语句,它在程序汇编编译时变得可执行。汇编器将指令翻译为机器语言字节,并且在运行时由 CPU 加载和执行。一条指令有四个组成部分:

  • 标号(可选)
  • 指令助记符(必需)
  • 操作数(通常是必需的)
  • 注释(可选)

不同部分的位置安排如下所示:

[label: ] mnemonic [operands] [;comment]

(1)标号

标号(label)是一种标识符,是指令和数据的位置标记。标号位于指令的前端,表示指令的地址。同样,标号也位于变量的前端,表示变量的地址。标号有两种类型:数据标号和代码标号。数据标号标识变量的位置,它提供了一种方便的手段在代码中引用该变量。汇编器为每个标号分配一个数字地址。可以在一个标号后面定义多个数据项。程序代码区(指令所在区段)的标号必须用冒号(:)结束。代码标号用作跳转和循环指令的目标。代码标号可以与指令在同一行上,也可以自己独立一行。

(2) 指令助记符

 

指令助记符(instruction mnemonic)是标记一条指令的短单词。在英语中,助记符是帮助记忆的方法。相似地,汇编语言指令助记符,如 mov, add 和 sub,给出了指令执行操作类型的线索。下面是一些指令助记符的例子:
 

助记符 说明 助记符 说明
MOV 传送(分配)数值 MUL 两个数值相乘
ADD 两个数值相加 JMP 跳转到一个新位置
SUB 从一个数值中减去另一个数值 CALL 调用一个子程序

(3)操作数

操作数是指令输入输出的数值。汇编语言指令操作数的个数范围是 0〜3 个,每个操作数可以是寄存器、内存操作数、整数表达式和输入输岀端口。操作数有固有顺序。当指令有多个操作数时,通常第一个操作数被称为目的操作数,第二个操作数被称为源操作数(source operand)。一般情况下,目的操作数的内容由指令修改。

(4)注释

 

注释是程序编写者与阅读者交流程序设计信息的重要途径。程序清单的开始部分通常包含如下信息:

  • 程序目标的说明
  • 程序创建者或修改者的名单
  • 程序创建和修改的日期
  • 程序实现技术的说明

注释有两种指定方法:

  • 单行注释,用分号(;)开始。汇编器将忽略在同一行上分号之后的所有字符。
  • 块注释,用 COMMENT 伪指令和一个用户定义的符号开始。汇编器将忽略其后所有的文本行,直到相同的用户定义符号出现为止。

(5) NOP(空操作)指令

最安全(也是最无用)的指令是 NOP(空操作)。它在程序空间中占有一个字节,但是不做任何操作。它有时被编译器和汇编器用于将代码对齐到有效的地址边界。

6.汇编器以及汇编流程

汇编语言编写的源程序不能直接在其目标计算机上执行,必须通过翻译或汇编将其转换为可执行代码。实际上,汇编器与编译器 (compiler) 很相似,编译器是一类程序,用于将 C++ 或 Java 程序翻译为可执行代码。汇编器生成包含机器语言的文件,称为目标文件 (object file)。这个文件还没有准备好执行,它还需传递给一个被称为链接器 (linker) 的程序,从而生成可执行文件 (executable file)。这个文件就准备好在操作系统命令提示符下执行。

汇编-链接-执行周期

  • 步骤1:编程者用文本编辑器 (text editor) 创建一个 ASCII 文本文件,称之为源文件。
  • 步骤2:汇编器读取源文件,并生成目标文件,即对程序的机器语言翻译。或者,它也会生成列表文件。只要出现任何错误,编程者就必须返回步骤 1,修改程序。
  • 步骤3:链接器读取并检查目标文件,以便发现该程序是否包含了任何对链接库中过程的调用。链接器从链接库中复制任何被请求的过程,将它们与目标文件组合,以生成可执行文件。
  • 步骤4:操作系统加载程序将可执行文件读入内存,并使 CPU 分支到该程序起始地址,然后程序开始执行。

列表文件 (listing file) 包括了程序源文件的副本,再加上行号、每条指令的数字地址、每条指令的机器代码字节(十六进制)以及符号表。符号表中包含了程序中所有标识符的名称、段和相关信息。

7.数据定义详解

汇编器识别一组基本的内部数据类型(intrinsic data type),按照数据大小(字节、字、双字等等)、是否有符号、是整数还是实数来描述其类型。序员用 SDWORD 告诉读程序的人,这个值是有符号的,但是,对于汇编器来说这不是强制性的。汇编器只评估操作数的大小。因此,举例来说,程序员只能将 32 位整数指定为 DWORD、SDWORD 或者 REAL4 类型。

 

下表给出了全部内部数据类型的列表,有些表项中的 IEEE 符号指的是 IEEE 计算机学会出版的标准实数格式。
 

类型 用法
BYTE 8 位无符号整数,B 代表字节
SBYTE 8 位有符号整数,S 代表有符号
WORD 16 位无符号整数
SWORD 16 位有符号整数
DWORD 32 位无符号整数,D 代表双(字)
SDWORD 32 位有符号整数,SD 代表有符号双(字)
FWORD 48 位整数(保护模式中的远指针)
QWORD 64 位整数,Q 代表四(字)
TBYTE 80 位(10 字节)整数,T 代表 10 字节
REAL4 32 位(4 字节)IEEE 短实数
REAL8 64 位(8 字节)IEEE 长实数
REAL10 80 位(10 字节)IEEE 扩展实数

数据定义语句(data definition statement)在内存中为变量留岀存储空间,并赋予一个可选的名字。数据定义语句根据内部数据类型(上表)定义变量。数据定义语法如下所示:

[name] directive initializer [,initializer]...

  • 名字:分配给变量的可选名字必须遵守标识符规范。
  • 伪指令:数据定义语句中的伪指令可以是 BYTE、WORD、DWORD、SBTYE、SWORD 或其他在上表中列出的类型。此外,它还可以是传统数据定义伪指令,如下表所示。
伪指令 用法 伪指令 用法
DB 8位整数 DQ 64 位整数或实数
DW 16 位整数 DT 定义 80 位(10 字节)整数
DD 32 位整数或实数     

MODEL 伪指令,它告诉汇编程序用的是哪一种存储模式。32 位程序总是使用平面(flat)存储模式,它与处理器的保护模式相关联。关键字 stdcall 在调用程序时告诉汇编器,怎样管理运行时堆栈。然后是 .STACK 伪指令,它告诉汇编器应该为程序运行时堆栈保留多少内存字节。ENDP 伪指令标记一个过程的结束。

(1)定义 BYTE 和 SBYTE 数据

BYTE(定义字节)和 SBYTE(定义有符号字节)为一个或多个无符号或有符号数值分配存储空间。每个初始值在存储时,都必须是 8 位的。问号(?)初始值使得变量未初始化,这意味着在运行时分配数值到该变量。如果同一个数据定义中使用了多个初始值,那么它的标号只指出第一个初始值的偏移量。并不是所有的数据定义都要用标号。比如,在 list 后面继续添加字节数组,就可以在下一行定义它们:在单个数据定义中,其初始值可以使用不同的基数。字符和字符串常量也可以自由组合。定义一个字符串,要用单引号或双引号将其括起来。最常见的字符串类型是用一个空字节(值为0)作为结束标记,称为以空字节结束的字符串,很多编程语言中都使用这种类型的字符串:每个字符占一个字节的存储空间。对于字节数值必须用逗号分隔的规则而言,字符串是一个例外。DUP 操作符使用一个整数表达式作为计数器,为多个数据项分配存储空间。在为字符串或数组分配存储空间时,这个操作符非常有用,它可以使用初始化或非初始化数据。

(2)定义 WORD 和 SWORD 数据

WORD(定义字)和 SWORD(定义有符号字)伪指令为一个或多个 16 位整数分配存储空间。也可以使用传统的 DW 伪指令。

(3)定义 DWORD 和 SDWORD 数据

DWORD(定义双字)和 SDWORD(定义有符号双字)伪指令为一个或多个 32 位整数分配存储空间,传统的 DD 伪指令也可以用来定义双字数据。DWORD 还可以用于声明一种变量,这种变量包含的是另一个变量的 32 位偏移量。

(4)定义 QWORD 数据

QWORD(定义四字)伪指令为 64 位(8 字节)数值分配存储空间,传统的 DQ 伪指令也可以用来定义四字数据。

(5)定义压缩 BCD(TBYTE)数据

Intel 把一个压缩的二进制编码的十进制(BCD, Binary Coded Decimal)整数存放在一个 10 字节的包中。每个字节(除了最高字节之外)包含两个十进制数字。在低 9 个存储字节中,每半个字节都存放了一个十进制数字。最高字节中,最高位表示该数的符号位。如果最高字节为 80h,该数就是负数;如果最高字节为 00h,该数就是正数。整数的范围是 -999 999 999 999 999 999 到 +999 999 999 999 999 999。MASM 使用 TBYTE 伪指令来定义压缩 BCD 变量。常数初始值必须是十六进制的,因为,汇编器不会自动将十进制初始值转换为 BCD 码。如果想要把一个实数编码为压缩 BCD 码,可以先用 FLD 指令将该实数加载到浮点寄存器堆栈,再用 FBSTP 指令将其转换为压缩 BCD 码,该指令会把数值舍入到最接近的整数。

(6)定义浮点类型

REAL4 定义 4 字节单精度浮点变量。REAL8 定义 8 字节双精度数值,REAL10 定义 10 字节扩展精度数值。每个伪指令都需要一个或多个实常数初始值。下表描述了标准实类型的最少有效数字个数和近似范围:

数据类型 有效数字 近似范围
短实数 6 1.18x 10-38 to 3.40 x 1038
长实数 15 2.23 x 10-308 to 1.79 x 10308
扩展精度实数 19 3.37 x 10-4932 to 1.18 x 104932

DD、DQ 和 DT 伪指令也可以定义实数。

(7)等号(=)伪指令

等号伪指令(equal-sign directive)把一个符号名称与一个整数表达式连接起来。通常,表达式是一个 32 位的整数值。当程序进行汇编时,在汇编器预处理阶段,所有出现的 name 都会被替换为 expression。假设下面的语句出现在一个源代码文件开始的位置。

最重要的符号之一被称为当前地址计数器(current location counter),表示为 $。用“=”定义的符号,在同一程序内可以被重新定义。

8.数组和字符串长度

显式声明数组的大小会导致编程错误,尤其是如果后续还会插入或删除数组元素。声明数组大小更好的方法是,让汇编器来计算这个值。$ 运算符(当前地址计数器)返回当前程序语句的偏移量。当要计算元素数量的数组中包含的不是字节时,就应该用数组总的大小(按字节计)除以单个元素的大小。

9.常用伪指令

(1)EQU伪指令

EQU 伪指令把一个符号名称与一个整数表达式或一个任意文本连接起来,它有 3 种格式:

name EQU expression
name EQU symbol
name EQU <text>

第一种格式中,expression 必须是一个有效整数表达式。第二种格式中,symbol 是一个已存在的符号名称,已经用 = 或 EQU 定义过了。第三种格式中,任何文本都可以岀现在<...>内。当汇编器在程序后面遇到 name 时,它就用整数值或文本来代替符号。在定义非整数值时,EQU 非常有用。

(2)TEXTEQU伪指令

TEXTEQU 伪指令,类似于 EQU,创建了文本宏(text macro)。它有 3 种格式:第一种为名称分配的是文本;第二种分配的是已有文本宏的内容;第三种分配的是整数常量表达式:

name TEXTEQU <text>
name TEXTEQU textmacro
name TEXTEQU %constExpr

 

猜你喜欢

转载自blog.csdn.net/qq_35789421/article/details/113722447