DATA SEGMENT ;数据段
DA1 DB 'This is a sample program.'
DB 0DH,0AH,?$? ;回车和换行符以及结束符
DATA ENDS
STACK SEGMENT ;堆栈段
ST1 DB 100 DUP(?)
STACK ENDS
CODE SEGMENT ;代码段
MAIN PROC FAR
ASSUME CS:CODE,DS:DATA,SS:STACK ;段分配
START,MOV AX,STACK ;送堆栈段地址
MOV SS,AX
PUSH DS ;返回 DOS
MOV AX,0
PUSH AX
MOV AX,DATA ;送数据段地址
MOV DS,AX
MOV AH,9 ; DOS 9号功能调用,显示字符串
MOV DX,OFFSET DA1
INT 21H
RET
MAIN ENDP
CODE ENDS
END START
第四章 汇编语言程序设计汇编语言,利用指令助记符、符号地址、标号来编写的计算机语言。是机器语言的符号表示,是面向机器的语言,是较低级的语言。
利用汇编语言编写的程序称为源程序,需要通过汇编程序翻译成二进制代码的目标程序,再经过与库文件的连接,最后得到可执行文件程序,才能在机器上直接运行。
4.1 汇编语言程序格式
4.1.1.汇编语言的格式
1,指令性语句指令性语句是指汇编程序可将其汇编成目标代码,能被机器执行的语句。
格式如下:
[标号,] 指令助记符 [操作数 ][;注解 ]
其中,用方括号括起来的部分,可以有也可以没有,是可选项 。
每部分之间用空格 (至少一个 )分开,一行最多可有 132个字符 。
标号,是给指令或某一存储单元地址所起的名字 。 可由字母,A
~ z ; 数字,0 ~ 9 ; 特殊字符,?,·,@,—,$等字符组成 。
数字不能作标识符的第一个字符,而圆点仅能用作第一个字符 。
标识符最长为 31个字符 。 当标识符后跟冒号时,表示是标号 。
它代表该行指令的起始地址;当标识符后不带冒号时,表示变量;伪指令前的标识符不加冒号 。
指令助记符,表示不同操作的指令,可以是 8086的指令助记符,
也可以是伪指令 。
操作数,是指令执行的对象 。 根据指令的要求,可能有一个,
两个或者没有,如果一条指令有多个操作数,则操作数之间必须用,,,分隔 。
例如,RET ;无操作数
COUNT,INC CX ;一个操作数如果是伪指令,则可能有多个操作数,例如:
COST DB 3,4,5,6,7 ; 5个操作数
MOV AX,[BP十 4] ;第二个操作数为表达式注释,该项是可选项,可有可无,是为源程序所加的注解,用于提高程序的可读性 。 注释前必须用分号 。
2,伪指令语句伪指令 用于对汇编程序进行控制,它不像机器指令 ( 指令性语句 ) 那样是在程序运行期间由计算机来执行的,它是在汇编程序对源程序汇编期间由汇编程序处理的操作,可以完成如 数据定义,分配存储区,指示程序结束等 功能 。 其格式和汇编指令一样,但一般不产生目的代码,即不直接命令 CPU去执行什么操作 。
格式,名字 伪指令指示符 操作数 [,操作数 ] [;注释 ]
名字,是给伪指令取的名称,它用符号地址表示,名字后不允许带冒号”,”,名字可以省略,伪指令中名字通常是变量名、
段名、过程名、符号名等。
伪指令指示符,是汇编程序 MASM规定的符号,常用的有变量定义语句 (DB DW),符号定义语句 (EQU =),段定义语句
(SEGMENT …ENDS),段分配语句 (ASSUME),结构定义语句
(STURC …ENDS),过程定义语句 (PROC …ENDP) 等。
操作数,是由伪指令具体要求的,有的伪指令不允许带操作数,
有的伪指令要求带多个操作数,多个操作数之间必须用逗号隔开。操作数可以是常数、变量、字符串、表达式等。
注释 的功能和使用与指令性语句相同。
3 表达式表达式是由运算符和操作数组成的序列,汇编语言中的表达式在汇编时计算,在运行时已经是常数了 。 在汇编时产生一个确定的值 。 这个值可以仅表示一个常量,也可以表示一个存储单元的偏移地址,相应的表达式称为常量表达式和地址表达式 。 表达式可出现在常数的地方 。
( 1) 常数汇编语言语句中出现的常数有 5种:
① 二进制数:后面加 B,如 01000001B。
② 八进制数:后面加字母 Q或 O,如 202Q或 202O。
③ 十进制数:后跟 D或不跟字母,如 85D或 85。
④ 十六进制数:在后面加 H,若是字母开始,前面要加 0,以示和变量的区别 。
⑤ 字符和字符串字符串用单引号 (?)或双引号 (“)引起来 。
( 2) 常量操作数常量操作数是一个数值操作数,一般是常量或者是表示常量的标识符 。 可以为数字常量操作数或字符串常量操作数 。 前者可采用二进制,八进制,十进制或十六进制等计数形式;而后者则为相应字符的 ASCII码 。
( 3) 存储器操作数存储器操作数是一个地址操作数,代表一个存储单元的地址,
通常以标识符的形式出现 。
存储器操作数可以分为变量及标号两种类型,如果存储器操作数所代表的是某个数据在数据段,附加段或堆栈段中的地址,那么这个存储器操作数就称为 变量 ;如果存储器操作数所代表的是某条指令代码在码段中的地址,那么这个存储器操作数称为 标号 。 变量所对应的存储单元内容在程序的运行过程中是可以改变的,标号通常作为转移指令或调用指令的目标操作数,在程序运行过程中不能改变 。
存储器操作数有三个方面的属性:
(1) 段地址:即存储器操作数所对应的存储单元所在段的段地址;
(2) 偏移地址:即存储器操作数所对应的存储单元在所在段内的偏移地址;
(3) 类型:变量的类型是相应存储单元所存放的数据项的字节数;而标号的类型则反映了相应存储单元地址在作为转移或调用指令的目标操作数时的寻址方式,可有两种情况,即 NEAR
( - 1 ) 和 FAR( - 2 ) 。
( 4) 常量表达式由常量操作数及运算符构成,在汇编时产生一个常量 。
如 PORT,VAL + 1,OFFSET SUM,SEG SUM,TYPE
CYCLE等 。
( 5) 地址表达式由存储器操作数与运算符构成,必须有明确的物理意义 。
例如 SUM+ 2,CYCLE- 5
表达式 SUM+ 2,CYCLE- 5的值仍然是一个存储器操作数,
该存储器操作数的段地址与类型属性分别与存储器操作数 SUM及
CYCLE相同,但偏移地址分别比 SUM及 CYCLE大 2或小 5。 表达式是在汇编时计算的,而变量单元的内容在程序的运行过程中可以改变 。
4.1.2 汇编语言的运算符 ( P128--136)
1,算术运算符,逻辑运算符和关系运算符运算符有:
算术运算,+、-,×,÷,mod,SHL,SHR
逻辑运算,and,or,xor,not
关系运算,EQ/NE/LT/GT/LE/GE(相等 /不等 /小于 /大于 /小于或等于 /大于或等于),结果为真,则返回 0FFFFH,假则为 0。
2,数值返回运算符 SEG,OFFSET,TYPE,SIZE和 LENGTH
( P131)
加在变量或标号前,返回运算对象的某个参数值 ( 如:段地址值,偏移地址值,类型属性,变量包含的单元数等 ) 。
( 1) OFFSET
格式,OFFSET 变量或标号功能,返回标号或变量的偏移地址值 。
( 2) SEG
格式,SEG 变量或标号功能,用于取标号或变量的段基值 。
例如,定义,SLOT DW 25
则,MOV AX,SLOT;从 SLOT地址中取一个字送入 AX
MOV AX,SEG SLOT;将 SLOT所在段的段地址送入 AX
MOV AX,OFFSET SLOT;
将 SLOT所在段的段内偏移地址送 AX
( 3) TYPE
格式,TYPE 变量或标号功能,加在变量前,返回变量的类型属性 。
TYPE运算符的返回值如下:
字节 ----1,字 ----2,双字 ----4,( P132表 4-2)
NEAR( 段内直接近转移,16位地址偏移量 ) ----- 1( 0FFH),
FAR( 段间直接远转移,段内 16位地址偏移量与段地址 ) ----- 2。
( 4) LENGTH
格式,LENGTH 变量功能,返回一个与存储器地址操作数相联系的单元数 。
( 5) SIZE
格式,SIZE 变量功能,返回一个为存储器操作数分配的字节数 。
LENGTH和 SIZE只应用于数据存储器操作数 ( 即用 DB/DW/DD等定义的操作数 ) 。
例如:若 MULT DW 50 DUP( 0)
则 LENGTH( MULT-WORD) = 50
SIZE( MULT-WORD) = 100
注意,SIZE( X) =( LENGTH X)? ( TYPE X)
3,修改属性运算符属性运算符用来给指令中的操作数指定一个临时属性,而暂时忽略当前的属性 。
(1) 段操作符格式:段前缀,变量或地址表达式功能:段前缀有段寄存器 CS,DS,ES,SS后根冒号,,,,用来表示某个变量或地址修改到哪个段寄存器提供的段基址中 。
例如,MOV AX,ES,[BX]
( 2) PTR
格式,类型 PTR 表达式功能,建立一个存储器地址操作数,它与其后的存储器地址操作数有相同的段地址偏移量,但有不同的类型 。
特点:不单独使用,与 8086指令一起使用。
类型,BYTE:字节
WORD:字
DWORD:双字例如,BUF DW 25
此时 BUF已定义成字单元 。 若我们想取出它的第一个字节内容,则可用
PTR对其作用,使它暂时改变为字节单元,即
MOV AL,BYTE PTR BUF
如,MOV BYTE PTR[DI],10H ; [DI]=10H
MOV WORD PTR[1000H],10H ; [1001] [1000]=0010H
JMP DWORD PTR [1000H] ;转移地址放在 1000H开始的 4个单元中,低 2
个字节为偏移量,高 2个字节为段地址 。
(3) THIS
格式:变量 /标号 EQU THIS 类型 /距离功能:是将 EQU THIS 右边的类型 /距离属性,赋给左边的变量 /
标号,该变量或标号的段地址和偏移地址与下一个存储单元的地址相同。
例如,FISRT EUQ THIS BYTE
TABLE DW 200 DUP(?)
FIRST的偏移地址值与 TABLE的偏移地址值相同,区别在于
FIRST变量为字节类型,TABLE为字类型。
(4) SHORT
格式,SHORT 标号功能,SHORT用来说明转移类指令中转向地址的属性,指出转向的目标地址与本指令之间的距离在- 128到+ 127之间,即限制在短转移范围内。
例如,L1,JMP SHORT L2

