- 题目要求
本题目是完成一个较为简单的电话本程序,能够实现插入信息(包括姓名和号码),查询信息(分为通过姓名查询和通过号码查询),显示所有联系人信息,数据导出到文本文件,修改背景颜色,字体颜色 - 题目分析
(1) 显示菜单与功能选择
可以使用代码的直接定址表,根据输入的序号得到对应的直接定址表中标号的地址,调用相应子程序,实现功能
(2) 数据存储方式
为了方便存储与查找,设定固定长度的内存空间存储姓名和号码,将人名和电话号码各以15个字节为单位存入数据单元中,不够补空格,并且是号码紧挨着人名排,以便查找。
因为使用21号中断的10号功能来输入字符串,所以分别设定姓名缓冲区,号码缓冲区,暂存用户输入的字符串。
内存中有一位用来存储当前用户个数,方便查找和显示所有信息。
将多条提示语句预先存在数据区,方便根据标号进行调用。
(3) 数据查找方式
通过姓名查询时,将用户输入到缓冲区的姓名与存储姓名的数据段的多条内容逐位比较,若能顺利读到该条数据的最后一位,即表示字符串匹配成功,即查找成功,通过号码查找同理。
(4) 文件操作
调用21号中断的3ch号功能,将数据段中的信息导出到文本文档。
(5) 字体,背景颜色操作
通过修改显存,改变字体和背景颜色。由于只能操作当前页的颜色,可以选择每次跳出菜单之前进行清屏,保证所有操作都在当前页进行。
F1键改变颜色,通过改写中断,定制键盘实现
(6) 显示提示信息
通过21号中断的9号功能显示数据段中的提示信息。由于很多地方都用到了,可以将该功能写成宏,把标号当做参数进行调用
(7) 多文件组织
由于代码较长,可以使用多文件进行组织 - 源代码
.8086
.MODEL small
.data
dw 0,0
table dw Quit,AddPerson,SearchByName,SearchByNum,DisplayAll,ExportTxt ;代码的直接定址表,存放各功能代码块的偏移地址
menu db 10,13,10,13,' MENU '
db 10,13
db 10,13,'1. Add Person'
db 10,13,'2. Search By Name'
db 10,13,'3. Search By Num'
db 10,13,'4. Display All'
db 10,13,'5. ExportTxt'
db 10,13,'0. Quit'
db 10,13
db 10,13,'please choose one of 0~5:','$'
content db 20 dup(15 dup (0),15 dup (0)) ;存储姓名,号码
name_buff db 15, ?, 15 DUP(?) ;输入缓冲区,21号中断10号功能
num_buff db 15, ?, 15 DUP(?)
count db 0 ;记录存储的信息条数
filename db 'data.txt',0 ;导出文件的文件名
handle dw ? ;导出文件的文件句柄
add1 db 10,13,'please input the name:','$'
add2 db 10,13,'please input the num:','$'
succ db 10,13,'Operation is successful!','$'
fail db 10,13,'No data at the moment!','$'
no_find db 10,13,'The message does not exist!','$'
.stack 128
.code
;*****************************************************************
; 宏指令:subprog
; 功能:显示指定标号处的字符串
; 入口参数:_Label——表示字符串标号
; 出口参数:无
;*****************************************************************
show macro _Label
lea dx,_Label ;字符串标号
mov ah,9
int 21h
endm
main proc
start:
push cs
pop ds
mov ax,0
mov es,ax
lea si,int9
mov di,204h
;安装新程序
mov cx,offset int9end - offset int9
cld
rep movsb
;保存原中断例程入口地址
push es:[9*4]
pop es:[200h]
push es:[9*4+2]
pop es:[202h]
cli
;改变后中断的入口地址
mov word ptr es:[9*4],204h ;IP
mov word ptr es:[9*4+2],0 ;CS
sti
mov ax,@data
mov ds,ax
call clear_screen
disp:
show menu
mov ah,1
int 21h ;调用21h中断的第1号功能,从键盘读入字符,AL保存读入字符的ASCII码
cmp al,30h
jb no
cmp al,35h
ja no
sub al,30h ;将输入的ASCII码转为BCD码
mov bl,al
mov bh,0
add bx,bx ;数字乘2才是直接定址表中的标号的地址
call word ptr table[bx] ;调用子程序
jmp disp ;重复显示菜单
no:call clear_screen
jmp disp
;*****************************************************************
; 子程序:Quit
; 功能:退出程序
; 入口参数:无
; 出口参数:无
;*****************************************************************
Quit proc near
mov ah,4ch
int 21h
Quit endp
;*****************************************************************
; 子程序:AddPerson
; 功能:向内存中添加一条信息
; 入口参数:无
; 出口参数:无
;*****************************************************************
AddPerson proc
show add1
call input_name ;输入姓名到缓冲区
;从缓冲区传到存放数据的地址
lea di,content
mov ax,@data
mov es,ax
mov ah,0
mov al,count
mov bl,30
mul bl
add di,ax ;当前存储条数*30,得到新姓名起始地址
lea si,name_buff+2 ;从第三个字节开始存储数据
cld
mov cx,15
rep movsb ;串传送
show add2
call input_num ;输入号码到缓冲区
;从缓冲区传到存放数据的地址
lea di,content
mov ax,@data
mov es,ax
mov ah,0
mov al,count
mov bl,30
mul bl
add di,ax
add di,15 ;当前存储条数*30+15,得到新号码起始地址
lea si,num_buff+2 ;从第三个字节开始存储数据
cld
mov cx,15
rep movsb ;串传送
call clear_screen
show succ
;数据区存放的人数加一
add byte ptr count,1
ret
AddPerson endp
;*****************************************************************
; 子程序:SearchByName
; 功能:通过姓名查找对应信息,并显示在屏幕上
; 入口参数:无
; 出口参数:无
;*****************************************************************
SearchByName proc
show add1
call input_name ;输入姓名到缓冲区
lea di,content
push di
mov bl,count
mov bh,0 ;bx存储当前剩余的要比较的数据条数
mov ax,@data
mov es,ax
find1:
lea si,name_buff+2
mov cx,15
repe cmpsb ;比较 si 和bi的前15个字节
jz ok1 ;不相等时不跳转
pop di
add di,30 ;di 偏移地址加20
push di
dec bx
jnz find1 ;bx不为0则继续比较下一条信息
call clear_screen
show no_find
jmp disp
ok1:
call clear_screen
pop di
mov cx,30
print1:
mov dl,[di] ;要输出的字符的ASCII码
mov ah,06h ;21号中断的6号功能
int 21h
inc di
loop print1
call wrap ;换行
pop cx
jmp disp
SearchByName endp
;*****************************************************************
; 子程序:SearchByNum
; 功能:通过号码查找对应信息,并显示在屏幕上
; 入口参数:无
; 出口参数:无
;*****************************************************************
SearchByNum proc
show add2
call input_num ;输入号码到缓冲区
lea di,content+15
push di
mov bl,count ;bx存储当前剩余的要比较的数据条数
mov bh,0
mov ax,@data
mov es,ax
find2:
lea si,num_buff+2
mov cx,15
repe cmpsb ;比较 si 和di的前15个字节
jz ok2 ;不相等时不跳转
pop di
add di,30 ;di 偏移地址加30
push di
dec bx
jnz find2 ;bx不为0则继续比较下一条信息
call clear_screen
show no_find
jmp disp
ok2:
call clear_screen
pop di
sub di,15
mov cx,30
print2:
mov dl,[di] ;要输出的字符的ASCII码
mov ah,06h ;21号中断的6号功能
int 21h
inc di
loop print2
call wrap ;换行
pop cx
jmp disp
SearchByNum endp
;*****************************************************************
; 子程序:input_name
; 功能:输入姓名到缓冲区
; 入口参数:name_buff---表示姓名缓冲区的起始地址
; 出口参数:无
;*****************************************************************
input_name proc
lea dx,name_buff
mov ah,10
int 21h ;21号中断的10号功能,输入字符串到缓冲区,回车结束输入
mov bh,0
mov bl,name_buff+1 ;bl 存放从键盘输入的字符串长度
mov cx,15
sub cx,bx ;计算剩下的长度
comp1:
mov name_buff[bx+2],20h ;剩下的地方补空格
inc bx
loop comp1
ret
input_name endp
;*****************************************************************
; 子程序:input_num
; 功能:输入号码到缓冲区
; 入口参数:num_buff---表示号码缓冲区的起始地址
; 出口参数:无
;*****************************************************************
input_num proc
lea dx,num_buff
mov ah,10
int 21h ;21号中断的10号功能,输入字符串到缓冲区,回车结束输入
mov bh,0
mov bl,num_buff+1 ;用bl 存放从键盘输入的字符串长度
mov cx,15
sub cx,bx ;计算剩下的长度
comp2:
mov num_buff[bx+2],20h ;剩下的地方补空格
inc bx
loop comp2
ret
input_num endp
;*****************************************************************
; 子程序:DisplayAll
; 功能:将数据区所有的用户和号码打印到屏幕
; 入口参数:count——表示数据区所有的信息条数
; content——表示信息的起始地址
; 出口参数:无
;*****************************************************************
DisplayAll proc near
call clear_screen
mov al,count
cmp al,0
jz return
mov ah,0
mov cx,ax
lea bx,content ;数据的起始地址
s:
push cx
mov cx,30
print:
mov dl,[bx] ;要打印的字符ASCII码
mov ah,06h ;21号中断的6号功能
int 21h
inc bx
loop print
call wrap
pop cx
loop s
ret
;显示提示字符串
return:
show fail
ret
DisplayAll endp
;*****************************************************************
; 子程序:ExportTxt
; 功能:将数据区中的所有姓名和号码导出到文本文件
; 入口参数:filename——表示文件名
; 出口参数:cx——在cx中存放……
; [bx]——在bx指示的单元中……
;调用注意事项:由于要利用cx返回处理结果,注意在调用程序中保护cx的值
;*****************************************************************
ExportTxt proc near
;创建文件
lea dx,filename
mov cx,0
mov ah,3ch
int 21h ;21号中断的3c号功能,用指定的文件名创建一个新文件
mov handle,ax ;保存文件句柄
;向文件中写入文本
mov bx,handle
mov cx,offset name_buff-offset content
lea dx,content ;dx存储起始地址
mov ah,40h ;21号中断的40号功能
int 21h
;关闭文件
mov bx,handle
mov ah,3eh
int 21h
;显示提示字符串
call clear_screen
show succ
ret
ExportTxt endp
;*****************************************************************
; 子程序:wrap
; 功能:输出换行
; 入口参数:无
; 出口参数:无
;*****************************************************************
wrap proc
mov dl,0Dh
mov ah,2
int 21h
mov dl,0Ah
mov ah,2
int 21h
ret
wrap endp
;*****************************************************************
; 子程序:clear_screen
; 功能:清屏
; 入口参数:无
; 出口参数:无
;调用注意事项:会改变es的值,要注意对其进行保护,可通过调整调用的位置或者设置寄存器压栈
;*****************************************************************
clear_screen proc
mov ax,0b800h
mov es,ax
mov bx,0
mov cx,2000
p:
mov byte ptr es:[bx],' ' ;将该页字符全部置为空格,实现清屏
add bx,2
loop p
mov ah,02h
mov bh,0 ; 显示页码
mov dx,0 ; x轴 y轴均置为0
int 10h ;10号中断的2号功能,设置光标位置
ret
clear_screen endp
;*****************************************************************
; 子程序:int9
; 功能:改写int9中断。定制键盘输入
;调用注意事项:由于要利用cx返回处理结果,注意在调用程序中保护cx的值
;*****************************************************************
int9:
push ax
push bx
push cx
push es
in al,60h ;读取键盘扫描码
pushf ;标志寄存器入栈
call dword ptr cs:[200h] ;调用旧中断例程,处理硬件细节
cmp al,3bh ;等于F1键的扫描码
jne int9ret
mov ax,0b800h
mov es,ax ;操作显存
mov bx,1
mov cx,2000
r:
inc byte ptr es:[bx] ;每一位字符颜色属性值加一
add bx,2
loop r
int9ret:
pop es
pop cx
pop bx
pop ax
iret
int9end:nop
main endp
end start