汇编语言编写增删改查的电话本

汇编语言编写增删改查的电话本

主要难点

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


上一篇:ThingsBoard 设备发送RPC 给服务器 (含规则链修改)


下一篇:esp32编译时报错Invalid certificate解决办法