L2,MOV AX,0
(5) HIGH 和 LOW
格式,HIGH/LOW 变量或标号功能,HIGH和 LOW称为字节分离运算符,对于一个数或地址表达式,HIGH从中分离出高位字节,LOW分离出低位字节。
例如,K1 EQU 0ABCDH
K2 EQU 1234H
MOV AH,HIGH K1
MOV BL,LOW K2
汇编后形成指令
MOV AH,0ABH
MOV BL,34H
4,其它运算符
(1)圆括号 ()
功能:用来改变运算符的优先级,()内的运算符具有最高的优先级。
(2)方括号 []
功能:主要是用来表示地址表达式或多重变量的下表值。
a.表示地址例如,MOV AL,[2000H]
b.表示多重变量的下标值例如,MOV AL,M1[3]
(3)尖括号 <>及圆点,
功能,<> 运算符在结构中专用,表示结构中的变量在预置结构付本是否修改,修改成什么数值。
,在结构中专用,表示结构付本名与变量名连接在一起,作为预置的结构体付本中的各个变量。
(4)MASK和 WIDTH
MASK为字段名,返回数值为 8/16位二进制数,对应指定字段的各位制,1”,其它位置,0”。
WIDTH为记录名 /字段名,运算后返回数值表示指定记录或字段的位的长度。
格式 含义
SEG 符号 取符号的段与偏移地址
OFFSET 符号
TYPE 符号 取符号的类型 (字节数 )
SIZE符号 取符号 DUP()定义的字节数与长度
LENGTH符号 (即 size=length*type)
BYTE PTR 符号或内存 强行定义新类型
(WORD/DWORD)
LOW/HIGH 表达式 取表达式的低或高字节小结
4.2 汇编语言程序汇编步骤
1,硬件环境目前 8086汇编语言程序一般多在 IBM PC/XT及其兼容机上运行,要求机器具备基本配置即可以 。
2,软件环境主要是指支持汇编语言程序运行和帮助建立汇编语言源程序的一些软件,主要包括:
( 1) DOS操作系统
( 2) 编辑程序 EDIT.COM
( 3) 宏汇编程序 MASM.EXE
( 4) 连接程序 LINK.EXE
( 5) 调试程序 DEBUG.COM
3.运行汇编语言程序的步骤汇编语言程序要能在机器上运行,还必需将汇编源程序汇编成可执行程序 。 为此必须完成以下几个步骤 。
( 1)用编辑程序 EDIT.COM建立扩展名为,ASM的汇编语言源程序文件。
( 2)用汇编程序 MASM.EXE将汇编语言源程序文件汇编成用机器码表示的目标程序文件,其扩展名为,OBJ。
( 3)若在汇编过程中出现语法错误,根据错误信息提示(如位置、
类型、说明),用编辑软件重新调入源程序进行修改。无错误时采用连接程序 LINK.EXE把目标文件转化成可执行文件,其扩展名为,EXE。
( 4)生成可执行文件后,在 DOS命令状态下用调试程序
( DEBUG,EXE或 TD,EXE)调试或直接键入文件名执行该文件。
调试工具 DEBUG的基本使用
DEBUG.EXE在微软提供的各种操作系统中都可以找到,
是用于调试汇编语言程序的基本工具。
( 1) A命令:在 DEBUG中直接进行汇编语言程序的编写,一般只是编写一些程序片段作实验。
( 2) R命令:观察或修改各寄存器中的内容。
( 3) T命令:单步调试汇编语言程序,并观察寄存器中内容的变化情况。
( 4) D命令:观察内存单元中的内容。
( 5) E命令:修改内存单元中的内容。
( 6) B命令:断点设置
( 7) G命令:连续运行。 G[=[段地址,]偏移量 ]
( 8) Q命令:退出 DEBUG。
( 9) U命令:反汇编命令。
( 10) I命令:从 I/O口输入数据并显示。
在 DEBUG中输入下面的程序片段,单步执行并观察寄存器中内容的变化。
MOV AX,0BC85H
MOV BX,4F2DH
MOV DX,BX
XCHG AX,BX
注意:
( 1)汇编语言中数的表示:
二进制,10101100B 末尾带一个‘ B’
十进制,537D 末尾带一个‘ D’
537 末尾没有任何符号十六进制,0F5B3H 末尾带一个‘ H’
如果以大于 9的字母为最高位,那么前面需要添一个‘ 0’。
( 2)在 DEBUG中编写程序和正规的汇编语言源程序不同,只能使用十六进制数,末尾不带‘ H’符号,最高位为字母时不需要添‘ 0’。
4.3 伪指令 ( P136----P156)
伪指令 ( 伪操作 ),用于对汇编程序进行控制,在汇编程序对源程序汇编期间由汇编程序处理的操作,可以完成如数据定义,分配存储区,指示程序结束等功能 。
特点,格式和汇编指令一样,但一般不产生目的代码,即不直接命令 CPU去执行什么操作 。
机器指令,在程序运行期间由计算机来执行的指令 。
4.3.1 数据定义及定义存储单元伪 指令功能,该类伪指令用来定义存储空间及其所存数据的长度 。
格式,[变量 ] 指令 助记符 操作数 [,操作数 ][;注释 ]
其中:
变量字段是可选项,它用符号地址表示,其作用与指令语句前的标号相同,但它的后面不跟冒号 。 如果语句中有变量则编程序使其记以第一个字节的偏移地址 。
注释字段用来说明该伪操作的功能,它也是可选项 。
助记符字段说明伪指令的伪操作,常用的有以下几种,
DB,定义字节,其后的每个操作数都占有一个字节 。
DW,定义字,其后的每个操作数都占有一个字 。 低位字节在第一个字节地址中,高位字节在第二个字节地址中 。
DD,定义双字,其后的每个操作数占有二个字 。低字部分在低地址,高字部分在高地址。
DQ,定义 4字长,其后的每个操作占有四个字 。
DT,定义 10个字节长。 其后的每个操作数占有十个字节,形成压缩的 BCD码 。
如,DATA1 DB 5,6,8,100
DATA2 DW 7,287
TABLE DB?
”?,表示在 TABLE单元中没有存放初值,TABLE单元中的内容是随机的,在汇编过程中在 TABLE单元处留出一个单元,
用户可以用这样的存储单元存放中间数据,运算结果 。
当一个定义的存储区内的每个单元要放置同样的数据或在存储器中预留多个存储单元时,可用 DUP操作符 。
格式,COUNT DUP(? )
其中,COUNT为重复的次数,,( ),中为要重复的数据 。
如:
BUF DB 100 DUP(0) ;表示以 BUF为首地址的 100个字节中存放 00H数据
BUF1 DB 100 DUP(? ) ;在存储器中留出 100个单元用以存放中间数据,运算结果 。
4.3.2 符号定义伪指令 EQU,=
功能,给符号定义一个值 。 在程序中,凡是出现该符号的地方,
汇编时均用其值代替 。
格式,[变量 ] EQU 操作数 [,操作数 ] [;注释 ]
如,TIMES EQU 50
另有一个与 EQU类似的伪操作,=” 也可以作为赋值操作使用。其区别是 EQU伪操作中的表达式名是不允许重复定义的,而
,=”伪操作则允许重复定义。
如,TIMES =100 ;与 TIMES EQU 50功能相同 。
EMP=7
EMP=EMP+1
注意,伪指令 DB与 EQU的区别
BUF1 DB 0AFH;表示在存储单元 BUF1 中存放了数值 AFH。
BUF1 EQU 0AFH;表示 BUF1就代表了 AFH。
4.3.3 段定义伪指令 SEGMENT和 ENDS
一般地,一个完整的汇编源程序由 3个段组成,即堆栈段,
数据段和代码段 。 段定义伪指令可将源程序划分成若干段,以便生成目的代码和连接时将各同名段进行组合 。
格式,段名 SEGMENT [定位类型 ] [组合类型 ] [类别 ]
段名 ENDS
注意,SEGMENT和 ENDS应成对使用,缺 —不可 。 其中段名是不可省略的,SEGMENT和 ENDS前的段名必须相同 。 其它是可选项,是赋予段名的属性,可以省略 。
例如,DATA SEGMENT
DW 20DUP(?)
DATA ENDS
(1)定位类型定位类型参数是对该段起始地址定位。通常段名确定了该段的首地址,整个逻辑段存放在首地址开始的一片连续的存储单元中,定位类型参数主要有以下 4种:
PARA:指定定位段的起始地址必须在节 (MASM把 1M的存储空间从 0开始,每 16个单元叫一节 )的整数边界,当定位类型参数缺省时,就当成 PARA。
BYTE:指定该段起始地址定位在存储单元的任何字节地址。
WORD:指定该段起始地址定位在字的边界,即段的首地址必须是偶数。
PAGE:指定该段起始地址定位在页的边界,即段的首地址必须是 256的整数倍。
(2)组合类型组合类型参数主要提出了各个逻辑段之间的组合方式,例各段覆盖或顺序组合等。
NONE,该段与其它同名段不进行连接,各段独立存在于存储器中,NONE可以作为缺省参数。
PUBLIC:该段与其它模块中的同名段连接时,由低地址到高地址连接起来,组成一个逻辑段,连接次序由连接命令指定,连接长度满足定位类型的要求。
COMMON:该段与其它模块中的同名段有相同的起始地址,
采用覆盖的方式在存储器中存放,连接长度为各分段中最大长度 。
AT表达式,定位该段的起始地址表达式所指定的节的边界上。
STACK:指定该段为堆栈段,此参数在堆栈中不可以省略,多个模块只需设置一个堆栈段,各个模块中的堆栈段采用覆盖的方式组合,容量为各个模块所设置的最大堆栈段容量。
MEMORY,定位该段与其它模块中同名段有相同的首地址,
采用覆盖方式在存储器中组合连接,其功能与 COMMON参数类似,区别是第一个带 MEMORY参数的逻辑段覆盖在其它同名段的最上层,其它带此参数的同名段按照 COMMON方式处理。
4.3.4 设定段寄存器伪指令 ASSUME
格式,ASSUME 段寄存器:段名 [,段寄存器:段名,…… ]
功能,通知汇编程序,哪一个段寄存器是该段的段寄存器,以便对使用变量或标号的指令汇编出正确的目的代码 。 在段名中,一般 CODE表示代码段,DATA表示数据段,STACK表示堆栈段 。
注意 1,由于 ASSUME伪指令只指明某一个段地址应存于哪一个段寄存器中,并没有包含将段地址送入该寄存器的操作 。 因此要将真实段地址装入段寄存器还需用汇编指令来实现 。 这一步是不可缺少的 。
例如,CODE SEGMENT
ASSUME CS,CODE,DS,DATA,SS,STACK
MOV AX,DATA ; DATA段值送 AX
MOV DS,AX ; AX内容送 DS,DS才有实际段值
CODE ENDS
注意 2,当程序运行时,由于 DOS的装入程序负责把 CS初始化成正确的代码段地址,SS初始化为正确的堆栈段地址,因此用户在程序中就不必设置。但是,在装入程序中 DS寄存器由于被用作其它用途,因此,在用户程序中必须用两条指令对 DS进行初始化,以装入用户的数据段地址。当使用附加段时,也要用 MOV
指令给 ES赋段地址。
例题:两个无符号二进制数相乘 (P142)
DATA SEGMENT ;数据段
D1 DW 1234H
D2 DW 5678H
P1 DD?
P2 DD?
DATA ENDS
STACK SEGMENT STACK?STACK?
DW 100 DUP (?)
STACK ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,SS:STACK
MAIN PROC FAR
START,MOV AX,STACK ; 初始化 SS,SP
MOV SS,AX
PUSH DS
SUB AX,AX ;返回 DOS
PUSH AX
MOV AX,DATA ;初始化 DS
MOV DS,AX
L1,MOV AX,D1
MUL D2
MOV BX,OFFSET P1
MOV [BX],AX ;存放计算结果的低位
MOV [BX+2],DX ;存放计算结果的高位
RET
MAIN ENDP
CODE ENDS
END START
4.3.5 定义过程的伪指令 PROC和 ENDP
在程序设计中,可将具有一定功能的程序段看成为一个过程(相当于一个子程序),它可以被别的程序调用。一个过程由伪指令 PROC和 ENDP来定义。
格式,过程名 PROC [类型 ]
过程体
RET
过程名 ENDP
其中,过程名是为过程所起的名称,不能省略,过程的类型由
FAR(远过程,为段间调用)和 NEAR(近过程,在本段内调用)来确定,如果缺省类型,则该过程就默认为近过程 。
ENDP表示过程结束。过程体结束前应有一条 RET指令,以便返回被调用处。过程可以嵌套,也可以递归使用。远过程调用时被调用过程必定不在本段内。
例如:一个延时 100ms的子程序,其过程可定义如下,
DELAY PROC
PUSH BX
PUSH CX
MOV BL,10;延时 10ms,改变 BL和 CX中的值,即可改变延时时间 。
AGAIN,MOV CX,2801 ;
WAIT,LOOP WAIT
DEC BL
JNZ AGAIN
POP CX
POP BX
RET
DELAY ENDP
CALL DELAY ;调用该过程
4.3.6 NAME
格式,NAME 程序名功能:为源程序目标模块赋名字放在程序开始,在输出汇编语言程序的列表文件时,将在每一页的开头打印出该程序名 。 若源程序中省略 NAME伪指令,汇编程序将源文件名作目标模块的名字 。
4.3.7 ORG伪指令
ORG伪指令规定了在某一段内,程序或数据代码存放的起始偏移地址 。
格式,ORG <表达式 >
例如,DATA SEGMENT
BUF1 DB 23,56H,‘ EOF?
ORG 2000H
BUF2 DB?STRING?
DATA ENDS
上述变量定义中,BUF1从 DATA段偏移地址为 0的单元开始存放,而 BUF2则从 DATA段偏移为 2000H的单元开始存放,两者不是连续存放 。
4.3.8 汇编结束伪指令 END
该伪指令表示源程序的结束,令汇编程序停止汇编 。 因此,
任何一个完整的源程序在最后均应有 END指令 。
格式,END [表达式 ]
其中,表达式表示该汇编程序的启动地址 。
例如:
END START ;表明该程序的启动地址为 START。
注,P147----P155没有讲,有精力的同学可以阅读 。
4.4 DOS系统功能调用和 BIOS中断调用计算机的硬件环境必须在操作系统的管理下才能工作。
裸机,缺少操作系统的计算机。
操作系统可分为,磁盘操作系统( DOS),Windows,Linux等。
DOS命令,DIR,CD,COPY,PRINT,FORMAT 等 。
DOS的管理功能,管理 I/O设备、磁盘和文件。一部分固化在
ROM(称为 DOS BIOS)中,另一部分存放在磁盘中。
DOS 的 BIOS应用,系统启动时,被调入内存,用户的应用程序和 DOS操作系统通过软件中断来调用它们。
DOS常用的软件中断命令,INT 20H~ INT 2FH。调用时,只要给定入口参数,写一条中断指令 INT n即可。
DOS常用的软中断命令软中断指令 功能 入口参数 出口参数
INT 20H 程序正常退出 无 无
INT 21H 系统功能调用 AH=功能号,相应入口号 相应出口号
INT 22H 结束退出
INT 23H Ctrl-Break处理
INT 24H 出错退出
INT 25H 读磁盘 AL(驱动器号)
CX(读入扇区号)
DX(起始逻辑扇区号)
DS:BX(内存缓冲区地址)
CF=0 成功
CF=1 出错
INT 26H 写磁盘 同上 同上
INT 27H 驻留退出 DS:DX(程序长度)
4.4.1 DOS系统功能调用( INT 21H)
DOS软件中断中,INT 21H功能最强。
使用,传入口参数到指定的寄存器中,子功能号( 0----57H)送入 AH中,INT 21H,根据出口参数分析功能调用的执行情况。
详见 P516 附录 F
( 1)键盘输入单字符( 1号) ( P159)
使用方法:
MOV AH,1
INT 21H
功能,执行 1号系统调用,系统等待键盘输入。当程序员按一下键时,系统首先检查是否是 Ctrl+Break键,如果是则退出,否则将键入的 ASCII码送入 AL中,并在屏幕上显示。
( 2)设置日期( 2BH) ( P163)
功能,设置有效日期。如,2006年 10月 09日。
使用方法,入口参数,年送 CX,月送 DH,日送 DL。
MOV CX,2006
MOV DH,10
MOV DL,09
MOV AH,2BH
INT 21H
执行结果是将有效日期设置为 2006.10.09。若设置成功,则
AL=00,若设置不成功,则 AL=0FFH。
( 3)取得日期( 2AH) ( P163)
功能,将当前的有效日期取到 CX,DX中,存放格式与设置日期相同。
使用方法:
MOV AH,2AH
INT 21H
( 4)设置时间( 2DH) ( P163)
功能,设置有效时间。如,8点 15分 20.5秒。
使用方法,入口参数,小时送 CH,分 送 CL,秒 送 DH,百分之一 秒 送 DL。
MOV CX,0815H
MOV DX,2050H
MOV AH,2DH
INT 21H
执行结果是将有效时间设置为 8点 15分 20.5秒。若设置成功,
则 AL=00,若设置不成功,则 AL=0FFH。
( 5)取得时间( 2CH) ( P164)
功能,将当前的有效时间取到 CX,DX中,存放格式与设置时间相同。
使用方法:
MOV AH,2CH
INT 21H
注:有关中断调用和系统调用可以查阅相关手册。
( 6)返回操作系统( 4CH) ( P165)
功能,结束当前正在执行的程序,并返回操作系统,屏幕上显示操作系统提示符。
使用方法,无入口参数
MOV AH,4CH
INT 21H
4.4.2 BIOS中断调用 ( P165)
BIOS( Base Input Output System)存放在系统主板上一块 ROM( Read Only Memory)中,硬件接口对应的中断服务程序和相关的底层系统调用都固化在这块芯片中。由于空间有限,BIOS中的汇编语言程序都设计得短小精干,十分巧妙。
BIOS调用(见 P165表 4-11)是面向设备的,DOS调用是面向应用的。
1,键盘的 BIOS调用( 16H号) (见 P166表 4-12)
有关键盘的中断调用中,除了 BIOS中 09H号中断服务程序直接访问键盘接口中的端口外,其余的无论是 DOS调用还是
BIOS调用,都是以内存中的键盘缓冲区为访问对象。
( 1) 00H号子功能功能,等待按键,把所按键的 ASCII码(由显卡解释的显示编码)和扫描码
(反映按键物理位置的键盘编码)分别返回到 AL和 AH中。
使用方法:
MOV AH,00H
INT 16H
注意,该调用不带回显,BIOS调用目标非常基础和明确,针对键盘的调用就只针对键盘,不会针对其它设备。
( 2) 01H号子功能功能,检测键盘缓冲区有无数据,如果没有数据,把 ZF标志置 1;如果有数据,
把 ZF标志置 0,并且把队首的 ASCII码返回到 AL中。
使用方法:
MOV AH,01H
INT 16H
注意,该调用返回键盘缓冲区的队首 ASCII码后,并不把它从键盘缓冲区清除掉,如果使用循环结构取按键的 ASCII码就有可能出错误。
2,显示的 BIOS调用( 10H号系统调用) (见 P166表 4-13)
( 1)设置显示模式( 00H号子功能)
功能,设置当前的显示模式。
使用方法:
MOV AH,00H
MOV AL,显示模式编号
INT 10H
( 2)读取当前显示模式编号( 0FH子功能)
功能,取得当前显示模式编号存放在 AL中,当前显示模式对应的列数存放在 AH中,当前显示页面的编号存放在 BH中。
在文本显示模式下,才有显示页面的概念。显存中有多个页面,
当前显示在屏幕上的只是其中一个,一般只使用第 0个页面。有时可以通过切换显示页面来快速的改变所显示内容。
使用方法:
MOV AH,0FH
INT 10H
用途,在应用程序改变显示模式前,需要先保存原有的显示模式,
以便应用程序退出时恢复以前的显示模式。
( 3)置光标位置( 02H)
功能,设置光标当前位置,也就是下一次字符显示的位置,仅在文本显示模式下可用。
使用方法:
MOV AH,02H
MOV BH,页号
(当前页号可用 0FH子功能获得)
MOV DH,行号
MOV DL,列号
INT 10H
DATA SEGMENT
BUF DB 30
ACTL DB?
STR DB 30 DUP(?)
MESS DB "What's you name?",0DH,0AH,'$'
DMESS DB 0DH,0AH,"Hello",'$'
DATA ENDS
CODE SEGMENT
ASSUME CS,CODE,DS,DATA
MAIN PROC FAR
PUSH DS
MOV AX,0
PUSH AX
MOV AX,DATA
例题:简单人机对话的实现 (NAME)
屏幕显示,What?s you name?
用户键入,Liping
屏幕显示,Helloliping!
MOV DS,AX
LEA DX,MESS
MOV AH,9
INT 21H ;显示,What's you name?”
LEA DX,BUF
MOV AH,10
INT 21H ;从键盘接收用户输入的信息
MOV AL,ACTL ;取得键入字符串的实际长度
CBW
MOV SI,AX
LEA BX,STR
MOV [BX+SI],BYTE PTR '!' ;在键入的字符串后加‘!’
MOV [BX+SI+1],BYTE PTR '$' ;在‘!’后加 '$',以便显示
LEA DX,DMESS ;显示 "Hello!"
MOV AH,9
INT 21H
LEA DX,STR ;显示键入的字符串
MOV AH,9
INT 21H
RET
MAIN ENDP
4.5 程序设计方法汇编语言程序设计的步骤:
1,分析问题,建立数学模型把要解决的问题用一定的数学表达式描述或者制定解决问题的规则。
2,确定算法就是确定解决问题的方法和步骤。
3,编制程序流程图把解题的方法、步骤用框图形式来表示。
4,合理分配存储空间和寄存器充分利用存储空间,节约使用存储单元是我们编制一个好的应用程序就注意的问题。 CPU 中的寄存器数量是有限的,所以在程序中要合理分配寄存器的用途。
5,编制源程序根据程序流程图,逐条编制源程序。正确使用各种寻址方式和指令格式。
6,调试程序静态检查,上机调试。一般先调试各模块,然后联调。
7,程序运行,分析结果汇编语言程序设计是应遵循的原则:
1,程序结构模块化,程序易读,易调试和维护。
2,执行速度快 (程序的总时钟周期数越小越好) 。
3,占用内存空间小 (程序的总指令字节数越小越好) 。
程序流程图规定画法示意图如下:
4.5.1 简单(顺序)程序设计简单程序设计是没有分支,没有循环的直线运行程序,程序执行按照 IP内容自动增加的顺序进行 。
例 1 利用查表法计算平方值 。 已知 0 ~ 9的平方值连续存在以 SQTAB开始的存储区域中,求 SUR单元内容 X的平方值,并放在 DIS单元中 。 假定 0≤ X≤ 9且为整数 。
分析:建立平方表,通过查表完成 。
STACK SEGMENT
DB 100 DUP(? )
STACK ENDS
DATA SEGMENT
SUR DB?
DIS DB?
SQTAB DB 0,1,4,9,16,25,36,49,64,81 ; 0~9的平方表
DATA ENDS
CODE SEGMENT
ASSUME CS,CODE,DS,DATA,SS,STACK,ES,DATA
BEGIN,PUSH DS
MOV AX,0
MOV AX,DATA
MOV DS,AX ;为 DS送初值
LEA BX,SQTAB ;以下程序部分完成查表求平方值
MOV AH,0 ;亦可用查表指令完成 ( 如下程序段 )
MOV AL,SUR ; AL=X LEA BX,SQTAB
ADD BX,AX ; MOV AL,SUR
MOV AL,[BX] ; XLAT
MOV DIS,AL ; MOV DIS,AL
POP DS
CODE ENDS
END BEGIN
4.5.2 分支程序设计分支程序的基本思想是根据逻辑判断的结果来形成程序的分支,
如右下图,若 A成立,则执行 P1;否则执行 P2。
例 2 试编写程序段,实现符号函数 。
分析:变量 X的符号函数可表示为:
1 X>0
Y= 0 X=0
-1 X<0
程序可通过对符号标志的判别来确定执行哪一分支 。
START,MOV AX,BUF ; ( BUF) =X
OR AX,AX
JE ZERO ; X= 0,则转 ZERO
JNS PLUS ; X为正数,则转 PLUS
MOV BX,0FFFFH ; X为负数,则- 1送 BX
JMP CONT1
ZERO,MOV BX,0
JMP CONT1
PLUS,MOV BX,1
CONT1,……
例 3 利用表实现分支子程序 R1—R8的入口地址表为:
P1 子程序 R1的入口偏移地址
P2 子程序 R2的入口偏移地址
P3 子程序 R3的入口偏移地址
…… ……
P7 子程序 R7的入口偏移地址
P8 子程序 R8的入口偏移地址根据 AL中各位被置位情况,控制转移到 8个子程序 P1~P8之一中去 。 转移表的结构如上表所示 。
分析:对于这种程序关键要找出每种情况的转移地址,从图中可见表地址 =表基地址 +偏移量,而偏移量可由 AL各位所在位置 × 2求得 。
流程图见上图 。
DATA SEGMENT
BASE DW SR0,SR1,SR2,SR3,SR4,SR5,SR6,SR7
DATA ENDS
CODE SEGMENT
ASSUME CS,CODE,DS,DATA,ES,DATA
START,PUSH DS
XOR AX,AX
MOV AX,DATA
MOV DS,AX
LEA BX,BASE ;表头送 BX
IN AL,PORT
LP1,RCR AL,1 ;右移一位
JC LP2 ;移出位是 1?
INC BX
INC BX ;修改指针
JMP LP1
LP2,JMP WORD PTR[BX];实现散转
POP DS
CODE ENDS
END START
根据跳转表构成方法不同,实现分支的方法也有所改变,若跳转表地址由段值和偏移量四个字节构成,程序应如何实现?
4.5.3 循环程序设计循环程序是经常遇到的程序结构,一个循环结构通常由以下几个部分组成 。
1,循环初始化部分一般要进行地址指针,循环次数及某标志的设置,相关寄存器的清零等操作 。 只有正确地进行了初始化设置,循环程序才能正确运行,及时停止 。
2,循环体是要求重复执行的程序段部分,对应于要求重复执行的操作 。
3,循环控制部分由该部分修改并判断控制循环的条件是否满足 。 以决定是否继续循环 。
4,循环结束部分如保存循环运行结果等 。
循环程序有两种结构形式,一种是把循环控制部分放在循环体的前面,先判断执行循环体的条件,
满足条件就执行循环体,否则就退出循环,
如图 (2)所示。这种结构的循环程序,其循环体有可能并不执行。
而另一种则是在执行循环体之后,再判断循环控制条件是否满足,若满足条件,则继续执行循环操作,否则,则退出循环。
如图 (1)所示。其循环程序的循环体至少必须执行一次。
循环程序有两种结构形式:
例 4 设内存 BUF开始的单元中依次存放着 30个 8位无符号数,求它们的和并放在 SUM单元中,试编写程序 。
分析:这是一个求累加的程序 。 ( 设计思想同 C语言 ) 程序如下:
MOV SI,BUF ;设地址指针
MOV CX,30 ;设计数初值
XOR AX,AX ;设累加器初值
AGAIN,ADD AL,[SI]
ADC AH,0
INC SI
DEC CX ( 还可以变换为? )
JNZ AGAIN ;循环累加
MOV SUM,AX
MOV AH,4CH
INT 21H ;返回系统
HLT ;停机例 5 在 DS所决定的数据段,从偏移地址 BUF开始顺序存放 100个无符号 16位数,
现要编写程序将这 100个字数据从大到小排序 (设置内外两层循环 ) 。
分析:排序的方法有很多,在这里,我们采用冒泡法 。
程序如下:
LEA DI,BUF ; DI作为指针,指向要排序的数据
MOV BL,99 ;循环控制初值
NEXT0,MOV SI,DI
MOV CL,BL
NEXT3,MOV AX,[SI] ;取一个数
ADD SI,2
CMP AX,[SI] ;与下一个数进行比较
JNC NEXT5 ;大于等于时转移
MOV DX,[SI] ;否则,两数交换 (注意 SI当前值发生变化 )
MOV [SI-2],DX
MOV [SI],AX
NEXT5,LOOP NEXT3 ;控制进行交换的次数
DEC BL ;修改交换的次数
JNZ NEXT0
HLT
4.5.4 子程序设计子程序,完成一定功能的相对独立的程序段 。
通过把一些固定的,经常使用的功能做成子程序的形式,可以使源程序及目标程序大大缩短,提高程序设计的效率和可靠性 。 子程序可以嵌套 ( 见 P186) 。
入口参数和出口参数,入口参数是由主程序传给子程序的参数,而出口参数是子程序运算完传给主程序的结果 。 另外,子程序所使用的寄存器和存储单元往往需要保护,以免影响返回后主程序的运行 。
主程序在调用子程序时,一方面初始数据要传给子程序,另一方面子程序运行结果要传给主程序,因此,主子程序之间的参数传递是非常重要的 。
入口参数传递一般有三种方法实现 ( 出口参数类似 ),
(1) 利用寄存器 。 这是一种最常见方法,把所需传递的参数直接放在主程序的寄存器中传递给子程序 。
(2) 利用存储单元 。 这种参数传递方法,把所需传递的参数直接放在子程序调用指令代码之前 。
(3) 利用堆栈 。 这种方法将参数压入堆栈,在子程序运行时从堆栈中取参数 。
例 6 两个 6字节数相加 。
分析:将一个字节相加的程序段设计为子程序 。 主程序分 3次调用该子程序,但每次调用的参数不同 。
程序如下:
DATA SEGMENT
ADD1 DB FEH,86H,7CH,35H,68H,77H
ADD2 DB 45H,BCH,7DH,6AH,87H,90H
SUM DB 6 DUP( 0)
COUNT DB 6
DATA ENDS
STACK SEGMENT
DB 100 DUP(? )
STACK ENDS
CODE SEGMENT
ASSUME CS,CODE,DS,DATA,SS,STACK
MADD,MOV AX,DATA
MOV DS,AX
MOV AX,STACK
MOV SS,AX
MOV SI,OFFSET ADD1
MOV DI,OFFSET ADD2
MOV BX,OFFSET SUM
MOV CX,COUNT ;循环初值为 6
CLC
AGAIN,CALL SUBADD ;调用子程序
LOOP AGAIN ;循环调用 6次
MOV AX,4C00H ;带返回码结束
INT 21H
HLT
完成一个字节相加子程序:
子程序入口参数,SI,DI,BX 出口参数,SI,DI,BX
SUBADD,PUSH AX ;保护 AX的值
MOV AL,[SI] ; SI是一个源操作数指针
ADC AL,[DI] ; DI是另一个源操作数指针
MOV [BX],AL ; BX是结果操作数指针
INC SI
INC DI
INC BX
POP AX ;恢复 AX的值
RET
CODE ENDS
END
作业:
1,试分析下列程序段:
ADD AX,BX
JNC L2
SUB AX,BX
JNC L3
JMP SHORT L5
如果 AX,BX的内容给定如下:
AX BX
( 1) 14C6H 80DCH
( 2) B568H 54B7H
问该程序在上述情况下执行后,程序转向何处?
2,编写程序,使用三种以上的方法,将存储器中 2000H开始的地址单元中的 100字节数据复制到 3000H开始的存储器地址单元中。
3,在 DATA开始的 4个单元中存放着一个 32位数,求出其中的,1”
的个数,并存入 COUNT单元中。
P206
15 22