<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-Hans-CN">
	<id>http://wiki.foofun.cn//index.php?action=history&amp;feed=atom&amp;title=VGA_Fonts</id>
	<title>VGA Fonts - 版本历史</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.foofun.cn//index.php?action=history&amp;feed=atom&amp;title=VGA_Fonts"/>
	<link rel="alternate" type="text/html" href="http://wiki.foofun.cn//index.php?title=VGA_Fonts&amp;action=history"/>
	<updated>2026-04-04T15:27:03Z</updated>
	<subtitle>本wiki上该页面的版本历史</subtitle>
	<generator>MediaWiki 1.37.1</generator>
	<entry>
		<id>http://wiki.foofun.cn//index.php?title=VGA_Fonts&amp;diff=993&amp;oldid=prev</id>
		<title>Zhang3：创建页面，内容为“{{TutorialTone}} 你已知道如何在文本模式下显示字符，现在你想要在图形模式下执行此操作。（译者注：内核中文本模式的显示方式是使用BIOS软中断INT指令，调用BIOS功能） 这并不复杂，但绝对比在内存中的特定偏移量下编写ASCII代码更复杂。 你必须逐像素地绘制。  但是你怎么知道该画什么呢？ 它存储在称为位图字体（bitmap fonts）的数据矩阵中。  ==…”</title>
		<link rel="alternate" type="text/html" href="http://wiki.foofun.cn//index.php?title=VGA_Fonts&amp;diff=993&amp;oldid=prev"/>
		<updated>2022-03-23T08:06:00Z</updated>

		<summary type="html">&lt;p&gt;创建页面，内容为“{{TutorialTone}} 你已知道如何在文本模式下显示字符，现在你想要在图形模式下执行此操作。（译者注：内核中文本模式的显示方式是使用BIOS软中断INT指令，调用BIOS功能） 这并不复杂，但绝对比在内存中的特定偏移量下编写ASCII代码更复杂。 你必须逐像素地绘制。  但是你怎么知道该画什么呢？ 它存储在称为位图字体（bitmap fonts）的数据矩阵中。  ==…”&lt;/p&gt;
&lt;p&gt;&lt;b&gt;新页面&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{TutorialTone}}&lt;br /&gt;
你已知道如何在文本模式下显示字符，现在你想要在图形模式下执行此操作。（译者注：内核中文本模式的显示方式是使用BIOS软中断INT指令，调用BIOS功能） 这并不复杂，但绝对比在内存中的特定偏移量下编写ASCII代码更复杂。 你必须逐像素地绘制。&lt;br /&gt;
&lt;br /&gt;
但是你怎么知道该画什么呢？ 它存储在称为位图字体（bitmap fonts）的数据矩阵中。&lt;br /&gt;
&lt;br /&gt;
== 位图字体的解码 ==&lt;br /&gt;
一个字符是如何存储在内存中的？ 很简单，0编码代表背景，1编码代表前景色。 VGA字体总是8位宽，因此每个字节只包含一行。&lt;br /&gt;
对于典型8x16字体的字母‘A’，它将是(二进制，共16字节)：&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
00000000b  byte  0&lt;br /&gt;
00000000b  byte  1&lt;br /&gt;
00000000b  byte  2&lt;br /&gt;
00010000b  byte  3&lt;br /&gt;
00111000b  byte  4&lt;br /&gt;
01101100b  byte  5&lt;br /&gt;
11000110b  byte  6&lt;br /&gt;
11000110b  byte  7&lt;br /&gt;
11111110b  byte  8&lt;br /&gt;
11000110b  byte  9&lt;br /&gt;
11000110b  byte 10&lt;br /&gt;
11000110b  byte 11&lt;br /&gt;
11000110b  byte 12&lt;br /&gt;
00000000b  byte 13&lt;br /&gt;
00000000b  byte 14&lt;br /&gt;
00000000b  byte 15&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
完整的位图包含每个ASCII码字符的位图，因此它是256*16字节，4096字节长。 如果你想得到一个特定字符的位图，你必须将ASCII码乘以16（字符中的行数），再加上位图的偏移量，你就可以开始了。&lt;br /&gt;
&lt;br /&gt;
存储这些内容的一个非常简单的文件格式是Linux控制台使用的[[PC Screen Font]]。 它以上面描述的方式存储字体，并带有一个小标头。 另一个解决方案是[[Scalable Screen Font|可缩放屏幕字体]]格式，它附带一个非常小的免费呈现ANSI C库。&lt;br /&gt;
&lt;br /&gt;
==如何获取字体？==&lt;br /&gt;
有几种方法。 你可以将其保存在文件系统中的文件中。 你可以在一个数组中对其进行硬编码。 但有时4k占用内存太多了，读取文件不是一个可行的选项（尤其在引导加载程序中），在这种情况下，你必须从VGA RAM中读取设备卡使用的字符（原用于显示文本模式）。&lt;br /&gt;
=== 将其存储在数组中 ===&lt;br /&gt;
最简单的方法，但会增加你的代码4k大小。 有几个源代码以二进制或源文件格式提供整个字体，因此您无需手动将其写出。&lt;br /&gt;
=== 将其存储在文件中 ===&lt;br /&gt;
最模块化的方式。 如果你愿意，你可以使用不同的字体。 缺点你需要一个有效的文件系统实现。 至于文件格式，我建议使用前面提到的[[PC Screen Font|PC屏幕字体]]（.psfu）或[[Scalable Screen Font|可缩放屏幕字体]]（.sfn）。&lt;br /&gt;
&lt;br /&gt;
=== 获取存储在VGA BIOS中的副本 ===&lt;br /&gt;
这是一个标准的BIOS调用 (不需要检查它的持久性)。 如果你仍然处于实模式，它很容易使用。&lt;br /&gt;
&amp;lt;source lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
		;输入: es:di=4k缓冲区&lt;br /&gt;
		;输出: 用字体填充的缓冲区&lt;br /&gt;
		push			ds&lt;br /&gt;
		push			es&lt;br /&gt;
		;要求BIOS返回VGA位图字体&lt;br /&gt;
		mov			ax, 1130h&lt;br /&gt;
		mov			bh, 6&lt;br /&gt;
		int			10h&lt;br /&gt;
		;复制字符映射&lt;br /&gt;
		push			es&lt;br /&gt;
		pop			ds&lt;br /&gt;
		pop			es&lt;br /&gt;
		mov			si, bp&lt;br /&gt;
		mov			cx, 256*16/4&lt;br /&gt;
		rep			movsd&lt;br /&gt;
		pop			ds&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 直接从VGA RAM获取 ===&lt;br /&gt;
