簡體   English   中英

將水平和垂直線繪制到 VBE 返回的 LFB 的結果不正確

[英]Incorrect results drawing horizontal and vertical lines to the LFB returned by VBE

我終於設法使用 VESA BIOS 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

我嘗試以這種方式使用“for循環”在屏幕上繪制一條青色水平線:

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

但是,這不起作用:它會畫一條綠線。


當我嘗試使用繪圖/繪圖像素代碼再次繪制垂直線時,它也不起作用。 它在各處繪制隨機 colors 的像素。 以下是我使用DrawPixel function 繪制垂直線的方法:

%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

有什么辦法可以解決這些問題?

繪制水平線

根據評論,我通過僅向視頻顯示器寫入 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寄存器中,並用零填充 rest。 它代表“ mov e z ero e xtend ”。

我修改后的垂直繪圖代碼:

%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

讓我們從重寫DrawPixel例程開始。 目前有點亂!

使用mul指令會不必要地破壞EDX寄存器是沒有意義的。 最好使用imul的變體。

而不是使用mov eax, 0 lea eax, [si]來加載EAX寄存器,你為什么不簡單地寫mov eax, esi呢?

還有一個錯誤需要考慮。 因為您正在使用 24 位真彩色屏幕,所以寫入整個 dword(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寄存器


主要部分有其自身的問題:

mov esi, ModeInfoBlock + 10h不會檢索BytesPerScanLine信息。 為此,您需要movzx esi, word [ModeInfoBlock + 10h]

循環在每次迭代中使用 2 個分支。 完全可以用單個分支編寫循環。

接下來是我的版本。 因為新的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

我的版本從左到右繪制這條水平線。 我相信它可能比從右到左繪制要快一點。

我沒有使用單獨的循環計數器 ( EDI ),而是通過三倍 X 坐標控制循環。 除了其他好處(比如速度,因為cmpjb很好地配對),這減輕了寄存器使用的壓力。

更好的水平和垂直線條繪制例程

尤其是繪制水平線和垂直線,重復調用DrawPixel例程不是一個好主意。 一遍又一遍地計算像素的地址是浪費時間。 下面我展示了一些專門用於這些任務的例程。

我添加了一些額外的更改:

  • 您不應該用解決視頻 memory 的技術細節來負擔主程序。 讓圖形例程檢索BytesPerScanLinePhysBasePtr值。
  • 主程序應處理 (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例程從左到右繪制一條水平線。

; 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例程從上到下繪制一條垂直線。

這是您可以使用這些的方式:

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     $

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM