汇编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: 取局部变量的地址
 

  

原文地址:https://www.cnblogs.com/ltyandy/p/11066543.html