装配体中的图形光标
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这里有点马虎
我正在尝试在 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这里有点马虎