鱼C论坛

 找回密码
 立即注册
查看: 1143|回复: 0

[技术交流] 分享一个电话本程序

[复制链接]
发表于 2022-9-17 16:48:38 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
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_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


                               
登录/注册后可看大图

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-12-22 23:32

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表