faryou 发布的文章

前言

在8086汇编中,“中断安装中断”可以理解为通过一个自定义中断来统一管理其他中断服务程序的安装/卸载,实现中断向量的动态配置。以下是一个实现这一功能的代码,通过int 62h作为“中断安装中断”,支持注册新中断、卸载已有中断。

代码实现

; 常量定义
VECTOR_TABLE_SEG equ 0 ; 中断向量表段地址(固定为0段)
MAX_INT_NUM equ 0FFh ; 最大中断号(0-255)
 
code segment
    assume cs:code, ds:data
 
; ------------------------------
; 中断安装中断服务程序(入口:AH=功能号)
; 功能:
; AH=00h:安装中断(AL=中断号,DS:SI=中断服务程序地址)
; AH=01h:卸载中断(AL=中断号,恢复为默认处理程序)
; AH=02h:查询中断(AL=中断号,返回DS:SI=当前中断服务程序地址)
; ------------------------------
install_int proc far
    push ax
    push bx
    push cx
    push dx
    push si
    push di
    push es
    push ds ; 保存所有寄存器
    push bp
    mov bp, sp
 
    ; 检查中断号合法性(0-255)
    cmp al, MAX_INT_NUM
    ja int_end ; 中断号超出范围,不处理
 
    ; 根据功能号跳转
    cmp ah, 00h
    je install ; 安装中断
    cmp ah, 01h
    je uninstall ; 卸载中断
    cmp ah, 02h
    je query ; 查询中断
    jmp int_end
 
; ------------------------------
; 功能00h:安装中断
; 入口:AL=中断号,DS:SI=中断服务程序(段:偏移)
; 操作:将DS:SI写入中断向量表对应位置
; ------------------------------
install:
    ; 计算中断向量在表中的地址:中断号×4(每个向量占4字节:偏移2字节+段2字节)
    mov bl, al
    mov bh, 0
    shl bx, 2 ; BX = 中断号×4(向量表偏移)
 
    ; 指向中断向量表(0段)
    mov ax, VECTOR_TABLE_SEG
    mov es, ax
 
    ; 关中断,保证写入向量的原子性
    cli
    ; 保存原中断向量(用于卸载时恢复)
    mov ax, es:[bx] ; 原偏移
    mov [old_vector + bx], ax
    mov ax, es:[bx + 2] ; 原段
    mov [old_vector + bx + 2], ax
    ; 写入新中断向量(DS:SI)
    mov ax, si ; 新偏移
    mov es:[bx], ax
    mov ax, ds ; 新段
    mov es:[bx + 2], ax
    sti ; 开中断
 
    jmp int_end
 
; ------------------------------
; 功能01h:卸载中断
; 入口:AL=中断号
; 操作:恢复中断向量表中该中断号的原始值
; ------------------------------
uninstall:
    mov bl, al
    mov bh, 0
    shl bx, 2 ; BX=中断号×4
 
    mov ax, VECTOR_TABLE_SEG
    mov es, ax
 
    cli ; 关中断
    ; 恢复原向量(从保存的old_vector中读取)
    mov ax, [old_vector + bx] ; 原偏移
    mov es:[bx], ax
    mov ax, [old_vector + bx + 2] ; 原段
    mov es:[bx + 2], ax
    sti ; 开中断
 
    jmp int_end
 
; ------------------------------
; 功能02h:查询中断
; 入口:AL=中断号;返回:DS:SI=当前中断服务程序地址
; ------------------------------
query:
    mov bl, al
    mov bh, 0
    shl bx, 2 ; BX=中断号×4
 
    mov ax, VECTOR_TABLE_SEG
    mov es, ax
 
    ; 读取当前向量(偏移→SI,段→DS)
    mov si, es:[bx] ; 当前偏移
    mov dx, es:[bx + 2] ; 当前段
    mov ds, dx ; DS=当前段
 
    jmp int_end
 
; ------------------------------
; 中断结束:恢复寄存器并返回
; ------------------------------
int_end:
    pop bp
    pop ds
    pop es
    pop di
    pop si
    pop dx
    pop cx
    pop bx
    pop ax
    iret ; 中断返回
install_int endp
 
; ------------------------------
; 初始化:安装"中断安装中断"自身(int 62h)
; ------------------------------
init proc
    cli ; 关中断
    mov ax, VECTOR_TABLE_SEG
    mov es, ax
    mov di, 62h * 4 ; int 62h的向量地址:62h×4=0000:0188
    ; 保存int 62h的原向量(用于程序退出时恢复)
    mov ax, es:[di]
    mov [orig_62h_off], ax
    mov ax, es:[di + 2]
    mov [orig_62h_seg], ax
    ; 写入自定义中断服务程序地址
    mov ax, offset install_int
    stosw ; 偏移
    mov ax, cs
    stosw ; 段
    sti ; 开中断
    ret
