VGA Hardware
即使VGA已经过时,但许多现代显卡都与之兼容,包括NVidia和ATI卡。 这可以让编写VGA驱动程序变得相当有吸引力。 但是,兼容性的大小各不相同,因此在没有适当的硬件检测的情况下,千万不要假设一个设备卡是兼容的。 除了真实的机器,还有几个模拟器和虚拟机提供VGA仿真: Bochs、QEMU和Microsoft Virtual PC等等。 2011年后,显卡制造商开始放弃VGA兼容性,转而使用UEFI下的GOP(VirtualBox UEFI和TianoCore都支持这一点)。
警告:不正确地更改 CRTC 或硬件设置可能对视频卡和连接的显示器有害。 配置图形硬件可能是一个值得一做的过程,许多人已经成功实现了他们的目标,甚至篡改了他们远远超出他们的设备原始规格。但是仍然要注意,有“好几个”已知的显示器实例——CRT 和 LCD 等——在输入不良数据后会烧坏。同样,视频卡也可能缺乏防止意外配置错误的类似保护措施。 免责声明:所提供的信息可能不准确,使用这些信息完全由你自己承担风险。在 OSDev.org 的整个历史中没有报告过任何事故,但万一你破坏了某些东西,我们 不负责任。 |
概述
虽然与现代视频设备相比,VGA芯片是一个相当简单的硬件,但它可能还有有一点编程复杂度的设备之一, 特别是在过去,你知道如何使用这种特殊的设备就足以建立相当大的声誉。 虽然目前是传统设备,但它是开始练习视频驱动程序技能的好地方。 虽然一个完整的VGA驱动程序可能会让USB控制器看起来微不足道,但幸运的是,有许多捷径可供选择。
未涵盖的内容
虽然此页面试图成为VGA可以做什么的完整概述,但它并没有完全涵盖整套图形。 毕竟,视频卡只能将内存中的字节转换为背面连接器上的信号。 比如说,在本wiki中几乎没有涉及要怎么得到那些放入内存中的字节。 - 有绘制像素和设置单个字符的示例,但是您的操作系统要确定图像形成的像素以及标题屏幕中有哪些字符。 在远程端,监视器有自己处理信号的方式。 显卡需要显示器指示的许多设置,并且每种分辨率都有自己的一组设置。 你可以通过 使用一组方程式 找出自己的一组设置, 但您可以跳过这一步,重新使用示例设置中提供的一个示例代替。 CRTC章节对它们进行了详细说明。
入门
网络上有相当一部分模式设置代码可用。 基本步骤包括计算所需的寄存器值,将其写入VGA,然后继续绘图。 你需要:
- Port I/O:VGA需要8位读/写和16位写。
- MMIO: VGA使用未缓存的字节访问0xA0000-0xBFFFF。 在某些情况下,也允许进行较大的写入。
- 每个VGA组件的读取和写入寄存器的操作 - 由于寄存器比端口多,因此您需要一个包装器。
- 包含VGA显示设置的结构体。 要获得想要设置的内容的漂亮列表,可以使用示例寄存器设置部分。 请记住,您可能还需要一种在绘制过程中更改的结构体,例如颜色和偏移量。
- 将该结构体写入设备的函数
- 填写该结构体的函数。您还可以在开始时使用硬编码结构。
硬件组件
VGA可分为几个部分。 在历史上,VGA的前身-EGA-有几个芯片来执行系统中的每个部分。 可以使用I/O总线根据您的喜好配置这些芯片。 在VGA上,这些已经合并到一个芯片中(DAC除外)。
下图显示了哪些单元负责哪些部件:
但是,为了便于编程,此图是一种简化,不应被认为是完全正确的。
VGA寄存器
VGA有很多功能(超过300个!)内部寄存器,同时又只占用了I/O地址空间的一小部分。 为了应对这种情况,许多寄存器都编制了索引。 这意味着您用要写入的寄存器的编号填充一个字段,然后读取或写入另一个字段以获取/设置实际寄存器的值。
所有寄存器均以8位方式访问。 寄存器中未使用的部分应该正式保留,尽管也有很多程序只是将它们设置为零。 但是,并非VGA中存在的所有字段都在本文进行了记录,因此您可以查找其他参考资料,也可以保留未记录的字段。
在下面的文档中,提供了一个端口号,可能还有一个索引。 端口通常是索引寄存器的基端口,或者是单个寄存器的实际端口。
请注意,PCI板*不*在其配置空间中报告VGA地址,并且无法重新映射这些地址。 因此,无法在VGA模式下同时正确操作两张卡。
端口0x3C0
此端口是一个令人困惑的端口: 您会将索引和数据字节都写入同一端口。 VGA会记录下一次写入应该是索引还是数据字节。 然而,它的初始状态是未知的。 通过从端口0x3DA读取,它将进入索引状态。 要读取内容,请将索引输入端口0x3C0,然后从0x3C1读取值 (然后先读取0x3DA,因为没有定义VGA下一个期望的是数据字节还是索引字节)。
端口0x3C2
这是杂项输出寄存器。 它使用端口0x3C2进行写入,使用0x3CC进行读取。 此寄存器的位0控制其他几个寄存器的位置: 如果清除0,端口0x3D4映射到0x3B4,端口0x3DA映射到0x3BA。 为了可读性,以下只列出第一个端口,并且假定设置了位0。
端口0x3C4、0x3CE、0x3D4
这些是最常用的索引寄存器。 将索引字节写入给定的端口,然后可以从端口+1读取/写入数据字节。 一些程序使用单个16位访问而不是两个字节访问进行写入,实际上行为也是这样做的。 (这样做时要注意字节排序)
端口0x3D4有一些额外的要求 - 在响应此地址之前,需要设置杂项输出寄存器的位0 (如果清除0,这些端口将出现在0x3B4)。 此外,0x3D4的寄存器0-7由保护位(索引0x11的位7)进行写保护
端口0x3C6
端口0x3C6仅包含DAC掩码寄存器,可以通过在此端口上进行简单的读/写操作轻松访问。 在正常情况下,它应该包含0xff。
端口0x3C8
端口0x3C8、0x3C9和0x3C7控制DAC。 DAC中的每个寄存器由18位组成,每个颜色分量6位。 要写入颜色,请将颜色索引写入端口0x3C8,然后按红色、绿色、蓝色顺序将3个字节写入0x3C9。 如果要编写多个连续的DAC条目,则只需将第一个条目的索引写入0x3C8,然后将所有值按红色,绿色,蓝色,红色,绿色,蓝色等顺序写入0x3C9。 被访问的DAC条目将在每写入三个字节后自动递增。 要读取DAC条目,请将要读取的索引写入0x3C7,然后以类似的方式从端口0x3C9读取字节 (与写入一样,每读取三个字节后,索引将递增)
视频内存布局
显存由四个“planes”(单个单元)内存组成,每个内存的大小为64KB,为VGA提供256K的显存。 与之相连的是序列发生器(Sequencer),它解释了该存储器以生成将其馈送到后续阶段的颜色。 颜色在内存中的组织方式主要取决于颜色深度(color depth,颜色值的字节数)。
有关如何从主机访问内存的具体详细信息可以通过阅读图形控制器找到,有关渲染视频内存的详细信息可以通过阅读Sequencer相关信息找到。
16色图形模式下的内存布局
16种颜色表示每种颜色使用4位。 VGA有四个Plane,对于每个像素,每个Plane包含绘制的每个像素的一位。 由于每个像素的信息分散在四个存储位置上,因此这是最困难的存储模型。
每个地址的第7位包含关于第一个像素的信息,第6位包含关于下一个像素的信息,依此类推。
Plane 0包含所有像素的第一位,Plane 1包含第二位,依此类推。
示例:
one byte | ||||||||
---|---|---|---|---|---|---|---|---|
Bits | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Plane 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 |
Plane 1 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 |
Plane 2 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 |
Plane 3 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 |
Colors displayed | 0000 (0) | 1100 (12) | 0110 (6) | 1010 (10) | 0011 (3) | 1111 (15) | 0101 (5) | 1001 (9) |
Plane Write Enable寄存器用于选择要写入的Plane,然后通过访问内存中相应的地址来写入内存。
256色图形模式下的内存布局
在这种模式下,视频存储器的每个字节恰好描述了一个像素。 像素是通过在线性模式下增加地址生成的,所有颜色取自Plane 0。 在planar模式(也称为模式X)中,每个地址描述4个连续像素,每个Plane一个像素。 描述第一个像素的Plane 0,下一个像素的Plane 1,依此类推。 从技术上讲,这是经常发生的事情,但标准的320x200x256模式将Plane“链”起来,这样两个最低阶位选择Plane,内存因此看起来是线性的。
在线性模式下,主机存储器中的每个字节对应于显示器上的一个像素,这使得该模式非常易于使用。 模式X要求使用Plane写入使能寄存器来选择要写入的Plane。
文本模式下的内存布局
在文本模式下,屏幕被分割为字符单元格而不是像素。 四个planes中只有三个实际正在使用中。 Plane0包含每个单元格的字符代码,Plane1包含各自的属性。
Plane2包含字体数据。 对于256个可用字符中的每一个,这个Plane有32个字节保留。 每个字节代表每个字符的一个水平横截面。 每组的第一个字节定义顶行,每个下一个字节描述它下面的行。 对于每个设置的位,使用前景色,对于每个清除的位,使用背景色。
虽然每个字符保留32个字节,但根据字符高度,通常只使用16、14或8个字节。
通过写入视频存储器范围,可以从主机访问Plane 0和1。 Plane 0在偶数地址上访问,Plane 1在奇数地址上访问,每个连续的16位值描述下一个字符。 访问Plane2更改字体需要更改寻址逻辑。
4色模式下的内存布局
CGA仅限于4种并发颜色,每种颜色有2位。 EGA通过增加一对额外的Plane来增加两个额外的位,从原来的每像素两个Plane增加到当前的四个Plane。 如果你想要4色模式,那就意味着你不应该使用Plane 2+3。
Todo:为CGA兼容性确定b/w/d、移位模式和奇偶模式 (猜测为字模式、交错移位、奇/偶使能,即除字母数字位外等效于文本模式)
图形控制器
图形控制器(缩写为GC)负责指导对视频内存的读写操作。
存储器由标准VGA上的四个64k Plane组成。 每个读写操作在所有这些Plane内选择一个地址,然后在所有四个Plane上操作。 这意味着对于写入的每一个字节的数据,可能会潜在地改变四个字节的视频存储器。
除了少数标准模式外,各种控制位的实现在实现之间有所不同。 但是,可以通过在要检查的模式下执行写入操作,然后在planar模式下读取来探测确切的细节。
这些模式似乎在我测试过的所有VGA兼容机和仿真器中都是一致的:
- 模式3 (文本模式) (现在,我只能通过保留旧模式的VGA寄存器的状态转储来进行设置)
- 模式11h(Planar 16色模式)
- 模式13h(线性256色模式)
- 模式X (Planar 256颜色模式)
我还没有编写在所有硬件上进入以下标准模式的代码
- 模式04H(4色模式)-未尝试,应通过设置交错移位模式并忽略Plane 2和3来实现。
图形控制器由一些寻址逻辑和一些特定的读/写逻辑组成。
寻址逻辑控制要访问的视频存储器中的地址,以及在某种程度上控制其中的各个页面。
读/写逻辑控制实际读取或写入存储器的哪些Plane,以及这些值如何与CPU发送的值相关。
寻址逻辑
涉及的寄存器:
寄存器名称 | 端口 | 索引 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|
Sequencer Memory Mode Register | 0x3C4 | 0x04 | Chain 4 | Odd/Even Disable |
Chain 4 Bit
Chain-4 位将视频模式的访问从清除时的Planar模式更改为设置时的线性模式。 虽然在一般情况下可以正确模拟此位,但是在不同的实现(尤其是仿真器)中,此位的实际工作方式非常不同,如果您不知道它,可能会产生奇怪的效果。 在所有情况下,如果chain-4与已建立模式常见的其他设置匹配 (即,如果启用chain-4,还应确保其他寄存器与模式13的预期值相匹配)。 下面是Chain-4的各种实现之间的差异列表。 到目前为止,真实的硬件已被证明与官方记录的行为是一致的。
Bochs | Qemu | Virtual PC | ATI Card (X300) | NVidia (GeForce 6 6150) | |
---|---|---|---|---|---|
Chain 4 has effect on writes | Yes | Yes | Yes | Yes | Yes |
Chain 4 has effect on output | Yes | No | No | No | No |
Plane Write Enable takes effect on writes | No | Yes | No | Yes | Yes |
在bochs + VPC以及经过测试的硬件中,该地址会发生Chain4写入:
plane = addr & 0x0003; // lower bits offset = addr & 0xfffb; // rest of the bits. multiples of 4 wont get written
在QEMU中,写入到这里:
plane = addr & 0x0003; // lower bits offset = addr >> 2; // only the first 16k of each page gets written.
请注意,在Bochs中,Chain-4会改变屏幕的物理显示 (相当于doubleword模式通常会做的事情)。 这使得Bochs可能很麻烦,因为您只需要切换该位即可进入模式X,而真正的硬件还需要将双字模式更改为字节模式。
奇数/偶数禁用位
在bochs qemu似乎是一个不在乎的位
VPC会产生某种形式的回声:
plane1 = addr & 0x0001; // lowest bit plane2 = plane1 + 2; // pick the other odd/even plane offset = addr & 0xfffe; // generate the offset write (plane1, offset); // write to the plane write (plane2, offset); // write to the other plane
这与NVIDIA卡匹配,但是我的ATI卡(稍微老一点,我的V2x00板)的行为略有不同(虽然非常接近):
offset = addr & 0xffff; // generate the offset
There are probably some more things involved here, todo
读/写逻辑
读/写逻辑对写/读数据和一组称为锁存器的内部寄存器执行若干操作。 从视频存储器读取数据会将视频存储器发出的值加载到这些锁存器中。 除从主机处理器写入的数据外,写入操作还将锁存器用作其他数据源。
读/写逻辑有几种不同的操作模式。 这些可以通过设置图形模式寄存器来选择。 VGA有四种写入模式和2种读取模式,可以独立设置。 默认情况下,VGA在读模式0和写模式0下工作,所有写入的数据都直接进入内存,从每个Plane读取的数据一起进行OR运算。
涉及的寄存器:
寄存器名称 | 港口 | 索引 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|
Graphics Mode Register | 0x3CE | 0x05 | Read Mode | Write Mode | ||||||
Map Mask Register | 0x3C4 | 0x02 | Memory Plane Write Enable | |||||||
Enable Set/Reset Register | 0x3CE | 0x01 | Enable Set/Reset | |||||||
Set/Reset Register | 0x3CE | 0x00 | Set/Reset Value | |||||||
Data Rotate Register | 0x3CE | 0x03 | Logical Operation | Rotate Count | ||||||
Bit Mask Register | 0x3CE | 0x08 | Bit Mask |
Todo:研究多字节读写对逻辑和锁存操作的影响
锁存器
VGA内部布线中最有趣的部分可能是数据锁存器的存在。 在过去,VGA一次只能接受8位。 锁存器可容纳4x8位,用作VGA读写的临时寄存器。 通过充分利用该寄存器,DOS时代的程序员可以大大超过8位总线能够实现的数据传输速率,而可以在视频卡上使用32位管道。
每当视频内存加载时,锁存器都会被写入。 一个地址被提供给视频存储器,它向锁存器发出4个字节,每个Plane一个字节。 从那里,视频卡确定要发送到CPU的内容。 写入视频存储器时,锁存器将再次使用。 与各种读取和写入模式一起,锁存器允许视频到视频的传输、图案和光栅操作,以及为执行部分写入提供原始数据。
Write Mode 0
写入模式0是标准写入模式。
写入字节时,它遵循以下步骤
- 输入字节按旋转计数中指定的量向右旋转,所有移位的位都送入位7
- 结果字节分布在4个单独的路径上,每个内存Plane一个
- 如果启用设置/重置寄存器中的某个位被清除,则相应的字节保持不变。 否则,如果SET/RESET值中的相应位被清除,则该字节被全0替换;如果该位为1,则被全1替换。
- 结果值和锁存器值传递给ALU
- 根据逻辑运算的值,执行以下操作:
逻辑运算的值! 结果 | |
---|---|
0 (0x00)
设置/重置操作的字节被forwarded | |
1 (0x08) | 两个输入进行AND运算 |
2 (0x10) | 两个输入进行OR运算 |
3 (0x18) | 两个输入进行XOR运算 |
- 检查位掩码寄存器,对于每个设置的位,转发来自ALU的相应位。 如果该位是清除0的,则该位直接从锁存器中取出。
- 内存Plane写入启用字段与地址逻辑的输入进行AND运算。 对于结果中的每个设置位,用结果加载相应的Plane。
Write mode 3
除其他外,写入模式3可以用于具有恒定颜色的透明写入
Todo: 需彻底的测试。这是对现有文件的未经验证的解释
写入字节时:
- 输入字节按旋转计数中指定的数量向右旋转,所有移位的位都输入到位7中
- 结果值与位掩码寄存器进行AND运算,得到要应用的位掩码。
- 每个Plane从设置/复位值寄存器中提取一位,并将其转换为0x00(如果设置1)或0xff(如果清除0)
- 检查计算的位掩码,对于每个设置位,将转发来自set/reset逻辑的相应位。 如果钻头是空的,则直接从锁存器中取出钻头。 结果被发送到内存。
- 最后,将内存Plane写入使能字段和来自地址逻辑的输入行一起确定。 保持不变的位是实际写入的Plane。
TODO:介绍更多写入模式、读取模式
Sequencer
Sequencer负责将视频内存转换为颜色索引。 与图形控制器一样,它具有一些特殊的寻址逻辑,该逻辑旨在以合理的方式在存储器上迭代,以从视频存储器数据中产生图像。
序列器在文本(字母数字)模式或图形模式下工作
Alphanumeric Mode
在字母数字模式下,四个Plane被分配不同的任务。 Plane 0包含字符数据,而Plane 1包含属性数据。 在标准文本模式下,这些Plane被交错插入主机内存。 Plane 2保存字体数据。 在显示文本数据时,定序器加载当前八个像素集合的字符/属性对,之后使用字节0的值在Plane 2中查找相应的字符,并添加字符行以获得所需的字体数据。 然后,它弹出从MSB到LSB的位,在遇到1时生成所选的前景色,在遇到0时生成背景色。
在典型文本模式下,存储的字体不能直接访问,需要读取或写入一些寻址逻辑中的更改。
TODO: 原理图
TODO: 测试
图形模式
在图形模式下,操作可以分为两个步骤: 地址计算和移位逻辑。 sequencer计算一个地址,然后从该地址读出四个Plane,并根据这些值生成8个像素。 移位逻辑有三种操作模式:单色、交错和256色移位。 尽管VGA “支持” 各种颜色深度,但这些基本上是16色模式下的变化。
Shift modes
当通过存储器时,定序器一次从四个Plane中的每个Plane读取4个字节,然后输出8个像素颜色。 VGA具有将此数据分组为像素值的三种不同模式。 设置取决于VGA寄存器中的两位: 256色移位和交错移位。 当两者都关闭时,选择单移位模式,否则使用相应的模式 (256颜色移位模式优先于交错移位模式)
寄存器名称 | 端口 | 索引 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|
图形模式寄存器 | 0x3CE | 0x05 | 256-Color Shift | Interleaved Shift |
- Single shift mode
:此模式用于16种颜色模式。 对于每个像素,从每个Plane弹出一位并放在一起以形成像素的值。 例如16色图形模式下的内存布局
- 交错换档模式
- 此模式使4色模式相对容易: 从Plane 0的最有效侧弹出2位。 Plane 2也是如此,在从Plane 0和2弹出4个像素后,Plane 2成为最高有效位(在四色模式中,这些为零),Plane 1和3也是如此,直到所有8个像素都已生成。
- 256-Color Shift Mode
:此模式导致每次弹出4位。 Plane 0给出前两个像素,Plane 1给出后两个像素,依此类推。 但是,没有定义这种情况发生的顺序。 由于此模式通常仅用于256色模式,其中颜色逻辑将两个4位像素合并在一起以形成一个8位像素,因此不能确定其间的通信。 但是,只能将位移出两个可能的侧面之一,并且可以监督支持两种可能性。 这种方法的另一个问题是,如果没有用户干预或保留列表,就无法检测使用了哪种方法。 无论哪种方式,此方法要么向左(从MSB)移位,要么向右(从LSB)移位。 如果您知道视频卡的顺序,则可以创建线性16位彩色模式。
地址计算
对于每组像素,定序器(sequencer)计算视频存储器中要从中加载数据的地址。 这个地址分三步计算:
首先计算视频存储器的起始地址: TODO
之后,将渲染扫描线。 读取第一个地址,并将内容拆分为像素。 然后,下一组像素的地址会增加1、2或4,直到扫描线完成(结果是每个扫描线都是8个像素宽的倍数)。 增量取决于VGA是工作在“字节模式”、“字模式”还是“双字模式”。 这些可以使用两位来设置 :( 双字模式优先于字节/字模式) TODO:寄存器
扫描线完成后,加载扫描线开始处的原始值,并增加扫描线计数器。 然后,VGA执行以下操作之一:
- 将虚拟宽度添加到偏移量 (并转到内存中的下一个像素数据序列)。 您可以更改虚拟宽度以更改扫描线之间的可用数据量,这对于滚动屏幕非常有用。
- 保持值不变(并以相同方式再次绘制相同的扫描线)。 这是在双重扫描 todo' 时完成的
- 将地址重置为0(并从视频内存中的其他位置开始渲染) 这可用于创建拆分屏幕“”TODO“”
颜色逻辑
该块围绕属性控制器、调色板RAM和DAC展开,它们共同负责从Sequencer生成的索引中生成颜色信号。
TODO
CRT控制器
阴极射线管控制器 (CRTC-Cathode Ray Tube Controller) 是根据DAC产生的数据创建视频信号的单元。 通过编程,你可以控制显示器的分辨率,以及一些硬件覆盖和平移效果。
下图快速概述了CRTC的一般配置方式(带寄存器名称)
分辨率和时间
水平计时基于字符时钟(8或9像素的倍数),垂直计时基于每条扫描线。 由于这些位很容易超过一个字节的255个限制,因此溢出寄存器用于存储高位。
在水平和垂直方向上,该区域可分为四个部分:
- 活动显示:从内存渲染的数据,从人的角度来看,该区域等于分辨率。
- 过扫描: 活动显示器周围的区域。 虽然通常为黑色,但可以通过改变颜色使其可见。 默认情况下,每一端的大小为8像素。*消隐区域:当CRTC禁用颜色输出时。 这是屏幕周围的黑色区域。 通过更改此选项,您可以移动、居中和缩放屏幕。*回溯期:该区域通常不可见。 这只是发送给监视器的一个信号,用于转到下一扫描线或下一帧。 但是,监视器可能会在这段时间中窃取一些空白区域,反之亦然。 因此,两侧的冲裁尺寸不相等。
寄存器
水平计时涉及的寄存器:
寄存器名 | 端口 | 索引 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|
Horizontal Total Register | 0x3D4 | 0x00 | Horizontal Total | |||||||
End Horizontal Display Register | 0x3D4 | 0x01 | Horizontal Display End | |||||||
Start Horizontal Blanking Register | 0x3D4 | 0x02 | Horizontal Blanking Start | |||||||
End Horizontal Blanking Register | 0x3D4 | 0x03 | Horizontal Display Skew | Horizontal Blanking End (bits 0..4) | ||||||
Start Horizontal Retrace Register | 0x3D4 | 0x04 | Horizontal Retrace Start | |||||||
End Horizontal Retrace Register | 0x3D4 | 0x05 | H. Blanking End (bit 5) | Horizontal Retrace End |
涉及垂直计时的寄存器:
寄存器名 | 端口 | 索引 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|
Vertical Total Register | 0x3D4 | 0x06 | Vertical Total (bits 0..7) | |||||||
Overflow Register | 0x3D4 | 0x07 | V.Retr.St. (9) | V.Disp.End (9) | V.Total (9) | V.Blank.St. (8) | V.Retr.St. (8) | V.Disp.End (8) | V.Total (8) | |
Maximum Scan Line Register | 0x3D4 | 0x09 | V.Blank.St. (9) | |||||||
Vertical Retrace Start Register | 0x3D4 | 0x10 | Vertical Retrace Start (bits 0..7) | |||||||
Vertical Retrace End Register | 0x3D4 | 0x11 | Vertical Retrace End | |||||||
Vertical Display End Register | 0x3D4 | 0x12 | Vertical Display End (bits 0..7) | |||||||
Vertical Blanking Start Register | 0x3D4 | 0x15 | Vertical Blanking Start (bits 0..7) | |||||||
Vertical Blanking End Register | 0x3D4 | 0x16 | Vertical Blanking End (bits 0..6) |
处理计时的其他寄存器:
寄存器名 | 端口 | 索引 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|---|---|
Miscellaneous Output Register | 0x3C2 | - | Clock Select | ||||||||
Clocking Mode Register | 0x3C4 | 0x01 | 9/8 Dot Mode |
计时模型
水平定时寄存器基于一个称为“字符”的单元(因为它们在文本模式下匹配一个字符)。 每个字符等于8(设置了“”9/8点模式“”)或9(“”9/8点模式“”是清除)像素。 每条扫描线包含 “'水平总计'” 5个字符,基于零。 “水平显示结束”告诉我们从内存计算的最后一个字符(即字符的水平分辨率减去1)。 ‘水平消隐开始’和‘’水平回溯开始‘给我们的最后一个字符是在这两个阶段开始之前。 “水平消隐结束” 和 “水平回溯结束” 需要更多解释,因为它们仅包含数字的一部分。 当启用消隐或水平回溯时,根据字符计数器检查有效位,如果这些位匹配,相应的周期将结束。 快速解决方案是计算适当的值,计算每个周期应处于活动状态的最后一个字符时钟,然后将其与0x3F(空白)或0x1F(回溯)进行AND运算,以获得寄存器的值。 请注意,周期必须在1到63 (空白)/31 (回溯) 字符时钟之间。 为了安全起见,屏幕两侧必须至少有一个过扫描字符,以避免额外的伪影。
除了这些寄存器在扫描线(像素)而不是字符上操作之外,垂直时序是相似的。 “垂直回扫结束” 和 “垂直空白结束” 寄存器的工作也相似,尽管它们的大小不同。 回扫端是4位宽的(并且具有0xf,周期是1-15扫描线),空白端尺寸至少为7位(有人说它的8,有人说它的7),所以该值是通过与0xFF的AND计算的,周期范围为1-127扫描线。 与水平定时一样,必须存在至少一条过扫描扫描线,以避免可能的伪影。
可以使用 时钟选择 来选择时钟。 所有VGA上只有四个可能的时钟中的两个。 当该字段为零时选择25 MHz的时钟,当该字段等于1时选择28 MHz。 有些板在值2和3下有其他时钟,但是除非你知道那里的时钟,否则你不应该写这些值。 请注意,选择28MHz时钟和每像素9像素的结果与选择25MHz时钟和每字符8像素的结果相同,只是分辨率不同。
刷新率可按如下方式计算:
- 水平刷新率 = 时钟频率 (以Hz为单位)/水平总像素
- 垂直刷新率=水平刷新率/总扫描行数
在VGA监视器上,水平刷新率应等于31.25 kHz。垂直方向上,仅使用400和480像素分辨率。
如果你的显示器支持它,你可以设置几乎任何你想要的疯狂分辨率。 (前提是水平分辨率是8的倍数,或者9-8似乎很常见) 大多数现代显示器允许以这种方式设置400x300到800x600之间的任何值。
样本定时方案
640x480 (16种颜色) 使用以下尺寸:
- 定时:25MHz点时钟,每个字符8像素
- 总计:水平800像素,(100个字符),524条扫描线
- 主动显示: 640x480 (80字符,480扫描线)
- 过扫描:每边8个像素(垂直方向8条扫描线/水平方向1个字符时钟)
- 水平回溯:12个字符(96个像素)
- 垂直回扫: 2条扫描线
- 消隐(左):2个字符(16像素)
- 空白(右):4个字符(32像素)
- 消隐 (顶部): 24条扫描线
- 消隐(底部):2条扫描线
哪些应与VGA兼容
采样寄存器设置
这些是可以加载到VGA以设置标准模式的寄存器值。 请注意,您应该在加载这些寄存器之前解锁CRTC并禁用输出,然后恢复这些寄存器,以使其适用于周围的旧监视器。
更改模式的伪代码大致如下:
DisableDisplay // disable output UnlockCRTC // unlock registers LoadRegisters // load registers ClearScreen // clear the screen contents LoadFonts // and for text mode, load fonts // note that this may need to alter GC settings // so be sure to restore those after that LockCRTC // optional: lock the registers again EnableDisplay // make sure there is output
寄存器设置列表
寄存器名 | 端口 | 索引 | mode 3h (80x25 text mode) | mode 12h (640x480 planar 16 color mode) | mode 13h (320x200 linear 256-color mode) | mode X (320x240 planar 256 color mode) |
---|---|---|---|---|---|---|
Mode Control | 0x3C0 | 0x10 | 0x0C | 0x01 | 0x41 | 0x41 |
Overscan Register | 0x3C0 | 0x11 | 0x00 | 0x00 | 0x00 | 0x00 |
Color Plane Enable | 0x3C0 | 0x12 | 0x0F | 0x0F | 0x0F | 0x0F |
Horizontal Panning | 0x3C0 | 0x13 | 0x08 | 0x00 | 0x00 | 0x00 |
Color Select | 0x3C0 | 0x14 | 0x00 | 0x00 | 0x00 | 0x00 |
Miscellaneous Output Register | 0x3C2 | N/A | 0x67 | 0xE3 | 0x63 | 0xE3 |
Clock Mode Register | 0x3C4 | 0x01 | 0x00 | 0x01 | 0x01 | 0x01 |
Character select | 0x3C4 | 0x03 | 0x00 | 0x00 | 0x00 | 0x00 |
Memory Mode Register | 0x3C4 | 0x04 | 0x07 | 0x02 | 0x0E | 0x06 |
Mode Register | 0x3CE | 0x05 | 0x10 | 0x00 | 0x40 | 0x40 |
Miscellaneous Register | 0x3CE | 0x06 | 0x0E | 0x05 | 0x05 | 0x05 |
Horizontal Total | 0x3D4 | 0x00 | 0x5F | 0x5F | 0x5F | 0x5F |
Horizontal Display Enable End | 0x3D4 | 0x01 | 0x4F | 0x4F | 0x4F | 0x4F |
Horizontal Blank Start | 0x3D4 | 0x02 | 0x50 | 0x50 | 0x50 | 0x50 |
Horizontal Blank End | 0x3D4 | 0x03 | 0x82 | 0x82 | 0x82 | 0x82 |
Horizontal Retrace Start | 0x3D4 | 0x04 | 0x55 | 0x54 | 0x54 | 0x54 |
Horizontal Retrace End | 0x3D4 | 0x05 | 0x81 | 0x80 | 0x80 | 0x80 |
Vertical Total | 0x3D4 | 0x06 | 0xBF | 0x0B | 0xBF | 0x0D |
Overflow Register | 0x3D4 | 0x07 | 0x1F | 0x3E | 0x1F | 0x3E |
Preset row scan | 0x3D4 | 0x08 | 0x00 | 0x00 | 0x00 | 0x00 |
Maximum Scan Line | 0x3D4 | 0x09 | 0x4F | 0x40 | 0x41 | 0x41 |
Vertical Retrace Start | 0x3D4 | 0x10 | 0x9C | 0xEA | 0x9C | 0xEA |
Vertical Retrace End | 0x3D4 | 0x11 | 0x8E | 0x8C | 0x8E | 0xAC |
Vertical Display Enable End | 0x3D4 | 0x12 | 0x8F | 0xDF | 0x8F | 0xDF |
Logical Width | 0x3D4 | 0x13 | 0x28 | 0x28 | 0x28 | 0x28 |
Underline Location | 0x3D4 | 0x14 | 0x1F | 0x00 | 0x40 | 0x00 |
Vertical Blank Start | 0x3D4 | 0x15 | 0x96 | 0xE7 | 0x96 | 0xE7 |
Vertical Blank End | 0x3D4 | 0x16 | 0xB9 | 0x04 | 0xB9 | 0x06 |
Mode Control | 0x3D4 | 0x17 | 0xA3 | 0xE3 | 0xA3 | 0xE3 |
另见
外部链接
- http://tinyvga.com/vga-timing — VGA Signal Timing