个人头像

汇编语言--简易电话簿

发表于2017-08-06 | 分类于
  1. 题目要求
    本题目是完成一个较为简单的电话本程序,能够实现插入信息(包括姓名和号码),查询信息(分为通过姓名查询和通过号码查询),显示所有联系人信息,数据导出到文本文件,修改背景颜色,字体颜色
  2. 题目分析
    (1) 显示菜单与功能选择
    可以使用代码的直接定址表,根据输入的序号得到对应的直接定址表中标号的地址,调用相应子程序,实现功能
    (2) 数据存储方式
    为了方便存储与查找,设定固定长度的内存空间存储姓名和号码,将人名和电话号码各以15个字节为单位存入数据单元中,不够补空格,并且是号码紧挨着人名排,以便查找。
    因为使用21号中断的10号功能来输入字符串,所以分别设定姓名缓冲区,号码缓冲区,暂存用户输入的字符串。
    内存中有一位用来存储当前用户个数,方便查找和显示所有信息。
    将多条提示语句预先存在数据区,方便根据标号进行调用。
    (3) 数据查找方式
    通过姓名查询时,将用户输入到缓冲区的姓名与存储姓名的数据段的多条内容逐位比较,若能顺利读到该条数据的最后一位,即表示字符串匹配成功,即查找成功,通过号码查找同理。
    (4) 文件操作
    调用21号中断的3ch号功能,将数据段中的信息导出到文本文档。
    (5) 字体,背景颜色操作
    通过修改显存,改变字体和背景颜色。由于只能操作当前页的颜色,可以选择每次跳出菜单之前进行清屏,保证所有操作都在当前页进行。
    F1键改变颜色,通过改写中断,定制键盘实现
    (6) 显示提示信息
    通过21号中断的9号功能显示数据段中的提示信息。由于很多地方都用到了,可以将该功能写成宏,把标号当做参数进行调用
    (7) 多文件组织
    由于代码较长,可以使用多文件进行组织
  3. 源代码
.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