实验4:(电话本)
要求:
可放50项的电话号码表,每项中有姓名(20)和电话号码(8),存放联系人,并且进行排序。并完成添加电话,根据姓名查询电话,显示输出等功能
算法设计:
①数据结构:可放50项的电话号码表,每项中有姓名(20)和电话号码(8)。数据定义则用tel_tab db 50 dup(20 dup(' '),8 dup(' ')),即可满足,每项用空白符初始化
②.1存放输入的数据:tel_name和tel_num分别存放用户输入的姓名和电话号码。di初始化为电话簿tel_tab的首地址,使用movsb完成字符串的赋值,因此,di指向传送的目标地址,si为源地址,cx赋值为传送的字节数,转送名字时,cx=20,传送电话时,cx=8。循环输入电话簿项中di会跟着movsb而变化,代表接下来用户传送的目标位置,而si每次需重置为缓存区的首地址。这样就完成了用户输入的名字电话存到tel_tab中。在此基础上,完成排序等功能。
②.2.用户输入缓冲区中的数据需要将剩下的空间用空格代替,具体原因会在讨论与结论中说明。主要原因是,用户输入的字符串一般为回车符结尾,倘若连回车符也一起赋到了tel_tab中,则在输出tel_tab中会完成了输出电话簿信息的效果。所以每次输入姓名和电话时,要把缓存区剩余空间(包括回车符)一起用空白符填满,空白符填入的个数=总长度-输入字符的长度
③、排序:每次名字转入电话簿后都要对电话簿进行重新排序(字典序)。采用排序的最佳方法是插入排序,因为每次插入数据时,tel_tab中的数据都已经排好了,只要找到比用户输入名字大的电话簿项时,将次内存后的所有数据都向后移28个字节,然后再把名字和电话存入该位置。
④、输出:起始地址tel_tab,每输出28个字符换行回车。
程序实现:(代码见附录)
①、输入名字和电话,剩余用空白符代替:调用inputname和inputtel子程序即可。
②、将tel_name和tel_num存入tel_tab中,调用stor_name和stor_mun,子程序。主要采用movsb命令符
③、指定字符串后移28个字节: moveto28byte子程序,因为电话簿每一项长度为28个字节,所以cx赋值28,然后si字符串首地址,di=si+28,使用movsb传送
④、插入排序:需要一个变量tel_count来存放电话簿项个数,当插入数据时比较到比自己大时,从电话簿最后一项开始,每一项调用moveto28byte子程序,然后再将数据放到电话簿中
⑤、输出电话簿:cx=28*tel_count。
⑥、查询名字并输出:用sea_name缓冲区存放要查询的名字(记得用空白符将剩下空间填满)。匹配两字符串是否相等,教课书上有代码就不再多说,把名字都遍历一遍,如果匹配成功,另匹配到的名字首地址加上20个字节,即为对应的电话号码首地址,将其输出即可。
DATAS SEGMENT
;此处输入数据段代码
tel_name db 20
namelen db 0
namefld db 20 dup(?)
tel_num db 8
numlen db 0
numfld db 8 dup(?)
tel_tab db 50 dup(20 dup(' '),8 dup(' '))
tel_count db 0
pushunit db 0
turnnewline db 28
string1 db 'Input name:$'
string2 db 'Input a telephone number:$'
string3 db 'Please add telephone into table(n/y)$'
string4 db 'search telephone by name:$ '
string5 db 'noresult$'
strSeaName db 20 ;根据名字查询电话
seanamelen db 0
seanamefld db 20 dup(?)
outputname db 0 ;查询电话时记录匹配不成功的次数
DATAS ENDS
STACKS SEGMENT
;此处输入堆栈段代码
STACKS ENDS
CODES SEGMENT
ASSUME CS:CODES,DS:DATAS,SS:STACKS
START:
MOV AX,DATAS
MOV DS,AX
mov es,ax
cld
lea di,tel_tab
; 输出Inputname
addphone:
mov dx,offset string3
call print
mov ah,01h
int 21h
call printnewline
;cmp al,59h
;jne searchphone
cmp al,79h
jne printtable ;如果键入不是y或Y则进入查询号码
mov dx,offset string1
call print
call Inputname
call stor_name ;把名字存放在tel_tab中
mov dx,offset string2
call print
call inputtel
call stor_num ;把电话存放在tel_tab中
call name_sort ;排序
jmp addphone
printtable: ;打印电话簿
lea bx,tel_tab
;lea dx,tel_tab
;call moveto28byte
mov al,tel_count
mov ah,28
mul ah
mov cx,ax
printtableline:
mov dl,[bx]
mov ah,02h
int 21h
inc bx
dec turnnewline
mov al,turnnewline
cmp al,0
jne nochange1
mov turnnewline,28
call printnewline
nochange1: loop printtableline
namesearch: ;查询电话
mov dx,offset string4
call print
lea dx,strSeaName
call cin
call printnewline
mov bl, seanamelen
mov cx, 20
sub cx, bx
namebrk1: ;将人名除外所剩下的字符用空格填满
mov seanamefld[bx], 20h
inc bx
loop namebrk1
;比较字符串大小
mov cl,tel_count
lastp:
push cx
mov si,offset seanamefld
mov di,offset tel_tab ;+outputname*28
mov al,outputname
mov bl,28
mul bl
add di,ax
mov cx,20
cld
lastagain: cmpsb
jnz lastunmat
loop lastagain
;匹配成功
mov al,0
jmp lastoutput
lastunmat: mov al,-1
pop cx
inc outputname
loop lastp
lastoutput:
cmp al,0
jne noresult
mov cx,8
matchname:
mov dl,[di]
mov ah,02h
int 21h
inc di
loop matchname
jmp stop
noresult:
mov dx,offset string5
call print
stop:
MOV AH,4CH
INT 21H
;其他子程序
name_sort proc ;对名字和电话一起排序 ,待完成
push dx
push bx
push cx
push ax
mov dx,di
push di
sub dx,28 ;刚刚输入的姓名的地址
lea bx,tel_tab
mov cl,tel_count
sortagain:
cmp bx,dx
je sortend
call strcompare
add bx,28
cmp al,1 ;如果刚刚输入的姓名大,那就继续循环
jne index1
loop sortagain
index1:
mov al,tel_count ;ax=(tel_count-cx)*28为要放的单元数 ;如果小的话,把当前之后的名字后移28个字节,再把名字插入到当前位置
sub al,cl
mov ah,28
MUL ah
allmove:
call moveto28byte
sub dx,28
loop allmove
jmp putinto
putinto:
mov si,di ;刚刚输入名字的新地址
lea di,tel_tab
add di,ax
mov cx,28
rep MOVSB
sortend:
pop di
pop ax
pop cx
pop bx
pop dx
ret
name_sort endp
moveto28byte proc ;dx为当前地址28位各后移28个字节
push cx
push bx
push si
push di
mov bx,dx
add bx,28
mov si,dx
mov di,bx
mov cx,28
rep MOVSB
pop di
pop si
pop bx
pop cx
ret
moveto28byte endp
Inputname proc ;输入名字
lea dx,tel_name
call cin
mov bl, namelen
mov cx, 20
sub cx, bx
namebrk: ;将人名除外所剩下的字符用空格填满
mov namefld[bx], 20h
inc bx
loop namebrk
ret
Inputname endp
stor_name proc
inc tel_count
cld
mov si,offset tel_name+2
;mov di,offset tel_tab
mov cl,20
rep MOVSB
ret
stor_name endp
Inputtel proc
lea dx,tel_num
call cin
mov bl, numlen
mov cx, 8
sub cx, bx
numbrk: ;将人名除外所剩下的字符用空格填满
mov numfld[bx], 20h
inc bx
loop numbrk
ret
Inputtel endp
stor_num proc
cld
lea si,numfld
;mov di,offset tel_tab
mov cl,8
rep MOVSB
ret
stor_num endp
;打印固定字符串,入口参数dx指向的地址以$符号结束
print proc
push ax
mov ah,09h
int 21h
pop ax
ret
print endp
;用户输入,入口参数dx指向第一个缓冲区地址
cin proc
push ax
mov ah,0AH
int 21h
pop ax
call printnewline
ret
cin endp
;输出换行
printnewline proc
push ax
push dx
mov ah,02h
mov dl,0dh
int 21h
mov dl,0ah
int 21h
pop dx
pop ax
ret
printnewline endp
;字符串大小比较(字典序)
strcompare proc ;入口参数dx,bx的首地址,dx大于bx则输出al为1,否则为0
push si
push di
push cx
mov si,dx
mov di,bx
push bx
push dx
mov cx,20
cld
stragain:
mov bl,[si]
cmp bl,[di]
jb unmat1
cmp bl,[di]
ja unmat2
inc si
inc di
loop stragain
unmat1: mov al,0
jmp endstrcompare
unmat2: mov al,1
endstrcompare:
pop dx
pop bx
pop cx
pop di
pop si
ret
strcompare endp
CODES ENDS
END START
输出结果如图: