4 汇编语言程序设计_第1页
4 汇编语言程序设计_第2页
4 汇编语言程序设计_第3页
4 汇编语言程序设计_第4页
4 汇编语言程序设计_第5页
已阅读5页,还剩92页未读 继续免费阅读

付费下载

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第四章汇编语言程序设计

§4.2伪指令§4.3汇编语言程序的结构

§4.4汇编语言程序的开发过程§4.5汇编语言程序结构设计

§4.6系统功能调用§4.7汇编语言设计实例§4.1汇编语言语句

在了解和熟悉了86系列指令系统的基础上,本章将介绍宏汇编语言程序的结构,伪指令,以及程序设计的一般过程和各种基本程序结构,然后介绍了系统功能调用,最后给出大量的编程实例。§4.1汇编语言语句

语句是汇编语言源程序的基本组成单位。一个汇编语言源程序中有3种基本语句:指令语句、伪指令语句和宏指令语句。前两种是最常见、最基本的语句。指令语句和伪指令语句不仅在程序中的功能不同,而且实现其功能的方法和时间也是不同的。1、指令语句指令语句就是计算机中指令系统的各条指令,每条指令语句在汇编时都产生一个供CPU执行的机器目标代码。计算机中每条指令语句表示一种基本功能,这些基本功能是在程序运行期间由计算机硬件来实现的。一条指令语句由四个字段组成,其一般格式如下:[标号:]指令助记符[操作数][;注释]一、汇编语言语句种类及其格式2、伪指令语句

伪指令语句指示汇编程序在汇编源程序时完成某些工作,比如完成数据定义、分配存储区、指示程序结束等。伪指令属于汇编控制命令,它所指示的操作是由汇编程序在汇编源程序时完成的,在汇编时,它不产生目标代码,在将源程序汇编成目标程序后,它就不复存在了。由上可知,伪指令语句与指令语句的主要区别是:伪指令语句经汇编后不产生机器目标代码,而指令语句经汇编后将产生相应的机器目标代码;伪指令语句所指示的操作是在程序汇编时完成的,而指令语句的操作必须在程序运行时才能完成。一条伪指令语句也由四个字段组成,其一般格式如下:[符号名]伪指令符操作数[;注释]§4.1汇编语言语句一、汇编语言语句种类及其格式(1)符号名符号名在伪指令语句中是一个可选项,符号名后面没有冒号,符号名可以是常量名、变量名、过程名等。(2)伪指令符伪指令符指定汇编程序要完成的具体操作,它是伪指令语句的核心部分,如数据定义伪指令DB、DW、DD,段定义伪指令SEGMENT,定义过程伪指令PROC等等。(3)操作数伪指令中的操作数可以是常数、字符串、变量、表达式,其个数由具体的伪指令决定,各个操作数之间用“,”分隔。(4)注释伪指令语句的注释也是可选项,需要时必须以“;”开始。

§4.1汇编语言语句一、汇编语言语句种类及其格式3、标识符指令语句中的标号和伪指令语句中的符号名统称为标识符。它们由若干字符组成,标识符的组成规则如下:(1)一个标识符由1--3个字符组成。(2)组成标识符的字符可以是字母(A--Z或a--z)、数字(0--9)、专用字符(“?”、“·”、“@”、“$”、“下划线_”)。(3)除数字外,上述其余字符均可作为标识符的首字符,“·”只能作为标识符的首字符。(4)不能使用属于系统的专用保留字。§4.1汇编语言语句一、汇编语言语句种类及其格式二、汇编语言数据与运算符标号指令寄存器说明程序或语句变量伪指令标号的功能宏指令变量

常数

表达式源程序的每条语句可表示为:

[名字]

操作码操作数

[;注释]§4.1汇编语言语句1、常量

常量是没有任何属性的纯数值,它的值在汇编期间已能完全确定,且在程序运行中也不会发生变化。常量分为数值常量、字符串常量和符号常量,它主要用于指令语句中的立即数或伪指令语句中给变量赋初值等。1)数值常量数值常量分为整数和实数。例如:11100011B,45693,0FF2AH,356703Q都是正确的整数形式,又如:543.567,﹣45.23,1.2E﹣2,﹣45.4E﹢9都是正确的实数形式。2)字符串常量字符串常量是用单引号括起来的一个字符或多个字符。字符串常量以单引号中各字符的ASCII码形式存储在内存中,如‘H’,在内存中就是41H,‘12’就是31H,32H。使用时可在单引号内直接写字符序列,如‘12AB’,也可写字符的ASCII码,ASCII码之间用逗号分隔(此时不需要用单引号),如31H,32H,41H,42H表示字符串‘12AB’。3)符号常量符号常量是指用EQU伪指令或赋值语句“=”定义过的符号名,可作操作数项或在表达式中使用。§4.1汇编语言语句二、汇编语言数据与运算符2、变量在汇编语言中,变量是一个数据存储单元的名称,即数据存放地址的符号表示。它代表存放在某些存储单元的数据,这些数据在程序运行期间随时可以改变。为方便访问变量,在程序中通过变量名来使用变量。(1)变量的定义

变量通常在数据段或附加段中使用数据定义伪指令来定义,定义变量就是给数据分配存储单元,有时为存储单元赋予一个变量名,并可同时为这些存储单元预置初值。数据定义伪指令的格式为:

[变量名]DB(DW、DD、DQ、DT)表达式1,表达式2,…§4.1汇编语言语句二、汇编语言数据与运算符

其中:变量名是可选项,它仅代表所定义数据存储区第一个单元的地址;DB、DW、DD、DQ和DT是伪指令符,具体一条数据定义伪指令取5种之一。表达式1,表达式2……是给变量或指定的存储单元赋予初值,它们有以下几种形式:(a)数值表达式(b)字符串表达式(c)?表达式(d)地址表达式(只适用于DW和DD这两种数据定义伪指令)(e)带DUP的表达式(2)变量的属性

