当需要重复执行某段程序时,就可以利用循环程序结构。循环结构一般是根据某一条件判断为真或假来确定是否重复执行循环体,条件永真或无条件的重复循环就是逻辑上的死循环(永真循环、无条件循环)。
一、用循环或转移指令实现循环。
专门用于循环控制的指令有LOOP、LOOPE/LOOPZ和LOOPNE/LOOPNZ,从某种意义上讲,他们都是计数循环,即用于循环次数已知或最大循环次数已知的循环控制,且需预先将循环次数或最大尊大循环次数植入CX寄存器。
例26.1:计算1~100数字之和,并将结果存入字变量SUM中。
分析:程序要求SUM=1+2+3+...+99+100,这是一个典型的计数循环,完成100次简单加法,循环开始将被加数清0,加数置1,循环体内完成一次累加,每次的加数递增1。LOOP指令要求循环次数预置给CX,每次循环CX递减1,这样,循环体内加数就可以直接用循环控制变量CX,简化循环体,完成与题目等价的100~1的累加。
.model small
.stack
.data
sum dw ?
.code
.startup
xor ax,ax
mov cx,100
again:
add ax,cx
loop again
mov sum,ax
.exit 0
end
例26.2:确定字变量wordX中为1的最低位数(0~15),并将结果存于变量byteY中;若wordX中没有为1的位,则将-1存入byteY。
分析:对wordX中的16个位,从低向高一次循环测试,第一个为1的位数便是题目所求,设循环的最大次数为16,循环开始前将计位数置-1,循环体内每次对计位数加1后,测试目标最低位,再将目标循环右移1位。由于循环移位指令不影响ZF标志位,故可根据当前ZF的设置情况得到判断:若ZF=0,则测试结果非0,被测位为1,从而找到定位,退出循环;若ZF=1,则被测位不满足要求,应进行下一次循环继续测试目标的次低位。
.model small
.stack 256
.data
wordX dw 56
byteY db ?
.code
.startup
mov ax,wordX
mov cx,16
mov dl,-1
again:
inc dl
test ax,1
ror ax
loope again
je notfound
mov byteY,dl
jmp done
notfound:
mov byteY,-1
done:
.exit 0
end
例26.3:把一个字符串中的所有大写字母改写为小写字母,该字符串以0结尾。
分析:这是一个循环次数不确定的循环程序结构,宜用转移指令决定是否循环结束,循环体判断每个字符,如果是大写字母就转换为小写字母,否则不予处理;循环体中具有分支结构。大小写字母的ASCII码不同之处:大写字母D5=0,而小写字母D5=1。
.model small
.stack 256
.data
string db 'HeLLO,eveRyboDy!',0
.code
.startup
mov bx,offset string
again:
mov al,[bx]
or al,al
jz done
cmp al,'A'
jb next
cmp al,'Z'
ja next
or al,20h
mov [bx],al
next:
inc bx
jmp again
done:
.exit 0
end
例26.4:采用“冒泡法”把一个长度已知的数组元素按从小到大排序。假设数组元素为无符号字节量。
分析:冒泡法是从第一个元素开始,依次对相邻的两个元素进行比较,使前一个元素不大于后一个元素,将所有元素比较完后,最大的元素排到了最后;然后,除掉最后一个元素之外的元素依上次方法再进行比较,得到次大的元素排在后面,如此重复,直至完成就实现元素从小到大排序。可见,这是一个双重循环程序结构。外循环由于循环次数已知,可以用LOOP指令实现,内循环次数每次外循环后减少一次,用DX表示。循环体比较两个元素大小,又是一个分支结构。
.model small
.stack 256
.data
count equ 20
array db 56h,23h,37h,78h,0ffh,0,12h,90h,64h,0b0h
db 78h,80h,34h,1,4,0fh,2ah,46h,32h,42h
.code
.startup
mov cx,count
dec cx
outlp:
mov dx,cx
mov bx,offset array
inlp:
mov al,[bx]
cmp al,[bx+1]
jna next
xchg al,[bx+1]
mov [bx],al
next:
inc bx
dec dx
jnz inlp
loop outlp
.exit 0
end
二、用循环控制伪指令实现循环
用处理器指令实现的循环控制结构非常灵活,但不易读、容易出错,不小心就会将循环和分支混淆,MASM6.x提供用于循环控制结构的流程控制伪指令:.WHILE和.ENDW、.REPEAT和.UNTIL以及.REPEAT和.UNTILCXZ,另外还有.BREAK和.CONTINUE分别表示无条件退出循环和转向循环体开始。利用这些可以形成两种基本循环结构形式:分别是先判断循环条件的WHILE结构和后判断循环条件的UNYIL结构。如下图:
WHILE结构的循环控制伪指令的格式为:
.WHILE 条件表达式 ;条件为真,执行循环体
循环体
.ENDW ;循环体结束
例26.1实现1~100求和,可以编写如下:
xor ax,ax
mov cx,100
.while cx!=0
add ax,cx
dec cx
.endw
mov sum,ax
UNTIL结构的循环控制伪指令的格式为:
.REPEAT ;重复执行循环体
循环体
.UNTIL 条件表达式 ;直到条件为真
例26.1又可编写为如下:
.repeat
add ax,cx
dec cx
.until cx==0
.UNTIL结构还有一种格式:
.REPEAT ;重复执行循环体
循环体
.UNTILCXZ [条件表达式] ;cx←cx-1 ,直到cx=0或条件为真
不带表达式的.repaet/.untilcxz伪指令将汇编成一条loop指令,即重复执行直到CX减1后,CX=0。带有表达式的.repeat /.untilcxz伪指令的循环结束条件是CX减1后等于0或指定的条件为真。.untilcxz伪指令的表达式只能是比较寄存器与寄存器、存储单元和常数,以及存储单元与常数相等(==)或不等(!=)。
例26.5:设array是100个字元素的数组,试计算其中前若干个非负数之和,直到出现第一个负数为止,并将结果存入result单元(不考虑进位和溢出)。
分析:由于已知array中最多有100个非负数,所以可以采用计数循环。循环开始前置循环计数器CX为100,累加和清零。依据题意,循环体内每取一个元素,都要判断是否大于等于0,若是则累加,否则应立即退出整个循环。循环出口后将累加和存入result单元。
.model small
.stack 256
.data
array sword 100 dup(?)
result sword ?
.code
.startup
mov cx,100
xor ax,ax
lea bx,array
.repeat
.if sword ptr [bx] >= 0
add ax,[bx]
.else
.break
.endif
inc bx
inc bx
.untilcxz
mov result,ax
.exit 0
end
相应的LST文件如下:
Microsoft (R) Macro Assembler Version 6.11 04/12/06 17:07:55
l26-5.asm Page 1 - 1
.model small
.stack 256
0000 .data
0000 0064 [ array sword 100 dup(?)
0000
]
00C8 0000 result sword ?
0000 .code
.startup
0000 :
0000 BA ---- R * mov dx, DGROUP
0003 8E DA * mov ds, dx
0005 8C D3 * mov bx, ss
0007 2B DA * sub bx, dx
0009 D1 E3 * shl bx, 001h
000B D1 E3 * shl bx, 001h
000D D1 E3 * shl bx, 001h
000F D1 E3 * shl bx, 001h
0011 FA * cli
0012 8E D2 * mov ss, dx
0014 03 E3 * add sp, bx
0016 FB * sti
0017 B9 0064 mov cx,100
001A 33 C0 xor ax,ax
001C 8D 1E 0000 R lea bx,array
.repeat
0020 :
.if sword ptr [bx] >= 0
0020 83 3F 00 * cmp sword ptr [bx], 000h
0023 7C 04 * jl @C0002
0025 03 07 add ax,[bx]
.else
0027 EB 02 * jmp @C0004
0029 :
.break
0029 EB 04 * jmp @C0006
.endif
002B :
002B 43 inc bx
002C 43 inc bx
.untilcxz
002D E2 F1 * loop @C0001
002F :
002F A3 00C8 R mov result,ax
.exit 0
0032 B8 4C00 * mov ax, 04C00h
0035 CD 21 * int 021h
end
Microsoft (R) Macro Assembler Version 6.11 04/12/06 17:07:55
l26-5.asm Symbols 2 - 1
Segments and Groups:
N a m e Size Length Align Combine Class
DGROUP . . . . . . . . . . . . . GROUP
_DATA . . . . . . . . . . . . . 16 Bit 00CA Word Public 'DATA'
STACK . . . . . . . . . . . . . 16 Bit 0100 Para Stack 'STACK'
_TEXT . . . . . . . . . . . . . 16 Bit 0037 Word Public 'CODE'
Symbols:
N a m e Type Value Attr
@CodeSize . . . . . . . . . . . Number 0000h
@DataSize . . . . . . . . . . . Number 0000h
@Interface . . . . . . . . . . . Number 0000h
@Model . . . . . . . . . . . . . Number 0002h
@Startup . . . . . . . . . . . . L Near 0000 _TEXT
@code . . . . . . . . . . . . . Text _TEXT
@data . . . . . . . . . . . . . Text DGROUP
@fardata? . . . . . . . . . . . Text FAR_BSS
@fardata . . . . . . . . . . . . Text FAR_DATA
@stack . . . . . . . . . . . . . Text DGROUP
array . . . . . . . . . . . . . Word 0000 _DATA
result . . . . . . . . . . . . . Word 00C8 _DATA
0 Warnings
0 Errors