一、 实验目的与要求
学习用汇编语言
与编写子程序。
题目:查找电话号码phone
二、 实验内容
(1) 要求程序建立一个可存放50项的电话号码
,每项包括人名(20个字符)及电
话号码(8个字符)两部分;
(2) 程序可接收输入人名及相应的电话号码,并把它们加入电话号码表中;
(3) 凡有新的输入后,程序应按人名对电话号码表重新排序;
(4) 程序可接收需要查找电话号码的人名,并从电话号码表中查出其电话号码,
再在屏幕上以如下格式显示出来。
name tel.
X X X X X X X X
三、 实验步骤
1. 设计分析
1) 根据题目要求必须要定义的空间:
✧ 可存50个联系人的号码表
✧ 姓名缓冲区
✧ 号码缓冲区
✧ 各种提示字符串
2) 为操作方便,多定义以下空间:
✧ tab_len用来记录联系人数目
✧ endaddr 用来记录最后一个联系人的地址+28
✧ temp 一个联系人记录的大小,用于排序时的缓存数据
2.
程序执行流程图
3. 模块层次图
4. 模块说明
模块名
输入
输出
功能
main总控制模块
用户的各种操作选择
如选择是否继续插入,继续查询等
各种提示信息,如提示输入姓名、号码、提示是否继续操作
建立联系人号码表,对号码表人名排序,提供查询、显示功能
input_name
联系人的姓名到tname缓冲区
无
读入姓名到tname缓冲区并把不满20位部分补空格
name_search
tname缓冲区的姓名
查找结果放在bx寄存器
在号码表查找tname缓冲区的姓名,找到则bx存放该记录的地址,否则(bx)=-1
crlf
无
回车换行
输出回车换行
stor_name
tname缓冲区的姓名
把tname缓冲区的姓名移动到号码表,对应记录数tab_len增1,endaddr增28
把姓名从缓冲区移动到号码表
inphone
电话号码
把inphone缓冲区的号码移动到号码表中
读入电话号码并转存到号码表中
name_sort
从tab_len取出记录数,从endaddr取出最后一个记录地址。号码表中只有最后一个记录无序,前面记录有序
排序
对号码表进行排序(用插入排序,每次输入一个联系人后进行排序)
print_all
号码表的地址
无
显示号码表中的所有电话号码
printline
要显示的联系人地址存放在bx
无
显示单个联系人的信息
四、 实验总结与体会。
( 先画好程序框图和模块调用图,并在编码中不断修改完善,对把握程序的整体结构很有帮助。
( 把模块设计成子程序在主程序调用,分解了整个程序功能,降低了设计的难度,同时方便调试和修改。
( 程序中添加适当的辅助变量,可以简化操作,如本程序设表长tab_len 和endaddr(电话表最后一个基址)方便排序、插入等操作。
( 程序需要良好的人机交互,应该有各种操作提示和警告信息。
( 程序中输入输出等要有容错处理,要能对各种可能情况作出反应。
( 使用跳转指令时应注意跳转是否超出范围,Jmp指令能够实现无条件远转移,但je,jb等条件转移指令只能段内短转移,如有需要可以跳转到一个地方再用jmp跳转。
( 使用循环结构时不要忘记了结束条件,导致死循环。
( 因为汇编接近机器语言,直接对内存操作,所以脑中要有内存单元,单元地址等的清晰概念和形象,对数据在内存中表示、各种寻址方式、各寄存器的用途、用法都应该相当熟悉。
( 汇编语言没有像其他高级语言的数据类型的概念,数据都是以二进制存储。
( 通过这个综合设计,对寄存器的默认组合、使用,常用指令,各种寻址方式基本能够熟练运用。
( 个人感觉汇编与其他高级语言的操作上的区别:汇编语言把每一步操作都细化。所以写起来比较麻烦些,但只有想不到,没有做不到的,只要想的出来的,汇编应该都可以写出来。
( 程序调试主要是用debug。Debug是个十分好用的工具,通过设置断点,可以调试任意程序任意部分,通过单步执行可以十分清楚各寄存器和地址单元的变化来找出错误。
五、 源程序
data segment
tel_tab db 50 dup( 28 dup(' ')) ; tel_tab电话本空间
tab_len dw 0 ; 已存联系人数目
endaddr dw 0 ; 最后一个联系人的地址+28
tname db 21,?,20 dup(' '),? ; 姓名缓冲区
tphone db 9,?,8 dup(' '),? ; 号码缓冲区
temp db 28 dup(?) ; 一个联系人的临时空间
iname db 13,10,'Input name:',13,10,'$'
iphone db 13,10,'Input a telephone number:',13,10,'$'
go_on db 13,10,'Continue insert?
',13,10,'$'; 提示是否继续插入联系人
sname db 13,10,'Name?',13,10,'$'
name_e db 13,10,13,10,'The name has been in the table! Please input again!',13,10,'$'
text2 db 13,10,'Name Tel. ',13,10,'$'
text3 db 13,10,'The name is not in the telephone table!',13,10,'$'
text4 db 13,10,13,10,'Do you want a telephone number? ',13,10,'$'
data ends
code segment
; ****************************************************************************
; 主程序
; -------------------------------------------------------------------------------------
main proc far
assume cs:code ,ds:data,es:data
start:
push ds ; 保存旧数据用于返回
sub ax,ax
push ax
mov ax,data ; 数据段、附加段初始化
mov ds,ax
mov es,ax
inname: lea dx,iname ; 提示输入姓名
mov ah,09h
int 21h
call input_name ; 调用读入姓名子程序
call name_search ; 调用查找子程序,
cmp bx,-1 ; 如表中不存在该联系人
je stor ; 则跳转到stor
call crlf ; 回车换行
lea dx,name_e ; 否则提示该联系人已在表中,提示重新输入
mov ah,09h
int 21h
jmp inname
stor: call stor_name ; 调用姓名转存子程序,把姓名移动到表中
lea dx,iphone ; 提示输入电话号码
mov ah,09h
int 21h
call inphone ; 调用读入号码子程序
call name_sort ; 排序
call crlf
lea dx,go_on ; 提示是否继续插入
mov ah,09h
int 21h
choice1: mov ah,07 ; 读取用户选择
int 21h
cmp al,'y'
je inname
cmp al,'Y'
je inname
cmp al,'n'
je print_all ; 如选择不插入,则显示所有记录
cmp al,'N'
je print_all
jmp choice1
print_all: call printall ; 显示所有记录
want_search: call crlf
lea dx,text4 ; 提示是否查找号码
mov ah,09
int 21h
call crlf
choice2: mov ah,07 ; 读取用户选择
int 21h
cmp al,'y'
je search ; 如果为y或Y则跳转到查找search
cmp al,'Y'
je search
cmp al,'n' ; 为n或N则退出程序
je exit_m
cmp al,'N'
je exit_m
jmp choice2
search: lea dx,sname ; 提示用户输入要查找的姓名
mov ah,09
int 21h
call input_name ; 读入姓名
call name_search ; 查找
call crlf
call crlf
cmp bx,-1 ; 是否查找到?
je not_find ; (bx)=1 则跳转到未找到not_find
lea dx,text2 ; 找到则输出 'Name Tel. '
mov ah,09
int 21h
call printline ; 显示查找到的联系人
jmp want_search ; 跳转到提示查找
not_find: ; 未找到
call crlf
lea dx,text3 ;输出 'The name is not in the telephone table!'
mov ah,09h
int 21h
jmp want_search
exit_m: ret
printall proc near
;**************************************************************************
;显示所有电话。显示电话表中的所有姓名和号码,查看排序结果是否正确
;----------------------------------------------------------------------------------------------
call crlf
call crlf
lea dx,text2 ; 输出'Name Tel. '
mov ah,09
int 21h
lea bx,tel_tab ; 号码表基址
rept1: call printline ; 显示联系人
add bx,28 ; 求 下一个联系人首地址
cmp bx,endaddr ; 是否到达表尾?
jb rept1 ; 未到达则继续显示
ret
printall endp
;*******************************************************************
;输入姓名子程序:读入姓名到tname缓冲区,并把不满20位的部分
;补上空格(方便查找时的比较)
;----------------------------------------------------------------------------------
input_name proc near
call crlf
noinputn: lea dx,tname ; 姓名缓冲区
mov ah,0ah ; 调用dos 0ah读入字符串功能
int 21h
cmp tname[1],0 ; 如果输入为回车
je noinputn ; 继续等待输入
xor bx,bx
mov bl,tname[1]
mov cx,20
sub cx,bx
set_blank: mov tname[bx+2],20h ; 把不满20位的部分补空格
inc bx
loop set_blank
call crlf
ret
input_name endp
;*****************************************************************
;stor_name,该子程序把tname缓冲区的姓名转存入号码表中
;---------------------------------------------------------------------------------
stor_name proc near
xor cx,cx
mov cl,tname[1] ;字符个数
lea si,tname[2]
mov di,endaddr
cld
rep movsb
inc tab_len ;联系人人数增1
add endaddr,28 ;最后一个联系人地址增28
ret
stor_name endp
;***************************************************************
;获取号码子程序:读入用户输入的号码到tphone缓冲区,然后
;转存入号码表对应位置
;------------------------------------------------------------------------------------
inphone proc near
noinputp: call crlf
lea dx,tphone
mov ah,0ah
int 21h ;调用dos 0a号功能输入字符串
cmp tphone[1],0 ;判断输入是否为回车
je noinputp ;是,则继续等待输入
xor cx,cx
mov cl,tphone[1]
lea si,tphone[2]
mov di,endaddr
sub di,8 ;待插入位置
cld
rep movsb ;移动
ret
inphone endp
;**********************************************************************
;排序子程序(用直接插入排序),对号码表进行按人名从小到大排序
;--------------------------------------------------------------------------------------------------
name_sort proc near
cmp [tab_len],1 ;记录数1,不用排序
je exitn
lea di,tel_tab ;第一个记录地址
mov si,endaddr
sub si,28 ;最后一个记录-待排序记录的地址
next1: mov cx,20
mov ax,si ;暂存两个地址
mov dx,di
cld
repe cmpsb ;查找插入位置
jb insert
mov si,ax
mov di,dx
add di,28 ;比较下一个
cmp di,si ;是否比较完
jb next1 ;没有则继续比较
jmp exitn ;否则排序完成
insert:
mov cx,28
mov si,ax
lea di,temp
rep movsb ;待排序数据放到缓冲区
mov di,ax
next2: mov cx,28
mov si,di
sub si,28
rep movsb ;记录后移
sub di,56
cmp di,dx
ja next2
mov cx,28
mov di,dx
lea si,temp
rep movsb ;插入到待插位置
exitn: ret
name_sort endp
;******************************************************************************
;姓名查找子程序。入口参数为tname缓冲区的人名,用寄存器bx返回结果,找到则返回该
;姓名对应记录的地址,未找到则返回(bx)=-1
;-------------------------------------------------------------------------------------------------------------------
name_search proc near
cmp tab_len,0 ; 记录为0,无法查找
je exit_nofind
lea si,tname[2] ; 待查姓名地址
mov ax,si ; 暂存si
lea bx,tel_tab ; 从第一个记录开始查找
rsearch: mov cx,20
mov di,bx
repe cmpsb
jz exit_n
mov si,ax
add bx,28 ; 查找下一个
cmp bx,endaddr
jb rsearch
exit_nofind: mov bx,-1 ;未找到则(bx)=-1
exit_n: ret
name_search endp
;******************************************************************************
;显示姓名和电话子程序 。待显示的记录的地址保存在bx寄存器。
;-----------------------------------------------------------------------------------------
printline proc near
push ax ;保存ax
call crlf
xor si,si
mov cx,28 ;共28个字符
mov ah,02h ;调用dos 2 号显示字符功能
nextc: mov dl,[bx][si] ;待显示字符
int 21h
inc si ;输出下一个字符
loop nextc
call crlf
pop ax
ret
printline endp
;******************* ***********************************************************
;回车换行子程序
;-----------------------------------------------------------------------------------------
crlf proc near
push ax
push dx
mov ah,02h
mov dl,0dh ; 回车
int 21h
mov dl,0ah ; 换行
int 21h
pop dx
pop ax
ret
crlf endp
;-----------------------------------------------------------------------------------------
main endp ; 主程序结束
code ends ; 代码段结束
end start ; 程序结束