第一章 基础知识第六章 循环与分支程序设计
1)分析题意,确定算法 。 这一步是能否编制出高质量程序的关键,因此不应该一拿到题目就急于写程序,而是应该仔细地分析和理解题意,找出合理的算法及适当的数据结构 。
2)根据算法画出程序框图 。 这一点对初学者特别重要,这样做可以减少出错的可能性 。 画框图时可以从粗到细把算法逐步地具体化 。
3)根据框图编写程序 。
4)上机调试程序 。 任何程序必须经过调试才能检查出你的设计思想是否正确以及你的程 序是否符合你的设汁思想 。 在调试程序的过程中应该善于利用机器提供的调试工具 ( 如 DEBUG) 来进行工作,你会发现它会给你提供很大的帮助 。
分析问题寻找解决问题的思路、方法、算法描述算法编写程序实现算法设计测试数据和预期计算结果上机调试程序程序设计的解题基本步骤如下:
顺序,分支,循环 程序和 子程序 的设计是汇编语言程序设计的基本内容。
在此基础上还要掌握汇编语言程序设计的基本方法和技巧,包括 递归子程序 设计,COM和 EXE
格式程序 的结构和特点,多模块程序 设计方法等。
一、汇编语言程序设计的基本方法概述概述二、程序的基本结构
1、顺序结构程序
2、分支结构程序
3、循环(重复)结构程序顺序结构 分支结构 循环(重复)结构第一节 顺序结构程序设计
顺序结构程序完全按指令书写的前后顺序执行每一条指令,是最基本、最常见的程序结构
特点顺序性、结构简单,只适应于简单问题的处理例 1 计算例 2 移位例,X+Y+Z W
stack segment para stack
dw 64h dup(?)
stack ends
data1 segment
X dw 5
Y dw 6
Z dw 7
W dw?
data1 ends
code segment
assume cs:code,
ss:stack,ds:data1
start:mov ax,data1
mov ds,ax
mov ax,X
add ax,Y
adc ax,Z
mov W,ax
mov ah,4ch
int 21h
code ends
end start
例,64位数据移位data segment
var db 21h,43h,65h,87h,78h,56h,34h,12h
data ends
code segment
assume cs:code,ds:data
start,mov ax,data
mov ds,ax
mov al,var[6]
mov var[7],al
mov al,var[5]
mov var[6],al
mov al,var[4]
mov var[5],al
mov al,var[3]
mov var[4],al
图示
mov al,var[2]
mov var[3],al
mov al,var[1]
mov var[2],al
mov al,var[0]
mov var[1],al
mov byte ptr var[0],0
mov ah,4ch
int 21h
code ends
end start
12 34 56 78 87 65 43 21h
34 56 78 87 65 43 21 00h移位后图示例,64位数据移位程序结束的方法程序执行完毕,正常结束要返回 DOS,有两种方法
( 1)使用 PSP中的,INT 20H”指令,实现用户程序结束,返回 DOS
方法,① 将用户程序设置成一个远过程
② 程序开始时,执行如下指令
PUSH DS
MOV AX,0
PUSH AX
③ 用户程序结束时,用 RET指令程序结束的方法
( 2)使用 DOS功能调用的,INT 21H”指令,实现用户程序结束,返回 DOS
方法,在程序结束前,使用 如下指令
MOV AH,4CH
INT 21H
注意:一般情况下,使用第 2种方法结束用户程序。
第二节:循环结构结束初始化 循环的初始状态循环体循环的工作部分及修改部分计数控制循环条件控制循环修改部分控制条件Y
N
第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计循环程序设计根据条件重复执行一段指令就构成了循环程序结构 。
MOV AX,0 ;累加求和寄存器清 0
MOV BX,OFFSET ARRAY ;数组始址送 BX中
MOV CX,50 ;循环控制计数初始化
AGAIN,ADD AX,[BX]
ADD BX,2 ;数组地址增 2,BX指向下一个元素
DEC CX ;循环控制计数减 1
JNZ AGAIN ;计数不为 0时继续循环
MOV S,AX ;计数为 0时循环结束,和存于 S中保证循环正常执行和结束的条件:
正确地 初始化 循环控制计数和初始条件
正确地 检测 循环条件
必须保证能 达到循环结束条件 (例如在循环体内有 修改循环控制值 的指令 )
例,将 ARRAY数组中的 50个数求和,并将和存入字变量 S中 。
指令系统提供了专门用于循环结构的 循环控制指令,更加简便的地实现循环结构。
循环控制指令循环控制指令的转移范围为,-128~ +127字节
( 2)等于继续循环指令格式,LOOPZ/LOOPE 标号
( 3)不等于继续循环指令格式,LOOPNZ/LOOPNE 标号
LOOP AGAIN
(段内短转移)
格式,LOOP 标号操作,如图所示
( 1) LOOP指令
(CX)≠ 0?
执行循环体
(CX) (CX) -1
是否退出循环
(CX) ≠ 0 且 ZF =1?) ≠ 0 且 ZF=0?
在前面的求和例子中,可用一条循环指令代替两条指令:
DEC CX
JNZ AGAIN
MOV CX,80
MOV SI,-1
MOV AL,?$?
AGAIN,INC SI
CMP AL,STRS[SI]
LOOPNE AGAIN
MOV LEN,SI
循环程序设计方法控制循环的方法可分为两类:
计数循环 —— 用于循环次数已知的情况条件循环 —— 用于循环次数不确定的情况设,STRS DB?string$?
例,求 存放在 STRS存储区中的 字符串长度。
例,将字变量 num中的数按 16位 二进制数位 分离开,逐位 转换为 ASCII码,并从最高有效位开始依次逐位存入以 binbuf为起始地址的 16个字节单元中 。
设,( num) = 4230H
要求将 4230H= 0 1 0 0 0 0 1 0 0 0 1 1 0 0 0 0 B 逐位分离开并转换为:
30 31 30 30 30 30 31 30 30 30 31 31 30 30 30 30
先将 ( num) ->( DX),再讨论如何分离开各二进制位并转换为 ASCII码,分析如下,
根据上述分析,将 ( DX) 中的二进制数依次 循环左移 1位,
可以使得 各个数位的分离 ( AND) 和 ASCII码转换 ( ADD) 操作完全相同,便于组织循环,并且 循环次数已知 ( 16次 ) 。
例,将 十进制数 ( number) 转换为八进制数方法,( number) /8 保留余数,除法操作 循环次数事先未知,
仅当商为 0时,循环结束 。 此例为条件循环此例为计数循环
next
( DX) = 0 1 0 0 0 0 1 0 0 0 1 1 0 0 0 0
1 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 ( ROL DX,1 )
0 0 0 0 0 0 0 1 ( AND?,1)
0 0 0 0 0 0 0 0
+ 0 0 1 1 0 0 0 0 ( ADD?,30H)
0 0 1 1 0 0 0 0
30
0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 ( ROL DX,1)
0 0 0 0 0 0 0 1 ( AND)
0 0 0 0 0 0 0 1
+ 0 0 1 1 0 0 0 0 ( ADD)
0 0 1 1 0 0 0 1
31
0 1 0 0 0 0 1 0 0 0 1 1 0 0 0 0 ( ROL DX,1)
0 0 0 0 0 0 0 1 ( AND)
0 0 0 0 0 0 0 0
+ 0 0 1 1 0 0 0 0 ( ADD)
0 0 1 1 0 0 0 0
… …
30 31 30 30 30 30 31 30 30 30 31 31 30 30 30
1.
2.
16.
binbuf 返回30
例 统计 AX中的二进制数含,1”的总个数,程序段如下:
MOV CX,0
AGAIN,AND AX,AX ; ( AX) =0?
JZ QUIT ; 是,退出循环
SAL AX,1 ; 否,( AX) 的最高位移入 CF位
JNC NEXT ; CF≠1时,转向 NEXT再次循环
INC CX ; CF=1,计数器 (CX)←(CX) +1
NEXT,JMP AGAIN ; 再次循环
QUIT,?
上述程序为 当型循环 程序结构,先判断条件,后执行循环 。
课堂练习,
采用循环结构完成自然数 1到 100的求和运算。 next
当型循环与直到型循环,
存储模型与简化段定义伪指令较新版本的汇编程序( MASM5.0与 MASM6.0)除支持完整段定义伪指令外,
还提供了一种新的简单易用的存储模型和简化的段定义伪指令。
1,存储模型伪指令存储模型的作用是什么呢?存储模型决定一个程序的规模,
也确定进行子程序调用、指令转移和数据访问的缺省属性
( NEAR或 FAR)。当使用简化段定义的源程序格式时,在段定义语句之前必须有存储模型,MODEL语句,说明在存储器中应如何安放各个段。
MODEL伪指令的常用格式如下:
,MODEL 存储模型存储模型 功 能 适用操作系统
Tiny (微型 ) 所有数据和代码都放在一个段内,其访问都为 NEAR型,整个程序 ≤64K,并会产生,COM文件。 MS-DOS
Small (小型 ) 所有代码在一个 64KB的段内,所有数据在另一个64KB的段内(包括数据段,堆栈段和附加段)。 MS-DOSWindows
Medium (中型 )
所有代码> 64K时可放在多个代码段中,转移或调用可为 FAR型。所有数据限在一个段内,DS可保持不变。
MS-DOS
Windows
Compact(紧凑型 ) 所有代码限在一个段内,转移或调用可为 NEAR型。数据> 64K时,可放在多个段中。 MS-DOSWindows
Large (大型 ) 允许代码段和数据段都可超过 64K,被放置在有多个段内,所以数据和代码都是远访问。 MS-DOSWindows
Huge (巨型 ) 单个数据项可以超过 64K,其它同 Large模型 MS-DOSWindows
Flat (平展型 )
所有代码和数据放置在一个段中,但段地址是 32位的,所以整个程序可为 4GB。 MASM 6.0支持该模型。
OS/2
Windows
NT
在 DOS下用汇编语言编程时,可根据程序的不同特点选择前 6种模型,一般可以选用 SMALL模型。另外,TINY模型将产生 COM程序,其他模型产生 EXE程序。 FLAT模型只能运行在 32位 x86 CPU
上,DOS下不允许使用这种模型。当与高级语言混合编程时,两者的存储模型应当一致
2,简化的段伪指令简化的段定义语句书写简短,语句,CODE,.DATA
和,STACK分别表示代码数据段和堆栈段的开始,一个段的开始自动结束前面一个段。采用简化段指令之前必须有存储模型语句,MODEL。
简化段伪指令 功 能 注释
.CODE [段名 ] 创建一个代码段段名为可选项,如不给出段名,则采用默认段名。对于多个代码段的模型,则应为每个代码段指定段名。
.DATA 创建一个数据段 段名是,_DATA
.DATA? 创建无初值变量的数据 段 段名是,_BSS
.FARDATA [段名 ] 建立有初值的远调用数 据段 可指定段名,如不指定,则将以 FAR_DATA命名。
.FARDATA? [段名 ]
建立无初值的远调用数据段可指定段名,如不指定,则将以
FAR_BSS命名。
.CONST 建立只读的常量数据段 段名是,CONST
.STACK [大小 ] 创建一个堆栈段并指定 堆栈段大小 段名是,stack。如不指定堆栈段大小,则缺省值为 1KB
3.与简化段定义有关的预定义符号汇编程序给出了与简化段定义有关的一组预定义符号,它们可在程序中出现,并由汇编程序识别使用。有关的预定义符号如下:
( 1) @code 由,CODE 伪指令定义的段名或段组名。
( 2) @data 由,DATA 伪指令定义的段名,或由,DATA,,DATA?,.CONST和,STACK所定义的段组名。
( 3) @stack 堆栈段的段名或段组名。
在完整的段定义情况下,在程序的一开始,需要用段名装入数据段寄存器,例
mov ax,data_seg1
mov ds,ax
若用简化段定义,则数据段只用,data来定义,而并未给出段名,此时可用
mov ax,@data
mov ds,ax
这里预定义符号 @data就给出了数据段的段名。
返回参考答案 2:
.model small
.code
mov ax,@data
mov ds,ax
xor ax,ax
mov cx,100
again,add ax,cx
loop again
mov ax,4c00h
int 21h
end
参考答案 1:
.model small
.data
num dw 1
.code
mov ax,@data
mov ds,ax
mov ax,0
mov cx,100
again,add ax,num
inc num
loop again
mov ax,4c00h
int 21h
end
( 下述例子可用 5.X 或 6.X 版汇编程序汇编 )
思考题:显示下面数组中的数据:
ARRAY DB 1,3,7,8,5,4,6,8
第一章 基础知识第六章 循环与分支程序设计循环程序设计方法例 试编制一个程序把 BX寄存器内的二进制数用十六进制数的形式在屏幕上显示出来。
code_seg segment
main proc far
assume cs:code_seg
start:
mov bx,8222h
mov ch,4 ;四位循环一次,共进行
4次循环即可以
rotate:
mov cl,4 ;循环高 4位到低 4位
rol bx,cl;对 BX进行循环操作
mov al,bl;
思路:
四位二进制对应一位十六进制。
循环 4次每次循环 4位高位先显示,rol
判断结果和 10比较。
第一章 基础知识第六章 循环与分支程序设计
and al,0fh;取出要转换的 4位二进制数,存入 al寄存器中
cmp al,10;判断是否为十进制数制的字符
jae changetoa_f ;如果大于 10,跳转到 changetoa_f进行十六进制的转换
add al,30h ;0-9的十六进制转换
jmp disp
changetoa_f:
add al,37h ;a-f的十六进制转换
disp:
mov dl,al ;在屏幕上显示结果
mov ah,02
int 21h
dec ch ;利用 dec 和 jnz来实现循环操作,代替 loop的功能
jnz rotate
第一章 基础知识第六章 循环与分支程序设计
mov ax,4c00h
int 21h
main endp
code_seg ends
end start;程序结束例 在 ADDR单元中存放着数 Y的地址,试编制一程序把 Y中 1的个数存入
COUNT单元中。
算法:要测出 Y中 1的个数就应逐位测试,一个比较简单的办法是可根据最高有效位是否为 1来记数,然后用移位的方法把各位数逐次移到最高位去。循环的结束可以用计数值为 16来控制,但更好的办法是结合上述方法可以用测试数是否为 0来作为结束条件,这样可以在很多情况下缩短程序的执行时间。此外考虑到 Y本身为 0的可能性,应该采用 WHILE— DO的结构形式。
第一章 基础知识第六章 循环与分支程序设计程序框图第一章 基础知识第六章 循环与分支程序设计程序:
data_seg segment
Y equ 020FH
number dw Y
addr dw number
count dw?
data_seg ends
code_seg segment
assume cs:code_seg,ds:data_seg
main proc far
start:
mov ax,data_seg
mov ds,ax
mov cx,0
mov bx,addr
mov ax,[bx]
repeat:
test ax,0ffffh
jz result
jns shift
inc cx
shift:
shl ax,1
jmp repeat
result:
mov count,cx
mov ax,4c00h
第一章 基础知识第六章 循环与分支程序设计
int 21h
main endp
code_seg ends
end start
例 在附加段中有一个首地址为 LIST和未经排序的字数组,在数组的第一个字中存放着该数组的长度,数组的首地址已存放在 DI寄存器中。 AX寄存器中存放着一个数。要求编制一程序:在数组中查找该数,如果找到此数则把它从数组中删除。
算法,这一程序应该首先查找数组中是否有 (AX),如果没有则不对数组作任何处理就结束程序。如果找到这一元素则应把数组中位于其前 (指地址比该元素高 )的元素后移一个字 (即向低 地址方向移动 ),并修改数组长度值。如果找到的元素正好位于数组末尾,则不必移动任何元素,只要修改数组长度值就可以。这里第一部分的查找元素可以使用串处理指令,第二部分的删除元素则可使用循环结构,由于查找结束时就可以知道该元素的位置,因此可以作为循环次数已知的情况来设计。
第一章 基础知识第六章 循环与分支程序设计流程图:
第一章 基础知识第六章 循环与分支程序设计程序,data_seg segment
LIST dw 10,0,11,22,33,44,55,66,77,88,99
data_seg ends
code_seg segment
assume cs:code_seg,es:data_seg
main proc far
start:
mov ax,data_seg;代码段地址空间分配
mov es,ax
cld;设置 DF=0,地址自动递增操作
mov ax,33 ;设置 AX寄存器的数值
lea di,LIST
第一章 基础知识第六章 循环与分支程序设计
push di;将 LIST的首地址压栈,为将来改变数组元素个数,就可以弹出该;地址
mov cx,es:[di];数组个数放入 CX中
add di,2
repnz scasw;将 AX与 ES,[DI]中的元素进行比较,若相等则跳转到 DELETE;结束否则
je delete
pop di
jmp short exit
delete:
jcxz del_last;判断要删除的元素是否为最后的数组元素,是就跳转到; DEL_LAST执行
del_next:
第一章 基础知识第六章 循环与分支程序设计
mov bx,es:[di];数组元素向前移动
mov es:[di-2],bx
add di,2
loop del_next
del_last:
pop di
dec word ptr es:[di];改变数组元素的大小
exit:
mov ax,4c00h
int 21h
main endp
code_seg ends
end start
第一章 基础知识第六章 循环与分支程序设计例 试编制一程序;从键盘输入一行字符,要求第一个键入的字符必须是空格符,如不是则退出程序;如是则开始接收留入的字符并顺序存放在首地址为 BUFFER的缓冲区中(空格符不存人),直到接收到第二个空格符时退出程序。
算法,这一程序要求接收的字符从空格符开始又以空格符结束,
因此程序中必须区分所接收的字符是否是第一个字符,为此设立作为标志的存储单元 FLAG,一开始将其置为 0,接收第一个字符后可将其置 1。
第一章 基础知识第六章 循环与分支程序设计
data_seg segment
buffer db 100 dup(?)
flag db?
data_seg ends
code_seg segment
assume cs:code_seg,ds:data_seg
main proc far
start:
mov ax,data_seg
mov ds,ax
lea bx,buffer
mov flag,0 ; FLAG初始化为 0,准备接收第一个字符
next,mov ah,01
第一章 基础知识第六章 循环与分支程序设计
int 21h ;从键盘输入一个字符,存放在 AL寄存器中
test flag,01h;判断是否为第一个字符
jnz follow;不是第一个字符跳转到 FOLLOW
cmp al,20h;是第一个字符,判断该字符是否为空格
jnz exit
mov flag,1
jmp next
follow:
cmp al,20h;判断是否为空格
jz exit;是空格就退出
mov [bx],al;不是就将该字符的 ASC码存入缓冲区内
inc bx
jmp next
第一章 基础知识第六章 循环与分支程序设计
exit:
mov ax,4c00h
int 21h
main endp
code_seg ends
end start
第一章 基础知识第六章 循环与分支程序设计
( 3)多重循环第一章 基础知识第六章 循环与分支程序设计例 有一个首地址为 A的 N字数组,请编制程序使该数组中的数按照从大到小的次序排序。
算法,我们采用起泡排序算法从第一个数开始依次对相邻两个数进行比较,如次序对则不做任何操作;如次序不对则使这两个数交换位置。下表表示了这种算法的例子,可以看出.在做了第一遍的 (N一 1)次比较后,最小的数已经放到了最后,所以第二遍比较只需要考虑 (N-1)个数,即只需要比较 (N一 2)次,第三遍则只需要做 (N一 3)次比较 …… 总共最多 (N— 1)遍比较就可以完成排序。
第一章 基础知识第六章 循环与分支程序设计
loop1:mov dx,cx
mov bx,0
loop2:mov ax,a[bx]
cmp ax,a[bx+2]
jge continue
xchg ax,a[bx+2]
mov a[bx],ax
continue:
add bx,2
loop loop2
mov cx,dx
loop loop1
mov cx,num
程序:
data_seg segment
num dw 10
a dw 12,14,9,10,3,81,10,4,88,66
data_seg ends
code_seg segment
assume cs:code_seg,ds:data_seg
main proc far
start:
mov ax,data_seg
mov ds,ax
mov cx,num
dec cx
第一章 基础知识第六章 循环与分支程序设计
mov si,offset a
mov bl,0ah
call dispchar
mov bl,0dh
call dispchar
dispdec2:
mov bx,[si]
call disp
mov bl,' '
call dispchar
add si,2
loop dispdec2
mov ax,4c00h
int 21h
main endp
code_seg ends
end start
第一章 基础知识第六章 循环与分支程序设计第二节 分支结构程序设计第一章 基础知识第六章 循环与分支程序设计
( 2) 分支程序设计方法例 在附加段中,有一个按从小到大顺序排列的无符号数的数组,其首地址存放在 Dl寄存器中,数组中的第一个单元存放着数组长度。 在 AX中有一个无符号数,要求在数组中查找 (AX),如找到则使 CF= 0,并在 SI中给出该元素在数组中的偏移地址,如末找到则使 CF= 1。
算法,我们已经遇到过多个表格查找的例子,都是使用顺序查找的方法,本例是一个已经排序的数组,可以采用 折半查找法 以提高查找效率。折半查找法先取有序数组的中间元素与查找值进行比较,如相等则查找成功,如查找值大于中间元素,则再取高半部的中间元意与查找值相比较;如查找值小于中间元素,则再取低半部的中间元素与查找值相比较,如此重复直到查找成功或最终未找到该效 (查找不成功 )为止。折半查找法的效率高于顺序查找法。
第一章 基础知识第六章 循环与分支程序设计流程图,
第一章 基础知识第六章 循环与分支程序设计程序:;本模块功能是用寄存器 AX的内容对字数组 (从小到大顺序排列 )进行折半查找;其中查找的数组首地址位于寄存器 DI内;数组的第一个元素为数组的长度,其余的为数组的具体内容
dseg segment
start_addr dw?; 保存数组首地址用的变量
dseg ends
cseg segment
assume cs:cseg,ds:dseg
b_search proc near
push ds
push ax
mov ax,dseg
mov ds,ax
第一章 基础知识第六章 循环与分支程序设计;第一步;首先判断 AX寄存器的数是否在该数组的范围内,;即要判断是否小于数组元素的最小数或大于数组元素的最大数
cmp ax,es,[di+2];和最小数据进行比较
ja chk_last;若大于最小数,跳转到 CHK_LAST,同时 CF 为 0
lea si,es,[di+2];先把最小元素的地址存入 SI寄存器中
je exit_b;在判断是否和最小元素相等,如果相等结束
stc;否则小于最小元素,设置 CF为 1
jmp exit_b; 程序结束
chk_last:
mov si,ES,[di];si存放数组元素个数
shl si,1;也就是 × 2。 其结果就是数组占用的字节数量
add si,di;SI为数组下标最大的元素的地址第一章 基础知识第六章 循环与分支程序设计
cmp ax,es:[si];和最大数进行比较,同时 CF为 1
jb search;若小于最大数,就进入折半查找
je exit_b;//否则等于最大值则退出结束,表明 AX的数据与最大数组数据相同,
stc;置 CF为 1
jmp exit_b;第一步结束;第二步;折半查找功能
search:
mov start_addr,di;保存寄存器 DI的字数组的首地址
mov si,[di];读取数组长度第一章 基础知识第六章 循环与分支程序设计
even_idx:
test si,1;判断是否为奇数,注意数组长度的地址指定数组中间的元素 (因为数组是字数组 )
jz add_idx
inc si;奇数就要加 1,以便能取到完整的数据
add_idx:
add di,si;数组首地址加上中间数组的偏移量,得到中间数组的有效地址
compare:
cmp ax,es,[di];和寄存器数据进行比较
je all_done;相等,就结束操作,同时 CF为 0
ja highter;大就查找高段数组
cmp si,2
jne idx_ok
第一章 基础知识第六章 循环与分支程序设计
no_match:
stc
je all_done ;寄存器 si和 2进行比较的结果,程序退出的标志
idx_ok:
shr si,1
test si,1
jz sub_idx
inc si
sub_idx:
sub di,si
jmp short compare
第一章 基础知识第六章 循环与分支程序设计
higher:
cmp si,2;
je no_match
shr si,1;数据元素除以 2,减半
jmp short even_idx
all_done
mov si,di
mov di,start_addr
exit_b:
ret
b_search endp
cseg ends
end b_search
第一章 基础知识第六章 循环与分支程序设计
( 3)跳跃表法第一章 基础知识第六章 循环与分支程序设计例:根据 bx的数据进行跳转,从低位到高位分别判断 bx寄存器的位数是否为 1,
为 1跳转,否则继续判断,直到 bx寄存器为 0。
程序:
b_a segment
b_t dw r1
dw r2
dw r3
dw r4
dw r5
dw r6
dw r7
dw r8
b_a ends
code segment
assume cs:code,ds,b_a
main proc far
start:
push ds
xor ax,ax
push ax
mov ax,b_a
mov ds,ax
第一章 基础知识第六章 循环与分支程序设计
lea si,b_t;将表地址存入 si寄存器
l:
cmp bx,0;判断 bx是否为零
jz exit
shr bx,1;将 bx寄存器的最低位传入 CF中
jnb not_yet;若 CF为 0,跳转不执行任何操作
jmp word ptr[si]
not_yet:
add si,type b_t
jmp l
r1,mov dx,'0'
jmp short exit
r2,mov dx,'1'
jmp short exit
r3,mov dx,'2'
jmp short exit
r4,mov dx,'3'
jmp short exit
r5,mov dx,'4'
jmp short exit
r6,mov dx,'5'
jmp short exit
r7,mov dx,'6'
jmp short exit
r8,mov dx,'7'
exit:
ret
main endp
code ends
end start
第一章 基础知识第六章 循环与分支程序设计第三节 子程序结构第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计上述程序也可以采用如下方式:
第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计; SEGX 代码段; SEGY代码段;调用 SEGX代码段的子过程; SUBT必须具有 FAR属性以适应 SEGY段调用的需要。 SUBT既有 PAR属性,则不论在
SEGX和 SEGY段对 SUBT的调用就都具有 FAR
属性了,这样不会发生什么错误,反之,如果这里的 SUBT使用了 NEAR属性,则在 SEGY段内对它的调用就要出错了。
第一章 基础知识第六章 循环与分支程序设计
( 1)子程序使用过程中注意的问题
CALL使返回地址入栈,所以 RET时应该使返回地址出栈,如果子程序中不能正确使用堆栈而造成执行 RET前 SP并未指向进入子程序时的返回地址,则必然会导致运行出错,因此于程序中对堆栈的使用应该特别小心,以免发生错误。
CALL指令和
RET指令就完成了调用和返回的功能。为了保证其正确性,除 PROC
的属性要正确选取外,还应该注意子程序运行期间的堆栈状态。
第一章 基础知识第六章 循环与分支程序设计调用程序 (又称主程序 )和子程序经常是分别编制的,所以它们所使用的寄存器往往 会发生冲突 。
如果主程序在调用子程序以前的某个寄存器内容在从子程序返回后还有用.而子程序又恰好使用了同一个寄存器,造成破坏了该寄存器的原有内容,
那就会造成程序运行错误,
这是不能允许的。
第一章 基础知识第六章 循环与分支程序设计为避免这种错误的产生在一进入子程序后就应该把子程序所需要使用的寄存器内容保存在堆栈中,而在退出子程序前把寄存器的内容恢复原状。
第一章 基础知识第六章 循环与分支程序设计
( 2)主程序与子程序参数传递含义:将 dl中的字符在屏幕上显示出来第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计程序,decihex segment
assume cs:decihex
main proc far
mov cx,10
repeat:
call decibin
call crif
call binihex
call crif
loop repeat
mov ax,4c00h
int 21h
main endp
decibin proc near;十进制转换为二进制数据
mov bx,0;转换结果存放在 BX寄存器中
newchar:
mov ah,1;从键盘读取数据
int 21h
sub al,30h;转换为数制
jl exit;小于 30H不是数字键
cmp al,9d
jg exit;大于 9也不是数字键
cbw
xchg ax,bx
mov cx,10d
mul cx
xchg ax,bx
add bx,ax;加入 BX中
jmp newchar
exit:
ret
decibin endp
第一章 基础知识第六章 循环与分支程序设计
binihex proc near
mov ch,4
rotate:
mov cl,4
rol bx,cl
mov al,bl
and al,0fh
add al,30h
cmp al,3ah
jl print
add al,7h
print:
mov dl,al
mov ah,2
int 21h
dec ch
jnz rotate
ret
binihex endp
crif proc near
mov dl,0dh;换行
mov ah,2
int 21h
mov dl,0ah;
mov ah,2
int 21h
ret
crif endp
decihex ends
end main
远程调用
codeb segment
assume cs:codeb
btodecdisp proc far
push ax
push bx
push cx
push dx
mov cx,0; mov bx,1234h;
rotate1:
mov ax,bx
cwd
mov bx,10
div bx
add dl,30h
push dx
inc cx
mov bx,ax;
mov ax,bx
test ax,0ffffh
jnz rotate1
repeat1:
pop dx
mov ah,2
int 21h
loop repeat1
pop dx
pop cx
pop bx
pop ax
ret
btodecdisp endp
btohexdisp proc far
push ax
push bx
push cx
push dx
mov ch,4
mov cl,4
rotate2,rol bx,cl
mov al,bl
and al,0fh
cmp al,10
jc decimal2
add al,37h
jmp dispp2
decimal2:
add al,30h
dispp2:
mov dl,al
mov ah,2
int 21h
dec ch
jnz rotate2
pop dx
pop cx
pop bx
pop ax
ret
btohexdisp endp
btoo proc far
push ax
push bx
push cx
push dx
mov cl,1
mov ax,bx
loopto:
mov bx,ax
rol bx,cl
cmp cl,1
jz too
and bx,0007h
jmp tooo
too:
and bx,0001h
tooo:
xchg bl,dl
add dl,30h
xchg ax,bx
mov ah,2
int 21h
xchg ax,bx
shl ax,cl
jz exit3
mov cl,3
jmp loopto
exit3:
pop dx
pop cx
pop bx
pop ax
ret
btoo endp
codeb ends
code segment
assume cs:code
main proc far
mov bx,5678H
call btodecdisp
call btohexdisp
call btoo
mov ah,4ch
int 21h
main endp
code ends
end main
第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计例 从键盘输入十进制数据,以十六进制显示在屏幕上文件 asm_0.asm
public xyz
public crlf
data segment public
xyz dw 9
data ends
decihex segment public
assume cs:decihex
crlf proc near
mov dl,0dh
mov ah,2
int 21h
mov dl,0ah
mov ah,2
int 21h
ret
crlf endp
decihex ends
end
第一章 基础知识第六章 循环与分支程序设计文件 2,asm_1.asm
extrn crlf:near
extrn xyz:word
decihex segment public
assume cs:decihex
start:
mov ax,seg xyz
mov ds,ax
mov ax,xyz
call decibin
call crlf
call binihex
call crlf
mov ax,4c00h
int 21h
decibin proc near
mov bx,0
newchar:
mov ah,1
int 21h
sub al,30h
jl exit
cmp al,9d
jg exit
cbw
xchg ax,bx
mov cx,10d
mul cx
xchg ax,bx
add bx,ax
jmp newchar
exit:
ret
decibin endp
binihex proc near
mov ch,4
rotate:
mov cl,4
rol bx,cl
mov al,bl
and al,0fh
add al,30h
cmp al,3ah
jl print
add al,7h
print:
mov dl,al
mov ah,2
int 21h
dec ch
jnz rotate
ret
binihex endp
decihex ends
end start
第一章 基础知识第六章 循环与分支程序设计运行,masm asm_0.asm 形成 asm_0.obj
masm asm_1.asm 形成 asm_1.obj
link asm_0.obj asm_1.obj 形成 asm_0.exe执行文件
link asm_1.obj asm_0.obj 形成 asm_1.exe执行文件第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计
code_seg segment
org 100
table dw 3 dup(?)
ary1 dw 10,20,30,40,50
count1 dw 5
sum1 dw?
ary2 dw 10,20,30,40,50
count2 dw 5
sum2 dw?
assume cs:code_seg,ds:code_seg,ss:code_seg
main proc far
start:
mov ax,code_seg
mov ds,ax
mov ss,ax
mov table,offset ary1
mov table+2,offset count1
mov table+4,offset sum1
call near ptr proadd
mov si,table+4
mov bx,[si]
call disp
mov table,offset ary2
mov table+2,offset count2
mov table+4,offset sum2
call proadd
mov si,table+4
mov bx,[si]
call disp
mov ax,4c00h
int 21h
main endp
第一章 基础知识第六章 循环与分支程序设计;以十进制显示 BX寄存器中的数据
disp proc near
push ax
push dx
push cx
mov cx,0
disp_next:
mov ax,bx
cwd
mov bx,10
div bx
add dl,30h
push dx
inc cl
mov bx,ax
cmp ax,0000h
jz exit
jmp disp_next
exit:
mov ah,02h
print:
pop dx
int 21h
dec cl
jnz print
pop cx
pop dx
pop ax
ret
disp endp;显示模块结束
proadd proc near
push si
push di
push cx
push ax
mov si,table
mov di,table+2
mov cx,[di]
mov di,table+4
xor ax,ax
next:
add ax,[si]
add si,2
loop next
mov [di],ax
pop ax
pop cx
pop di
pop si
ret
proadd endp
code_seg ends
end start
第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计堆栈段最满时的状态:
第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计
<预赋值说明 >可以由一连串常数表达式组成,每个字段用一个表达式,相互之间用逗号隔开,如果使用 STRUC定义中的值则可不写任何值 (缺省 )。 预赋值说明也可以使用 DUP操作符。
第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计程序,data_seg segmentn_v dw 6
result dw?
data_seg ends
stack_seg segment;定义堆栈段
dw 128 dup(?)
tos label word;确定栈顶指针
stack_seg ends
code1_seg segment
assume cs:code1_seg,ss:stack_seg,ds:data_seg
main proc far
start:
mov ax,data_seg
mov ds,ax
mov ax,stack_seg
mov ss,ax
mov sp,offset top
mov bx,offset result
push bx
第一章 基础知识第六章 循环与分支程序设计
mov bx,n_v
push n_v
call far ptr fact
mov bx,result
call near ptr disp
mov ax,4c00h
int 21h
ret
main endp;以十进制显示 BX寄存器中的数据
disp proc near
push ax
push dx
push cx
mov cx,0
disp_next:
mov ax,bx
cwd
mov bx,10
div bx
add dl,30h
push dx
inc cl
mov bx,ax
cmp ax,0000h
jz exit
jmp disp_next
exit:
mov ah,02h
print:
pop dx
int 21h
dec cl
jnz print
pop cx
pop dx
第一章 基础知识第六章 循环与分支程序设计
pop ax
ret
disp endp;显示模块结束
code1_seg ends
code_seg segment
frame struc
save_bp dw?
save_cs_ip dw 2 dup(?)
n dw?
result_addrdw?
frame ends
assume cs:code_seg
fact proc far
push bp
mov bp,sp
push bx
push ax
mov bx,[bp].result_addr
mov ax,[bp].n
push bx
dec ax
je done
push ax
call far ptr fact
mov bx,[bp].result_addr
mov ax,[bx]
mul [bp].n
jmp short return
done,
mov ax,1
return:
mov [bx],ax
pop ax
pop bx
pop bp
ret 4
fact endp
code_seg ends
end start
用汇编语言可以编写两种格式的源程序:
格式一,经汇编、连接后产生,EXE可执行文件格式二,经汇编、连接、转换成,COM可执行文件。
( 1) EXE文件
EXE程序装入内存时的映像如图 4.25所示。
程序段前缀 PSP的结构:
( 256字节) 表 4-4( p116)
next
EXE文件与 COM文件
( 2) COM文件
COM程序装入内存时的映像如图 4.25所示。
( 1)代码段、数据段及堆栈段在同一段,
64KB。
( 4)所有的子过程应定义为 NEAR类型
( 5)直接用 INT 20H退出程序返回 DOS
( 2)第一条指令的偏移地址固定为 100H,
即有( IP) =100H。
( 3) SP指向该物理段末尾,故 不用定义堆栈段 。
第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计第一章 基础知识第六章 循环与分支程序设计