汇编语言循环程序设计

一、循环程序结构

程序中经常要处理有相同规律的大量重复操作,为此把程序设计成循环结构,也称为重复结构,使得一组指令能重复地执行,并根据某个条件是否成立来决定继续循环还是放弃循环。因此循环结构也可以看成是一种特殊的分支。

循环程序可以有两种结构,一种是 DO-WHILE 结构,另一种是 DO-UNTIL 结构。

DO-WHILE 结构是把循环控制条件放在循环的入口,先判断控制条件是否成立,再决定是否进入循环。DO-UNTIL 结构是先执行循环体,然后判断控制条件是否成立,再决定是否进入循环。两种循环结构如图 9 所示。

循环结构

图 9 循环结构

循环程序可以由以下三部分组成:循环初始状态、循环控制和循环体。

循环初始状态:为循环作准备,设置循环初始值,如地址指针和计数器的值。

循环体:重复执行的一段程序,并修改循环控制条件,如修改地址指针、计数器的值。

循环控制:判断循环条件,控制结束循环或继续循环。

如何选择循环控制条件,要根据具体情况,有以下三类循环控制条件。

① 计数循环:循环的次数事先已经知道,用一个变量(寄存器或存储器单元)记录循环的次数(称为循环计数器),可以采用加法或减法计数。进行加法计数时,循环计数器的初值设为 0,每循环一次将它加 1,将它和预定次数比较来决定循环是否结束;进行减法计数时,循环计数器的初值直接设为循环次数,每循环一次将计数器减 1,计数器减为 0 时,循环结束。如果用减法计数时,可以使用 LOOP 指令,该指令自动修改减法计数器 CX 的值,并实现循环控制。循环次数是有限的。

② 条件循环:循环的次数事先无法确定或无需确定,每次循环开始前或结束后测试某个条件,根据这个条件是否满足来决定是否继续下一次循环。这种情况可以使用条件转移指令以实现循环控制。但这种循环有可能出现死循环。

③ 条件计数循环:循环条件有两个因素,即某个条件和最大循环次数。实际循环次数事先并不确定,但循环可能的最大次数是可以确定的。每次循环开始前或结束后测试这两个条件,如果条件都满足则继续下一次循环。但无论怎样,循环的次数不会超过预定的最大次数。这种情况可以采用减法计数,使用 LOOPNE 或 LOOPE 指令,来判断循环条件并自动修改减法计数器 CX 的值以实现循环控制。

以上三类循环其实都是条件循环,因为计数循环中的计数值也是循环的条件,都可以使用条件转移指令实现循环控制。

二、计数循环程序

计数循环是基本的循环组织方式,用循环计数器的值来控制循环。

例 4 把 BX 寄存器中的二进制数用十六进制数格式显示输出。

算法分析:BX 寄存器每 4 位表示一位十六进制数位,从左到右循环移位,每移 4 位,就把要显示的 4 位二进制位移到最右边。取出最右边的 4 位,加上 30H,转换成 8 位 ASCII 字符码。因显示输出的十六进制数是数字(30H~39H)和 A~F(41H~46H),所以当 8 位二进制数大于 39H 时,应再加上 7。程序采用计数循环,计数值为 4。图 10 为程序流程图。

例 4 流程图

图 10 例 4 流程图

例 4 程序如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
code  segment

assume cs:code

start: mov cx,4

shift: rol  bx,1  ; 连续循环左移4位

rol bx,1

rol bx,1

rol bx,1

mov al,bl

and al,0fh     ; 取最右4位

add al,30h     ; 转为ASCII

cmp al,39h

jle dig       ; 是0~9则转dig

add al,7      ; 是A~F

dig:  mov dl,al

mov ah,2

int 21h

loop shift

mov ah,4ch

int 21h

code  ends

end start

该程序因没有对 BX 赋值,初值可能为 0,所以需要在调试状态下先设置 BX 的值再运行程序,如图 11 所示。

例 4 的运行情况

图 11 例 4 的运行情况

三、条件循环程序

在循环程序中,我们已经看到有时候每次循环所做的操作可能不同,即循环体中有分支的情况,需要依据某一个标志来决定做何操作。标志位为 1 表示要做操作 A,标志位为 0 表示要做操作 B,我们可把这种标志字称为逻辑尺。

例 5 先从键盘输入 8 位二进制数作为逻辑尺。再从键盘输入一个英文字母,根据逻辑尺当前的最高位标志显示输出该英文字母的相邻字符,标志位为 0 则显示其前趋字符,标志位为 1 则显示其后继字符。显示相邻字符后,逻辑尺循环左移一位,再接收下一个英文字母的输入,并依据逻辑尺显示相邻字符,直到回车键结束程序。

算法分析:8 位二进制数的输入构成一个 8 次循环,把输入整合到 8 位寄存器BL中。键盘输入一个英文字母后依据逻辑尺的最高位标志显示相邻字符,把最高位移到 CF 位,以 CF 位决定显示,构成一个条件循环,以回车键退出循环。图 12 为程序流程图。