init endp
 
; ------------------------------
; 退出:恢复int 62h的原始向量
; ------------------------------
exit proc
    cli
    mov ax, VECTOR_TABLE_SEG
    mov es, ax
    mov di, 62h * 4
    mov ax, [orig_62h_off]
    stosw
    mov ax, [orig_62h_seg]
    stosw
    sti
    ; 调用DOS退出
    mov ah, 4Ch
    int 21h
    ret
exit proc
 
; ------------------------------
; 测试用的示例中断服务程序(如键盘中断处理)
; ------------------------------
test_int proc far
    push ax
    mov ah, 02h
    mov dl, 'A' ; 触发时显示'A'
    int 21h
    pop ax
    iret
test_int endp
 
; ------------------------------
; 主程序:测试中断安装功能
; ------------------------------
main:
    mov ax, data
    mov ds, ax ; 初始化数据段
 
    call init ; 安装int 62h(中断安装中断)
 
    ; 测试1:通过int 62h安装test_int到int 63h
    mov ah, 00h ; 功能:安装
    mov al, 63h ; 目标中断号63h
    mov si, offset test_int ; 中断服务程序偏移
    ; DS已指向data,此处需将DS改为test_int所在段(即CS)
    push cs
    pop ds
    int 62h ; 调用中断安装中断
    ; 恢复DS为data段
    mov ax, data
    mov ds, ax
 
    ; 测试2:调用int 63h,验证是否安装成功
    int 63h ; 应显示'A'
 
    ; 测试3:通过int 62h查询int 63h的当前服务程序
    mov ah, 02h ; 功能:查询
    mov al, 63h ; 中断号63h
    int 62h ; 返回DS:SI=test_int地址(可自行验证)
 
    ; 测试4:通过int 62h卸载int 63h
    mov ah, 01h ; 功能:卸载
    mov al, 63h ; 中断号63h
    int 62h
 
    ; 退出程序(恢复环境)
    call exit
 
code ends
 
; 数据段:保存原始中断向量
data segment
    orig_62h_off dw 0 ; int 62h原始偏移
    orig_62h_seg dw 0 ; int 62h原始段
    ; 保存各中断的原始向量(每个中断占4字节,共256个中断)
    old_vector dw MAX_INT_NUM * 2 dup(0) ; 256×4字节=1024字节
data ends
 
    end main

代码说明

  1. 核心功能设计:自定义int 62h作为“中断安装中断”,提供三类操作:a.安装中断(AH=00h):将指定中断号与服务程序绑定,保存原始向量以便后续恢复。b.卸载中断(AH=01h):将中断号的向量恢复为安装前的原始值。c.查询中断(AH=02h):获取指定中断号当前绑定的服务程序地址。
  2. 中断向量表操作:中断向量表位于0段,每个中断号对应4字节(偏移2字节+段2字节)。通过 中断号×4 计算向量地址,操作时需关中断( cli )保证原子性,避免写入过程被其他中断打断。
  3. 自安装与恢复:程序初始化时,将自身的 install_int 服务程序注册到int 62h,并保存int 62h的原始向量;程序退出时恢复原始向量,避免破坏系统环境。
  4. 测试逻辑:主程序通过int 62h安装一个测试中断(int 63h),调用该中断验证效果,再查询、卸载,完整演示中断安装中断的使用流程。

前言

int 8h是8086系统中由8253定时器通道0触发的系统时钟中断,主要用于维持系统时间、触发定时任务等,默认由BIOS或DOS内核实现。以下是一个模拟int 8h核心功能的自定义中断服务程序,包含系统时间更新、定时中断处理等功能。

代码实现

; 常量定义
TIMER_CTRL_PORT equ 43h ; 8253定时器控制端口
TIMER_CH0_PORT equ 40h ; 定时器通道0端口
BASE_FREQ equ 1193180 ; 定时器基准频率(Hz)
CLK_TICKS_PER_SEC equ 18 ; 每秒时钟滴答数(DOS默认,18.2次/秒)
BIOS_TIME_SEG equ 0040h ; BIOS时间存储段地址
BIOS_TIME_OFF equ 006Ch ; BIOS时间存储偏移(dword:自午夜起的滴答数)
 
code segment
    assume cs:code, ds:data

