安防之家讯:循环程序可以有两种结构形式,如图所示。一种是DO_WHILE结构形式;另一种是DO_UNTIL结构形式。
例5.1设内存BUFF开始的单元中依次存放着30个8位无符号数,求它们的和并放在SUM单元中,试编写程序。
分析:这是一个求累加的程序。程序如下:
MOVSI,BUFF;设地址指针
MOV CX,30;设计数初值
XOR AX,AX ;设累加器初值
AGAIN: ADD AL,[SI]
ADC AH,0
INCSI
DEC CX
JNZAGAIN ;循环累加
MOV SUM,AX
例5.2在给定个数的16位数串中,找出大于零、等于零和小于零的个数,并紧跟着原串存放。
分析:这是一个统计问题,须设定三个计数器分别统计三种情况下的结果。程序如下:
DATA SEGMENT
BUFF DWX1,X2,X3,……,Xn
COUNTEQU$-BUFF ;此时,COUNT的值为BUFF所占的字节数
PLUSEDB ?
ZERO DB ?
MINUS DB ?
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
ASSUME ES:DATA,SS:STACK
BEGIN: MOV AX,DATA
MOV DS,AX
MOV CX,COUNT
SHR CX,1 ;相当于除2,正好为BUFF中的数据个数
MOV DX,0;设定计数器初值
MOV AX,0;设定计数器初值
LEABX,BUFF
AGAIN: CMPWORD PTR[BX],0
JAEPLU;大于等于0,则转PIU
INCAH ;<0,则统计
JMPNEXT
PLU: JZZER ;=0,则转ZER
INCDL;>0,则统计
JMPNEXT
ZER:INCDH;=0,则统计
NEXT:INCBX
INCBX
LOOP AGAIN
MOV PLUS,DL
MOV ZERO,DH
MOV MINUS,AH
MOV AX,4C00H
INT21H
CODEENDS
END BEGIN
例5.3 在ADDR单元中存放着16位数Y的地址,试编写一程序,把Y中1的个数存入COUNT单元中。
分析:这是一个循环统计的工作。采用DO—WHILE结构,做16次循环,每次将最高位移入CF中进行测试,先判断结果是否为0,若为0,则结束;否则统计计数后循环重复。
程序如下:
DATA SEGMENT
ADDR DWNUMBER
NUMBER DWY
COUNTDW?
DATAENDS
PROGRAM SEGMENT
MAINPROC FAR
ASSUME CS:PROGRAM,DS:DATA
START: PUSH DS
MOV AX,0
PUSH AX
MOV AX,DATA
MOV DS,AX
MOV CX,0 ;计数器初值=0
MOV BX,ADDR
MOV AX,[BX] ;取Y送AX
REPEAT: TEST AX,0FFFFH;检测是否为全0
JZEXIT ;是,则转EXIT
JNSSHIFT;最高位是0,则转SHIFT
INCCX ;最高位是1,则统计计数
SHIFT:SHLAX,1 ;处理下一位
JMPREPEAT
EXIT:MOV COUNT,CX
RET
MAINENDP
PROGRAM ENDS
END START
在实际应用中,有些问题较复杂,一重循环不够,必须使用多重循环实现,这些循环是一层套一层的,通常称为循环嵌套。
例5.4 在DS所决定的数据段,从偏移地址BUFFER开始顺序存放100个无符号16位数,现要编写程序将这100个字数据从大到小排序。
分析:排序的方法有很多,在这里,我们采用冒泡法。
程序如下:
LEADI,BUFFER ;DI作为指针,指向要排序的数据
MOV BL,99 ;循环控制初值
NEXT0:MOV SI,DI
MOV CL,BL
NEXT3:MOV AX,[SI];取一个数
ADD SI,2
CMP AX,[SI];与下一个数进行比较
JNCNEXT5;大于等于时转移
MOV DX,[SI];否则,两数交换
MOV [SI-2],DX
MOV [SI],AX
NEXT5:DEC CL ;控制进行交换的次数
JNZNEXT3
DEC BL ;修改交换的次数
JNZNEXT0
HLT
例5.5试编制一个程序,把BX寄存器中的二进制数以十六进制的形式显示在屏幕上。
解析:根据题目要求应将BX中的内容从左到右每4位一组显示出来,共显示4个十六进制数位。如果显示的数位是0~9,则把4位二进制数加上30H,转换成相应的ASCII码30H~39H;如果是A~F,则应加上37H(30H+7),转换成ASCII码41H~46H。显示字符可以使用DOS功能调用来实现。下图是程序框图。
以binihex.asm为文件名,建立源程序如下:
; binihex.Asm
prognam segment ; 定义代码段
main proc far
assume cs:prognam
start:; 程序从此处开始执行
; 为正常返回DOS而设置堆栈
pushds
sub ax,ax
pushax
; 下面是程序的主要部分
movch,4 ; 4组二进制数
rotate:
movcl,4; 每组4个二进制位
rolbx,cl; 把bx循环左移4位
moval,bl; 暂存bl到al中
andal,0fh; 仅保留al的低4位
addal,30h ; 转换成ASCII码
cmpal,3ah ; 要显示的数大于9 ?
jl printit ; 如果数在0~9之间则显示
addal,7h; 数在A~F之间则调整
printit:
movdl,al; 把要显示字符的ASCII码送dl
movah,2 ; 功能号2送ah
int21h ; DOS功能调用
decch; (ch)-1
jnzrotate; 4组都处理完?否,循环处理下一组
ret; 返回DOS
main endp; 主程序main结束
prognamends; 代码段结束
endstart; 结束汇编
例5.6 从键盘接收十进制数并存入 BX
prognam segment
main proc far
assume cs:prognam
start:
push ds
subax,ax
push ax
mov bx, 0
newchar:
mov ah, 1;键盘输入
int 21h
sub al, 30h
jl exit;<0退出
cmp al, 9
jg exit;>9退出
cbw
例5.7 从键盘接收十六进制数并存入 BX
codesegment
assume cs:code, ds:data
start:
mov ax,4c00h
int 21h; 返回DOS
codeends
endstart
例5.8 将正数N插入一个已整序的字数组的正确位置。该数组的首地址和末地址分别为ARRAY_HEAD和ARRAY_END,其中所有数均为正数且已按递增的次序排列。
; 定义数据段
datarea segment
xdw?
array_headdw23,37,49,52,65,78,99
array_enddw105
n dw32
datarea ends
; 定义代码段
prognam segment
mainprocfar; 主程序部分 assume cs:prognam,ds:datarea
start: ; 程序从此处开始执行
pushds
subax,ax
pushax
movax,datarea
movds,ax
; 程序的主要部分
movax,n
movarray_head-2,0ffffh; -1送array_head-2单元
movsi,0
compare:
cmparray_end[si],ax
jleinsert
movbx,array_end[si]
movarray_end[si+2],bx
subsi,2
jmpshort compare
insert:
movarray_end[si+2],ax
ret
mainendp; 主程序main结束
prognam ends
endstart
例5.9设有数组X (,…,) 和Y (,…,) ,编程计算数组Z (,…,) ,其中:
; 定义数据段
datarea segment
x dwx1,x2,x3,x4,x5,x6.x7,x8,x9,x10
ydwy1,y2,y3,y4,y5,y6,y7,y8,y9,y10
zdwz1,z2,z3,z4,z5,z6,z7,z8,z9,z10
logic_rule dw00dch
datarea ends
; 定义代码段
prognam segment
mainprocfar
assume cs:prognam,ds:datarea
start:
pushds
subax, ax
pushax
movax, datarea
movds, ax
movbx, 0
movcx, 10
movdx, logic_rule
next:
movax, x[bx]
shrdx, 1
jcsubtract
addax, y[bx]
jmpshort result
subtract:
subax, y[bx]
result:
movz[bx], ax
addbx, 2
loopnext
ret
mainendp
prognamends
endstart
这种设置逻辑尺的方法是很常用的。例如,在矩阵运算中,为了跳过操作数为0的计算,经常采用这种方法。又如,把一组数据存入存储器时,如果其中数值为0的元素很多,也可用这种方法设立一个每位表示一个下标的逻辑尺(这样的逻辑尺可能占有几个字,由数组的长度确定。),0元素就可不占有存储单元了。每个标志只占一位,如果要表示的特征数更多,则每个标志可占有几位,而在处理方法上是完全相同的。设立标志位的方法除了如逻辑尺那样可静态地预置外,还可以在程序中动态地修改标志位的值,以达到控制的目的,下例将说明这种方法。
例5.10 试编制一程序:从键盘输入一行字符,要求第一个键入的字符必须是空格符,如不是,则退出程序;如是,则开始接收键入的字符并顺序存放在首地址为BUFFER的缓冲区中(空格符不存入),直到接收到第二个空格符时退出程序。
这一程序要求接收的字符从空格符开始又以空格符结束,因此程序中必须区分所接收的字符是否是第一个字符。为此,设立作为标志的存储单元FLAG。一开始将其置为0,接收第一个字符后可将其置1。整个程序的框图如图所示。
;定义数据段
datareasegment
buffer db 80 dup(?)
flag db ?
datareaends
;定义代码段
prognamsegment
main proc far
assume cs:prognam,ds:datarea
start:
push ds
subax,ax
push ax
movax,datarea
movds,ax
leabx,buffer
movflag,0
next:
movah,01
int21h
test flag,01h
jnz/jnefollow
cmpal,20h
jnz/jneexit
movflag,1
jmpnext
follow:
cmpal,20h
jz/jeexit
mov[bx],al
incbx
jmpnext
exit:
ret
mainendp
prognam ends
endstart
例5.11 有一个首地址为A的N字数组,请编制程序使该数组中的数按照从小到大的次序整序。
解析:这里采用起泡排序算法实现数组整序。从第一个数开始依次对相邻两个数Ki和Ki+1进行比较,若Ki≤Ki+1,Ki的位置不动,Ki+1继续和Ki+2比较;若Ki>Ki+1,则两者交换位置,Ki+1(交换前的Ki)继续和Ki+2比较。
(气泡算法,多重循环) 32,85,16,15, 8
; 定义数据段
dsegsegment
n equ5 ; 数组中数的个数
a dwn dup(?)
dseg ends
; 定义代码段
csegsegment
mainprocfar
assume cs:cseg, ds:dseg
start:
movax,dseg
movds,ax
movcx,n
deccx; 设置count1
loop1:
movdi,cx ; 保存count1
movsi, 0; 初始化si
loop2:
movax,a[si]
cmpax,a[si+2]; Ki与Ki+1比较
jlecontinue ; 如果Ki≤Ki+1,不交换
xchg ax,a[si+2]
mova[si],ax ; 如果Ki>Ki+1,交换
continue:
addsi,2; 修改地址
looploop2 ; 内循环
movcx,di ; 恢复count1
looploop1 ; 外循环
movax,4c00h
int21h
mainendp
csegends
endstart
安防之家专注于各种家居的安防,监控,防盗,安防监控,安防器材,安防设备的新闻资讯和O2O电商导购服务,敬请登陆安防之家:http://anfang.jc68.com/