由于存储器是分段使用的,因而源程序定义的变量具有3个属性: (a)段属性(SEG) (b)偏移属性(OFFSET) (c)类型属性(TYPE)二、汇编语言数据与运算符§4.1汇编语言语句(3)变量的使用定义后的变量,在程序中的引用有两种情况:(a)在指令语句中,采用存储器操作数的几种寻址方式,除寄存器间接寻址方式不使用变量名外,其余各种寻址方式均可使用变量名。例如,某数据段已定义一变量ARRAY:ARRAYDW5000H,4000H(b)在数据定义伪指令DW和DD中,操作数字段可直接引用已定义过的变量名。如:

ADB50H,40HBDWACDDB§4.1汇编语言语句二、汇编语言数据与运算符3、标号标号是一条指令语句的符号地址,在汇编源程序中,只有在需要转向一条指令语句时,才为该指令语句设置标号,以便在控制转移指令中直接引用这个标号。标号一般在代码段中定义和引用。由于标号代表了指令的符号地址,所以标号也有3个属性:(a)段属性(SEG)。标号的段属性是指标号定义所在段的段首地址。(b)偏移属性(OFFSET)。标号的偏移属性是指标号所在段的段首地址到该标号的字节距离。(c)类型属性(TYPE)。标号的类型属性表示了它的转移特性,即该标号是作为段内还是段间转移(或调用)指令的目标地址。标号的类型属性有2种:NEAR类型和FAR类型。§4.1汇编语言语句二、汇编语言数据与运算符4、表达式和运算符汇编语言的指令语句和伪指令语句中,表达式是操作数项的常见形式,表达式是由常量、变量、标号用运算符连接而成有意义的式子。表达式分为数值表达式和地址表达式,任一表达式的值只计算一次,表达式的计算是在源程序汇编过程中进行的,而不是在程序运行中进行的,汇编程序将表达式计算后得到一个数值或一个地址。在8086汇编语言中,运算符分为:算术运算符、逻辑运算符、关系运算符、数值回送运算符、属性运算符和字节分离运算符。§4.1汇编语言语句二、汇编语言数据与运算符(1)数值表达式数值表达式是由常量与算术运算符、逻辑运算符或关系运算符构成的有意义的式子。数值表达式在汇编期间进行运算,运算结果为一数值常量,它只有大小而没有属性。(2)地址表达式地址表达式是由常量、变量、标号、寄存器(BX、BP、SI、DI)内容(用寄存器名加方括号表示)和运算符组成的有意义的式子。单个的变量、标号、寄存器的内容是地址表达式的特例。地址表达式中可以使用算术运算符中的“+”、“-”运算符、关系运算符、属性运算符、数值返回运算符和分离运算符。在地址表达式中,常用的形式是:变量±常量。

§4.1汇编语言语句二、汇编语言数据与运算符(a)算术运算符:+,-,*,/,MOD,SHL,SHR。(b)逻辑运算符:AND,OR,XOR,NOT。(c)关系运算符:EQ,NE,LT,LE,GT,GE。当关系成立时,结果为0FFFFH,当关系不成立时,结果为0。(d)数值回送运算符:SEG,OFFSET,TYPE,SIZE,LENGTH。§4.1汇编语言语句二、汇编语言数据与运算符OFFSET/SEG

变量/标号功能:回送变量或标号的偏址/段址TYPE变量

/标号

/常数

DBDWDDDFDQDTNEARFAR常数

1246810-1-20LENGTH变量功能:回送由DUP定义的变量的单元数,其它情况回送1SIZE变量功能:LENGTH*TYPE(3)表达式运算符(5)属性运算符:PTR,段操作符,THIS,SHORT。 类型PTR表达式

MOVWORDPTR[BX],5 段操作符

MOVES:[BX],AL SHORT标号

JMPSHORTNEXT THIS类型

TAEQUTHISBYTETDDW1234HNEXTEQUTHISFARMOVAX,2(6)分离运算符:LOW,HIGH。

HIGH和LOW

CONSEQU1234H MOVAH,HIGHCONSMOVAL,LOWCONS§4.1汇编语言语句二、汇编语言数据与运算符§4.2伪指令1数据定义伪指令2符号定义伪指令3段定义伪指令SEGMENT/ENDS4假定伪指令ASSUME5

定位伪指令ORG与地址计数器($)6过程定义伪指令PROC/ENDP7源程序开始和结束伪指令常用的数据定义伪指令有DB,DW,DD,DQ,DT。格式:[变量名]数据定义伪指令表达式[,…]功能:定义数据存储区,类型由数据定义伪指令确定,初值由表达式给定。

-

0AH

04H

10H

-

64H

00H

00H

01H

FBH

FFH

-DATA_BYTEDATA_WORD例:DATA_BYTEDB10,4,10H,?DATA_WORDDW100,100H,-5,?1数据定义伪指令§4.2伪指令例:

ARRAYDB‘HELLO’ DB‘AB’ DW‘AB’

48H

45H

4CH

4CH

4FH

41H

42H

42H

41H

ARRAY例:

PAR1DW100,200PAR2DW300,400ADDR_TABLEDWPAR1,PAR2VARDB100DUP(?)DB2DUP(0,2DUP(1,2),3)1数据定义伪指令§4.2伪指令OPER1DB?,?OPER2DW?,?……MOVOPER1,0;字节指令

MOVOPER2,0;字指令OPER1DB1,2OPER2DW1234H,5678H……MOVAX,OPER1+1×MOVAL,OPER2×类型不匹配

