装配体中的图形光标

Graphics Cursor in Assembly

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

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

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

P.S。我知道我可以通过将一堆 procs 变成宏来优化其中的很多,一旦我解决了错误,我可能会在未来这样做。

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

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

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

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。

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

据此page:

ax =0: resets mouse to default driver values:

  • mouse is positioned to screen center
  • mouse cursor is reset and hidden
  • no interrupts are enabled (mask = 0)
  • double speed threshold set to 64 mickeys per second
  • horizontal mickey to pixel ratio (8 to 8)
  • vertical mickey to pixel ratio (16 to 8)
  • max width and height are set to maximum for video mode

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

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

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

  • 图形模式
  • 重置鼠标
  • 恢复画面
  • stdBrush
  • 显示光标

或者尽量减少 set/reset 之类的东西,如果你一直停留在同一个图形模式下,"restore screen" 不能全部重绘吗?对于鼠标,我发现每次重置它的好处更少(尽管在 gfx 模式更改后可能是安全的)。


Dosbox 模拟您set it to

cputype = auto | 386 | 386_slow | 486_slow | pentium_slow | 386_prefetch


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

我确实记得,当我在 13h DOS 模式下做我的 "sprite editor" 时,我转而使用我自己的光标绘制例程,所以我可以使用 256 色精灵(它们的最终版本在编辑器本身)。但我不记得任何技术细节,大约 25 年前。 :)

您不需要 hide/show 光标来改变它的形状。

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

  • 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。

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

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

这来自 AND 和 XOR 的属性。


1 Ralf Brown Interrupt entry这里有点马虎