第5章程序设计.docx
- 文档编号:20255055
- 上传时间:2023-04-25
- 格式:DOCX
- 页数:38
- 大小:81.20KB
第5章程序设计.docx
《第5章程序设计.docx》由会员分享,可在线阅读,更多相关《第5章程序设计.docx(38页珍藏版)》请在冰豆网上搜索。
第5章程序设计
5.汇编语言程序设计
编程步骤──分析问题、设计算法、画出框图、写出程序、上机调试。
编程要点──编程是上面五个步骤的总称,写出程序是以前面三步为基础。
程序就是指令的(有机、合理)组合。
指令是计算机会做的基本动作(能用指令表达的就是基本动作),程序就是指挥计算机连续完成一系列基本动作从而达到一个整体功能的指令的集合。
指令要点──充分理解计算机指令的功能是编程的第一个前提。
充分理解了每条指令的功能,才能在拿到一个问题时,能够正确地对解决问题的步骤进行分解(动作分解),步骤的细致程度以能用指令表示为度。
比如“10个数求平均值”这样一个问题,在高级语言中可以用一个赋值语句表达出来,但是用汇编语言(机器语言)表达,就不能有那么笼统的步骤,因为没有相应的命令来表达这样一个步骤。
用汇编语言(机器语言)表达“10个数求平均值”,必须说成:
设置10次循环──每次循环累加一个数──循环结束对累加结果除以10。
算法要点──算法是编程的第二个前提。
开设高级语言编程课程的主要目的是学习和建立算法概念。
掌握了算法基本内容和理解了前人总结的典型算法,无论拿到什么样的问题都能有解决大致思路,逐步完善就可以设计出完整算法。
注意:
算法包含两个层次的内容,首先是算法的设计,然后才是算法的实现。
算法设计体现了水平和能力,至于算法用什么语言实现是相对简单的事情。
总之,忽略算法的理解、归纳和积累,生硬理解(背诵)程序的结果只能是事倍功半。
高级语言教材的章节安排:
基本知识──算法概念──数据类型──基本命令──三种基本结构及其可以解决的基本问题(典型算法例题)──基本结构的综合运用(复杂问题)。
汇编语言教材的结构也是同样。
学习了指令系统,接下来就该学习利用汇编指令构造三种基本结构,然后通过一些典型问题传授一些基本的算法,最终达到能够综合运用基本算法解决复杂问题。
基本结构不清楚肯定掌握不好基本算法,基本算法不清楚何谈综合运用!
伪指令和程序结构不是算法的内容,那是上机操作的辅助内容。
5.1循环程序
5.1.1循环指令
LOOP标号;无条件循环,只受CX次数控制,循环指定次数结束。
LOOPZ/LOOPE标号;条件循环,次数和零标志双重控制,出现不等则中止,用于查找不相等的数据。
比如密码验证程序:
程序内部事先存有一个密码(字符串),用户注册时输入了一个密码(又一个字符串),用两个指针分别指向两个字符串的首字符,然后利用循环将两个字符串的对应字符进行比较,如果对应字符相同,则零标志ZF=1,继续比较。
如果出现对应字符不同,则零标志ZF=0,循环中途结束。
(任何位置的字符不一致就没有继续判断的必要了。
)
LOOPNZ/LOOPNE标号;条件循环,次数和零标志双重控制,出现相等则中止,用于查找相等的数据。
比如关键字搜索:
从100个数据中查找78。
设置100次的LOOPNZ循环,每次取一个数与78比较,不等于则零标志ZF=0,继续循环。
如果出现相等,则零标志ZF=1,循环中止。
(找到了就无需再循环了。
)
寻址特点:
源于条件跳转,所以只有段内直接短跳转,范围-128~+127
次数控制:
默认CX寄存器为计数器,每次先CX-1,然后判断CX和ZF状态。
应用特点:
无条件循环用于没有中途退出的循环操作;
条件循环用于需要中途退出的循环,找出不同点或相同点。
在循环简单或CX寄存器使用有冲突的时候,可以用条件转移语句自己构造循环。
5.1.2循环程序结构
与高级语言程序相同,循环程序包括3部分:
初始化指令、循环体、循环控制(条件判断)。
学习过程中,一定要注意清晰地区分一段循环程序的3个部分。
程序根据条件判断语句是在循环体之前还是在循环体之后,循环程序分为两个类型:
●当型──先判断条件,当条件成立才执行循环体,可以避免首次错误执行
●
直到型──先执行循环体,再判断条件。
汇编语言程序中较常用直到型循环。
5.1.3汇编指令的循环程序框架
所谓框架,就是不考虑循环体的内容(功能),单独理解空循环的指令结构(初始化和循环控制)。
这是很简单、很基础的内容。
很简单是指内容很固定,没有什么变化,容易掌握。
很基础是指任何循环都要有一个正确的框架,填写不同的循环体就可以实现不同的具体功能。
1.用LOOP指令构造的循环:
(直到型循环)
MOVCX,1000;LOOP指令默认CX为循环次数
NEXT:
(循环体)
LOOPNEXT;CX=CX-1,CX<>0则跳NEXT
8086CPU的指令系统有LOOP指令,所以构造循环结构很简单:
甭管循环体是什么,上述循环框架可以实现1000次的循环。
注意循环标号(NEXT)一定要在CX赋值的下面!
即CX赋值不能重复执行!
初始化在循环体外!
例1:
在2000H地址存放有1000个成绩,求和。
例2:
2000H存有500个人的数学成绩,3000H存500个人的物理成绩,对应两门成绩求和存于4000H。
例3:
对例2求出的存于4000H的500个和,两两比较,把大数交换到前面。
(这是排序的一轮操作!
)
2.用条件跳转指令构造的循环:
(直到型循环)
认真理解LOOP指令执行的操作,就会发现它其实是DEC指令和JNZ指令的合成。
所以,如果程序中CX寄存器使用有冲突,可以不用LOOP指令构造循环,而使用任何一个其它的寄存器自己构造循环:
MOVDX,10000
NEXT:
(循环体)
DECDX
JNZNEXT;不用LOOP指令构造的10000次循环
在循环次数不超过255的时候,没有必要用16位寄存器来装循环次数,可以使用AH、AL、BH、BL、CH、CL、DH、DL这8个8位的寄存器中的任何一个来构造循环:
MOVCH,50
REPT:
(循环体)
DECCH
JNZREPT
3.当型循环的结构:
当型循环结构特点就是循环控制指令在循环体的前面。
这类循环的条件一般不是次数。
例如:
AX中有一个数,统计其中为1的位有几个。
分析:
这个问题可以用固定16次循环来求解,每次用移位指令移出一个位,判断是否为1,为1则累加,为0则不累加。
16次循环结束,得到所要的结果。
这种算法不够优化,比如AX的初始值就等于0(16位全是0),程序还煞有介事地判断了16次。
也可能AX中只有最低3位是1,右移3次(循环3次)以后剩下的全是0,其余13次循环已经无意义。
优化的算法是以AX<>=0为循环的条件:
汇编语言中构造当型循环的形式多样,是较难掌握的内容。
5.1.4多重循环
1.双重循环的构造方法
基本原则:
层次清楚,避免交叉;
初始化指令的位置应该在本层循环的外边;
循环控制和循环体分开编写,以保证思路清晰。
基本结构:
外循环次数
外循环标号:
(外循环体的其他指令)
内循环次数
内循环标号:
(内循环的循环体)内循环外循环的循环体
内循环控制
(外循环体的其他指令)
外循环控制
具体用指令表达的时候,如果每层循环都用LOOP指令就有CX冲突问题,解决方案如下:
MOVCX,外循环次数
LOOP1:
MOVDI,CX;这句话必须在循环以内。
“PUSHCX”也可!
MOVCX,内循环次数
LOOP2:
(循环体)
LOOPLOOP2
MOVCX,DI;如果上面用“PUSHCX”,此处用“POPCX”
LOOPLOOP1
用LOOP指令以及PUSH、POP构造双重循环:
MOVCX,1000
NEXT1:
PUSHCX
......
MOVCX,200
NEXT2:
LOOPNEXT2
......
POPCX
LOOPNEXT1
更多的情况下,多重循环自己构造比较简单,可一避免每层都用CX引起的入出栈操作:
MOVCH,100
NEXT1:
......
MOVCL,200
NEXT2:
DECCL
JNZNEXT2
......
DECCH
JNZNEXT1
练习题:
用LOOP指令写一个三重循环的框架;
不用LOOP指令,用DEC和JNZ构造三重循环。
5.2分支程序
5.2.1分支程序结构
两种结构:
IF分支──两分支,用一个条件跳转指令即可实现
CASE分支──多分支,多个条件跳转指令组合实现
5.2.2IF分支的结构
8086CPU提供了19条(17条)条件跳转指令,都可以用来构造分支程序。
1.条件跳转指令复习
①按标志转移(5对10条):
JZ/JNZOPR;为零/非零转移
JS/JNSOPR;为负/非负转移
JO/JNOOPR;溢出/无溢出转移
JP/JNPOPR;为偶/非偶转移
JC/JNCOPR;进位/无进位转移
②按无符号数比较结果转移:
JC/JB/JNAEOPR;借位、小于、不大于等于则转移
(4条)JNC/JNB/JAEOPR;无借位、不小于、大于等于转移
JBE/JNAOPR;小于等于、不大于则转移
JNBE/JAOPR;不小于等于、大于则转移
③按有符号数比较结果转移:
JL/JNGEOPR;小于、不大于等于则转移
(4条)JNL/JGEOPR;不小于、大于等于则转移
JLE/JNGOPR;小于等于、不大于则转移
JNLE/JGOPR;不小于等于、大于则转移
④测试CX=0转移:
JCXZOPR;用于串指令或条件循环之后
2.分支程序的基本结构
某种影响标志的操作(算术指令、逻辑指令等)
JXL1;满足条件X执行L1(这里用X代表上面指令的某种条件)
(分支一);不满足条件执行此处的指令
[JMPL3;多数情况需要这样一句,可以称之为各个分支的收尾指令]
L1:
(分支二);满足条件执行此处的指令
L3:
......;分支结束之后的统一部分
例1:
比较AX和BX值,AX大于BX则计算AX-BX,否则计算BX-AX。
CMPAX,BX;此指令是假减法,按照减法结果设置标志,但是不保存结果
JCNEXT
SUBAX,BX
();括号内应该有一句什么指令?
NEXT:
SUBBX,AX
CONT:
……
例2:
变量X指向100个字的数组,变量Y指向100个字的数组,变量Z中预留了100个字的空间。
求X和Y变量中对应字的差的绝对值,保存于Z变量中。
分析:
后面例题5.5有类似的问题的完整程序,这里只分析程序算法。
在汇编程序中两数差的绝对值怎么求?
其实,上面例1中关于AX和BX相减的算法就是求AX和BX两数差的绝对值:
大数减去小数。
所以,本例其实就是要在上例处理一个数的基础上通过套上100次的循环处理100个数。
需要变化的一个内容就是数据的表达方式。
要用循环连续处理一系列数据,必须在循环体中用通式表达被操作的数据。
在例1用的AX、BX寄存器显然不能胜任(第一次循环没有问题,第二次循环AX、BX中的数据怎么变成第二组数据?
),这就需要用到间接寻址的操作数形式。
注意,能够间接寻址的寄存器只有BX、SI、DI以及BP。
LEABX,X;BX获得变量X的地址(BX指向数组X)
LEASI,Y;SI获得变量Y的地址(SI指向数组Y)
LEADI,Z;DI获得变量Z的地址(DI指向数组Z)
MOVCX,100;循环次数
REPT:
MOVAX,[BX];AX通过BX间接寻址获得X数组的一个数据
MOVDX,[SI];DX过SI间接寻址获得Y数组的一个数据
CMPAX,DX;比较大小
JCDSUBA;AX SUBAX,DX;AX>=DX,执行AX-DX MOV[DI],AX;保存AX中的结果到DI所指的单元 JMPCONT;跳过DX-AX的分支(分支) DSUBA: SUBDX,AX;DX-AX MOV[DI],DX;保存DX中的结果到DI所指的单元 CONT: ADDBX,2;修改X数组的指针 ADDSI,2;修改Y数组的指针 ADDDI,2;修改Z数组的指针 LOOPREPT 这段程序功能没有问题,但是不够优化。 并行处理X、Y、Z三个数组的对应元素根本用不着用三个间接寻址寄存器。 类似数学中的矩阵元素和高级语言中的数组元素,当处理到第5个元素的时候,同时操作的是X5、Y5和Z5。 寄存器相对寻址就是用于这种情况的: MOVBX,0;BX装上相对于各个数组开头的位移量 MOVCX,100;循环次数 REPT: MOVAX,X[BX];BX值与X变量所指的地址合成得到一个数据 MOVDX,Y[BX];BX值与Y变量所指的地址合成得到另一个数据 CMPAX,DX JCDSUBA SUBAX,DX MOVZ[BX],AX;结果保存到BX值与Z变量合成的地址中 JMPCONT DSUBA: SUBDX,AX MOVZ[BX],DX;结果保存到BX值与Z变量合成的地址中 CONT: ADDBX,2;修改位移量 LOOPREPT 5.2.3CASE分支的结构 某种影响标志的操作 JX1L1;满足条件1执行L1 JX2L2;满足条件2执行L2 ...... JXnLn;满足条件n执行Ln (分支N+1);n个条件均不满足执行此处指令 [JMPLm;多数情况需要这样一句,] L1: (分支1) [JMPLm;多数情况需要这样一句,] L2: (分支2) [JMPLm;多数情况需要这样一句,] …… Ln: (分支N)……;这个分支结尾不需要JMP Lm: 多分支以后的内容 其实,由于任何跳转条件都可以从正反两面来说(为0跳转/不为0跳转、小于跳转/不小于跳转、有进位跳转/无进位跳转、…),所以分支程序中哪个分支在前、哪个分支在后是可以交换的。 例: AX中有一个学生成绩,按照5级分的标准,判断此成绩对应A、B、C、D、E哪个字母,结果存入AL中。 两种写法如下: CMPAX,90;判断够否90 JNCITSA;够90跳转 CMPAX,80;不够90,判80 JNCITSB;够80跳转 CMPAX,70;不够80,判70 JNCITSC;够70跳转 CMPAX,60;不够70,判60 JNCITSD;够60跳转 MOVAL,‘E’;不够60,得E JMPNEXT;收尾 ITSA: MOVAL,‘A’;够90,得A JMPNEXT;收尾 ITSA: MOVAL,‘B’;够80,得B JMPNEXT;收尾 ITSA: MOVAL,‘C’;够70,得C JMPNEXT;收尾 ITSA: MOVAL,‘D’;够60,得D NEXT: …… CMPAX,90;判断够否90 JCNOTA;不够90跳转 MOVAL,‘A’;够90,得A JMPNEXT;收尾 NOTA: CMPAX,80;判断够否80 JCNOTB;不够80跳转 MOVAL,‘B’;够80,得B JMPNEXT;收尾 NOTB: CMPAX,70;判断够否70 JCNOTC;不够70跳转 MOVAL,‘C’;够70,得C JMPNEXT;收尾 NOTC: CMPAX,60;判断够否60 JCNOTD;不够60跳转 MOVAL,‘D’;够60,得D JMPNEXT;收尾 NOTD: MOVAL,‘D’;不够60,得E NEXT: …… 5.3汇编语言基本结构例题 5.3.1循环程序编写技巧 高级语言课程中,结构化程序设计思想的要诀: 自顶向下、逐步细化; 或者: 自下而上、逐步完善。 这个思想用于程序总体还是程序局部都是有效的。 循环程序可以用如下两种方法构造: 先外后内──先写出循环控制指令(循环框架),再填写循环内容。 多用于次数明确的循环。 先内后外──先写出完成一次处理的程序(循环体),再用循环控制指令包装循环体,多用于次数不明确的循环。 5.3.2循环程序例题 例题5.1将BX内容在屏幕上显示为16进制数 (比如BX=111111*********1,显示为FFFF) 解: 程序说明: 这个程序是对寄存器BX进行处理,没有用到大量数据,所以此例题的程序没有数据段,更没有附加段和堆栈段,只有一个代码段。 处理思路: BX内容为16位2进制数,每4位可以用1位16进制表示; 利用移位指令和逻辑运算可以得到BX中任何4位的值; 对于4位2进制数的16种可能取值,需要转换为对应字符的ASCII码; 如果值为0~9,只需高4位置为0011,或者说加上30H; 如果值为10~15,需要转换为41H~46H,或者说加上37H; 显示一个字符是利用系统功能调用,固定的3句话。 算法分析: BX为16位,分为4个4位,所以需要一个4次的循环; 屏幕显示顺序是从高位开始,所以对BX处理要从高4位开始,左移4位; 得到4位以后,先加30H,然后判断是否大于39H,大于则再加7; 寄存器分配: 由于移位指令用到CL,循环指令需要用CX,所以没有使用LOOP指令。 程序: 1伪指令程序框架: (仅有一个代码段的程序框架) ;=========================== prognamsegment mainprocfar assumecs: prognam start: pushds subax,ax pushax;以上3句真指令设置返回地址 ;-------------------------- ;具体程序代码 ret mainendp;MAIN过程结束伪指令 prognamends;代码段结束伪指令 ;------------------------- endstart;程序结束伪指令 24次循环的空壳为: MOVCH,4;循环次数 ROTATE: (循环体) DECCH JNZROTATE ③循环体: MOVCL,4;移位次数 ROLBX,CL MOVAL,BL ANDAL,0FH ADDAL,30H;也可以用ORAL,30H CMPAL,3AH JBPRINTIT;也可以用JC/JNAE ADDAL,7 PRINTIT: MOVDL,AL MOVAH,2 INT21H 例题5-2: 变量number中存有一个16位的数,变量number的地址存于变量addr中。 要求用间接寻址把number中的数取到AX中,然后统计该数中1的个数,结果存入count变量。 解: 处理思路: 数据段有三个变量ADDR、Y、COUNT; 设计一个16次的循环; 每次判断符号位是否为1进行计数,然后将符号位移出; 循环结束,得到1的个数,传送到COUNT变量。 算法优化: 为了提高效率,不用16次循环,而是以剩余Y值为0作为循环中止条件; 所以没用LOOP指令,而是用条件跳转构造了一个当型循环。 程序结构: 此例题提到了变量,所以程序涉及到数据段。 本例题的程序有数据段和代码段。 数据定义: NUMBERDW1234H ADDRDWNUMBER COUNTDW? 程序: 此程序没有确切的循环次数,比较适合先内后外的方法: 初始化内容: MOVCX,0 MOVBX,ADDR MOVAX,[BX] 最后插入当型循环控制: REPEAT: TESTAX,0FFFFH JZEXIT 循环体为: JNSSHIFT;AX符号位为0,跳到SHIFT INCCX;AX符号位为1,CX加1 SHIFT: SHLAX,1 JMPREPEAT 循环后的处理: EXIT: MOVCOUNT,CX 完整程序如下: ;=========================== datasegment addrdwnumber numberdw26feh countdw? dataends ;------------------------------- codesegment mainprocfar assumecs: code,ds: data start: pushds subax,ax pushax ; movax,data movds,ax ; movbx,addr movax,[bx] movcx,0 repeat: testax,0ffffh jzexit jnsshift inccx shift: shlax,1 jmprepeat exit: movcount,cx ret mainendp;MAIN过程结束伪指令 codeends;代码段结束伪指令 ;------------------------- endstart;程序结束伪指令 例5.3附加段有名为LIST的未排序字数组,其中第一个字是数组长度。 设DI已经指向LIST,AX中有一个数。 要求编写一个子程序,能够从LIST数组中找出与AX相等的数,找到后删除该数并修改数组长度。 (没有考虑多个元素于AX相等的情况) 解: 处理思路: 这是查找程序,对无序数组的查找只能用顺序查找方式。 顺序查找是程序的一个处理内容,可用串搜索指令实现。 找到以后的删除又是一个处理内容。 所谓删除其实就是数据移动,要点在于移位点的确定。 删除之后不要忘记修改长度值。 算法分析: 程序: 本例题给出的条件是要求写一个通用的查找删除子程序,所以涉及入口参数和出口参数的问题。 一般原则是返回时一切参数维持原状。 理解此程序三个部分: 子程序整体框架: DEL_ULPROCNEAR PUSHDI ;串搜索部分 ;如果没找到,POPDI,退出(返回) ;如果找到,进行删除、修改长度、POPDI,退出(返回) 串搜索部分: CLD MOVCX,ES: [DI];获得数组长度 ADDDI,2;指向第一个数据 REPNESCASW JZDELETE;串搜索结束因为出现相等,说明找到,跳到删除 POPDI;否则,恢复DI,返回 RET 删除部分: JCXZDEL_CNT;如果此时CX=0,说明最后一个元素该删除 NEXT_EL: MOVBX,ES: [DI]; MOVES: [DI-2],BX ADDDI,2 LOOPNEXT_EL DEC_CNT: POPDI DECWORDPTRES: [DI] RET DEL_UL
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 章程 设计