cnkizy 发表于 2022-9-17 16:48:38

分享一个电话本程序

ReadAsm编写
.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_szScanFormatBYTE   '%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_szTabBYTE    0dh,0ah,0h ;换行回车\r\n
        g_szTipsDelBYTE   '<已删除这条信息>',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,
        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, ; 电话本首地址 + 偏移地址 = 第i个元素的地址
        lea edx, ; 保存第i个元素地址
        mov g_nTmp, edx
        ; 判断这个元素的姓名和输入的姓名是否匹配
        lea esi,
        cmpsd dword ptr,dword ptr
        jnz not_find
        ; 找到了就输出信息
        lea eax,
        push eax
        lea eax,
        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, ; 电话本首地址 + 偏移地址 = 第i个元素的地址
       
        ; 提示输入姓名
        push offset g_szNameIn
        call crt_printf
        add esp,4
        ; 用户输入姓名
        lea eax,
        push eax
        push offset g_szScanNameFormat
        call crt_scanf
        add esp,8
       
        ; 提示输入电话
        push offset g_szPhoneNumberIn
        call crt_printf
        add esp,4
        ; 用户输入电话
        lea eax,
        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,       ; i元素地址
        ;lea esi, ; 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,
        lea esi,
        movsd dword ptr, dword ptr
        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,
        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,
        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, ; 电话本首地址 + 偏移地址 = 第i个元素的地址
       
        ; 打印信息
        lea eax,
        push eax
        lea eax,
        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


https://img-blog.csdnimg.cn/7c2cc63b35504b2c9e3fa911e06ae406.png
页: [1]
查看完整版本: 分享一个电话本程序