也许你已经处于保护模式，因此无法访问BIOS功能。 在这种情况下，你仍然可以通过编程VGA寄存器获得位图。 请注意，VGA始终以8x32字体保留空间，因此你需要在复制过程中修剪每个字符的底部16个字节：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;asm&amp;quot;&amp;gt;&lt;br /&gt;
		; 输入: edi = 4k缓冲区&lt;br /&gt;
		; 输出：用字体填充的缓冲区&lt;br /&gt;
		;清除偶数/奇数模式&lt;br /&gt;
		mov			dx, 03ceh&lt;br /&gt;
		mov			ax, 5&lt;br /&gt;
		out			dx, ax&lt;br /&gt;
		; 将VGA内存映射到0A0000h&lt;br /&gt;
		mov			ax, 0406h&lt;br /&gt;
		out			dx, ax&lt;br /&gt;
		; 设置bitplane 2&lt;br /&gt;
		mov			dx, 03c4h&lt;br /&gt;
		mov			ax, 0402h&lt;br /&gt;
		out			dx, ax&lt;br /&gt;
		; 清除偶数/奇数模式（另一种方式，不要问为什么）&lt;br /&gt;
		mov			ax, 0604h&lt;br /&gt;
		out			dx, ax&lt;br /&gt;
		; 复制charmap&lt;br /&gt;
		mov			esi, 0A0000h&lt;br /&gt;
		mov			ecx, 256&lt;br /&gt;
		; 将16个字节复制到位图&lt;br /&gt;
@@:		movsd&lt;br /&gt;
		movsd&lt;br /&gt;
		movsd&lt;br /&gt;
		movsd&lt;br /&gt;
		;再跳过16个字节&lt;br /&gt;
		add			esi, 16&lt;br /&gt;
		loop			@b&lt;br /&gt;
		; 将VGA状态恢复为正常运行&lt;br /&gt;
		mov			ax, 0302h&lt;br /&gt;
		out			dx, ax&lt;br /&gt;
		mov			ax, 0204h&lt;br /&gt;
		out			dx, ax&lt;br /&gt;
		mov			dx, 03ceh&lt;br /&gt;
		mov			ax, 1005h&lt;br /&gt;
		out			dx, ax&lt;br /&gt;
		mov			ax, 0E06h&lt;br /&gt;
		out			dx, ax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
