基于8086+DOSBox的驻留计时程序运行异常问题咨询
DOS驻留时钟程序运行其他EXE时失效的问题排查
背景
基于8086微处理器+DOSBox环境开发驻留时钟程序,编译链接命令为:
TASM.exe program.asmTLINK.exe /t program.obj
程序目标是在屏幕显示分秒,依赖INT 1Ch每秒触发18次的特性实现约1秒更新一次,但运行其他.exe程序时功能失效,程序代码如下:
.MODEL SMALL .CODE ORG 100H PROGRAMA: JMP RESIDE MINUTOS DB 0d SEGUNDOS DB 0d FONDO DB 00001111b CONTADOR DB 18d RUTINA PROC CLI PUSH AX PUSH BX PUSH CX PUSH SI PUSH ES MOV AX, SEG @DATA MOV DS, AX CMP CONTADOR, 0b JNE FIN CMP SEGUNDOS, 60d JNE CONTINUE SISEG60: MOV SEGUNDOS, 0d INC MINUTOS CONTINUE: MOV AX, 0B800h MOV ES, AX XOR AX, AX ;APLICAMOS ATRIBUTO A LAS 4 CASILLAS MOV AH, FONDO MOV SI, 3001d MOV ES:[SI], AH ADD SI, 2d MOV ES:[SI], AH ADD SI, 4d MOV ES:[SI], AH ADD SI, 2d MOV ES:[SI], AH XOR AX,AX MOV SI, 3000d ;ESCRIBIMOS LOS MINUTOS XOR AX,AX MOV AL, MINUTOS MOV BL, 10d DIV BL ADD AL, 30h MOV ES:[SI], AL ADD SI, 2d ADD AH, 30h MOV ES:[SI], AH ;ESCRIBIMOS LOS SEGUNDOS XOR AX, AX ADD SI, 4d MOV AL, SEGUNDOS MOV BL, 10d DIV BL ADD AL, 30h MOV ES:[SI], AL ADD SI, 2d ADD AH, 30h MOV ES:[SI], AH INC SEGUNDOS MOV CONTADOR, 18d FIN: DEC CONTADOR POP ES POP SI POP CX POP BX POP AX STI IRET ENDP RESIDE: MOV DX, offset RUTINA MOV AX, 0 MOV ES, AX MOV SI, 1Ch CLI MOV ES:[SI], DX MOV ES:[SI+2], CS STI MOV DX, offset RESIDE INT 27h END PROGRAMA
核心问题原因分析
1. 未保存并链式调用原INT 1Ch中断处理程序
INT 1Ch是DOS系统的时钟滴答中断,大量程序依赖该中断实现定时逻辑。当前程序直接覆盖中断向量表中的INT 1Ch入口,既未保存原处理程序地址,也未在自身中断处理末尾调用原程序:
- 其他程序运行时可能重新修改INT 1Ch向量,直接覆盖你的程序入口
- 系统依赖INT 1Ch的基础功能被破坏,连锁导致驻留时钟程序失效
2. DS寄存器设置错误
当前编译的是COM格式程序(ORG 100H+TLINK /t),这类程序的代码段、数据段、堆栈段共用同一个段寄存器。但在RUTINA中断处理中,使用MOV AX, SEG @DATA设置DS,驻留后会导致DS指向错误的段地址——其他程序运行时会修改DS上下文,你的中断程序无法正确访问自身的MINUTOS、SEGUNDOS等变量,直接造成时钟更新失效。
3. 驻留内存范围计算错误
使用INT 27h驻留程序时,DX需要指向驻留部分最后一个字节的下一个偏移地址。当前设置MOV DX, offset RESIDE,会导致程序仅驻留到RESIDE标签位置,而中断处理程序RUTINA及变量区的部分内容可能未被正确驻留,其他程序运行时会覆盖这部分未被保护的内存。
4. 视频内存操作未考虑模式切换(次要)
程序直接写入0B800h文本模式视频内存,但部分程序运行时会切换视频模式,此时直接写入0B800h会失效。
修复方向
保存并链式调用原INT 1Ch中断:
- 在变量区添加
OLD_INT1C_OFF DW ?和OLD_INT1C_SEG DW ? - 在
RESIDE段先读取原中断向量:MOV AX, 0; MOV ES, AX; MOV SI, 1Ch; MOV AX, ES:[SI]; MOV OLD_INT1C_OFF, AX; MOV AX, ES:[SI+2]; MOV OLD_INT1C_SEG, AX - 在
RUTINA的FIN标签前调用原中断:PUSH CS; POP DS; CALL DWORD PTR [OLD_INT1C_OFF]
- 在变量区添加
修正DS寄存器设置:
- 将
RUTINA中的MOV AX, SEG @DATA; MOV DS, AX替换为MOV AX, CS; MOV DS, AX,适配COM程序的段结构
- 将
修正驻留内存范围:
- 在程序末尾添加
END_RESIDE:标签,设置MOV DX, OFFSET END_RESIDE后再调用INT 27h,确保所有代码和变量都被驻留保护
- 在程序末尾添加
内容的提问来源于stack exchange,提问作者slvdr510
相关产品推荐
相关产品推荐