MOVAX,WORDPTROPER1+1MOVAL,BYTEPTROPER2(AX)=3402H(AL)=34H1数据定义伪指令§4.2伪指令§4.2伪指令1)等价伪指令EQU2)等号伪指令=3)定义符号名伪指令LABEL2符号定义伪指令1)等价伪指令EQU格式:符号名EQU表达式功能:为常量、表达式及其他各种符号定义一个等价的符号名,但它不申请存储单元。用途:①用符号表示常量、数值表达式,即定义符号常量。使用符号常量可使程序简单明了,增强程序的可读性和通用性。②EQU与属性运算符PTR或THIS联合使用,可以给变量或标号定义新的类型属性并重新命名,但保持其段偏移地址属性不变。③利用EQU可以用一个符号名替代一个复杂的地址表达式和其他一些符号,如指令助记符、变量名、标号、段名、寄存器名、宏定义名等。2符号定义伪指令-等价伪指令EQU例如:

ALPHAEQU9 BETAEQUALPHA+18 BBEQU[BP+8]§4.2伪指令格式:符号名=表达式功能:为常量、表达式及其他各种符号定义一个等价的符号名,并能对所定义的符号多次重复定义,且以最后一次定义的值为准。【例】定义等价符号名。 ┆ COST=20 M =MOV LOST=LOST+10 ;30→LOST M =ADD ;M=ADD ┆2符号定义伪指令-等号伪指令=§4.2伪指令格式:变量名或标号LABEL

类型功能:定义与原有变量类型不同的新变量或为指令语句定义有指定类型的标号。通常与数据定义伪指令连用,其功能类似语句“变量名或标号EQUTHIS类型”。【例】定义新类型的变量。 ┆ DDBUF LABELDWORD BUF DB 200DUP(0) ┆

ALABELFAR B: LEA DX,BUF ┆§4.2伪指令2符号定义伪指令-定义符号名伪指令LABEL

编制一个80X86汇编语言源程序,段是基础,这有两方面含义:一是必须按段来构造程序,二是在程序执行时,要凭借四个段寄存器对各个段的存储单元进行访问。格式:段名

SEGMENT[定位类型][组合类型][‘类别’]

段体

段名

ENDS功能:定义一个逻辑段,指定段的名字和范围,段在内存中的起始位置、段与段之间的连接关系。说明:①段名由用户指定,开始与结束的段名须一致。②SEGMENT/ENDS伪指令把程序分成若干逻辑段。③删节号为段体,段体内为指令和伪指令序列。 ④段的长度不超过64KB。SEGMENT后面的参数是可选项。§4.2伪指令3段定义伪指令SEGMENT/END

假定伪指令主要用于指示汇编程序哪些段是当前段以及这些段与段寄存器之间的对应关系。格式:ASSUME段寄存器:段名[,段寄存器:段名]功能:该伪指令告诉汇编程序在汇编时,段寄存器CS、DS、SS和ES应具有的符号段基址,以便汇编指令时确定段和建立错误信息。但是段寄存器实际值(CS除外)还要由传送指令在执行程序时赋值。该伪指令一般出现在代码段中。【例】用ASSUME伪指令建立代码段、堆栈段与CS和SS的对应关系。 DATA1 SEGMENT A DB1,2,3 DATA1 ENDS STACK SEGMENTSTACK DB 200DUP(0) STACK ENDS DATA2 SGEMENT B DB‘123ABC

DATA2 ENDS4假定伪指令ASSUME§4.2伪指令DATA3 SEGMENTC DB?,?,?DATA3 ENDSCODE SEGMENT ASSUME DS:DATA1,ES:DATA2,CS:CODE,SS:STACKSTART: MOV AX,DATA1 MOV DS,AX ;DATA1→DS MOV AX,DATA3 MOV ES,AX ;DATA3→ES ┆Q1: MOV AL,AQ2: MOV C,AL ASSUME DS:DATA2;建立DS与B段的对应关系

MOV AX,DATA2 MOV DS,AX MOV AL,B MOV C,AL ┆CODE ENDS END START§4.2伪指令4假定伪指令ASSUME5定位伪指令ORG与地址计数器($)§4.2伪指令汇编程序在汇编源程序时,每遇到一新段,就为该段设置一个初值为0的汇编地址计数器,汇编程序使用汇编地址计数器保存正在汇编的数据或指令的目标代码在当前段内的偏移地址。当前地址计数器的值可用符号$表示,用户可在程序中直接使用$,表示引用当前汇编地址计数器的值。例如:

ARRAYDW4000H,5000H,6000H COUNTEQU($-ARRAY)/2汇编地址计数器的值可以用定位伪指令ORG设置。

格式:ORG数值表达式功能:将数值表达式的值赋给汇编地址计数器。数值表达式的值须为0-65535之间的非负整数。该伪指令把以下语句定义的内存数据或程序,从表达式指定的起点(偏移地址)开始连续存放,直至遇到新的ORG指令。【例】给汇编地址计数器赋值。 DATA SEGMENT ORG 10 ;

置$值为10 VAR1 DW100H,200H ORG $+5 ;置$的值为14+5,即为19 VAR2 DB1,2,$+1,$+2 N EQU$-VAR2 ;($)=23 DATA ENDS5定位伪指令ORG与地址计数器($)§4.2伪指令

在程序设计中,常把具有某种功能的程序段设计成一个过程。80X86宏汇编语言用于过程定义的伪指令的格式为:

过程名PROC[NEAR或FAR] ┆;过程体 过程名ENDP其中过程名是过程入口地址的符号表示,它由程序员指定,且开始处和结束处的过程名一致。过程名同标号一样,具有三种属性:段属性、偏移属性和类型属性,类型属性可指定为NEAR或FAR两种。默认时系统约定是NEAR。过程体为过程内的指令和伪指令序列。