值得一提的是，切换到VBE图形模式'''之前'''必须先完成，因为VGA寄存器之后通常无法访问。 这意味着你将无法将VGA卡的字体内存映射到屏幕内存，并且只能读取到垃圾文件。&lt;br /&gt;
&lt;br /&gt;
== 设置VGA字体 ==&lt;br /&gt;
如果你仍处于文本模式，并且希望VGA卡绘制不同的字形，还可以设置VGA字体。 在图形模式下它毫无价值（因为字符是通过代码显示的，而不是卡显示的），我写这一部分只是为了描述完整。 如果你仔细阅读了到目前为止所写的内容，那么修改VGA RAM中的字体位图并不困难。 我会把它作为作业留给你。（译者注：注意以下内容仍属于这个大纲内，作者还是做了一些提示。）&lt;br /&gt;
&lt;br /&gt;
=== 通过BIOS设置字体 ===&lt;br /&gt;
提示：检查Ralph Brown中断列表Int 10/AX = 1110h。&lt;br /&gt;
&lt;br /&gt;
=== 直接设置字体 ===&lt;br /&gt;
提示：使用与上面相同的代码，但将源代码和目标代码交换为“movsd”。&lt;br /&gt;
&lt;br /&gt;
== 显示字符 ==&lt;br /&gt;
最后，我们到了可以显示字符的地步。 我想你已经准备好了。&lt;br /&gt;
我们必须绘制8x16像素，位图中的每一位都需要一个像素。&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// 这是您加载的位图字体&lt;br /&gt;
unsigned char *font;&lt;br /&gt;
&lt;br /&gt;
void drawchar(unsigned char c, int x, int y, int fgcolor, int bgcolor)&lt;br /&gt;
{&lt;br /&gt;
	int cx,cy;&lt;br /&gt;
	int mask[8]={1,2,4,8,16,32,64,128};&lt;br /&gt;
	unsigned char *gylph=font+(int)c*16;&lt;br /&gt;
&lt;br /&gt;
	for(cy=0;cy&amp;lt;16;cy++){&lt;br /&gt;
		for(cx=0;cx&amp;lt;8;cx++){&lt;br /&gt;
			putpixel(glyph[cy]&amp;amp;mask[cx]?fgcolor:bgcolor,x+cx,y+cy-12);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
参数很简单。 你可能想知道为什么要从y中减去12。 它用于对齐基线（baseline）：认为你指定的y坐标作为字符的底部，未将向下的字形中的“猪尾（piggy tail）”计算在内 (例如在 “p”，“g”，“q” 等中)。 换言之，就是字母“A”的最底有已设置1的bit位的那行。&lt;br /&gt;
&lt;br /&gt;
虽然直接擦除字形下的屏幕像素也够用，但在某些情况下可能不好(例如：在闪亮的渐变按钮上写字)。 这是一个稍微修改的版本，考虑了透明背景。&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// 这是您加载的位图字体&lt;br /&gt;
unsigned char *font;&lt;br /&gt;
&lt;br /&gt;
void drawchar_transparent(unsigned char c, int x, int y, int fgcolor)&lt;br /&gt;
{&lt;br /&gt;
	int cx,cy;&lt;br /&gt;
	int mask[8]={1,2,4,8,16,32,64,128};&lt;br /&gt;
	unsigned char *gylph=font+(int)c*16;&lt;br /&gt;
&lt;br /&gt;
	for(cy=0;cy&amp;lt;16;cy++){&lt;br /&gt;
		for(cx=0;cx&amp;lt;8;cx++){&lt;br /&gt;
			if(glyph[cy]&amp;amp;mask[cx]) putpixel(fgcolor,x+cx,y+cy-12);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
正如你所见，我们这次只有前景色，putpixel调用有一个条件：仅当位图中的相应位被设置时调用。&lt;br /&gt;
&lt;br /&gt;
当然，上面的代码会非常慢(主要是因为一次只处理一个像素，并且反复重新计算“putPixel()”函数中每个像素的地址)。 为了获得更好的性能，上面的代码可以优化为使用布尔运算和 “掩码查找表”。 例如（对于8-bpp模式）：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// 这是您加载的位图字体&lt;br /&gt;
unsigned char *font;&lt;br /&gt;
&lt;br /&gt;
void drawchar_8BPP(unsigned char c, int x, int y, int fgcolor, int bgcolor)&lt;br /&gt;
{&lt;br /&gt;
	void *dest;&lt;br /&gt;
	uint32_t *dest32;&lt;br /&gt;
	unsigned char *src;&lt;br /&gt;
	int row;&lt;br /&gt;
	uint32_t fgcolor32;&lt;br /&gt;
	uint32_t bgcolor32;&lt;br /&gt;
&lt;br /&gt;
	fgcolor32 = fgcolor | (fgcolor &amp;lt;&amp;lt; 8) | (fgcolor &amp;lt;&amp;lt; 16) | (fgcolor &amp;lt;&amp;lt; 24);&lt;br /&gt;
	bgcolor32 = bgcolor | (bgcolor &amp;lt;&amp;lt; 8) | (bgcolor &amp;lt;&amp;lt; 16) | (bgcolor &amp;lt;&amp;lt; 24);&lt;br /&gt;
	src = font + c * 16;&lt;br /&gt;
	dest = videoBuffer + y * bytes_per_line + x;&lt;br /&gt;
	for(row = 0; row &amp;lt; 16; row++) {&lt;br /&gt;
		if(*src != 0) {&lt;br /&gt;
			mask_low = mask_table[*src][0];&lt;br /&gt;
			mask_high = mask_table[*src][1];&lt;br /&gt;
			dest32 = dest;&lt;br /&gt;
			dest32[0] = (bgcolor32 &amp;amp; ~mask_low) | (fgcolor32 &amp;amp; mask_low);&lt;br /&gt;
			dest32[1] = (bgcolor32 &amp;amp; ~mask_high) | (fgcolor32 &amp;amp; mask_high);&lt;br /&gt;
		}&lt;br /&gt;
		src++;&lt;br /&gt;
		dest += bytes_per_line;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void drawchar_transparent_8BPP(unsigned char c, int x, int y, int fgcolor)&lt;br /&gt;
{&lt;br /&gt;
	void *dest;&lt;br /&gt;
	uint32_t *dest32;&lt;br /&gt;
	unsigned char *src;&lt;br /&gt;
	int row;&lt;br /&gt;
	uint32_t fgcolor32;&lt;br /&gt;
&lt;br /&gt;
	fgcolor32 = fgcolor | (fgcolor &amp;lt;&amp;lt; 8) | (fgcolor &amp;lt;&amp;lt; 16) | (fgcolor &amp;lt;&amp;lt; 24);&lt;br /&gt;
	src = font + c * 16;&lt;br /&gt;
	dest = videoBuffer + y * bytes_per_line + x;&lt;br /&gt;
	for(row = 0; row &amp;lt; 16; row++) {&lt;br /&gt;
		if(*src != 0) {&lt;br /&gt;
			mask_low = mask_table[*src][0];&lt;br /&gt;
			mask_high = mask_table[*src][1];&lt;br /&gt;
			dest32 = dest;&lt;br /&gt;
			dest32[0] = (dest[0] &amp;amp; ~mask_low) | (fgcolor32 &amp;amp; mask_low);&lt;br /&gt;
			dest32[1] = (dest[1] &amp;amp; ~mask_high) | (fgcolor32 &amp;amp; mask_high);&lt;br /&gt;
		}&lt;br /&gt;
		src++;&lt;br /&gt;
		dest += bytes_per_line;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
在这种情况下，显示内存（display memory）中的地址只计算一次(而不是最多128次)，并且并行计算8个像素(这完全消除了内部循环)。&lt;br /&gt;
&lt;br /&gt;
这种方法的主要缺点是，你需要为每个 “每像素位” 使用不同的功能，除了15-bpp和16-bpp可以使用相同的代码。 在最坏的情况下（32-bpp），查找表的成本为8kib。 用于32-bpp的查找表可以重新用于24-bpp，而对于4-BPP根本不需要查找表。 为了支持VBE能够实现的所有标准位深度; 给出了每个 “绘制字符” 功能的总共5个版本 (4-bpp，8-bpp，15-bpp和16-bpp，24-bpp，32-bpp) 和3个查找表 (8-bpp，15-bpp和16-bpp，24-bpp和32-bpp)，如果使用静态表，则总共花费14 KiB的数据 （不在需要时动态生成所需的查找表的话）。&lt;br /&gt;
&lt;br /&gt;
==另见==&lt;br /&gt;
* [[VGA Hardware]] - 如果你想自己实现&lt;br /&gt;
* [[PC Screen Font]] - wiki中有一个关于如何显示它们的教程&lt;br /&gt;
* [[Scalable Screen Font]] - 附带一个小的、免费的ANSI C渲染库&lt;br /&gt;
* [[TrueType Fonts]] - 一种非常复杂的矢量字体格式，部分专有的&lt;br /&gt;
&lt;br /&gt;
==外部链接==&lt;br /&gt;
* [http://www.inp.nsk.su./~bolkhov/files/fonts/univga/ UNI-VGA] - 免费的Unicode VGA字体(.bdf)&lt;br /&gt;
* [http://sourceforge.net/projects/bdf2c/ bdf2c] - .bdf字体到C源代码转换器。&lt;br /&gt;
&lt;br /&gt;
[[Category:VGA]] [[Category:Video]]&lt;/div&gt;</summary>
		<author><name>Zhang3</name></author>
	</entry>
</feed>