例 5 流程图

图 12 例 5 流程图

例 5 程序如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
code  segment

assume cs:code

start: mov bx,0          ; 初始化

mov cx,8

inlog: mov  ah,1      ; 键盘输入0/1

int 21h

cmp al,30h

jb exit            ; 非法输入

cmp al,31h

ja exit            ; 非法输入

sub al,30h          ; 输入是0/1

shl bl,1

add bl,al

loop inlog

mov ah,2

mov dl,10          ; 输出换行

int 21h

inchr: mov ah,1          ; 键盘输入字母

int 21h

cmp al,13

je exit            ; 回车键

mov dl,al

rol bl,1

jnc k30            ; 是0 则转k30

inc dl

jmp putc

k30:  dec dl

putc:  mov ah,2

int 21h

jmp inchr

exit:  mov ah,4ch     ; 程序终止并退出

int 21h

code  ends

end start

假设输入的逻辑尺为 10101010,程序的运行结果如图 13 所示。

例 5 的运行情况

图 13 例 5 的运行情况

四、条件计数循环程序

例 6 设置键盘缓冲区为 16 个字节,从键盘输入一串字符,然后再从键盘输入一个单个字符,查找这个字符是否在字符串中出现,如果找到,显示该字符串,否则显示“NOT FOUND”。

算法分析:该程序使用DOS系统功能调用(INT 21H)10 号功能实现键盘缓冲区输入,使用 1 号功能实现单个字符输入,使用 9 号功能实现字符串显示输出。定义键盘缓冲区大小为 16 个字节(含回车),缓冲区首字节存放 16,接下来存放实际输入的字符个数(不含回车)和输入的字符。程序采用循环结构实现查找,最大计数值为实际输入的字符个数。图 14 为程序框图。

例 6 程序框图

图 14 例 6 程序框图

例 6 程序如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
data  segment

buffer  db  16,?,16 dup(?),13,10,'$'

inputs  db  13,10,'input string:$'

getc  db  13,10,'input char:$'

output  db  13,10,'not found$'

data  ends

code  segment

assume cs:code,ds:data

start: mov ax,data       ; ds赋值

mov ds,ax

lea dx,inputs      ; 信息提示输入串

mov ah,9

int 21h

lea dx,buffer      ; 键盘输入串到缓冲区

mov ah,10

int 21h

lea dx,getc       ; 信息提示输入字符

mov ah,9

int 21h

mov ah,1         ; 输入字符到al

int 21h

mov bl,al        ; 保存到bl

lea di,buffer+1     ; di作为指针指向缓冲区

mov cl,buffer+1     ; cx设置计数值

mov ch,0

seek:  inc di

cmp bl,[di]

loopne seek       ; 未完且没找到,转seek继续循环

jne nof         ; 没找到,转nof输出’not found'

mov dl,10        ; 输出换行

mov ah,2

int 21h

lea dx,buffer+2     ; 指向缓冲区,输出字符串

mov ah,9

int 21h

jmp exit

nof:  lea dx,output

mov ah,9

int 21h

exit:  mov ah,4ch

int 21h

code  ends

end start

程序中要注意数据段中的缓冲区和各串变量的定义。回车(13D)和换行(10D)是为了显示信息不会被覆盖。缓冲区的初始定义,以及程序运行后缓冲区的存储情况如图 15 所示。程序中的查找也可以用串处理指令实现。

例 6 的运行情况

图 15 例 6 的运行情况

五、多重循环程序

例 7 显示输出 20H~7EH 的 ASCII 字符表。每行 16 个字符。

算法分析:20H~7EH 的 ASCII 字符共有 95 个,需 6 行显示。该程序需两重循环,内循环输出每行 16 个字符,循环计数初值为 16,程序终止条件是显示最后一个字符。这里我们不用流程图而用高级语言程序来描述算法,也可以导出汇编语言程序。

例 7 高级语言程序如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
first=20h

last=7eh

char=first

x=1       ; 行号

y=1       ; 列号

do while char<last

k=16

do  while  k>0 and char<last

@ x,y say char

char=char+1

y=y+1

k=k-1

enddo

x=x+1

y=1

enddo

例 7 汇编语言程序如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
code  segment

assume cs:code

k=16

first=20h

last=7eh

start: mov dx,first       ; 从第一个开始

a10:  mov cx,k         ; 每行16个

a20:  mov ah,2

int 21h

cmp dl,last       ; 是最后一个则退出

je exit

push dx        ; 暂存dx

mov dl,20h     ; 空2格

int 21h

int 21h

pop dx        ; 恢复dx

add dx,1

loop a20       ; 进入内循环

push dx       ; 暂存dx

mov dl,13      ; 回车

int 21h

mov dl,10      ; 换行

int 21h

pop dx        ; 恢复dx

loop a10       ; 进入外循环

exit:  mov ah,4ch

int 21h

code  ends

end  start

程序的运行结果如图 16 所示。

例 7 的运行情况

图 16 例 7 的运行情况

请参阅

(完)

comments powered by Disqus