汇编:int指令、BIOS与DOS中断、端口
int指令
一、int 指令的核心定义
int(Interrupt,中断指令)是 x86 汇编中的核心指令,用于主动触发软件中断,本质是 “自愿” 调用中断处理程序(中断服务例程,ISR)。它会暂停当前程序执行,切换到操作系统 / BIOS 预设的中断处理逻辑,完成后再返回原程序继续执行。
指令格式
int n ; n为中断号(0~255,8位立即数)
其中n是中断向量号,对应中断向量表(IVT) 中存储的中断处理程序入口地址(实模式下 IVT 位于内存 0x0000~0x03FF,每个中断占 4 字节:段地址 + 偏移地址)。
二、int 指令的执行过程(实模式为例)
CPU 执行int n时,会自动完成以下步骤(保护模式逻辑类似,但中断表为 IDT,地址结构不同):
- 标志寄存器入栈:将 FLAGS 寄存器压栈,随后清除 IF(中断使能)和 TF(单步)标志,避免嵌套中断干扰。
- 返回地址入栈:先压栈 CS(代码段寄存器),再压栈 IP(指令指针),保存当前执行位置(中断处理完成后通过
iret恢复)。 - 查找中断处理程序:计算中断向量表地址:
地址 = n * 4,从该地址读取 4 字节(高 2 字节 = CS,低 2 字节 = IP)。 - 跳转到中断处理程序:将读取的 CS/IP 加载到寄存器,执行中断服务例程。
- 中断返回:处理程序执行
iret指令,反向恢复 IP、CS、FLAGS,回到原程序继续执行。
三、常见用途(经典场景)
int指令是 DOS/BIOS 时代与系统交互的核心方式,典型中断号及功能:
| 中断号 | 功能 | 调用示例(DOS 实模式) |
|---|---|---|
| 0x00 | 除法错误(除零) | 被动触发(CPU 自动),非手动调用 |
| 0x03 | 断点中断(调试) | 调试器(如 DEBUG)用int 3设置断点 |
| 0x10 | BIOS 视频服务 | mov ah,0x0E; mov al,'A'; int 0x10(打印字符) |
| 0x20 | DOS 程序退出 | int 0x20(终止当前程序) |
| 0x21 | DOS 系统调用 | mov ah,0x01; int 0x21(读取键盘输入) |
| 0x80 | Linux 系统调用(32 位) | mov eax,1; int 0x80(进程退出,对应 exit) |
int 21H DOS中断例程的应用
这段 8086 汇编代码的核心功能是:在 DOS 环境下,将字符串 Welcome to masm! 显示在屏幕的第 5 行、第 12 列位置。整体流程分为三步:设置光标位置 → 输出字符串 → 程序退出。
assume cs: code ; 声明代码段寄存器cs关联到code段
data segment ; 定义数据段,存放要显示的字符串
db ' Welcome to masm!','$' ; 字符串以'$'结尾(DOS中断int 21h的09h功能要求)
data ends ; 数据段结束
code segment ; 定义代码段,存放执行逻辑
start: ; 程序入口标签
mov ah,2 ; ah=2:功能号,调用int 10h的光标位置设置功能
mov bh,0 ; bh=0:指定显示页为第0页(DOS默认显示页)
mov dh,5 ; dh=5:光标行号(从0开始计数,第5行对应屏幕实际第6行)
mov dl,12 ; dl=12:光标列号(从0开始计数,第12列)
int 10h ; 调用BIOS中断,完成光标定位
mov ax, data ; 将数据段段地址送入ax(8086不能直接给ds赋值,需通过ax中转)
mov ds, ax ; ds=data:设置数据段寄存器,指向字符串所在的段
mov dx,0 ; dx=0:字符串在数据段内的偏移地址(字符串从data段偏移0处开始)
mov ah,9 ; ah=9:调用DOS中断int 21h的字符串输出功能
int 21h ; 执行中断,输出ds:dx指向的字符串(直到'$'结束)
mov ax,4c00h ; ax=4c00h:ah=4Ch(退出功能号),al=00h(返回码,通常设为0)
int 21h ; 调用DOS中断,终止程序并返回DOS系统
code ends ; 代码段结束
end start ; 汇编结束,指定程序入口为start标签
关键:int 21h的09h功能 要求字符串必须以'$'结尾,否则会输出乱码。
运行效果:

