屏幕还是借来的
咱们把 stage2 的家底盘点一遍:栈是咱们自己的了,顶在 0x7000 的地方往下长。A20 的门开了,地址线全数放了行。内存图谱问回了七条,全躺在 .bss 的存档里。可是您抬眼看看终端,咱们打的每一行字,走的都是 debugcon,那是 port 0xE9 上专供调试的暗线。真正的屏幕呢?它还停在 BIOS 开机时给的文本模式上,还是 80 列 25 行的老样子,光标一闪一闪的。咱们从头到尾写下的字,没有一个是给这块屏幕的。
这有什么不妥吗?眼下倒也没有。可您想想机器接下来的路:进了保护模式,BIOS 就下班了,而文本模式是 BIOS 替咱们维持的。它一旦离场的话,咱们的屏幕就定格在了最后一帧上头,咱们连一个像素都碰不着。想要一块自己的画布,咱们就得趁 BIOS 还应答的当口,把图形模式的许可拿到手。
图形模式里咱们要的是线性帧缓冲,它的英文名字,咱们写作 linear framebuffer。显卡的显存,比实模式一次够得着的范围大得多,老式的应付法子叫 banked mode:显存被切成一扇一扇 64KB 的窗口,您要画哪儿,咱们就得来回挑窗口、挪窗口,咱们画一屏图的时候,切换窗口的活儿还得来好些回了。线性帧缓冲就大方多了:显卡把整块显存连续地映射到一个物理地址上,第几行第几列的像素住在哪儿,咱们做一个乘法就能算出来。将来咱们画字、开窗口,脚下踩的就是这块连续的地板。
要这块地板,咱们能走的路只剩一条了:VBE,也就是 VESA 挂到 BIOS 那套老视频服务上的扩展。它住的那个 INT 10h,服务号是从 0x4F00 起头的,跟我们问内存时用的 INT 15h 是同一个路子。具体怎么问,咱们下一节慢慢来,这里咱们把两件事定下来。
咱们要看的第一件事是次序。上一站的末尾咱们预告过,配屏是起居室里的最后一件家具。这话咱们现在可以说得更重些:它不光要排在最后,咱们还只能排在现在。OSDev 的 VBE 页上记着各家 BIOS 的一条脾性,设模式的那个调用,有的实现为了自己方便,会悄悄地切进保护模式再切回来,顺手把调用方的 GDT 换掉。GDT 是保护模式要用的全局描述符表,咱们下一站才动手搭它。眼下咱们手里还没有,这一手伤不着咱们,等咱们自建 GDT 之后再调它,它一伸手就可能把咱们的表换掉。所以这件事天然属于 BIOS 还在岗的现在,往后就轮不上咱们了。
咱们要定的另一件事是层次。本站咱们把模式切过去,把 framebuffer 的参数问回来、存好,然后就收工了,咱们一个像素都没画。理由很朴素:本站还没有消费帧缓冲的资格,地址怎么分配、映射怎么立起来,这些都得等内存管理那一套成形了。所以本站交付的就一句话:画布挂上墙了,尺寸、地址、行距这些参数写进存档。消费它们的活,等内存管理立起来之后的章节再算。上一站咱们对内存图谱就是这么办的:探测的归探测,解释的归消费。
收工的时候,咱们敲的还是那道熟悉的命令:
cmake -B build && cmake --build build --target run咱们敲下去,终端里前头那几行还是老样子了,跟 E820 有关的部分一个字都没动。崭新的是它们下面接上的三行:
E820: 7 entries
#00 base=0000000000000000 len=000000000009FC00 type=1
...(七条,跟上一站一模一样)
VESA: VBE 0x0300 93 modes
VESA: 0x4144 1024*768*32 LFB
fb 00000000FD000000 pitch 4096
[stage2] alive咱们一行行来读。头一行 VESA: VBE 0x0300 93 modes 报的是固件版本和它支持的模式数。第二行 VESA: 0x4144 1024*768*32 LFB 就是咱们挑中并切过去的那个模式,末尾的 LFB 是线性帧缓冲的记号,置上了它,显卡给咱们的才是一整块连续显存。第三行 fb 00000000FD000000 pitch 4096 给出画布的物理地址和一行像素占的字节数。这三行该怎么读,九十三个模式又怎么挑出来一个,就是本站接下来的全部故事。