8086/8088汇编语言程序设计
6.1.1汇编语言的语句格式
汇编语句分为指令语句和汇编命令语句。
指令语句的格式如下:
(标号:)(前缀指令)助记符(操作数);(注释)
其中()中的内容是可以省略的,多个操作数间是以逗号隔开的。
汇编命令语句不汇编成机器代码,它仅仅在汇编过程中告诉汇编程序应如何编译汇编命令式为汇编程序在编译时用的。
汇编命令语句用四个字段组成,格式如下:
(名字)助记符(操作数);(注释)
当伪指令是数据定义的伪指令时,它称为变量。
6.1.2 标号、变量及表达式
标号是用符号表示的地址。
标号有3个属性:段地址、偏移地址和类型。
标号的段地址和偏移地址是指标号对应的指令首字节所在的段地址和段内的偏移地址。
标号的类型属性有两种:NEAR和FAR类型。
在转移和调用指令中常将标号作为转移目标地址使用。
变量是与一个数据项的第一字节相对应的标识符。
它表示该数据项第一字节在现行段中的偏移量。
变量有3个属性:段地址(SEG)、偏移地址(OFFSET)、类型(TYPE)。
表达式由运算符和运算对象组成。
运算符有算术、逻辑、关系运算符和汇编语言特定的运算符(分析、合成等)运算对象可以是常数、变量和标号,也可以是操作数,还可以是构成程序的段、偏移量或圆括号内的偏移量。
6.1.3 伪指令
伪指令共有20条,常用有以下几条:
(1)赋值伪指令EQU, 利用EQU指令可用标号来代表数据、数据地址或程序地址。
伪指令EQU有两种格式,即
A. 名称EQU 表达式
B. 名称EQU 被定义过的名称或标号
(2)定义变量(分配存储单元)伪指令,就是给变量分配内存的伪指令。
这些伪指令是DB, DW, DD, DQ, DT。
A. 当用DW定义地址表达式时,地址表达式中的变量名称表示该变量的第一个存储单元的偏移地址。
B. 当用DD定义地址表达式时,低字节用预置偏移地址,高位字用于预置段地址。
C. 地址表达式中的变量或标号可与常数值相加减。
对于变量来说运算结果的
类型不变;对标号来说,运算结果仍表示原标号段中的偏移地址。
D. 变量或标号不能相加,但可相减,结果是没有属性的纯数值。
(3)定义存储单元类型伪指令(PTR)。
这类指令不单独使用,而是和指令结合起来使用。
利用这些伪指令,对存储单元指定数据类型。
(4)段定义伪指令
A. 段定义伪指令SEGMENT/ENDS主要是用来定义段的名称和范围,还可指明段的定位类型、组合类型和分类名。
段定义伪指令的格式是:
段名1 SEGMENT [定位类型][组合类型][分类名]
┆
段名1 ENDS
当定义数据段、附加数据和堆栈段时,在SEGMENT/ENDS伪指令中间的语句,只能包括伪指令语句,不能包括指令语句。
只有当SEGMENT/ENDS定义代码段时,中间的语句才能为指令语句以及与指令有关的伪指令语句。
由SEGMENT/ENDS所定义的段小于64KB。
B. OGR伪指令。
该指令用来规定目标程序存放单元的偏移量。
它的格式如下:
ORG 表达式
该指令前面不能带标识符。
C. ASSUME 伪指令。
该指令语句用来告诉汇编程序在指令执行期间内存的哪一段是数据段,哪一段是堆栈段,哪一段是代码段。
该指令的格式为:ASSUME 段寄存器名:段名符[,段寄存器名:段名符,…] ASSUME语句只能安排在代码段内,一般应排在代码段作为首指令。
(5)定义过程的伪指令有PROC, ENDP, NEAR和FAR。
一个过程可以被其他程序所调用,它的最后一条指令总是返回指令,返回到主程序。
定义过程的伪指令总是成对出现的,在这两条伪指令间的内容就作为一个过程,即一个子程序。
(6)定义结构的伪指令STRUC/ENDS。
定义结构伪指令的格式如下:
结构名称STRUC
┆由DB, DW, DD伪指令所组成的语句序列字段
标识符
结构名称ENDS
结构定义并不保留任何存储空间,也不为任何存储单元赋值,它仅仅是一种模式。
因而在引用结构和其字段之前,必须为结构分配空间或赋值。
要给结构分配存储空间或赋值,必须有一个引用该结构的语句,其格式如下:变量结构名称<赋值说明>
访问结构的方法是:变量名,字段名
(7)源程序结束伪指令END。
该指令表明了源程序的终结,其格式如下:
END 表达式(表达式通常就是程序第一条指令的语句标号)
6.1.4 分支程序设计
程序的分支主要是靠条件转移指令来实现。
这里需要注意的是条件转移语句都是近程跳转。
若程序所要转移的地址超出其范围时,需利用一条无条件转移语句作为中转。
6.1.5 循环程序设计
循环程序设计主要用于某些需要重复进行的操作,主要使用循环指令LOOP,LOOPZ或LOOPNZ或条件转移指令。
循环程序的设计可分为设置循环初始状态、循环体和循环控制条件三部分。
(1)设置循环初始状态主要是指设置次数的计数初值,以及其他为能使循环体正常工作而设置的初始状态等。
(2)循环体是循环操作(重复执行)的部分,包括循环的工作部分及修改部分。
循环的工作部分是实现程序功能的主要程序段;循环的修改部分是指当程序循环执行时,对一些参数如地址、变量的有规律的修正。
(3)循环控制部分是循环程序设计的关键。
每个循环程序必须选择一个控制循环程序运行和结束的条件。
6.1.6 子程序设计
子程序可以被调用,且该子程序完成确定的功能后便返回调用程序处。
(1)过程的定义和调用,过程的定义格式为:
过程名PROC属性
┆
RET
过程名ENDP
调用过程时只要在CALL指令后写上该过程名即可。
属性字段用来指明过程的类型属性是NEAR还是FAR。
RET指令总是放在过程体的末尾,用来返回主程序。
(2)寄存器内容的保护和恢复。
即保护现场和恢复现场,通常是用堆栈压入指令和弹出指令来实现。
必须注意:并不是过程中用的所有寄存器内容都要保护(只有那些子程序和主程序都要用的寄存器才予以保护)。
例如,若用寄存器在主程序和过程间传递参数就不需要保护。
(3)主程序和过程间的参数传递。
主程序调用过程时,必须先把过程所需的初始数据(即入口参数)设置好,过程执行完毕返回主程序时也必须将过程运行所得的结果(即出口参数)送给主程序。
过程入口参数传入和出口参数的送出称为主程序和过程间的参数传送。
参数传送的方法主要有以下4种:
A. 用CPU内部的寄存器传送参数。
B. 当过程和主程序同在一个代码段时,过程可以直接访问该代码段中的变量(即参数)。
C. 通过地址表传送变量地址。
该方法是将所有变量的偏移地址顺序存放在一张地址表中,然后通过寄存器将地址表的地址传送给过程,进入过程后可用寄存器间接寻址方式从地址表中取出变量地址,以便访问所需变量。
D. 通过堆栈传送参数或参数地址。
该方法是:调用过程前在主程序中用PUSH指令将参数地址压入堆栈;进入过程后再用基址寄存器BP从堆栈中取出这些参数地址,并送寄存器,以便寄存器间接寻址方式访问所需变量。
4. 过程的重入。
保证子程序可重入性的方法,通常也是将每次调用子程序时所用到的参数和中间结果逐层压入堆栈,以达到每次调用的结果都能正确保存的目的。
5. 过程的嵌套、递归的调用。
过程嵌套的层次是没有限制的,只要堆栈空间允许即可,但当嵌套层次较多时,应特别注意寄存器内容的保护和恢复,以免发生冲突。
6.1.7 宏指令、宏定义、宏调用
宏定义的格式为:
宏指令名 MACRO(形式参数)
┆
ENDM
其调用的格式为:宏指令名(实际参数)
当具有宏指令调用的源程序被汇编时,每个宏调用将被MASM进行宏展开。
宏展开实际上是用宏定义时设计的宏体去代替相应的宏指令名,并且用实际参数一一取代形式参数。