关键知识点总结
| 中断 / 功能号 | 功能 | 寄存器参数要求 |
|---|---|---|
int 10h (ah=2) | 设置光标位置 | bh = 显示页,dh = 行号,dl = 列号 |
int 21h (ah=9) | 输出字符串 | ds:dx 指向字符串首地址,字符串以 '$' 结尾 |
int 21h (ah=4Ch) | 程序退出 | al = 返回码(通常为 0) |
I/O端口的读写
在 8086 CPU 中,I/O 端口具有独立的地址空间,这被称为 “独立 I/O 编址”(也称为 “端口映射 I/O” 或 “Port-Mapped I/O, PMIO”)。
具体说明:
8086 CPU 使用 16 位地址线 来寻址 I/O 端口,因此最多可以寻址 2^16 = 65536(即 64KB)个 I/O 端口地址,地址范围为 0x0000 到 0xFFFF。
这个 I/O 地址空间 与内存地址空间是分开的。也就是说,I/O 端口地址和内存地址互不冲突,各自独立编址。
访问 I/O 端口使用专门的指令:
IN 指令:从 I/O 端口读取数据(如 IN AL, DX)。
OUT 指令:向 I/O 端口写入数据(如 OUT DX, AL)。
对比:统一编址(Memory-Mapped I/O)
在某些其他体系结构(如 ARM、现代 x86 的部分外设)中,I/O 设备被映射到内存地址空间中,称为 统一编址 或 内存映射 I/O(MMIO)。这种情况下,访问 I/O 就像访问普通内存一样,使用 MOV 等内存访问指令。
但在 8086 架构中采用的是独立 I/O 编址方式,这是其设计特点之一。
✅ 总结:
8086 CPU 中,I/O 端口确实拥有 独立于内存的地址空间,使用专用的 IN/OUT 指令进行访问,地址范围为 0x0000 ~ 0xFFFF(共 64KB)。
8086CPU的CMOS端口
8086CPU 通过70H(地址端口)和71H(数据端口)两个专用 I/O 端口与 CMOS RAM 芯片交互,这两个端口采用地址 - 数据分离的访问方式,是操作 CMOS 实时钟和系统配置信息的核心通道。
一、CMOS 端口的核心结构与功能
CMOS RAM 芯片内置 128 字节 RAM(早期为 64 字节),包含实时钟、系统配置信息等,靠电池供电保证断电后数据不丢失。8086CPU 通过 70H 和 71H 端口实现对 CMOS 的读写,具体分工如下:
| 端口地址 | 类型 | 权限 | 功能 |
|---|---|---|---|
| 70H | 地址端口 | 只写(8bit) | 写入要访问的 CMOS RAM 单元地址(低 6 位为有效地址,对应 CMOS 的 00H~3FH 单元) |
| 71H | 数据端口 | 可读可写(8bit) | 读取或写入 70H 端口指定的 CMOS RAM 单元数据 |
二、8086CPU 访问 CMOS 端口的流程
访问 CMOS RAM 需分两步执行OUT和IN指令,先指定地址,再读写数据:
- 写地址到 70H 端口:用
OUT 70H, AL指令将要读取的目标CMOS 单元地址送入地址端口; - 读写 71H 数据端口:
- 读操作:用
IN AL, 71H读取指定单元的数据; - 写操作:用
OUT 71H, AL将数据写入指定单元。
- 读操作:用
示例:读取 CMOS 的 “分钟” 信息(CMOS 02H 单元存储分钟)
; 读取CMOS 02H单元的分钟数据
MOV AL, 02H ; 将CMOS单元地址02H送入AL
OUT 70H, AL ; 输出到地址端口70H
IN AL, 71H ; 从数据端口71H读取分钟数据(BCD码格式)
三、CMOS 端口的典型应用场景
CMOS 的 00H~0DH 单元用于存储实时钟信息,其余单元保存系统配置(如硬盘类型、内存容量等),通过 70H/71H 端口可实现以下操作:
- 读取系统时间:访问 00H(秒)、02H(分)、04H(时)等单元;
- 修改系统配置:向 10H(软盘类型)、12H(硬盘类型)等单元写入配置数据;
- 控制实时钟工作模式:通过 0AH(状态寄存器 A)、0BH(状态寄存器 B)设置时钟频率、中断使能等。
四、BCD码
在 x86 架构的 PC 系统中,CMOS RAM(通常由 RTC 芯片如 Motorola MC146818 或其兼容芯片实现)使用一组固定的 I/O 端口(0x70 为索引端口,0x71 为数据端口)来访问内部寄存器。时间信息以 BCD 格式(默认,除非设置为二进制模式)存储在特定地址(偏移)中。
以下是 当前时间(2025年12月10日,星期三 21:30) 在 CMOS RAM 中的存放形式,包括 寄存器地址、字段含义、十进制值、BCD 编码(十六进制表示) 的完整表格:

