Emu8086中X86汇编菜单文本左键点击功能失效问题排查
修复Emu8086中鼠标点击菜单退出功能失效的问题
以下是针对你代码中问题的分析和修复方案:
核心问题分析
1. 鼠标按键判断逻辑颠倒
在buttonStatus过程中,int 33h ax=03h返回的bx寄存器中,bit0为1表示左键按下,但当前代码按下左键时会跳去noClick,不记录坐标,导致点击事件根本没被捕获:
test bx,01h jnz noClick ; 这里逻辑反了!按下左键时应该记录坐标,而不是跳过
2. 等待点击的条件无效
waitForClick循环中的jnc waitForClick没有依据,因为buttonStatus过程从未设置CF标志位,导致循环无法正确响应点击事件。
3. 坐标单位不匹配+寄存器使用错误
- 菜单的
X/Y是文本模式的列/行号(每个字符占8x16像素),而鼠标返回的是像素坐标,直接比较数值完全不匹配。 - 鼠标坐标变量
clickX/clickY是16位的dw,但代码中用8位寄存器cl/dl读取,导致坐标值被截断。
修复后的完整代码
multi-segment executable file template. data segment votar db "Votar",13,10,"$" gerir db "Gerir",13,10,"$" creditos db "Creditos",13,10,"$" sair db "Sair",13,10,"$" teste db "Teste",13,10,"$" ; 菜单的文本列/行号(从0开始) votarCol db 38 votarRow db 04 gerirCol db 38 gerirRow db 08 creditosCol db 38 creditosRow db 12 sairCol db 38 sairRow db 16 ; 鼠标像素坐标(16位) clickX dw 00 clickY dw 00 ends stack segment dw 128 dup(0) ends code segment start: mov ax, data mov ds, ax mov es, ax ; 打印菜单 call printVotar call printGerir call printCreditos call printSair call initMouse waitForClick: call buttonStatus jz waitForClick ; 未点击则继续等待 call clickLocation jmp waitForClick exitOS: mov ax, 4c00h ; 退出到操作系统 int 21h ;****FUNCTIONS**** printf proc mov ah,09h int 21h ret endp moveCursor proc mov ah,02h mov bh, 0 int 10h ret endp printVotar proc mov dl,votarCol mov dh,votarRow call moveCursor mov dx,offset votar call printf ret endp printGerir proc mov dl,gerirCol mov dh,gerirRow call moveCursor mov dx,offset gerir call printf ret endp printCreditos proc mov dl,creditosCol mov dh,creditosRow call moveCursor mov dx,offset creditos call printf ret endp printSair proc mov dl,sairCol mov dh,sairRow call moveCursor mov dx,offset sair call printf ret endp initMouse proc mov ax,00h int 33h cmp ax,0FFFFh je showMouse ; 鼠标初始化失败则直接退出 jmp exitOS showMouse: mov ax,01h int 33h ; 显示鼠标指针 ret endp buttonStatus proc mov ax,03h int 33h ; 获取鼠标状态和坐标 test bx,01h jz noClick ; 左键未按下则返回 ; 保存鼠标像素坐标 mov clickX,cx mov clickY,dx stc ; 设置CF表示有点击 ret noClick: clc ; 清除CF表示无点击 ret endp clickLocation proc push cx push dx mov cx,clickX mov dx,clickY ; 转换鼠标像素坐标为文本列/行号(80x25模式:8像素/列,16像素/行) mov ax,dx mov bl,16 div bl ; al = 行号(dh对应的数值),ah = 剩余像素 mov dl,al ; 鼠标所在文本行 mov ax,cx mov bl,8 div bl ; al = 列号(dl对应的数值),ah = 剩余像素 mov cl,al ; 鼠标所在文本列 ; 检查Votar选项 checkVotar: cmp dl,votarRow jne checkGerir cmp cl,votarCol jl checkGerir mov al,votarCol add al,5 ; "Votar"长度5 cmp cl,al jg checkGerir mov dx,offset teste call printf jmp endLocation ; 检查Gerir选项 checkGerir: cmp dl,gerirRow jne checkCreditos cmp cl,gerirCol jl checkCreditos mov al,gerirCol add al,5 ; "Gerir"长度5 cmp cl,al jg checkCreditos mov dx,offset teste call printf jmp endLocation ; 检查Creditos选项 checkCreditos: cmp dl,creditosRow jne checkSair cmp cl,creditosCol jl checkSair mov al,creditosCol add al,8 ; "Creditos"长度8 cmp cl,al jg checkSair mov dx,offset teste call printf jmp endLocation ; 检查Sair选项 checkSair: cmp dl,sairRow jne endLocation cmp cl,sairCol jl endLocation mov al,sairCol add al,4 ; "Sair"长度4 cmp cl,al jg endLocation ; 点击Sair,退出程序 mov ax, 4c00h int 21h endLocation: pop dx pop cx ret endp ends end start ; set entry point and stop the assembler.
关键修复点说明
修正鼠标按键判断:
- 在
buttonStatus中,左键按下时保存坐标并设置CF,未按下时清除CF,waitForClick用jz waitForClick判断(无点击则循环)。
- 在
坐标单位转换:
- 将鼠标像素坐标转换为文本模式的列/行号(80x25模式下,每列8像素,每行16像素),这样才能和菜单的列/行号正确比较。
修正字符串长度判断:
- 每个菜单文本的长度不同,比如"Sair"是4个字符,所以列范围是
[sairCol, sairCol+4),确保点击区域准确覆盖文本。
- 每个菜单文本的长度不同,比如"Sair"是4个字符,所以列范围是
简化鼠标初始化逻辑:
- 如果鼠标初始化失败(返回ax=0),直接退出程序,避免后续错误。
内容的提问来源于stack exchange,提问作者Dimmezy
相关产品推荐
相关产品推荐

