首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >绘制VBE返回的LFB的水平线和垂直线的错误结果

绘制VBE返回的LFB的水平线和垂直线的错误结果
EN

Stack Overflow用户
提问于 2020-05-26 19:29:50
回答 2查看 252关注 0票数 2

最后,我使用VESA扩展(1920 to *1080 to,24 BIOS )在屏幕上绘制了一个青色像素。

代码语言:javascript
运行
复制
;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循环“这样的方式在屏幕上画一条青色的水平线:

代码语言:javascript
运行
复制
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

然而,这是行不通的:它画了一条绿线。

当我试图再次用绘制/绘制像素代码绘制垂直线时,它也不起作用。它到处用随机颜色绘制像素。下面是如何使用DrawPixel函数绘制一条垂直线:

代码语言:javascript
运行
复制
%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

有办法解决这些问题吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-05-31 22:33:50

让我们从重写DrawPixel例程开始。现在有点乱!

使用mul指令是没有意义的,因为它会不必要地破坏EDX寄存器。最好使用imul的变体。

与其使用mov eax, 0 lea eax, [si]加载EAX寄存器,不如直接编写mov eax, esi

还有一个错误需要考虑。因为您正在使用24位的真彩色屏幕,所以编写一个完整的dword (32位)将改变相邻像素的一部分。

代码语言:javascript
运行
复制
;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]

循环在每次迭代中使用两个分支。用一个分支编写循环是完全可能的。

下一个是我的版本。由于新的DrawPixel例程保留了所有寄存器(EAX除外),因此可以进行很大的简化:

代码语言:javascript
运行
复制
    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例程不是一个好主意。一遍又一遍地计算像素的地址是浪费时间。下面我展示了一些专门针对这些任务的例程。

我增加了一些额外的修改:

  • 您不应该将寻址视频内存的技术细节给主程序带来负担。让图形例程检索BytesPerScanLine和PhysBasePtr值。主程序
  • 应该在(X,Y)级别处理像素。那个“乘以3”的东西又是一个属于图形routines.
  • Hard的技术细节,它编码绘图例程中的颜色是非常不灵活的。

代码语言:javascript
运行
复制
; 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例程从左到右画一条水平线。

代码语言:javascript
运行
复制
; 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例程从上到下画一条垂直线。

这就是你如何使用这些:

代码语言:javascript
运行
复制
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
EN

Stack Overflow用户

发布于 2020-05-29 13:27:52

绘制水平线

根据注释,我解决了绘制水平线的问题,只将3个字节写入视频显示,而不是为每个像素编写4个字节。额外的字节正在改变屏幕上下一个像素的颜色。修改后的代码如下:

代码语言:javascript
运行
复制
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寄存器中,并用零填充其余部分。它的意思是"move zero ex“。

我修改过的垂直绘图代码:

代码语言:javascript
运行
复制
%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

这是我最后的绘图功能:

代码语言:javascript
运行
复制
;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
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62029926

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档