如何在没有BIOS的情况下在屏幕上绘制像素?

时间:2018-12-20 20:00:42

标签: user-interface operating-system osdev

我正在编写一个操作系统,并希望具有GUI。我找不到在屏幕上绘制像素的良好教程。

我想要一些汇编+ C示例,可以在某些仿真器(如BOCHS或v86)上构建并运行

2 个答案:

答案 0 :(得分:3)

基本思想是:

1)引导程序使用固件(BIOS上的VBE,UEFI上的GOP或UGA)设置显示器,视频卡和OS支持的图形模式;在执行此操作时,它会从固件中获取有关帧缓冲区的相关信息(帧缓冲区的物理地址,水平和垂直分辨率,像素格式,水平行之间的字节),并将其传递给操作系统;这样,操作系统就可以在“早期初始化”(启动本机视频驱动程序之前)期间使用此信息,并在没有合适的本机视频驱动程序的情况下继续使用它(作为一种“渐进模式”)。

2)OS使用该信息来确定如何写入帧缓冲区。这可能是类似physical_address = base_address + y * bytes_between_lines + x * bytes_per_pixel的计算(其中bytes_per_pixel是根据像素格式确定的。)

“早期初始化”注意事项:

  • 出于性能方面的考虑,最好将所有内容绘制到RAM中的缓冲区中,然后将数据从RAM中的缓冲区复制(“ blit”)到帧缓冲区。
  • 出于性能原因,将数据从RAM中的缓冲区复制(“ blit”)到帧缓冲区的代码可以/应该使用一些技巧来避免复制自上次以来没有改变的数据。
  • 为支持许多不同的像素格式,可以对RAM中的缓冲区使用“标准”像素格式(例如,“ 8位红色,8位绿色,8位蓝色,8位填充”)并在将数据从RAM中的缓冲区复制到帧缓冲区时,将其转换为视频卡碰巧需要的任何像素格式(例如“ 5位蓝色,6位绿色,5位红色,无填充”)。这样一来,您就可以使用所有功能的单一版本来绘制事物(字符,线条,矩形,图标等),而不必使用具有多种功能的多个不同版本(每种可能的像素格式一个)。

“中间初始化”注意事项:

    最终,操作系统将尝试为所有不同的设备查找并启动合适的设备驱动程序。这包括尝试为视频卡找到合适的驱动程序(例如,支持垂直同步,GPU,GPGPU等)。
  • 您需要设计一个视频驱动程序接口,本机视频驱动程序可以使用该接口(理想情况下)支持现代功能(例如,可能具有完整的3D图形和着色器)。
  • 当没有本机视频驱动程序时,操作系统可以/应该启动一个“通用帧缓冲区”驱动程序,该驱动程序实现相同的视频驱动程序接口(旨在支持硬件加速),该接口可以在软件中完成所有工作而没有硬件的好处加速。
  • 启动视频驱动程序时,操作系统需要某种“切换”,其中帧缓冲区的所有权从较早的启动代码传递给视频驱动程序。在“移交”之后,较早的启动代码(旨在将内容直接吸引到帧缓冲区)不应触摸帧缓冲区,而应要求视频驱动程序执行“转换像素数据并将其复制到帧缓冲区”的工作。

“初始化后”的注意事项:

  • 对于传统的“ 2D GUI”;通常,您为背景/桌面有一个缓冲区(或“画布”或“纹理”或其他东西),为每个窗口或对话框有更多的缓冲区/画布,对于较小的东西(例如,鼠标指针,拖放)可能有更多的缓冲区/画布。下拉菜单,“小部件”等);这样,应用程序就可以修改其缓冲区/画布(但出于安全原因而被阻止直接或间接访问任何其他缓冲区/画布)。然后,GUI告知视频驱动程序应在何处绘制这些缓冲区/画布中的每一个;视频驱动程序(如果是本地视频驱动程序,则使用硬件加速)将这些片段组合在一起(“合成”)以获取整个帧的像素数据,然后进行像素格式转换(希望使用GPU)以获取原始像素数据到显示/发送到显示器。这意味着在存在本机视频驱动程序时,各种操作(在屏幕上移动窗口,在窗口之间“ alt切换”,在鼠标之间移动等)变得非常快,因为CPU不执行任何操作,而视频卡本身也可以完成所有工作

  • 理想情况下,应用程序会有一种方法(例如OpenGL)要求视频驱动程序在应用程序的缓冲区/画布中绘制内容;这样,视频卡就可以完成更多工作(而CPU无法完成)。这对于3D游戏尤为重要,但是没有理由为什么普通的2D应用程序无法从对2D图形使用相同的方法中受益。

