• 613.00 KB
  • 2022-04-29 14:19:44 发布

微机原理与接口技术课件PPT第6章 汇编语言程序设计.ppt

  • 119页
  • 当前文档由用户上传发布,收益归属用户
  1. 1、本文档共5页,可阅读全部内容。
  2. 2、本文档内容版权归属内容提供方,所产生的收益全部归内容提供方所有。如果您对本文有版权争议,可选择认领,认领后既往收益都归您。
  3. 3、本文档由用户上传,本站不保证质量和数量令人满意,可能有诸多瑕疵,付费之前,请仔细先通过免费阅读内容等途径辨别内容交易风险。如存在严重挂羊头卖狗肉之情形,可联系本站下载客服投诉处理。
  4. 文档侵权举报电话:19940600175。
'第6章汇编语言程序设计6.1汇编语言程序6.28086/8088汇编语言语句的类型及格式6.38086/8088汇编语言的数据项及表达式6.4伪指令6.5宏指令6.6DOS和BIOS调用6.7汇编语言程序基本结构和程序设计实例1 【重点难点】1.指令语句、伪指令语句;2.各种操作数、操作符以及汇编语言的段结构;3.程序的三种基本结构;【教学内容】本章主要讲授汇编语言中所使的各种操作数、操作符、伪指令以及汇编语言程序的四种结构形式,伪指令不产生目标代码,对汇编程序在汇编过程中提供必要的控制信息2 6.1汇编语言程序6.1.1汇编语言源程序实例汇编语言是用意义明确的助记符来表示指令的操作码、操作数。汇编语言是面向机器的语言,不同的机器有不同的汇编语言。用汇编语言编写的程序具有执行速度快、占用存储空间小、实时性能好等特点。汇编语言被大量用于编写计算机系统程序、实时控制程序等。汇编语言程序和高级语言程序一样,有顺序、分支、循环、子程序四种结构形式。但是汇编语言程序的设计思想和设计方法却与高级语言不同,汇编语言程序设计要了解硬件工作的特性、接触机器底层,要求熟知汇编指令的功能和用法,同时还要掌握用于汇编过程的伪指令。3 1.编写一个汇编语言程序的基本步骤如下:(1)分析题意,确定设计思想及算法。(2)对于复杂的算法要画出程序及流程图。(3)根据框图编写程序。(4)上机调试程序。通过第5章的指令介绍和个案分析,我们已经了解汇编语言程序的结构和编辑运行过程,下面再通过一个具体的实例分析汇编语言源程序的组成。4 [例6.1]在屏幕上输出”hello,world!”;hello.asmdatasegment;数据段定义开始msgdb"hello,world!",0Dh,0Ah,"$";0Dh、0Ah表示回车、换行dataends;数据段定义结束codesegment;代码段定义开始assumecs:code,ds:data;assume伪指令指定段寄存器与对应段名start:movax,data;.段名即表示段起始地址的高16位即段基址movds,ax;将数据段段基址装入DS寄存器movdx,offsetmsg;取待显示字符串的有效地址movah,09h;dos调用09号功能,显示输出int21h;doit!movah,4ch;dos调用4C号功能,返回操作系统int21hcodeendsendstart5 2.程序结构分析:(1)程序结构中定义了两个段,一个是数据据段DATA,另一个是代码段CODE。数据存放在数据段中,指令代码存放在代码段中。段的划分用段定义伪指令SEGMENT来实现。(2)数据段中的变量msg可看成字符串在数据段中的偏移地址。(3)用assume伪指令说明段寄存器与段名之间的对应关系,实际是确定段的类型。(4)Start加冒号代表其后的指令所在代码段的偏移地址,其逻辑地址应为:CS:START;(5)INT21H是DOS中断调用指令,有很多功能。功能号由AH指出。09号功能表示在当前位置显示输出,遇’$’符结束。4C号功能的作用是结束程序,返回操作系统。(6)程序中的分号“;”后面的内容为注释。编写程序时最好加入注释,便于以后的阅读和修改。(7)程序最后一句ENDSTART是伪指令,用于通知汇编程序,整个程序结束;后跟标号START表示程序执行时是从START开始的。6 6.1.2从源程序到可执行程序图6.1源程序到可执行程序1.步骤:⑴用记事本或其他编辑软件建立源程序,汇编语言源程序即可用大写也可用小写,扩展名为.ASM。(2)源程序经过汇编程序MASM(翻译)后生成二进制目标文件,文件默认与源程序同名,扩展名为.OBJ。在汇编过程中可以指定生成列表文件.LST和符号索引文件.CRF。(3)目标程序需经过连接生成可执行文件,文件默认与源程序同名,扩展名为.EXE。在连接过程中,可以指定生成内存映像文件.MAP和库文件.LIB。7 2.汇编器MASM和连接器LINK的使用设上述源程序存在C:8086hello目录下,打开【开始】→【程序】→【附件】→【命令提示符】进入DOS目录。(1)进入hello目录,执行edithello.asm,如图6.2所示,编辑源程序,或用记事本等其他编辑软件。(2)执行MASM命令对源程序进行汇编,如图6.3,确定源程序没有编译错误,若有错误,则应修改源程序中错误后重新编译。在MASM命令行后加“;”不提示确认信息。若在汇编时输入列表文件名,除生成.obj目标文件外还会产生.lst列表文件。下面是上述示例的列表文件。8 图6.2进入edit编辑窗口图6.3汇编后提示信息9 图6.4连接后提示信息10 行号偏移量:机器码源文件[1]:;hello.asm[2]:datasegment;数据段定义开始[3]0000:68656C6C6Fmsgdb"hello,world!",0Dh,0Ah,"$"2C776F726C64210D0A24[4]:dataends;数据段定义结束[5]:codesegment;代码段定义开始[6]:assumecs:code,ds:data[7]0010:B80000start:movax,data;.段名表示段基址的高16位[8]0013:8ED8movds,ax;将数据段基址装入DS寄存器[9]0015:BA0000movdx,offsetmsg;取待显示字符串的有效地址[10]0018:B409movah,09h;dos调用09号功能,显示输出[11]001A:CD21int21h;doit![12]001C:B44Cmovah,4ch;返回操作系统[13]001E:CD21int21h[14]:codeends[15]:endstart11 清单中最左边一列为行号,第二列(偏移量)为源程序目标代码段内存放的偏移地址的一个对照表,第三列(机器码)为对应指令的机器码或内存初始数据。当源程序出现语法错误时,MASM则在错误行后面给出错误性质提示。(3)执行LINK命令对生成的hello.obj进行连接。确定没有错误如图6.4所示,若有错误,则应修改源程序中错误重新编译。在LINK命令行后加“;”不提示确认信息。3.调试多数情况下源程序编译通过后可能还隐藏有其他一些非语法或结构性错误,如在输入程序时漏掉一行指令,宏汇编程序是不会发现并报错的。我们可以用DOS操作系统所带的DEBUG调试程序对可执行文件hello.exe进行调试,在进入debug时应加上文件的扩展名,如:12 (1)反汇编U命令图6.5反汇编窗口13 如图6.5所示,反汇编命令的最左边一列是程序的逻辑地址,从上边的状态中可知当前程序第一条指令CS段值为13DAH,IP偏移量为0000H,这里要说明一点是debug中的所有数据均为16进制数;反汇编命令输出的中间部分为对应指令的机器码;最右边部分为反汇编后的源指令。U命令显示的第一条指令是源程序中标号为START指令MOVAX,DATA,在debug下DATA已经变为13D9H,说明系统已将数据段分配到13D90H单元开始的存储区。本程序较短,红线下面的反汇编代码是系统内存区中存放的随机无效指令,如果程序较长可连续使用u命令。或用“u[起始地址][结束地址]”显示指定偏移地址的反汇编代码如图6.6所示。14 图6.6反汇编窗口(2)查看和修改寄存器R命令R命令有两种用法,如图6.7所示:直接键入R将显示CPU所有寄存器和标专位;在R后跟寄存器名,则显示寄存器的内容,在冒号后可键入新值;再用R命令就可看到修改后的内容了。15 图6.7R命令窗口R命令的最后一行是代码段的一条指令的反汇编。所谓反汇编是将二进制的机器指令显示成汇编指令。该行由三部分组成:最左边0C1C:0005表示该指令所在单元的逻辑地址,即CS:IP;中间BA0000表示该指令的机器吗;最右边显示的是汇编指令MOVDX,0000。R命令的第二行右边显示的是CPU标志寄存器各标志位的状态。其含义如表6.1。16 OFDFIFSFZFAFPFCF标志位OVDNEINGZRACPECY表示各标志位置位NVUPDIPLNZNAPONC表示各标志位复位表6.1debug下FLAG标志含义(3)单步执行命令T用于跟踪一条指令或多条指令的执行,每执行一条指令后,都显示出当前各寄存器内容和标志位的状态以及下一条将要执行的指令。在执行T命令前应确认当前程序IP地址,我们可用u命令查看当前的反汇编指令地址或用R命令查看寄存器状态IP。17 图6.8T命令窗口用-rip可查看和修改当前ip的值,如要修改在“:”后输入新值,若不需要修改直接按回车。从图6.8状态中可看到执行t命令前ds寄存器的值为13C9,执行t命令后ds寄存器的值为13D9。18 (4)查看存储单元内容命令D在用D命令查看存储单元内容之前应先加载段基址如图6.9,这里我们先用T命令单步执行前两条指令,用以加载数据段基值,然后用D命令显示内存区数据段的内容。图6.9的操作中分别用D命令显示了数据段、代码段、附加段、数据段的内存区数据,D命令后直接跟偏移量则默认显示数据段的内容,如不跟结束地址,则显示8行。D命令状态中最左边部分显示的是对应数据段的逻辑地址,中间部分显示的是对应段的内存区数据,最右边部分是对应单元的ASCII字符。如本程序中数据段定义了“hello,world!”,其对应单元的ASCII代码分别为68、65等,其偏移地址分别是0000、0001,一般我们把程序中DB伪指令前面的标识符称为变量,如本例中msg,其有效地址就是0000,在数据段中,为字节变量,这就是变量的属性。19 图6.9D命令窗口(5)执行程序命令GG命令可连续的执行指令一一直到所给出的断点为止。如图6.10中我们在000C(图中省略了前面3个0)地址设置了程序断点,程序执行到此指令,暂停并显示寄存器的状态。20 图6.10G命令窗口(6)退出debug命令Q4.执行在命令行后输入生成的可执行文件名即可执行该文件,如图6.11所示。图6.11执行窗口21 6.28086/8088汇编语言语句的类型及格式6.2.1汇编语言语句的类型汇编语言源程序的语句可分为二大类:指令性语句(简称指令语句)和指示性语句(简称伪指令语句)。指令性语句是由指令组成的一种可执行语句,它在汇编时,汇编程序将产生与它一一对应的机器目标代码。在第5章中讲的六大类指令均为可执行指令,例如:汇编指令机器码MOVDX,AX8ED8ADDAX,BX03C3指示性语句是指由伪指令组成的一种只起说明作用而不能执行的语句,它在汇编时只为汇编程序提供进行汇编所需要的有关信息,如定义符号,分配存储单元,初始化存储器等,而本身并不生成目标代码。例如:22 DATASEGMETNAADW20H,-30HDATAENDS这三条伪指令语句只是告诉汇编程序定义一个段名为DATA的数据段。在汇编时,汇编程序将把变量AA定义为一个字类型数据区的首地址,在内存区的数据段中存放两个字类型的数据20H和-30H,数据的存放形式如图6.12。图6.12AA字变量数据区23 6.2.2汇编语言语句的格式汇编语言源程序的语句一般由4个字段组成,但在指令性语句和指示性语句中的含义有些区别。1.指令性语句的格式[标号:]指令助记符[操作数表][;注释][]表示可以任选的部分;操作数表是由逗号分隔开的多个操作数。(1)标号标号代表“:”后面的指令所在的存储单元地址,供JMP、CALL和LOOP等指令作操作数使用,以寻找转移目标地址。另外标号还具有一些其它“属性”,我们后续介绍。(2)指令助记符包括8086/8088的全部指令助记符,以及用宏定义语句定义过的宏指令名。宏指令在汇编时将用相应指令序列的目标代码插入。(3)操作数表对8086/8088的一般性指令来说,操作数表可以是一个或两个操作数,若是两个操作数,则称左边的操作数为目的操作数,右边的操作数为源操作数;对宏指令来说,可能有多个操作数。操作数之间用逗号隔开。(4)注释以“;”开始,用来简要说明该指令在程序中的功能,以提高程序的可读性。24 2.伪指令语句的格式[名字]伪操作命令[操作数表][;注释]其中“名字”可以是标识符定义的常量名、变量名、过程名、段名等。所谓标识符是由字母开头,由字母、数字、特殊字符(如?、下划线、@等)组成的字符串。注意:名字的后面没有冒号,这是它同指令语句中标号在格式上的主要区别。25 6.38086/8088汇编语言的数据项及表达式操作数是汇编语言语句中的一个重要字段。它可以是存储器、存储器单元或数据项。而汇编语言能识别的数据项以可以是常量、变量、标号和表达式。6.3.1常量常量是指汇编时已经有确定数值的量,它主要作为指令语句中的立即数或存储器操作数的组成部分(如位移量)或在伪指令语句中用于给变量赋值。常量有多种表示形式,常见的有二进制数、十六进制数、十进制数和ASCII码字符串。其中,十六进制数的第一个数值必须是0~9,如7A65H、0FA9H等,ASSII码字符串是用单引号括起来的一个或多个字符,如’IBMPC’、’OK’等。26 常量可以用数值形式,直接写在汇编语言语句中,也可以用符号形式预先给它定义一个“名字”,供编程时直接引用。我们把用“名字”表示的常量称为符号常量,符号常量是用伪指令“EQU”或“=”来定义的。例如:PIEQU314REQU6MOVAX,2*PI*R常量是没有属性的纯数据,它的值是在汇编时确定的。6.3.2变量变量是内存中的一个数据区的名字,即数据所在存储单元的符号地址,它可以作为指令中的存储器操作数来引用。由于存储器是分段使用的,因而对源程序中定义的变量有3种属性:段属性(变量所在段的段地址)、偏移属性(该变量与起始地址之间相距的字节数)类型属性(数据项的存取长度单位,即一个数据所占单元数)。27 变量名由字母开头,其长度不能超过31个字符。变量的定义是由伪指令完成的。在定义变量时,变量名对应的是数据区的首地址。若需对数据区中其他数据进行操作时,必须修改地址值以指出哪个数据项是指令中的操作数。例如:xdb‘0123456789’;从x开始存放十个数值的ASCII码……MOVAL,x+1;取1的ASCII码31H送AL寄存器6.3.3标号标号是为指令性语句所在地址所起的名字,它是指令的符号地址,表明该指令在存储器中的位置,作为程序转移的转向地址(目标地址)。和变量一样,标号也具有3个属性:段属性(段地址)、偏移属性(段内偏移地址)和类型属性(段内转移还是段间转移)。标号的段属性和偏移属性分别指段地址和段内偏移地址,而类型属性分为NEAR和FAR两种。28 “标号”通常只在循环、转移和调用指令中使用。使用时要注意两种类型标号的不同点:NEAR类型的标号是指标号所在的语句和调用指令或转移指令在同一个代码段中,执行调用指令或转移指令时,只需要把标号的偏移地址送给IP,就可以实现调用和转移,并不需要改变代码段寄存器CS的值,而FAR类型的标号不同,它所在的语句与其调用指令或转移指令不在同一个代码段中,执行调用指令或转移指令时,不仅需要改变偏移地址IP的值,而且还需要改变代码段寄存器CS的值。6.3.4表达式与运算符以上介绍的常量、变量和标号是汇编语言中表示数据的3种基本形式。在实际使用时,通常需要将它们用运算符组合成所谓的表达式作为汇编语言的数据。注意,表达式并不是指令,所以它本身不能执行,而只能在汇编时由汇编程序预先对它们进行运算,然后再将所得的值作为操作数形成机器编码,也就是说表达式的值是由汇编程序来计算的。29 8086/8088汇编语言中使用的表达式有两类:一类是数值表达式,它在汇编时只产生一个数值,仅具有大小而无其它属性,可以作为指令的立即数或数据区的初值使用。另一类是地址表达式,它产生的结果表示一个存储器的地址,具有段属性、偏移属性和类型属性。表达式由运算对象和运算符组成。运算对象可根据不同的运算符选用常量、变量或标号,常用的运算符有以下几种类型。1.算术运算符常用的算术运算符包括加(+)、减(-)、乘(*)、除(/)和取余(MOD)。其中MOD运算符表示两整数相除以后取余数,例如17MOD7结果为3。[例6.2]MOVAL,17MOD7汇编后相当于指令MOVAL,330 算术运算符用于数值表达式时,其汇编结果是一个数值。注意,除了加和减运算符可以使用变量和标号外,其他运算符只适用于常量的数值运算。2.逻辑运算符逻辑运算符包括AND(与)、OR(或)、XOR(异或)和NOT(非)。它能够对二进制数进行按位的操作。[例6.3]MOVAX,00FFHAND0FF00H;汇编为:MOVAX,0000HANDBX,5500HAND0F00H;汇编为:ANDBX,0500H逻辑运算符的功能与逻辑指令不同,前者在汇编时产生指令操作数的数值,而后者在执行程序时完成逻辑操作。3.关系运算符31 关系运算符包括EQ(相等)、NE(不等)、LT(小于)、GT(大于)、LE(小于或等于)和GE(大于或等于)共6种。关系运算符必须对两个性质相同的操作数进行关系运算,比如都是数字或是同一段内的两个存储器地址。运算结果关系成立时逻辑值为真,用0FFFFH表示;关系不成立时逻辑值为假,用0表示。[例6.4]设X和Y是变量MOVAX,XLTY若偏移量X=0?92 DATASEGMENT;数据段XDB-7YDB0DATAENDSCODESEGMENT;代码段ASSUMECS:CODE,DS:DATASTART:MOVAX,DATA;初始化DSMOVDS,AXMOVAL,X;取数XCMPAL,0JNSPLUS;X≥0则转移NEGAL;X<0则求补JMPMINUPLUS:ADDAL,5MINU:MOVY,ALMOVAH,4CHINT21HCODEENDSENDSTART93 6.7.3循环结构程序设计在实际应用中,如果一段程序需要多次重复执行,这种程序结构为循环程序,主要用来处理带有重复性的问题,可以简化源程序,缩短目标程序,节约内存。循环结构程序由四部分构成(1)初始化部分循环准备部分,包括循环工作部分的初态和结束条件初态。例如建立地址指针,设置计数器初值,设置变量初值。(2)循环处理部分这是循环程序的主体,该段程序在整个操作中要重复执行多次。(3)循环控制部分即用什么条件来控制循环的继续进行和循环的结束。主要有两种方法:94 ①用计数器控制循环:这种程序的循环次数是已知的,LOOP指令是构成这类控制的很好工具。②用条件控制循环:若程序循环的最大次数是已知的,使用LOOPZ或LOOPNZ指令可提前结束程序。若程序的循环次数是未知的或难以判定,则应用其它条件指令控制循环结束。(4)循环结束后处理部分这部分是对结果进行存储或输出的处理部分。循环程序的基本结构形式有如图6.23所示两种:①当型程序结构当型程序结构执行时,先判循环的条件是否满足,所以有可能循环体一次也不执行,所以这种循环结构称为允许零次循环结构,又称“先判断,后执行”循环结构。②直到型循环结构直到型循环程序结构执行时,循环体至少要执行一次后,才判别循环是否结束。所以这种循环结构称为不允许零次循环结构,即不论循环是否满足,至少要执行一次循环体,又称“先执行,后判断”循环结构。95 图6.23循环程序的基本结构形式96 循环结构分为单重循环和多重循环。单重循环循环路径仅一个,循环控制变量也为一个;多重循环循环路径有多个,大循环中有小循环,循环控制变量有多个,多重循环内外循环不能交叉,多重循环可从内循环转到外循环,但不允许从外循环直接转到内循环。[例6.20]已知内存BUF开始的地址单元存放8个字节数据,请将其中的奇数顺序存入OS开始的存储单元、并统计奇数的个数。分析:(1)奇偶判断:对二进制数,判断其奇偶性看最低位即可,具体请同学们思考?(2)实现:先赋循环初值,使SI指向BUF源地址、DI指向OS目的地址,CX置数据个数,BX寄存器用于累加奇数个数,初始值为0,进入循环后,取一个数判断是否为偶数,若不是偶数,则把该数存入DI指向的目的存储区,并使DI加1,若是偶数,则使SI加1指向下一个数,并判断8个数是否判断完,若没有,则继续。流程如图6.24。97 YYN图6.24奇偶判断SI指向BUF源地址,DI指向OS目的地址保存奇数个数返回AL为偶数?8→CX,0→BX取SI指向的内存单元数据→ALBX+1→BXAL→DI指向的目的地址DI+1→DISI+1→SICX-1≠0?NYN返回数据个数COUNT→CX,0→AX(累加和)AX→SUMSI+2→SIAX+[SI]→AXCX-1=0?BLOCK→首址SI图6.26求累加和98 DATASEGMENT;数据段BUFDB12H,23H,34H,56H,78HDB9AH,0ABH,0BCHCOUNTEQU8OSDB8DUP(?)OSUMDW0DATAENDSCODESEGMENT;代码段ASSUMECS:CODE,DS:DATA,ES:DATASTART:MOVAX,DATA;初始化DSMOVDS,AXLEASI,BUF;指向数组首址LEADI,OS;指向结果首址MOVCX,COUNT;取计数值XORBX,BXAGAIN:MOVAL,[SI];取一个数TESTAL,01;测试数据的奇偶性JZEVENS;是偶数则转移INCBX;是奇数则加一MOV[DI],AL;存奇数INCDI;修改数组指针EVENS:INCSIDECCX;修改计数值JNZAGAIN;数据没有测试完继续MOVOSUM,BX;存奇数个数MOVAH,4CHINT21H;返回DOSCODEENDSENDSTART99 [例6.21]内存BLOCK开始连续存放10个数据,每个数据都是16位带符号数,求其累加和并存入SUM单元(和不大于16位)。分析:循环开始前SI指向BLOCK数据区首址,置CX为数据个数,用LOOP指令控制循环,循环结束保存累加和。DATASEGMENTBLOCKDW1,2,3,4,5,6,7,8,9,10COUNTEQU($-BLOCK)/2SUMDW?DATAENDSCODESEGMENTASSUMECS:CODE,DS:DATASTART:MOVAX,DATAMOVDS,AX;初始化DSMOVAX,0;初值为0100 MOVSI,OFFSETBLOCK;数据区地址MOVCX,COUNT;数据个数LOP:ADDAX,[SI];加上一个数ADDSI,2;修改指针LOOPLOP;CX减1,不为0转LOPMOVSUM,AX;存和MOVAH,4CHINT21HCODEENDSENDSTART101 [例6.22]软件延时程序。由于每条指令执行所需的T状态是固定的,可以用多重循环的方法,实现延时。设fosc=10MHz,时钟周期tc=0.1us,即1个T状态为0.1us。DELAYPROC;指令T状态MOVBX,100;4DEL1:MOVCX,5882;4DEL2:LOOPDEL2;17或5(请思考什么时候为17?什么时候为5?)DECBX;2JNZDEL1;16或4RET;8DELAYENDP延时Td={100*[17*(5882-1)+5+4+2+16]-12+4+8}*0.1us=1000.0004ms≈1s102 [例6.23]冒泡排序从变量ARRAY开始存放6个字的数组,每个数组元素都是16位带符号数,现要求把10个数按照降序(从大到小)排列,仍然存放到ARRAY开始的存储区内,一个无序表变成有序表。方法如下:(1)数组中有N个元素(N个数据)。从第一个元素开始,每次把一个数A(i)与相邻的A(i+1)进行比较,例如A(1)与A(2)比较、A(2)与A(3)比较……直到A(N-1)与A(N)比较。每次比较中,若前一个数比后一个数小,则交换两数,使大数放到前面(低地址),小数放到后面(高地址),若每次比较前一个数比后一个数大,则不交换。这样一次从头到尾比较过程称为一次分选。一次分选最多要进行N-1次比较。第一次分选后,N个数中的最小数必然落到最后位置上,即沉到最底部,N个数中至少有一个已经按要排好。(2)然后进行二次分选,从第一数开始再逐个与相邻的数比较,完成第二次分选后,至少有两个数已按要求顺序排好。最多进行N-1次排序即可完成整个数组的排序过程。(3)若某次排序中,未发生交换,则说明整个数组已经按要求的顺序排好,即可退出排序操作。103 以下以6个数为例,每次分选的过程如下:574386原始顺序754863第一次分选结果758643第二次分选结果786543第三次分选结果876543第四次分选结果876543第五次分选结果104 DATASEGMENTARRAYDW5,7,4,3,8,6COUNTEQU($-ARRAY)/2DATAENDSCODESEGMENTASSUMECS:CODE,DS:DATASTART:MOVAX,DATA;初始化DSMOVDS,AXMOVBL,0FFH;交换标志A1:CMPBL,0FFHJNEA4;交换标志位不等-1说明排序结束XORBL,BL;交换标志位清0MOVCX,COUNT-1;内循环次数(数组个数)XORSI,SI;SI清0105 A2:MOVAX,ARRAY[SI]CMPAX,ARRAY[SI+2];ARRAY(I)和ARRAY(I+1)比较JGEA3XCHGARRAY[SI+2],AX;ARRAY(I)>=ARRAY(I+1)则交换MOVARRAY[SI],AXMOVBL,0FFH;有交换重置交换标志A3:INCSIINCSILOOPA2JMPA1A4:MOVAH,4CHINT21HCODEENDSENDSTART106 6.7.4子程序设计子程序是一种标准的功能程序段,它可被主程序或其它子程序多次调用,以简化程序设计,节约内存,优化程序结构等。子程序具有通用性、可浮动性、使用方便性等特点。在编写子程序时,应注意以下问题:子程序名称,每个子程序应当有唯一的子程序名称。入口参数,调用本过程时,要求的参数。出口参数,过程结束时,运算结果的存放位置。[例6.24]已知内存BUF1和BUF2开始的地址单元存放两组整型无符号数,请分别找出其中的最大数,并将两个最大值的平均值存入RESULT单元。分析:此程序设计了一个求最大数的子程序,供主程序在段内调用,如图6.28,同时还设置了堆栈段以保证主程序与子程序的调用、返回及现场的保护和恢复。107 YNYYNCX-1=0?取第一个数[SI]→AXSI+2→SI[SI]→AXAX≥[SI]?CX-1=0?返回N寻找最大数子程序图6.28调用子程序求两组数的最大值BX右移1位(BX除2)SI指向BUF1BUF1数据个数→CX调用FMAX最大值AX→BXSI指向BUF2BUF2据个数→CX调用FMAXBX+AX→BXBX→RESULT结束主程序图6.28调用子程序求两组数的最大值108 DATASEGMENT;数据段BUF1DW0712H,08723H,0034H,7856H,0C123H,909AHCOUNT1=$-BUF1BUF2DW0712H,7008H,909AH,0A13BH,0C123HCOUNT2=$-BUF2RESULTDW?DATAENDS;………………………………………………………STACKSEGMENTSTACKDW20DUP(?)STACKENDS;………………………………………………………CODESEGMENT;代码段ASSUMECS:CODE,DS:DATA,SS:STACK109 START:MOVAX,DATA;初始化DS,SSMOVDS,AX;………………………………………………………MOVSI,OFFSETBUF1;指向第一个数组首址MOVCX,COUNT1;取第一个计数值CALLFMAX;调用求最大值子程序MOVBX,AX;存第一个最大值MOVSI,OFFSETBUF2;指向第二个数组首址MOVCX,COUNT2;取第二个计数值CALLFMAXADDBX,AX;求最大值之和RCRBX,1;求最大值平均值MOVRESULT,BX;存最大值平均值MOVAH,4CHINT21H;返回DOS110 FMAXPROCNEARMOVAX,[SI];取第一个数据DECCXJZRTN;计数值为0则转移FMAX1:INCSI;修改数组指针INCSICMPAX,[SI];两个数比较JAEFMAX2;第一个数大则转移MOVAX,[SI];第二个数大则送入AXFMAX2:LOOPFMAX1;计数值减一不为0则转移RTN:RETFMAXENDPCODEENDSENDSTART111 [例6.25]从键盘输入一个十六进制(0~ffffH)正数,并转换为十进制数在屏幕上显示出来。输入以非0~9,a~f键为结束符。HTODSEGMENTASSUMECS:HTODSTART:CALLHTOB;十六进制转换为二进制数CALLCRLF;打印回车和换行CALLBTOD;二进制转换为十进制数CALLCRLFMOVAH,4CHINT21H;返回DOS112 ;子程序HTOB:将键盘输入的十六进制数转换为二进制数;入口参数:键盘输入的0~f键的ASCII码存在AL中;出口参数:二进制数在BX中HTOBPROCMOVBX,0NEXT:MOVAH,01;键盘输入0~f键的ASCII码存在AL中INT21HSUBAL,30H;ASCII码转换为二进制数JLEXIT;小于0则退出CMPAL,10113 JLADTO;是(0~9)则转换SUBAL,27H;是(0~f)?CMPAL,0AHJLEXIT;不是(0~f)则退出CMPAL,10HJGEEXIT;不是(0~f)则退出;十六进制数0~fH转换为二进制数ADTO:MOVCL,4SHLBX,CLMOVAH,0ADDBX,AXJMPNEXTEXIT:RET;返回主程序HTOBENDP114 ;子程序BTOD:将二进制数转换为十进制数并显示;入口参数:二进制数存在BX中;出口参数:一位十进制数存在DL中BTODPROCNEARMOVCX,10000;除以10000CALLDDIVMOVCX,1000;除以1000CALLDDIVMOVCX,100;除以100CALLDDIVMOVCX,10;除以10CALLDDIVMOVCX,1;除以1CALLDDIVRET;返回主程序;……………………………………………………………BTODENDP115 ;除法子程序DDIV:实现(DX,BX)/(CX),并将商转换为ASCII码显示DDIVPROCNEARMOVAX,BXMOVDX,0DIVCXMOVBX,DX;余数在BX中MOVDL,AL;商在DL中ADDDL,30H;商转换为ASCII码显示MOVAH,02INT21HRET;返回BTODDDIVENDP116 ;………………………………………………………CRLFPROCNEAR;产生回车换行MOVDL,0AHMOVAH,02INT21HMOVDL,0DHMOVAH,02INT21HRETCRLFENDP;………………………………………………………HTODENDSENDSTART117 本章小结汇编语言源程序由一条条语句组成。语句分为指令语句和伪指令语句。指令语句与机器指令相对应,是在程序运行期间执行的;伪指令是在汇编程序对源程序进行汇编时由汇编程序执行的。变量与标号类似,都是存储单元的符号地址,但变量对应的存储单元中存放的是数据,而标号对应的存储单元中存放的是指令。表达式是操作数和运算符组合的序列,在汇编时得到一个确定的值。8086有算术、逻辑、关系、分析、属性运算符。要特别注意语句中操作数的类型应该明确。对有源和目的操作数的语句,还要求两者类型一致。如果两个操作数中的一个类型明确,一个类型不明确,则汇编程序规定以明确的那个类型作为两个操作数的共同类型。118 符号定义中的EQU伪指令不允许重复定义,而=伪指令可以重复定义。伪指令DB、DW、DD分别定义字节、字和双字。段定义SEGMENT伪指令,一般情况下可以不加类型及属性说明,但如果需要各其它段连接时需要加上这些说明。ASSUME伪指令仅建立起各段与段寄存器的对应关系,但不等于段寄存器中有了具体的段基值,在程序中应注意段寄存器的加载。程序的基本结构有四种:顺序、分支、循环和子程序。任何复杂的程序都是由基本结构组成。在编写程序时对经常使用的程序段,可以子程序,然后以子程序的形式调用。注意区分宏调用与子程序调用,宏调用是程序段的展开,实质是用宏程序段替换宏指令,可以带参数,多次使用宏指令会增加目标程序的长度;子程序调用不允许带参数,需要CALL指令完成调用过程。119'

您可能关注的文档