<?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=Printing_To_Screen</id>
	<title>Printing To Screen - 版本历史</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.foofun.cn//index.php?action=history&amp;feed=atom&amp;title=Printing_To_Screen"/>
	<link rel="alternate" type="text/html" href="http://wiki.foofun.cn//index.php?title=Printing_To_Screen&amp;action=history"/>
	<updated>2026-04-04T01:41:15Z</updated>
	<subtitle>本wiki上该页面的版本历史</subtitle>
	<generator>MediaWiki 1.37.1</generator>
	<entry>
		<id>http://wiki.foofun.cn//index.php?title=Printing_To_Screen&amp;diff=1028&amp;oldid=prev</id>
		<title>Zhang3：创建页面，内容为“==基础知识==  假设您处于 保护模式 并且不使用 BIOS 将文本写入屏幕，则您将直接写入 “视频” 存储器。  这很容易。 彩色显示器的文本屏幕视频存储器位于&lt;tt&gt;0xB8000&lt;/tt&gt;，单色显示器的文本屏幕视频存储器位于地址&lt;tt&gt;0xB0000&lt;/tt&gt; (有关更多信息，请参见 检测彩色和单色显示器)。  文本模式内存对屏幕…”</title>
		<link rel="alternate" type="text/html" href="http://wiki.foofun.cn//index.php?title=Printing_To_Screen&amp;diff=1028&amp;oldid=prev"/>
		<updated>2022-03-25T02:55:47Z</updated>

		<summary type="html">&lt;p&gt;创建页面，内容为“==基础知识==  假设您处于 &lt;a href=&quot;/index.php?title=Protected_mode&quot; class=&quot;mw-redirect&quot; title=&quot;Protected mode&quot;&gt;保护模式&lt;/a&gt; 并且不使用 &lt;a href=&quot;/index.php?title=BIOS&quot; title=&quot;BIOS&quot;&gt;BIOS&lt;/a&gt; 将文本写入屏幕，则您将直接写入 “视频” 存储器。  这很容易。 彩色显示器的文本屏幕视频存储器位于&amp;lt;tt&amp;gt;0xB8000&amp;lt;/tt&amp;gt;，单色显示器的文本屏幕视频存储器位于地址&amp;lt;tt&amp;gt;0xB0000&amp;lt;/tt&amp;gt; (有关更多信息，请参见 &lt;a href=&quot;/index.php?title=Detecting_Colour_and_Monochrome_Monitors&quot; title=&quot;Detecting Colour and Monochrome Monitors&quot;&gt;检测彩色和单色显示器&lt;/a&gt;)。  文本模式内存对屏幕…”&lt;/p&gt;