定义过程是为实现子程序调用而设的,调用格式为:

CALL〈过程名〉过程由RET指令返回。6过程定义伪指令PROC/ENDP§4.2伪指令(1)源程序开始伪指令在源程序开始处可以用NAME或TITLE为模块取名字。①NAME伪指令

格式:NAME模块名汇编程序将以给出的“模块名”作为模块的名字。②TITLE伪指令如果程序中没有NAME伪指令,则也可使用TITLE伪指令。格式:TITLEtextTITLE伪指令可指定每一页上打印的标题。同时,若程序中没有NAME伪指令,则汇编程序将用text中的前6个字符作为模块名。text最多可有60个字符。若程序中既无NAME又无TITLE伪指令,则将用源程序名作为模块名。所以NAME及TITLE伪指令并不是必要的,但一般常使用TITLE,以便在列表文件中能打印出标题来。

7源程序开始和结束伪指令§4.2伪指令(2)源程序结束伪指令格式:END[地址表达式]功能:该语句为源程序的最后一个语句,用以标志整个程序的结束,即告诉汇编程序汇编工作到此结束。其中,地址表达式为可选项,若选用地址表达式,则表示此程序是主程序,它可以单独执行,其地址表达式为该程序的启动地址,即程序开始运行的第一条指令的地址,例如,“END

START”,此START是第一条指令的标号;若不选用表达式,则说明此程序为一子程序,不能单独运行,只能被其它程序调用。

7源程序开始和结束伪指令§4.2伪指令§4.3汇编语言程序的结构

一个汇编源程序是分段的,由若干个段形成一个源程序。其中,必不可少的是代码段和堆栈段,堆栈段也可以不用显示定义,可以直接使用隐式堆栈段,如果程序中需要使用数据存储区,还要定义数据段,必要时还要定义附加段。一般情况下,对于不太复杂的程序只需要三个段(即数据段、堆栈段和代码段)就可以了,而对于复杂的程序,除了使用上述三个段以外还可以使用多个段,甚至可以使用多个程序模块。汇编语言源程序结构通常有两种,它们的区别在于代码段中某些部分的编写稍有不同。具体地说,就是用户程序运行结束后,返回DOS的方法上有所不同。

完整的段定义格式datasegment;定义数据段

…dataends;----------------------------------------extrasegment;定义附加段

…extraends;----------------------------------------codesegment;定义代码段

assumecs:code,ds:data,es:extrastart:

movax,data

mov

ds,ax;段地址段寄存器

…codeendsendstartPSPDSESSSCSEXE程序的内存映象图装入模块文件头§4.3汇编语言程序的结构

1、用INT21H返回DOS的程序结构模式INT21H是DOS系统功能调用,用这种方法返回DOS的程序结构比较简单,我们编写程序常采用此结构,具体编写时只要在用户程序的最后安排两条指令即可,即:

MOVAH,4CHINT21H2、用过程返回DOS的程序结构模式采用此种方法返回DOS的程序结构,要求将用户程序定义为一个类型为FAR的过程,在用户程序的开始处要将DS的值压栈,偏移地址0压栈,在程序的最后安排一条RET指令。这样当程序运行到RET指令后便可返回DOS。§4.3汇编语言程序的结构

……codesegmentmainprocfarassume……start:pushds

movax,0pushax……retmainendpcodeendsendstart……codesegmentmainprocfarassume……start:…………

movax,4c00h

int21hmainendpcodeendsendstart§4.3汇编语言程序的结构

§4.4汇编语言程序的开发过程myfile.asm编辑程序汇编程序链接程序myfile.crfotherfiles.objmyfile.lstmyfile.objmyfile.mapmyfile.exe1.源程序的编辑2.源程序的汇编3.目标文件的链接§4.5汇编语言程序结构设计

在汇编语言程序设计中,程序有顺序、分支、循环和子程序四种基本结构形式。

4.5.1顺序程序设计 4.5.2分支程序设计 4.5.3循环程序设计 4.5.4子程序的设计 4.5.5宏指令汇编语言程序设计步骤:

(1)分析问题:已知条件、要解决的问题、功能/性能要求等。

(2)建立数学模型:把问题数学化、公式化,便于计算机处理。

(3)确定算法:简单、速度快、精度高、代码量小、编程容易。

(4)绘制程序流程图:用箭头、框图、菱形图等表示程序结构。

(5)内存空间分配:为程序或数据分配内存空间。

(6)编制程序与静态检查:程序结构层次简单、清楚、易懂。

(7)程序调试:利用调试软件DEBUG进行调试。汇编语言程序设计的特点:

(1)算法要分解至指令级;高级语言为语句级。

(2)要详细考虑内存空间分配问题:任何一个变量、一条指令都要明确其存储位置。§4.5汇编语言程序结构设计

顺序程序设计也称为简单程序设计,这种结构的程序不使用分支、循环结构,程序本身的逻辑非常简单,所以只能完成一些简单操作。其特点是程序顺序执行,由前向后逐条执行指令。【例】试编写一程序计算下列表达式的值。 w=(v-(x*y+z-540))/x

式中x、y、z、v均为有符号字数据。 设x、y、z、v的值存放在字变量X、Y、Z、V中,结果存放在双字变量W之中,程序的流程图如图所示。§4.5.1顺序程序设计§4.5.1顺序程序设计顺序运算程序流程图源程序如下:DATA SEGMENT X DW 200 Y DW 100 Z DW 3000 V DW 10000 W DW 2DUP(?)DATA ENDSSTACK SEGMENTSTACK DB200DUP(0)STACK ENDSCODE SEGMENT ASSUMEDS:DATA,CS:CODE,SS:STACKSTART: MOV AX,DATA MOV DS,AX ;DATA→AX MOV AX,X IMUL Y;(X)*(Y)→DX:AX MOV CX,AX MOV BX,DX ;(DX:AX)→(BX:CX)

