第5章 程序控制结构及其程序设计.ppt

上传人:文库蛋蛋多 文档编号:2340669 上传时间:2023-02-13 格式:PPT 页数:173 大小:779KB
返回 下载 相关 举报
第5章 程序控制结构及其程序设计.ppt_第1页
第1页 / 共173页
第5章 程序控制结构及其程序设计.ppt_第2页
第2页 / 共173页
第5章 程序控制结构及其程序设计.ppt_第3页
第3页 / 共173页
第5章 程序控制结构及其程序设计.ppt_第4页
第4页 / 共173页
第5章 程序控制结构及其程序设计.ppt_第5页
第5页 / 共173页
点击查看更多>>
资源描述

《第5章 程序控制结构及其程序设计.ppt》由会员分享,可在线阅读,更多相关《第5章 程序控制结构及其程序设计.ppt(173页珍藏版)》请在三一办公上搜索。

1、第5章 程序控制结构及其程序设计,5.1 汇编语言程序设计概述 5.2 顺序程序设计 5.3 分支程序设计 5.4 循环程序设计 5.5 在实模式下发挥80386及其后继机型的优势 习题5,5.1 汇编语言程序设计概述,5.1.1 汇编语言程序设计的基本步骤 编制汇编语言程序的基本步骤如下:(1)分析问题,抽象出描述问题的数学模型。遇到一个题目,特别是一个较为复杂的题目,先要对其进行全面的分析,看它给出了什么条件,有什么特点,找出规律,归纳出数学模型。当然,也可能有些问题不用写出数学模型或写不出数学模型。,(2)确定算法。有了数学模型,或虽然没有数学模型但已把题目分析清楚了,就选择一个合适的算

2、法和适当的数据结构。如果没有可供选用的现成的算法和结构,就需要针对具体问题设计一个算法或结构。(3)绘制流程图。流程图就是用图形的方式把解决问题的算法直观地描述出来。对于一个比较复杂的问题,画出流程图,这有助于对问题的理解以及有助于编写出正确的程序。当然,如果算法比较简单,也可不画流程图。,(4)分配存储空间和工作单元。用汇编语言编写程序时,需要给程序中的变量指定内存单元地址或指定寄存器。(5)编写程序。要把题目中需要处理的数据合理地根据(2)、(3)、(4)步的工作,选用适合的指令,并按一定的语法规则编写相应的程序。(6)静态检查。静态检查就是用人工的方式检查程序是否有错误,包括算法错误和语

3、法错误等,如果有错误,及时改正过来。处理得好,静态检查能够发现和改正程序中的大部分错误。,(7)上机调试运行。任何程序必须经过调试,才能检查出解题目的是否正确以及程序是否符合设计思想。在调试程序的过程中,应该善于利用机器提供的调试工具(如DEBUG)和有效的其他工具软件来进行工作,经过反复的“运行发现错误改正错误运行”,才能得到正确的程序。这一点对初学者特别重要,它将给汇编语言编程提供很大的帮助。程序的编写和调试运行是学好汇编语言的重要手段。只有多编写程序和多调试运行程序,才能有效地提高编写和阅读程序的能力。,5.1.2 程序流程图 表示一个算法,可以用不同的方法。常用的有自然语言、传统流程图

4、、结构化流程图、伪代码和PAD图等。1.用自然语言表示算法 很多算法是用自然语言表示的,自然语言是指人们日常使用的语言。用自然语言表示算法通俗易懂,但文字冗长,容易出现“歧义性”。自然语言表示的含义往往不大严格,要根据上下文才能判断其正确的含义。假如有这样一句话:“王先生对刘先生说孩子考上了大学。”是王先生的孩子考上大学呢,还是刘先生的孩子考上大学呢?光从这句话本身难以判断。此外,用自然语言描述包含分支和循环的算法很不方便。因此,除了很简单的问题以外,一般程序设计不用自然语言描述算法。,2.流程图的组成 流程图是用一些图框表示各种操作。用图形表示算法,直观形象,易于理解。美国国家标准化协会AN

5、SI(American National Standard Institute)规定了一些常用的流程图图元(见图5.1),已被世界各国程序工作者普遍采用。借助于流程图可以清晰地把程序思路表达出来,有助于编写正确的程序。流程图对于程序设计人员,特别是初学者是一种非常有用的工具。流程图一般由6种成分组成,如图5.1所示。,图5.1 流程图的组成成分图,1)执行框(矩形框)图5.1中的方框,其作用是表示一段程序或一个模块的功能,对于结构化程序,一个执行框只有一个入口和一个出口。2)判别框(菱形框)图5.1中的菱形框,其作用是对一个给定的条件进行判断,根据给定的条件是否成立来决定如何执行其后的操作。它

