微型计算机原理及应用学习笔记 汇编语言程序设
立即购买
《自考视频课程》名师讲解,轻松易懂,助您轻松上岸!低至199元/科!
一、概述
单一的指令其功能比较简单,要解决一个实际问题总是需要一组指令按一定的思路有 机地组合起来才能完成,这样一组完成特定功能的指令有序集合称为程序。
(一)程序设计的步骤
从具体问题到编好程序要经过如下基本步骤:
(1) 分析课题——弄清问题的性质、目的,已知数据,运算精度以及速度等方面的要 求。
(2)确定算法——把实际问题转化为计算机求解的步骤和方法,即算法,而程序是用来描述算法的。
(3)画流程图——流程图是算法的一种直观而形象的表示方法,是对程序执行过程的 一种形象化的描述,又称为框图。
(4)编写程序——熟悉8086/8088的指令系统及程序设计常用技巧按流程图编写程 序。要求做到简单明了、层次清晰、运算迅速、少占内存。要编写高质量的汇编语言程序,必须加深对指令系统功能的理解,注意内存工作单元和工作寄存器的分配。
(5)上机调试、修改——可以通过单扳机或系统机进行调试、修改直至通过。
(二)程序的基本结构
程序的基本结构有四种:顺序结构,分支程序结构,循环程序结构,子程序结构。现 分节筒述于下。
二、顺序结构程序
顺序结构的程序又称简单程序,这种结构的程序是顺序执行的, 无分支,无转移,无循环,程序本身的逻辑很简单,它只依赖于计算机能够顺序执行指令(语句)的特点,只要语句安排的顺序正确即可。
例1 内存中自TABLESQ开始的16个单元连续存放着自然数0到15的平方值,任
给一数X(0≤X≤15)在XX单元中,查表求出X的平方值,将结果存入YY单元中。
首先在数据段中建立平方表,表的首地址为TABLESO,然后用程序找到X2值在平方表中的位置,即计算表地址,表地址 = 表起始地址(TABLESQ)+X,据此可写出如下程序。
DATA SEGMENT
TABLESQ DB O,1,4,9,16,25,36,49,
DB 64,81,100,121,144,169,225
XX DB ?
YY DB ?
DATA ENDS
STACK SEGMENT PARA STACK‘STACK’
DB 50 DUP(?)
STACK ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,SS:STACK
START PROC FAR * *
BEGIN: PUSH DS * *
MOV,AX,0 * *
PUSH AX * *
MOV AX,DATA
MOV DS,AX
MOV BX,OFFSET TABLESQ
MOV AH,0
MOV AL,XX
ADD BX,AX
MOV AL,[BX]
MOV YY,AL
RET * *
START ENDP * *
CODE ENDS
END BEGIN
其中带 * * 的六行语句的作用是为了在用户程序运行结束后,正确返回操作系统。 汇编语言源程序经过汇编、连接后生成 .EXE文件,可在DOS下直接键入文件名运行,例如命令 A>EXAMPLE 将把带有.EXE的文件EXAMPLE装入内存,并从程序中指定地址开始运行。装入文件并设置启动地址是由操作系统的COMMAND.COM文件完成的。COMMAND.COM文件在装入.EXE文件前,首先确定最低可用地址作为被装入程序的可用内存起点(该区段称为程序段)并在程序内从段内偏移地址处开辟100H字节的程序段前缀(PSP),在该PSP的前两字节存放一条 INT 20H 指令(即退出当前程序返回操作系统)。而COMMAND.COM把.EXE文件装入后,自动设置DS和ES寄存器指向程序段前缀,即此时的DS和ES值为程序段前缀之段基值。用户程序结束为RET,为使RET后程序转向程序段前缀中INT 20H指令,必须使CS为程序段前缀的段基值,IP为INT 20H的段内偏移值00H,因此必须把用户程序设计为过程,即加下START PROC FAR,在用户程序的前三条指令写入PUSH DS,MOV AX,0和PUSH AX,把DS = 程序段前缀之段基值,AX = 00H进栈,在用户程序最后用RET指令出栈到CS和IP,以保证程序转向INT 20H,进而返回操作系统。
代码段结束的处理还可用DOS系统功能调用,在结尾处安排如下两条指令
MOV AH、4CH
INT 21H
执行至此,返回调用程序,常用于应用DEBUG进行程序调试,可返回DEBUG。这 时,可不用把用户程序设计为过程,即去除带 * * 的六行语句。
例2 把非压缩十进制数转换为压缩十进制数,被转换数存放在DATl开始的两个单 元,转换后的数存回DATl单元。
程序如下:
MOV AX,DATI ;(AX)=0109H
MOV CL,4 ;(CL)=4
SAL AH,CL ;(AH)=10H
ROL AX,CL ;(AX)=0091H
ROL AL,CL ;(AL)=19H
MOV BYTE PTR DATl,AL ;(DATl)=19H
┅
DATl DW 0109H
三、分支结构程序
在实际的程序设计中,始终是顺序执行的情况是很少的,大部分程序在执行过程中,总是要求计算机作出一些判断,井根据判断作出不同的处理,这就引出了分支程序的概念。
(一)分支程序的二要素
分支结构程序是具有判断和转移功能的程序。
1.判断——根据运算结果的状态标志
判断前一定要经过运算(能影响状态标志的运算),状态标志反映了运算结果的特性。 这些状态标志是:进位标志CF、奇偶标志PF、零标志ZF、符号标志SF以及溢出标志OF。
2.转移 — 主要由条件转移指令来实现(也可用无条件转移指令JMP)
在8086/8088的指令系统中,条件转移指令可分为两大类,一类是按单标志位来判断 的,如JAE,JC,JZ,JO,JS等;另一类是按多标志位来判断,如JGE,JG,JA等,详见表3-6所示。
分支结构程序框图如图4—1所示。
(二)利用比较转移指令实现分支
这是实现分支的一种常用方法,用于比较、判断的指令是CMP(比较指令)、CMPS(串比较指令)以及SCAS(串搜索指令)等。转移指令已如前述。而分支的次数可由具体问题决定是单重分支还是多重分支。n次判断可形成n+l路分支。
例3 符号函数的处理
有一符号函数
1, 当 X>O (-128≤X≤127)
Y = O, 当 X=0
-1, 当 X<O
设给定值x存放于LY单元,函数Y值存放于YY单元,则按X的不同取值给Y赋值的程序如下:
MOV AL.XX
CMP AL,0
JGE BIGR
MOV AL,0FFH
MOV YY,AL ;X<0时,—1送入YY单元
HLT
BIGR:JE EQUL
MOV AL,1
一 MOV YY,AL ;X>O时,l送人YY单元
HLT
EQUL:MOV YY,AL ;X=O时,O送人YY单元
HLT
这是一个多重分支的符号函数赋值程序,其流程图见图4-2 所示。
例4数据块传送程序
要求把内存中某一区域的源数据块传送到另一区域。在编写该程序时,必须判别一下 源数据区同目的数据区之间有无重叠,以决定是增量传送还是减量传送。程序清单如下:
DATA SEGMENT
STRG DB l000 DUP(?)
STGl EQU STRG+7
STG2 EQU STRG+25
SIRSE EQU 50
DATA ENDS
STACK SEGMENT PARA STACK‘STACK’
STARN DB l000 DUP(?)
STACK ENDS
COSEG SEGMENT
ASSUME CS:COSEG,DS:DATA,ES:DATA,SS:STACK
GOO PROC FAR
BEGIN: PUSH DS
MOV AX,0
PUSH AX
MOV AX,DATA
MOV DS,AX
MOV ES,AX
MOV CX,STRSE
MOV SI,OFFSET STGl
MOV DI,OFFSET STG2
CLD ;增量方式
PUSHI SI
ADD SI,STRSE-1
CMP SI,DI
POP SI
JB OK
STD ;减量方式传送
ADD DI,STRSE-1 ;指向数据块底部
ADD SI,STRSE-1
OK:REP MOVSB ;重复传送50个数据
RET
GOO ENDP
COSEG ENDS
END BEGIN
(三)利用跳转表实现分支
1.跳转表的建立
在内存的一个连续区中,连续存放一系列跳转地址、跳转指令或关键字,组成一个决
定程序分支的跳转表。
例5 某工厂有8种产品的加工程序RO到R7分别存放在以SBRO,SBRl,…,SBR7为首地址的内存区域中,这8个首地址的偏移量连续存放在以BASE为首地址跳转表内,如图4-3 所示。
2.跳转表的使用
上例中,8种产品的编号为0,1,2,…,7 如果已知目前要加工的产品编号,要求编写程序,利用跳转表自动转入该产品的加工程序。
在跳转表已知的情况下,关键问题要计算所要求的加工程序的入口地址在跳转表中的地址,即计算表地址。从图 4-3可见:
表地址=表基地址+偏移量
表基地址即跳转表的首地址,偏移量即对应的程序入口地址在表中的地址与表基地址
的距离。在本例中,分析表的结构可见,偏移量=产品编号 * 2,已知偏移量后,即可求出表地址。据此可写出[例5]的程序清单
DATA SEGMENT
BASE DW SBR0,SBRl,SBR2,SBR3
DW SBR4,SBR5,SBR6,SBR7
BN DB ?
DATA ENDS
STACK SEGMENT PARA STACK ‘STACK’
DB l00 DUP(?)
STACK ENDS
COSEG SEGMENT
ASSUME CS:COSEG,DS:DATA,SS:STACK
START PROC FAR
BEGIN:PUSH DS
MOV AX,O
PUSH AX
MOV AX,DATA
MOV DS,AX
MOV AL,BiN
MOV AH.O
ADD AL,AL
MOV BX,OFFSETBASE
ADD BX,AX
MOV AX,[Bx]
JMP AX
RET
START ENDP
COSEG ENDS
END BEGIN
3.根据跳转表内指令分支
在计算机系统的监控程序,键盘管理程序中经常要用到跳转表。在跳转表中既可存放 跳转地址(如例5),也可存放一系列的跳转指令JMP。
例6 有一监控程序的键盘程序控制12个命令键——执行键(EXEC)、存储器检查键(MEM)等等,按下任一命令键相当于发出一条键盘命令,而这些命令的实现分别由监控程序中的12个于程序完成,这些子程序的入口地址分别为ADR0,ADRl,…,ADRll。据此可以 组成命令键跳转表如图4-4所示。该命令键跳转表存放在以BASE0为首地址的内存区域中,表内存放着12条转移指令:
JMP ADR0,JMP ADRl, …,JMP ADRll。
设12个命令键的编号分别为0 ~ 11。命令跳转表中每三个单元存放一条转移指令,如果命令键的编号X已送入寄存器AL,则实现转向相应的命令子程序的程序如下
MOV AH,0
MOV BL,AL
ADD AL,AL
ADD AL,BL
ADD AL, BL
MOV BX,OFFSET BASE0
ADD BX,AX ;表地址计算
JMP BX
四、循环结构程序
(一)概述
1.循环程序
实现重复执行某一段程序的程序结构称为循环程序。用来处理带重复性的问题,可以缩短源程序及目标程序。
循环结构程序的框图之一如图4-5所示。
循环程序由五部分组成:
(1)初始化部分——这是循环的准备部分,为程序操作、地址指针、循环计数、结束条件等设置初始值。
(2)循环工作部分——这是循环程序的主体,完成程序的基本操作,循环多少次,这部分的语句就执行多少次。
(3)循环修改部分——修改循环工作部分的变量地址等,为下一轮重复操作作准备。
(4)循环控制部分——修改计数器或判断循环结束条件以决定是继续循环还是终止循环,是典型的分支结构,不过继续
循环的分支去向是循环体的头部。通常把循环工作部分称为循环体,循环体的第一条指令为循环体的头部。
(5)循环结束部分——循环终止后,对循环结果的处理部分。
2.循环程序的几种结构形式
循环程序除图4-5所示的基本结构形式外,还有两种常用的结构形式,如图4-6和图
4-7所示。
这两种结构的特点是工作部分放在循环控制部分之后,这样可以允许循环次数为零的 循环程序或不需要循环时不进入循环工作部分。
3.循环程序分类
(1)按循环控制条件分类
1)计数循环——重复次数已知,用计数值控制循环的开始与终止。
2)条件控制循环——重复次数未知或不确定,需找出循环控制的条件。
(2)按循环体内结构分类
1)单重循环——循环体内只是顺序程序或分支程序,不再有循环程序。
2)多重循环——循环体内再套有循环程序。可把重复处理部分独立出来,前面加上 循环准备,后面加上结束判断。
4.循环程序设计要点
循环程序设计要点可归结为两点:
(1)怎样把求解的问题变为循环结构的程序类型——怎样实现重复,即计算方案的循 环化。这里循环工作部分的设计尤为重要,因为这是多次重复的部分,注意程序的精练,及循环体头部的确定。
(2)怎样使循环准确地执行完毕,这就要注意循环控制部分的设计。特别是对条件控 制的循环,注意设置循环结束标志。
(二)循环程序举例
例7计算Y=∑ai , 0≤i≤100
有100个数a1,a2,…,a100,求这100个数之和(设和值不大于2个字节)。采用循环结构可写出如下程序:
DATA SEGMENT
TABL DW a1,a2,…,a10
DW all,a12,…,a20
…
DW a91,a92,…,a100
YY DW ?
DATA ENDS
STACK SEGMENT PARA STACK‘STACK’
DB l00 DUP(?)
STACK ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,SS:STACK
GO PROC FAR
BEGIN:PUSH DS
MOV AX,O
PUSH AX
MOV AX,DATA
MOV DS, AX
MOV AX,0
MOV BX, OFFSET TABL
MOV CX, 100
LOP: ADD AX,[Bx]
INC BX
INC BX
DEC CX
JNZ LOP
MOV YY, AX
RET
GO: ENDP
CODE ENDS
例8 统计数组中负元素的个数
数据段的定义如下:
DATA SEGMENT
D1 DB -1,-3,5,6,-9,…
COUNT EQU $-D1
RS DW ?
DATA ENDS
代码段程序为:
CODE SEGMENT
ASSUME CS:CODE,SS:STACK, DS:DATA
START PROC FAR
BEGING: PUSH DS
MOV AX,0
PUSH AX
MOV AX,DATA
MOV DS,AX
MOV BX,OFFSET D1 ;建立数据指针
MOV CX,COUNT ;置计数器初值
MOV DX,0 ;置结果初值
LOP1: MOV AL,[BX]
CMP AL,0
JGE JUS
INC DX
JUS: INC BX
DEC CX
JNZ LOP1
MOV RS,DX
RET
START ENDP
CODE ENDS
以上两例都是‘‘先执行,后判断”的结构。
例9 寄存器AX中有一个16位二进制数,编程统计其中“1”的个数,结果存放在
CX中。
本例最好采用“先判断、后执行”的结构,即先检查凡x中有无为“1”的位。则代码段的有关程序如下:
MOV CX, 0
LOP: AND AX, AX
JZ STP
SAL AX, 1
JNC LOP
INC CX
JMP LOP
STP: HLT
例10 软件延时程序如下:
SOFTDLY PROC
MOV BL,10
DELAY: MOV CX, 2801
WAIT: LOOP WAIT
DEC BL
JNZ DELAY
RET
SOFTDLY ENDP
这是一个二重循环结构,内循环即WAIT:LOOPWAIT,每个内循环可实现延时10ms左右,外循环人口为DELAY:MOV CX,2801,共进行10次,总的延时时间约lOOms。
注意:可以从内循环直接跳到外循环,但不能从外循环直接跳进内循环。特别要注意 100循环体头部,防止死循环。
五、子程序
(一)概述
1.子程序结构
如果在一个程序中的多个地方或在多个程序中都要用到同一段程序,可以把该程序段 独立出来存放在内存的某一区域以供其他程序调用,这段程序称为子程序或过程。可见子 程序是可供其他程序调用的独立的、相对固定的程序段。调用子程序的程序体称为主程序 或调用程序。在实用中总把常用的子程序标准化后存放在一个内存区中,称为“子程序 库”。
(1)结构。子程序的第一个语句前必须有“过程名”——入口地址的符号表示,出口是返回指令RET。
(2)调用与返回。主程序通过书写调用指令CALL后跟子程序的入口地址来调用子 程序,8086/8088允许子程序在现行代码段中(子程序名为NEAR类型),也可以不在现行代码段中(子程序名为FAR类型)。为了保证正确返回,RET指令的类型,必须与CALL指令的类型相匹配。
1)调用指令中的目标地址有两种表示方法:
a.直接调用,目标地址就在指令中。例如:CALL NEAR-PRG 。
b.间接调用,目标地址在由指令指定的寄存器或内存单元中。例如:CALL DWORD PTR[BX] 对段内的直接调用而言,CALL指令首先将SP减2,使断点的IP栈,从指令中得到的目标过程的相对偏移量(最大不能越界)加到IP上。对段内的间接调用而言,以CALL AX为例,SP减2,IP进栈,AX→IP。
对段间的直接调用而言,SP减2,现行代码段寄存器CS的内容进栈,CS由指令中目标过程的段基值代入。SP再减2,IP进栈,然后将指令中的偏移地址代入IP。
对段间的间接调用而言,SP减2,把现行的CS值进栈,CS由指令指定的双字存储器指针的第二个字的内容代入,SP再减2,IP进栈,然后IP由指令中指定的双字指针的第一个字的内容代入。双字存储器由数据段定义。
2)返回指令RET也有两种情况:
a. 段内返回指令,把SP所指的堆栈顶部的一个字的内容弹回IP,SP加2。
b.段间返回指令,把SP所指的堆栈顶部的两个字的内容,先弹回IP,后弹回CS,SP加4。
2.子程序文件
子程序应以文件形式编写,子程序文件由子程序说明和子程序构成。
(1)子程序说明。子程序说明包括如下部分:
1)功能描述:包括子程序名称、功能,以及性能指标(如执行时间)等o
2)所用寄存器和存储单元。
3)子程序的入口、出口参数。
4)子程序中又调用的其他子程序。
5)调用实例(可无)。
子程序说明举例如下:
;子程序PTOB
;两位十进制数(BCD码)转换成二进制数
;入口参数:AL中存放被转换数
;出口参数:CL中存放转换后的二进制数
;所用寄存器:BX
;执行时间:0.06ms
(2)子程序。在8086/8088中,子程序本身常以过程形式存放在代码段中,以一个过 程名开始,以RET指令结束。
例如: DTOB PROC
…
RET
DTOB ENDP
3.子程序应用中应注意的问题
(1)主程序与子程序的连接。主程序与子程序的连接由下例可见:
例如;
CODE SEGMENT
START:…
MOV AL,XX ;被转换的数在XX单元
PUSH BX
CALL DTOB
MOV YY,CL ;转换结果存于YY单元
POP BX
…
DTOB PROC
RET
DTOB ENDP
CODE ENDS
END START
(2)子程序中所用寄存器及工作单元内容的保护。为了不破坏原有信息,对于在子程 序中要用到的某些寄存器和存储单元的内容,必须压入堆栈加以保护,也可存入一些空闲单元或某些目前不用的寄存器中。称为现场信息的保护。保护可以在子程序中实现,可在主程序中实现,一般在子程序中实现保护比较好。而对于用作中断服务的子程序,则一定在子程序中安排保护指令,因为中断的出现是随机的,无法在主程序中安排保护程序。
(3)参数的传递。主程序与子程序通常需要交换信息,在多数情况下主程序需要向子 程序传递参数,子程序执行的结果要传送给主程序。子程序清单中的人口参数使子程序 对不同的数进行处理,出口参数可送出不同的结果。
参数传递一般有三种方法:
1)用寄存器传递,适用于参数较少的情况;
2)用参数表传递,适用于参数较多的情况,要求事先建立参数表,参数表一般建立在内存中。
3)用堆栈传递,适用于参数多并子程序有嵌套、递归调用的情况,主程序将参数压入堆栈,子程序中将参数从堆栈弹出。
4.子程序嵌套和子程序递归
(1) 子程序嵌套 。子程序调用别的子程序,称为嵌套,。嵌套的层次只受空间的大小
的限制。
(2)子程序递归。子程序直接或间接地调用子程序自身,称为递归。
(二)子程序结构举例
例11 数据段定义了两个数组ARYl和,ARY2,编一程序分别求此两数之和。主程序和过程分别安排在两个不同的段中,因此过程应是FAR类型。
该程序清单如下
STACK SEGMENT PARA STACK
DW 20 DUP(?)
STACK ENDS
DATA SEGMENT
ARY1 DB 100 DUP(?)
SUM1 DW ?
ARY2 DB 50 DUP(?)
SUM2 DW ?
DATA ENDS
MAIN SEGMENT ;主程序段
ASSUME CS:MAIN, DS:DATA,SS:STACK
STR:PUSH DS
MOV AX,0
PUSH AX
MOV AX,DATA
MOV DS,AX
MOV AX,SIZE ARY1
PUSH AX ;SUM过程的入口参数1 进栈
MOV AX,OFFSET ARY1
PUSH AX
CALL SUM
…
MOV AX,SIZE ARY2
PUSH AX ;SUM过程的入口参数2 进栈
MOV AX,OFFSET ARY2
PUSH AX
CALL SUM
HIT
MAIN ENDS
PROCE SEGMENT ; 过程段
ASSUME CS:PROCE,DS:DATA,SS:STACK
SUM PROC FAR
PUSH AX
PUSH BX
PUSH CX
PUSH BP
N40V BP,SP
PUSHF
MOV CX,[BP+14]
MOV BX,[BP+12]
MOV AX,0
AND: ADD AL,[BX]
INC BX
ADC AH,0
LOOP ADN
MOV[BX],AX ;数组之和送到结果区
POPF ;恢复现场
POP BP
POP CX
POP BX
POP AX
RET 4 ;返回并废除参数1和2
SUM ENDP
PROCE ENDS
END STR
从程序清单可见主程序向过程的参数传递是通过堆栈实现的,程序执行过程中堆栈变化如图所示。
图4-8 例u中堆栈变化情况
程序中出现RET 4,该指令的功能是返回后,SP再加 4
例12 求n!
算法是
n! = n(n-1)! 当n>0时;
1 当n=0时
设数n存放在AL中,n! 存放在BX中。
;主程序
MAIN: MOV AX,3 ;设n=3
CALL FACT
X1: MOV BX,DX
HLT
;阶乘子程序
;人口参数:AL中存放n
;出口参数:DX中存放n!
;所用寄存器:CX
FACT PROC
CMP AL,0
JNZ IIA
MOV DL,1
RET ;①
IIA: PUSH AX
DEC AL
CALL FACT
X2: POP CX
CALL MULT
X3: MOV DX,AX
RET ;②
FACT ENDP
;无符号字节数乘法子程序
;入口参数:CL、DL中各为一乘数
;出口参数:AX中为乘积
MULT PROC
…
RET ;③
MULT ENDP
该程序既包括子程序嵌套,也包括子程序递归。该子程序名为FACT,嵌套子程序即
FACT调用另一子程序MULT(无符号字节数乘法子程序),递归子程序即FACT调用FACT。注意三个RET指令的作用。
单一的指令其功能比较简单,要解决一个实际问题总是需要一组指令按一定的思路有 机地组合起来才能完成,这样一组完成特定功能的指令有序集合称为程序。
(一)程序设计的步骤
从具体问题到编好程序要经过如下基本步骤:
(1) 分析课题——弄清问题的性质、目的,已知数据,运算精度以及速度等方面的要 求。
(2)确定算法——把实际问题转化为计算机求解的步骤和方法,即算法,而程序是用来描述算法的。
(3)画流程图——流程图是算法的一种直观而形象的表示方法,是对程序执行过程的 一种形象化的描述,又称为框图。
(4)编写程序——熟悉8086/8088的指令系统及程序设计常用技巧按流程图编写程 序。要求做到简单明了、层次清晰、运算迅速、少占内存。要编写高质量的汇编语言程序,必须加深对指令系统功能的理解,注意内存工作单元和工作寄存器的分配。
(5)上机调试、修改——可以通过单扳机或系统机进行调试、修改直至通过。
(二)程序的基本结构
程序的基本结构有四种:顺序结构,分支程序结构,循环程序结构,子程序结构。现 分节筒述于下。
二、顺序结构程序
顺序结构的程序又称简单程序,这种结构的程序是顺序执行的, 无分支,无转移,无循环,程序本身的逻辑很简单,它只依赖于计算机能够顺序执行指令(语句)的特点,只要语句安排的顺序正确即可。
例1 内存中自TABLESQ开始的16个单元连续存放着自然数0到15的平方值,任
给一数X(0≤X≤15)在XX单元中,查表求出X的平方值,将结果存入YY单元中。
首先在数据段中建立平方表,表的首地址为TABLESO,然后用程序找到X2值在平方表中的位置,即计算表地址,表地址 = 表起始地址(TABLESQ)+X,据此可写出如下程序。
DATA SEGMENT
TABLESQ DB O,1,4,9,16,25,36,49,
DB 64,81,100,121,144,169,225
XX DB ?
YY DB ?
DATA ENDS
STACK SEGMENT PARA STACK‘STACK’
DB 50 DUP(?)
STACK ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,SS:STACK
START PROC FAR * *
BEGIN: PUSH DS * *
MOV,AX,0 * *
PUSH AX * *
MOV AX,DATA
MOV DS,AX
MOV BX,OFFSET TABLESQ
MOV AH,0
MOV AL,XX
ADD BX,AX
MOV AL,[BX]
MOV YY,AL
RET * *
START ENDP * *
CODE ENDS
END BEGIN
其中带 * * 的六行语句的作用是为了在用户程序运行结束后,正确返回操作系统。 汇编语言源程序经过汇编、连接后生成 .EXE文件,可在DOS下直接键入文件名运行,例如命令 A>EXAMPLE 将把带有.EXE的文件EXAMPLE装入内存,并从程序中指定地址开始运行。装入文件并设置启动地址是由操作系统的COMMAND.COM文件完成的。COMMAND.COM文件在装入.EXE文件前,首先确定最低可用地址作为被装入程序的可用内存起点(该区段称为程序段)并在程序内从段内偏移地址处开辟100H字节的程序段前缀(PSP),在该PSP的前两字节存放一条 INT 20H 指令(即退出当前程序返回操作系统)。而COMMAND.COM把.EXE文件装入后,自动设置DS和ES寄存器指向程序段前缀,即此时的DS和ES值为程序段前缀之段基值。用户程序结束为RET,为使RET后程序转向程序段前缀中INT 20H指令,必须使CS为程序段前缀的段基值,IP为INT 20H的段内偏移值00H,因此必须把用户程序设计为过程,即加下START PROC FAR,在用户程序的前三条指令写入PUSH DS,MOV AX,0和PUSH AX,把DS = 程序段前缀之段基值,AX = 00H进栈,在用户程序最后用RET指令出栈到CS和IP,以保证程序转向INT 20H,进而返回操作系统。
代码段结束的处理还可用DOS系统功能调用,在结尾处安排如下两条指令
MOV AH、4CH
INT 21H
执行至此,返回调用程序,常用于应用DEBUG进行程序调试,可返回DEBUG。这 时,可不用把用户程序设计为过程,即去除带 * * 的六行语句。
例2 把非压缩十进制数转换为压缩十进制数,被转换数存放在DATl开始的两个单 元,转换后的数存回DATl单元。
程序如下:
MOV AX,DATI ;(AX)=0109H
MOV CL,4 ;(CL)=4
SAL AH,CL ;(AH)=10H
ROL AX,CL ;(AX)=0091H
ROL AL,CL ;(AL)=19H
MOV BYTE PTR DATl,AL ;(DATl)=19H
┅
DATl DW 0109H
三、分支结构程序
在实际的程序设计中,始终是顺序执行的情况是很少的,大部分程序在执行过程中,总是要求计算机作出一些判断,井根据判断作出不同的处理,这就引出了分支程序的概念。
(一)分支程序的二要素
分支结构程序是具有判断和转移功能的程序。
1.判断——根据运算结果的状态标志
判断前一定要经过运算(能影响状态标志的运算),状态标志反映了运算结果的特性。 这些状态标志是:进位标志CF、奇偶标志PF、零标志ZF、符号标志SF以及溢出标志OF。
2.转移 — 主要由条件转移指令来实现(也可用无条件转移指令JMP)
在8086/8088的指令系统中,条件转移指令可分为两大类,一类是按单标志位来判断 的,如JAE,JC,JZ,JO,JS等;另一类是按多标志位来判断,如JGE,JG,JA等,详见表3-6所示。
分支结构程序框图如图4—1所示。
(二)利用比较转移指令实现分支
这是实现分支的一种常用方法,用于比较、判断的指令是CMP(比较指令)、CMPS(串比较指令)以及SCAS(串搜索指令)等。转移指令已如前述。而分支的次数可由具体问题决定是单重分支还是多重分支。n次判断可形成n+l路分支。
例3 符号函数的处理
有一符号函数
1, 当 X>O (-128≤X≤127)
Y = O, 当 X=0
-1, 当 X<O
设给定值x存放于LY单元,函数Y值存放于YY单元,则按X的不同取值给Y赋值的程序如下:
MOV AL.XX
CMP AL,0
JGE BIGR
MOV AL,0FFH
MOV YY,AL ;X<0时,—1送入YY单元
HLT
BIGR:JE EQUL
MOV AL,1
一 MOV YY,AL ;X>O时,l送人YY单元
HLT
EQUL:MOV YY,AL ;X=O时,O送人YY单元
HLT
这是一个多重分支的符号函数赋值程序,其流程图见图4-2 所示。
例4数据块传送程序
要求把内存中某一区域的源数据块传送到另一区域。在编写该程序时,必须判别一下 源数据区同目的数据区之间有无重叠,以决定是增量传送还是减量传送。程序清单如下:
DATA SEGMENT
STRG DB l000 DUP(?)
STGl EQU STRG+7
STG2 EQU STRG+25
SIRSE EQU 50
DATA ENDS
STACK SEGMENT PARA STACK‘STACK’
STARN DB l000 DUP(?)
STACK ENDS
COSEG SEGMENT
ASSUME CS:COSEG,DS:DATA,ES:DATA,SS:STACK
GOO PROC FAR
BEGIN: PUSH DS
MOV AX,0
PUSH AX
MOV AX,DATA
MOV DS,AX
MOV ES,AX
MOV CX,STRSE
MOV SI,OFFSET STGl
MOV DI,OFFSET STG2
CLD ;增量方式
PUSHI SI
ADD SI,STRSE-1
CMP SI,DI
POP SI
JB OK
STD ;减量方式传送
ADD DI,STRSE-1 ;指向数据块底部
ADD SI,STRSE-1
OK:REP MOVSB ;重复传送50个数据
RET
GOO ENDP
COSEG ENDS
END BEGIN
(三)利用跳转表实现分支
1.跳转表的建立
在内存的一个连续区中,连续存放一系列跳转地址、跳转指令或关键字,组成一个决
定程序分支的跳转表。
例5 某工厂有8种产品的加工程序RO到R7分别存放在以SBRO,SBRl,…,SBR7为首地址的内存区域中,这8个首地址的偏移量连续存放在以BASE为首地址跳转表内,如图4-3 所示。
2.跳转表的使用
上例中,8种产品的编号为0,1,2,…,7 如果已知目前要加工的产品编号,要求编写程序,利用跳转表自动转入该产品的加工程序。
在跳转表已知的情况下,关键问题要计算所要求的加工程序的入口地址在跳转表中的地址,即计算表地址。从图 4-3可见:
表地址=表基地址+偏移量
表基地址即跳转表的首地址,偏移量即对应的程序入口地址在表中的地址与表基地址
的距离。在本例中,分析表的结构可见,偏移量=产品编号 * 2,已知偏移量后,即可求出表地址。据此可写出[例5]的程序清单
DATA SEGMENT
BASE DW SBR0,SBRl,SBR2,SBR3
DW SBR4,SBR5,SBR6,SBR7
BN DB ?
DATA ENDS
STACK SEGMENT PARA STACK ‘STACK’
DB l00 DUP(?)
STACK ENDS
COSEG SEGMENT
ASSUME CS:COSEG,DS:DATA,SS:STACK
START PROC FAR
BEGIN:PUSH DS
MOV AX,O
PUSH AX
MOV AX,DATA
MOV DS,AX
MOV AL,BiN
MOV AH.O
ADD AL,AL
MOV BX,OFFSETBASE
ADD BX,AX
MOV AX,[Bx]
JMP AX
RET
START ENDP
COSEG ENDS
END BEGIN
3.根据跳转表内指令分支
在计算机系统的监控程序,键盘管理程序中经常要用到跳转表。在跳转表中既可存放 跳转地址(如例5),也可存放一系列的跳转指令JMP。
例6 有一监控程序的键盘程序控制12个命令键——执行键(EXEC)、存储器检查键(MEM)等等,按下任一命令键相当于发出一条键盘命令,而这些命令的实现分别由监控程序中的12个于程序完成,这些子程序的入口地址分别为ADR0,ADRl,…,ADRll。据此可以 组成命令键跳转表如图4-4所示。该命令键跳转表存放在以BASE0为首地址的内存区域中,表内存放着12条转移指令:
JMP ADR0,JMP ADRl, …,JMP ADRll。
设12个命令键的编号分别为0 ~ 11。命令跳转表中每三个单元存放一条转移指令,如果命令键的编号X已送入寄存器AL,则实现转向相应的命令子程序的程序如下
MOV AH,0
MOV BL,AL
ADD AL,AL
ADD AL,BL
ADD AL, BL
MOV BX,OFFSET BASE0
ADD BX,AX ;表地址计算
JMP BX
四、循环结构程序
(一)概述
1.循环程序
实现重复执行某一段程序的程序结构称为循环程序。用来处理带重复性的问题,可以缩短源程序及目标程序。
循环结构程序的框图之一如图4-5所示。
循环程序由五部分组成:
(1)初始化部分——这是循环的准备部分,为程序操作、地址指针、循环计数、结束条件等设置初始值。
(2)循环工作部分——这是循环程序的主体,完成程序的基本操作,循环多少次,这部分的语句就执行多少次。
(3)循环修改部分——修改循环工作部分的变量地址等,为下一轮重复操作作准备。
(4)循环控制部分——修改计数器或判断循环结束条件以决定是继续循环还是终止循环,是典型的分支结构,不过继续
循环的分支去向是循环体的头部。通常把循环工作部分称为循环体,循环体的第一条指令为循环体的头部。
(5)循环结束部分——循环终止后,对循环结果的处理部分。
2.循环程序的几种结构形式
循环程序除图4-5所示的基本结构形式外,还有两种常用的结构形式,如图4-6和图
4-7所示。
这两种结构的特点是工作部分放在循环控制部分之后,这样可以允许循环次数为零的 循环程序或不需要循环时不进入循环工作部分。
3.循环程序分类
(1)按循环控制条件分类
1)计数循环——重复次数已知,用计数值控制循环的开始与终止。
2)条件控制循环——重复次数未知或不确定,需找出循环控制的条件。
(2)按循环体内结构分类
1)单重循环——循环体内只是顺序程序或分支程序,不再有循环程序。
2)多重循环——循环体内再套有循环程序。可把重复处理部分独立出来,前面加上 循环准备,后面加上结束判断。
4.循环程序设计要点
循环程序设计要点可归结为两点:
(1)怎样把求解的问题变为循环结构的程序类型——怎样实现重复,即计算方案的循 环化。这里循环工作部分的设计尤为重要,因为这是多次重复的部分,注意程序的精练,及循环体头部的确定。
(2)怎样使循环准确地执行完毕,这就要注意循环控制部分的设计。特别是对条件控 制的循环,注意设置循环结束标志。
(二)循环程序举例
例7计算Y=∑ai , 0≤i≤100
有100个数a1,a2,…,a100,求这100个数之和(设和值不大于2个字节)。采用循环结构可写出如下程序:
DATA SEGMENT
TABL DW a1,a2,…,a10
DW all,a12,…,a20
…
DW a91,a92,…,a100
YY DW ?
DATA ENDS
STACK SEGMENT PARA STACK‘STACK’
DB l00 DUP(?)
STACK ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,SS:STACK
GO PROC FAR
BEGIN:PUSH DS
MOV AX,O
PUSH AX
MOV AX,DATA
MOV DS, AX
MOV AX,0
MOV BX, OFFSET TABL
MOV CX, 100
LOP: ADD AX,[Bx]
INC BX
INC BX
DEC CX
JNZ LOP
MOV YY, AX
RET
GO: ENDP
CODE ENDS
例8 统计数组中负元素的个数
数据段的定义如下:
DATA SEGMENT
D1 DB -1,-3,5,6,-9,…
COUNT EQU $-D1
RS DW ?
DATA ENDS
代码段程序为:
CODE SEGMENT
ASSUME CS:CODE,SS:STACK, DS:DATA
START PROC FAR
BEGING: PUSH DS
MOV AX,0
PUSH AX
MOV AX,DATA
MOV DS,AX
MOV BX,OFFSET D1 ;建立数据指针
MOV CX,COUNT ;置计数器初值
MOV DX,0 ;置结果初值
LOP1: MOV AL,[BX]
CMP AL,0
JGE JUS
INC DX
JUS: INC BX
DEC CX
JNZ LOP1
MOV RS,DX
RET
START ENDP
CODE ENDS
以上两例都是‘‘先执行,后判断”的结构。
例9 寄存器AX中有一个16位二进制数,编程统计其中“1”的个数,结果存放在
CX中。
本例最好采用“先判断、后执行”的结构,即先检查凡x中有无为“1”的位。则代码段的有关程序如下:
MOV CX, 0
LOP: AND AX, AX
JZ STP
SAL AX, 1
JNC LOP
INC CX
JMP LOP
STP: HLT
例10 软件延时程序如下:
SOFTDLY PROC
MOV BL,10
DELAY: MOV CX, 2801
WAIT: LOOP WAIT
DEC BL
JNZ DELAY
RET
SOFTDLY ENDP
这是一个二重循环结构,内循环即WAIT:LOOPWAIT,每个内循环可实现延时10ms左右,外循环人口为DELAY:MOV CX,2801,共进行10次,总的延时时间约lOOms。
注意:可以从内循环直接跳到外循环,但不能从外循环直接跳进内循环。特别要注意 100循环体头部,防止死循环。
五、子程序
(一)概述
1.子程序结构
如果在一个程序中的多个地方或在多个程序中都要用到同一段程序,可以把该程序段 独立出来存放在内存的某一区域以供其他程序调用,这段程序称为子程序或过程。可见子 程序是可供其他程序调用的独立的、相对固定的程序段。调用子程序的程序体称为主程序 或调用程序。在实用中总把常用的子程序标准化后存放在一个内存区中,称为“子程序 库”。
(1)结构。子程序的第一个语句前必须有“过程名”——入口地址的符号表示,出口是返回指令RET。
(2)调用与返回。主程序通过书写调用指令CALL后跟子程序的入口地址来调用子 程序,8086/8088允许子程序在现行代码段中(子程序名为NEAR类型),也可以不在现行代码段中(子程序名为FAR类型)。为了保证正确返回,RET指令的类型,必须与CALL指令的类型相匹配。
1)调用指令中的目标地址有两种表示方法:
a.直接调用,目标地址就在指令中。例如:CALL NEAR-PRG 。
b.间接调用,目标地址在由指令指定的寄存器或内存单元中。例如:CALL DWORD PTR[BX] 对段内的直接调用而言,CALL指令首先将SP减2,使断点的IP栈,从指令中得到的目标过程的相对偏移量(最大不能越界)加到IP上。对段内的间接调用而言,以CALL AX为例,SP减2,IP进栈,AX→IP。
对段间的直接调用而言,SP减2,现行代码段寄存器CS的内容进栈,CS由指令中目标过程的段基值代入。SP再减2,IP进栈,然后将指令中的偏移地址代入IP。
对段间的间接调用而言,SP减2,把现行的CS值进栈,CS由指令指定的双字存储器指针的第二个字的内容代入,SP再减2,IP进栈,然后IP由指令中指定的双字指针的第一个字的内容代入。双字存储器由数据段定义。
2)返回指令RET也有两种情况:
a. 段内返回指令,把SP所指的堆栈顶部的一个字的内容弹回IP,SP加2。
b.段间返回指令,把SP所指的堆栈顶部的两个字的内容,先弹回IP,后弹回CS,SP加4。
2.子程序文件
子程序应以文件形式编写,子程序文件由子程序说明和子程序构成。
(1)子程序说明。子程序说明包括如下部分:
1)功能描述:包括子程序名称、功能,以及性能指标(如执行时间)等o
2)所用寄存器和存储单元。
3)子程序的入口、出口参数。
4)子程序中又调用的其他子程序。
5)调用实例(可无)。
子程序说明举例如下:
;子程序PTOB
;两位十进制数(BCD码)转换成二进制数
;入口参数:AL中存放被转换数
;出口参数:CL中存放转换后的二进制数
;所用寄存器:BX
;执行时间:0.06ms
(2)子程序。在8086/8088中,子程序本身常以过程形式存放在代码段中,以一个过 程名开始,以RET指令结束。
例如: DTOB PROC
…
RET
DTOB ENDP
3.子程序应用中应注意的问题
(1)主程序与子程序的连接。主程序与子程序的连接由下例可见:
例如;
CODE SEGMENT
START:…
MOV AL,XX ;被转换的数在XX单元
PUSH BX
CALL DTOB
MOV YY,CL ;转换结果存于YY单元
POP BX
…
DTOB PROC
RET
DTOB ENDP
CODE ENDS
END START
(2)子程序中所用寄存器及工作单元内容的保护。为了不破坏原有信息,对于在子程 序中要用到的某些寄存器和存储单元的内容,必须压入堆栈加以保护,也可存入一些空闲单元或某些目前不用的寄存器中。称为现场信息的保护。保护可以在子程序中实现,可在主程序中实现,一般在子程序中实现保护比较好。而对于用作中断服务的子程序,则一定在子程序中安排保护指令,因为中断的出现是随机的,无法在主程序中安排保护程序。
(3)参数的传递。主程序与子程序通常需要交换信息,在多数情况下主程序需要向子 程序传递参数,子程序执行的结果要传送给主程序。子程序清单中的人口参数使子程序 对不同的数进行处理,出口参数可送出不同的结果。
参数传递一般有三种方法:
1)用寄存器传递,适用于参数较少的情况;
2)用参数表传递,适用于参数较多的情况,要求事先建立参数表,参数表一般建立在内存中。
3)用堆栈传递,适用于参数多并子程序有嵌套、递归调用的情况,主程序将参数压入堆栈,子程序中将参数从堆栈弹出。
4.子程序嵌套和子程序递归
(1) 子程序嵌套 。子程序调用别的子程序,称为嵌套,。嵌套的层次只受空间的大小
的限制。
(2)子程序递归。子程序直接或间接地调用子程序自身,称为递归。
(二)子程序结构举例
例11 数据段定义了两个数组ARYl和,ARY2,编一程序分别求此两数之和。主程序和过程分别安排在两个不同的段中,因此过程应是FAR类型。
该程序清单如下
STACK SEGMENT PARA STACK
DW 20 DUP(?)
STACK ENDS
DATA SEGMENT
ARY1 DB 100 DUP(?)
SUM1 DW ?
ARY2 DB 50 DUP(?)
SUM2 DW ?
DATA ENDS
MAIN SEGMENT ;主程序段
ASSUME CS:MAIN, DS:DATA,SS:STACK
STR:PUSH DS
MOV AX,0
PUSH AX
MOV AX,DATA
MOV DS,AX
MOV AX,SIZE ARY1
PUSH AX ;SUM过程的入口参数1 进栈
MOV AX,OFFSET ARY1
PUSH AX
CALL SUM
…
MOV AX,SIZE ARY2
PUSH AX ;SUM过程的入口参数2 进栈
MOV AX,OFFSET ARY2
PUSH AX
CALL SUM
HIT
MAIN ENDS
PROCE SEGMENT ; 过程段
ASSUME CS:PROCE,DS:DATA,SS:STACK
SUM PROC FAR
PUSH AX
PUSH BX
PUSH CX
PUSH BP
N40V BP,SP
PUSHF
MOV CX,[BP+14]
MOV BX,[BP+12]
MOV AX,0
AND: ADD AL,[BX]
INC BX
ADC AH,0
LOOP ADN
MOV[BX],AX ;数组之和送到结果区
POPF ;恢复现场
POP BP
POP CX
POP BX
POP AX
RET 4 ;返回并废除参数1和2
SUM ENDP
PROCE ENDS
END STR
从程序清单可见主程序向过程的参数传递是通过堆栈实现的,程序执行过程中堆栈变化如图所示。
图4-8 例u中堆栈变化情况
程序中出现RET 4,该指令的功能是返回后,SP再加 4
例12 求n!
算法是
n! = n(n-1)! 当n>0时;
1 当n=0时
设数n存放在AL中,n! 存放在BX中。
;主程序
MAIN: MOV AX,3 ;设n=3
CALL FACT
X1: MOV BX,DX
HLT
;阶乘子程序
;人口参数:AL中存放n
;出口参数:DX中存放n!
;所用寄存器:CX
FACT PROC
CMP AL,0
JNZ IIA
MOV DL,1
RET ;①
IIA: PUSH AX
DEC AL
CALL FACT
X2: POP CX
CALL MULT
X3: MOV DX,AX
RET ;②
FACT ENDP
;无符号字节数乘法子程序
;入口参数:CL、DL中各为一乘数
;出口参数:AX中为乘积
MULT PROC
…
RET ;③
MULT ENDP
该程序既包括子程序嵌套,也包括子程序递归。该子程序名为FACT,嵌套子程序即
FACT调用另一子程序MULT(无符号字节数乘法子程序),递归子程序即FACT调用FACT。注意三个RET指令的作用。
以上就是关于《微型计算机原理及应用学习笔记 汇编语言程序设》的全部内容,如需了解更多学历提升、自考报名报考时间、自考报名流程、自考院校和专业查询、开考时间安排、自考课程、自考教材购买等的相关问题,可随时添加广东自考网《专业老师微信》进行在线沟通了解哦~
《广东自考网》免责声明:
1、由于考试政策等各方面情况的调整与变化,本网提供的考试信息仅供参考,最终考试信息请以省考试院及院校官方发布的信息为准。
2、本站内容部分信息均来源网络收集整理或来源出处标注为其它媒体的稿件转载,免费转载出于非商业性学习目的,版权归原作者所有,如有内容与版权问题等请与本站联系。联系邮箱:812379481@qq.com