MOV AX,Z CWD ;(Z)符号扩展

ADD CX,AX ADC BX,DX ;(BX:CX)+ (DX:AX)→(BX:CX)

SUB CX,540

SBB BX,0 ;(BX:CX)-540→ (BX:CX) MOV AX,V

CWD ;(V)符号扩展

SUB AX,CX SBB DX,BX ;(DX:AX)- (BX:CX)→(DX:AX)

IDIV X ;(DX:AX)/X MOV W,AX ;商→W MOV W+2,DX ;余数DX→W+2 MOV AH,4CH INT 21HCODE ENDS

END START§4.5.1顺序程序设计§4.5.2分支程序设计

CASE结构…

case1case2casen条件?条件2?

case1case2casen条件1?NYYNIF-THEN-ELSE结构分支程序是利用条件转移指令实现程序执行次序改变的一种程序结构形式,即当程序执行到某一指令后,根据某个条件是否满足,分别执行不同的指令序列。一般来说,分支程序经常是先用比较指令或数据操作及位检测指令等来改变标志寄存器各个标志位。然后用条件转移指令进行分支。分支程序结构可以有两种形式,如下图所示,它们分别相当于高级语言中的IF-THEN-ELSE语句和CASE语句。§4.5.2分支程序设计【例】编写计算右面函数值的程序:设输入数据为X、输出数据Y,且皆为字节变量。程序流程图如下图所示。1 X>0Y=0 X=0-1X<0§4.5.2分支程序设计程序如下:data segmentx db-10y db?data endsstack segmentstack db200dup(0)stack endscode segment assumeds:data,ss:stack,

cs:codestart: mov ax,data

mov

ds,ax

cmp x,0 ;与0进行比较

jge a1 ;x≥0转a1

mov y,-1 ;x<0时,-1→y

jmp exita1:jg a2 ;x>0转a2

mov y,0 ;x=0时,0→y

jmp exita2:mov y,1 ;x>0,1→yexit:mov ah,4ch

int 21hcodeends end start§4.5.2分支程序设计

在实现CASE结构时,可以使用跳转表法使程序能根据不同的条件转移到多个程序分支中去。需要在数据段事先安排一个按顺序排列的转移地址表。输入数字作为偏移量。关键是要理解间接寻址方式JMP指令。【例5.5】设某程序有8路分支,试根据给定的N值(1-8),将程序的执行转移到其中的一路分支。 程序流程如图所示。用跳转表实现多路分支程序如下:DATA SEGMENT TAB DWP1,P2,P3,P4,

P5,P6,P7,P8 N DB5DATA ENDSSTACK SEGMENT DB200DUP(0)STACK ENDSCODE SEGMENTASSUMEDS:DATA,SS:STACK,

CS:CODESTART: MOV AX,DATA MOV DS,AX ┆ MOV AL,N DEC AL ADD AL,AL MOV BL,AL MOV BH,0 JMP TAB[BX]P1: …… ┆ JMP EXITP2: …… ┆ JMP EXITP2: …… ┆ JMP EXIT§4.5.2分支程序设计P3: …… ┆

JMP EXIT ┆P8: …… ┆EXIT: MOV AH,4CH INT 21HCODE ENDS END START

上述程序中的无条件转移指令的转移地址采用的是变址寻址。同理,转移地址也可以用寄存器间接寻址或基址加变址寻址。§4.5.2分支程序设计§4.5.3循环程序设计循环结构一般是根据某一条件判断为真或假来确定是否重复执行循环体。循环结构程序缩短了程序的长度、减少了占用的内存空间;但它并不简化程序执行过程,相反,由于增加了一些循环控制等环节,总的程序执行语句和时间会有所增加。循环程序一般由4部分组成:初始化、循环体、循环控制和循环结束处理。循环程序有两种结构形式:

DO-WHILE结构和DO-UNTIL结构DO-WHILE结构YN初始化循环体循环控制结束处理循环控制初始化循环体YN结束处理DO-UNTIL结构循环程序各部分的内容:(1)初始化:建立循环次数计数器,设定变量和存放数据的内存地址指针(常用间址方式)的初值等。(2)循环体:实现程序功能的、被重复执行的指令序列。(3)循环控制:修改变量和地址指针,为下一次循环做准备;修改循环计数器或者判断循环条件是否满足,满足则继续循环,否则结束循环。(4)结束处理:它主要用来分析和存放程序的结果。循环控制方式有多种,如计数控制、条件控制等。不管哪一种控制循环方式,最终都是要达到控制循环的目的。若考虑不周,会造成死循环,对这一点要注意。循环可以用跳转语句实现,如JMP,JZ等;也可以用专用循环控制语句实现,如LOOP、LOOPE/LOOPZ、LOOPNE/LOOPNZ。§4.5.3循环程序设计【例】求两个多字节数之和。这两个数在10050H地址开始的内存单元中,连续存放,低位在小地址一端,结果放在这两个数之后。设这两个多字节数均为8个字节长。S=A+BBAa0a1a2a3a4a5a6a7b0b1b2b3b4b5b6b7s0s1s2s3s4s5s6s7cf1cf2cf3+§4.5.3循环程序设计编制的程序START:MOV

AX,1000H

MOV

DS,AX;DS=1000H

MOV

SI,50H;第一个数指针SI=50H

MOV

DI,58H;第二个数指针DI=58H

MOV