6、有一个入口,两个出口,如图5.2所示。,图5.2 流程图的绘制示意,3)开始框和终止框 图5.1中的圆头方框表示程序的起始和终止。4)指向线 指向线表示程序执行的顺序。5)连接点 图5.1中小圆圈是连接点,用于将画在不同地方的流程线连接起来。如图5.2中有两个以为标志的连接点,它表示这两个点是互相连接在一起的。实际上它们是同一个点,只是当在纸张上画不下时才分开来画。用连接点,可以避免流程线的交叉或过长,使流程图清晰。,流程图是表示算法的较好工具。一个流程图包括以下几部分:(1)表示相应操作的框;(2)带箭头的流程线;(3)框内外必要的文字说明。绘制流程线不要忘记画箭头,因为它是反映流程执行的先

7、后次序的,如不画出箭头就难以判定各框的执行次序了。,用流程图表示算法直观、形象,能比较清楚地显示出各个框之间的逻辑关系。前一时期国内外计算机书刊都广泛使用这种流程图表示算法,但是,这种流程图占用篇幅较多,尤其当算法比较复杂时,画流程图既费时又不方便,在结构化程序设计方法推广之后,许多书刊已用N-S结构化流程图代替这种传统的流程图。但是每一个程序编制人员都应当熟练掌握传统流程图,做到会看会画。,3.三种基本结构和改进的流程图 1)传统流程图的弊端 传统的流程图用流程线指出各框的执行顺序,对流程线的使用没有严格限制。因此,使用者可以不受限制地使流程随意地转来转去,使流程图变得毫无规律。阅读者要花很

8、大精力去追踪流程,使人难以理解算法的逻辑。这种情况如图5.3所示。这种如同乱麻一样的算法称为BS型算法,意为就像一碗面条(A Bowl of Spaghetti),乱无头绪。,图5.3 杂乱流程示意,这种算法不好,难以阅读,也难以修改,可靠性和可维护性难以保证。如果我们写出的算法能限制流程的无规律任意转向,如同一本书那样,由各章各节顺序组成,那样,阅读起来就很方便,从头到尾顺序地看下去即可。而如果一本书不是由各章节顺序组成,各章节内各节毫无规律地乱排,阅读这种书就很困难。,为了提高算法的质量,使算法的设计和阅读更方便,必须限制滥用箭头,即不允许无规律地使流程随意转向,只能顺序地进行下去。但是,

9、算法上难免会包含一些分支和循环,而不可能全部由一个一个框顺序组成。为了解决这个问题,人们设想,规定出几种基本结构,然后由这些基本结构按一定规律组成一个算法结构(如同用一些基本预制构件来搭成房屋一样),整个算法的结构是由上而下地将各个基本结构顺序排列起来的。如果能做到这一点,算法的质量就能得到保证和提高。,2)三种基本结构 1966年,BOHRA和JACOPINI提出了以下三种基本结构,用这三种基本结构作为表示一个良好算法的基本单元。(1)顺序结构。如图5.4所示,虚线框内是一个顺序结构。其中A和B两个框是顺序执行的。即在执行完A框所指定的操作后,必然接着执行B框所指定的操作。顺序结构是最简单的

10、一种基本结构。,图5.4 顺序结构图,(2)选择结构(也称选取结构或分支结构)。如图5.5所示,虚线框内是一个选择结构。此结构中必包含一个判断框。根据给定的条件P是否成立而选择执行A框或B框。请注意,无论P条件是否成立,只能执行A框或B框之一,不可能既执行A框又执行B框。无论走哪一条路径,在执行完A或B之后,都经过b点,然后脱离本选择结构。A或B两个框中可以有一个是空的,即不执行任何操作。(3)循环结构(又称重复结构)。循环结构指反复执行某一部分的操作,有当型(WHILE型)循环、直到型(UNTIL型)循环和计数型(FOR-NEXT)循环。,图5.5 选择结构图,4.结构化程序设计的特点 三种

11、基本循环结构的共同特点如下:(1)只有一个入口。(2)只有一个出口。尽管一个菱形判断框有两个出口,但由它构成的一个选择结构仍只有一个出口。不要将菱形框的出口和选择结构的出口混淆。(3)各功能框均可执行。结构内的每一部分都有机会被执行到,也就是说,对每一个框而言,都应当有一条从入口到出口的路径通过它。,(4)结构中无死循环。实践证明,由以上三种基本结构顺序组成的算法结构,可以解决任何复杂的问题。由基本结构所构成的算法属于“结构化”的算法,它不存在无规律的转向,只在本结构内才允许存在分支、向前或向后的跳转。基本结构不一定只限于上面三种,只要具有上述四个特点的都可以作为基本结构。人们可以自己定义基本

