手写操作系统(四)-MBR程序操控显卡

代码、内容参考来自于包括《操作系统真象还原》、《一个64位操作系统的设计与实现》以及《ORANGE’S:一个操作系统的实现》。

1.MBR概述

MBR(Master Boot Record,主引导记录)是存储在计算机硬盘的第一个扇区(通常是第0号扇区)中的特殊数据结构。它用于引导加载程序,MBR包含一个小程序,通常称为引导加载程序(Boot Loader),负责引导操作系统的加载过程。当计算机启动时,BIOS会读取硬盘的MBR,并将控制权交给其中的引导加载程序。

MBR的大小必须是512字节,这是为了保证0x55和0xaa这两个魔数恰好出现在该扇区的最后两个字节处,即第510字节处和第511字节处,这是按起始偏移为0算起的。 由于我们模拟的是x86平台,所以是小端字节序,故其最后两个字节内容是0xaa55,也可以拆开,就是0x55和0xaa。

 

2.IO接口

CPU与硬件一般不会直接交流,否则CPU的速度将被拖慢。

CPU与外设通信的桥梁就是IO接口。

IO 接口是连接 CPU 与外部设备的逻辑控制部件,既然称为逻辑,就说明可分为硬件和软件两部分。 硬件部分所做的都是一些实质具体的工作,其功能是协调CPU和外设之间的种种不匹配,如双方由于速度不匹配,那IO接口就实现数据缓冲以减少等待时间,数据格式不匹配, IO接口就在这两种格式间互相转换。 IO接口内部实际上也是由软件来控制运作的,这就是所谓的“逻辑”部分,所以软件是指用来控制接口电路工作的驱动程序以及完成内部数据传输所需要的程序。

IO接口是连接CPU和硬件的桥梁,一端是CPU,另一端是硬件。端口是 IO 接口开放给 CPU 的接口,一般的 IO接口都有一组端口,每个端口都有自己的用途,甚至有时,一个端口在不同情况下有不同的用途。

端口也是寄存器,寄存器就有数据宽度,有 8 位、16 位、32 位,各个设备是不一样的,看厂商自己安排了。既然如此,端口当然也可以操控。

IA32体系系统中,因为用于存储端口号的寄存器是16位的,所以最大有65536个端口,即0~65535。

要是通过内存映射,端口就可以用mov指令来操作。 但由于用的是独立编址,所以就不能把它当作内存来操作,因此CPU提供了专门的指令来干这事, in和out。

Intel汇编语言的形式是:操作码 目的操作数 源操作数。

in 指令用于从端口中读取数据,其一般形式是:

  • in al,dx;
  • in ax,dx

其中 al 和 ax 用来存储从端口获取的数据,dx 是指端口号。 这是固定用法,只要用 in 指令,源操作数(端口号)必须是dx,而目的操作数是用al,还是ax,取决于dx 端口指代的寄存器是8位宽度,还是16位宽度。

out 指令用于往端口中写数据,其一般形式是:

  • out dx, al
  • out dx,ax
  • out 立即数,al
  • out 立即数,ax

这和in指令相反, in指令的源操作数是端口号,而out指令中的目的操作数是端口号。

在以上两个指令的两个操作数中,无论是对于源操作数,还是目的操作数,除端口号外,那个作为数据的操作数(in指令中作为数据目的地,out指令中作为数据源),一律用al寄存器存储8位宽度的数据,用ax寄存器存储16位宽度的数据,至于用al,还是ax存数据,要看端口指向的寄存器宽度是多少,它要和端口寄存器的位宽保持一致,不能丢失数据精度。

注意:in指令中,端口号只能用dx寄存器。而out指令中,可以选用dx寄存器或立即数充当端口号。

 

3.显卡

适配器是驱动某一外部设备的功能模块。 显卡也称为显示适配器,不过归根结底它就是IO接口,专门用来连接CPU和显示器。 我们想操作显示器只能通过它的IO接口–显卡。

如图:

各外部设备都是通过软件指令的形式与上层接口通信的,显卡(显示适配器)也不例外,所以它也有自己的BIOS。 位置是0xC0000到0xC7FFF。 显卡支持三种模式:文本模式、黑白图形模式、彩色图形模式。 我们只关注文本模式就好了,最终我们要实现类似Linux终端那样的字符界面。

从起始地址0xB8000到0xBFFFF,这片32KB大小的内存区域是用于文本显示。 我们往0xB8000处输出的字符直接会落到显存中,显存中有了数据,自然显卡就将其搬到显示器屏幕上了。

显存是从0xB8000~0xBFFFF,范围是32KB,一屏可以显示2000个字符,显示器上的每个字符占2字节大小,故每屏字符实际占用4000字节。这样,我们的32KB的显存可以容纳8屏的数据。

这里加载b800开始,也就是文本模式显示。

屏幕上每个字符的低字节是字符的 ASCII 码,高字节是字符属性元信息。 在高字节中,低4位是字符前景色,高4位是字符的背景色。 颜色用RGB红绿蓝三种基色调和,第4位用来控制亮度,若置1则呈高亮,若为0则为一般正常亮度值。 第7位用来控制字符是否闪烁(不是背景闪烁)

对应样式:

举个例子:0x07 表示白色字体、字体正常、白色背景、不闪烁

 

4.代码演示

mov ax,0xb800
mov es,ax

mov byte [es:0x00],'D'
mov byte [es:0x01],0x05
jmp $
times 510 - ($-$$) db 0
dw 0xaa55

代码解释:

  • mov ax, 0xb800:将地址 0xb800 装入寄存器 AX 中。在实模式下,这是显存起始地址,用于文本模式下的显存操作。
  • mov es, ax:将 AX 中的值加载到 ES 寄存器,这样 ES 就指向了显存的起始地址。
  • mov byte [es:0x00], ‘D’:将字符 ‘I’ 写入显存地址 0xb800:0x00 处,即屏幕的第一个字符位置。
  • byte 关键字指示操作的数据大小为一个字节。[es:0x00] 指定了内存地址,其中 es 是段寄存器,0x00 是偏移量。这将在段寄存器 es 指向的段内存地址的偏移量 0x00 处存储数据,也就是es*16+0x00。
  • mov byte [es:0x01], 0x05:将属性字节 0x05 写入显存地址 0xb800:0x01 处。这里 0x05 是字体为紫色,背景为黑色的属性值。
  • jmp $:无条件跳转到当前位置,实际上是一个死循环,程序将永远停留在这里。
  • $属于“隐式地”藏在本行代码前的标号,也就是编译器给当前行安排的地址,$$指代本 section 的起始地址,此地址同样是编译器给安排的。

这段代码的功能是在屏幕左上角显示字符 ‘D’,并设置其为紫色字体、黑色背景。然后,它通过一个无限循环保持屏幕显示状态。其余的部分主要是为了确保程序的长度为 512 字节,以符合标准的主引导记录格式。

编译:

nasm E:\OS\echoStr.asm -o E:\OS\echoStr.bin
dd if=echoStr.bin of=E:\\OS\\dingst.vhd bs=512 count=1

 

运行DreamsOS,可以看到出现了紫色的”D”了。

 

同理,输出更多的字符

mov ax,0xb800
mov es,ax

mov byte [es:0x00],'D'
mov byte [es:0x01],0x05
mov byte [es:0x02],'r'
mov byte [es:0x03],0x07
mov byte [es:0x04],'e'
mov byte [es:0x05],0x02
mov byte [es:0x06],'a'
mov byte [es:0x07],0x03
mov byte [es:0x08],'m'
mov byte [es:0x09],0x04

jmp $
times 510 - ($-$$) db 0
dw 0xaa55

编译后

 

5.参考

郑钢著操作系统真象还原

田宇著一个64位操作系统的设计与实现

丁渊著ORANGE’S:一个操作系统的实现

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