; 自定义int 8h中断服务程序
; 功能:
; 1. 定时器中断处理(维持系统时钟)
; 2. 更新BIOS时间(滴答数累计)
; 3. 调用原int 8h服务程序(链式调用,保留系统功能)

int8_proc proc far
    push ax
    push bx
    push cx
    push dx
    push si
    push di
    push es
    push ds ; 保存所有寄存器

    ; 步骤1:更新BIOS系统时间(滴答数)
    mov ax, BIOS_TIME_SEG
    mov es, ax ; ES指向BIOS数据段
    inc dword ptr es:[BIOS_TIME_OFF] ; 滴答数+1(每中断一次加1)

    ; 步骤2:触发int 1Ch(用户定时中断,默认无操作,可由用户扩展)
    int 1Ch ; 调用用户定时中断钩子
 
    ; 步骤3:发送EOI信号给8259A中断控制器,允许后续中断
    mov al, 20h ; EOI命令
    out 20h, al ; 发送给主8259A

    ; 步骤4:链式调用原int 8h服务程序(保留系统默认功能)
    call dword ptr cs:[orig_int8] ; 调用原int 8h
 
    ; 恢复寄存器并返回
    pop ds
    pop es
    pop di
    pop si
    pop dx
    pop cx
    pop bx
    pop ax
    iret ; 中断返回
int8_proc endp
 
; 初始化定时器通道0(设置中断频率)
; 频率 = BASE_FREQ / 计数初值 → 计数初值 = BASE_FREQ / 目标频率
init_timer proc
    ; 计算计数初值(18.2Hz对应的初值:1193180 / 18.2 ≈ 65536)
    mov ax, 0 ; 计数初值=65536(0000h,16位最大值)
    ; 配置定时器通道0:模式3(方波输出),16位计数
    mov al, 00110110b ; 控制字:通道0,读写高8+低8,模式3,二进制
    out TIMER_CTRL_PORT, al
    ; 写入计数初值(65536的低8位和高8位均为00h)
    mov al, 0
    out TIMER_CH0_PORT, al ; 低8位
    out TIMER_CH0_PORT, al ; 高8位
    ret
init_timer endp
 
; 安装自定义int 8h中断(替换系统默认中断)
install_int8 proc
    cli ; 关中断,防止安装过程被打断
    mov ax, 0
    mov ds, ax ; DS指向中断向量表(0段)
    ; 保存原int 8h中断向量(段:偏移)
    mov ax, ds:[8*4] ; 原偏移
    mov word ptr cs:[orig_int8], ax
    mov ax, ds:[8*4 + 2] ; 原段
    mov word ptr cs:[orig_int8 + 2], ax
    ; 写入自定义中断服务程序地址到int 8h向量
    mov ax, offset int8_proc
    mov ds:[8*4], ax ; 偏移
    mov ax, cs
    mov ds:[8*4 + 2], ax ; 段
    sti ; 开中断
    ret
install_int8 end

; 恢复原int 8h中断向量(程序退出时调用)
restore_int8 proc
    cli
    mov ax, 0
    mov ds, ax
    ; 恢复原向量
    mov ax, word ptr cs:[orig_int8]
    mov ds:[8*4], ax
    mov ax, word ptr cs:[orig_int8 + 2]
    mov ds:[8*4 + 2], ax
    sti
    ret
restore_int8 endp

; 测试用的int 1Ch用户定时中断(每秒显示一次)
int1c_proc proc far
    push ax
    push bx
    push cx
    push dx
    push ds
    mov ax, data

    mov ds, ax ; 初始化数据段
    ; 每CLK_TICKS_PER_SEC次中断(约1秒)执行一次
    inc word ptr [tick_count]
    mov ax, [tick_count]
    cmp ax, CLK_TICKS_PER_SEC
    jne int1c_end

    ; 1秒到,显示字符'.'
    mov ah, 02h
    mov dl, '.'
    int 21h
    mov word ptr [tick_count], 0 ; 重置计数器

int1c_end:
    pop ds
    pop dx
    pop cx
    pop bx
    pop ax
    iret
int1c_proc endp

; 安装int 1Ch用户定时中断
install_int1c proc
    cli
    mov ax, 0
    mov ds, ax
    ; 保存原int 1Ch向量
    mov ax, ds:[1Ch*4]
    mov word ptr cs:[orig_int1c], ax
    mov ax, ds:[1Ch*4 + 2]
    mov word ptr cs:[orig_int1c + 2], ax
    ; 写入自定义int 1Ch处理程序
    mov ax, offset int1c_proc
    mov ds:[1Ch*4], ax
    mov ax, cs
    mov ds:[1Ch*4 + 2], ax
    sti
    ret