12、结构,并由这些基本结构组成结构化程序。,5.2 顺序程序设计,在顺序程序结构中,完全按顺序逐条执行指令序列,这种情况在程序中大量存在,但仅由顺序结构构成的作为完整的程序则很少见。顺序结构的程序是最简单的程序。,【例5-1】将两个字节数据相加,并存放到一个结果单元中。DATASEGMENT AD1DB4CH;定义第1个加数 AD2DB25H;定义第2个加数 SUMDB?;定义结果单元 DATAENDS CODESEGMENT,ASSUME CS:CODE,DS:DATASTART:MOV AX,DATA MOV DS,AX MOV AL,AD1;取出第1个加数 ADD AL,AD2;和第2个加数

13、相加 MOV SUM,AL;存放结果 MOV AH,4CH INT 21H;返回DOS CODE ENDS ENDSTART,【例5-2】两个32位数的乘法程序。.386DATA SEGMENTNUM1DD12345678H;定义第1个乘数NUM2DD5A4BEF06H;定义第2个乘数RESULTDD2 DUP(?);定义结果单元DATAENDSCODESEGMENT,ASSUME CS:CODE,DS:DATA START:MOVAX,DATA MOVDS,AX MOVEAX,NUM1;取出第1个乘数 MULNUM2;和第2个乘数相乘,MOVRESULT,EAX;存放结果的低4字节部分MOV

14、RESULT+4,EDX;存放结果的高4字节部分MOVAH,4CH;INT21H;返回DOSCODEENDS ENDSTART,【例5-3】将一个字节压缩BCD码转换为两个ASCII码。分析:一个字节的压缩BCD码就是用一个字节的二进制数表示两位十进制数,如十进制数96表示成压缩BCD码就是96H,转换成ASCII码就是把压缩BCD码表示的十进制数的高位和低位分开,并以ASCII码表示,即转换成39H和36H。,DATASEGMENTBCDBUFDB96H;定义1个字节的压缩BCD码ASCBUF DB2DUP(?);定义2个字节的结果单元DATA ENDSCODE SEGMENT ASSUME

15、 CS:CODE,DS:DATA,START:MOVAX,DATA MOVDS,AX MOV AL,BCDBUF;取出BCD码 MOVBL,AL;送BL暂存 MOVCL,4 SHRAL,CL;高4位变成低4位,高4位补0(96H09H)ADDAL,30H;变成ASCII码(39H),MOVASCBUF,AL;存储第1个ASCII码ANDBL,0FH;屏蔽掉高4位,只保留低4位(96H06H)ADDBL,30H;变成BCD码(36H)MOVASCBUF+1,BL;存储第2个码MOVAH,4CHINT21HCODE ENDSENDSTART,【例5-4】利用直接查表法完成将键盘输入的一位10进制数

16、(09)转换成对应的平方值并存放在SQRBUF单元中。(用P单步调试)分析:09的平方值分别为0、1、4、9、16、25、36、49、64、81。把平方值放在一起形成一个平方值表,根据输入的值和对应平方值所在单元地址之间的关系(表首地址加上输入的值),查出相应的平方值。,DATASEGMENTSQUTABDB0,1,4,9,16,25,36,49,64,81SQUBUFDB?DATAENDSCODESEGMENTASSUMECS:CODE,DS:DATASTART:MOVAX,DATA,MOVDS,AXMOVBX,OFFSETSQUTAB;平方表首地址MOVAH,1INT21H;由键盘输入个数

17、,得到其ASCII码SUBAL,30H;由ASCII码得到相应的数XLAT;查表MOVSQUBUF,AL;存储结果,MOVAH,4CHINT21HCODEENDS END START,5.3 分支程序设计,5.3.1 分支程序的结构形式 分支程序结构可以有两种形式,如图5.6所示。不论哪一种形式,它们的共同特点是:运行方向是向前的,在某一种特定条件下,只能执行多个分支中的一个分支。,图5.6 分支程序的结构形式,SKIP,5.3.2 分支程序设计方法 程序的分支一般用条件转移指令来产生,利用条件转移指令不影响条件码的特性,可以连续使用条件转移指令使程序产生多个不同的分支如下例。【例5-5】在附

18、加段中,有一个按从小到大顺序排列的无符号数数组,其首地址存放在DI寄存器中,数组中的第一个单元存放着数组长度,在AX中有一个无符号数,要求在数组中查找(AX)。如找到,则使CF=0,并在SI中给出该元素在数组中的偏移地址;如未找到,则使CF=1。,上述章节遇到的过多个表格查找的例子,大都使用顺序查找的方法。本例是一个已经排序的数组,可以采用折半查找法以提高查找效率。折半查找法先取有序数组的中间元素与查找值相比较。如相等则查找成功;如查找值大于中间元素,则再取高半部的中间元素与查找值相比较。如查找值小于中间元素,则再取低半部的中间元素与查找值相比较。如此重复直到查找成功或最终未找到该数为止。,折

