[英]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 坐標控制循環。 除了其他好處(比如速度,因為cmp
和jb
很好地配對),這減輕了寄存器使用的壓力。
尤其是繪制水平線和垂直線,重復調用DrawPixel例程不是一個好主意。 一遍又一遍地計算像素的地址是浪費時間。 下面我展示了一些專門用於這些任務的例程。
我添加了一些額外的更改:
; 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.