4.1 汇编语言的格式
4.1.1 8086汇编语言程序的一个例子
- 8086汇编的一个语句行是由4个部分组成的,即:
标号 操作码 操作数 ;注释(或名字)
4.1.2 8086汇编语言源程序的格式
- 源程序的一般格式位:
NAME1 SEGMENT
语句
语句
NAME1 ENDS
NAME2 SEGMENT
语句
语句
NAME2 ENDS
END<标号>
语句行的构成
4.2.1 标记(Token)
1.IBM宏汇编的字符集
(1)字母
- 包含大写的英文字母
- 小写的英文字母
(2)数字 - 阿拉伯数字
(3)特殊字符 -
IBM中宏汇编字符集中可打印字符
2.界符
-
IBM宏汇编字符集中可打印字符
3.常量
(1)数字(整数)常量
①二进制常量
- 字母B结尾
②十进制常量 - 字母D结尾
③八进制常量 - 以字母Q结尾
④十六进制 - 以字母H结尾
(2)字符串常量
- 字符串常量是由包含在单引号内的1或2个ASCII码构成的
4.标识符
- 标识符是由程序员自由建立起来的、有特定意义的字符序列,如例子中的SUM、CYCLE、PORT_VAL等
- 一个标识符是由最多31个字母、数字及规定的特殊字符(?@_$)等组成的,且不能用数字打头(以免与十六进制数向混淆)
5.保留字
- 保留字看上去像标识符,但是它们在语言中有特殊的意义,而且不能用它们作为标识符
IBM宏汇编中的保留字
6.注释
- 为了使汇编语言的源程序更便于阅读和理解,常在源程序中加上注释
- 注释是在分号(;)后面的任意的字符序列,知道行的结尾
- 在汇编时,汇编程序对它们并不进行处理
- 可打印的文件中,注释和源程序一起打印
4.2.2 符号(Symbol)
1. 寄存器
2.变量
- 所有变量都有三种属性:
(1)段值(SEGMENT),即变量单元所在段的段地址(段的起始地址)的高16位,低4位始终为0
(2)偏移量(OFFSET),即变量单元地址与段的起始地址之间的偏移量(16位)
(3)类型(TYPE),变量有三种类型:字节(Byte)、字(word)和双字(Double Word)
3.标号(Label)
- 标号是某条指令所存放单元的符号地址,它是转移(条件转移或无条件转移)指令或调用(CALL)指令的目标操作数
- 标号的类型与变量不同,它的类型是NEAR或是FAR
4.数
- 汇编源程序中的常数也常以符号的形式出现
5.其他符号
4.2.3 表达式(Expressions)
1.操作数(Operands)
- 一个操作数或者一个寄存器名,或是一个常量(数字常量或字符串常量),或是一个存储器操作数
(1)常量操作数
- 具有数字值的操作数是常量或是表示常量的标识符(符号)
- 例中的常量操作数是100、PORT_VAL
- 常量操作数的值允许范围是从-65535 ~ +65535
(2)存储器操作数
- 存储器操作数,通常是标识符
- 具体地说,可以分为标号(Label)和变量(Variable)两种
变量可以具有以下几种寻址方式:
①直接寻址
②基址寻址
③变址(索引)寻址
④基址变址寻址
2.运算符(Operators)
(1)算术运算符(Arithmetic Operators)
- 一个是加(+)减、乘、除运算发
- 另一个算术运算符是MOD
因此对例子中地存储其地址作如下运算:
SUM+2
CYCLE-5
NOT_DONE-GO
是有效地表达式。而
SUM-CYCLE
(2)逻辑运算符
- 逻辑运算符是按位操作地AND、OR、XOR和NOT
(3)关系运算符 - 在IBM宏汇编中有以下关系运算符
①相等Equal(EQ)
②不等Not Equal(NE)
③小于Less Than(LT)
④大于Greater Than(GT)
⑤小于或等于Less Than or Equal(LE)
⑥大于或等于Greater Than or Equal(GE)
MOV BX ,PORT_VAL LT 5
若PORT_VAL地值小于5,关系为真,则会变程序在汇编后产生地语句为:
MOV BX ,OFFFFH
若PORT_VAL的值不小于5,则关系为假,汇编后产生的语句为:
MOV BX,0
(4)分析运算符
- 把存储器操作数分解为它的组成部分,如它的段值、段内偏移量和类型
(5)合成运算符 - 由已存在的存储器操作数生成一个段值和偏移量相同而类型不同的新的存储器操作数
4.2.4 语句(Statements)
一个汇编语言的源程序是由一条条语句组成的,语句就是对需要计算机完成的动作的说明
源程序中的语句可分为两类:指令语句和指示性语句
指令语句,汇编程序把它们翻译成机器代码,这些代码命令8086执行某些操作。如MOV、ADD、JMP等
对于指示性语句(伪指令),汇编程序并不把它们翻译成机器代码,实际上也不可能翻译成机器代码,只是用来指示、引导汇编程序在汇编时做一些操作,如定义符号、分配存储单元、初始化存储器等,所以伪指令本身不占用存储单元
例如:
MY_PLACE DB ?
指令语句的格式:
标号:助记符 参数,...,参数 ;注释
指示性语句的格式为:
名字 命令 参数,...,参数 ;注释
4.3 指示性语句
4.3.1 符号定义语句
1.等值语句EQU
NAME EQU EXPRESSION
- EQU语句给符号定义一个值,或定义为别的符号名,甚至可定义为一条可执行的指令等
-
一些例子如下:
2.等号(Equal Sign) =语句
- 能对符号进行再定义
EMP = 6
EMP = 7
EMP = EMP + 1
3.解除语句PURGE
PURGE 符号1,符号2,...,符号n
-
已经用EQU命令定义的符号,若以后不再用了,就可以用PURGE语句接触
例如:
4.3.2 数据定义语句
数据定义语句,为一个数据项分配存储单元,用一个符号名与这个存储单元相联系,且为这个数据提供一个人选的初始值
与该项相联系的符号名称为变量
-
例子:
例如:THING DB 25
不仅使THING这个符号与一个字节的存储单元相联系,而且在汇编时会把25放入THING相联系的存储单元中
所以THING是一个字节变量,它的初始值为25BIGGER_THING DW 4142H
在汇编时就会把41H与42H分别放至与BIGGER_THING相联系的两个连续的字节单元中(一个字中),而且42H放在地址低的字节,41H放在地址高的字节
所以,若BIGGER_THING是一个字变量,则它的初始值为4142H-
语句:BIGGEST_THING DD 12345678H
它定义了一个双字变量,且给了初始值
一个存储单元的类型如下:
(1)数据字节
SUM DB ? ;定义一个字节
(2)数据字(两个连续的字节)
BIGGER DW ?; 定义一个字
(3)数据双字(四个连续的字节)
BIGGER DD ? ;定义一个双字
(4)Near指令单元
CYCLE : CMP SUM,100
(5)Far指令单元
-
若在一个程序中,对它的数据段有如下定义:
其中的每一个存储单元都有一些属性(或组成成分)
分析运算符SEG,返回的是一个存储单元的段地址(即它所在段的起始地址)OFFSET运算符返回的是每一个存储单元地址的段内偏移量,即它与段地址之间的偏差。故:
SEG BUFFER1
SEG BUFFER2
SEG BUFFER3
都是相同的,它们返回的地址都是DATA_TABLES的地址
所以,若要对数据段寄存器初始化,则可采用指令:
MOV AX,SEG BUFFER1
MOV DS,AX
而
OFFSET BUFFER1
OFFSET BUFFER2
OFFSET BUFFER3
是各不相同的,若要向这些缓冲区填入新的数据,可以采用一些地址指针,则可以用以下指令来初始化地址指针:
MOV BX ,OFFSET BUFFER1
MOV SI,OFFSET BUFFER2
MOV DI,OFFSET BUFFER3
以后,就可以用这些指针来间接寻址这些缓冲区
存储器地址操作数的类型值
4.3.3 段定义语句
段定义的主要命令有①SEGMENT,②ENDS,③ASSUME,④ORG
SEGMENT和ENDS语句把汇编语言源程序分成段
这些段就相应于存储器段,在这些存储器段中,存放相应段的目标码
-
如何使用SEGMENT、ENDS和ASSUME命令,以定义代码段、堆栈段、数据段和附加段
(1)不要搭接,段中的第一个可用字节是在16字节界限上
MY_SEG SEGMENT
.
.
.
MY_SEG ENDS
这是一种正常情况
(2)允许搭接,但第一个可用字节必须在字的界限上
MY_SEG SEGMENT WORD
.
.
.
MY_SEG ENDS
(3)段开始在指定的16个字节界值上,但第一个可用字节在指定的偏移位置上
MY_SEG SEGMENT AT 1A2BH ; 段地址为1A2BH
ORG 0003H ;段内从偏移量0003H开始
MY_SEG ENDS
伪指令ORG的一般格式为:
ORG <表达式>
此语句指定了段内在它以后的程序或数据块存放的起始地址,也即以语句中的表达式的值作为起始地址,连续存放,除非遇到一个新的ORG语句
4.3.4 过程定义语句
过程定义语句的格式:
PROCEDURE_NAME PROC [NEAR]
或
PROCEDURE_NAME PROC FAR
RET
PROCEDURE _NAME ENDP
-
例子
4.3.5 结束语句
START:
END START
4.4 指令语句
4.4.1 指令助记符
1.NOP(No Operation)
- 使汇编产生一字节指令,使寄存器AX的内容自行交换
2.保留空格(Place Holder)
- NIL是使汇编程序不产生任何指令的唯一的指令助记符
4.4.2 指令前缀
- IBM宏汇编中允许作为前缀的助记符:
LOCK
REP(Repeat)
REPE (当相等时重复)
REPNE (当不相等时重复)
REPZ (当标志ZF=1时重复)
PEPNZ (当标志ZF=0时重复)
具有前缀的指令语句例子
CYCLE: LOCK DEC COUNT
4.4.3 操作数寻址方式
1.立即
MOV AX,15 ;15是一个立即数
2.寄存器
MOV AX,15 ;15是一个寄存器操作数
3.直接
SUM DB ?
MOV SUM,15 ;SUM是一个直接存储操作数
4.通过基址寄存器间接
MOV AX,[BX]
MOV AX,[BP]
5.通过变址寄存器间接
MOV AX,[SI]
MOV AX,[DI]
6.通过基址寄存器加变址寄存器间接
MOV AX,[BX][SI]
MOV AX,[BX][DI]
MOV AX,[BP][SI]
MOV AX,[BP][DI]
7.通过基址或变址寄存器加位移量间接
MANY_BYTES DB 100 DUP (?)
.
.
.
MOV AX,MANY_BYTES[BX]
MOV AX,MANY_BYTES[BP]
MOV AX,MANY_BYTES[SI]
MOV AX,MANY_BYTES[DI]
8.通过基址寄存器加变址寄存器加位移量间接存储器
MANY_BYTES DB 100 DUP (?)
.
.
.
MOV AX,MANY_BYTES[BX][SI]
MOV AX,MANY_BYTES[BX][DI]
MOV AX,MANY_BYTES[BP][SI]
MOV AX,MANY_BYTES[BP][DI]
4.4.4 串操作指令
ALPHA DB ?
BETA DB ?
MOV SI,OFFSET ALPHA
MOV DI,OFFSET BETA
MOV BETA,ALPHA
- 一个完整的汇编语言源程序应该由可执行命令组成的指令性语句和由对符号定义、分配存储单元、分段等组成的指示性语句构成的。而且,一个完整的程序至少应该包含三种段:由源程序组成的代码段。堆栈操作所需要的堆栈段和存放数据的数据段
该例子是把两个分别由未组合的BCD码(一个字节为一位BCD数)的串相加;由于8086中允许两个未组合的十进制数相加,只要经过适当的调整就可以得到正确的结果。所以,在程序中把第一个串的一位BCD数取至AL中,与第二个串的相应位相加,经过AAA调整,再把结果存至存储器中。程序中的前面部分是为了设置段,先设置数据段,用DB伪指令定义两个数据串,用COUNT表示数据的长度。接着是定义堆栈段,为堆栈留下了100个单元的空间(实际上当然由需要来定),然后是当以代码段,从标号GO开始就是可执行指令部分。
4.5 汇编语言程序设计及举例
4.5.1 算术运算程序设计(直线运行程序)
- 例 两个32位无符号数乘法程序
4.5.2 分支程序设计
4.5.3 循环程序设计
1.用计数器控制循环
例题:在一串给定个数的数中寻找最大值(或最小值),放至指定的存储单元。每个数用16位表示
2.多重循环
4.5.4 字符串处理程序
1.确定字符串的长度
例题:从头搜索字符串的结束标志,统计搜索的字符个数
2.加偶校验到ASCII字符
- 例题:若有一个ASCII字符串,它的起始地址放在单元STRING内,要求从串中取出每一个字符,检查其中包含的“1”的个数,若已为偶数,则它的最高有效位置“0”;否则,最高有数位置“1”,然后送回
4.5.5 码转换程序设计
1.十六进制到ASCII的转换
例4-5 若有一个二进制数码串,要把每一个字节中的二进制转换位两位十六进制数的ASCII,高4位的ASCII放在第hi高的单元。串中的第一个字节为串的长度(小于128)
2.从二进制到ASCII串的转换
- 例4-6 把在内存变量NUMBER中的16位二进制数,每一位转换为相应的ASCII,存入串变量STRING中
4.5.6 有关I/O的DOS功能调用
1.在CRT上连续输出0 ~ 9
4.5.7 宏汇编与条件汇编
1.宏命令的用途
(1)在汇编语言的源程序中,若有的程序段要多次使用,为了使在源程序中不重复书写这个程序段,可以一条宏指令来代替。由宏汇编程序在汇编时产生所需要的代码
MOV CL,4
SAL AL,CL
若要多次使用,就可以用一条宏指令代替:
SHFT MACRO
MOV CL,4
SAL AL,CL
ENDM
(2)宏定义不但能使源程序书写简洁,而且由于宏指令具有接受参量的能力,所以功能就更灵活
SHFT MACRO X,Y
MOV CL,X
SAL Y,CL
ENDM
(3)形式参量不只可以出现在操作数部分,也可以出现在操作码部分。如:
SHFT MACRO X,Y,Z
MOV CL,X
S&Z Y,CL
ENDM
若有以下调用:
SHFT 4,AL,AL
SHIFT 6,BX,AR
SHIFT 8,SI,HR
在汇编时,分别产生以下指令的目标代码
MOV CL,4
SAL AL,CL
MOV CL,6
SAR CL,6
SAR BX,CL
MOV CL,8
SHR SI,CL
2.IBM宏汇编中主要宏操作伪指令
(1)MACRO
宏定义名 MRRCO <形式参量表>
(2)PURGE
PURGE 宏定义名 [,......]
(3)LOCAL
- 例题在AL中有一位十六进制数码要转换为ASCII,则可以用以下宏定义:
CHANGE MACRO
CMP AL,10
JL ADD_0
ADD AL,'A'-'0'-10
ADD_0 ADD AL,'0'
ENDM
上面的CHANGE宏定义在下有多次调用的情况,应定义为:
CHANGE MACRO
LOCAL ADD_0
CMP AL,10
JL ADD_0
ADD AL,'A'-'0'-10
ADD_0 ADD AL,'0'
ENDM
(4)REPT
REPT <表达式>
ENDM
- 这个伪指令可以重复执行在它的指令体部分所包含的语句。重复执行的次数,由表达式的值所决定
例4-7 把数字0 ~ 9的ASCII填入表TABLE。CHAR = ‘0’
例题4-8 建立一个地址表,其中每个字的内容是下一个字的地址(用作地址指针),而最后一个字的内容是第一个字的地址
(5)IRP
IRP 形式参量(参数表)
ENDM
(6)IRPC
IRPC 形式参量,字符串(或<字符串>)
ENDM
3.宏定义嵌套
4.宏指令与子程序的区别
(1)宏指令是为了简化源程序的书写,在汇编时,汇编程序处理宏指令,还把宏定义体插入到宏调用处。所以,宏指令并没有简化目标程序。有多少次宏调用,在目标程序中仍需要有同样多次的目标代码插入。所以,宏指令不能节省目标程序所占的内存单元
(2)若一个源程序中多次调用一个程序段,则可用子程序,也可以用宏指令来简化源程序。用子程序的办法,汇编后产生的目标代码少,也即目标程序占用的内存少,节约了内存空间。但是,子程序在执行时,每调用一次都要先保护断点,通常在程序中还要保护现场;在返回时先要恢复现场,然后恢复断点(返回)
这些操作都额外增加了时间,因而执行时间长,速度慢指令恰好相反,它的目标程序长,占用的内存的那元多;但是执行时不需要保护断点,也不需要保护现场以及恢复、返回等这些额外的操作,因而执行时间短、速度快