19、半查找法的效率高于顺序查找法,对于长度为N的表格,顺序查找法平均要作N2次比较,而折半查找法的平均比较次数为lb N。所以,如果数组长度为100,则顺序查找法平均要作50次比较,而折半查找法平均作7次比较就可以了。在一个长度为N的有序数组R中,查找元素K的折半查找算法可描述如下:,(1)初始化被查找数组的首尾下标,LOWL,HIGHN。(2)若LOWHIGH,则查找失败,置CF1,退出程序。否则,计算中点:MID(LOW+HIGH)2。(3)K与中点元素RMID比较。若K=RMID则查找成功,程序结束;若KRMID,则转步骤(5)。(4)低半部分查找(Lower),HIGHMID-1,返回步骤

20、(2),继续查找。,(5)高半部分查找(Higher),LOWMID+1,返回步骤(2),继续查找。图5.7表示了折半查找算法的程序框图。给出的程序首先把查找值与数组的第一个元素和最后一个元素相比较,如果找到该数小于第一个元素或大于最后一个元素则结束查找,否则从SEARCH开始折半查找。SEARCH以前的工作在图5.7中未表示出来。折半查找算法的程序实现如程序清单所示。,图5.7 折半查找算法的程序框图,例5-5折半查找算法程序。DSEGSEGMENTLOW_IDW?HIGH_IDW?DSEGENDSCSEGSEGMENTASSUME CS:CSEG,DS:DSEG,ES:DSEG,B_SRC

21、HPROCNEARPUSHDS PUSHAXMOVAX,DSEGMOVDS,AXMOVES,AXPOPAXCMPAX,ES:DI+2JACHK_LAST,LEASI,ES:DI+2 JEEXIT STC JMPEXIT CHK_LAST:MOVSI,ES:DI SHLSI,1 ADDSI,DI CMPAX,ES:SI JBSEARCH JEEXIT,STC JMPEXITSEARCH:MOVLOW_I,1 MOVBX,ES:DI MOVHIGH_I,BX MOVBX,DI MID:MOVCX,LOW_I MOVDX,HIGH_I CMPCX,DX JANO_MATCH,ADDCX,DX SHR

22、CX,1 MOVSI,CX SHLSI,1COMPARE:CMPAX,ES:BX+SI JEEXIT JAHIGHER DECCX MOVHIGH_I,CX JMPMID,HIGHER:INCCX MOV LOW_I,CX JMPMIDNO_MATCH:STCEXIT:POPDS RET B_SRCH ENDPCSEG ENDS ENDB SRCH,若数组元素如下:LIST DW 12,11,22,33,44,55,66,77,88,99,111,222,333 如果要查找的数为(AX)=55。数组长度为12,第一次比较的是数组的第六个元素。因5533,所以第三次用高半部折半查找,比较的是第四

23、个元素44。因5544,所以第四次用高半部折半查找,比较的是第五个元素55。这样经过四次比较后,因查找成功而退出程序。,如果要查找的数为(AX)=57,则第一次比较的仍是第六个元素66。因5733,所以第三次用高半部折半查找,比较的是第四个元素44。因5744,所以第四次用高半部折半查找,比较的是第五个元素55,因5755,再用高半部折半查找时,因LOWHIGH而以查找失败退出程序。,可以看出,在这个例子中,同样用CMP指令以及条件转移指令产生两个或多个程序分支。当然,由于多数运算型指令置条件码,所以在条件转移指令之前并不一定要使用CMP或TEST指令,只要保证使用条件转移指令时的条件码符合要

24、求就可以了。以上多个例子都是既有分支结构又包括循环结构,实际上,多数程序都是各种程序结构的组合。而且,循环结构可以看作分支结构的一种特例,它只是多次走一个分支,只在满足循环结束条件时,走另一个分支罢了。,5.3.3 跳跃表法 分支程序的两种结构形式都可以用上面所述的方法来实现。此外,在实现CASE结构时,还可以使用跳跃表法,使程序能根据不同的条件转移到多个程序分支中去。下面举例说明。【例5-6】试根据AL寄存器中哪一位为1(从低位到高位)就把程序转移到8个不同的程序分支中去。,下面列出了用变址寻址方式实现跳跃表法的程序,还可以使用寄存器间接和基址变址寻址方式来达到同一目的,这三种方法并无实质的

