本节我们将了解计算机的外设之一:显示器的底层工作原理。通过本节,你会知道电脑显示器是如何实时展示我们在计算机上的操作的,比如显示出一张“E”的字符。最后总结了计算机编程的本质,就是人们是通过设计,让字节代表不同的含义而已。
显示器是如何工作的?
电视和电脑显示屏的工作方式相同,它们之间的主要区别只是显示的内容。显示器不能称为计算机技术,并且计算机并不是一定需要显示器的,但是大多数计算机都有一个显示器,并且计算机花费了大量时间来让屏幕展示它做的事情。
电视是同时播放图片和声音的东西,其实图片和声音是分开的,在本节中,我们只关心图片的工作原理。首先要知道的是,虽然这张照片看起来是在移动,但实际上它是一系列静止的照片,呈现得如此迅速,以至于眼睛根本没有注意到。当你看电影时候,就是在播放一系列图片。要看电影,你把电影胶片放在放映机里,放映机把光照进一张照片,然后移到下一张胶片上,把光照进去,等等循环。它通常以每秒24张照片的速度运行,速度足够快,让人产生一种不断移动的画面的错觉。
电视播放速度稍快,大约每秒30张,但电影和电视之间还有另一个更大的区别。在电影胶片中,每一张静止图像都会同时显示出来。每一张图片都是完整的,当你把光照过它时,图片的每一部分都会同时出现在屏幕上。电视无法做到这一点。它没有一张完整的画面可以一次全部放在屏幕上。
电视在一瞬间所能做的就是点亮屏幕上的一个点,然后快速地点亮下一个点,再快速地点亮另一点,直到点亮了一整张照片的点。整个屏幕上的点组成了一张静止图片,因此它必须在三十分之一秒内点亮所有的点,然后在下一张图片上重复一遍,直到它在一秒内在屏幕上放置了30张图片上的点。因此,电视机很忙,每秒钟点亮一个点,是屏幕上点的30倍。
通常,首先点亮左上角的点,然后点亮其右侧的点,以此类推,穿过屏幕的顶部到达右上角。然后它从第二行点开始,再次穿过屏幕,第三行等等,直到它扫描了整个屏幕。每个点的亮度是高还是低,这样屏幕上的每个部分都会被点亮到合适的亮度,以使屏幕看起来像预期的图像。
在任何时刻,电视只处理屏幕上的一个单独的点。所以对于电视来说,有两种错觉——一种是来自一系列静止图像的运动错觉,另一种是一次画一个点的完整静止图像错觉。这种第二种幻觉是由屏幕的组成来辅助的,每个点只点亮了一小段时间,然后立即开始消失。幸运地,无论屏幕是由什么材料制成的发光点,在点被点亮的一次和同一点再次点亮的1/30秒之间,都会继续发光到一定程度。在肉眼看来,你只是看到了一张运动的图片,但有很多事情会让它看起来像这样。
在计算机中,屏幕上的单个点被称为“图片元素”或简称“像素”。电脑屏幕就像电视一样工作。他们还必须每秒扫描整个屏幕30次,以点亮每个像素,从而显示图像。即使屏幕内容没有改变,计算机中的某些东西也必须每秒在屏幕上扫描30次不变的图像。没有扫描,就没有图片——这就是它的工作方式。
我们不会像CPU和RAM那样详细讨论,这两个是计算机的组成部分,但如果我们想知道我们的计算机如何在屏幕上显示我们可以阅读的内容,我们需要了解它的工作原理。接下来我们介绍最简单的屏幕类型,即黑白屏幕,其像素只能完全打开或完全关闭。这种类型的屏幕可以显示由白描制成的字符和图片类型。
显示器的工作主要有三部分。首先是电脑,我们已经看到了它的工作原理。它有一个I/O总线,可以在计算机外部的东西之间移动字节。其次是屏幕。屏幕只是一个大的像素网格,每个像素都可以选择,一次一个,在选择时,可以打开,也可以不打开。第三项是“显示适配器”显示器适配器一端连接到I/O总线,另一端连接到屏幕。
显示适配器的核心是一些RAM。显示适配器需要自己的RAM,这样它可以“记住”哪些像素应该打开,哪些像素应该关闭。在我们将在这里描述的屏幕类型中,屏幕上的每个像素需要有一位RAM。
为了使屏幕每像素每秒扫描30次,显示适配器需要自己的时钟,时钟以屏幕上像素数的30倍的速度滴答作响。在时钟的每一个滴答声中,选择一个像素,并由RAM中的相应位开启或不开启。
例如,让我们使用早期时候的屏幕。这是一个黑白屏幕,屏幕上显示320像素,屏幕下显示200像素。屏幕上有64000个像素。屏幕上的每个像素都有一个由两个数字组成的唯一地址,第一个是左右或水平位置,另一个是上下或垂直位置。左上像素的地址为0,0,右下像素的地址是319,199。64000像素乘以每秒30张图片意味着此显示适配器的时钟需要每秒滴答192000次。由于一个字节中有8位,我们需要8000字节的显示RAM来告诉64000个屏幕像素中的每一个是打开还是关闭。
显示适配器具有设置当前像素水平位置的寄存器。显示适配器在时钟的每一个滴答声都向该寄存器加1。它从零开始,当其中的数字达到319时,下一步将其重置为零。所以它一次又一次地从0变为319。还有一个寄存器,用于设置当前像素的垂直位置。每次水平寄存器复位为零时,显示适配器都会向垂直寄存器加1。当垂直寄存器达到199时,下一步将其重置为零。因此,当水平寄存器从0变到319 的200次时,垂直寄存器从0到199一次。
当前选择的屏幕像素由这些寄存器控制,因此当水平寄存器从0变为319时,当前像素在屏幕上移动一次。然后垂直寄存器加上一个,当前像素向下移动到下一行的第一个像素。因此,时钟和水平和垂直寄存器选择屏幕上的每个像素,一次一个,在一行中从左到右,然后向下选择下一行中的每个像素、然后选择下一个等等,直到屏幕上的每一个像素都被选择了一次。然后一切又开始了。
同时,还有另一个寄存器包含显示RAM地址。一个字节有八个bit,所以能表示八个像素点。每次发送一个字节,就有八个连续的像素发送到屏幕,以打开或关闭它们。在每八个像素之后,RAM地址寄存器加1。当所有的像素都被步进时,整个RAM也被步进,并且绘制了一张完整的图片。当水平和垂直寄存器都达到最大值并复位为零时,RAM地址也复位为零。
显示适配器将大部分时间用于绘制屏幕。它唯一需要做的另一件事是接受来自I/O总线的命令,该命令将改变显示适配器RAM的内容。当CPU中运行的程序需要更改屏幕上的内容时,它将使用I/O OUT命令选择显示适配器,然后发送显示适配器RAM地址,然后发送一字节数据存储在该地址。然后,随着适配器继续重新绘制屏幕,新数据将出现在屏幕上的适当位置。
显示适配器RAM的构建与我们计算机中的RAM不同。它将输入和输出功能分开。所有存储位置的输入连接到输入总线,所有存储位置输出连接到输出总线,但输入总线和输出总线保持分离。然后有两个独立的存储器地址寄存器,一个用于输入,一个用作输出。输入MAR有一个网格,它只选择哪个字节将被“设置”,而输出MAR有单独的网格,它仅选择哪个字节被“启用”。通过此设置,仅使用输出MAR和”enable”位即可连续扫描屏幕和显示RAM。当I/O总线用于写入显示RAM时,它仅使用输入MAR和“set”位。
这就是显示适配器在屏幕上创建图像的方式。由于它的工作方式,在显示RAM中的哪些位与屏幕上的哪些像素对应之间有一种有趣的关系。当它扫描顶行的前八个像素时,它使用RAM的字节0的各个位来打开或关闭像素。当它扫描第二个八个像素,它使用其RAM的字节1的各个bit等等。一共需要40个字节的RAM来绘制第一行,因此最后八个像素(编号为312到319)来自RAM字节39。第二行使用字节40绘制其前8个像素,以此类推。
如果你想在屏幕上写字母和数字,你怎么做?如果你把“A”的ASCII码放入显示RAM中的一个字节中,你会得到一行八个像素,其中一个关闭,然后一个打开,然后五个关闭,最后一个打开。但这并不是“A”在显示器应该有的样子。
显示器是如何展示字符的?
当你想打印或显示书面语言时,你需要把ASCII码翻译成人类可读的东西。我们有一个代码0100 0101,它出现在ASCII代码表的字母“E”旁边,但是计算机如何将0100 0101变成可读的“E”呢?
我们有一个显示屏,但屏幕只是一个像素网格,到目前为止,我们所描述的任何东西都没有人类可读的“E”。为了在屏幕上显示“E”,必须有某种东西使其形状成为字母表中的字母。
因此,我们需要另一个代码。这个代码实际上是关于由点组成的小图片。对于我们希望能够在屏幕上绘制的每个角色,我们需要一张该角色的小照片。如果你选择一个8像素宽、8像素高的网格,你可以决定要在哪个像素上绘制一张看起来像你想在屏幕上绘制的角色的小图片,如下所示
如果你把这张图片转换成开和关,你可以把它存储在八个字节中。如果你想在屏幕上显示100个不同的字符,那么你需要100个像这样的小图片,并且需要800字节的RAM来存储。我们的小计算机只有256字节的RAM,所以该如何解决呢?这800字节是一种称为“font”的代码类型。
如果你想让一个字符出现在屏幕上的某个位置,你需要从字体中选择正确的小图片,然后使用I/O指令将图片的八个字节复制到显示适配器RAM中的正确字节。
如果字体中的图片与ASCII代码表的排列顺序相同,那么我们可以使用ASCII代码的数值来查找字体中的相应图片。
“E”的ASCII代码是0100 0101。如果您将二进制数字代码应用于相同的1和0模式,您将得到十进制数字69。那么,“E”是ASCII中的第69个代码,而“E”图片将是字体中的第六十九个图片。由于每张图片中有八个字节,你将69乘以8,这告诉你“E”的图片将是从地址552开始的八个字节。
现在我们需要知道将这些字节复制到显示RAM中的何处。假设我们想在屏幕的左上角显示一个“E”。打开我们感兴趣的像素的比特在哪里?好吧,第一行很简单,它是显示RAM的前八位,地址0。所以我们使用一系列OUT指令将RAM地址552复制到显示RAM地址0。现在,显示RAM中的第二行在哪里?显示器在向下移动到第二行之前绘制顶行的所有320位。这意味着它在每行上使用40字节,所以顶行使用字节0-39。这意味着RAM地址553处的“E”图片的第二个字节需要写入显示RAM的地址40。类似地,第三到第八个字节被写入字节80、120、160、200、240和280。当你完成所有这些之后,你会在屏幕上看到一个完整的“E”。如果你想在“E”旁边的屏幕上写一个“X”,你可以找到“X”字体中的八个字节,并将它们复制到显示RAM的字节1、41、81、121、161、201、241和281中。如果你的屏幕上需要27个“E”,你只需将字体中的一个“E’复制到显示内存中的27个不同位置即可。
当然,为了让一个字母出现在屏幕上,这似乎需要做很多工作。执行此操作的程序需要一个指令循环,计算第一个“from”和“to”地址,然后发出适当的OUT指令,将第一个字节复制到显示RAM。然后循环将重复,每次更新两个地址,直到所有八个字节都复制到适当的位置。我们不打算写这个程序,但是它很容易是一个50指令的程序,在完成之前必须循环8次。这意味着仅仅在屏幕上显示一个字符就需要400个指令周期!如果在屏幕上绘制1000个字符,可能需要400000个指令周期。另一方面,这仍然只是这台计算机在一秒钟内所能完成任务的四分之一。
这只是为了告诉你为什么计算机需要这么快。他们所做的每件事都很小,要完成任何事情都需要大量的步骤。
关于计算机编码的最后几句话
我们在电脑中看到了几种代码。每一个都是为特定目的而设计的。单个编码消息以字节为单位,四处移动并用于完成任务。
字节不“知道”哪个代码被用来选择它们包含的模式。字节本身没有任何内容可以告诉您它应该是哪个代码。计算机的某些部分在构建时利用了各种代码。在ALU中,加法器和比较器被构造成将字节视为包含用二进制数字编码的值。存储器地址寄存器和指令地址寄存器也是如此。
指令寄存器被构建为将其内容视为包含用指令代码编码的值。显示适配器的RAM位只是单个像素的开或关。图片和字体是字节串,当它被组织起来,亮度通过显示适配器和屏幕的连线来设置时,这些字节串将产生一个人可以识别的东西。
ASCII代码表不会出现在计算机内的任何位置,因为除了使用代码之外,无法表示字母表中的字母。ASCII在字符和字符代码之间转换的唯一位置是外围设备。当你在键盘上按“E”时,你会得到“E”的ASCII码当您将“E”的ASCII码发送到打印机时,它会打印字母“E”。制造这些外围设备的人面前有一个ASCII代码表,当他们制造键盘时,第二排第四个按钮下的开关(上面印有字母“E”)连接到适当的总线上,以产生ASCII代码表格上字母“E“旁边的代码。
“E”是人们在书写口语过程中用来表示声音和单词的字母表中的第五个字母。电脑里只有键盘上的E和屏幕上的E。所有以字节为单位的“E”只是出现在ASCII代码表中“E”旁边的代码。它们不是“E”,没有办法在计算机中输入“E”。即使你把一张“E”的图片放在电脑里,在屏幕上显示之前,它实际上也不是“E”。这时,一个人可以看着它说“这是E”。
字节是不会发声的,它们只包含开关模式。如果一个字节是0100 0101,并且您将其发送到打印机,它将打印字母“E”;如果将其发送到指令寄存器,计算机将执行跳转指令;如果您将其发送到内存地址寄存器,它将选择RAM的字节号69;如果你把它发送到加法器的一侧,它将把加法器另一侧的任何内容加69;如果您将其发送到显示屏,它将设置为打开三个像素,关闭五个像素。
计算机的每一部分设计时都有一个代码,但一旦构建完成,就不用再想这个代码是什么了,它只是做了它设计的事情。计算机中可以发明和使用的代码没有限制。程序员总是发明新代码。就像前面提到的快餐店的收银台一样,机器里的某个地方的bit表示 “包括薯条”的意思。