IBM PC/XT 上的 Turbo C 鼠标事件回调为何不生效?
应先修正回调访问全局变量时的 DS,并将 fDone 声明为 volatile。原代码中的 far 只解决远调用的返回方式,不会自动保证回调使用程序的数据段。现有信息不足以确定真实机器上的唯一故障原因,还需检查鼠标驱动和回调是否实际执行。
为什么回调执行了,fDone 仍可能没有变化?
普通 C 函数访问全局变量时,依赖编译器约定的数据段环境。鼠标驱动调用事件处理函数时,这个前提不一定成立。Microsoft Mouse 文档说明,回调入口的 DS 指向鼠标驱动的数据段,处理函数需要自行设置所需的 DS,并通过远返回指令返回。Microsoft Mouse Programmer’s Reference Guide,第 6-19 页
因此,直接执行:
fDone = TRUE;
可能会写入错误的内存位置,导致主循环读取的 fDone 没有改变,也可能破坏驱动数据。
另一个独立问题是异步更新。应使用:
volatile int fDone = FALSE;
这样可以要求编译器在循环判断时重新读取变量。volatile 无法修复错误的数据段,因此两处都需要处理。
如何修改事件处理函数?
下面的替换示例仅适用于 Turbo C 的 small memory model,假设全局变量位于 DGROUP,并通过 Turbo Assembler 处理内联汇编。其他内存模型需要按实际段布局调整,不能直接套用。
#pragma inline
#include <dos.h>
#define FALSE 0
#define TRUE 1
volatile int fDone = FALSE;
void far mouseHandler(void)
{
asm {
push ax
push ds
mov ax, DGROUP
mov ds, ax
mov word ptr fDone, TRUE
pop ds
pop ax
}
}
代码保存并恢复了汇编中修改的 AX 和 DS。函数保持 far,由编译器生成相应的远返回。不要直接改为 interrupt 函数:该修饰通常生成 IRET,与这里要求的远返回不匹配。
编译后,检查生成的汇编,确认返回指令是 RETF,并确认设置 DS 前没有编译器插入的运行库调用。如果启用了函数入口的栈检查,应关闭该回调的栈检查,或使用独立汇编入口。这个示例尚未在问题中的编译器和真实硬件上验证。
注册代码还需要调整什么?
事件掩码 4 | 16 是正确的:0x0004 表示左键释放,0x0010 表示右键释放,合并后为 0x0014。注册入口使用 ES:DX。FreeDOS mouse 仓库中的 INT 33H 接口说明
原代码只设置了 seg.es。建议先用 segread() 初始化段寄存器结构,再覆盖 ES:
segread(&seg);
r.x.ax = 0x000C;
r.x.cx = 0x0004 | 0x0010;
r.x.dx = FP_OFF(mouseHandler);
seg.es = FP_SEG(mouseHandler);
int86x(0x33, &r, &r, &seg);
while (!fDone) {
/* Main program work. */
}
退出前仍需通过 AX=000CH、CX=0 取消回调,避免程序内存释放后驱动继续调用该地址。
如何判断是驱动问题,还是回调问题?
按以下顺序排查,可以减少相互干扰:
- 先在文本模式测试。暂时去掉
set_mode(0x13)。不能仅根据 IBM PC/XT 机型名称推断它支持 VGA mode13H,需要确认实际显示适配器。 - 确认驱动存在并检查复位结果。老机器应先确认 INT 33H 向量有效;调用
AX=0000H后检查返回的AX。兼容接口通常以FFFFH表示驱动和鼠标已安装,0000H表示未安装。原代码忽略了这个结果。FreeDOS mouse 的复位接口说明 - 在主循环中轮询
AX=0003H,检查返回的BX按钮状态是否随按下、释放而变化。如果轮询也没有变化,应先处理真实机器上的驱动、鼠标连接或硬件配置。 - 轮询正常后再定位回调。检查注册的
ES:DX是否对应函数入口,通过调试器或汇编跟踪确认入口是否执行。如果入口执行了但主循环不退出,重点检查写入地址、DS和循环是否重新读取fDone。 - 检查实际循环内容。精简示例省略了循环中的操作。如果实际代码关闭中断后没有恢复,也可能阻止鼠标事件到达。
回调中只设置标志即可,把输出、文件访问和退出处理放回主循环,避免在异步回调中调用不可重入的 DOS 或运行库功能。
DOSBox-X 中运行成功,只能说明该环境下没有暴露问题。驱动实现、回调时的寄存器状态和硬件配置都可能不同;尚无证据能把差异归结为某一个 DOSBox-X 行为。
备注:内容仅供参考。
