Presentation is loading. Please wait.

Presentation is loading. Please wait.

汇编语言程序设计 简明教程.

Similar presentations


Presentation on theme: "汇编语言程序设计 简明教程."— Presentation transcript:

1 汇编语言程序设计 简明教程

2 第四章 选择和循环 4.1 测试和控制指令 4.2 选择结构程序 4.3 循环结构程序 4.4 程序的调试 习题四

3 按照指令执行的顺序,程序的结构可以划分成以下三种。
顺序结构:程序按照它编写的顺序执行,每条指令只执行一 次,这样的程序称为“顺序结构”的程序。 循环结构:一组指令被反复地执行,这样的程序称为“循环结 构”或者“重复结构”的程序。 选择结构:根据某个条件,一部分指令被执行,另一部分指 令没有被执行,这样的程序称为“选择结构”或者 “分支结构”的程序。 一个实际运行的程序,常常是由以上三种结构的程序组合而成的,上面的三种结构称为程序的“基本结构”。使用这三种基本结构,可以编写出任何所需要的程序。

4 4.1 测试和转移控制指令 无条件转移指令 比较和测试指令 条件转移指令

5 无条件转移指令 无条件转移指令的一般格式: JMP 目的位置 执行JMP指令后,程序转移到新的“目的位置”执行。

6 [例4-1] 用JMP指令实现转移 CODE SEGMENT ASSUME CS: CODE START: MOV DL, 20H
ONE: MOV AH, 2 INT 21H ;输出DL中的字符 INC DL ;修改DL中的字符代码 JMP ONE ;转移到“ONE”处继续执行 MOV AX, 4C00H INT 21H CODE ENDS END START

7 1.近程无条件转移指令 如果转移的目的位置与出发点在同一个段里,这样的转移称为“近程”转移或者“段内”转移。实现“近程”转移,实质上是把目标位置的“偏移地址”置入IP寄存器。 按照寻址方式的不同,近程无条件转移指令有三种格式。

8 由于用一个字节补码表示目的地址与当前地址的距离,所以转移范围为下一条指令地址-128 ~ +127字节以内。
(1)短转移 如果目的位置离开出发点很近,可以使用以下格式: JMP SHORT LABEL 其中“LABEL”是目的位置的标号。这种格式产生的机器指令代码最短,为2字节。 100H: JMP SHORT TWO 102H: …… …… 10CH: TWO: …… 指令“JMP SHORT TWO”汇编后产生的机器指令为“EB0A”。 “EB”是这种类型转移指令的“操作码”,“0A”是目的位置离开出发点的距离,10CH-102H=0AH。 由于用一个字节补码表示目的地址与当前地址的距离,所以转移范围为下一条指令地址-128 ~ +127字节以内。

9 使用近程直接转移指令可以实现同一个段内64KB范围的转移。
(2)近程直接转移 JMP 目的位置标号 100H: JMP TWO 103H: ONE: …… …… 0F000H: TWO:JMP ONE 0F003H: …… 指令“JMP TWO”汇编后得到的机器指令代码为“E9FDEE”。 “E9”为操作码 “0EEFDH”为位移量,0EEFDH=0F000H-103H。 指令“JMP ONE”对应的机器指令代码为“E90011” “E9”为操作码 位移量“1100H”,0F003H+1100H=0103H(舍去进位) 使用近程直接转移指令可以实现同一个段内64KB范围的转移。

10 把转移的目的地址事先存放在某个寄存器或存储器单元中,通过这个寄存器或存储单元实现转移。
(3)近程间接转移 把转移的目的地址事先存放在某个寄存器或存储器单元中,通过这个寄存器或存储单元实现转移。 JMP CX ;寄存器间接转移,可使用任何一个通用寄存器 JMP WORD PTR[BX] ;存储器间接转移,目的地址在存储单元中

11 已在数据段定义存储器单元“TARGET” : TAEGET DW ONE 下面四组指令都可以实现向标号“ONE”的转移:
1)JMP ONE ;近程直接转移 2)LEA DX, ONE JMP DX ;寄存器间接段内转移 3)LEA BX, TARGET JMP WORD PTR[BX] ;存储器间接段内转移 4)JMP TARGET ;存储器间接段内转移

12 2.远程无条件转移指令 远程无条件转移指令可以实现不同的段之间的转移,执行该指令时,CPU把目的段的段基址装入CS,目的位置的段内偏移地址装入IP。有直接寻址和间接寻址两种格式。 (1)远程直接转移 JMP FAR PTR 远程标号 指令汇编后,对应的机器指令为5个字节: 1个字节操作码“0EAH” 2个字节目的地址的段内偏移地址 2个字节目的标号所在段的段基址

13 远程转移需要32位的目的地址,使用间接转移时,需要把32位目的地址事先装入用“DD”定义的存储单元。
(2)远程间接转移 远程转移需要32位的目的地址,使用间接转移时,需要把32位目的地址事先装入用“DD”定义的存储单元。 假设已在数据段定义存储器单元“FAR_TGT”如下: FAR_TGT DD TWO 下面三组指令都可以实现向远程标号“TWO”的转移: 1)JMP FAR PTR TWO ;远程直接转移 2)LEA BX, FAR_TGT JMP DWORD PTR[BX] ;远程间接转移 3)JMP FAR_TGT ;远程间接转移

14 4.1.2 比较和测试指令 (1)CMP(Compare, 比较)指令 指令格式: CMP 目的操作数,源操作数
比较和测试指令 (1)CMP(Compare, 比较)指令 指令格式: CMP 目的操作数,源操作数 目的操作数:8位/16位/32位的寄存器/存储器操作数。 源操作数:与目的操作数同类型的寄存器/存储器/立即数。 功能:目的操作数-源操作数,保留运算产生的标志位,不保留 运算的差。用来比较两个有符号数或无符号数的大小。

15 假设(ECX)= 8090A0B0H,指令“CMP ECX, 0”执行后:
ZF=0 (ECX)≠ 0 OF=0 减法操作没有产生溢出(SF是正确的结果符号位) SF= 如果ECX中存放的是有符号数,这个数是负数 CF=0 如果ECX中存放的是无符号数,这个数大于0 这条指令与下面的指令等效: OR ECX, ;根据ECX的值确定SF,ZF AND ECX, 0FFFFFFFFH ;根据ECX的值确定SF,ZF XOR ECX, 0 ;根据ECX的值确定SF,ZF

16 对于有符号数: OF=0时,SF为正确的结果符号 OF=1时,SF与正确的符号位相反 OF⊕SF的运算结果反映了正确的结果符号
对于无符号数: CF=0,目的操作数≥源操作数 CF= 1, 目的操作数<源操作数

17 假设存储器变量(X)= 80H,指令“CMP X, 5”执行后:
ZF=0 (X)≠ 5 OF=0 减法操作没有产生溢出,SF是正确的结果符号位 SF=1 如果X中存放的是有符号数,X<5 (由于OF=0,所以符号标志SF有效/正确) CF=0 如果X中存放的是无符号数,X>5 (由于ZF=0,所以不相等)

18 (2)TEST(Test,测试)指令 指令格式: TEST 目的操作数,源操作数 目的操作数:8位/16位/32位的寄存器/存储器操作数。 源操作数:与目的操作数同类型的寄存器/存储器/立即数。 功能:TEST指令将目的操作数与源操作数进行逻辑乘运算,保 留运算产生的各标志位,但是不保留逻辑乘的结果。该 指令用来测试目的操作数中某几位二进制的特征。

19 指令 TEST VAR, 1 执行后: 如果ZF = 0,说明变量VAR的D0位为1,该数为奇数 如果ZF = 1,说明变量VAR的D0位为0,该数为偶数 指令 TEST BL, 6 执行后: 如果ZF = 0, 说明BL寄存器的D2D1≠00, 这两位为01, 10或11。 如果ZF = 1,说明BL寄存器的D2D1=00,这两位为00。