BX,60H;结果指针BX=60H

MOV

CX,4;循环次数CX=4

CLC;清进位CF=0AA:MOV

AX,[SI];取一个字到AX

ADC

AX,[DI];AX←AX+[DI]+CF

MOV[BX],AX;存一个字到[BX]

PUSHF;保护进位CF

ADD

SI,2;修改第一个数的地址指针SI←SI+2

ADD

DI,2;修改第二个数的地址指针DI←DI+2

ADD

BX,2;修改结果指针BX←BX+2

POPF;恢复标志寄存器

LOOP

AA;CX←CX-1,若CX≠0转AA

HLT;CX=0,暂停例:

将首地址为A的字数组从大到小排序(冒泡算法,多重循环)

8,5,16,84,32

序号地址数比较遍数12341A82A+253A+4164A+6845A+83281684325168432858432168584321685§4.5.3循环程序设计多重循环NYN

开始(COUNT1)←N-1

AI>=AI+1?

结束Y(COUNT1)=0?(COUNT2)←(COUNT1)

I←0

AI←→AI+1

I=I+1(COUNT2)←(COUNT2)-1(COUNT2)=0?(COUNT1)←(COUNT1)-1NY

movcx,n;元素个数

deccx;比较遍数loop1:movdi,cx

;比较次数

mov

bx,0loop2: movax,A[bx];相邻两数

cmpax,A[bx+2];比较

jgecontinue

xchgax,A[bx+2];交换位置

mov

A[bx],axcontinue: addbx,2 looploop2

mov

cx,di looploop1

┆§4.5.4子程序的设计从功能上来讲,子程序是主程序的一个组成部分。为了实现程序的结构化、模块化,提高程序的可重用性,通常将主程序中具有公用性、重复性、功能相对独立和完整的一个程序段,单独设计成一个程序模块,供主程序调用,该程序模块就称为子程序。子程序可以实现源程序的模块化,简化源程序结构,提高编程效率。子程序可以嵌套。一定条件下,还可以递归和重入。§4.5.4子程序的设计子程序的定义是由过程定义伪指令PROC和ENDP来完成的。其格式如下: 过程名PROC[NEAR/FAR] ┆ 过程名ENDP子程序调用与返回子程序调用与返回由CALL和RET指令实现。子程序调用方式:近程(段内)调用、远程(段间)调用、直接调用(指令中直接给出调用地址)和间接调用(用寄存器或内存单元给出调用地址)。子程序调用实际是程序的转移,但与转移指令有所不同:子程序调用指令CALL执行时要保存返回地址,每个子程序都有RET指令实现子程序返回。转移指令不考虑返回问题。1子程序的定义、调用与返回F1PROC..

RET;将堆栈中的地;址弹出到CS:IP中F1ENDP

. .

CALLF1;将下条指令;的地址压入堆栈

. (1)现场保护与恢复转子前,CPU有关寄存器和内存有关单元是父程序的工作现场,在调用子程序前要设法保护这个现场。例如:

subnameproc ;具有缺省属性的subname过程 pushax ;保护现场:顺序压入堆栈 pushbx

;ax/bx/cx仅是示例

pushcx … ;过程体

popcx

;恢复恢复:逆序弹出堆栈

popbx popax ret ;过程返回

subname

endp

;过程结束§4.5.4子程序的设计2子程序设计(2)参数传递

指主程序与子程序之间相关信息或数据的传递。传递参数需要父程序与子程序默契配合,否则会产生错误结果。

入口参数(输入参数):主程序提供给子程序

出口参数(输出参数):子程序返回给主程序

参数的形式:①数据本身(传值)②数据的地址(传址)

参数传递方式:①寄存器②内存单元③堆栈§4.5.4子程序的设计2子程序设计子程序出口参数入口参数例:累加数组ary中的元素,将累加和存入变量sum,数据段的定义如下: datasegment

ary

dw1,2,3,4,5,6,7,8,9,10countdw10sumdw? dataends

用子程序计算数组元素的累加和

入口参数: 数组的逻辑地址(传址) 元素个数(传值)

出口参数: 求和结果(传值)§4.5.4子程序的设计①用寄存器传递参数把参数存于约定的寄存器中,可以传值,也可以传址。子程序对带有出口参数的寄存器不能保护和恢复(主程序视具体情况进行保护)子程序对带有入口参数的寄存器可以保护,也可以不保护;但最好一致本例中:入口参数:(CX)=元素个数, DS:BX=数组的段地址:偏移地址出口参数:(AX)=校验和§4.5.4子程序的设计codesegmentassumecs:code,ds:datastart:movax,data

mov

ds,ax

mov

bx,offset

ary

;BX←数组的偏移地址

mov

cx,count

;CX←数组的元素个数

callproadd;调用求和过程

mov

sum,ax;处理出口参数

mov ax,4c00h

int21hcodeendsendstartproaddprocnear

xorax,ax;累加器清0next: addax,[bx];求和 addbx,2;指向下一个字 loopnext retproadd

endp§4.5.4子程序的设计①通过寄存器传递参数子程序②通过内存单元传送参数主程序和子程序直接采用同一个变量名共享同一个变量,实现参数的传递不同模块间共享时,需要声明本例中: 入口参数: count=元素个数

ary=数组名(含段地址:偏移地址) 出口参数: sum=累加和§4.5.4子程序的设计§4.5.4子程序的设计datasegment

ary

dw1,2,3,4,5,6,7,8,9,10countdw10sumdw?dataendscodesegmentassumecs:code,ds:datastart: movax,data

mov

ds,ax

callproadd

mov ax,4c00h

int21hcodeendsendstartproaddprocnearpushaxpushcxpushsileasi,ary

mov

cx,count