注:年、月、日、秒、分、时,每个信息的长度为1个字节。
问题:从CMOS RAM的8号单元中读出当前月份的BCD码,以十进制形式显示到屏幕上
assume cs:code
code segment
start:
; 步骤1:读取CMOS 8号寄存器(小时数,BCD码)
mov al,8 ; CMOS地址寄存器端口70H写入要读取的寄存器号8
out 70h,al ; 将寄存器号写入70H端口
in al,71h ; 从71H数据端口读取8号寄存器的值到AL
; 步骤2:拆分高4位(十位)和低4位(个位)
mov ah,al ; 暂存AL的值到AH
mov cl,4
shr ah,cl ; AH右移4位,得到十位(高4位)
and al,00001111B ; AL与运算,得到个位(低4位)
; 步骤3:BCD码转ASCII码(+30H是数字0-9的ASCII偏移)
add ah,30h ; 十位转ASCII
add al,30h ; 个位转ASCII
; 步骤4:写入显存(B800H是文本模式显存段地址)
mov bx,0B800H
mov es,bx ; 修正:原ex是笔误,应为es(段寄存器)
; 屏幕第12行40列:显存偏移=160*行 + 2*列(每个字符占2字节:ASCII+属性)
mov byte ptr es:[160*12 + 40*2], ah ; 十位ASCII写入40列
mov byte ptr es:[160*12 + 40*2 + 2], al ; 个位ASCII写入41列
; 步骤5:程序退出
mov ax,4c00h
int 21h ; DOS中断,程序正常退出
code ends
end start

关键逻辑解释
| 步骤 | 详细说明 |
|---|---|
| CMOS 读取 | CMOS 时钟的端口:70H 是地址端口(写入要读取的寄存器号),71H 是数据端口(读取对应值)。8 号寄存器存储小时数(BCD 格式,如 15 点存储为 0x15)。 |
| 拆分高低 4 位 | shr ah,4 将 AH(原 AL)右移 4 位,提取高 4 位(十位),即:0001 0010->0000 0001,12月中的1;and al,0x0F 保留低 4 位(个位),即:0001 0010 -> 0000 0010,12月中的2。 |
| BCD 转 ASCII | 数字 0-9 的 ASCII 码是 0x30-0x39,因此加 0x30 即可完成转换。 |
| 显存写入 | 文本模式显存段地址为 0xB800,每个字符占 2 字节:第 1 字节是 ASCII 码。第 12 行 40 列的偏移计算:160*12 + 2*40(每行 80 字符,共 160 字节)。 |

浙公网安备 33010602011771号