Графический курсор в сборке

Я пытаюсь сделать небольшую программу, похожую на рисование, на ассемблере для DOSBox. Я не уверен, какой тип процессора эмулирует DOSBox, но из того, что я нашел, это может быть 386.

Я провел исследование и выяснил, как использовать прерывание мыши, 33h. Мне также удалось использовать маски экрана и маски курсора для определения собственного курсора. Однако я хочу иметь возможность изменить свой курсор позже в программе, когда пользователь переключится, например, на инструмент цветового сэмплера. Когда я пытался это сделать, курсор превращался в черный квадрат. Это потому, что я не знаю точно шаги, необходимые для выполнения такого изменения.

Я должен скрыть курсор, затем сбросить его, затем изменить маску курсора, а затем снова показать? Менять маску и только ПОТОМ сбрасывать? Мне вообще не нужно сбрасывать мышь? Вот весь мой код, связанный с мышью.

P.S. Я знаю, что многое из этого можно оптимизировать, превратив кучу процессов в макросы, и я мог бы сделать это в будущем, когда разберусь с ошибками.

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.

Если вам нужно увидеть какие-либо другие части моего кода, пожалуйста, дайте мне знать.


person Itamar    schedule 20.07.2016    source источник


Ответы (2)


Вам не нужно скрывать/показывать курсор, чтобы изменить его форму.

Если вы видите черный ящик, дважды проверьте

  • Сегмент ES. Если вы создаете COM, он должен быть равен CS; если вы создаете EXE, он должен быть равен DS, если курсоры находятся в сегменте данных, или CS, если они находятся в сегменте кода.
  • Растровые изображения курсоров. Протестируйте с помощью полного инвертирующего квадрата dw 32 DUP(0ffffh) (TIMES 32 dw 0ffffh в синтаксисе NASM), который виден на всех цветах и ​​его легко создать.

Отладка чужого кода неинтересна для этого сайта, вместо этого я предоставлю 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

Пиксел 16 x 16 означает, что каждый пиксель под курсором сопоставляется с битом в этой матрице.
Размер курсора – 16 x 16, поэтому каждое СЛОВО (16 бит) определяет строку.
Крайний левый пиксель в строке сопоставляется с LSb слова WORD.

Например, СЛОВО 4807h (0100 1000 0000 0111) имеет 1 для 1-го, 2-го, 3-го, двенадцатого и пятнадцатого пикселя.

Маска И используется для очистки пикселей под курсорами, 1 означает, что пиксель остается незатронутым, 0 делает его черным.

Маска XOR используется для инвертирования пикселей под курсорами, 1 означает инвертирование значения пикселя (в режиме 13h только младший полубайт), 0 означает, что он не изменяется.

Это происходит из-за свойств AND и XOR.


1 Запись прерывания Ральфа Брауна здесь немного неаккуратна .

person Margaret Bloom    schedule 21.07.2016
comment
Спасибо за подробное объяснение.! Оказывается, ES не был должным образом установлен на DS, потому что я оставил его на A000 для удобства, которое он обеспечивает, когда вы пишете много пикселей на экран. Я также удалил сброс мыши, и теперь все работает. - person Itamar; 21.07.2016
comment
@Itamar Я сейчас звучу плаксиво, но, если этот ответ решил вашу проблему, отметьте его как таковой (приняв его), чтобы будущие читатели знали, что он работает (и это даст мне несколько баллов!) - person Margaret Bloom; 21.07.2016

Согласно этой странице:

ax =0: сбрасывает мышь к значениям драйвера по умолчанию:

  • мышь находится в центре экрана
  • мышь курсор сброшен и скрыт
  • прерывания не разрешены (маска = 0)
  • двойной порог скорости установлен на 64 микки в секунду
  • отношение горизонтального микки к пикселю (8 к 8)
  • отношение вертикального микки к пикселю (16 к 8)
  • максимальная ширина и высота установлены на максимум для видеорежима

Таким образом, ваш stdBrush + graphicsMode + restoreScreen + resetMouse + showCursor, скорее всего, является проблемой.

И что такое graphicsMode? Если он настраивает режим gfx, он, скорее всего, также уничтожит графику курсора.

Итак, если вы хотите назвать все это, я бы сначала попробовал такой порядок:

  • графический режим
  • сбросить мышь
  • восстановить экран
  • стандартная кисть
  • показатькурсор

Или, может быть, попытаться свести к минимуму такие вещи, как установка / сброс, если вы все время находитесь в одном и том же графическом режиме, «экран восстановления» не может все это перерисовать? С мышью я вижу еще меньше пользы от ее сброса каждый раз (хотя это может быть безопасно после смены режима gfx).


Dosbox эмулирует то, что вы настроили.

тип процессора = авто | 386 | 386_медленно | 486_медленно | пентиум_медленный | 386_prefetch


О том, как изменить gfx курсора.. вам не нужно вызывать никаких скрыть/сбросить/показать/и т.д. Просто снова вызовите набор "INT 33,9" с новыми данными gfx. Он немедленно заменит его (он просто устанавливает два адреса в драйвере gfx, чтобы сообщить ему, откуда он должен получать данные маски + чернила, и драйвер gfx использует это каждый кадр дисплея ... IIRC, как это работало).

Я точно помню, когда я делал свой «редактор спрайтов» в 13-часовом режиме DOS, вместо этого я использовал свою собственную процедуру рисования курсора, поэтому я мог использовать 256-цветные спрайты (их окончательные версии, нарисованные в самом редакторе). Но я не могу вспомнить никаких технических подробностей, это ~ 25 лет назад. :)

person Ped7g    schedule 20.07.2016