install_int1c endp

; 恢复原int 1Ch中断
restore_int1c proc
    cli
    mov ax, 0
    mov ds, ax
    mov ax, word ptr cs:[orig_int1c]
    mov ds:[1Ch*4], ax
    mov ax, word ptr cs:[orig_int1c + 2]
    mov ds:[1Ch*4 + 2], ax
    sti
    ret
restore_int1c proc

; 主程序:安装中断并测试
main:
    mov ax, data
    mov ds, ax ; 初始化数据段

    ; 安装int 8h和int 1Ch中断
    call install_int8
    call install_int1c
    call init_timer ; 初始化定时器

    ; 等待用户按键退出
    mov ah, 01h
    int 21h ; 等待按键

    ; 恢复系统中断
    call restore_int1c
    call restore_int8

    ; 程序退出
    mov ah, 4Ch
    int 21h

; 数据区(在代码段中定义,避免额外段操作)
orig_int8 dd 0 ; 原int 8h中断向量(段:偏移)
orig_int1c dd 0 ; 原int 1Ch中断向量
tick_count dw 0 ; 用于int 1Ch的滴答计数器


code ends
data segment
data ends ; 数据段仅作占位

    end main

代码说明

  1. 核心功能:自定义int 8h中断服务程序模拟了系统时钟中断的核心功能,包括:a.每触发一次中断,更新BIOS数据区的滴答数(自午夜起的累计中断次数,用于系统时间计算)。b.调用int 1Ch(用户定时中断钩子),允许用户扩展定时任务(如示例中每秒显示一个点)。c.发送EOI信号给8259A中断控制器,确保后续中断能被响应。d.链式调用原int 8h服务程序,保留系统默认功能(如DOS的时间管理)。
  2. 定时器配置:8253定时器通道0被配置为模式3(方波输出),计数初值为65536,对应中断频率约18.2次/秒(DOS系统默认),计算公式为:计数初值=基准频率(1193180Hz)/目标频率(18.2Hz≈65536。
  3. 中断安装与恢复:a.安装时保存原int 8h和int 1Ch的中断向量,将自定义处理程序地址写入中断向量表。b.程序退出前恢复原向量,避免破坏系统正常运行。
  4. 测试逻辑:主程序安装中断后,通过int 1Ch的自定义处理程序实现每秒显示一个点,直观展示定时中断的效果,按任意键退出并恢复系统环境。

前言

在8086汇编环境中,直接实现关机功能通常依赖硬件支持(如ATX电源的控制端口),且不同系统的实现方式存在差异。以下是一个基于ATX电源控制逻辑的关机中断模拟实现,通过操作特定端口发送关机命令,实际使用需结合硬件环境。

代码实现

; 常量定义(ATX电源相关端口和命令)
POWER_CTRL_PORT equ 0604h ; 假设的电源控制端口(不同硬件可能不同)
SHUTDOWN_CMD equ 0FEh ; 关机命令(ATX电源通常使用特定命令码)
WAIT_LOOP_COUNT equ 10000 ; 等待硬件响应的循环次数
 
code segment
    assume cs:code, ds:data
 
; 关机中断服务程序(入口:AH=00h表示执行关机)
; 功能:向电源控制端口发送关机命令,等待硬件响应
shutdown_int proc far
    push ax
    push bx
    push cx
    push dx
    push si
    push di
    push es
    push ds ; 保存所有寄存器
 
    ; 仅处理AH=00h的关机命令
    cmp ah, 00h
    jne int_end
 
    ; 步骤1:发送关机命令到电源控制端口
    mov dx, POWER_CTRL_PORT
    mov al, SHUTDOWN_CMD
    out dx, al ; 向硬件发送关机指令
 
    ; 步骤2:等待硬件响应(循环延迟,确保命令被接收)
    mov cx, WAIT_LOOP_COUNT
wait_shutdown:
    loop wait_shutdown ; 空循环等待
 
    ; 步骤3:若支持,可检测电源状态(此处简化,不处理反馈)
 
int_end:
    pop ds
    pop es
    pop di
    pop si
    pop dx
    pop cx
    pop bx
    pop ax
    iret ; 中断返回
shutdown_int endp
 
; 安装关机中断到int 63h(用户自定义中断号)
install proc
    cli ; 关中断,防止安装过程被打断
    mov ax, 0
    mov es, ax ; ES指向中断向量表(0段)
    mov di, 63h * 4 ; int 63h的向量地址:63h×4=0000:018C
    ; 保存原中断向量(用于恢复)
    mov ax, es:[di]
    mov [orig_63h_off], ax
    mov ax, es:[di + 2]
    mov [orig_63h_seg], ax
    ; 写入自定义关机中断的地址
    mov ax, offset shutdown_int
    stosw ; 写入偏移量
    mov ax, cs
    stosw ; 写入段地址
    sti ; 开中断
    ret
install endp
 
; 恢复原始中断向量(程序退出时调用)
restore proc
    cli
    mov ax, 0
    mov es, ax
    mov di, 63h * 4
    ; 恢复原中断向量
    mov ax, [orig_63h_off]
    stosw
    mov ax, [orig_63h_seg]
    stosw
    sti
    ret
restore proc
 
; 主程序:测试关机中断
main:
    mov ax, data
    mov ds, ax ; 初始化数据段
 
    call install ; 安装关机中断
 
    ; 测试:调用关机中断(实际使用时需谨慎!)
    mov ah, 00h ; 功能:执行关机
    int 63h ; 触发关机中断
 
    ; 若关机失败,恢复中断并退出(仅作演示)
    call restore
    mov ah, 4Ch
    int 21h
 
code ends
 
; 数据段:保存原始中断向量
data segment
    orig_63h_off dw 0 ; int 63h原始偏移量
    orig_63h_seg dw 0 ; int 63h原始段地址
data ends
 
    end main

代码说明

  1. 硬件交互逻辑:代码假设系统使用ATX电源,通过特定控制端口(0604h,实际需根据硬件手册修改)发送关机命令(0FEh)。ATX电源通常支持通过端口命令触发关机,具体端口和命令码需参考硬件文档。
  2. 中断功能设计:a.自定义int 63h作为关机中断,仅响应AH=00h的功能调用,执行以下操作:- 向电源控制端口发送关机命令;b.循环延迟等待硬件处理命令(避免命令未被接收)。
  3. 中断安装与恢复:b.安装时将自定义中断服务程序地址写入int 63h的向量表,并保存原始向量;b.程序退出前恢复原始向量,避免影响系统默认中断处理。
  4. 注意事项:a.实际关机端口和命令因硬件而异(如某些系统使用0x60/0x64端口或ACPI协议),需根据具体环境调整;b.现代操作系统(如Windows/Linux)在保护模式下禁止直接端口操作,此代码仅适用于实模式环境(如DOS)。

前言

在8086汇编环境中,网络连接功能通常依赖硬件(如网卡)和底层协议(如TCP/IP简化版),且8086本身无原生网络支持,需通过外接网卡及端口操作实现。以下是一个模拟网络连接中断的简化实现,包含基本的连接建立、数据发送/接收功能,基于假设的网卡端口和简化协议,供学习参考。

代码实现

; 常量定义(假设的网卡端口和状态码)
NET_CTRL_PORT equ 0300h ; 网卡控制端口
NET_DATA_PORT equ 0301h ; 网卡数据端口
NET_STATUS_PORT equ 0302h ; 网卡状态端口
NET_CONNECTED equ 01h ; 连接成功状态
NET_DISCONNECTED equ 00h ; 断开状态
NET_DATA_READY equ 02h ; 数据就绪状态
 
code segment
    assume cs:code, ds:data
 
; 网络连接中断服务程序(入口:AH=功能号)
; 功能:
; AH=00h:建立网络连接(DX=目标IP地址低16位,CX=目标端口)
; AH=01h:断开网络连接
; AH=02h:发送数据(SI=数据缓冲区地址,CX=数据长度)
; AH=03h:接收数据(DI=接收缓冲区地址,CX=最大接收长度,返回CX=实际长度)
; AH=04h:获取连接状态(返回AL=状态:0=断开,1=连接)
net_int proc far
    push ax
    push bx
    push cx
    push dx
    push si
    push di
    push es
    push ds ; 保存所有寄存器
    push bp
    mov bp, sp
 
    ; 根据功能号跳转处理
    cmp ah, 00h
    je net_connect ; 建立连接
    cmp ah, 01h
    je net_disconnect ; 断开连接
    cmp ah, 02h
    je net_send ; 发送数据
    cmp ah, 03h
    je net_recv ; 接收数据
    cmp ah, 04h
    je net_get_status ; 获取状态
    jmp int_end ; 不支持的功能
 
; 功能00h:建立网络连接
; 入口:DX=目标IP(低16位),CX=目标端口
; 原理:向网卡发送连接请求,等待响应
net_connect:
    ; 保存目标IP和端口
    mov [dest_ip], dx
    mov [dest_port], cx
 
    ; 向网卡控制端口发送连接命令(假设命令01h)
    mov al, 01h
    out NET_CTRL_PORT, al
 
    ; 等待网卡响应(轮询状态端口)
    mov cx, 0FFFFh ; 超时计数
wait_connect:
    in al, NET_STATUS_PORT
    test al, NET_CONNECTED
    jnz connect_ok ; 连接成功
    loop wait_connect
    jmp int_end ; 超时失败
 
connect_ok:
    mov [net_state], NET_CONNECTED ; 更新状态为连接
    jmp int_end
 
; 功能01h:断开网络连接
; 操作:发送断开命令,重置连接状态
net_disconnect:
    mov al, 02h ; 断开命令(假设02h)
    out NET_CTRL_PORT, al
    mov [net_state], NET_DISCONNECTED ; 更新状态为断开
    jmp int_end
  
; 功能02h:发送数据
; 入口:SI=数据缓冲区,CX=数据长度(<=255)
; 操作:逐个字节写入数据端口
net_send:
    cmp [net_state], NET_CONNECTED
    jne int_end ; 未连接则退出
 
    mov bl, cl ; BL保存长度
    mov bh, 0
    mov si, si ; SI指向数据
 
send_loop:
    mov al, [si] ; 读取数据字节
    out NET_DATA_PORT, al ; 写入网卡
    inc si
    dec bl
    jnz send_loop
    jmp int_end
 
; 功能03h:接收数据
; 入口:DI=接收缓冲区,CX=最大长度;返回:CX=实际接收长度
; 操作:从数据端口读取可用数据
net_recv:
    cmp [net_state], NET_CONNECTED
    jne int_end ; 未连接则退出
 
    in al, NET_STATUS_PORT
    test al, NET_DATA_READY
    jz recv_end ; 无数据则退出
 
    mov bl, 0 ; BL计数实际长度
recv_loop:
    in al, NET_DATA_PORT ; 从网卡读取数据
    mov [di], al
    inc di
    inc bl
    dec cx
    jz recv_end ; 达到最大长度
    ; 检查是否还有数据(简化:假设一次读完)
    in al, NET_STATUS_PORT
    test al, NET_DATA_READY
    jnz recv_loop
 
recv_end:
    mov cx, bx ; 返回实际接收长度
    jmp int_end
 
; 功能04h:获取连接状态
; 返回:AL=当前状态(0=断开,1=连接)
net_get_status:
    mov al, [net_state]
    jmp int_end
 
; 中断结束:恢复寄存器并返回
int_end:
    pop bp
    pop ds
    pop es
    pop di
    pop si
    pop dx
    pop cx
    pop bx
    pop ax
    iret ; 中断返回
net_int endp
 
; 安装自定义网络中断向量(使用int 61h,用户自定义中断)
install proc
    cli ; 关中断,防止安装冲突
    mov ax, 0
    mov es, ax ; ES指向中断向量表(0段)
    mov di, 61h*4 ; int 61h向量地址:61h×4=0000:0184
    mov ax, offset net_int ; 中断服务程序偏移地址
    stosw ; 写入偏移量
    mov ax, cs ; 中断服务程序段地址
    stosw ; 写入段地址
    sti ; 开中断
    ret
install endp
 
; 主程序:测试网络连接功能
main:
    mov ax, data
    mov ds, ax ; 初始化数据段
 
    call install ; 安装网络中断
 
    ; 测试1:建立连接(目标IP=0A0A0A0A(模拟10.10.10.10),端口=80)
    mov ah, 00h
    mov dx, 0A0A0h ; IP低16位(0A0A)
    mov cx, 80 ; 端口80
    int 61h
 
    ; 测试2:发送数据("Hello")
    mov ah, 02h
    mov si, offset send_buf
    mov cx, 5 ; 长度5
    int 61h
 
    ; 测试3:接收数据(最多10字节)
    mov ah, 03h
    mov di, offset recv_buf
    mov cx, 10
    int 61h ; 返回CX=实际长度
 
    ; 测试4:获取状态并显示(简化:通过字符表示)
    mov ah, 04h
    int 61h
    cmp al, NET_CONNECTED
    jne show_disconnected
    ; 连接成功,显示'C'
    mov ah, 09h
    mov al, 'C'
    mov bl, 07h
    mov cx, 1
    int 10h
    jmp end_test
show_disconnected:
    ; 断开,显示'D'
    mov ah, 09h
    mov al, 'D'
    mov bl, 07h
    mov cx, 1
    int 10h
 
end_test:
    ; 断开连接
    mov ah, 01h
    int 61h
 
    jmp $ ; 暂停
 
code ends
 
; 数据段:保存网络状态和缓冲区
data segment
    net_state db NET_DISCONNECTED ; 网络状态(0=断开,1=连接)
    dest_ip dw 0 ; 目标IP地址(低16位)
    dest_port dw 0 ; 目标端口
    send_buf db 'Hello' ; 发送缓冲区
    recv_buf db 10 dup(0) ; 接收缓冲区(最大10字节)
data ends
 
    end main

代码说明

  1. 硬件抽象:假设网卡通过3个端口(控制、数据、状态)与CPU通信,定义了连接、断开等命令及状态码,模拟真实网卡的交互逻辑。
  2. 中断功能:实现了网络连接的核心操作:a.建立/断开连接:通过控制端口发送命令,轮询状态判断结果。b.发送/接收数据:通过数据端口读写字节,限制长度以简化处理。c.状态查询:返回当前连接状态(连接/断开)。
  3. 中断安装:使用用户自定义中断号(int 61h),修改中断向量表指向自定义服务程序,安装时关中断保证原子性。
  4. 测试逻辑:主程序依次测试连接建立、数据发送、接收及状态查询,并通过屏幕显示状态('C'表示连接,'D'表示断开)。

前言

在8086汇编中,鼠标中断通常通过int 33h(鼠标BIOS中断)实现,其功能包括初始化鼠标、获取鼠标位置、处理按键等。下面我们学习一个模拟int 33h核心功能的自定义鼠标中断程序,支持鼠标初始化、位置获取、按键检测。

代码实现

; 常量定义
MOUSE_DATA_PORT equ 03f8h ; 鼠标数据端口(COM1)
MOUSE_CTRL_PORT equ 03f9h ; 鼠标控制端口
MOUSE_PACKET_LEN equ 3 ; 鼠标数据包长度(3字节)
 
code segment
    assume cs:code, ds:data
 
; 自定义鼠标中断服务程序(入口:AX=功能号)
; 功能:
; AX=0000h:初始化鼠标(返回BX=0表示成功)
; AX=0003h:获取鼠标状态(BX=按键状态,CX=X偏移,DX=Y偏移)
; AX=000Bh:设置鼠标显示(CX=X坐标,DX=Y坐标,显示鼠标指针)
mouse_int proc far
    push ax
    push bx
    push cx
    push dx
    push si
    push di
    push es
    push ds ; 保存所有寄存器
    push bp
    mov bp, sp
 
    ; 根据功能号跳转处理
    cmp ax, 0000h
    je mouse_init ; 初始化鼠标
    cmp ax, 0003h
    je get_status ; 获取鼠标状态
    cmp ax, 000Bh
    je show_cursor ; 显示鼠标指针
    jmp int_end ; 不支持的功能
 
; 功能0000h:初始化鼠标
; 返回:BX=0(成功),BX≠0(失败)
mouse_init:
    ; 向鼠标控制器发送初始化命令(实际硬件需按协议操作)
    mov dx, MOUSE_CTRL_PORT
    mov al, 0Ah ; 复位命令
    out dx, al
 
    ; 等待鼠标响应(简化:假设初始化成功)
    mov bx, 0 ; BX=0表示成功
    mov [mouse_ready], 1 ; 标记鼠标就绪
    jmp int_end
 
; 功能0003h:获取鼠标状态
; 返回:BX=按键状态(bit0=左键,bit1=右键),CX=X偏移,DX=Y偏移
get_status:
    cmp [mouse_ready], 0
    je int_end ; 鼠标未初始化,直接返回
 
    ; 读取鼠标数据包(3字节:状态字节+X偏移+Y偏移)
    mov si, offset mouse_packet ; SI指向数据包缓冲区
    mov cx, MOUSE_PACKET_LEN
read_packet:
    mov dx, MOUSE_DATA_PORT
    in al, dx ; 从数据端口读取1字节
    mov [si], al
    inc si
    loop read_packet
 
    ; 解析数据包:第1字节=状态,第2字节=X偏移,第3字节=Y偏移
    mov al, [mouse_packet] ; 状态字节
    mov bl, 0
    test al, 01h ; 检测左键(bit0=1表示按下)
    jz no_left
    or bl, 01h
no_left:
    test al, 02h ; 检测右键(bit1=1表示按下)
    jz no_right
    or bl, 02h
no_right:
    mov bx, bl ; BX=按键状态
 
    mov cl, [mouse_packet+1] ; CX=X偏移
    mov ch, 0
    mov cx, cx
 
    mov dl, [mouse_packet+2] ; DX=Y偏移(注意Y方向通常与屏幕相反)
    mov dh, 0
    neg dx ; 反转Y偏移(适应屏幕坐标系)
    jmp int_end
 
; 功能000Bh:显示鼠标指针(简化为在指定位置画字符)
; 入口:CX=X坐标,DX=Y坐标
show_cursor:
    ; 保存当前光标位置
    push dx
    push cx
 
    ; 转换坐标为文本模式行列(假设80x25文本模式,每个字符8x16像素)
    mov ax, dx ; AX=Y坐标
    mov bl, 16
    div bl ; AL=行号(Y/16)
    mov dh, al
 
    mov ax, cx ; AX=X坐标
    mov bl, 8
    div bl ; AL=列号(X/8)
    mov dl, al
 
    ; 在计算出的行列位置显示鼠标指针(用'#'表示)
    mov ah, 02h ; 设置光标位置
    int 10h
    mov ah, 09h ; 显示字符
    mov al, '#'
    mov bl, 0Ch ; 红色属性
    mov cx, 1
    int 10h
 
    ; 恢复光标位置
    pop cx
    pop dx
    jmp int_end
 
; 中断结束:恢复寄存器并返回
int_end:
    pop bp
    pop ds
    pop es
    pop di
    pop si
    pop dx
    pop cx
    pop bx
    pop ax
    iret ; 中断返回
mouse_int endp
 
; 安装自定义鼠标中断向量(替换int 33h)
install proc
    cli ; 关中断
    mov ax, 0
    mov es, ax ; ES指向中断向量表(0段)
    mov di, 33h*4 ; int 33h向量地址:33h×4=0000:00D4
    mov ax, offset mouse_int ; 中断服务程序偏移
    stosw
    mov ax, cs ; 中断服务程序段地址
    stosw
    sti ; 开中断
    ret
install endp
 
; 主程序:初始化并测试鼠标中断
main:
    mov ax, data
    mov ds, ax ; 初始化数据段
 
    call install ; 安装自定义鼠标中断
 
    ; 测试1:初始化鼠标
    mov ax, 0000h
    int 33h ; 调用自定义中断
    cmp bx, 0
    jne exit ; 初始化失败则退出
 
    ; 测试2:循环获取鼠标状态并更新指针
loop_test:
    mov ax, 0003h ; 获取鼠标状态
    int 33h
    ; BX=按键状态,CX=X偏移,DX=Y偏移
 
    ; 更新鼠标绝对坐标(累加偏移)
    add [mouse_x], cx
    add [mouse_y], dx
 
    ; 限制坐标在屏幕范围内(320x200为例)
    cmp [mouse_x], 320
    jl x_ok
    mov [mouse_x], 320
x_ok:
    cmp [mouse_x], 0
    jge y_ok
    mov [mouse_x], 0
y_ok:
    cmp [mouse_y], 200
    jl y_ok2
    mov [mouse_y], 200
y_ok2:
    cmp [mouse_y], 0
    jge show
    mov [mouse_y], 0
 
show:
    ; 显示鼠标指针
    mov ax, 000Bh
    mov cx, [mouse_x] ; X坐标
    mov dx, [mouse_y] ; Y坐标
    int 33h
 
    ; 检测左键按下,按左键退出
    test bx, 01h
    jz loop_test
exit:
    mov ah, 4Ch
    int 21h ; 程序退出
 
code ends
 
; 数据段:保存鼠标状态和缓冲区
data segment
    mouse_ready db 0 ; 鼠标就绪标记(1=就绪)
    mouse_packet db 3 dup(0) ; 鼠标数据包缓冲区(3字节)
    mouse_x dw 160 ; 鼠标X坐标(初始中心)
    mouse_y dw 100 ; 鼠标Y坐标(初始中心)
data ends
 
    end main

代码说明

  1. 中断功能设计:模拟int 33h的核心功能,包括鼠标初始化(检测鼠标是否存在)、状态获取(按键和移动偏移)、指针显示(在指定坐标画字符模拟指针)。
  2. 鼠标数据处理:a.鼠标通过串行端口(如COM1,端口03f8h)通信,数据包为3字节(状态字节+X偏移+Y偏移)。b.状态字节的bit0表示左键按下,bit1表示右键按下;X/Y偏移表示相对移动量。
  3. 指针显示:简化为在文本模式下,将鼠标坐标转换为行列位置,显示字符 # 作为指针(实际图形模式需绘制鼠标形状)。
  4. 中断安装:修改中断向量表中int 33h的入口地址,指向自定义服务程序,安装时关中断以避免冲突。