25、区别,只是其中关键的JMP指令所用的寻址方式不同而已。跳跃表法是一种很有用的分支程序设计方法,应当通过例子掌握要领,灵活使用。用变址寻址方式实现跳跃表法的程序:,DATASEGMENTDATATABDW ROUTINE_1DW ROUTINE_2DW ROUTINE_3DW ROUTINE_4DW ROUTINE_5DW ROUTINE_6DW ROUTINE_7DW ROUTINE_8,DATA ENDS CODE SEGMENT MAIN PROCFARASSUME CS:CODE,DS:DATASTART:PUSHDS SUBBX,BX PUSH BX MOVBX,DATA MOVDS,

26、BX CMPAL,0,JECONT MOVSI,0LP:SHRAL,1 JNBNOT_YET JMPDATATABSI NOT_YET:ADDSI,TYPE DATATAB JMPLPCONT:ROUTINE_1:ROUTINE_2:,RETMAINENDPCODEENDSENDSTART,【例5-7】用间接寻址方式实现跳跃表法的程序:CMPAL,0 JECONTINUE_MAIN_LINE LEABX,DATATABLP:SHRAL,1 JNBNOT_YET JMPWORD PTRBX NOT_YET:ADDBX,TYPE DATATAB JMPLPCONTINUE_MAIN_LINE:,5

27、.4 循环程序设计,5.4.1 循环程序结构 循环程序结构可以总结为两种结构形式,如图5.8所示。一种是DO_WHILE结构形式,另一种是DO_UNTIL结构形式。,图5.8 循环程序的结构形式,1.DO_WHILE结构 DO_WHILE结构把对循环控制条件的判断放在循环的入口,先判断条件,满足条件就执行循环体,否则就退出循环。2.DO_UNTIL结构 DO_UNTIL结构则先执行循环体,然后再判断控制条件,不满足条件则继续执行循环操作,一旦满足条件则退出循环。,这两种结构可以根据具体情况选择使用。如果允许循环次数等于0的情况,应选择DO_WHILE结构,否则使用DO_UNTIL结构。不论哪一

28、种结构形式,循环程序都可由如下三部分组成:(1)循环的初始状态。设置循环次数的计数值,为使循环体正常工作而建立的初始状态等。(2)循环体。这是循环工作的主体,由循环的工作部分和修改部分组成。循环的工作部分是为完成程序功能而设计的主要程序段;循环的修改部分则是为保证每一次重复(循环)时,参加执行的信息能发生有规律的变化而建立的程序段。,(3)循环控制部分。循环控制本来应该属于循环体的一部分,由于它是循环程序设计的关键,所以要对它作专门的讨论。每个循环程序必须选择一个循环控制条件来控制循环的运行和结束,而合理地选择该控制条件就成为循环程序设计的关键问题。,有时,循环次数是已知的,此时可以用循环次数

29、作为循环的控制条件,LOOP指令使这种循环程序设计能很容易地实现。有时循环次数是已知的,但有可能使用其他特征或条件来使循环提前结束,LOOPZ和LOOPNZ指令又是设计这种循环程序的工具。然而,有时循环次数是未知的,那就需要根据具体情况找出控制循环结束的条件。循环控制条件的选择是很灵活的,有时可供选择的方案不止一种,此时就应分析比较,选择一种效率最高的方案来实现。,5.4.2 循环程序设计方法【例5-8】试编制一个程序,把BX寄存器内的二进制数用十六进制数的形式在屏幕上显示出来。根据题意应该把BX的内容从左到右每四位为一组在屏幕上显示出来,每次循环显示一个十六进制数位,计数初值为4。,图5.9

30、 二进制数到十六进制数转换的程序框图,程序框图如图5.9所示。采用循环移位的方式把所要显示的4位二进制数移到最右面,以便做数字到字符的转换工作。此外,由于数字09的ASCII值为30H39H,而字母AF的ASCII值为41H46H,所以在把4位二进制数加上30H后还需再作一次判断。如果是字符AF,则还应加上7才能显示出正确的十六进制数。以BINIHEX.ASM为文件名建立“二进制到十六进制数转换程序”源文件。,在程序中没有使用LOOP指令,这是因为循环移位指令要使用CL寄存器,而LOOP指令要使用CX寄存器,为了解决CX寄存器的冲突问题,这里用CH寄存器存放循环计数值,而用DEC及JNZ两条指

31、令完成LOOP指令的功能。这说明使用计数值控制循环结束也可以不用LOOP指令。当然也可以把计数值初始化为0,用每循环一次加1然后比较次数是否达到要求的方法来实现;或者仍用LOOP指令,而用堆栈保存其中的一个信息(例如计数值)来解决CX寄存器的冲突问题等。总之,程序设计是很灵活的,只要算法和指令的使用没有错误,都可以达到目的。二进制数到十六进制数转换程序:,CODE SEGMENT MAINPROC FAR ASSUME CS:CODESTART:PUSH DS SUB AX,AX PUSH AX MOV CH,4LP:MOV CL,4 ROL BX,CL MOV AL,BL,AND AL,0F

