• 汇编5汇编电话本


    
    

    汇编版本电话本

     

    demo.inc

    ; 头文件可以放置数据的定义和结构体
    ​
    ; 定义需要的结构体
    UserContent struct
        ; 需要定义的数据
        user db 32 dup(0)
        tel db 16 dup(0)
    UserContent ends
    ​
    ​
    .data   ; 全局变量(静态变量区)
    ​
    ; 定义一个结构体数组,用于存放所有的联系人
    contents UserContent 2 dup(<>)
    ​
    ; 用于保存菜单的选项
    choose dd 0
    ​
    ; 当前最多能存放多少个数据
    max_count dd 2
    ​
    ; 已经存放了多少个数据
    count dd 0
    ​
    ; 用于保存用户名
    user db 32 dup(0)
    ​
    ​
    ​
    .const  ; 常量数据(常量数据区)
    ​
    ; 菜单项
    str_show_menu db "1. 添加数据", 0dh, 0ah,
             "2. 显示数据", 0dh, 0ah,
             "3. 查找数据", 0dh, 0ah,
             "4. 退出程序", 0dh, 0ah,
             "请输入选项: ", 0
             ; 0dh 0ah == 
     0 字符串结尾
             
    ; 格式控制符
    str_format_int db "%d", 0
    ​
    ; 格式控制符
    str_format_str db "%s", 0
    ​
    ; 
    str_printf_find db "请输入要查找的用户名: ", 0
    ​
    ; 清空屏幕
    str_system_cls db "cls", 0
    ​
    ; 清空屏幕
    str_printf_size db "超出容量", 0dh, 0ah, 0
    ​
    ; 提示输出
    str_printf_info db "请输入联系人的信息: ", 0
    ​
    ; 输入格式控制符
    str_fomat_info db "%s %s", 0
    ​
    ; 输出联系人信息
    str_show_info db "%d: %s %s", 0dh, 0ah, 0
    ​
    ; 暂停
    str_system_pause db "pause", 0dh, 0ah, 0
    ​
    ​
    ​
    

      



    demo.asm

    .386            ; 代表编写的是 32 位程序
    .model flat, stdcall    ; flat 表示访问的是线性地址
                ; stdcall 表示自己编写的函数默认的调用方式  
    option casemap:none ; 表示区分大小写,C 语言默认区分大小写
    ​
    ; 包含需要的头文件和 lib
    include demo.Inc
    ​
    ; 提供 C 运行时函数的头文件
    include msvcrt.inc
    includelib msvcrt.lib
    ​
    ; 用于存储跳转表的数据
    .data
    ; 跳转表,保存 case 块的地址
    jmp_table dd case1, case2, case3, case4
    ​
    ; 所有的代码,都必须编写在 .code 中
    .code
    ​
    ; 函数:退出程序
    exit_pragma proc
        push 0
        call crt_exit
        ret
    exit_pragma endp 
    ​
    ​
    ; 函数:添加数据
    add_content proc
        
        ; 获取当前已存放的个数
        mov ecx, [count]
        ; 判断是否大于容量
        cmp ecx, [max_count]
        ; 如果超出了,就报错
        jae size_falid
        
        ; 如果没有超出就执行以下
        ; 1. 提示用户的输入
        ; 因为 printf 会修改 edx, ecx 和 eax 的值,所以需要进行保存
        push edx
        push eax
        push ecx
        push offset str_printf_info
        call crt_printf
        add esp, 4
        pop ecx
        pop eax
        pop edx
        
        ; 2. 接收用户的输入 scanf("%s %s", xx[i].xx, xx[i].yy)
        ; 2.1 获取结构体的大小
        mov eax, sizeof(UserContent)
        ; 2.2 计算偏移
        imul eax, ecx
        ; 2.3 获取数组首地址
        lea esi, [contents] 
        ; 2.4 计算对应下标元素的首地址
        add esi, eax
        ; 2.4 传入两个参数
        lea ebx, [esi+UserContent.tel]
        push ebx
        lea ebx, [esi+UserContent.user]
        push ebx
        push offset str_fomat_info
        call crt_scanf
        add esp, 12
        ; 3. 自增计数器 count
        inc [count]
        ; 4. 跳过打印错误信息的位置
        jmp ret_addr
        
    size_falid:
        ; 输出错误信息
        push offset str_printf_size
        call crt_printf
        add esp, 4
        
        ; 用于返回调用处
    ret_addr:
        ret
    add_content endp
    ​
    ; 函数:查看数据
    show_content proc
        
        ; for(int i = 0; i < count; ++i)
        ; 初始化下标
        mov ecx, 0
        ; 被循环的语句
    begin_for:
        ; 比较 count 的值
        cmp ecx, [count]
        ; 如果数值大于等于就会退出
        jnb end_for
        ; 循环体
        mov eax, sizeof(UserContent)
        imul eax, ecx
        lea esi, [contents] 
        add esi, eax
        
        push ecx    ; 保存 ecx
        lea ebx, [esi+UserContent.tel]
        push ebx
        lea ebx, [esi+UserContent.user]
        push ebx
        push ecx
        push offset str_show_info
        call crt_printf
        add esp, 10h
        pop ecx     ; 恢复 ecx
        
        ; 自增下标
        inc ecx
        jmp begin_for
        
        ; 结束循环的地方
    end_for:
        ret
    show_content endp
    ​
    ; 函数:查找数据
    find_content proc
        ; 提示输入要查找的
        push offset str_printf_find
    call crt_printf
    add esp, 4
    ​
        ; 获取要查找的用户名 scanf("%s", user)
    push offset user
    push offset str_format_str
    call crt_scanf
    add esp, 8
    
        ; for(int i = 0; i < count; ++i)
        ; 初始化下标
    mov edx, 0
        ; 被循环的语句
    begin_for:
        ; 比较 count 的值
    cmp edx, [count]
        ; 如果数值大于等于就会退出
    jnb end_for
        ; 循环体
    mov eax, sizeof(UserContent)
    imul eax, edx
    lea esi, [contents] 
    add esi, eax
    
        ; 只有输入的数据和遍历的用户相同时才显示
    lea edi,  [user]
    lea esi, [esi + UserContent.user] 
    mov ecx, 20h
    repe cmpsb
        ; 比完了,如果不相等就跳转到下次遍历
    jne next_for
    
        ; 相等的位置
    push edx    ; 保存 ecx
    lea ebx, [esi+UserContent.tel]
    push ebx
    lea ebx, [esi+UserContent.user]
    push ebx
    push edx
    push offset str_show_info
    call crt_printf
    add esp, 10h
    pop edx    ; 恢复 ecx
    jmp end_for
    
        ; 自增下标
    next_for:
    inc edx
    jmp begin_for
    
        ; 结束循环的地方
    end_for:
    ret
    find_content endp
    ​
    ; 标签实际上在编译时会被替换成对应的的地址
    main:
    begin_while:
        ; 清空屏幕
    push offset str_system_cls
    call crt_system
    add esp, 4
    ​
        ; 打印菜单项: print(arg)
    push offset str_show_menu
    call crt_printf
        ; crt 函数是 c 语言提供的的
        ; 所以需要平衡堆栈 _cdecl
    add esp, 4
    
        ; 获取用户的输入 scanf("%d", &choose)
    push offset choose
    push offset str_format_int
    call crt_scanf
        ; 有两个参数,需要平衡 2*4 字节
    add esp, 8
    
        ; switch case 的实现
        ; 1. 获取到当前输入的数据对应的[下标]
    mov ecx, [choose]
    dec ecx
        ; 2. 判断下标是否在有效范围(case)之内
    cmp ecx, 3
        ; 3. 如果传入的数据不在表中,那么跳转到 default
    ja default
        ; 4. 否则查表进行跳转
    jmp dword ptr [jmp_table+ecx*4]
    case1:
        ; 添加数据
    call add_content
    jmp endswitch
    case2:
        ; 显示数据
    call show_content
    jmp endswitch
    case3:
        ; 查找数据
    call find_content
    jmp endswitch
    case4:
        ; 退出程序
    call exit_pragma
    jmp endswitch
    default:
        ; 表示输入错误,直接进入下一次循环
    endswitch:
        ; 添加一个暂停
    push offset str_system_pause
    call crt_system
    add esp, 4
    
    jmp begin_while
    
    
    ; 代码必须写在 end xxx 之前
    end main
    ​
    ​
    ; offset: 取全局变量的地址
    ; addr: 取局部变量的地址
     
    

      

  • 相关阅读:
    枚举
    枚举
    比特币中的密码学原理
    贪心
    dp
    二分
    mac解决matplotlib中文乱码
    Keras使用多个GPU并行
    pyspark使用-dataframe操作
    箱线图
  • 原文地址:https://www.cnblogs.com/ltyandy/p/11066543.html
Copyright © 2020-2023  润新知