你的位置: 首页 > 通信技术 > 汇编语言

循环程序的设计

2016-10-24 13:52:48 | 人围观 | 评论:

    循环程序可以有两种结构形式,如图所示。一种是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





标签: