Неправильные результаты рисования горизонтальных и вертикальных линий в LFB, возвращенные VBE - PullRequest
2 голосов
/ 26 мая 2020

Мне наконец удалось нарисовать голубой пиксель на экране с помощью VESA B IOS Extensions (1920px * 1080px, 24bpp).

;esi = bytes per scan line
;edx = physical address of linear framebuffer memory.
;ebx = x coord * 3
;ecx = y coord

DrawPixel:
    push edx
    mov edx, 0
    mov eax, 0
    lea eax, [esi]
    ;mov ecx, 0
    mul ecx
    add eax, ebx
    jmp draw

draw:
    pop edx
    add edx, eax
    mov ebx, 0x3296fa

    mov [edx], ebx
    ret

Я попытался нарисовать голубую горизонтальную линию на экране используя «для l oop» следующим образом:

mov edi, 1920
call drawLoop
jmp $

drawLoop:
    dec edi                                       ;decrease edi
    cmp edi, 0                                    ;is edi equal to zero?
    jl doneLoop                                   ;then return
    imul ebx, edi, 3                              ;multiply edi by three and save the result in ebx
    mov ecx, 0                                    ;y = 0
    mov esi, ModeInfoBlock + 10h
    mov edx, dword[ModeInfoBlock + 28h]
    call DrawPixel                                ;Draw it!
    jmp drawLoop                                  ;run this again

doneLoop:
    ret

Однако это не работает: вместо этого отображается aws зеленая линия.


Когда я снова пытаюсь нарисовать вертикальную линию с помощью кода рисования / рисования пикселей, это тоже не работает. Он отображает пиксели со случайными цветами везде. Вот как я использую функцию DrawPixel для рисования вертикальной линии:

%include "../kernel/Services/Display/display.asm"

kernel:
    mov edi, 1080
    call drawLoop
    jmp $

drawLoop:
    dec edi
    cmp edi, 0
    jl doneLoop
    mov ecx, edi
    mov ebx, 0
    mov esi, ModeInfoBlock + 10h
    mov edx, dword[ModeInfoBlock + 28h]
    call DrawPixel
    jmp drawLoop

doneLoop:
    ret

Каким образом можно решить эти проблемы?

Ответы [ 2 ]

2 голосов
/ 01 июня 2020

Начнем с переписывания подпрограммы DrawPixel . В настоящее время это немного беспорядок!

Нет смысла использовать инструкцию mul, которая без надобности затирает регистр EDX. Лучше использовать вариант imul.

И вместо использования mov eax, 0 lea eax, [si] для загрузки регистра EAX, почему бы вам просто не написать mov eax, esi?

Также следует учитывать ошибку. Поскольку вы работаете на 24-битном истинном цветном экране, запись всего двойного слова (32 бита) изменит часть соседнего пикселя.

;esi = bytes per scan line
;edx = physical address of linear framebuffer memory.
;ebx = x coord * 3
;ecx = y coord

; IN (ebx,ecx,edx,esi) OUT () MOD (eax)
DrawPixel:
    mov     eax, esi                ; BytesPerScanLine
    imul    eax, ecx                ; BytesPerScanLine * Y
    add     eax, ebx                ; BytesPerScanLine * Y + X * 3
    mov     word [edx+eax], 0x96FA  ; Low word of RGB triplet
    mov     byte [edx+eax+2], 0x32  ; High byte of RGB triplet
    ret

Эта новая процедура теперь изменяет только регистр EAX


Часть main имеет собственные проблемы:

mov esi, ModeInfoBlock + 10h не будет извлекать информацию BytesPerScanLine . Для этого вам понадобится movzx esi, word [ModeInfoBlock + 10h]

l oop использует 2 ветви на каждой итерации. Вполне возможно написать l oop с помощью одной ветки.

Следующая моя версия. Поскольку новая процедура DrawPixel сохраняет все регистры (кроме EAX), возможны большие упрощения:

    xor     ebx, ebx                         ; X = 0  -> EBX = X * 3
    xor     ecx, ecx                         ; Y = 0
    movzx   esi, word [ModeInfoBlock + 10h]  ; BytesPerScanLine
    mov     edx, [ModeInfoBlock + 28h]       ; PhysBasePtr
    call    drawLoop
    jmp     $

drawLoop:
    call    DrawPixel                        ; Modifies EAX
    add     ebx, 3                           ; Like X = X + 1
    cmp     ebx, 1920*3                      ; Length of the line is 1920 pixels
    jb      drawLoop
    ret

Моя версия dr aws эта горизонтальная линия слева направо. Я считаю, что это может быть немного быстрее, чем рисование справа налево.

Вместо использования отдельного счетчика l oop (EDI) я управляю l oop через тройную координату X. Среди других преимуществ (например, скорость, потому что cmp и jb прекрасно сочетаются друг с другом), это снижает нагрузку на использование регистров.

Улучшенные процедуры рисования горизонтальных и вертикальных линий

Специально для рисования горизонтальных и вертикальные линии, не рекомендуется повторно вызывать процедуру DrawPixel . Снова и снова вычислять адрес пикселя - пустая трата времени. Ниже я показываю пару подпрограмм специально для этих задач.

Я добавил несколько дополнительных изменений:

  • Вы не должны перегружать основную программу техническими деталями адресации видеопамяти. . Пусть графические процедуры извлекают значения BytesPerScanLine и PhysBasePtr .
  • Основная программа должна работать с пикселями на уровне (X, Y). Этот материал « умножить на 3 » снова является технической деталью, относящейся к графическим процедурам.
  • Жесткое кодирование цвета в процедурах рисования очень не гибко.
; IN (eax,ebx,ecx,edx) OUT () MOD (eax)
; EAX = X
; EBX = Y
; ECX = Color
; EDX = Line length
HLine:
    push    edx
    push    edi
    movzx   edi, word [ModeInfoBlock + 10h]  ; BytesPerScanLine
    imul    edi, ebx                         ; BytesPerScanLine * Y
    imul    eax, 3                           ; X * 3
    add     edi, eax                         ; BytesPerScanLine * Y + X * 3
    add     edi, [ModeInfoBlock + 28h]       ; ... + PhysBasePtr
    mov     eax, ecx                         ; Color 24 bits
    shr     eax, 8
    imul    edx, 3                           ; Line length * 3
    add     edx, edi                         ; Address of the end of line
.a: mov     [edi], cx                        ; Low word of RGB triplet
    mov     [edi+2], ah                      ; High byte of RGB triplet
    add     edi, 3                           ; Like (X + 1)
    cmp     edi, edx
    jb      .a
    pop     edi
    pop     edx
    ret

Выше HLine процедура dr aws горизонтальная линия слева направо.

; IN (eax,ebx,ecx,edx) OUT () MOD (eax)
; EAX = X
; EBX = Y
; ECX = Color
; EDX = Line length
VLine:
    push    edx
    push    esi
    push    edi
    movzx   esi, word [ModeInfoBlock + 10h]  ; BytesPerScanLine
    mov     edi, esi
    imul    edi, ebx                         ; BytesPerScanLine * Y
    imul    eax, 3                           ; X * 3
    add     edi, eax                         ; BytesPerScanLine * Y + X * 3
    add     edi, [ModeInfoBlock + 28h]       ; ... + PhysBasePtr
    mov     eax, ecx                         ; Color 24 bits
    shr     eax, 8
    imul    edx, esi                         ; Line length * BytesPerScanLine
    add     edx, edi                         ; Address of the end of line
.a: mov     [edi], cx                        ; Low word of RGB triplet
    mov     [edi+2], ah                      ; High byte of RGB triplet
    add     edi, esi                         ; Like (Y + 1)
    cmp     edi, edx
    jb      .a
    pop     edi
    pop     esi
    pop     edx
    ret

Приведенное выше VLine процедура dr aws вертикальная линия сверху вниз.

Вот как вы можете использовать это:

Main:
    xor     eax, eax                         ; X = 0
    xor     ebx, ebx                         ; Y = 0
    mov     ecx, 0x003296FA                  ; Color cyan
    mov     edx, 1920                        ; Line length
    call    HLine                            ; -> (EAX)
    mov     edx, 1080
    call    VLine                            ; -> (EAX)
    jmp     $
2 голосов
/ 29 мая 2020

Рисование горизонтальной линии

Основываясь на комментариях, я решил свою проблему с рисованием горизонтальной линии, записав на экран видео только 3 байта вместо 4 байтов на каждый пиксель. Дополнительный байт изменял цвет следующего пикселя на экране. Мой исправленный код выглядит так:

DrawPixel:
    push edx
    mov edx, 0
    mov eax, 0
    mov eax, esi
    mul ecx
    add eax, ebx
    jmp draw

draw:
    pop edx
    add edx, eax
    mov word[edx], 0x96fa
    mov byte[edx + 2], 0x32
    ret

Рисование вертикальной линии

В коде, который генерирует вертикальную линию, мне удалось исправить проблему, заменив mov esi, ModeInfoBlock + 10h на movzx esi, word[ModeInfoBlock + 10h] .

Потому что инструкция movzx перемещает 16-битное bytesPerScanLine значение в 32-битный esi регистр, а остальные заполняет нулями. Это означает « mov e z ero e x тенд».

Мой измененный код вертикального рисования:

%include "../kernel/Services/Display/display.asm"
kernel:
    mov edi, 1920
    call drawLoop
    jmp $

drawLoop:
    dec edi
    cmp edi, 0
    jl doneLoop
    imul ebx, edi, 3
    mov ecx, edi
    movzx esi, word[ModeInfoBlock + 10h]
    mov edx, dword[ModeInfoBlock + 28h]
    call DrawPixel
    jmp drawLoop

doneLoop:
    ret

Это мои окончательные функции рисования:

;esi = bytes per scan line
;edx = physical address of linear framebuffer memory.
;ebx = x coord * 3
;ecx = y coord

DrawPixel:
    push edx
    mov edx, 0
    mov eax, 0
    mov eax, esi
    mul ecx
    add eax, ebx
    jmp draw

draw:
    pop edx
    add edx, eax
    mov word[edx], 0x96fa
    mov byte[edx + 2], 0x32
    ret
...