&lt;p&gt;&lt;b&gt;新页面&lt;/b&gt;&lt;/p&gt;&lt;div&gt;==基础知识==&lt;br /&gt;
&lt;br /&gt;
假设您处于 [[protected mode|保护模式]] 并且不使用 [[BIOS]] 将文本写入屏幕，则您将直接写入 “视频” 存储器。&lt;br /&gt;
&lt;br /&gt;
这很容易。 彩色显示器的文本屏幕视频存储器位于&amp;lt;tt&amp;gt;0xB8000&amp;lt;/tt&amp;gt;，单色显示器的文本屏幕视频存储器位于地址&amp;lt;tt&amp;gt;0xB0000&amp;lt;/tt&amp;gt; (有关更多信息，请参见 [[Detecting Colour and Monochrome Monitors|检测彩色和单色显示器]])。&lt;br /&gt;
&lt;br /&gt;
文本模式内存对屏幕上的每个“字符”占用两个字节。 一个是“ASCII代码”字节，另一个是“属性”字节。因此，文本“Hello”将存储为：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0x000b8000: 'H', colour_for_H&lt;br /&gt;
0x000b8002: 'e', colour_for_e&lt;br /&gt;
0x000b8004: 'L', colour_for_L&lt;br /&gt;
0x000b8006: 'l', colour_for_l&lt;br /&gt;
0x000b8008: 'o', colour_for_o&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
“属性” 字节以其最低的4位带有 “前景颜色”，以其最高的3位带有 “背景颜色”。 位#7的解释取决于您（或BIOS）如何配置硬件（有关更多信息，请参阅[[VGA Resources|VGA参考资料]]）。&lt;br /&gt;
&lt;br /&gt;
例如，使用&amp;lt;tt&amp;gt;0x00&amp;lt;/tt&amp;gt;作为属性byte意味着黑底黑字(您将什么也看不到)。 &amp;lt;tt&amp;gt;0x07&amp;lt;/tt&amp;gt; 为黑色浅灰色 (DOS默认)，&amp;lt;tt&amp;gt;0x1F&amp;lt;/tt&amp;gt; 为蓝色白色 (Win9x的死亡蓝屏)，&amp;lt;tt&amp;gt;0x2a&amp;lt;/tt&amp;gt; 用于绿色单色怀旧。&lt;br /&gt;
&lt;br /&gt;
对于彩色视频卡，您可以使用32KB的文本视频内存。 由于80x25模式不会使用全部32KB(80x25x2=4,000字节/屏幕)，因此您有8个显示页面可用。&lt;br /&gt;
&lt;br /&gt;
当您打印到0以外的任何其他页面时，它将 “不” 出现在屏幕上，直到该页面 “启用” 或 “复制” 到第0页内存空间中。&lt;br /&gt;
&lt;br /&gt;
====颜色表====&lt;br /&gt;
&lt;br /&gt;
{| {{wikitable}}&lt;br /&gt;
|-&lt;br /&gt;
! 颜色编号&lt;br /&gt;
! 颜色名称&lt;br /&gt;
! RGB值&lt;br /&gt;
! Hex值&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Black&lt;br /&gt;
| 0 0 0&lt;br /&gt;
| 00 00 00&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Blue&lt;br /&gt;
| 0 0 170&lt;br /&gt;
| 00 00 AA&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Green&lt;br /&gt;
| 0 170 0&lt;br /&gt;
| 00 AA 00&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Cyan&lt;br /&gt;
| 0 170 170&lt;br /&gt;
| 00 AA AA&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Red&lt;br /&gt;
| 170 0 0&lt;br /&gt;
| AA 00 00&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Purple&lt;br /&gt;
| 170 0 170&lt;br /&gt;
| AA 00 AA&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Brown&lt;br /&gt;
| 170 85 0&lt;br /&gt;
| AA 55 00&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Gray&lt;br /&gt;
| 170 170 170&lt;br /&gt;
| AA AA AA&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| Dark Gray&lt;br /&gt;
| 85 85 85&lt;br /&gt;
| 55 55 55&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| Light Blue&lt;br /&gt;
| 85 85 255&lt;br /&gt;
| 55 55 FF&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| Light Green&lt;br /&gt;
| 85 255 85&lt;br /&gt;
| 55 FF 55&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| Light Cyan&lt;br /&gt;
| 85 255 255&lt;br /&gt;
| 55 FF FF&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| Light Red&lt;br /&gt;
| 255 85 85&lt;br /&gt;
| FF 55 55&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| Light Purple&lt;br /&gt;
| 255 85 255&lt;br /&gt;
| FF 55 FF&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| Yellow&lt;br /&gt;
| 255 255 85&lt;br /&gt;
| FF FF 55&lt;br /&gt;
|-&lt;br /&gt;
| 15&lt;br /&gt;
| White&lt;br /&gt;
| 255 255 255&lt;br /&gt;
| FF FF FF&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 打印字符串 ==&lt;br /&gt;
&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;
// 屏幕一行&lt;br /&gt;
void write_string( int colour, const char *string )&lt;br /&gt;
{&lt;br /&gt;
    volatile char *video = (volatile char*)0xB8000;&lt;br /&gt;
    while( *string != 0 )&lt;br /&gt;
    {&lt;br /&gt;
        *video++ = *string++;&lt;br /&gt;
        *video++ = colour;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
这只是循环遍历字符串中的每个字符，并将其复制到视频内存中的适当位置。&lt;br /&gt;
&lt;br /&gt;
对于更高级的打印功能，您需要存储x和y的变量，因为显示控制器不会打印换行符。 这涉及一个switch语句或类似的构造。&lt;br /&gt;
您还必须测试x&amp;gt;80或y&amp;gt;25，如果x&amp;gt;80将x设置为0并递增y，或者在y&amp;gt;25滚动的情况下。&lt;br /&gt;
&lt;br /&gt;
==打印整数==&lt;br /&gt;
&lt;br /&gt;
就像在任何其它环境中一样，需求您反复将值除以进制基数（Base，译者注：这里说明了把整数转成字符的算法），除法的其余部分为您提供该值的最低有效数字。&lt;br /&gt;
&lt;br /&gt;
例如，由于1234=4+3*10+2*100+1*1000，如果重复将“1234”除以10，并使用除法的剩余部分，则得到以下数字：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1234 = 123*10 + 4&lt;br /&gt;
123 = 12*10 + 3&lt;br /&gt;
12 = 1*10 + 2&lt;br /&gt;
1 = 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
由于此算法以“错误”的顺序(从最后到第一)检索数字，因此您必须使用递归，或者通过随后工作反转数字序列。 如果您知道 &amp;lt;tt&amp;gt;number % 10&amp;lt;/tt&amp;gt; 的数值，则只需将其添加到字符char '0' 即可具有正确的字符 （例如“0”+4==“4”）&lt;br /&gt;
&lt;br /&gt;
以下是itoa()函数的示例实现(该函数不是标准的，但由许多库提供)：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
char * itoa( int value, char * str, int base )&lt;br /&gt;
{&lt;br /&gt;
    char * rc;&lt;br /&gt;
    char * ptr;&lt;br /&gt;
    char * low;&lt;br /&gt;
    // 检查支持的进制基础。&lt;br /&gt;
    if ( base &amp;lt; 2 || base &amp;gt; 36 )&lt;br /&gt;
    {&lt;br /&gt;
        *str = '\0';&lt;br /&gt;
        return str;&lt;br /&gt;
    }&lt;br /&gt;
    rc = ptr = str;&lt;br /&gt;
    // 为负数设置“-”负号。&lt;br /&gt;
    if ( value &amp;lt; 0 &amp;amp;&amp;amp; base == 10 )&lt;br /&gt;
    {&lt;br /&gt;
        *ptr++ = '-';&lt;br /&gt;
    }&lt;br /&gt;
    // 记住数字从哪里开始。&lt;br /&gt;
    low = ptr;&lt;br /&gt;
    // 实际转换。&lt;br /&gt;
    do&lt;br /&gt;
    {&lt;br /&gt;
        // 对于负值，Modulo为负值。此技巧使abs()变得不必要。&lt;br /&gt;
        *ptr++ = &amp;quot;zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz&amp;quot;[35 + value % base];&lt;br /&gt;
        value /= base;&lt;br /&gt;
    } while ( value );&lt;br /&gt;
    // 终止字符串。&lt;br /&gt;
    *ptr-- = '\0';&lt;br /&gt;
    // 把数字倒过来。&lt;br /&gt;
    while ( low &amp;lt; ptr )&lt;br /&gt;
    {&lt;br /&gt;
        char tmp = *low;&lt;br /&gt;
        *low++ = *ptr;&lt;br /&gt;
        *ptr-- = tmp;&lt;br /&gt;
    }&lt;br /&gt;
    return rc;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
参考[http://www.strudel.org.uk/itoa/，这里是一个较短的版本]&lt;br /&gt;
&lt;br /&gt;
== 故障排除 ==&lt;br /&gt;
&lt;br /&gt;
===不显示任何内容===&lt;br /&gt;
&lt;br /&gt;
请记住，只有当屏幕正确设置为80x25视频模式(即模式03)时，这种写入视频内存的方式才会起作用。 您可以通过手动初始化每个VGA寄存器来执行此操作，也可以在仍处于实模式 (例如，在引导扇区中) 时调用BIOS Int10h的 “设置视频模式” 服务。 大多数BIOS都会为您进行初始化，但其他一些（主要是在笔记本电脑上）不会。 有关详细信息，请查看[[Ralf Brown‘s Interrupt List]]。 还请注意，某些模式被模式列表报告为 “both text &amp;amp; graphic” 的模式实际上是具有BIOS功能的图形模式，当您通过Int10h调用char/message输出时，这些功能会绘制字体 （这意味着一旦进入[[Protected mode|保护模式]]，您将结束当前模式，进入纯图形模式）。&lt;br /&gt;
&lt;br /&gt;
([[GRUB]]为您执行此设置。)&lt;br /&gt;
&lt;br /&gt;
另一个常见的错误，例如在遍布网络的众多教程中，是链接内核/操作系统的.text节到错误的内存地址。 如果还没有完成内存管理功能，请确保在链接器脚本中使用物理内存位置。&lt;br /&gt;
&lt;br /&gt;
===打印字符===&lt;br /&gt;
&lt;br /&gt;
在保护模式下，尝试一个简单的命令，如:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// C&lt;br /&gt;
*((int*)0xb8000)=0x07690748;&lt;br /&gt;
&lt;br /&gt;
// NASM&lt;br /&gt;
mov [0xb8000], 0x07690748&lt;br /&gt;
&lt;br /&gt;
// GAS&lt;br /&gt;
movl $0x07690748,0xb8000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
它应该在你的屏幕上方以灰色和黑色显示“Hi”。 如果这不起作用，请检查您的分页/分段设置是否将假定的视频内存地址正确映射到0xB8000(或0xB0000)。&lt;br /&gt;
&lt;br /&gt;
=== 缺少字符串 ===&lt;br /&gt;
&lt;br /&gt;
有时打印单个字符是可行的，但打印字符串失败。 这通常是因为链接器脚本中缺少&amp;lt;tt&amp;gt;.rodata&amp;lt;/tt&amp;gt;部分。&lt;br /&gt;
&lt;br /&gt;
以前，GCC有一个选项 &amp;lt;tt&amp;gt;-fwritable-strings&amp;lt;/tt&amp;gt;，可以用作解决此问题的方法，但它在3.0版中被弃用，并在4.0版和更高版本中删除，2005年发布。 即使在有选择的情况下，这也是一个难题；真正的解决方案是，现在仍然是，添加&amp;lt;tt&amp;gt;.rodata&amp;lt;/tt&amp;gt;到脚本中。&lt;br /&gt;
&lt;br /&gt;
==另见==&lt;br /&gt;
*[[Printing to the screen without a db]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Tutorials]]&lt;br /&gt;
[[Category:Video]]&lt;br /&gt;
[[Category:Text UI]]&lt;/div&gt;</summary>
		<author><name>Zhang3</name></author>
	</entry>
</feed>