请注意,大多数初学者会做错所有事情(没有设计良好的本机视频驱动程序界面),因此永远不会有任何本机视频驱动程序,因为他们的所有软件始终无法使用本机视频驱动程序。这些人可能会试图说服您这不值得麻烦(因为在他们的经验中,原生视频驱动程序将永远不存在)。现实情况是,大多数本机视频驱动程序极难编写,但其中一些(对于虚拟机)并不难编写。而您的目标应该是允许其他人(通过设计合适的界面并提供足够的文档)最终编写驱动程序,而不是自己编写所有驱动程序。

答案 1 :(得分:3)

最佳答案在解释方面做得很好。您确实要求提供一些示例代码,所以这是我的GitHub上的代码段,下面将进行详细说明。

1. bios_setup:
2.  mov ah, 00h     ; tell the bios we'll be in graphics mode
3.  mov al, 13h
4.  int 10h         ; call the BIOS

5.  mov ah, 0Ch     ; set video mode
6.  mov bh, 0       ; set output vga
7.  mov al, 0       ; set initial color
8.  mov cx, 0       ; x = 0
9.  mov dx, 0       ; y = 0

10. int 10h         ; BIOS interrupt

第2行是乐趣的起点。首先,我们将值0移到ah寄存器中。在第3行,我们将13进制数移入al -现在我们可以进行BIOS调用了。 第4行调用中断向量为10 hex的bios。 BIOS现在检查ah和al。

AH:
 - tells BIOS to set video mode
AL:
 - tells BIOS to enter write string mode.

现在我们在第4行调用了中断,现在可以将新值移入某些寄存器了。 在第5行,我们将0C hex放入ah寄存器。 这告诉BIOS我们要写入图形像素。 在第6行,我们将0放入bh寄存器,这告诉BIOS我们将使用CGA,EGA,MCGA或VGA适配器进行输出。所以输出模式基本上是0。 接下来,我们要做的就是设置颜色。因此,让我们从0开始,它是黑色的。 都很好,但是我们要将黑色像素实际绘制到哪里呢? 那是第8-9行进入的地方,寄存器cx和dx分别存储要绘制的像素的x,y坐标。 设置好后,我们将以中断10进制的形式调用BIOS。然后画出像素。

在阅读了布伦丹详尽而翔实的答案后,这段代码将使 更有意义。某些值必须在某些寄存器中才能调用 BIOS仅仅因为这些是相应中断所位于的寄存器 会检查。其他一切都非常简单。如果你想要另一个 颜色,只需更改al中的值即可。您想在其他地方像素化吗? 混乱在cx和dx中的x和y值。再次,这不是很 对于图形密集型程序非常有效,因为它相当慢。 但是,出于教育目的,它胜过编写自己的图形驱动程序;)

通过将所有内容绘制到RAM中的缓冲区之前,您仍然可以获得一些效率 就像布兰登所说的那样在屏幕上闪烁,但我宁愿保持简单 我的例子。

my GitHub上查看完整的免费示例。我还提供了自述文件和Makefile,但它们是Linux独有的。如果您在Windows上运行,则进行一些谷歌搜索会生成将OS组装到可引导软盘上所需的任何信息,几乎任何虚拟机主机都可以。另外,请随时问我任何不清楚的问题。干杯!

Ps:我没有写工具,只是在NASM中写了一个小脚本,打算将其组装到软盘上并作为内核(如果需要,可以在VM中运行)