第5章汇编语言程序设计5.1汇编语言软件开发步骤及输入/输出问题5.1.1汇编语言软件开发步骤(略)5.1.2汇编语言编程时的输入/输出问题1.DOS功能调用的方法调用DOS功能调用与调用一般子程序不同,有特定的步骤和指令,如下:(1).调置入口参数,如所调用的子程序不需参数则可省略此步。(2).欲调用的子程序编号送到AH寄存器中。(3).发中断调用指令INT21H。2.DOS基本I/O功能调用(1).1号功能调用(键盘输入)入口参数:无出口参数:所读取的字符ASCII码在AL内。功能:此调用扫描键盘,若有键按下,先检查是否是Ctrl-Break键,若是则退出命令执行并调用DOS的Ctrl-Break处理程序;若否,则将字符ASCII码设置到AL中,同时在屏幕上显示这个字符,然后返回.若无键按下,该调用等待直到有键按下为止。(2).2号功能调用(在屏幕上显示一个字符)入口参数:DL=待显示字符的ASCII码出口参数:无功能:将字符显示在屏幕上.但如果字符是控制符,则实际执行相应的功能.(3).9号功能调用(字符串显示)入口参数:DS:DX指向待显示字符串,该字符串必须以“$”作为结束符出口参数:无功能:结束符之前的字符都被显示在屏幕上。(4).10号功能调用(字符串输入)入口参数:DS:DX指向输入缓冲区。输入缓冲区必须由用户在调用前准备好,具有如下格式:出口参数:用户实际输入字符个数设置在缓冲区第二个单元中。功能:从键盘上读取一个字符串。5.2顺序程序设计顺序程序设计即程序内指令顺序执行,无分支和循环,这是最简单的一类程序。5.2.1字符串的输入输出例5.1编一程序先提示用户输入一个字符串,然后读取用户的输入,图5-1DOS的10号调用输入缓冲区并将用户输入的数据在下行输出。(程序见课本)5.2.2BCD码的显示一个BCD码数据不能整体进行显示,要先将其转换为单个字符的ASCII码,再用DOS系统功能调用的2号功能完成字符显示。例5.2在字节存储单元DAT中有一个BCD码数据69H,要求将该数据显示到屏幕上。(程序参见课本)5.2.3查表利用表格进行数据检索处理,是数据处理的重要方法之一,一般称之为查表法.当表格不太复杂时,可采用顺序程序设计。例5.3在以TABLE为首地址的内存中顺序存有0~6的立方数表。今从键盘上输入一个数(≤6),求其立方值,结果保存至RESULT单元。分析:因为立方表已顺序排放在内存中。又已知首地址TABLE,只要把输入数据作为偏移量与首址相加,就指向了表中立方值的地址,取其内容即为该数的立方值。(程序参见课本).5.3分支程序设计5.3.1基本分支程序设计在分支结构中,如果条件成立则完成某项操作,否则执行其它操作或后续指令,其结构如图5-2所示,该图同时也给出了分支程序的二种典型结构。图5-2分支结构下面通过一些实例介绍分支程序设计。例5.4十六进制数显示:在存储单元DAT中存放着一位十六进制数,将其显示到屏幕上。分析:十六进制数据不能作为一个整体进行显示,要首先将其分解为各个字符的ASCII码,然后再调用DOS系统功能调用的2号功能完成各个字符显示。(程序参见课本).5.3.2跳转表法实现分支在分支程序设计中,实现分支的方法是值得考虑的问题,如果仅用比较转移指令实现分支,当分支较多时,比较转移的实现非常麻烦,使得进入各个分支的时间不相等,进入最后一个分支的等待时间最长。用跳转表法实现分支可以解决这类问题,具体做法是:在内存中建立一张表,表中顺序存放着各个分支处理程序的首地址,这样的表称为跳转表,要进入某个分支,只要查找跳转表即可。5.4循环程序设计5.4.1循环程序的结构循环程序是多种多样的,但一般认为它由四部分构成:①初始化部分、②循环工作部分、③参数调整部分、④循环控制部分循环程序根据循环控制条件的不同,通常又分两种结构,如图所示。图5-3循环程序结构5.4.2循环程序设计例5.6求1~200的所有奇数的和,结果保存到RESULT存储单元。(程序见课本).例5.7在以BUFFER为首地址的缓冲区中有一批无符号字数据,个数为10,编程找出其最大的值,结果存入RESULT单元。分析:求最大值的算法参见§3.2.6中的例3.19。(程序参见课本).5.5专题应用程序设计5.5.1子程序设计子程序在宏汇编语言中称为过程。1.子程序的调用与返回指令(1).子程序的调用指令CALL格式:CALL子程序名功能:调用指定的子程序.执行操作:先把本指令的下一条指令的地址(即返回点)压入堆栈保存,再转向目标地址处执行子程序。根据是段内调用还是段间调用(这一点依据子程序名来得知,子程序在定义时指定了类型NEAR或FAR),具体的执行过程又有所不同,如下:①段内调用先将指令指针IP入栈保护,然后改变IP使之指向子程序的起始位置。②段间调用先将当前CS入栈,再将当前IP入栈,然后改变CS和IP使之指向子程序的起始位置。(2).子程序的返回指令RET格式:RET功能:子程序执行完后,通过本指令返回主程序。执行操作:对段内调用,栈顶一个字→IP(同时SP+2→SP),从而返回到主程序中CALL指令的下条指令处。对段间调用,栈顶一个字→IP(同时SP+2→SP),再将新栈顶一个字→CS(同时SP+2→SP),从而返回到主程序中CALL指令的下条指令处。2.子程序定义伪指令在MASM中专门提供了过程定义伪指令来定义子程序,如下:格式:过程名PROC类型…RET过程名ENDP3.子程序设计例5.8编写一子程序,能在屏幕上显示字符A,并调用之完成二次显示。(程序见课本).4.主程序的现场保护问题主程序在调用子程序之前,如果某些寄存器中保存有重要数据,并且这些数据后面还要用到,那么在子程序中对这些寄存器的内容就不能修改,但由于寄存器个数有限,子程序如果也必须使用这些寄存器,就必须在修改前对这些寄存器内的数据予以保护,在返回主程序前再恢复这些寄存器的内容。子程序保护主程序寄存器的最简单办法是将其压入堆栈,恢复时再从堆栈中将数据弹回原寄存器,要注意的是由于堆栈先进后出的特性,必须按入栈顺序相反的顺序恢复寄存器。(示例程序参见课本).5.主、子程序间参数的传递方法有时主程序需要向子程序传递参数,方法有三种。(1).通过寄存器传递参数主程序在调用子程序前将参数放到某一约定的寄存器内,子程序从中取出即可。(示例程序参见课本).(2).通过内存单元传递参数主程序在调用子程序前将参数放到某一约定的内存单元中,子程序从中取出即可。(示例程序参见课本).(3).利用堆栈传递参数利用堆栈也可以实现参数传递,高级语言中函数间的参数传递就是用此方法进行的。利用堆栈传递参数的方法为主程序将参数依次入栈,然后调用子程序,子程序从堆栈中读取参数即可。例5.12改写5.10,使之通过堆栈传递参数。(见课本)6.子程序向主程序返回值的方法子程序也可以向主程序有返回值,返回值一般使用寄存器或存储单元传递,方法是子程序将返回值写入事先约定的寄存器或存储单元中,主程序从中读取,就同主程序向子程序传递入口参数一样。返回值一般不通过堆栈传递,因为子程序返回主程序前必须保证栈顶是主程序的返回的地址,否则RET指令不能正常工作。(示例程序参见课本)5.5.2代码转换原理及程序设计计算机的数据处理常常包含各种不同的代码,如二、十、十六进制代码,ASCII码,BCD码及七段显示码等。有时计算机需要显示某一种代码,但此时该数据却以另外一种代码形式存在,这时往往需要进行代码转换,将当前代码转换为我们需要的形式。1.代码转换原理(1).十六进制数据与ASCII码的转换①十六进制数据转换为ASCII码:由于一位十六进制数在内存中是4位二进制数,这种转换就是将4位二进制数转换成一个十六进制字符的ASCII码。若4位二进制数的值在0~9之间,则转换成字符“0”~“9”;若4位二进制数的值在10~15之间,则转换成字符“A”~“F”。具体程序可参见例5.4。②ASCII码转换为十六进制数:这是上面转换的逆过程,即将一个十六进制字符的ASCII码转换成4位二进制数。若字符为“0”~“9”的ASCII码,则减去30H得“0000~1001”;若字符为“A”~“F”的ASCII码,则减去37H得“1010~1111”;AL为“a”~“f”的ASCII码,则减去57H得1010~1111。(2).BCD数与其它数的转换①BCD数转换为ASCII码对于一个组合型BCD数,高4位清0,再加30H,可得个位字符的ASCII码;原数逻辑右移4位,再加30H,可得十位数字ASCII码。这种转换可参见例3.13。②ASCII码转换为BCD数对于二个十进制数字的ASCII码,个位数字高4位清0,十位数字左移4位,然后将二者相加,可得到其组合型BCD码。这种转换可参见例3.14。③BCD数与二进制数的转换在程序设计中,经常遇到这样的问题:输入数据为BCD数,并且要进行算术运算,结果以BCD形式输出。此时可用BCD直接运算的方法,但也可以首先将BCD数转换为二进制数并以二进制数据运算,结果再转换为BCD数输出。尤其是多字节BCD数的运算,更适于使用这种方法。(3).二进制数据与十进制数据的相互转换十进制数据向二进制数据的转换程序运行时需要读取用户的键盘输入,这种读取是通过DOS的10功能调用完成的,所读取的是ASCII码串。我们需要通过一定的算法将这串字符转换为该十进制数的补码,就是十进制数据向二进制数据的转换。②二进制数据向十进制数据的转换当用户输入的所有十进制数据都转换为对应的二进制数据后,就可以对其运算.运算完后,结果可能需要显示在屏幕上,这时可用DOS的9号功能调用完成,但9号功能调用要求显示的内容必须为字符串,为此必须把二进制数转换为十进制数。转换算法:(设转换后最高只产生万位数字)⊙检查二进制数补码的符号位,为0,符号位字符为“+”;为1,符号位字符为“-”,并将原数求补,转换为对应的正数。⊙将上步所得二进制数除以10000,所得商为万位数,再将余数除以1000,所得商为千位数,以此类推求出百,十位数,个位数。同样,为便于用循环程序实现,上述过程常变形为:二进制数除以10,所得余数为个位数,再将商除以10,所得余数为十位数,再将商除以10,所得余数为百位数,以此类推。如12345除以10余数为5(个位数字),商1234再除以10,余数为4(十位数字),商123再除以10,余数为3(百位数字),…。⊙将上步所得的所有数字各加30H,转换为ASCII码,并按符号位、万位、千位、百位、十位、个位的顺序依次显示在屏幕上。2.二-十进制数据转换程序在上面的几种代码转换算法中,只有二-十进制数据的相互转换比较复杂,而且这种转换经常需要进行,下面再通过一个实例来进一步说明这二种转换操作。例5.14编程实现从键盘读取二个十进制数据,然后求出其和,显示在屏幕上。说明:程序中假设用户输入的十进制数据不超过5位,这样运算结果可以限制在16位补码表示范围之内。程序运行后,首先提示用户输入一个数,读取后将其转换为二进制数并入栈保存。然后再读取用户输入的下一个数,并将其转换为二进制数放在AX中.最后,将这二个二进制数加起来,将和再转换为十进制的数的ASCII码串并显示在屏幕上。程序流程如图5-6所示(图及程序代码参课本)。5.5.3BCD码运算及其程序设计1.BCD码的调整原理压缩型BCD码用一个字节表示二位十进制数,其运算规律与普通二进制运算不同,图5-7以实例形明了压缩型BCD加法操作的调整原量。图5-7BCD码的调整原理2.BCD码调整指令(1).压缩的BCD码调整指令①加法的十进制调整指令格式:DAA功能:AL←把AL中的和调整到压缩的BCD码格式.若调整时高4位向前有进位,则置CF=1。注意:这条指令之前必须执行ADD或ADC指令,加法指令必须把两个压缩的BCD码相加,并把结果存放在AL寄存器中。②减法的十进制调整指令格式:DAS功能:AL←把AL中的差调整到压缩的BCD格式。若调整时高4位向前有借位,则置CF