20 (3)BT(Bit Test,位测试)指令 指令格式: BT 目的操作数,源操作数 目的操作数: 16位/32位的寄存器/存储器操作数。 源操作数:与目的操作数同类型的寄存器操作数或0~255以内 的立即数。 功能:BT指令将目的操作数内部由源操作数指定的那一位二进 制的值送入CF,两个操作数的值均不改变。该指令用来 测试目的操作数中某一位二进制的值。

21 设(EDX)= 12345678H, (ECX)= 5 与BT指令类似的还有以下三条指令:
BT EDX, ECX ; 由于EDX寄存器D5=1,执行后CF= 1,EDX值不变 BT EDX, ; 由于EDX寄存器D2=0,执行后CF= 0,EDX值不变 与BT指令类似的还有以下三条指令: BTS 目的操作数,源操作数 ;测试目的操作数的指定位,并把该位置为1 BTR 目的操作数,源操作数 ;测试目的操作数的指定位,并把该位置为0 BTC 目的操作数,源操作数 ;测试目的操作数的指定位,并把该位取反

22 4.1.3 条件转移指令 条件转移指令格式: “J”是条件转移指令操作码的第一个字母 “cc”是代表转移条件的1~3个字母
条件转移指令 条件转移指令格式: Jcc label “J”是条件转移指令操作码的第一个字母 “cc”是代表转移条件的1~3个字母 “label”是转移目的地的标号。

23 (1)根据两个有符号数比较结果的条件转移指令 两个有符号数的比较结果通过OF,SF,ZF反映出来
G (Greater,大于) L (Less,小于) E (Equal,等于) N (Not,否) 例:JG/JNLE 大于(不小于等于)则转移 JNL/JGE 不小于(大于或等于)则转移 JE/JZ 等于(为零)则转移

24 根据有符号数大小的条件转移指令 指令助记符 指令功能 转移条件 JG, JNLE 大于(不小于等于)时转移 OF⊕SF= 0且ZF=0
JGE, JNL 大于等于(不小于)时转移 OF⊕SF= 0 JZ, JE 为零(相等)时转移 ZF= 1 JNZ, JNE 不为零(不相等)时转移 ZF=0 JL, JNGE 小于(不大于等于)时转移 OF⊕SF= 1 JLE, JNG 小于等于(不大于)时转移 OF⊕SF= 1或ZF=1

25 下面程序根据有符号字变量X和Y的大小决定程序的走向。
MOV AX, X ;取出X的值送AX CMP AX, Y ;比较两个操作数,建立需要的标志位 JG GREATER ;如果X>Y,转移到 “GREATER”处执行 JE EQUAL ;如果X=Y,转移到 “EQUAL”处执行 LESS: ;否则,执行标号“LESS”处的指令 …… GREATER: EQUAL:

26 下面程序能够正确运行,但最后一条指令有“画蛇添足”之嫌。
JG GREATER ;如果X>Y,转移到 “GREATER”处 JE EQUAL ;如果X=Y,转移到 “EQUAL”处 JL LESS ;如果X<Y,转移到 “LESS”处 LESS: …… 下面的程序计算 AX = |AX-BX| SUB AX, BX ;AX←(AX)-(BX), 建立标志位 JGE SKIP ;如果(AX)≥0,转标号“SKIP” NEG AX ;如果(AX)<0,把AX的值取反 SKIP:

27 (2)根据两个无符号数比较结果的条件转移指令 两个有符号数的比较结果通过CF,ZF反映出来,代表转移条件的字母:
A(Above,高于) B(Below,低于) E(Equal,等于)

28 根据无符号数大小的条件转移指令 指令助记符 指令功能 转移条件 JA, JNBE 高于(不低于等于)时转移 CF= 0且ZF=0
JAE, JNB, JNC 高于等于(不低于)时转移 CF= 0 JZ, JE 为零(相等)时转移 ZF= 1 JNZ, JNE 不为零(不相等)时转移 ZF=0 JB, JNAE, JC 低于(不高于等于)时转移 CF= 1 JBE, JNA 低于等于(不高于)时转移 CF= 1或ZF=1

29 (3)根据单个标志位的条件转移指令 指令操作码助记符 指令功能 转移条件 JC, JB, JNAE 有进位时转移 CF= 1
JNC, JNB, JAE 无进位时转移 CF= 0 JZ, JE 为零(相等)时转移 ZF= 1 JNZ, JNE 不为零(不相等)时转移 ZF=0 JS 为负时转移 SF=1 JNS 为正时转移 SF=0 JO 溢出时转移 OF=1 JNO 不溢出时转移 OF=0 JP, JPE “1”的个数为偶数时转移 PF=1 JNP, JPO “1”的个数为奇数时转移 PF=0

30 对于16位80X86CPU,条件转移指令的转移范围在下一条指令地址-128~+127字节之间。如果转移目的位置超出了上述范围,汇编时将报告错误。
JG Label ;如果标号“Label”超出范围,汇编时将出错 可以把上面指令修改为: JNG Skip JMP Label Skip: …… 对于32位80X86CPU,条件转移指令汇编后产生4字节机器代码,前2字节是它的操作码,后面2字节是表示转移距离的位移量,可以实现64KB范围内的转移。

31 (4)根据CX/ECX寄存器值的条件转移指令
指令格式: JCXZ Label ;若CX=0,转移到Label JECXZ Label ;若ECX=0,转移到Label 它们的转移范围固定为下一条指令地址-128~+127字节以内。

32 4.2 选择结构程序 基本选择结构 单分支选择结构 复合选择结构 多分支选择结构

33 计算分段函数的值 为“|X|>3”和“|X|≤3”分别编制了进行不同处理的指令序列。 如果条件“|X|≤3”成立(为“真”),执行 “Y=3X-5” 如果条件“|X|≤3”不成立(为“假”),执行Y=6 通过在不同的程序之间进行选择,实现程序的不同功能, “选择结构”因此得名。

34 基本选择结构

35 [例4-2] 判断变量X的值是否为“偶数” CODE SEGMENT ASSUME CS: CODE START: JMP BEGIN
X DB ? ; 被测试的数,汇编之前置入 YES DB 0AH, 0DH, “It’s a even number.”, 0AH, 0DH, ‘$’ NO DB 0AH, 0DH, “It’s a odd number.”, 0AH, 0DH, ‘$’ BEGIN:PUSH CS POP DS

36 TEST X, 1 ;测试X的最低位,确定是否为偶数
JZ EVN ;ZF=1,该数是偶数,转向“EVN” ODD: LEA DX, NO ;否则,该数是奇数 MOV AH, 9 INT 21H ;输出奇数的相关信息 JMP DONE ;跳过程序“EVN” EVN: LEA DX, YES INT 21H ;输出偶数的相关信息 DONE: MOV AX, 4C00H INT 21H CODE ENDS END START

37 如果两个“平行”分支有相同的处理过程,可以把它们“合并” :
…… TEST X, 1 ;测试X的最低位,确定是否为偶数 JZ EVN ;ZF=1,该数是偶数,转向“EVN” ODD: LEA DX, NO ;否则,该数是奇数 JMP DONE ;跳过程序“EVN” EVN: LEA DX, YES ;该数是偶数 DONE: MOV AH, 9 INT 21H ;输出该数的相关信息 MOV AX, 4C00H

38 变量X取值93H,汇编、连接后运行该程序,程序输出:
It’s a odd number. 变量X取值94H,汇编、连接后运行该程序,程序输出: It’s a even number.

39 [例4-3] 从键盘上输入一个小写字母,显示该字母的前导和后继。
DATA SEGMENT PROMPT DB 0DH, 0AH, “ Input a lowercase letter: $”;提示 ERR_MSG DB 0DH, 0AH, “ Input error . $” ;输入错误警告 BUF DB 0DH, 0AH, ‘Prev: ’ ;输出缓冲区 PREV DB 20H DB 0DH, 0AH, ‘Succ: ’ SUCC DB 20H DB 0DH, 0AH, ‘$’ DATA ENDS CODE SEGMENT ASSUME CS: CODE, DS: DATA

40 START: MOV AX, DATA MOV DS, AX INPUT: LEA DX, PROMPT MOV AH, 9 INT 21H ;输出提示信息 MOV AH, 1 INT 21H ;输入一个字符 CMP AL, ‘a’ ;输入正确性检查 JB ERROR CMP AL, ‘z’ JA ERROR MOV BL, AL ;计算“前导”字母 DEC BL

41 CMP BL, ‘a’ JB SKIP1 ;“前导”非字母,跳过 MOV PREV, BL ;保存“前导”字母 SKIP1: INC AL ;计算“后继”字母 CMP AL, ‘z’ JA SKIP2 ;“后继”非字母,跳过 MOV SUCC, AL ;“后继”为字母,保存 SKIP2: LEA DX, BUF ;输出“前导”和“后继”字母 MOV AH, 09H INT 21H JMP EXIT ;跳过出错处理程序

42 程序要点: ERROR:LEA DX, ERR-MSG ;显示出错信息 MOV AH, 09H INT 21H
JMP INPUT ;要求重新输入 EXIT: MOV AX, 4C00H ;返回OS CODE ENDS END START 程序要点: 健壮性 预设结果

43 [例4-4] 计算分段函数 INCLUDE YLIB.H DATA SEGMENT
PROMPT DB 0DH, 0AH, “Input X ( ~ ): $” X DW ? OUT_MSG DB 0DH, 0AH, “Y= $” DATA ENDS CODE SEGMENT ASSUME CS: CODE, DS: DATA START: MOV AX, DATA MOV DS, AX ;装载DS

44 LEA DX, PROMPT ;输入提示信息 CALL READINT ;从键盘上输入X的值 MOV X, AX ;保存输入值 COMP: CMP X, 3 ;比较,X>3 ? JG GREATER ;X>3成立,转“GREATER” CMP X, -3 ;比较,X<-3 ? JL GREATER ;X<-3成立,转“GREATER” LESS: ;|X|≤3 的程序段 MOV BX, AX ;BX←X SAL AX, 1 ;AX←2X ADD AX, BX ;AX←2X+X SUB AX, 5 ;AX←3X-5 JMP OUTPUT

45 GREATER: MOV AX, 6 ;|X|>3 的程序段 OUTPUT: LEA DX, OUT_MSG ;结果的前导文字 CALL WRITEINT ;输出计算结果 CALL CRLF ;输出回车换行 EXIT: MOV AX, 4C00H INT 21H CODE ENDS END START

46 复合逻辑表达式的分解

47 4.2.2 单分支选择结构 如果选择结构的一个分支为“空”,这样的程序流程称为 “单分支选择结构”。
单分支选择结构 如果选择结构的一个分支为“空”,这样的程序流程称为 “单分支选择结构”。 合理地选择Jcc指令所使用的条件,可以使程序更加流畅。 可以把一些基本选择结构程序改写为单分支选择结构。

48 计算AX ← |AX| 的两种判断方法:

49 在例4-4中,可以将Y“预设”为6,一旦条件|X|>3成立,立即转向OUTPUT输出“预设”的结果,否则进行相应的计算。
…… COMP: MOV AX, 6 ;预设AX= 6 CMP X, 3 ;比较,X>3 ? JG OUTPUT ;若X>3,转OUTPUT输出 CMP X, -3 ;比较,X<-3 ? JL OUTPUT ;若X<-3, 转OUTPUT输出 LESS: ;|X≤3 的程序段 MOV AX, X ;AX←X …… ;AX←3X-5 OUTPUT: LEA DX, OUT_MSG ;结果的前导文字 CALL WRITEINT ;输出计算结果

50 [例4-5] 将4位二进制转换成对应的十六进制字符
[例4-5] 将4位二进制转换成对应的十六进制字符 MOV AL, X CMP AL, 9 JA ALPH ADD AL, 30H JMP DONE ALPH: ADD AL, 37H DONE: MOV Y, AL MOV AL, X OR AL, 30H CMP AL, ‘9’ JBE DONE ADD AL, 7 DONE: MOV Y, AL

51 4.2.3 复合选择结构 选择结构一个分支的程序中又出现了选择结构,这样的 结构称为“复合选择结构”或者“嵌套选择结构”。
复合选择结构 选择结构一个分支的程序中又出现了选择结构,这样的 结构称为“复合选择结构”或者“嵌套选择结构”。 排除法:每次判断排除若干可能,留下一种可能情况进行处理; 确认法:每次判断确认一种可能,对已确认的情况进行处理。

52 [例4-6] 计算Y=SGN(X) ;方法a,逐项排除 CMP X, 0 JGE UN_MINUS MINUS: MOV Y, -1
JMP DONE UN_MINUS: JE ZERO MOV Y, 1 ZERO: MOV Y, 0 DONE: …… ;方法b,逐项确认 CMP X, 0 JG PLUS JE ZERO MINUS: MOV Y, -1 JMP DONE PLUS: MOV Y, 1 ZERO: MOV Y, 0 DONE: ……

53 复合分支选择结构

54 多分支选择结构 在选择结构程序里,如果可供选择的程序块多于两个,这样的结构称为多分支选择结构,如下图 (a)所示,下图 (b)是汇编语言程序的实现方法。

55 [例4-7] 从键盘上输入数字“1”到“3”,根据输入选择对应程序 块执行。
[例4-7] 从键盘上输入数字“1”到“3”,根据输入选择对应程序 块执行。 DATA SEGMENT PROMPT DB 0DH, 0AH, “Input a number (1~3): $” MSG1 DB 0DH, 0AH, “FUNCTION 1 EXECUTED . $” MSG2 DB 0DH, 0AH, “FUNCTION 2 EXECUTED . $” MSG3 DB 0DH, 0AH, “FUNCTION 3 EXECUTED . $” DATA ENDS CODE SEGMENT ASSUME CS: CODE, DS: DATA START: MOV AX, DATA MOV DS, AX

56 INPUT: LEA DX, PROMPT MOV AH, 9 INT 21H ;输出提示信息 MOV AH, 1 INT 21H ;输入一个数字 CMP AL, ‘1’ JB INPUT ;“0”或非数字,重新输入 JE F1 ;数字“1”,转F1 CMP AL, ‘2’ JE F2 ;数字“2”,转F2 CMP AL, ‘3’ JE F3 ;数字“3”,转F3 JMP INPUT ;大于“3”,重新输入

57 F1: LEA DX, MSG1 ;F1程序块 JMP OUTPUT F2: LEA DX, MSG2 ;F2程序块 F3: LEA DX, MSG3 ;F3程序块 OUTPUT: MOV AH, 9 INT 21H MOV AX, 4C00H CODE ENDS END START

58 把完成各功能的程序块入口地址放在一张表格中,根据输入,计算出该功能程序块入口地址在表中的位置,通过存储器间接转移转入对应位置执行。
DATA SEGMENT PROMPT DB 0DH, 0AH, “Input a number (1~3): $” MSG1 DB 0DH, 0AH, “FUNCTION 1 EXECUTED . $” MSG2 DB 0DH, 0AH, “FUNCTION 2 EXECUTED . $” MSG3 DB 0DH, 0AH, “FUNCTION 3 EXECUTED . $” ADDTBL DW F1, F2, F3 DATA ENDS CODE SEGMENT ASSUME CS: CODE, DS: DATA

59 START:MOV AX, DATA MOV DS, AX INPUT:LEA DX, PROMPT MOV AH, 9 INT 21H ;显示提示信息 MOV AH, 1 INT 21H ;输入一个数字 CMP AL, ‘1’ JB INPUT ;不正确输入,重新输入 CMP AL, ‘3’ JA INPUT ;不正确输入,重新输入

60 SUB AL, ‘1’ ;将数字字符“1”到“3”转换为0, 1, 2
SHL AL, 1 ;转换为0,2,4 MOV BL, AL MOV BH, 0 ;转入BX JMP ADDTBL[BX] ;间接寻址,转移到对应程序块 F1: LEA DX, MSG1 ;F1程序块 JMP OUTPUT F2: LEA DX, MSG2 ;F2程序块 F3: LEA DX, MSG3 ;F3程序块 JMP OUTPUT ;这条指令可以省略

61 OUTPUT: MOV AH, 9 INT 21H MOV AX, 4C00H CODE ENDS END START

62 4.3 循环结构程序 循环指令 计数循环 条件循环 多重循环

63 循环结构也称为“重复结构”,它使得一组指令重复地执行,可以用有限长度的程序完成大量的处理任务,几乎所有的应用程序中都离不开循环结构。
循环一般由以下4个部分组成: (1)初始化部分:为循环做准备,如累加器清零,设置地址指 针和计数器的初始值等。 (2)工作部分:实现循环的基本操作,也就是需要重复执行的 一段程序。 (3)修改部分:修改指针、计数器的值,为下一次循环做准备。 (4)控制部分:判断循环条件,结束循环或继续循环。

64 按照循环结束的条件,有以下两类循环: 计数循环:循环的次数事先已经知道,用一个变量(寄存器 或存储器单元)记录循环的次数(称为“循环计数 器”)。进行减法计数时,循环计数器的初值直接设为 循环次数,每循环一次将计数器减1,计数器减为0时, 循环结束。 条件循环:循环的次数事先并不确定,每次循环开始时或结 束后测试某个条件,根据这个条件是否满足来决定是 否继续下一次循环。

65 按照循环结束判断在循环中的位置,有以下两种结构的循环:
WHILE循环:进入循环后,先判断循环结束条件,条件满足 则退出循环,循环次数最少为0次。 DO-WHILE循环:进入循环后,先执行工作部分,然后判断循环 继续的条件,条件满足则转向工作部分继续循环,循环 次数最少1次。

66

67 4.3.1 循环指令 循环指令采用相对寻址方式,Label距离循环指令的下一条 指令必须在-128~+127B之内。
循环指令 LOOP Label ; CX←CX-1,若(CX)≠0,转移到Label LOOPZ/LOOPE Label ; CX←CX-1,若(CX)≠0且ZF=1,转移到Label LOOPNZ/LOOPNE Label ; CX←CX-1,若(CX)≠0且ZF=0,转移到Label 循环指令采用相对寻址方式,Label距离循环指令的下一条 指令必须在-128~+127B之内。

68 LOOP指令的功能可以用Jcc指令实现:
DEC CX ; CX←CX-1 JNZ Label ; 若(CX)≠0(也就是ZF=0),转移到Label LOOPZ/LOOPE,LOOPNZ/LOOPNE指令的功能也可以由 Jcc指令实现。 由于对CX先减1,后判断,如果CX的初值为0,将循环 次。 循环指令的执行不影响标志位。

69 4.3.2 计数循环 计数循环是基本的循环组织方式,用循环计数器的值来控制循环,有时候也可以结合其它条件共同控制。
计数循环 计数循环是基本的循环组织方式,用循环计数器的值来控制循环,有时候也可以结合其它条件共同控制。 [例4-8] 从键盘上输入一个字符串(不超过80个字符),将它 逆序后输出。 INCLUDE YLIB.H DATA SEGMENT BUFFER DB 81, ?, 81 DUP(?) MESS DB 0AH, 0DH, “Input a string please : $” DATA ENDS

70 CODE SEGMENT ASSUME CS:CODE, DS:DATA START:MOV AX, DATA MOV DS, AX LEA DX, MESS MOV AH, 09H INT 21H ; 输出提示信息 MOV AH, 0AH LEA DX, BUFFER INT 21H ; 输入字符串 CALL CRLF LEA BX, BUFFER ; 缓冲区首地址送BX MOV CL, BUFFER+1 MOV CH, 0 ; 输入字符个数送CX(循环次数)

71 ADD BX, CX INC BX ; 计算字符串末地址送BX(指针) DISP: MOV DL, [BX] MOV AH, 02H INT 21H ; 逆序输出一个字符 DEC BX ; 修改指针 LOOP DISP ; 计数循环 CALL CRLF ; 输出换行、回车,结束本行 MOV AX, 4C00H INT 21H CODE ENDS END START

72 [例4-9] 从键盘上输入一个十进制无符号整数,将它用十六进 制格式输出。
[例4-9] 从键盘上输入一个十进制无符号整数,将它用十六进 制格式输出。 INCLUDE YLIB.H DATA SEGMENT MESS1 DB 0AH, 0DH, “Input a number : $” MESS2 DB 0AH, 0DH, “The number in hexdecimal is: $” HEXTAB DB “ ABCDEF” DATA ENDS CODE SEGMENT ASSUME CS: CODE, DS: DATA START: MOV AX, DATA MOV DS, AX

73 LEA DX, MESS1 CALL READDEC ; 输入一个十进制无符号数 MOV SI, AX ; 转存在SI中 LEA DX, MESS2 MOV AH, 9 INT 21H ; 输出文字前导 MOV CX, 4 ; 循环计数器初值 LEA BX, HEXTAB ; 换码表首地址 ONE: PUSH CX MOV CL, 4 ROL SI, CL ; 把最高4位移到最低4位

74 MOV AX, SI ; 转入AX中 AND AX, 000FH ; 保留最低4位 XLAT ; 查表,转换成十六进制字符的ASCII代码 MOV DL, AL MOV AH, 2 INT 21H ; 输出一个十六进制字符 POP CX LOOP ONE ; 计数循环 CALL CRLF ; 输出回车换行, 结束本行 MOV AX, 4C00H INT 21H CODE ENDS END START