32、H ADD AL,30H CMP AL,3AH JL PRINTA ADD AL,07HPRINTA:MOV DL,AL MOV AH,2 INT 21H DEC CH,JNZ LP RET MAIN ENDPCODE ENDS ENDSTART,【例5-9】在ADDR单元中存放着数Y的地址,试编制一程序把Y中1的个数存入COUNT单元中。要测出Y中1的个数就应逐位测试。一个比较简单的办法是可以根据最高有效位是否为1来计数,然后用移位的方法把各位数逐次移到最高位去。,循环的结束可以用计数值为16来控制,但更好的办法是:结合上述方法可以用测试数是否为0来作为结束条件,这样可以在很多情况下缩短程序

33、的执行时间。此外,考虑到Y本身为0的可能性,应该采用DO_WHILE的结构形式。根据以上考虑,可以画出图5.10的程序框图并编制“数1的程序”。,图5.10 数1的程序框图,TITLE数1的程序DATASEGMENT ADDRDW NUMBER NUMBERDW Y COUNTDW?DATAENDSCODESEGMENTMAINPROC FAR,ASSUME CS:CODE,DS:DATASTART:PUSHDS SUBAX,AX PUSHAX MOVAX,DATA MOVDS,AX MOVCX,0 MOVBX,ADDR MOVAX,BX,REPEAT:CMPAX,0 JZEXIT JNS S

34、HIFT INCCXSHIFT:SHLAX,1 JMPREPEATEXIT:MOVCOUNT,CX,RET MAIN ENDPCODE ENDS END START;运行时,Y应赋具体值,【例5-10】在附加段中,有一个首地址为LIST和未经排序的字数组。在数组的第一个字中,存放着该数组的长度,数组的首地址已存放在DI寄存器中,AX寄存器中存放着一个数。要求编制一程序:在数组中查找该数,如果找到此数,则把它从数组中删除。这一程序应该首先查找数组中是否有(AX),如果没有则不对数组作任何处理就结束程序。如果找到这一元素,则应把数组中位于其前(指地址比该元素高)的元素后移一个字(即向低地址方向移动

35、),并修改数组长度值。如果找到的元素正好位于数组末尾,则不必移动任何元素,只要修改数组长度值就可以了。,这里,第一部分的查找元素可以使用串处理指令,第二部分的删除元素则可以使用循环结构。由于查找结束时就可以知道元素的位置,因此可以作为循环次数已知的情况来设计。画出程序框图如图5.11所示。程序主体部分见“删除数组中一元素程序。,图5.11 删除数组中一元素的程序框图,TITLE 删除数组中一元素程序DEL_U1PROCNEARCLDPUSHDIMOVCX,ES:DIADDDI,2 REPNESCASWJEDELETEPOPDIJMPSHORTEXIT,DELETE:JCXZDEC_CNTNEX

36、T_EL:MOVBX,ES:DIMOVES:DI-2,BXADDDI,2LOOPNEXT_ELDEC_CNT:POP DIDECWORD PTR ES:DIEXIT:RETDEL_ULENDP,【例5-11】将正数N插入一个已排序的字数组的正确位置。该数组的首地址和末地址分别为ARRAY_HD和ARRAY_ED,其中所有数均为正数且已按递增的次序排列。由于数组的首地址和末地址都是已知的,因此数组的长度是可以确定的。但是,这里只要求插入一个数,并不一定要扫描整个数组,所以可以用找到应插入数的位置作为循环的结束条件。此外,为空出要插入数的位置,其前的全部元素都应前移一个字(即向地址增大的方向移动一

37、个字,,这里的前后是指程序运行的方向为前,反之则为后),所以算法上应该从数组的尾部向头部查找,可逐字取出数组中的一个数K与N作比较。如KN,则把K前移一个字,然后继续往后查找;如KN,则把N插在K之前就可以结束程序了。,在考虑算法时,必须把可能出现的边界情况考虑在内。如例5-10中对有可能出现要删除的元素正好在数组末尾的考虑。这个问题是初学者易于忽略的,应该引起注意。在例5-11中,应该考虑N大于或小于数组中所有数的两种可能性。如果N大于数组中所有数,则第一次比较就可以结束循环,也就是说循环次数有可能等于0,所以应该选用DO_WHILE结构形式;如果N小于数组中所有数,则必须使循环及时结束,也

