汇编语言编写增删改查的电话本
主要难点
find_item 函数的编写
这个函数的参数是一个待查询名字的字符串首地址,若找到则返回保存名字,电话的首地址,否则返回0
这里涉及到两个循环,一个是比较字符串的小循环,一个保证所有数据都被比较过的大循环
每次大循环需要重置EDI和ESI这两个对比的地址
不足
关于增加函数计算地址的地方,可以做一个全局变量(在.data中定义)来保存,下一个增加位置,不用每次都计算一次地址
代码
以下是汇编完整的代码
;模式定义-------------------------------------------------
.386
.model flat,stdcall
option casemap:none
;头文件--------------------------------------------------
include windows.inc
include msvcrt.inc
includelib msvcrt.lib
;数据区--------------------------------------------------
.data
;定义结构体,保存名字和电话
ITEM_STRUCT struct
szName BYTE 25 dup(0)
szPhNumber BYTE 25 dup(0)
ITEM_STRUCT ends
;全局变量 变量名 个数 初始值
g_stItem ITEM_STRUCT 100 dup(<'0'>)
g_stItem_In ITEM_STRUCT <'0','0'>
g_nCount DWORD 0
g_nCountMax DWORD 100
g_cCmd BYTE 0h
;格式控制符字符串,方便调用
g_szScanFormat BYTE '%s %s',0h
g_szScanNameFormat BYTE '%s',0h
g_szPrintItemFormat BYTE '%s:',09h,09h,'%s',0d,0ah,0h
g_szGetCmdFormat BYTE '%c',0h
g_szCountFormat BYTE 'There are %d phone numbers:',0dh,0ah,0h
;错误信息
g_szErrMax BYTE 'ERR->Storage is full',0dh,0ah,0h
;普通信息
g_szClr BYTE 'cls',0h
g_szPause BYTE 'pause',0h
g_szAddMsg BYTE 'Please enter a name and a phone number, name first:',0dh,0ah,0h
g_szNameIn BYTE 'Please enter a name:',0h
g_szPhoneNumberIn BYTE 'Please enter a phone number:',0h
g_szMenu BYTE 'a.Add phone number',0dh,0ah,'s.Show all',0dh,0ah,'g.Get phone number by name',0dh,0ah,'m.Modify phone number by name',0dh,0ah,'d.Delete phone number by name',0dh,0ah,'c.Clear all',0dh,0ah,'q.Quit',0dh,0ah,'Please enter the first letter as command:',0h
g_szLineFeed BYTE 0dh,0ah,0h
g_szNotFind BYTE 'Not find!',0dh,0ah,0h
g_szAllClear BYTE 'All clear!',0dh,0ah,0h
;代码区-------------------------------------------------
.code
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
add_item proc
;更新栈底
push ebp
mov ebp,esp
;开始ADD_ITEM
;判断是否存满
mov edx,g_nCount
mov eax,g_nCountMax
cmp edx,eax
jae add_item_err_max
;调用scanf接受信息,根据count计算出当前地址,此时edx还是count
;输出提示语
push offset g_szAddMsg
call crt_printf
add esp,4
;计算首地址
lea edi,[g_stItem]
mov ecx,g_nCount
imul eax,ecx,sizeof(ITEM_STRUCT)
add edi,eax
;scanf函数参数入栈
lea eax,[edi+ITEM_STRUCT.szPhNumber]
push eax
lea eax,[edi+ITEM_STRUCT.szName]
push eax
push offset g_szScanFormat
call crt_scanf
add esp,12
inc g_nCount
jmp add_item_return
;如果大了,就用printf输出错误信息
add_item_err_max:
push offset g_szErrMax
call crt_printf
add esp,4
;恢复栈底,返回
add_item_return:
pop ebp
ret
add_item endp
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
show_item proc
;保存栈状态
push ebp
mov ebp,esp
;利用loop与printf输出数据
;获取当前count,并减一,加载源地址到esi
;输出下当前数量
push dword ptr g_nCount
push offset g_szCountFormat
call crt_printf
add esp,8
mov ecx,g_nCount
;将首地址入edi
lea edi,[g_stItem]
;判断是否为零
or ecx,ecx
jne show_loop
;显示提示语
push offset g_szNotFind
call crt_printf
add esp,4
jmp show_item_return
show_loop:
;保存ecx
push ecx
;printf函数参数入栈
lea eax,[edi+ITEM_STRUCT.szPhNumber]
push eax
lea eax,[edi+ITEM_STRUCT.szName]
push eax
push offset g_szPrintItemFormat
call crt_printf
add esp,12
;换行
push offset g_szLineFeed
call crt_printf
add esp,4
;计算下一个读取地址
add edi,sizeof(ITEM_STRUCT)
;恢复ecx
pop ecx
loop show_loop
;恢复栈底,返回
show_item_return:
;暂停看结果....
push offset g_szPause
call crt_system
add esp,4
;恢复栈
pop ebp
ret
show_item endp
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++
find_item proc
;保存栈状态
push ebp
mov ebp,esp
;开辟16个字节空间
sub esp,10
;初始化比较首地址,保存当前计数
lea edi,[g_stItem]
mov [ebp-4],edi
mov ecx,g_nCount
;初始化方向
cld
;重置比较地址,最大长度
find_tiem_loop_big:
;重置待比较地址
mov edi,[ebp-4]
mov esi,[ebp+8]
;字符串比较,一样就下一个字符比较
find_tiem_loop_small:
;判断是否有一个为零
cmp byte ptr [edi],0
je find_item_loop_small_find_0
cmp byte ptr [esi],0
je find_item_loop_small_find_0
;到这里就说明俩个都不是\0,就正常比较
;判断当前字节是否相同,cmpsb自动加地址!
cmpsb
;若不同就直接到大循环
jne find_tiem_loop_big_next
;跳到小循环
jmp find_tiem_loop_small
;找到\0了,到这里说明至少有一个为零!
find_item_loop_small_find_0:
;先判断是否两个数都为\0
mov al,[edi]
add al,[esi]
;若结果为零则说明一样,直接找到了,到这里说明前面是判断完的,至少有个零才会到这里
je find_item_success
;到这里就说明,不一样,可以进行下一个字符串比较了
find_tiem_loop_big_next:
;判断ecx是否为零
;计数器自减,判断是否为零,判断是否查询完毕
dec ecx
or ecx,ecx
;为零就说明到了最后一个字符串了,就该退出了
je find_item_fail
;不为零就继续下一个
;没有到最后一个就自增一个结构体大小
;保存带比较的局部变量自增
mov edi,[ebp-4]
add edi,sizeof(ITEM_STRUCT)
mov [ebp-4],edi
jmp find_tiem_loop_big
find_item_success:
;调用printf输出找到的值
;edi即需要返回的值的地址
mov eax,[ebp-4]
jmp find_item_return
find_item_fail:
;若没找到,返回0
mov eax,0
;恢复栈底,返回
find_item_return:
;返回开辟的栈
add esp,10
pop ebp
ret
find_item endp
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++
get_item_number proc
;保存栈状态
push ebp
mov ebp,esp
;显示提示语
push offset g_szNameIn
call crt_printf
add esp,4
;判断count是否为0,若零返回????????????????????????????????????????????????
;scanf 输入查询名字
push offset g_stItem_In.szName
push offset g_szScanNameFormat
call crt_scanf
add esp,8
;获取名字后调用find_item 返回找到的地址或者null
push offset g_stItem_In.szName
call find_item
add esp,4
;判断是否找到
or eax,eax
je get_item_number_not_find
;找到了
;printf输出
lea ebx,[eax+ITEM_STRUCT.szPhNumber]
push ebx
push eax
push offset g_szPrintItemFormat
call crt_printf
add esp,12
jmp get_item_number_return
;没有找到,输出提示语
get_item_number_not_find:
push offset g_szNotFind
call crt_printf
add esp,4
;恢复栈底,返回
get_item_number_return:
;暂停看结果....
push offset g_szPause
call crt_system
add esp,4
pop ebp
ret
get_item_number endp
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++
modify_item proc
;保存栈状态
push ebp
mov ebp,esp
;判断count是否为0
mov ecx,g_nCount
or ecx,ecx
;若不等于零就继续,否则输出找不到并返回
jne modify_item_normal
;输出提示语
push offset g_szNotFind
call crt_printf
add esp,4
;直接调到结尾
jmp modify_item_return
modify_item_normal:
;显示提示语,输入名字
push offset g_szNameIn
call crt_printf
add esp,4
;scanf 输入查询名字
push offset g_stItem_In.szName
push offset g_szScanNameFormat
call crt_scanf
add esp,8
;获取名字后调用find_item 返回找到的地址或者null
push offset g_stItem_In.szName
call find_item
add esp,4
;判断是否找到
or eax,eax
je modify_item_number_not_find
;这里就找到了,显示提示语,输入号码
;先保存eax数据
push eax
;printf
push offset g_szPhoneNumberIn
call crt_printf
add esp,4
;scanf参数,直接保存到位置
pop eax
add eax,ITEM_STRUCT.szPhNumber
push eax
push offset g_szScanNameFormat
call crt_scanf
add esp,8
jmp modify_item_return
;这里是没找到
modify_item_number_not_find:
push offset g_szNotFind
call crt_printf
add esp,4
;恢复栈底,返回
modify_item_return:
;暂停看结果....
push offset g_szPause
call crt_system
add esp,4
pop ebp
ret
modify_item endp
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++
copy_item proc
;保存栈状态
push ebp
mov ebp,esp
cld
;将参数,存入寄存器,第一个参数是复制目的地址,第二个参数是源地址
;复制name
mov edi,[esp+8]
mov esi,[esp+0ch]
copy_item_name_loop:
mov al,[esi]
movsb
;判断esi指向是否为\0,若是则结束,若不是则继续复制
or al,al
jne copy_item_name_loop
;复制phone number
mov edi,[esp+8]
add edi,ITEM_STRUCT.szPhNumber
mov esi,[esp+0ch]
add esi,ITEM_STRUCT.szPhNumber
copy_item_phone_number_loop:
mov al,[esi]
movsb
;判断esi指向是否为\0,若是则结束,若不是则继续复制
or al,al
jne copy_item_phone_number_loop
;恢复栈底,返回
copy_item_return:
pop ebp
ret
copy_item endp
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++
delete_item proc
;保存栈状态
push ebp
mov ebp,esp
;获取当前个数
mov ecx,g_nCount
;判断是否为0
cmp ecx,0
je delete_item_not_find
;获取输入
;显示提示语,输入名字
push offset g_szNameIn
call crt_printf
add esp,4
;scanf 输入查询名字
push offset g_stItem_In.szName
push offset g_szScanNameFormat
call crt_scanf
add esp,8
;判断 找到了咩?
push offset g_stItem_In
call find_item
add esp,4
;判断是否为零,为零则找不到,直接退出,eax保留地址!
je delete_item_not_find
;到这里就说明找到了,判断下是否是一个,一个的话直接清零
;先删掉再说
mov byte ptr [eax],0
;放到目的寄存器中
mov edi,eax
;获取当前个数
mov ecx,g_nCount
;个数减一
dec ecx
;更新到全局变量
mov g_nCount,ecx
;判断是否在最后一位!若是直接返回,若不是,则将最后一位复制到这里,eax保留地址!ecx可以用了
imul eax,ecx,sizeof(ITEM_STRUCT)
add eax,offset g_stItem
mov esi,eax
cmp esi,edi
;判断是否是最后一个,若是直接返回,若不是则将最后一位移到删除位置
je delete_item_return
;调用复制函数,第一个参数是复制目的地址,第二个参数是源地址
;保存复制地址
push esi
;调用复制
push esi
push edi
call copy_item
add esp,8
;弹出复制地址,首地址赋值为零
pop eax
mov byte ptr [eax],0
;下面是特殊处理,所以要跳过了
jmp delete_item_return
;到这里就说明找不到
delete_item_not_find:
push offset g_szNotFind
call crt_printf
add esp,4
;暂停输出
push offset g_szPause
call crt_system
add esp,4
;恢复栈底,返回
delete_item_return:
pop ebp
ret
delete_item endp
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++
clear_all proc
;保存栈状态
push ebp
mov ebp,esp
;所有的首地址改为\0,并将count改为0
;判断count是否为0
mov ecx,g_nCount
or ecx,ecx
je clear_all_return
;改\0
lea edi,[g_stItem]
clear_all_loop:
mov byte ptr [edi],0
add edi,sizeof(ITEM_STRUCT)
dec ecx
or ecx,ecx
;若ecx不是零就继续循环
jne clear_all_loop
;改count
mov g_nCount,0
;恢复栈底,返回
clear_all_return:
;输出提示语
push offset g_szAllClear
call crt_printf
add esp,4
;暂停输出
push offset g_szPause
call crt_system
add esp,4
pop ebp
ret
clear_all endp
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
main:
main_loop:
;清屏
push offset g_szClr
call crt_system
add esp,4
;获取命令
;显示菜单
push offset g_szMenu
call crt_printf
add esp,4
;获取命令
push offset g_cCmd
push offset g_szGetCmdFormat
call crt_scanf
add esp,8
;1 判断命令是否为Add phone number
case_1:
cmp g_cCmd,'a'
jne case_2
call add_item
jmp main_loop
;2 判断命令是否为Show all
case_2:
cmp g_cCmd,'s'
jne case_3
call show_item
jmp main_loop
;3 判断命令是否为Get phone number by name
case_3:
cmp g_cCmd,'g'
jne case_4
call get_item_number
jmp main_loop
;4 判断命令是否为Modify phone number by name
case_4:
cmp g_cCmd,'m'
jne case_5
call modify_item
jmp main_loop
;5 判断命令是否为Delete phone number by name
case_5:
cmp g_cCmd,'d'
jne case_6
call delete_item
jmp main_loop
;6 判断命令是否为Clear all
case_6:
cmp g_cCmd,'c'
jne case_7
call clear_all
jmp main_loop
;7 判断命令是否为Quit
case_7:
cmp g_cCmd,'q'
jne main_loop
main_return:
ret
end main
end