75 [例4-10] 从键盘上输入七名裁判的评分(0~10),扣除一个最. 高分,一个最低分,计算出其它五项评分的平均值(保留
[例4-10] 从键盘上输入七名裁判的评分(0~10),扣除一个最 高分,一个最低分,计算出其它五项评分的平均值(保留 一位小数),在显示器上输出。 用总分减去最高分、最低分,最后除以5,得到需要的成绩。 求N个数据中最大值的方法: 预设一个“最大值”, 取出一个数据与这个“最大值”进行比较, 如果数据大于“最大值”,则将该数据作为新的“最大值”。 进行N次比较之后留下的就是这N个数据的最大值。 预设的“最大值”的初值可以从N个数据中任取一个,也可以根 据数据的范围,取一个该范围内的最小的数。 计算最小值的方法与此类似。

76 INCLUDE YLIB.H DATA SEGMENT MESS1 DB 0DH, 0AH, “Input a score ( 0~10 ) : $” MESS2 DB 0DH, 0AH, “The final score is : $” C5 DB 5 MAX DB ? MIN DB ? SUM DB ? DATA ENDS CODE SEGMENT ASSUME CS: CODE, DS: DATA START: MOV AX, DATA MOV DS, AX

77 MOV SUM, 0 ; 累加器清零 MOV MAX, 0 ; “最大值”预设为0 MOV MIN, 255 ; “最小值”预设为255 MOV CX, 7 ; 循环计数器,初值7 ONE: LEA DX, MESS1 CALL READDEC ; 键盘输入一个分数 ADD SUM, AL ; 累加 CMP MAX, AL ; 与“最大值”比较 JA L1 MOV MAX, AL ; 大于“最大值”则保留 L1: CMP MIN, AL ; 与“最小值”比较 JB L2 MOV MIN, AL ; 小于“最小值”则保留 L2: LOOP ONE ; 计数循环

78 MOV AL, SUM SUB AL, MAX SUB AL, MIN ; 从总分中减去最大、最小值 MOV SUM, AL XOR AH, AH ; 高8位清零 DIV C5 ; 求平均值 PUSH AX ; 保留余数(在AH中) MOV AH, 0 ; 清余数 LEA DX, MESS2 CALL WRITEDEC ; 输出结果的整数部分

79 MOV DL, ‘.’ MOV AH, 2 INT 21H ; 输出小数点 POP AX ; 从堆栈弹出余数 SHL AH, 1 ; 计算小数部分:(AH÷5)×10=AH×2 MOV DL, AH OR DL, 30H ; 转换成ASCII代码 INT 21H ; 输出结果的小数部分 CALL CRLF ; 输出回车换行,结束本行 MOV AX, 4C00H INT 21H CODE ENDS END START

80 [例4-11] 求稀疏矩阵ARRAY非零元素的平均值。
“稀疏矩阵”是指存在大量零元素的矩阵。 为了节约存储空间,通常不存储那些值为零的元素。 稀疏矩阵通常用一个“标尺”记录哪些位置上有非零元素。 标尺RULE = B表示一个16个元素的 稀疏矩阵,它仅在第3,10位置上有非零元素

81 .386 DATA SEGMENT USE16 ARRAY DW 123, -39, 211, …… ;稀疏矩阵非零元素 RULE DW B ;“标尺” AVG DW ? ;非零元素的平均值 SUM DD ? ;累加器 DATA ENDS CODE SEGMENT USE16 ASSUME CS: CODE, DS: DATA START: MOV AX, DATA MOV DS, AX

82 LEA SI, ARRAY ;装载非零元素地址指针
MOV CX, 16 ;设置计数器初值 MOV BX, ;设置非零元素个数计数器初值 MOV SUM, 0 ;设置累加器初值 ONE: ROL RULE, 1 ;测试标尺 JNC NEXT ;该位置上为零元素,转NEXT MOVSX EAX, WORD PTR[SI] ;取出一个元素,转换成32位有符号数 ADD SUM, EAX ;累加 INC BX ;统计非零元素个数 ADD SI, 2 ;修改地址指针 NEXT: LOOP ONE ;计数循环控制

83 CMP BX, 0 ;有非零元素? JE EMPTY ;无非零元素,转向“EMPTY” MOV DX, WORD PTR[SUM+2] MOV AX, WORD PTR[SUM] ;取出32位累加值 IDIV BX ;计算平均值 MOV AVG, AX ;保存结果 JMP EXIT EMPTY:MOV AVG, -1 ;无非零元素,置平均值为-1 EXIT: MOV AX, 4C00H INT 21H CODE ENDS END START

84 4.3.3 条件循环 [例4-12] 字符串STRING以代码0结束,求这个字符串的长度(字符个数)。 DATA SEGMENT
条件循环 [例4-12] 字符串STRING以代码0结束,求这个字符串的长度(字符个数)。 DATA SEGMENT STRING DB “A string for testing . ”, 0 LENTH DW ? DATA ENDS CODE SEGMENT ASSUME CS: CODE, DS: DATA START:MOV AX, DATA MOV DS, AX

85 LEA SI, STRING ;装载字符串指针 MOV CX, 0 ;设置计数器初值 TST: CMP BYTE PTR [SI], 0;比较 JE DONE ;字符串结束,转向DONE保存结果 INC SI ;修改指针 INC CX ;计数 JMP TST ;转向TST,继续循环 DONE: MOV LENTH, CX ;保存结果 MOV AX, 4C00H INT 21H CODE ENDS END START

86 比较一下,您喜欢这种风格吗? …… LEA SI, STRING-1 ;装载字符串指针 MOV CX, -1 ;装载计数器初值
TST: INC SI ;修改指针 INC CX ;计数 CMP BYTE PTR [SI], 0 ;比较 JNE TST ;未结束,转TST继续循环 MOV LENTH, CX ;字符串结束,保存结果

87 错在哪里?运行结果会怎样? …… TST: CMP BYTE PTR [SI], 0 ;比较 INC SI ;修改指针 INC CX ;计数
JNE TST ;转向TST,继续循环

88 [例4-13] 一维无符号字数组ARRAY以-1作为数组结束标志, 求这个数组各元素的平均值。
DATA SEGMENT ARRAY DW 1, 2, 3, 4, 5, 6, -1 AVRG DW ? DATA ENDS CODE SEGMENT ASSUME CS: CODE, DS: DATA START: MOV AX, DATA MOV DS, AX

89 LEA BX, ARRAY ;装载数组指针 XOR CX, CX ;设置计数器初值 XOR DX, DX XOR AX, AX ;清累加器 ONE: CMP WORD PTR[BX], -1 ;判数组是否结束 JE DONE ;数组结束, 转DONE,结束处理 ADD AX, [BX] ;累加 ADC DX, 0 ;保留进位 ADD BX, 2 ;修改指针 INC CX ;数组元素个数计数 JMP ONE

90 DONE: JCXZ NULL ;数组元素个数为0, 不能求平均值
DIV CX ;计算数组平均值 MOV AVRG, AX ;保存结果 JMP EXIT NULL: MOV AVRG, -1 ;数组为“空”,记平均值为-1 EXIT: MOV AX, 4C00H INT 21H CODE ENDS END START

91 [例4-14] 查找字母’a’在字符串STRING中第一次出现的位置, 如果未出现,置位置值为-1。
DATA SEGMENT POSITION DW ? STRING DB “This is a string for example. ” , 0 DATA ENDS CODE SEGMENT ASSUME DS: DATA, CS: CODE START: MOV AX, DATA MOV DS, AX

92 MOV SI, -1 ;SI用作字符串字符指针 MOV CX, 30 ;字符串长度30 L0: INC SI ;修改指针 CMP STRING[SI], ‘a’ ;一个字符与’a’进行比较 LOOPNE L0 ;字符串未结束,未找到,继续 JNE NOTFOUND ;未找到,转“NOTFOUND” MOV POSITION, SI ;保存位置值 JMP EXIT NOTFOUND:MOV POSITION, -1 ;未找到,置位置值为-1 EXIT: MOV AX, 4C00H INT 21H CODE ENDS END START

93 程序使用LOOPNE指令来控制循环,既有计数控制,又有条件控制。循环结束有两种可能性:
字符串内找到字符’a’:循环结束时ZF=1,SI内是字符的出 现位置(从0开始); 字符串内未找到字符’a’:循环结束时ZF=0,SI内是字符串 的长度-1(30-1=29)。 对于LOOPZ/LOOPE,LOOPNZ/LOOPNE控制的循环,一般应在循环结束后用条件转移指令分开这两种情况,分别处理。

94 [例4-15] 从键盘上输入一个有符号整数(假设在-32768 ~ +32767 之间),将它转换成二进制补码,存入NUM单元。
DATA SEGMENT C10 DW 10 NUM DW ? SIGN DB ? ERRMSG DB 0DH, 0AH, “Input a Decimal Digit ( 0~9 ) : $” DATA ENDS CODE SEGMENT ASSUME DS: DATA, CS: CODE START: MOV AX, DATA MOV DS, AX

95 MOV NUM, 0 ;累加器清零 MOV SIGN, 0 ;符号预设为0(表示“+”) BEGIN:MOV AH, 1 INT 21H ;从键盘输入一个字符 CMP AL, 0DH JE EXIT ;是回车,转EXIT,结束 CMP AL, “+” JE INPUT ;是“+”,转INPUT输入下一个字符 CMP AL, “-” JNE TWO ;非符号字符,转TWO处理该字符 MOV SIGN, 1 ;是“-”,把符号标识为1(表示“-”)

96 INPUT: MOV AH, 1 INT 21H ;从键盘再输入一个字符 CMP AL, 0DH JE DONE ;是回车,转DONE,结束处理 TWO: CMP AL, “0” JB ERRINPUT ;输入非数字,显示出错信息 CMP AL, “9” JA ERRINPUT ;输入非数字,显示出错信息 MOV BX, AX ;输入字符转移到BX寄存器 AND BX, 000FH ;转换成二进制数 MOV AX, NUM MUL C10 ADD AX, BX ;新输入数字拼接到已输入数字中 MOV NUM, AX JMP INPUT ;转INPUT,输入下一个字符

97 ERRINPUT: LEA DX, ERRMSG MOV AH, 9 INT 21H ;显示出错信息 JMP INPUT ;转INPUT,重新输入 DONE: CMP SIGN, 0 ;判符号位 JE EXIT NEG NUM ;符号为“-”,对已输入数求补 EXIT: MOV AX, 4C00H INT 21H CODE ENDS END START

98 多重循环 如果一个循环的循环体内包含了另一个循环,称这个循环为“多重循环”,各层循环可以是计数循环或者条件循环。

99 ! “ # $ % & ‘ ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
[例4-16] 打印20H~7FH之间的ASCII字符表。 ! “ # $ % & ‘ ( ) * + , - . / : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h I j k l m n o p q r s t u v w x y z { | } ~

100 打印格式: 每行16个字符,共6行。 打印1行: 打印1个字符的过程重复16次,构成一个计数循环(内循环)。 打印6行: 打印1行字符的过程重复6次,构成另一个计数循环(外循环) 。

101 INCLUDE YLIB.H CODE SEGMENT ASSUME CS: CODE, DS: CODE START: MOV BL, 20H ;第一个字符的ASCII代码 MOV CH, 6 ;行数计数器初值 ; ============ 打印一行循环开始 ======= ====== L0: CALL CRLF ;开始一个新行 MOV CL, 16 ;列计数器初值

102 ; ------------------- 打印一个字符的循环开始 ---------------------
L1: MOV DL, BL ;装入一个字符ASCII代码 MOV AH, 2 INT 21H ;输出一个字符 MOV DL, 20H INT 21H ;输出一个空格 INC BL ;准备下一个待输出的ASCII码 DEC CL ;列数计数 L11: JNZ L1 ;列数未满(本行未完),转L1继续 ; 打印一个字符的循环结束

103 DEC CH ;行数计数 L00: JNZ L0 ;行数未满,转L0继续 ; ============= 打印一行的循环结束 ============ CALL CRLF ;结束最后一行 MOV AX, 4C00H INT 21H CODE ENDS END START

104 借助于堆栈,将CX“分身”为两个计数器 …… MOV BL, 20H ;第一个字符的ASCII代码 MOV CX, 6 ;行数计数器初值
; ============= 打印一行循环开始 ============== L0: CALL CRLF ;开始一个新行 PUSH CX ;保存CX中的行计数器值 MOV CX, 16 ;CX中置入列计数器初值

105 ; -------------------- 打印一个字符的循环开始 --------------------
L1: MOV DL, BL ;装入一个字符ASCII代码 …… L11: LOOP L1 ;列数未满(本行未完),转L1继续 ; 打印一个字符的循环结束 POP CX ;恢复CX为行计数器 L00: LOOP L0 ;行数计数,行数未满,转L0继续 ; ============== 打印一行的循环结束 ==============

106 [例4-17] 用“冒泡”的方法对数组P的元素排序,按从小到大的顺序排列。
遍数 本遍 整序前 第1次 整序后 第2次 第3次 第4次 1 2 3 4

107 INCLUDE YLIB.H DATA SEGMENT P DW 10 DUP(?) N DW ? MESS1 DB 0DH, 0AH, ‘Input Numbers of Elements ’ DB ‘in Array P (1~10): $’ MESS DB 0DH, 0AH, ‘Input Values of Elements in Array P: $’ MESS3 DB 0DH, 0AH, ‘NO. $’ MESS DB 0DH, 0AH, ‘Array P After Sort: ’, 0DH, 0AH, ‘$’ DATA ENDS

108 CODE SEGMENT ASSUME CS: CODE, DS: DATA START:MOV AX, DATA MOV DS, AX LEA DX, MESS1 CALL READINT ;输入数组元素个数 MOV N, AX LEA DX, MESS2 MOV AH, 9 INT 21H ;输出提示信息,准备输入数组各元素的值 ;****** 从键盘输入数组各元素 ************** MOV BX, 0 ;输入数组循环准备 MOV CX, N ; BX=元素在数组内位移, CX=元素个数

109 INPUT:LEA DX, MESS3 MOV AX, N SUB AX, CX INC AX CALL WRITEINT ;输出元素序号,从1开始 MOV DX, 0FFFFH CALL READINT ;输入一个元素 MOV P[BX], AX ;保存该元素的值 INC BX INC BX ;修改指针 LOOP INPUT ;输入未结束,转INPUT继续

110 ;************* 开始排序 ***************
MOV CX, N ;设置外层循环计数器 DEC CX ;CX中为排序的“遍数”(=N-1) ;========= 外层循环循环体开始 ============ LOOP1:PUSH CX ;保存外循环计数器 MOV BX, 0 ;BX=整序元素在数组内的位移 ;每一遍从第一个元素开始 ;----- 内层循环循环体开始,CX的值是内层循环的次数 LOOP2:MOV AX, P[BX] CMP AX, P[BX+2] ;邻元素比较 JLE NEXT ;不需要整序,转NEXT XCHG AX, P[BX+2] ;交换邻元素位置 XCHG AX, P[BX]

111 NEXT: INC BX ;修改指针 INC BX LOOP LOOP ;本遍未结束,转LOOP2继续 ; 内层循环循环体结束 POP CX ;恢复外层循环计数器 LOOP LOOP1 ;“遍数”未满,转LOOP1继续 ;========= 外层循环循环体结束 =============== ;********* 输出排序后的数组P ************** LEA DX, MESS4 MOV AH, 9 INT 21H ;输出提示信息 MOV BX, 0 MOV CX, N ;输出循环准备:BX=位移,CX=循环次数

112 OUTPUT: MOV DX, 0FFFFH MOV AX, P[BX] ;取出一个排序后的数组元素 CALL WRITEINT ;交“WRITEINT”输出 INC BX INC BX ;修改指针 LOOP OUTPUT ;输出未完成,转OUTPUT继续 CALL CRLF ;结束本行 MOV AX, 4C00H INT 21H CODE ENDS END START

113 该程序汇编,连接,运行(带下划线的内容从键盘输入):
D:\TASM5>TASM SORT Assembling: sort.asm Error messages: none Warning messages: None Passes: 1 Remaining memory: 452k D:\TASM5>TLINK SORT, , , YLIB16 Turbo Link Version Copyright © 1987,1996 Borland International Warning: No stack

114 D:\TASM5>SORT Input Number of Elements in Array P (1~10): 5 Input Values of Elements in Array P: NO NO NO NO. 4 8 NO. 5 5 Array P After Sort: 5   

115 [例4-18] 从键盘上输入一个无符号字整数,分解出它的所有质因数,输出在显示器上。例如: 17=1*17 24=1*2*2*2*3 ……
解决这个问题的基本思路: 将键盘输入的数据被2除,如果整除,输出“*2”,将商继续被2 除,直到不能被2整除。 将余下的商被3除,如果整除,输出“*3”,将商继续被3除,直 到不能被3整除。 重复以上的过程,直到商为1。

116

117 INCLUDE YLIB.H DATA SEGMENT MESS1 DB 0DH, 0AH, 'Input a Number ( =0, Exit ): $' MESS2 DB '= 1$' DATA ENDS CODE SEGMENT ASSUME CS: CODE, DS: DATA START: MOV AX, DATA MOV DS, AX

118 ONE: LEA DX, MESS1 ;从键盘输入一个无符号数
CALL READDEC CMP AX, 0 JZ EXIT ;如果输入数据为0,转EXIT结束程序  PUSH AX ;为了输出,保存AX中的输入数据 MOV DX, 0FFFFH CALL WRITEDEC ;输出这个数 LEA DX, MESS2 MOV AH, 9 INT 21H ;输出“=1”  MOV CX, 2 ;准备分解因数,预设除数CX=2 POP AX ;把输入数据从堆栈中弹出,恢复AX

119 TWO: CMP AX, 1 ;已分解结束(AX=1)?
JE ONE ;分解结束,转ONE MOV DX, 0 ;通过除法试探是否含有该因数 PUSH AX ;除法进行之前保存AX的值 DIV CX CMP DX, 0 ;是否整除? JNZ THREE ;不含有该因数,转THREE POP SI ;整除,含有该因数,废弃堆栈里的数据 PUSH AX ;把除法的商置入堆栈保护 MOV DL, '*' ;输出“*” MOV AH, 2 INT 21H MOV AX, CX ;输出该因数

120 MOV DX, 0FFFFH CALL WRITEDEC POP AX ;恢复堆栈中保存的商 JMP TWO ;转TWO,继续试探分解同一个因数  THREE:POP AX ;试探不成功,从堆栈恢复原数据 INC CX ;除数加1,试探下一个因数 JMP TWO ;转TWO继续分解 EXIT: CALL CRLF ;处理结束,输出一个空行 MOV AX, 4C00H INT H CODE ENDS END START

121 4.4 程序的调试 程序调试的基本过程 语法错误的调试 程序测试 程序逻辑错误的调试

122 程序调试是为了找出程序中的错误,而不是着眼于证明程序是正确的。

123 4.4.1 程序调试的基本过程 源程序书写、输入可能发生错误(文字/语法错误);
程序调试的基本过程 导致程序出错的原因 源程序书写、输入可能发生错误(文字/语法错误); 对指令的功能,汇编语言中各种语句的作用理解有误,导致不正确地使用(语义错误); 对待求解问题的算法,特别是复杂问题的算法,有一个从“必然王国”到“自由王国”,逐步深入的认识过程(算法错误)。

124 (1)通过汇编、连接,发现并改正源程序中的语法错误。
中、小规模的程序的调试,可以分为如下三个步骤: (1)通过汇编、连接,发现并改正源程序中的语法错误。 (2)程序测试:运行这个程序,输入几组代表性数据,观察它的输出结果或程序产生的动作,找出它在功能上的错误。这一步仅仅是发现错误,还不能立即纠正错误。 (3)程序调试:针对已经发现的错误,通过多种方法,找出导致产生错误的原因,并改正这些错误。 上面的三个步骤可能需要重复多次,直到找不到新的错误。

125 4.4.2 语法错误的调试 汇编阶段发生的错误: 源程序输入错误 拼错保留字 阅读和分析出错信息 注意成对出现语句的配套检查
语法错误的调试 汇编阶段发生的错误: 源程序输入错误 拼错保留字 阅读和分析出错信息 注意成对出现语句的配套检查 错误信息过多时,产生列表文件 2. 连接阶段发生的错误: 模块之间的相互引用

126 4.4.3 程序测试 程序测试:运行这个程序,按照预定的方案,有目的地进行 典型数据的输入。观察程序的输出,判断程序能否完 成预定的功能。
程序测试 程序测试:运行这个程序,按照预定的方案,有目的地进行 典型数据的输入。观察程序的输出,判断程序能否完 成预定的功能。 程序测试阶段的效率取决于测试数据的选取,也就是测试方案的制定。

127 程序测试划分为两种类型: 正确性测试:输入正确范围内的数据,将输出结果与人工计算得到的正确结果进行比较,判断程序完成预定功能的正确性; 健壮性测试: 对程序输入“不正确”的数据,不应该导致程序“死循环”等其它不正确运行状态。 一个“健壮”的程序应能辨别输入数据的正确性,对“不正确”的输入数据不应产生貌似“正确”的结果。

128 测试数据应具有完整性,代表性。 测试数据的选取: 每一个“等价类”至少要选取一个(组)数据; 应包含每个“临界点”。

129 以计算分段函数为例: 正确性测试: 三个“等价类”: (-∞, -3), (-3, +3), (+3, +∞) 两个“临界点”:-3、+3。 这个程序至少应进行5组数据的测试: (-∞,-3), (-3, +3), (+3, +∞)三个区间内各一个数据 两个“临界点”数据+3和-3。

130 隐含的“等价类”: 等价类(-3,+3)应进一步划分为(-3,0]和[0,+3),这个划分主要针对X求绝对值算法的正确性。 如果变量X定义为字节变量,等价类(3,+∞)应进一步划分为[4,252]、[254,255]两个等价类和253这个临界点。 对于有符号数U和无符号数V,设[U]补码=254,[V]二进制=254,则有:U<-3,V>-3(因为[-3]补码=253)。如果程序中进行X:-3比较时混淆了两种不同类型数据的不同判断方法,使用X=254或者X=-2进行测试就能发现存在的错误。

131 健壮性测试: (1)超出原规定范围的数据。如原题规定X为字节数据,可以通过输入(-∞,-129),(+128,+∞)之间的数据进行测试。 (2)输入非数字,或者“十六进制”数字进行测试。 (3)输入“空”,即直接输入回车进行测试。

132 程序逻辑错误的调试 静态调试 程序员对照着源程序,人工模仿计算机进行处理。对每一组输入,仿照CPU的处理方法,一条一条指令地“执行”,确定程序执行的路线是否正确,验证每一个阶段的处理结果是否正确。 经验表明,静态调试可以发现70%以上的程序错误,是一种效率很高的调试方法,应该加以提倡。而且,通过认真地阅读源程序,还可能发现一些在测试中尚未发现的错误,发现程序中可以进一步“优化”的地方。

133 动态调试 在计算机上运行这个程序。主要着眼于两个方面:
(1)  程序流程正确性:对于一组输入,检查程序是否执行了正确的流程。对于选择结构程序来说,程序是否按照预定的路线执行?对于循环结构程序,程序是否如设计的那样,进入了循环?循环次数是否正确?有无提前退出循环? 测试“程序流程正确性”的主要方法是跟踪程序的执行过程: “单步”执行程序 设置“断点”,分段执行程序

134 在调试程序“TD”中,跟踪命令主要有: F7:单步执行,遇到CALL,INT指令则进入子程序继续调试;

135 (2) 程序处理正确性:对于一组输入,是否进行了正确的计算,进行了正确的响应?
(2) 程序处理正确性:对于一组输入,是否进行了正确的计算,进行了正确的响应? 测试“程序处理正确性”的主要方法是对每一阶段/每条指令的执行结果进行验证,判断这一阶段/这条指令执行的正确性。“执行结果”包括程序/指令执行后相关的寄存器、存储器、标志位的值。

136 [例4-19]假设为计算如下分段函数: 已编制汇编语言源程序如下: INCLUDE YLIB.H ; 1 ; 2
DATA SEGMENT ; 3 MESS1 DB DH, 0AH,'Input a number X: $' ; 4 MESS2 DB DH, 0AH,'The value of Y = $' ; 5 DATA ENDS ; ; 7

137 CODE SEGMENT ; 8 ASSUME CS: CODE,DS: DATA ; 9 START:MOV AX, DATA ; 10 MOV DS, AX ; 11 LEA DX, MESS1 ; 12 CALL READINT ; 13 CMP AX, 3 ; 14 JAE GREAT ; 15 CMP AX, ; 16 JAE LESS&EQU ; 17 GREAT: MOV AX,6 ; 18 ; 19

138 LESS&EQU:MOV BX, AX ; 20 SHL AX,1 ; 21 ADD AX,BX ; 22 SUB AX, 5 ; 23 DISP:LEA DX, MESS2 ; 24 CALL WRITEINT ; 25 MOV AX, 4C00H ; 26 INT H ; 27 CODE ENDS ; 28 END START ; 29

139 第一次测试的结果: 序号 输入 预定输出值 实际输出值 测试结果 1 4 6 13 错误 2 3 -2 -11 -3 -14 5 -4
  对程序进行跟踪,输入X=4,程序流程为:……,12(√),13(√),14(√),15(√),18(√),20(×)。 执行第18行“MOV AX, 6”之后,对Y的计算已经结束,程序应转向标号“DISP”而不是进入第20行“GREAT”。 将第19行改为“JMP DISP”,对源程序重新汇编连接 。

140 第2次测试结果 序号 输入 预定输出 实际输出 测试结果 1 4 6 正确 2 3 错误 -2 -11 -3 -14 5 -4 再次对程序进行跟踪,输入X=3,程序流程为:……,13(√),14(√),15(√),18(×)。X=3,应归属于|X|≤3一类,进入18行(GREAT)是错误的。 将第15行改为“JG GREAT”,对源程序重新汇编连接。

141 第3次测试结果 序号 输入 预定输出 实际输出 测试结果 1 4 6 正确 2 3 错误 -2 -11 -3 -14 5 -4
输入X=3,程序流程为:……,13(√),14(√),15(√),16(√),17(√),18(×)。X=3,X≯3,进入16,17行是正确的,但X=3>-3,进入18行是错误的。 将第17行改为“JGE LESS&EQU”,对源程序重新汇编连接。

142 第4次测试结果 序号 输入 预定输出 实际输出值 测试结果 1 4 6 正确 2 3 -2 -11 -3 -14 5 -4
至此,对选定的测试数据运行正确。可以初步认为,程序基本能够完成预定的功能。 但是,任何测试方案都难以覆盖所有的输入可能,测试方案也有优劣之分。所以,通过了测试,不等于程序已经完全正确。 可以发现,上述错误大多数都可以通过“静态调试”来纠正。

143 习 题 四 4.1 什么是“三种基本结构”?解释“基本”两个字在其中的含义。
4.2 什么叫做“控制转移指令”?它和数据传送、运算指令有什么 区别?它是怎样实现它的功能的? 4.3 指令“JMP DI”和“JMP WOR PTR [DI]”作用有什么不同? 请说明。 4.4 什么是“近程”转移?什么是“远程”转移?它们的实现方法有 什么不同? 4.5 已知(AX)= 836BH,分别执行“CMP AX, X”后,标志 位ZF、CF、OF、SF各是什么? (1)X=3000H (2)X=8000H (3)X=7FFFFH (4)X=0FFFFH (5)X=0

144 4.6 已知(AX)= 836BH,分别执行“TEST AX, X”后,标志位 ZF、CF、OF、SF各是什么?
(1)X=0001H (2)X=8000H (3)X=0007H (4)X=0FFFFH (5)X=0 4.7 要求测试名为X的一个字节,如果X的第1,3位均为1,转移 到L1,如果只有一位为1,转移到L2,如果两位全为0,转 移到L3。写出对应的指令序列。 4.8 假设X和X+2字单元存放有双精度数P,Y和Y+2字单元存 放有双精度数Q,下面程序完成了什么工作? MOV DX, X+2 MOV AX, X ADD AX, X ADC DX, X+2

145 CMP DX, Y+2 JL L2 JG L1 CMP AX, Y JBE L2 L1: MOV Z, 1 JMP SHORT EXIT L2: MOV Z, 2 EXIT: …… 4.9 编写指令序列,将AX和BX中较大的绝对值存入AX,较 小的绝对值存入BX。

146 4.10 编写指令序列,比较AX、BX中的数的绝对值,绝对值较 大的数存入AX,绝对值较小的数存入BX。
4.11 编写指令序列,如果AL寄存器存放的是小写字母,把它 转换成大写字母,否则不改变AL内容。 4.12 计算分段函数: X的值从键盘输入,Y的值送显示器输出。 4.13 计算分段函数: A,B的值从键盘输入,Y的值送显示器输出(∧表示“并且”, ∨表示“或者”)。

147 4.14 编写程序,求10元素字数组LIST中绝对值最小的数,存入 MIN单元。
4.15 编写程序,求20元素无符号字数组ARRAY中最小的奇数, 存入ODD单元,如果不存在奇数,将ODD单元清零。 4.16 一个有符号字数组以0为结束标志,求这个数组的:最大值、 最小值、平均值。 4.17 数组SCORE中存有一个班级40名学生的英语课程成绩。按 照0~59,60~74,75~84,85~100统计各分数段人数,存入N0, N1, N2, N3变量内。 4.18 STRING是一个16个字符组成的字符串,RULE是一个字整 数。编写程序,测试STRING中的每一个字符,如果该字符 为数字字符,把RULE中对应位置1,否则置0。 4.19 S_ARRAY是一个5个字符串组成的字符串数组,每个字符串 由16个字符组成,S_RULE是一个5个元素的字数组。编写程 序,按照4.18题的规则,用S_RULE数组记录S_ARRAY数组 的特征。

148 4.20 编写程序,从键盘上输入一个无符号字整数,用“四进制” 格式输出它的值(也就是,每2位二进制看作一位四进制 数,使用数字0~3)。
4.21 编写程序,把一个30个元素的有符号字数组ARRAY按照 各元素的正负分别送入数组P和M,正数和零元素送P数组, 负数送M数组。 4.22 缓冲区BUFFER中存放有字符串,以00H为结束标志。编 写程序,把字符串中的大写字母转换成小写字母。 4.23 编写程序,从键盘上输入无符号字整数X,Y的值,进行 X+Y的运算,然后按以下格式显示运算结果和运算后对应 标志位的状态。 SUM=XXXX ZF=X,OF=X,SF=X,CF=X 4.24 编写程序,从键盘上输入一个字符串,统计其中数字字符, 小写字母,大写字母,空格的个数并显示。

149 4.25 编写程序,从键盘输入一个无符号字整数,判断它是否为 素数,输出判断结果。
4.26 编写程序,按学号(1~40)输入一个班的汇编语言考试成 绩,统计每个学生成绩在班内的排名,按学号顺序输出这 个排名。(提示:排名等于成绩高于他/她的人数加1) 4.27 编写程序,读入20个数据,统计每个相同数据出现的次数。 4.28 编写程序,打印九九乘法表。 4.29编写程序,显示1000以内的所有素数。 4.30 编写程序,输入N,计算:S=1*2+2*3+……+(N-1)*N 4.31 编写程序,输入N,输出如下矩阵(设N=5)


Download ppt "汇编语言程序设计 简明教程."

Similar presentations


Ads by Google