You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

8086汇编捕获定时器中断求助:TASM/DOSBox代码随机终止问题

问题分析与修复

你的程序随机停止、逻辑异常的核心原因集中在循环指令误用、中断服务程序(ISR)严重错误、溢出判断缺失这几个点上,下面逐一拆解并修复:


1. 随机停止的直接原因:LOOP指令滥用

TO_THE_SECOND_POWER循环里用了LOOP,但CX寄存器从未初始化。LOOP指令依赖CX的值递减计数,未初始化的CX是随机值,导致循环会随机终止。

2. 中断服务程序(ISR)的致命错误

你的1Ch中断处理逻辑完全不符合需求,且存在破坏系统的问题:

  • 拼写错误:JС OVERFLOW_REACHED里的С是全角字符,应该是半角JC,但更关键的是——这里根本没有任何溢出判断逻辑,定时器中断触发就直接跳转到程序结束,完全违背了“计算平方直到溢出”的需求。
  • 缺少IRET返回:中断服务程序必须用IRET指令返回,而非普通RET,否则会破坏栈结构,导致系统崩溃。
  • 原中断调用错误:调用原1Ch中断需要用远跳转,正确写法是JMP FAR PTR [OLD_INT_SEG:OLD_INT_OFF],你只写了偏移地址,段地址错误会导致系统异常。
  • ISR逻辑冗余:你不需要用1Ch中断来检测溢出,直接在平方计算后判断即可;1Ch中断本身是系统定时器触发的空钩子,你的用法完全偏离了需求。

3. 平方计算与溢出判断的逻辑错误

  • NUM定义为DB(8位),最大255,平方值65025未超过16位无符号数上限65535,永远不会触发溢出,应该把NUM改为DW(16位),这样当NUM≥256时,平方值65536会超过16位,此时MUL指令会设置CF=1(无符号溢出)。
  • 缺少溢出判断:每次计算平方后,需要检查CF或OF(无符号用CF),如果溢出则终止程序。

4. PRINT_NUM子程序的错误

  • 未保存AX:调用PRINT_NUM时AX存储着平方值,但子程序开头未入栈保存,直接执行DIV会破坏AX。
  • 数字转ASCII的逻辑冗余:不需要反复PUSH AX/POP AX,直接处理余数即可。

修复后的完整代码

.MODEL SMALL
.STACK 1024H 
.DATA     
  NUM    DW 1          ; 改为16位,才能触发16位溢出
  ; SQUARE 可以省略,直接用AX存储结果
.CODE  
  ASSUME CS:@CODE, DS:@DATA, ES:NOTHING 
  START:    
  ; 由于不需要1Ch中断检测溢出,以下中断替换代码全部移除
  MOV AX, @DATA
  MOV DS, AX

  ; 平方计算主循环
  TO_THE_SECOND_POWER:  
    MOV AX, NUM   
    MUL NUM             ; AX = NUM * NUM,若溢出则CF=1
    JC OVERFLOW_REACHED ; 检测到溢出,跳转结束
    
    CALL PRINT_NUM      ; 输出当前平方数
    INC NUM
    JMP TO_THE_SECOND_POWER ; 用JMP实现无限循环,直到溢出

  ; 输出数字子程序
  PRINT_NUM PROC 
    PUSH AX             ; 保存原AX值
    PUSH BX
    PUSH DX
    PUSH CX 
    MOV CX, 0
    MOV BX, 10  
    LOOP_HERE: 
      MOV DX, 0 
      DIV BX            ; AX=商,DX=余数
      ADD DL, "0"       ; 余数转ASCII
      PUSH DX           ; 入栈保存(逆序存储)
      INC CX  
      CMP AX, 0 
    JNZ LOOP_HERE

    MOV AH, 2  
    LOOP_HERE_2: 
      POP DX      
      INT 21H           ; 调用DOS输出字符(AH=2已设置)
    LOOP LOOP_HERE_2   

    ; 输出换行回车
    MOV DL, 10
    INT 21H
    MOV DL, 13
    INT 21H

    POP CX 
    POP DX 
    POP BX 
    POP AX 
    RET
  PRINT_NUM ENDP  

  ; 溢出处理与程序终止
  OVERFLOW_REACHED:  
    MOV AH, 4CH                     
    MOV AL, 0H                    
    INT 21H                     
END START   
CODE ENDS

额外说明

如果你确实需要用1Ch中断来控制输出频率(比如每隔18.2ms输出一个平方数),而非直接循环输出,那正确的做法是:

  1. 在ISR里递增NUM、计算平方、输出,然后检测溢出。
  2. ISR必须保存所有修改的寄存器,用IRET返回,正确调用原1Ch中断。
  3. 主程序进入休眠循环(比如MOV AH,01H; INT 21H; JNZ EXIT; JMP $),等待中断触发。

不过根据你的需求描述,直接在主循环计算并检测溢出是更简单且正确的实现,不需要修改1Ch中断——教授用08h的做法不妥,而你误用1Ch的逻辑也不符合需求,去掉中断替换后程序就能稳定运行。

内容的提问来源于stack exchange,提问作者Sju Ton

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 04:24:57