循环程序可以有两种结构形式,如图所示。一种是DO_WHILE结构形式;另一种是DO_UNTIL结构形式。 例5.1 设内存BUFF开始的单元中依次存放着30个8位无符号数,求它们的和并放在SUM单元中,试编写程序。 分析:这是一个求累加的程序。程序如下: MOV SI,BUFF ;设地址指针 MOV CX,30 ;设计数初值 XOR AX,AX ;设累加器初值 AGAIN: ADD AL,[SI] ADC AH,0 INC SI DEC CX JNZ AGAIN ;循环累加 MOV SUM,AX 例5.2 在给定个数的16位数串中,找出大于零、等于零和小于零的个数,并紧跟着原串存放。 分析:这是一个统计问题,须设定三个计数器分别统计三种情况下的结果。程序如下: DATA SEGMENT BUFF DW X1,X2,X3,……,Xn COUNT EQU $-BUFF ;此时,COUNT的值为BUFF所占的字节数 PLUSE DB ? 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 ;设定计数器初值 LEA BX,BUFF AGAIN: CMP WORD PTR[BX],0 JAE PLU ;大于等于0,则转PIU INC AH ;<0,则统计 JMP NEXT PLU: JZ ZER ;=0,则转ZER INC DL ;>0,则统计 JMP NEXT ZER: INC DH ;=0,则统计 NEXT: INC BX INC BX LOOP AGAIN MOV PLUS,DL MOV ZERO,DH MOV MINUS,AH MOV AX,4C00H INT 21H CODE ENDS END BEGIN 例5.3 在ADDR单元中存放着16位数Y的地址,试编写一程序,把Y中1的个数存入COUNT单元中。 分析:这是一个循环统计的工作。采用DO—WHILE结构,做16次循环,每次将最高位移入CF中进行测试,先判断结果是否为0,若为0,则结束;否则统计计数后循环重复。 程序如下: DATA SEGMENT ADDR DW NUMBER NUMBER DW Y COUNT DW ? DATA ENDS PROGRAM SEGMENT MAIN PROC 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 JZ EXIT ;是,则转EXIT JNS SHIFT ;最高位是0,则转SHIFT INC CX ;最高位是1,则统计计数 SHIFT: SHL AX,1 ;处理下一位 JMP REPEAT EXIT: MOV COUNT,CX RET MAIN ENDP PROGRAM ENDS END START 在实际应用中,有些问题较复杂,一重循环不够,必须使用多重循环实现,这些循环是一层套一层的,通常称为循环嵌套。 例5.4 在DS所决定的数据段,从偏移地址BUFFER开始顺序存放100个无符号16位数,现要编写程序将这100个字数据从大到小排序。 分析:排序的方法有很多,在这里,我们采用冒泡法。 程序如下: LEA DI,BUFFER ;DI作为指针,指向要排序的数据 MOV BL,99 ;循环控制初值 NEXT0:MOV SI,DI MOV CL,BL NEXT3:MOV AX,[SI] ;取一个数 ADD SI,2 CMP AX,[SI] ;与下一个数进行比较 JNC NEXT5 ;大于等于时转移 MOV DX,[SI] ;否则,两数交换 MOV [SI-2],DX MOV [SI],AX NEXT5:DEC CL ;控制进行交换的次数 JNZ NEXT3 DEC BL ;修改交换的次数 JNZ NEXT0 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而设置堆栈 push ds sub ax,ax push ax ; 下面是程序的主要部分 mov ch,4 ; 4组二进制数 rotate: mov cl,4 ; 每组4个二进制位 rol bx,cl ; 把bx循环左移4位 mov al,bl ; 暂存bl到al中 and al,0fh ; 仅保留al的低4位 add al,30h ; 转换成ASCII码 cmp al,3ah ; 要显示的数大于9 ? jl printit ; 如果数在0~9之间则显示 add al,7h ; 数在A~F之间则调整 printit: mov dl,al ; 把要显示字符的ASCII码送dl mov ah,2 ; 功能号2送ah int 21h ; DOS功能调用 dec ch ; (ch)-1 jnz rotate ; 4组都处理完?否,循环处理下一组 ret ; 返回DOS main endp ; 主程序main结束 prognam ends ; 代码段结束 end start ; 结束汇编 例5.6 从键盘接收十进制数并存入 BX prognam segment main proc far assume cs:prognam start: push ds sub ax,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
code segment assume cs:code, ds:data
start: mov ax,4c00h int 21h ; 返回DOS code ends end start
例5.8 将正数N插入一个已整序的字数组的正确位置。该数组的首地址和末地址分别为ARRAY_HEAD和ARRAY_END,其中所有数均为正数且已按递增的次序排列。
; 定义数据段 datarea segment x dw ? array_head dw 23,37,49,52,65,78,99 array_end dw 105 n dw 32 datarea ends ; 定义代码段 prognam segment main proc far ; 主程序部分 assume cs:prognam,ds:datarea start: ; 程序从此处开始执行 push ds sub ax,ax push ax mov ax,datarea mov ds,ax ; 程序的主要部分 mov ax,n mov array_head-2,0ffffh ; -1送array_head-2单元 mov si,0 compare: cmp array_end[si],ax jle insert mov bx,array_end[si] mov array_end[si+2],bx sub si,2 jmp short compare insert: mov array_end[si+2],ax ret main endp ; 主程序main结束 prognam ends end start 例5.9设有数组X (,…,) 和Y (,…,) ,编程计算数组Z (,…,) ,其中:
; 定义数据段 datarea segment x dw x1,x2,x3,x4,x5,x6.x7,x8,x9,x10 y dw y1,y2,y3,y4,y5,y6,y7,y8,y9,y10 z dw z1,z2,z3,z4,z5,z6,z7,z8,z9,z10 logic_rule dw 00dch datarea ends ; 定义代码段 prognam segment main proc far assume cs:prognam,ds:datarea start: push ds sub ax, ax push ax mov ax, datarea mov ds, ax mov bx, 0 mov cx, 10 mov dx, logic_rule next: mov ax, x[bx] shr dx, 1 jc subtract add ax, y[bx] jmp short result subtract: sub ax, y[bx] result: mov z[bx], ax add bx, 2 loop next
ret main endp prognam ends end start 这种设置逻辑尺的方法是很常用的。例如,在矩阵运算中,为了跳过操作数为0的计算,经常采用这种方法。又如,把一组数据存入存储器时,如果其中数值为0的元素很多,也可用这种方法设立一个每位表示一个下标的逻辑尺(这样的逻辑尺可能占有几个字,由数组的长度确定。),0元素就可不占有存储单元了。每个标志只占一位,如果要表示的特征数更多,则每个标志可占有几位,而在处理方法上是完全相同的。设立标志位的方法除了如逻辑尺那样可静态地预置外,还可以在程序中动态地修改标志位的值,以达到控制的目的,下例将说明这种方法。 例5.10 试编制一程序:从键盘输入一行字符,要求第一个键入的字符必须是空格符,如不是,则退出程序;如是,则开始接收键入的字符并顺序存放在首地址为BUFFER的缓冲区中(空格符不存入),直到接收到第二个空格符时退出程序。 这一程序要求接收的字符从空格符开始又以空格符结束,因此程序中必须区分所接收的字符是否是第一个字符。为此,设立作为标志的存储单元FLAG。一开始将其置为0,接收第一个字符后可将其置1。整个程序的框图如图所示。
;定义数据段 datarea segment buffer db 80 dup(?) flag db ? datarea ends ;定义代码段 prognam segment main proc far assume cs:prognam,ds:datarea start: push ds sub ax,ax push ax mov ax,datarea mov ds,ax lea bx,buffer mov flag,0 next: mov ah,01 int 21h test flag,01h jnz/jne follow cmp al,20h jnz/jne exit mov flag,1 jmp next follow: cmp al,20h jz/je exit mov [bx],al inc bx jmp next exit: ret main endp prognam ends end start
例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
; 定义数据段 dseg segment n equ 5 ; 数组中数的个数 a dw n dup(?) dseg ends ; 定义代码段 cseg segment main proc far assume cs:cseg, ds:dseg start: mov ax,dseg mov ds,ax mov cx,n dec cx ; 设置count1 loop1: mov di,cx ; 保存count1 mov si, 0 ; 初始化si loop2: mov ax,a[si] cmp ax,a[si+2] ; Ki与Ki+1比较 jle continue ; 如果Ki≤Ki+1,不交换 xchg ax,a[si+2] mov a[si],ax ; 如果Ki>Ki+1,交换 continue: add si,2 ; 修改地址 loop loop2 ; 内循环 mov cx,di ; 恢复count1 loop loop1 ; 外循环 mov ax,4c00h int 21h main endp cseg ends end start |
电工学习网 ( )
GMT+8, 2021-12-6 20:45