38、就是说不允许查找的范围超过数组的首地址,,这当然可以把数组的首地址也同时作为结束条件来考虑,或者同时用循环次数作为结束条件之一来考虑。本例的更好办法是:可以利用所有数均为正数的条件,在ARRAY_HEAD 2单元中存放-1这个数,这样可以保证如果数N小于数组中的所有数,那它必然大于-1,这样就可以正确地把N放在数组之首了,循环的结束仍然可以使用KN这一条件。根据上述有关算法的考虑可画出程序框图如图5.12所示。编制了“数组中插入一元素程序”。,图5.12 数组中插入一元素的程序框图,TITLE 数组中插入一元素程序DATASEGMENTXDW?ARR_HDDW3,5,15,23,37,49DW

39、52,65,78,99ARR_EDDW105NDW32DATAENDSCODESEGMENTMAINPROCFARASSUMECS:CODE,DS:DATA,START:PUSHDSSUBAX,AXPUSHAX MOVAX,DATA MOVDS,AX MOVAX,N MOVARR_HD_2,0FFFFH MOVSI,0,COMP:CMPARR_EDSI,AX JLEINSERTMOVBX,ARR_EDSIMOVARR_EDSI+2,BXSUBSI,2JMPSHORTCOMPINSERT:MOVARR_EDSI+2,AX RETMAINENDPCODEENDSENDSTART,【例5-12】设有

40、数组X和Y,X数组中有X1,X10;Y数组中有Y1,Y10。试编制程序计算 Z1X1+Y1 Z5X5-Y5 Z8X8-Y8 Z2X2+Y2 Z6X6+Y6 Z9X9+Y9 Z3X3-Y3 Z7X7-Y7 Z10X10+Y10 Z4X4-Y4 结果存入Z数组。,对于这种问题,也可用循环程序结构来完成。已知循环计数值为10,每次循环的操作数是可以顺序取出的,但所作的操作却有所不同,这里有加法和减法两种操作。为了区别每次应该做哪一种操作,可以设立标志位,如标志位为0做加法,为1则做减法。这样,进入循环后只要判别标志位就可确定应该做的操作了。显然,这里要做10次操作就应该设立10个标志位,我们把它放在

41、一个存储单元LOGIC_RULE中,这种存储单元一般称为逻辑尺。本例设定的逻辑尺为 0000000011011100,从低位开始所设的标志位反映了每次要做的操作顺序,最高的6位没有意义。程序框图如图5.13所示。,图5.13 例5-12的程序框图,TITLE 例5-12程序DATASEGMENTXDWX1,X2,X3,X4,X5,X6,X7,X8,X9,X10YDWY1,Y2,Y3,Y4,Y5,Y6,Y7,Y8,Y9,Y10ZDWZ1,Z2,Z3,Z4,Z5,Z6,Z7,Z8,Z9,Z10LOG_RU DW00DCHDATAENDS CODESEGMENTMAINPROCFAR,ASSUME

42、CS:CODE,DS:DATA START:PUSHDS SUBAX,AX PUSH AX MOVAX,DATA MOVDS,AX MOVBX,0 MOVCX,10 MOVDX,LOG_RU,NEXT:MOVAX,XBX SHRDX,1 JCSUBT ADDAX,YBXJMPSHORTRESULTSUBT:SUBAX,YBXRESULT:MOVZBX,AXADDBX,2LOOPNEXTRETMAINENDPCODEENDS END START,本例中X1X10,Y1Y10,Z1Z10均需采用实际数值才可运行 这种设置逻辑尺的方法是很常用的。例如,在矩阵运算中,为了跳过操作数为0的计算,经常采用

43、这种方法。又如,把一组数据存入存储器时,如果其中数值为0的元素很多,也可用这种方法设立一个每位表示一个下标的逻辑尺(这样的逻辑尺可能占有几个字,由数组的长度确定),0元素就可不占用存储单元了。,在例5-12中,每个标志只占一位,如果要表示的特征数更多,则每个标志可占用几位,而在处理方法上是完全相同的。设立标志位的方法除了如逻辑尺那样可静态地预置外,还可以在程序中动态地修改标志位的值,以达到控制的目的。下例将说明这种方法。,【例5-13】试编制一程序。从键盘输入一行字符,要求第一个键入的字符必须是空格符。如不是,则退出程序;如是,则开始接收键入的字符并顺序地存放在首地址为BUFFER的缓冲区中(

44、空格符不存入),直到接收到第二个空格符时退出程序。这一程序要求接收的字符从空格符开始又以空格符结束,因此程序中必须区分所接收的字符是否为第一个字符。为此,设立作为标志的存储单元FLAG。一开始将其置为0,接收第一个字符后可将其置1。整个程序的框图如图5.14所示。,图5.14 例5-13的程序框图,TITLE 例5-13程序DATA1SEGMENTBUFFERDB80 DUP(?)FLAG DB?DATA1ENDSCODESSEGMENTMAIN PROCFARASSUME CS:CODES,DS:DATA1,START:PUSHDS SUBAX,AXPUSH AXMOVAX,DATA1MOV

