.386
.model flat,stdcall
option casemap:none
include masm32rt.inc
.data
; ========= 结构体 =========
;电话本-成员
PHONE struct
m_name byte 8 dup(0) ;姓名
m_phone byte 8 dup(0) ;电话
PHONE ends
; ========= 常量 =========
;输入格式:名称 电话号码
g_szScanFormat BYTE '%s %s',0h
;输入格式:名称
g_szScanNameFormat BYTE '%s',0h
;打印格式:名称\t\t电话号
g_szPrintItemFormat BYTE '姓名:%s',09h,09h,'电话:%s',0ah,0dh,0h
;输入指令
g_szGetCmdFormat BYTE '%d',0h
;菜单
g_szMenu db "电话本",0ah,0dh,
"1=查询",0ah,0dh,
"2=增加",0ah,0dh,
"3=删除",0ah,0dh,
"4=修改",0ah,0dh,
"5=查看所有",0ah,0dh,
"当前储存了 %d / %d 条信息",0ah,0dh,
">>",0h
g_szClr BYTE 'cls',0h
g_szPause BYTE 'pause',0h
g_szCmdIn BYTE '%d',0h
g_szNameIn BYTE '输入姓名:',0h
g_szNewNameIn BYTE '输入新的姓名:',0h
g_szPhoneNumberIn BYTE '请输入电话号码:',0h
g_szNewPhoneNumberIn BYTE '请输入新的电话号码:',0h
g_szTab BYTE 0dh,0ah,0h ;换行回车\r\n
g_szTipsDel BYTE '<已删除这条信息>',0dh,0ah,0h
g_szTipsAdd BYTE '<插入成功>',0dh,0ah,0h
g_szTipsUpdate BYTE '<修改成功>',0dh,0ah,0h
g_szTipsNoMore BYTE '<没有更多信息>',0dh,0ah,0h
g_szErrNotFind BYTE '<没有这条信息>',0dh,0ah,0h
g_szErrMax BYTE '<电话本储存满了>',0dh,0ah,0h
g_szErrUnknowCmd BYTE '<未知指令>',0dh,0ah,0h
; ========= 全局变量 =========
;电话本
g_book PHONE 20 dup(<'0'>)
;最大容量 最多保存20条记录
g_max dword 1 dup(20)
;当前容量
g_nCount dword 1 dup(0)
;单个电话本临时变量
g_tmpPhone PHONE <'0','0'>
;当前用户选择的功能 1=查询 2=增加 3=删除 4=修改 5=看所有
g_nCmd byte 1 dup(0)
;索引查找数
g_nI dword 1 dup(0)
;临时指针
g_nTmp dword 1 dup(0)
;找到了的索引
g_nFindIndex dword 1 dup(-1)
;找到了的地址
g_nFindPoint dword 1 dup(0)
.code
; 打印菜单并接收用户指令到eax
; 功能 1=查询 2=增加 3=删除 4=修改
SelectMenu proc
; 清屏
push offset g_szClr
call crt_system
add esp,4
; 打印菜单
push g_max
push g_nCount
push offset g_szMenu
call crt_printf
add esp,12
; 用户输入指令,放入g_nCmd
push offset g_nCmd
push offset g_szCmdIn
call crt_scanf
add esp,8
ret
SelectMenu endp
; 查找用户
PhoneFind proc
; 提示输入姓名
push offset g_szNameIn
call crt_printf
add esp,4
; 用户输入姓名
lea eax,[g_tmpPhone + PHONE.m_name]
push eax
push offset g_szScanNameFormat
call crt_scanf
add esp,8
; 重置i
mov g_nI,0 ; i = 0
find_loop:
mov eax,g_nI
mov ebx,g_max ; max
cmp eax,ebx
ja find_empty ;i > g_max ,goto proc_end
; 计算地址
imul eax,g_nI,sizeof(PHONE) ; i*单个元素大小 = 偏移地址给edi
lea edi,[g_book + eax] ; 电话本首地址 + 偏移地址 = 第i个元素的地址
lea edx,[edi] ; 保存第i个元素地址
mov g_nTmp, edx
; 判断这个元素的姓名和输入的姓名是否匹配
lea esi,[g_tmpPhone + PHONE.m_name]
cmpsd dword ptr[edi + PHONE.m_name],dword ptr[esi]
jnz not_find
; 找到了就输出信息
lea eax, [edx + PHONE.m_phone]
push eax
lea eax, [edx + PHONE.m_name]
push eax
push offset g_szPrintItemFormat
call crt_printf
add esp,12
; 记录索引
mov edx,g_nI
mov g_nFindIndex,edx
; 记录地址
mov edx,g_nTmp
mov g_nFindPoint,edx
ret
not_find:
; 没找到则继续找
inc g_nI ; i++
jmp find_loop
find_empty:
; 提示没有找到这条信息
push offset g_szErrNotFind
call crt_printf
add esp,4
mov g_nFindIndex,-1 ; 没找到 索引为-1
mov g_nFindPoint,0 ; 没找到 地址为0
ret
PhoneFind endp
; 增加用户
PhoneAdd proc
;判断容量是否满了
mov eax,g_nCount
mov ebx,g_max
cmp eax,ebx
jb phone_insert
; 如果容量满了输出提示
push offset g_szErrMax
call crt_printf
add esp,4
ret
phone_insert:
; 计算地址
mov ecx,g_nCount ; 当前容量
imul eax,ecx,sizeof(PHONE) ; i*单个元素大小 = 偏移地址给edi
lea edi,[g_book + eax] ; 电话本首地址 + 偏移地址 = 第i个元素的地址
; 提示输入姓名
push offset g_szNameIn
call crt_printf
add esp,4
; 用户输入姓名
lea eax,[edi + PHONE.m_name]
push eax
push offset g_szScanNameFormat
call crt_scanf
add esp,8
; 提示输入电话
push offset g_szPhoneNumberIn
call crt_printf
add esp,4
; 用户输入电话
lea eax,[edi + PHONE.m_phone]
push eax
push offset g_szScanNameFormat
call crt_scanf
add esp,8
; 当前容量+1
inc g_nCount
; 插入成功
push offset g_szTipsAdd
call crt_printf
add esp,4
ret
PhoneAdd endp
; 删除用户
PhoneDel proc
; 寻找用户
call PhoneFind
cmp g_nFindIndex, -1
je find_empty; 没有此用户
; 找到了用户后,第i个后面依次往前移动
; 循环次数 = 总容量 - 找到的索引
; 若找到的索引i=3 ,总容量max = 5, 5-3=2次,索引从0开始,即 3覆盖2, 4覆盖3
mov ecx, g_max ;总容量
mov eax, g_nFindIndex ;找到的数据,索引从0开始
sub ecx, eax ;循环次数
copy_loop:
; copy到当前容量后还没到Max需要提前结束循环
; 即 g_nFindIndex >= g_nCount 退出
mov eax,g_nFindIndex
mov ebx,g_nCount
cmp eax, ebx
jnb copy_over ; 提前结束
mov eax, g_nFindIndex ;找到的数据,索引从0开始
; 计算i元素地址 = 电话本首地址 + 找到的索引 * 一个元素大小
imul esi, eax, sizeof(PHONE)
lea edi, [g_book + esi] ; i元素地址
;lea esi, [edi + sizeof(PHONE)] ; i下一个元素地址
mov g_nTmp, edi
; PHONE类型刚好为16字节 也就是4次movsd就能实现拷贝了
; 保存外层 ecx
push ecx
; 循环4次
mov ecx,4
copy_item:
mov edi,g_nTmp
mov esi,edi
imul eax, ecx, 4
lea edi,[edi + eax - 4]
lea esi,[eax + esi + sizeof(PHONE) -4]
movsd dword ptr[edi], dword ptr[esi]
loop copy_item
; 内层循环完毕,还原ecx
pop ecx
; 复制一次,寻找索引+1
inc g_nFindIndex
loop copy_loop ; 循环ecx次
copy_over:
; 当前容量 - 1
dec g_nCount
; 删除成功
push offset g_szTipsDel
call crt_printf
add esp,4
find_empty:
ret
PhoneDel endp
; 修改用户
PhoneUpdate proc
; 寻找用户
call PhoneFind
cmp g_nFindIndex, -1
je find_empty; 没有此用户
; 找到了 这个用户 进行数据更新
; 提示输入姓名
push offset g_szNewNameIn
call crt_printf
add esp,4
; 用户输入新的姓名
mov edx,g_nFindPoint
lea eax,[edx + PHONE.m_name]
push eax
push offset g_szScanNameFormat
call crt_scanf
add esp,8
; 提示输入电话
push offset g_szNewPhoneNumberIn
call crt_printf
add esp,4
; 用户输入新的电话
mov edx,g_nFindPoint
lea eax,[edx + PHONE.m_phone]
push eax
push offset g_szScanNameFormat
call crt_scanf
add esp,8
; 输出提示 修改成功
push offset g_szTipsUpdate
call crt_printf
add esp,4
find_empty:
ret
PhoneUpdate endp
; 打印所有用户
PhoneShowAll proc
mov g_nI,0
print_loop:
mov eax,g_nI
cmp eax,g_nCount
; i<g_nCount 则打印
jb print
push offset g_szTipsNoMore
call crt_printf
add esp,4
ret
print:
; 计算地址
imul eax,g_nI,sizeof(PHONE) ; i*单个元素大小 = 偏移地址给edi
lea edi,[g_book + eax] ; 电话本首地址 + 偏移地址 = 第i个元素的地址
; 打印信息
lea eax, [edi + PHONE.m_phone]
push eax
lea eax, [edi + PHONE.m_name]
push eax
push offset g_szPrintItemFormat
call crt_printf
add esp,12
inc g_nI
jmp print_loop
PhoneShowAll endp
main:
; 打印菜单获取用户指令
call SelectMenu
cmp g_nCmd, 1
jne menu_add; 输入的是1(查询)继续执行,否则跳到下一个判断
call PhoneFind
jmp menu_end
menu_add:
cmp g_nCmd, 2
jne menu_del ; 输入的是2(增加)继续执行,否则跳到下一个判断
call PhoneAdd
jmp menu_end
menu_del:
cmp g_nCmd, 3
jne menu_update ; 输入的是3(删除)继续执行,否则跳到下一个判断
call PhoneDel
jmp menu_end
menu_update:
cmp g_nCmd, 4
jne menu_showall ; 输入的是4(修改)继续执行,否则跳到下一个判断
call PhoneUpdate
jmp menu_end
menu_showall:
cmp g_nCmd, 5
jne menu_unknow ; 输入的是5(查看所有)继续执行,否则跳到下一个判断
call PhoneShowAll
jmp menu_end
menu_unknow: ; 输入的未知指令
push offset g_szErrUnknowCmd
call crt_printf
add esp,4
menu_end: ; 执行完毕返回菜单
push offset g_szPause
call crt_system
add esp,4
jmp main
quit: ; 退出
ret
end main
end