清空电脑装系统前备份以前文档,汇编的精简学习
# 汇编语言超详细中文文档,
### 核心大纲。计算机基本组成 :CPU、内存、I/O设备的关系,汇编语言本质 :人类可读的机器指令助记符,寄存器概念 :CPU内部的超高速存储单元内存模型 :段地址+偏移地址的寻址方式
# 2023年6月 · 元歌 · 广州软件公司捉鱼学习笔记 ## 📌 文档说明 - **创建时间**: 2023年6月 - **作者**: 元歌(广州软件公司) - **用途**: 捉鱼学习笔记,专为零基础新手打造
学习以下需要的环境, 1. **编辑器**: - 推荐:VS Code(免费)+ 汇编插件 - 备选:Notepad++、Sublime Text 2. **汇编器**: - MASM 6.11(经典版本) - NASM(跨平台推荐) 3. **运行环境**: - DOSBox(模拟DOS环境,Windows 10/11必备) - 或:VMware + MS-DOS 6.22虚拟机 ### 安装步骤(Windows 10/11) ```bash # 1. 安装DOSBox 下载地址:https://www.dosbox.com/download.php 默认安装即可 # 2. 配置工作目录 在D盘创建文件夹:D:\asm_workspace 将汇编文件(.asm)放在这里 # 3. 挂载目录到DOSBox 打开DOSBox,输入: mount c d:\asm_workspace c:
以上当时我是随便做的笔记,最近硬盘空间不哆,可能要重做系统,所以备份一些以前的文档。
; 示例:计算1+2并存储结果
mov ax, 1 ; 将立即数1传送到AX寄存器(AX=1)
mov bx, 2 ; 将立即数2传送到BX寄存器(BX=2)
add ax, bx ; 将AX和BX相加,结果存回AX(AX=AX+BX=3)
mov [0x100], ax ; 将结果3存储到内存地址DS:0100处
编写程序计算1到10的和并存储到内存:示例二
mov cx, 10 ; 循环计数器设为10
mov ax, 0 ; 累加器清零
mov bx, 1 ; 从1开始加
sum_loop: ; 循环标签
add ax, bx ; 累加当前数字
inc bx ; BX自增1(bx=bx+1)
loop sum_loop ; CX减1,不为0则继续循环
mov [0x200], ax ; 存储结果55到内存
示例三 ## 寄存器(CPU工作原理)
# ## 2.1 通用寄存器(对应视频006-007) 核心大纲
- AX累加器 :算术运算主力寄存器
- BX基址寄存器 :常用于内存寻址
- CX计数器 :循环和字符串操作专用
- DX数据寄存器 :I/O操作和乘除法辅助
; 演示寄存器的基本操作 mov ax, 0x1234 ; AX=0x1234(16位立即数) mov ah, 0x56 ; AX高8位=0x56,此时AX=0x5634 mov al, 0x78 ; AX低8位=0x78,此时AX=0x5678 xchg ax, bx ; AX和BX交换内容(交换后AX=原BX,BX=原AX) push ax ; 将AX压入堆栈(SP=SP-2,[SP]=AX) pop bx ; 从堆栈弹出到BX(BX=[SP],SP=SP+2)
### 段寄存器与内存访问(对应视频008-010) 核心大纲
- CS代码段 :存储程序指令
- DS数据段 :存储程序数据
- SS堆栈段 :存储函数调用和临时数据
- ES附加段 :字符串操作的额外数据段
; 演示各种内存寻址方式 mov bx, 0x0100 ; BX作为基址寄存器 mov si, 0x0020 ; SI作为变址寄存器 mov ax, [0x1234] ; 直接寻址:从DS:1234读取数据 mov ax, [bx] ; 间接寻址:从DS:0100读取数据 mov ax, [bx+10] ; 相对寻址:从DS:0110读取数据(0100+0x0A) mov ax, [bx+si] ; 基址变址:从DS:0120读取数据(0100+0020) mov ax, [bx+si+5]; 相对基址变址:从DS:0125读取数据
## 寄存器(内存访问)
### 3.1 内存寻址方式(对应视频013-015) 核心大纲
- 直接寻址 :[偏移地址]
- 寄存器间接寻址 :[BX]
- 寄存器相对寻址 :[BX+偏移量]
- 基址变址寻址 :[BX+SI]
; 演示各种内存寻址方式 mov bx, 0x0100 ; BX作为基址寄存器 mov si, 0x0020 ; SI作为变址寄存器 mov ax, [0x1234] ; 直接寻址:从DS:1234读取数据 mov ax, [bx] ; 间接寻址:从DS:0100读取数据 mov ax, [bx+10] ; 相对寻址:从DS:0110读取数据(0100+0x0A) mov ax, [bx+si] ; 基址变址:从DS:0120读取数据(0100+0020) mov ax, [bx+si+5]; 相对基址变址:从DS:0125读取数据
### 堆栈操作(对应视频016-019) 核心大纲
- PUSH/POP :后进先出的数据存储
- CALL/RET :函数调用的核心机制
- BP寄存器 :栈帧基址指针
mov ax, 0x1000 ; 设置堆栈段地址 mov ss, ax mov sp, 0xFFFE ; 设置栈顶(堆栈向下增长) push ax ; 将AX压栈:SP=SP-2=0xFFFC,[0xFFFC]=AX push bx ; SP=0xFFFA,[0xFFFA]=BX pop cx ; CX=[0xFFFA],SP=0xFFFC pop dx ; DX=[0xFFFC],SP=0xFFFE ; 使用BP访问栈中数据 mov bp, sp ; BP指向当前栈顶 mov ax, [bp+2] ; 访问压入的第一个数据(原AX值)
实战一
- 段定义 :代码段、数据段、堆栈段
- 程序入口 :end start指定程序起点
- DOS中断 :程序退出的21h中断
; 完整的第一个汇编程序
assume cs:code, ds:data, ss:stack ; 告诉汇编器各段与寄存器的对应关系
data segment ; 定义数据段
msg db 'Hello, World!', '$' ; 定义字符串,$是DOS字符串结束符
num dw 1234h ; 定义16位数据
ends
stack segment stack ; 定义堆栈段
dw 128 dup(0) ; 预留128个字的空间
ends
code segment ; 定义代码段
start: ; 程序入口标签
mov ax, data ; 加载数据段地址
mov ds, ax ; 设置DS寄存器
mov ah, 09h ; DOS功能号:显示字符串
lea dx, msg ; DX指向字符串
int 21h ; 调用DOS中断显示字符串
mov ah, 4ch ; DOS功能号:程序结束
int 21h ; 返回DOS
ends
end start ; 程序结束,指定入口点
## [BX]和loop指令
### 5.1 循环结构实现(对应视频023-028) 核心大纲
- LOOP指令 :CX寄存器自动递减的循环
- [BX]寻址 :灵活的内存访问方式
- 数组处理 :批量数据处理技巧
; 使用LOOP指令实现数组求和
data segment
arr db 1,2,3,4,5,6,7,8,9,10 ; 定义10个字节的数组
sum dw 0 ; 存储累加结果
ends
code segment
assume cs:code, ds:data
start:
mov ax, data
mov ds, ax
mov cx, 10 ; 循环次数=数组长度
mov bx, 0 ; BX作为数组索引
mov ax, 0 ; AX存储累加结果
sum_loop: ; 循环开始
add al, arr[bx] ; 将数组元素加到AL
adc ah, 0 ; 处理进位
inc bx ; 索引递增
loop sum_loop ; CX减1,不为0则继续
mov sum, ax ; 存储最终结果
mov ah, 4ch
int 21h
ends
end start
多重循环实现
; 双重循环实现冒泡排序
sort_array proc near
push cx ; 保存寄存器
push si
push di
mov cx, 9 ; 外层循环9次(10个元素)
outer_loop:
mov si, cx ; 保存外层循环计数器
mov di, 0 ; 内层循环索引
mov cx, si ; 内层循环次数=外层剩余次数
inner_loop:
mov al, arr[di] ; 加载当前元素
mov bl, arr[di+1] ; 加载下一个元素
cmp al, bl ; 比较两个元素
jbe no_swap ; 如果有序则跳过交换
; 交换两个元素
mov arr[di], bl
mov arr[di+1], al
no_swap:
inc di ; 索引递增
loop inner_loop ; 内层循环
mov cx, si ; 恢复外层循环计数器
loop outer_loop ; 外层循环
pop di ; 恢复寄存器
pop si
pop cx
ret
sort_array endp
## 包含多个段的程序
### 6.1 复杂程序结构(对应视频029-031) 核心大纲
- 段间数据访问 :跨段数据处理
- 段的重叠 :灵活的内存布局
- 大型程序组织 :模块化设计思想
; 多段程序示例:学生成绩管理系统
datas segment ; 数据段1:学生信息
names db 'Tom',0,'Ann',0,'Bob',0 ; 学生姓名
scores db 85,92,78 ; 对应成绩
count equ 3 ; 学生数量
ends
datas2 segment ; 数据段2:统计结果
total dw 0 ; 总分
average db 0 ; 平均分
ends
stack segment stack ; 堆栈段
dw 256 dup(0) ; 512字节堆栈空间
ends
code segment
assume cs:code, ds:datas, es:datas2, ss:stack
start:
mov ax, datas ; 加载数据段1
mov ds, ax
mov ax, datas2 ; 加载数据段2
mov es, ax
; 计算总分
mov cx, count ; 学生数量
mov si, 0 ; 成绩索引
mov ax, 0 ; 累加器清零
calc_total:
add al, scores[si] ; 累加成绩
adc ah, 0 ; 处理进位
inc si ; 下一个成绩
loop calc_total
mov es:total, ax ; 存储到另一个段
; 计算平均分
mov bl, count ; 除数=学生数量
div bl ; AX/BL,商在AL,余数在AH
mov es:average, al ; 存储平均分
mov ah, 4ch
int 21h
ends
end start
## 更灵活定位内存地址
### 7.1 寻址方式综合运用(对应视频032-037) 核心大纲
- and/or指令 :位操作基础
- ASCII码处理 :字符数据操作
- [bx+idata]寻址 :更灵活的地址计算
; 字符串大小写转换程序
data segment
str db 'Hello ASM World!',0 ; 待转换字符串
len equ $-str-1 ; 字符串长度(不含0)
ends
code segment
assume cs:code, ds:data
start:
mov ax, data
mov ds, ax
mov cx, len ; 循环次数=字符串长度
mov bx, 0 ; 字符串索引
convert_loop:
mov al, str[bx] ; 加载字符
cmp al, 'a' ; 判断是否小写字母
jb not_lower ; 小于'a'则跳过
cmp al, 'z' ; 判断是否小写字母
ja not_lower ; 大于'z'则跳过
; 小写转大写:AND 0xDF(清第5位)
and al, 0xDF ; 小写转大写
mov str[bx], al ; 存回字符串
not_lower:
inc bx ; 下一个字符
loop convert_loop
mov ah, 4ch
int 21h
ends
end start
附列二
; 3x4矩阵转置程序
data segment
matrix db 1,2,3,4 ; 原始矩阵3行4列
db 5,6,7,8
db 9,10,11,12
rows equ 3 ; 行数
cols equ 4 ; 列数
result db 12 dup(0) ; 转置结果4行3列
ends
code segment
assume cs:code, ds:data
start:
mov ax, data
mov ds, ax
mov si, 0 ; 源矩阵索引
mov di, 0 ; 目标矩阵索引
mov cx, rows ; 外层循环=原矩阵行数
row_loop:
push cx ; 保存外层计数器
mov cx, cols ; 内层循环=原矩阵列数
mov di, 0 ; 重置目标列索引
col_loop:
mov al, matrix[si] ; 加载源矩阵元素
; 计算转置位置:result[col*rows+row]
mov bl, cl ; 当前列号
dec bl ; 调整为0基
mov ah, rows ; 每列元素数=原行数
mul ah ; AX=col*rows
add al, ch ; 加上当前行号
mov bx, ax ; BX=目标索引
mov result[bx], al ; 存储到转置位置
inc si ; 源矩阵下一个元素
loop col_loop
pop cx ; 恢复外层计数器
loop row_loop
mov ah, 4ch
int 21h
ends
end start
## 数据处理的两个基本问题
### 8.1 数据位置与长度(对应视频038-041) 核心大纲
- 立即数寻址 :数据在指令中
- 寄存器寻址 :数据在寄存器中
- 内存寻址 :数据在内存中
- 数据长度指定 :byte ptr/word ptr
; 演示各种数据位置的处理 mov al, 100 ; 立即数:100直接编码在指令中 mov ax, bx ; 寄存器:数据在BX寄存器中 mov al, [0x100] ; 内存:数据在内存地址DS:0100处 ; 明确指定数据长度 mov byte ptr [0x200], 0x55 ; 明确指定按字节操作 mov word ptr [0x200], 0x1234 ; 明确指定按字操作 ; 不同长度的数据处理 mov al, byte ptr [0x100] ; 从内存读取1字节 mov ax, word ptr [0x100] ; 从内存读取2字节(0x100和0x101)
其它例子
; 学生结构体数组处理
data segment
student struct ; 定义学生结构体
id db 0 ; 学号1字节
name db 'ABC',0 ; 姓名4字节(含结束符)
score dw 0 ; 成绩2字节
student ends
; 创建3个学生的结构体数组
students student 3 dup(<>)
; 初始化数据
db 1,'Tom',0,0,0,85
db 2,'Ann',0,0,0,92
db 3,'Bob',0,0,0,78
ends
code segment
assume cs:code, ds:data
start:
mov ax, data
mov ds, ax
; 计算学生总数
mov cx, 3 ; 学生数量
mov bx, 0 ; 结构体索引
mov si, 0 ; 结构体内偏移
calc_average:
mov al, students[bx].score ; 加载学生成绩
mov ah, 0 ; 扩展为16位
add total, ax ; 累加到总分
add bx, sizeof student ; 下一个结构体
loop calc_average
; 计算平均分
mov ax, total
mov bl, 3
div bl ; AL=平均分
mov ah, 4ch
int 21h
ends
end start
## 转移指令的原理
### 9.1 条件转移指令(对应视频043-046) 核心大纲
- 标志位判断 :ZF、CF、SF、OF的含义
- 无符号比较 :JA/JB/JE等指令
- 有符号比较 :JG/JL/JE等指令
; 成绩等级判断程序
data segment
score db 85 ; 测试成绩
grade db 0 ; 存储等级A/B/C/D/F
ends
code segment
assume cs:code, ds:data
start:
mov ax, data
mov ds, ax
mov al, score ; 加载成绩
cmp al, 90 ; 与90比较
jae grade_a ; >=90跳转到A级
cmp al, 80 ; 与80比较
jae grade_b ; >=80跳转到B级
cmp al, 70 ; 与70比较
jae grade_c ; >=70跳转到C级
cmp al, 60 ; 与60比较
jae grade_d ; >=60跳转到D级
mov grade, 'F' ; 否则为F级
jmp done
grade_a:
mov grade, 'A'
jmp done
grade_b:
mov grade, 'B'
jmp done
grade_c:
mov grade, 'C'
jmp done
grade_d:
mov grade, 'D'
done:
mov ah, 4ch
int 21h
ends
end start
## CALL和RET指令
### 10.1 子程序设计(对应视频048-053) 核心大纲
- CALL调用 :保存返回地址并跳转
- RET返回 :从堆栈恢复返回地址
- 参数传递 :寄存器/堆栈/内存传递方式
; 子程序示例:计算阶乘
stack segment stack
dw 256 dup(0)
ends
data segment
n db 5 ; 计算5的阶乘
result dw 0 ; 存储结果
ends
code segment
assume cs:code, ds:data, ss:stack
; 阶乘子程序:输入AL=n,输出AX=n!
factorial proc near
push bp ; 保存旧的BP
mov bp, sp ; 建立栈帧
push cx ; 保存使用的寄存器
mov cx, ax ; CX=循环计数器=n
mov ax, 1 ; AX=累乘结果=1
fact_loop:
mul cx ; AX=AX*CX
loop fact_loop ; CX减1,不为0继续
pop cx ; 恢复寄存器
pop bp ; 恢复BP
ret ; 返回调用点
factorial endp
start:
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
mov sp, 256
mov al, n ; 加载参数n
mov ah, 0 ; 扩展为16位
call factorial ; 调用阶乘子程序
mov result, ax ; 保存结果(5! = 120)
mov ah, 4ch
int 21h
ends
end start
## 标志寄存器
### 11.1 标志位详解(对应视频054-059) 核心大纲
- 状态标志 :ZF、CF、SF、OF、PF、AF
- 控制标志 :DF、IF、TF
- 标志位操作 :LAHF/SAHF、PUSHF/POPF
; 标志位操作演示
data segment
a dw 0x7FFF ; 最大正数
b dw 1 ; 加1会溢出
flags dw 0 ; 存储标志位
ends
code segment
assume cs:code, ds:data
start:
mov ax, data
mov ds, ax
; 演示溢出标志OF
mov ax, a ; AX=0x7FFF(32767)
add ax, b ; 0x7FFF+1=0x8000(-32768)产生溢出
pushf ; 将标志寄存器压栈
pop bx ; BX=标志寄存器值
mov flags, bx ; 存储标志位
; 检查各标志位
test bx, 0x8000 ; 检查SF(符号位)
jnz negative ; 结果为负
test bx, 0x0040 ; 检查ZF(零标志)
jz not_zero ; 结果不为零
test bx, 0x0001 ; 检查CF(进位标志)
jnc no_carry ; 无进位
negative:
; 处理负数结果
jmp done
not_zero:
; 处理非零结果
jmp done
no_carry:
; 处理无进位情况
done:
mov ah, 4ch
int 21h
ends
end start
## 内中断
### 12.1 中断处理机制(对应视频060-063) 核心大纲
- 中断向量表 :0000:0000开始的256个中断入口
- 中断过程 :CPU自动保存现场并跳转
- IRET指令 :中断返回
; 自定义除零中断处理程序
data segment
msg db 'Division by zero!', '$'
ends
code segment
assume cs:code, ds:data
; 自定义中断处理程序
div_zero_handler proc far
push ax
push dx
push ds
mov ax, data
mov ds, ax
; 显示错误信息
mov ah, 09h
lea dx, msg
int 21h
pop ds
pop dx
pop ax
iret ; 中断返回
endp
start:
; 这里只是概念演示,实际设置中断向量需要修改中断向量表
mov ah, 4ch
int 21h
ends
end start
## INT指令
### 13.1 BIOS/DOS中断调用(对应视频064-066) 核心大纲
- INT 21h :DOS系统调用
- INT 10h :BIOS显示服务
- INT 16h :BIOS键盘服务
; 使用BIOS显示字符串
stack segment stack
dw 256 dup(0)
ends
data segment
str db 'Hello BIOS!', 0Dh, 0Ah, '$' ; 字符串含回车换行
ends
code segment
assume cs:code, ds:data, ss:stack
start:
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
mov sp, 256
; 设置光标位置(第5行第10列)
mov ah, 02h ; BIOS功能:设置光标
mov bh, 0 ; 显示页号0
mov dh, 5 ; 行号5
mov dl, 10 ; 列号10
int 10h ; 调用BIOS
; 显示字符串
mov ah, 09h ; DOS功能:显示字符串
lea dx, str ; DX指向字符串
int 21h ; 调用DOS
; 等待按键
mov ah, 00h ; BIOS功能:读取键盘
int 16h ; 等待按键
mov ah, 4ch
int 21h
ends
end start
## 端口 核心大纲
- IN/OUT指令 :CPU与硬件设备通信
- 端口地址 :00h-FFh的256个端口
- 设备控制 :通过端口控制外设
; 演示端口操作(概念性,实际硬件相关)
; 控制PC扬声器发声
code segment
assume cs:code
start:
; 设置扬声器频率(概念演示)
mov al, 0xB6 ; 控制字:选择计数器2,方波模式
out 0x43, al ; 发送到控制端口43h
mov ax, 1193 ; 1000Hz频率(1193180/1000)
out 0x42, al ; 发送低8位到端口42h
mov al, ah
out 0x42, al ; 发送高8位到端口42h
; 打开扬声器
in al, 0x61 ; 读取端口61h当前值
or al, 0x03 ; 设置位0和位1(开启扬声器)
out 0x61, al ; 写回端口61h
; 延时(简单循环)
mov cx, 0xFFFF
delay:
loop delay
; 关闭扬声器
in al, 0x61
and al, 0xFC ; 清除位0和位1
out 0x61, al
mov ah, 4ch
int 21h
ends
end start
## 外中断 核心大纲
- 可屏蔽中断 :由IF标志控制
- 非屏蔽中断 :NMI,优先级最高
- 中断控制器 :8259A可编程中断控制器
; 键盘中断处理示例(概念性)
code segment
assume cs:code
; 键盘中断处理程序(IRQ1,中断号09h)
keyboard_handler proc far
push ax
push bx
in al, 0x60 ; 从键盘端口读取扫描码
; 处理扫描码(这里只是存储)
mov bl, al ; 保存扫描码
; 发送EOI(中断结束)给8259A
mov al, 0x20
out 0x20, al
pop bx
pop ax
iret
endp
start:
; 中断处理程序的安装需要修改中断向量表
; 这里只是概念演示
mov ah, 4ch
int 21h
ends
end start
## 直接定址表
查表法应用 - 查表法 :用空间换时间的算法 - 地址表 :函数指针数组
- 跳转表 :实现多分支选择
; 星期显示程序:使用查表法
stack segment stack
dw 256 dup(0)
ends
data segment
weekday db 3 ; 星期三(0=周日,6=周六)
; 星期字符串表
week_table dw offset sun, offset mon, offset tue
dw offset wed, offset thu, offset fri, offset sat
sun db 'Sunday$',0
mon db 'Monday$',0
tue db 'Tuesday$',0
wed db 'Wednesday$',0
thu db 'Thursday$',0
fri db 'Friday$',0
sat db 'Saturday$',0
ends
code segment
assume cs:code, ds:data, ss:stack
start:
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
mov sp, 256
mov al, weekday ; 加载星期索引
mov ah, 0 ; 清零高8位
shl ax, 1 ; 乘以2(字偏移)
mov bx, ax ; BX=偏移量
mov ax, week_table[bx] ; 从地址表获取字符串偏移
mov dx, ax ; DX=字符串地址
mov ah, 09h ; 显示字符串
int 21h
mov ah, 4ch
int 21h
ends
end start
## 使用BIOS进行键盘输入和磁盘读写
### 17.1 键盘输入处理(对应视频075-077) 核心大纲
- INT 16h :键盘BIOS中断
- 扫描码与ASCII码 :键盘输入的双重编码
- 磁盘读写 :INT 13h磁盘服务
; 完整键盘输入和文件操作示例
stack segment stack
dw 256 dup(0)
ends
data segment
filename db 'test.txt',0 ; 文件名
buffer db 512 dup(0) ; 读写缓冲区
key_msg db 'Press any key...$',0
ends
code segment
assume cs:code, ds:data, ss:stack
start:
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
mov sp, 256
; 显示提示信息
mov ah, 09h
lea dx, key_msg
int 21h
; 等待按键
mov ah, 00h ; BIOS功能:读取键盘
int 16h ; AL=ASCII码,AH=扫描码
; 显示按下的键
mov dl, al ; DL=ASCII字符
mov ah, 02h ; DOS功能:显示字符
int 21h
; 磁盘操作示例(概念性)
; 实际文件操作需使用DOS INT 21h的文件功能
mov ah, 4ch
int 21h
ends
end start
### 简易计算器 项目要求
实现一个支持加减乘除的命令行计算器
; 简易计算器:支持+ - * /
stack segment stack
dw 256 dup(0)
ends
data segment
prompt db 'Enter expression (e.g., 5+3): $'
result_msg db 13,10,'Result: $'
num1 dw 0 ; 第一个操作数
num2 dw 0 ; 第二个操作数
op db 0 ; 运算符
buffer db 10 dup(0) ; 输入缓冲区
ends
code segment
assume cs:code, ds:data, ss:stack
; 数字输入子程序:将ASCII数字转为二进制
decimal_input proc near
push bx
push cx
push dx
xor ax, ax ; AX=结果
xor cx, cx ; CX=数字计数
input_loop:
mov ah, 01h ; DOS功能:读取字符并回显
int 21h
cmp al, 13 ; 回车结束
je input_done
cmp al, '0' ; 检查是否为数字
jb input_done
cmp al, '9'
ja input_done
sub al, '0' ; ASCII转数字
mov bl, al
mov ax, cx ; 当前结果*10
mov dx, 10
mul dx
add ax, bx ; 加上新数字
mov cx, ax
jmp input_loop
input_done:
mov ax, cx ; 返回结果
pop dx
pop cx
pop bx
ret
decimal_input endp
; 显示结果子程序:二进制转ASCII显示
display_result proc near
push bx
push cx
push dx
mov cx, 0 ; 数字位数计数
mov bx, 10 ; 除数
convert_loop:
xor dx, dx ; DX清零
div bx ; AX/10,余数在DX
push dx ; 保存余数
inc cx ; 位数+1
cmp ax, 0
jne convert_loop
; 显示数字
show_loop:
pop dx ; 取出数字
add dl, '0' ; 转为ASCII
mov ah, 02h ; 显示字符
int 21h
loop show_loop
pop dx
pop cx
pop bx
ret
display_result endp
start:
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
mov sp, 256
; 显示提示
mov ah, 09h
lea dx, prompt
int 21h
; 输入第一个数字
call decimal_input
mov num1, ax
; 输入运算符
mov ah, 01h
int 21h
mov op, al
; 输入第二个数字
call decimal_input
mov num2, ax
; 执行运算
mov ax, num1
mov bx, num2
cmp op, '+'
je do_add
cmp op, '-'
je do_sub
cmp op, '*'
je do_mul
cmp op, '/'
je do_div
jmp done
do_add:
add ax, bx
jmp show_result
do_sub:
sub ax, bx
jmp show_result
do_mul:
mul bx ; AX*BX,结果在DX:AX
jmp show_result
do_div:
xor dx, dx ; DX清零
div bx ; DX:AX/BX,商在AX
show_result:
push ax ; 保存结果
; 显示结果标签
mov ah, 09h
lea dx, result_msg
int 21h
pop ax ; 恢复结果
call display_result
done:
mov ah, 4ch
int 21h
ends
end start
当是钞的笔记,如果有空的话我们应写一个对应的c 来参考学习,,

浙公网安备 33010602011771号