45、DS,AXLEABX,BUFFERMOVFLAG,0,NEXT:MOVAH,01INT21HTESTFLAG,01HJNZFOLLOWCMPAL,20HJNZEXITMOVFLAG,1 JMPNEXT,FOLLOW:CMPAL,20HJZEXITMOVBX,ALINCBXJMPNEXTEXIT:RET MAINENDP CODESENDSENDSTART,5.4.3 多重循环程序设计 循环可以有多重结构。多重循环程序设计的基本方法和单重循环程序设计的基本方法是一致的,应分别考虑各重循环的控制条件及其程序实现,相互之间不能混淆。此外,应该注意在每次通过外层循环再次进入内层循环时,初始条件必须重新

46、设置。下面举例加以说明。,【例5-14】有一个首地址为A的N字数组,编制程序使该数组中的数按照从大到小的次序排列。这里采用起泡排序算法,从第一个数开始依次对相邻两个数进行比较。如次序对,则不做任何操作;如次序不对,则使两个数交换位置。表5-1表示了这种算法的例子。从中可以看出,在做了第一遍的(N?1)次比较后,最小的数已经放到了最后,所以第二遍只需要考虑(N?1)个数,即只需要比较(N?2)次。第三遍则只需要做(N?3)次比较总共最多(N?1)遍比较就可以完成排序。图5.15表示了起泡排序算法的程序框图,并编制了起泡排序算法的程序。,表5-1 起泡排序算法举例,图5.15 起泡排序算法的程序框

47、图,DATAENDS PROGRAMSEGMENT MAINPROCFAR ASSUME CS:PROGRAM,DS:DATASTART:PUSHDSSUBAX,AXPUSHAXMOVAX,DATAMOVDS,AXMOVCX,NDECCX,LOOP1:MOVDI,CXMOVBX,0LOOP2:MOVAX,ABXCMPAX,ABX+2JGECOTINUEXCHGAX,ABX+2MOVABX,AX,COTINUE:ADDBX,2LOOPLOOP2MOVCX,DILOOPLOOP1RETMAINENDPPROGRAM ENDSENDSTART;执行时N值需更换为实际值,5.4.4 串操作程序【例5-

48、15】位串插入程序。程序要求把一个小于32位的位串插入存储器内的一个大位串中的任意位置中去。欲插入的位串存放在BITSG中,它是一个右对齐的位串,可称其为子串,其长度用BITSG_LENGTH为符号名的“=”伪指令来说明。大位串存放在STRING中,并为要插入的子串准备了一个符号名为SG_END的双字单元。这里用双字来定义位串的原因在于用双字处理位串可以获得更快的速度。这一程序主要由移动大位串位置和插入子串这两个子程序组成,主程序框图见图5.16(a),子程序框图见图5.16(b)和(c),程序则见下面“例5-15位串插入程序”。,图5.16 位串插入程序框图,主程序中除调用子程序外,主要是作

49、进入子程序条件的判断。一是对子串长度的限制:0子串长度32;二是测试子串插入的位偏移(即在大位串中子串插入的起始位偏移值)与大位串位置的关系。如果正好在大位串尾,则可直接把子串接在大位串之后;如果已超出大位串的范围,则退出程序不作处理;只有在大位串的范围之内时,才可调用子程序完成本程序所规定的任务。移动大位串位置子程序要完成的任务是为插入子串而移动大位串的位置。这一工作分两步完成:,第一步,先移动好插入位偏移所在双字之后的其余双字,这些双字应从尾部开始后移,其后移位数应等于插入串长度(参见图5.17)。这部分程序是在该子程序的LOOP指令之前。其中,要移动的双字数=大位串双字数-插入位偏移所在

50、双字地址。,图5.17 大位串和插入子串举例(位偏移为58,子串长度为15位),第二步,移动插入位偏移所在双字。这里要注意的是,插入位偏移之前的各位应保持原位置不变,而其后各位应后移插入串长度位。在这一子程序中,多处用到双字长移位指令SHLD或SHRD。应该注意该指令执行完后,目的寄存器得到所移入的新内容,而源寄存器则维持原状不变。,插入位串子程序的任务是按指定的插入位偏移值插入子串。也就是用指定的子串取代原有该位置的值。在程序中,先取出插入位偏移所在的双字和与其相邻的高阶双字,然后用一串移位指令来达到既移入新子串,又不影响子串外原来两个双字的内容的目的。主、子程序都在同一个源文件中,因而参数

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 建筑/施工/环境 > 项目建议


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号