📄 汇006.txt
字号:
解:
DATA1 SEGMENT
…
X DW ?
Y DW ?
…
DATA1 ENDS
CODE1 SEGMENT
…
MOV AX, X
CMP AX, 0
JGE case23
ADD AX, 10 ;第一种情况的计算结果
JMP result
case23: CMP AX, 10D
JG case3
MOV BX, 30D
IMUL BX ;第二种情况的计算结果
JMP result
case3: SUB AX, 9 ;第三种情况的计算结果
result: MOV Y, AX ;把计算结果保存到变量Y中
…
CODE1 ENDS
例6.7 把下列C语言的语句改写成等价的汇编语言程序段(不考虑运算过程中的溢出)。
If (a+b > 0 && c%2 == 0) a = 62;
else a = 21;
其中:变量a,b和c都是有符号的整型(int)变量。
解:
DATA1 SEGMENT
…
A DW ?
B DW ?
C DW ?
…
DATA1 ENDS
CODE1 SEGMENT
…
MOV AX, A
ADD AX, B
JLE _ELSE ;ADD指令会改变算术标志位
TEST C, 1 ;C%2==0,也就是:看C的最低位是否为0
JNZ _ELSE
MOV A, 62D
JMP NEXT
_ELSE: MOV A, 21D
NEXT: …
CODE1 ENDS
例6.8 用地址转移表实现下列C语言的switch语句,其中:变量A和B是有符号的整型(int)变量。
switch (a%8)
{case 0: b = 32;
break;
case 1:
case 2: b = a + 43;
break;
case 3: b = 2*a;
break;
case 4: b--;
break;
case 5:
case 6:
case 7: printf(“Function 5_6_7”);
break;
}
解:
DATA1 SEGMENT
…
A DW ?
B DW ?
Table DW case0. case12, case12, case3
DW case4, case567, case567, case567
MSG DB 'Function 5_6_7$'
…
DATA1 ENDS
CODE1 SEGMENT
…
MOV AX, A
MOV BX, AX
AND BX, 7 ;得到BX的低三位,实现a%8的计算
SHL BX, 1 ;由于地址表是字类型,其下标要乘2
JMP Table[BX] ;利用地址表实现多路转移
case0: MOV B, 32D
JMP next
case12: ADD AX, 43D
MOV B, AX
JMP next
case3: SHL AX, 1
MOV B, AX
JMP next
case4: DEC B
JMP next
case567: LEA DX, MSG
MOV AH, 9
INT 21H
JMP next
next: …
CODE1 ENDS
用地址表实现多路转移的关键在于:转移入口的地址表和转移情况可整数化。如果这二个要求有一个不满足,或很难构造,则无法使用该方法。
二、用伪指令实现的分支结构
为了改善汇编语言源程序的结构,减少显式转移语句所带来混乱,在宏汇编MASM 6.11系统中,增加了表达分支结构的伪指令。该伪指令的书写格式与高级语言的书写方式相类似,汇编程序在汇编时会自动增加转移指令和相应的标号。理解并掌握该知识,对将来学习《编译原理》课程也有一定的帮助。
分支伪指令的具体格式如下:
格式1:
.IF condition ;以英文“句号”开头
指令序列 ;条件"condition"成立时所执行的指令序列
.ENDIF
格式2:
.IF condition
指令序列1
.ELSE
指令序列2 ;条件"condition"不成立时所执行的指令序列
.ENDIF
格式3:
.IF condition1
指令序列1
.ELSEIF condition2
指令序列2 ;条件"condition2"成立时所执行的指令序列
.ENDIF
其中:条件表达式“condition”的书写方式与C语言中条件表达式的书写方式相似,也可用括号来组成复杂的条件表达式。
条件表达式中可用的操作符有:==(等于)、!=(不等)、>(大于)、>=(大于等于)、<(小于)、<=(小于等于)、&(位操作与)、!(逻辑非)、&&(逻辑与)、||(逻辑或)等。
若在条件表达式中检测标志位的信息,则可以使用的符号名有:CARRY?(相当于CF==1)、OVERFLOW?(OF==1)、PARITY?(PF==1)、SIGN?(SF==1)、ZERO?(ZF==1)等。例如:
.IF CARRY? && AX != BX ;检测CF==1且AX!=BX是否成立
;汇编语言指令序列
.ENDIF
在指令序列中,还可再含有其它的.IF伪指令,即:允许嵌套。伪指令.ELSEIF引导出另一个二叉分支,但它不能作伪指令块的第一个伪指令。
汇编程序在对“条件表达式”进行代码转换时将进行代码优化处理,以便尽可能生成最好的指令代码。如:
.IF ax == 0
汇编程序会把它转换为指令“OR ax, ax”,而不是“CMP ax, 0”,因为前者比后者更好,而不是简单直接地转换为后者。
如果用伪指令来书写分支结构,那么,例6.5的代码段部分就可写成如下程序段:
…
MOV AL, CHAR1
.IF AL>='a' && AL<='z' ;语句象C语言语句吗?
SUB CHAR1, 20H
.ENDIF
…
也可把例6.6的代码段部分就可写成如下程序段:
…
MOV AX, X
.IF AX < 0
ADD AX, 10 ;计算第一种情况的结果
.ELSEIF AX <= 10
MOV BX, 30D
IMUL BX ;计算第二种情况的结果
.ELSE
SUB AX, 9 ;计算第三种情况的结果
.ENDIF
MOV Y, AX
… ;把计算结果保存到变量Y中
例6.9 根据当前计算机的时间和日期,显示上午(AM)或下午(PM),以及所在的季节。
解:
DATA1 SEGMENT
MSG DB "Time: "
AmPm DB "AM", 13, 10
DB "Season: $"
winter DB "Winter$"
spring DB "Spring$"
summer DB "Summer$"
autumn DB "Autumn$"
DATA1 ENDS
CODE1 SEGMENT
…
MOV AH, 2CH ;取当前系统时间
INT 21H
.IF CH >= 12 ;下午时间
MOV AmPm, 'P' ;为显示PM作安排
.ENDIF
MOV DX, OFFSET MSG
MOV AH, 09H
INT 21H ;显示字符串MSG,直到'$'结束
MOV AH, 2AH
INT 21H ;取当前系统日期
.IF (DH == 12) || (DH < 3) ;判断是否为12月,1月和2月
MOV DX, OFFSET winter
.ELSEIF (DH >= 3) && (DH < 6) ;判断是否为3、4和5月
MOV DX, OFFSET spring
.ELSEIF (DH >= 6) && (DH < 9) ;判断是否为6、7和8月
MOV DX, OFFSET summer
.ELSE ;9、10和11月
MOV DX, OFFSET autumn
.ENDIF
MOV AH, 09H ;显示季度名称
INT 21H
…
END
6.2.3 循环结构
循环结构是一个重要的程序结构,它具有重复执行某段程序的功能。通常,循环结构包括以下四个组成部分:
1、循环初始化部分——初始化循环控制变量、循环体所用到变量;
2、循环体部分——循环结构的主体;
3、循环调整部分——循环控制变量的修改、或循环终止条件的检查;
4、循环控制部分——程序执行的控制转移。
以上四部分可以在程序中用各种不同的形式体现出来,有时也并非清析地表达出来。常用的循环结构如图6.3所示。
(a)、Do—While结构 (b)、While结构
图6.3 常用的循环结构示意图
一、用循环指令构成循环结构
在编写循环结构的程序片段时,我们可以多种方法来循环结构。如:循环次数是已知的,可用LOOP指令来构造循环;当循环次数是未知或不定的,则可用条件转移或无条件转移来构成循环结构。
例6.10 分类统计字数组data中正数、负数和零的个数,并分别存入内存字变量Positive、Negative和Zero中,数组元素个数保存在其第一个字中。
解:显示解答
例6.11 计算数组score的平均整数,并存入内存字变量Average中,数组以-1为结束标志。
解:
DATA1 SEGMENT
data DW 90, 95, 54, 65, 36, 78, 66, 0, 99, 50, -1
Average DW 0
DATA1 ENDS
CODE1 SEGMENT
ASSUME CS:CODE1, DS:DATA1
START: MOV AX, DATA1
MOV DS, AX
XOR AX, AX
XOR DX, DX ;用(DX,AX)来保存数组元素之和
XOR CX, CX ;用CX来保存数组元素个数
LEA SI, data ;用指针SI来访问整个数组
again: MOV BX, word ptr [SI]
CMP BX, 0
JL over
ADD AX, BX
ADC DX, 0 ;把当前数组元素之值加到(DX,AX)中
INC CX ;数组元素个数加1
ADD SI, 2
JMP again
over: JCXZ exit ;防止零作除数,即数组是空数组
DIV CX
MOV Average, AX
exit: MOV AX, 4C00H
INT 21H
CODE1 ENDS
END START
二、用伪指令实现的循环结构
在宏汇编MASM 6.11系统中,还增加了表达循环结构的伪指令,以便更清晰地表达WHILE循环、REPEAT-UNTIL循环。另外,还增加两个辅助循环的伪指令。这些伪指令的书写格式和含义与高级语言中相应语句的书写格式和含义相一致,所以,这些伪指令是很容易掌握的,也是非常有用的。
循环伪指令的格式和含义如下:
1、WHILE型循环伪指令
.WHILE condition
循环体的指令序列 ;条件"condition”成立时所执行的指令序列
.ENDW
其中:.ENDW与前面的.WHILE相匹配,它标志着其循环体到此结束。
如果条件表达式“condition”在循环开始时,就为“假”(false),那么,该循环体一次也不会被执行。
2、REPEAT型循环伪指令
.REPEAT
循环体的指令序列
.UNTIL condition .REPEAT
循环体的指令序列
.UNTILCXZ [condition]
REPEAT型循环在执行完循环体后,才判定逻辑表达式condition的值。若该表达式的值为真,则终止该循环,并将执行伪指令.UNTIL[CXZ]后面的指令,否则,将向上跳转到伪指令.REPEAT之后的指令,为继续执行其循环体作准备。
如果.UNTILCXZ后面没有写逻辑表达式,那么,由.REPEAT-.UNTILCXZ所构成的循环与用LOOP指令所过程的循环是一致的,它们都是以“CX=0”为循环终止条件。
如果.UNTILCXZ后面书写了逻辑表达式,那么,该逻辑表达式的形式只能是:“EXP1==EXP2”或“EXP1!=EXP2”。所以,这时由“.REPEAT-.UNTILCXZ condition”所构成的循环就与用LOOPNE/LOOPE指令所过程的循环是一致的,它们都是以“condition || CX=0”为循环终止条件。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -