【问题标题】:Graphics Cursor in Assembly装配体中的图形光标
【发布时间】:2016-07-20 20:26:21
【问题描述】:

我正在尝试在 Assembly 中为 DOSBox 制作一个类似于绘画的小程序。我不确定 DOSBox 模拟什么 CPU 类型,但据我发现它可能是 386。

我进行了研究,并弄清楚了如何使用鼠标中断,33 小时。我还设法使用屏幕掩码和光标掩码来定义我自己的光标。但是,当用户切换到例如颜色采样器工具时,我希望稍后能够在程序中更改我的光标。当我尝试这样做时,光标变成了一个黑色方块。这是因为我不确切知道执行此类更改所需的步骤。

我是否应该隐藏光标,然后重置它,然后更改光标掩码,然后再次显示?我是否更改了面具,然后才重置?我根本不需要重置鼠标吗?这是我所有与鼠标相关的代码。

附:我知道我可以通过将一堆 proc 转换为宏来优化其中的大部分内容,并且一旦我解决了错误,我可能会在将来这样做。

stdBrush PROC
    push bx cx ax dx
    mov bx, stdBrushHotSpots
    mov cx, stdBrushHotSpots + 2
    mov ax, 9
    mov dx, offset stdBrushMask
    int 33h
    pop dx ax cx bx
    ret
stdBrush ENDP

pickerTool PROC
    push bx cx ax dx
    mov bx, pickerToolHotSpots
    mov cx, pickerToolHotSpots + 2
    mov ax, 9
    mov dx, offset pickerToolMask
    int 33h
    pop dx ax cx bx
    ret
pickerTool ENDP

mouseReset PROC
     push ax
    mov ax, 0
    int 33h
    pop ax
    ret
mouseReset ENDP

showCursor PROC
    push ax
    mov ax, 1
    int 33h
    pop ax
    ret
showCursor ENDP

hideCursor PROC
    push ax
    mov ax, 2
    int 33h
    pop ax
    ret
hideCursor ENDP

getCursorStat PROC
    push ax
    mov ax, 3
    int 33h
    pop ax
    ret
getCursorStat ENDP

initCursor PROC
    mov ax, dseg
    mov es, ax
    call mouseReset
    call stdBrush
    call showCursor
    mov ax, DISPLAY_SEG
    mov es, ax
    ret
initCursor ENDP

这些是用作使用 int 33h 不同功能的快捷方式的小程序。例外是 initCursor,它结合了这些快捷方式来在程序开始时初始化光标,以及 stdBrush 和 pickerTool 两者都将图形光标设置为特定光标(你可以猜到,stdBrush 是标准画笔光标,而 pickerTool 设置颜色采样器工具的光标)。

下面是我的两个光标的掩码。

stdBrushMask    dw 1111111011111111b
            dw 1111111011111111b
            dw 1111111011111111b
            dw 1111111011111111b
            dw 1111111011111111b
            dw 1111111011111111b
            dw 1111111011111111b
            dw 0000000100000000b
            dw 1111111011111111b
            dw 1111111011111111b
            dw 1111111011111111b
            dw 1111111011111111b
            dw 1111111011111111b
            dw 1111111011111111b
            dw 1111111011111111b
            dw 1111111011111111b

            dw 0000000100000000b
            dw 0000000100000000b
            dw 0000000100000000b
            dw 0000000100000000b
            dw 0000000100000000b
            dw 0000000100000000b
            dw 0000000100000000b
            dw 1111111011111111b
            dw 0000000100000000b
            dw 0000000100000000b
            dw 0000000100000000b
            dw 0000000100000000b
            dw 0000000100000000b
            dw 0000000100000000b
            dw 0000000100000000b
            dw 0000000100000000b
stdBrushHotSpots dw 7
                 dw 7

pickerToolMask  dw 1111100001000001b
            dw 1111100000000000b
            dw 1111100000000000b
            dw 1111100000000000b
            dw 1111100000000000b
            dw 1111000000000000b
            dw 1110000000000000b
            dw 1100000000000000b
            dw 1000000000000000b
            dw 1000000000000000b
            dw 1000000000000000b
            dw 1000000000011111b
            dw 0000000000111111b
            dw 0000000001111111b
            dw 0000000011111111b
            dw 0000111111111111b

            dw 0000000000000000b
            dw 0000001100011100b
            dw 0000001111111110b
            dw 0000000111111110b
            dw 0000000111111110b
            dw 0000001111111100b
            dw 0000011111111100b
            dw 0000111111111100b
            dw 0001111111111110b
            dw 0011111111100110b
            dw 0001111111000000b
            dw 0001111110000000b
            dw 0011111100000000b
            dw 0111001000000000b
            dw 0110000000000000b
            dw 0000000000000000b

pickerToolHotSpots dw 1
                   dw 14

由于将代码复制到 stackoverflow 导致了一些缩进差异,我懒得逐行修复它们。

这是程序中给我带来麻烦的部分:

paletteModeToggle PROC
    push ax
    call hideCursor
    mov pos_backup, cx
    mov pos_backup+2, dx
    mov al, colorpicker_flag
    not al
    mov colorpicker_flag, al
    test al, al
    jz palette_mode_off
    palette_mode_on:
        call pickerTool
        call backupScreen
        call graphicsMode
        call paletteDraw
        call mouseReset
        call showCursor
        pop ax
        jmp input_loop
    palette_mode_off:
        call stdBrush
        call graphicsMode
        call restoreScreen
        call mouseReset
        call showCursor
    pop ax
    jmp input_loop
paletteModeToggle ENDP

它显示了一个调色板,它应该将光标更改为颜色采样器工具光标。相反,光标变成黑色方块,即使在调色板模式关闭时也保持这种状态。我怀疑我在更改光标时没有采取正确的步骤。当光标尚未显示时,它在程序开始时工作得很好。

在此过程中,我隐藏光标,然后更改光标掩码,然后将鼠标重置为默认驱动程序值(甚至不确定是否有必要),然后再次使其可见。我做错了吗?

顺便说一句,如果你还没有注意到,我正在使用 TASM。

如果您需要查看我的代码的更多部分,请告诉我。

【问题讨论】:

    标签: assembly mouse dosbox


    【解决方案1】:

    您无需隐藏/显示光标即可更改其形状。

    如果您看到一个黑框,请仔细检查

    • ES 细分。如果你正在构建一个 COM,它应该等于CS;如果你正在构建一个 EXE,如果游标在数据段中,它应该等于 DS,如果在代码段中,它应该等于 CS
    • 光标的位图。使用在所有颜色上都可见且易于生成的完整反转正方形 dw 32 DUP(0ffffh)(NASM 语法中为 TIMES 32 dw 0ffffh)进行测试。

    调试其他人的代码对于这个站点来说是无趣的,我将提供一个 MCWE(最小完整工作示例)来说明如何更改光标形状。

    按任意键改变光标。 再按一下退出。

    .286
    .MODEL TINY
    
    _CODE SEGMENT PARA PUBLIC 'CODE' USE16
        ASSUME CS:_CODE, DS:_CODE 
        ORG 100h
    
     __START__:
    
        call initGraphics                   ;Get into graphic mode and show cursor 
    
        push 08h 
        push 08h 
        push OFFSET barCursor
        call setCursor 
    
        xor ah, ah
        int 16h
    
        push 08h 
        push 08h 
        push OFFSET checkerCursor
        call setCursor 
    
        xor ah, ah
        int 16h
    
        call finalizeGraphics
    
        mov ax, 4c00h
        int 21h 
    
        ;
        ; PROCEDURES
        ;
    
        ;Set graphic mode, reset mouse and show cursor
     initGraphics:
        push es
    
        mov ax, 13h
        int 10h
    
        push 0a000h
        pop es 
        xor di, di 
        mov ax, 0909h
        mov cx, 320*200/2 
        rep stosw 
    
        xor ax, ax 
        int 33h 
    
        mov ax, 01h 
        int 33h
    
        pop es
        ret
    
     ;Hide cursor and set text mode
     finalizeGraphics:
        mov ax, 02h
        int 33h 
    
        mov ax, 03h 
        int 10h 
    
        ret 
    
     ;Set cursor
     ;Hotspot X
     ;Hotspot Y
     ;Ptr to cursor bitmaps
     setCursor:
        push bp 
        mov bp, sp
    
        pusha
        push es
    
        mov ax, 09h
        mov bx, WORD PTR [bp+08h]
        mov cx, WORD PTR [bp+06h]
        mov dx, WORD PTR [bp+04h]
        push ds                         ;Setting ES = DS is not necessary in COM
        pop es                          ;files unless somebody changed ES
        int 33h 
    
        pop es
        popa
    
        pop bp  
        ret 06h     
    
    
        ;
        ; CURSORS
        ;
    
        barCursor       dw  16 DUP(0fe7fh)
                        dw  16 DUP(0180h)
    
        checkerCursor   dd  8 DUP(5555aaaah)
                        dd  8 DUP(0aaaa5555h)
    
    _CODE ENDS 
    
    END __START__
    

    对于其他阅读器,光标位图的格式为1:

    OFFSET     SIZE       DESCRIPTION
     00h        32         16x16 pixel AND mask
     20h        32         16x16 pixel XOR mask
    

    16x16 像素 表示光标下的每个像素都映射到该矩阵中的一个位。
    光标大小为 16x16,因此每个 WORD(16 位)定义一行。
    一行中最左边的像素映射到 WORD 的 LSb。

    例如 WORD 4807h (0100 1000 0000 0111) 的第 1、第 2、第 3、第 12 和第 15 个像素为 1。

    AND掩码用于清除光标下的像素,1表示不影响像素,0表示为黑色。

    XOR 掩码用于反转光标下的像素,1 表示反转像素值(在模式 13h 中只是低半字节),0 表示不受影响。

    这来自 AND 和 XOR 的属性。


    1Ralf Brown Interrupt entry这里有点马虎。

    【讨论】:

    • 感谢您的深入解释。!事实证明 ES 没有正确设置为 DS,因为我将它保留在 A000 上是为了在您将大量像素写入屏幕时提供方便。我还删除了鼠标重置,现在一切正常。
    • @Itamar 我现在听起来很生气,但是,如果这个答案解决了你的问题,请将其标记为(通过接受它),以便未来的读者知道它有效(它会给我一些点!)
    【解决方案2】:

    据此page

    ax =0:将鼠标重置为默认驱动程序值:

    • 鼠标位于屏幕中心
    • 鼠标光标已重置并隐藏
    • 没有启用中断(掩码 = 0)
    • 双倍速度阈值设置为每秒 64 个米奇
    • 水平米奇与像素的比率(8 比 8)
    • 垂直米奇与像素的比率(16 比 8)
    • 视频模式的最大宽度和高度设置为最大值

    所以你的 stdBrush + graphicsMode + restoreScreen + resetMouse + showCursor 很可能是个问题。

    还有什么是graphicsMode?如果是设置gfx模式,很可能也会破坏光标图形。

    所以如果你想调用所有这些,我会首先尝试这个顺序:

    • 图形模式
    • 重置鼠标
    • 恢复屏幕
    • 标准刷
    • 显示光标

    或者尝试最小化设置/重置之类的东西,如果您一直处于相同的图形模式,“恢复屏幕”不能全部重绘?使用鼠标,我发现每次重置它的好处更少(尽管在 gfx 模式更改后可能是安全的)。


    Dosbox 模拟您 set it to 的内容。

    cputype = 自动 | 386 | 386_慢 | 486_慢 | pentium_slow | 386_prefetch


    关于如何更改光标 gfx .. 你不需要调用任何隐藏/重置/显示/等。只需使用新的 gfx 数据再次调用 set "INT 33,9"。它将立即替换它(它只是在 gfx 驱动程序中设置两个地址,让它知道它应该在哪里获取掩码 + 墨水数据,并且 gfx 驱动程序正在使用每个显示帧...... IIRC 那个是如何工作的)。

    我肯定记得,当我在 13 小时 DOS 模式下执行“精灵编辑器”时,我改为使用自己的光标绘制例程,所以我可以使用 256 色精灵(它们在编辑器中绘制的最终版本) .但我不记得任何技术细节,大约 25 年前。 :)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-16
      • 1970-01-01
      • 2012-02-20
      • 1970-01-01
      相关资源
      最近更新 更多