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