xorax,axnext:addax,[si]addsi,2loopnext

mov

sum,axpopsipopcxpopaxretproadd

endp子程序②通过内存单元传送参数③通过堆栈传送参数主程序将子程序的入口参数压入堆栈,子程序从堆栈中取出参数。子程序将出口参数压入堆栈,主程序弹出堆栈取得它们。本例中: 入口参数: 顺序压入偏移地址和元素个数 出口参数:

(AX)=累加和§4.5.4子程序的设计例:累加数组中的元素datasegment

ary

dw10,20,30,40,50,60,70,80,90,100countdw10sumdw?dataendsstacksegment

dw100dup(?)

toslabelwordstackends§4.5.4子程序的设计③通过堆栈传送参数code1segment assumecs:code1,ds:data,ss:stackstart:

movax,stack

mov

ss,ax

movsp,offsettos

movax,data

mov

ds,ax

mov

bx,offsetarypushbx

mov

bx,offsetcountpushbx

mov

bx,offsetsumpushbxcallfarptr

proadd

movax,4c00h

int21hstartendp code1ends§4.5.4子程序的设计③通过堆栈传送参数code2segmentassumecs:code2proaddprocfar

pushbp

mov

bp,sppushaxpushcxpushsipushdi

movsi,[bp+0ah]

movdi,[bp+8]

mov

cx,[di]

movdi,[bp+6]code2endsendstart

xorax,axnext:addax,[si]addsi,2loopnext

mov[di],axpopdipopsipopcxpopaxpopbp

ret6proadd

endp(ip)(cs)sumcountarray

(di)

(si)

(cx)

(ax)(sp)(bp)(bp)

(bp)+0ah

(bp)+8

(bp)+6tos§4.5.4子程序的设计③通过堆栈传送参数要注意堆栈的分配情况,保证参数存取正确、子程序正确返回,并保持堆栈平衡。主程序实现平衡堆栈:addsp,n

子程序实现平衡堆栈:retn§4.5.4子程序的设计(1)子程序的嵌套子程序不但可以被主程序调用,而且也可以被其他子程序调用。这种一个子程序调用另一个子程序的情况,称为子程序的嵌套。(2)子程序的递归当子程序直接或间接地嵌套调用自身时称为递归调用,含有递归调用的子程序称为递归子程序。递归子程序一般对应于数学上对函数的递归定义,它往往能设计出效率较高的程序,完成相当复杂的计算,因而是很有用的。递归子程序必须采用寄存器或堆栈传递参数,递归深度受堆栈空间的限制。3子程序的嵌套与递归§4.5.4子程序的设计主程序子程序A子程序B子程序嵌套示意图……callproc_A……proc_A

……callproc_B……Callproc_Aretproc_B……ret3子程序的嵌套与递归§4.5.4子程序的设计在编写汇编语言程序的过程中,有时某个程序段要出现许多次,有时只是参数不同,但功能完全一样,则这样的程序段可以定义成一条指令,称它为宏指令。当程序中遇到这个程序段时,只需用一条宏调用语句,这样有效地缩短了源程序的长度,使源程序易读,也减少了由于重复书写而引起的错误。宏指令的使用要经过如下3个步骤:(1)宏定义:对各个宏指令进行定义,并分别起一个名字;

macro_name

MACRO[哑元表];形参 [LOCAL标号表]…………;宏定义体

ENDM(2)宏调用:在需要使用的地方,通过宏指令名来调用它;

macro_name[实元表];实参(3)宏扩展:由宏汇编程序用宏定义中的指令来代替宏指令名。 宏定义体复制到宏指令位置,实参代形参

LOCAL中的标号??0000~??ffff§4.5.5宏指令1宏指令的作用例题:保存寄存器宏展开:1pushax1pushbx1pushcx1pushdx1pushsi1pushdi宏定义:savereg

MACROpushaxpushbxpushcxpushdxpushsipushdi

ENDM宏调用:…

savereg…

§4.5.5宏指令例题:两个字相乘宏定义:multiply

MACROopr1,opr2,resultpushdxpushax

movax,opr1

imulopr2

movresult,axpopaxpopdx

ENDM宏调用: …

multiply

cx,var,xyz[bx] …宏展开:1pushdx1pushax1mov

ax,cx1imul

var1mov

xyz[bx],ax1popax1popdx§4.5.5宏指令例题:求绝对值宏定义:absol

MACRO

oper

LOCALnext

cmpoper,0

jge

next

neg

opernext:

ENDM宏调用:……absol

var……absol

bx……宏展开:

……1cmpvar,01jge

??00001neg

var1??0000:…………1cmpbx,01jge

??00011neg

bx1??0001:……§4.5.5宏指令宏的参数参数的形式灵活多变,可以是常数、变量、存储单元、指令操作码或它们的一部分,也可以是表达式,使用灵活多变的参数,同一个宏定义甚至可以执行不同的操作。几个宏操作符&:替换操作符:用于将参数与其他字符分开。用在宏体中。如果参数紧接在其他字符之前或之后,或者参数出现在带引号的字符串中,就必须使用该伪操作符。<>:字符串传递操作符:用在宏调用的实参中。在宏调用中,如果传递的字符串实参数含有逗号、空格等间隔符号,则必须用这对操作符,以保证字符串的完整。

2宏的参数及宏操作符§4.5.5宏指令

!:转义操作符:指示其后的一个字符作为一般字符,无特殊意义。如!!/!&%:表达式操作符:将后面跟的表达式的值作为实参,而不是将表达式本身作为参数。用在实参中。;;:宏注释符,用于表示在宏定义中的注释。采用这个符号的注释,在宏展开时不出现。§4.5.5宏指令

