ASM16 19 通讯录
通讯录
1 | MyStack segment stack |
数据段定义
1 | MyStack segment stack |
定义了一个堆栈段,包含256个字节的未初始化数据。
1 | MyData segment |
定义菜单选项字符串,包含换行符。
1 | INPUT_ERROR db "input error", 0dh, 0ah, '$' |
定义错误信息、输入名称提示、输入电话号码提示字符串,字符串以$
结尾以便于DOS的09h
服务显示。
1 | NAME_BUF db 16, 0, 16 dup(0) |
定义了用于存储名称和电话号码的缓冲区,格式为:第一个字节表示长度,第二个字节是已输入字符数量,后面是实际数据。
输入缓冲区结构
对于0Ah功能码,输入缓冲区的结构如下:
- 第一个字节:缓冲区的最大长度
- 第二个字节:实际输入的字符数(由DOS填充)
- 后续字节:存储实际输入的字符
1 | ALL_DATA db 340 dup(0) ;flag[2] name[16] phone [16] |
定义了一个存储所有联系人信息的缓冲区,每个联系人占34个字节(标志2字节,姓名16字节,电话16字节)。DATA_COUNT
记录了联系人数量。
代码段定义
1 | MyCode segment |
设置数据段寄存器DS
和附加段寄存器ES
指向MyData
段。
1 | SHOW_MENU: |
显示菜单选项。
1 | GET_INPUT: |
获取用户输入的菜单选项。
处理菜单选项
1 | cmp al,'1' |
根据用户输入的选项跳转到对应的处理代码段。如果输入无效,跳转到DEFAULT1
处理。
添加联系人
1 | mov dx, offset NEXT_LINE |
换行
1 | ADD1: ;增加联系人 |
提示输入联系人姓名。
09h - 显示字符串
功能描述:显示从DX寄存器指向的内存地址开始的字符串,直到遇到字符串结束符号$
为止。
输入:
DX
:指向要显示的字符串的地址AH
:09h
输出:将字符串显示在屏幕上
1 | mov dx,offset NAME_BUF |
读取用户输入的姓名,存储在NAME_BUF
中。
0Ah - 输入字符串
功能描述:从标准输入设备(通常是键盘)读取字符串并存储到由DX指向的缓冲区中。用户输入的字符串存储在缓冲区中,以回车键(Enter)结束。
输入:
DX
:指向输入缓冲区的地址AH
:0Ah
输出:输入的字符串存储在缓冲区中,第一个字节表示缓冲区的大小,第二个字节表示实际输入的字符数,后续字节存储实际输入的字符。
1 | mov bx, dx ; 将 DX 寄存器的值(缓冲区地址)移动到 BX 寄存器中 |
将姓名字符串的结尾设为$
,以便显示。
mov bx, dx
- 将
DX
寄存器的值移动到BX
寄存器。此时,BX
寄存器中存储的是输入缓冲区的地址。
- 将
mov al, byte ptr [bx+1]
BX
寄存器指向输入缓冲区。- 输入缓冲区的结构是:
- 第一个字节:缓冲区的最大长度
- 第二个字节:实际输入的字符数
- 后续字节:存储实际输入的字符
[bx+1]
是实际输入的字符数的地址。将该值即实际输入的字符数移动到al
寄存器中。
mov byte ptr [bx+si+2], '$'
SI
寄存器中存储的是实际输入的字符数。[bx+si+2]
是输入字符串末尾的下一个位置。- 将
$
字符存储到[bx+si+2]
位置,标志输入字符串的结束。
示例
假设缓冲区 NAME_BUF
定义如下:
1 | NAME_BUF db 20, 0, 20 dup(0) |
输入 "John"
后,缓冲区的内容如下:
1 | 20, 4, 'J', 'o', 'h', 'n' |
执行这段代码后:
BX
指向缓冲区起始地址。- 从
[bx+1]
读取实际输入的字符数4
,存储到SI
。 - 在
[bx+4+2]
即[bx+6]
位置写入$
,缓冲区变为:
1 | 20, 4, 'J', 'o', 'h', 'n', '$' |
这使得输入的字符串以 $
结束,可以使用 DOS 中断 21h, 功能码 09h 显示字符串。
1 | mov dx,offset INPUT_PHONE |
提示输入电话号码。
1 | mov dx,offset PHONE_BUF |
读取用户输入的电话号码,存储在PHONE_BUF
中。
1 | mov bx,dx |
将电话号码字符串的结尾设为$
,以便显示。
1 | ;保存数据 |
计算新联系人在ALL_DATA
中的存储位置。
1 | ;给标志 |
复制姓名数据到ALL_DATA
中的对应位置。
**mov ax, word ptr ds:[bp+si]
**:
- 从
NAME_BUF
(BP
寄存器指向)加上SI
偏移量的位置读取一个字(16 位)到AX
寄存器。
**mov word ptr [bx+si], ax
**:
- 将
AX
中的数据写入到BX
指向的位置加上SI
偏移量的位置。
**add si, 2
**:
- 更新
SI
寄存器,将其偏移量增加 2,移动到下一个字(16 位)的位置。
**dec cx
**:
- 将
CX
寄存器的值减 1,减少循环次数。
**jnz LOOP1
**:
- 如果
CX
不为零,则跳回LOOP1
标签,继续循环
示例
假设 NAME_BUF
的内容如下:
1 | NAME_BUF db 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120 |
执行这段代码时,NAME_BUF
从偏移量 2
开始的数据(30 40
, 50 60
, 70 80
)将被复制到 BX
指向的内存位置,每次复制一个字(16 位)。这段代码假设 NAME_BUF
至少有 16 字节的数据。
1 | mov cx,8 |
复制电话号码数据到ALL_DATA
中的对应位置,并增加联系人数量。
1 | jmp SHOW_MENU |
返回显示菜单。
查询联系人
1 | QUERY1: |
开始查询联系人,从ALL_DATA
中遍历联系人。
1 | lea dx,[bx+2] |
显示联系人姓名和电话号码。
1 | LABEL1: |
处理下一个联系人。
1 | jmp SHOW_MENU |
返回显示菜单。
退出程序
1 | EXIT1: |
退出程序。
默认处理无效输入
1 | DEFAULT1: |
显示输入错误信息,并返回显示菜单。
代码段结束
1 | MyCode ends |
结束代码段,并定义程序入口点为MAIN
。
调试和运行
开始调试
输入姓名和电话
找到存储数据的地址并查看,可以看到数据已经被存入
开始运行
输入数据
查询数据,成功存入数据
成功退出