Reputation: 137
I'm trying to make a small paint-like program in Assembly for DOSBox. I'm not sure what CPU type DOSBox emulates though, but from what I've found it might be 386.
I did my research and figured out how to use the mouse interrupt, 33h. I also managed to use the screen masks and cursor masks to define my own cursor. However, I want to be able to change my cursor later in the program when the user switches to, for example, the color sampler tool. When I tried to do this, the cursor turned into a black square. This is because I do not know exactly the steps required to perform such a change.
Am I supposed to hide the cursor, then reset it, then change the cursor mask, then show it again? Do I change the mask and only THEN reset? Do I not need to reset the mouse at all? Here is all my mouse-related code.
P.S. I know that I can optimize a lot of this by turning a bunch of procs into macros, and I might do that in the future once I have the bugs sorted out.
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
Those are small procedures that act as shortcuts for using the different functions of int 33h. The exceptions are initCursor, which combines those shortcuts to initialize the cursor at the start of the program, and stdBrush & pickerTool which both set the graphics cursor to a specific cursor (as you can guess, stdBrush is the standard brush cursor, and pickerTool sets the cursor for the color sampler tool).
Below are the masks for my two cursors.
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
There are some indentation discreprencies that are caused by the copying of the code to stackoverflow, I couldn't bother to go line-by-line and fix them.
Here is the part of the program that causes me trouble:
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
It shows a color palette, and it's supposed to change the cursor into the color sampler tool cursor. Instead, the cursor turns into a black square and stays that way even when palette mode is toggled off. I suspect that I did not take the correct steps while changing the cursor. It works just fine at the start of the program, when the cursor hasn't been shown yet.
In this procedure, I hide the cursor, then I change the cursor mask, then I reset the mouse to default driver values (not even sure this is necessary) and then make it visible again. Am I doing this wrong?
BTW in case you haven't noticed yet, I'm using TASM.
If you need to see any more parts of my code, please let me know.
Upvotes: 2
Views: 3779
Reputation: 44046
You don't need to hide/show the cursor in order to change its shape.
If you see a black box, double check
ES
segment. If you are building a COM it should be equal to CS
; if you are building an EXE it should be equal to DS
if the cursors are in the data segment or CS
if they are in the code segment. dw 32 DUP(0ffffh)
(TIMES 32 dw 0ffffh
in NASM syntax) that is visible over every color and easy to generate.Debugging other people code is uninteresting for this site, I'll provide instead a MCWE (Minimal Complete Working Example) of how to change the cursor shape.
Press any key to change the cursor. Press again to exit.
.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__
For other readers, the format of the cursor bitmaps is1:
OFFSET SIZE DESCRIPTION
00h 32 16x16 pixel AND mask
20h 32 16x16 pixel XOR mask
The 16x16 pixel means that each pixel under the cursor is mapped to a bit in that matrix.
The cursor size is 16x16 so each WORD (16 bits) define a row.
The leftmost pixel in a row is mapped to the LSb of the WORD.
For example the WORD 4807h (0100 1000 0000 0111) has a 1 for the 1st, 2nd, 3rd, twelfth and fifteenth pixel.
The AND mask is used to clear the pixels under the cursors, a 1 means to leave the pixel unaffected, a 0 set it to black.
The XOR mask is used to invert the pixels under the cursors, a 1 means to invert the pixel value (in mode 13h just the lower nibble), a 0 means to leave it unaffected.
This comes from the properties of AND and XOR.
1 Ralf Brown Interrupt entry is a little sloppy here.
Upvotes: 2
Reputation: 16596
According to this 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
So your stdBrush + graphicsMode + restoreScreen + resetMouse + showCursor is very likely a problem.
Also what is graphicsMode
? If it is setting up the gfx mode, it will very likely destroy cursor graphics too.
So if you want to call all that, I would at first try this order:
Or maybe try to minimize things like set/reset, if you are staying all the time in the same graphics mode, the "restore screen" can't redraw it all? With mouse I see even less benefit of resetting it each time (although may be safe after gfx mode change).
Dosbox emulates what you set it to.
cputype = auto | 386 | 386_slow | 486_slow | pentium_slow | 386_prefetch
About how to change cursor gfx .. you don't need to call any hide/reset/show/etc. Just call set "INT 33,9" again with new gfx data. It will replace it immediately (it's just setting up two addresses in gfx driver, to let it know where it should fetch the mask+ink data, and the gfx driver is using that every display frame ... IIRC how that one worked).
I recall for sure, when I was doing my "sprite editor" in 13h DOS mode, that I went instead for my own cursor draw routine, so I could use the 256color sprites (their final versions drawn in the editor itself). But I can't recall any technical details, it's ~25y back. :)
Upvotes: 0