2宏的参数及宏操作符§4.5.5宏指令3与宏有关的伪指令(1)局部标号伪指令格式:LOCAL标号列表功能:当宏定义体内有标号,同一程序内多次调用,会造成标号的重复定义。这种情况下,应使用LOCAL加以说明,它必须是宏定义MACRO语句之后的第一条语句。用LOCAL说明的标号,第一次宏展开时,产生的标号为??0000,第二次宏展开时产生的标号为??0001,(??ffff)注意:※只能在宏体内使用。

※只能紧接在宏定义语句之后使用。(2)宏定义删除伪指令格式:PURGE宏名表功能:删除(注销)程序中引用的宏指令,使之在宏调用的地方不再生成宏扩展。(3)宏定义退出伪指令格式:EXITM功能:在宏体或重复定义的语句中,遇到EXITM语句时,终止以后的宏扩展。仅是源程序级的简化:宏调用在汇编时进行程序语句的展开,不需要返回;不减小目标程序,执行速度没有改变。通过形参、实参结合实现参数传递,简捷直观、灵活多变。还是目标程序级的简化:子程序调用在执行时由CALL指令转向、RET指令返回;形成的目标代码较短,执行速度减慢。需要利用寄存器、存储单元或堆栈等传递参数。宏子程序宏与子程序具有各自的特点,程序员应该根据具体问题选择使用那种方法通常,当程序段较短或要求较快执行时,应选用宏;当程序段较长或为减小目标代码时,要选用子程序。§4.5.5宏指令4宏与子程序的比较系统功能调用是OS向应用程序提供的编程接口,通过它,应用程序可以使用OS提供的各种功能,它有近百个功能供用户选择使用,主要包括I/O设备管理、存储管理、文件管理和作业管理等功能,从而实现对计算机软硬件资源的使用。(1)BIOS功能调用:存放在BIOS芯片中的程序段,支持最底层的硬件操作。(2)DOS功能调用:由OS提供的,实现OS功能的程序段。§4.6系统功能调用系统功能调用中的几十个子程序成为汇编语言程序员的重要工具,程序员不必了解所使用设备的物理特性、接口方式及内存分配等,不必编写繁锁的控制程序。调用它们时采用统一的格式,只需使用以下3个语句:(1)传送入口参数到指定寄存器中;(2)功能号送入AH寄存器中;(3)INT21H。调用结束后,系统将出口参数送到指定寄存器中或从屏幕显示出来。例如:

MOVDL,’A’ MOVAH,2 INT21H;在屏幕当前光标位置显示字符‘A’§4.6系统功能调用1系统功能调用的实现方法:软中断指令INT(1)键盘输入单字符(1号调用) 格式:

MOVAH,1 INT21H它没有入口参数,执行1号系统功能调用时,系统等待键盘输入,待程序员按下任何一键,系统先检查是否Ctrl-Break键,如果是则退出,否则将键入字符的ASCII码置入AL寄存器中,并在屏幕上显示该字符。(2)键盘输入字符串(0AH号调用)其功能是将键盘输入的字符串写入到内存缓冲区中,因此必须事先在内存储器中定义一个缓冲区。其第1字节给定该缓冲区中能存放的字节个数,第2字节留给系统填写实际键入的字符个数,从第3个字节开始用来存放键入的字符串,最后键入回车键表示字符串结束。如果实际键入的字符数不足填满缓冲区时,则其余字节填“0”;如果实际键入的字符数超过缓冲区的容量,则超出的字符将被丢失,而且响铃,表示向程序员发出警告。§4.6系统功能调用2常用的系统功能调用从键盘输入数据0AH号系统功能调用的使用格式如下所示:

……BUF

DB

20

DB

?

定义缓冲区

DB

20

DUP(?)

……

MOVDX,OFFSETBUF

MOVAH,0AH

0AH号系统功能调用

INT21H以上程序中,由变量定义语句定义了一个可存放20个字节的缓冲区,执行到INT21H指令时,系统等待用户键入字符串。程序员每键入一个字符,其相应的ASCII码将被写入缓冲区中,待程序员最后键入回车键时,由系统输出实际键入的字符数,并将其写入缓冲区的第2字节中。§4.6系统功能调用2常用的系统功能调用(3)无回显键盘输入单字符(8号调用)等待从键盘输入单字符,将其ASCII码置入AL寄存器中。但不送屏幕显示,其使用格式如下:

MOVAH,8INT21H它没有入口参数,与1号系统功能调用的区别仅在于键入的字符不送屏幕显示。§4.6系统功能调用2常用的系统功能调用向显示器输出字符(4)输出单字符(2号调用)

格式:

MOV

DL,‘A’MOV

AH,2INT21H执行2号系统功能调用时,将置入DL寄存器中的字符从屏幕上显示输出(或打印机打印输出)。(5)输出字符串(9号调用)其功能是将指定的内存缓冲区中的字符串从屏幕显示输出(或从打印机打印输出)。缓冲区中的字符串以“$”字符作为结束标志。§4.6系统功能调用2常用的系统功能调用9号系统功能调用的使用格式如下所示:……BUFDB′goodbye$′……MOVDX,OFFSETBUFMOVAH,9INT21H……执行9号系统功能调用时,将内存缓冲区BUF中存放的字符串(以“$”字符为结束)送屏幕显示输出(或送打印机打印输出)。§4.6系统功能调用2常用的系统功能调用返回操作系统(4CH号调用)

格式:

MOVAH,4CHINT21H它没有入口参数,执行结果是结束当前正在执行的程序,并返回操作系统。屏幕显示操作系统提示符(N>),N为当前使用的驱动器名。§4.6系统功能调用2常用的系统功能调用DOS键盘中断(INT21H)AH

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论