Jim Hall 发布的文章

我写了一个名为 “Toy CPU” 的教育性复古计算机程序,以便我的学生能够学习机器语言。

我兼职教授大学课程,包括一个对所有专业开放的一般计算机主题的课程。这是一门入门课程,向学生讲授技术是如何运作的,以消除围绕计算的神秘感。

虽然不是计算机科学课程,但这门课的一个部分涉及计算机编程。我通常用非常抽象的术语谈论编程,所以不会让听众听不懂。但是今年,我想让我的学生以 “老派” 的方式做一些需要 “动手” 的编程。同时,我又想保持简单,以便让每个人都能跟上。

我喜欢将我的课程结构化,以显示你是如何从 “那里” 到 “这里” 的。理想情况下,我会让我的学生学习如何编写一个简单的程序。然后,我将从这里开始,展示现代编程是如何让开发人员创建更复杂的程序的。我决定尝试一种非常规的方法 —— 教学生学习终极的低级别编程语言:机器语言。

机器语言编程

早期的个人电脑如 Apple II(1977 年)、TRS-80(1977 年)和 IBM PC(1981 年)让用户用键盘输入程序,并在屏幕上显示结果。但计算机并不总是带有屏幕和键盘。

Altair 8800 和 IMSAI 8080(均为 1975 年制造)要求用户使用面板上的 “开关和灯” 输入程序。你可以用机器语言输入指令,使用一组开关,机器会点亮 LED 灯以代表每个二进制指令的 1 和 0。

Altair 8800 计算机的图片

对这些早期机器进行编程,需要了解被称为 “ 操作码 opcode ” (操作代码的简称)的机器语言指令,以执行基本操作,如将两个数字相加或将一个值存储到计算机的存储器中。我想向我的学生展示程序员是如何通过开关和灯,手工输入一系列指令和内存地址的。

然而,在这门课上,使用实际的 Altair 8800 就有点太复杂了。我需要一些简单的、任何初级水平的学生都能掌握的东西。理想情况下,我希望能找到一个简单的 “业余” 复古计算机,其工作原理与 Altair 8800 相似,但我无法找到一个价格低于 100 美元的合适的 “类似 Altair” 的设备。我找到了几个 “Altair” 软件模拟器,但它们忠实地再现了 Altair 8800 的操作码,这对我的需求来说太过沉重。

我决定编写我自己的 “教育” 复古计算机。我称它为 “Toy CPU”。你可以在我的 GitHub 代码库 上找到它,包括几个可以运行的版本。第一版是一个实验性的原型,运行在 FreeDOS 上。第二版是一个更新的原型,在 Linux 上用 ncurses 运行。版本 3 是一个 FreeDOS 程序,在图形模式下运行。

Toy CPU 的编程

Toy CPU 是一个非常简单的复古计算机。它只有 256 字节的内存和一个最小化的指令集,其目的是在复制 “开关和灯” 编程模式的同时保持简单化。它的界面模仿 Altair 8800,有一系列 8 个 LED 灯,分别代表计数器(程序的 “行号”)、指令、累积器(用于临时数据的内部存储器)和状态。

当你启动 Toy CPU 时,它通过清除内存来模拟 “启动”。当它启动时,它也会在屏幕右下方的状态灯中显示 “INI”(初始化)。“PWR”(电源)灯亮表示 Toy CPU 已被打开。

Toy CPU 的启动屏幕

当 Toy CPU 准备好让你进入一个程序时,它通过状态灯指示 “INP”(“输入”模式),并让你从程序的计数器 0 开始。Toy CPU 的程序总是从计数器 0 开始。

在 “输入” 模式下,用上下方向键显示不同的程序计数器,按回车键编辑当前计数器上的指令。当你进入 “编辑” 模式时,Toy CPU 的状态灯上会显示 “EDT”(“编辑” 模式)。

Toy CPU 编辑屏幕

Toy CPU 有一张速查表,被 “贴” 在显示屏的前面。它列出了 Toy CPU 可以处理的不同操作码。

  • 00000000STOP):停止程序执行。
  • 00000001RIGHT):将累加器中的位向右移动一个位置。值 00000010 变成 0000000100000001 变成 00000000
  • 00000010LEFT):将累加器中的位向左移动一个位置。值 01000000 变成 1000000010000000 变成 00000000
  • 00001111NOT):对累加器进行二进制非操作。例如,值 10001000 变成 01110111
  • 00010001AND):对累加器用存储在某一地址的值进行二进制与操作。该地址被存储在下一个计数器中。
  • 00010010OR):对累积器用存储在某一地址的值进行二进制或运算。
  • 00010011XOR):对累加器用存储在某一地址的值进行二进制异或运算。
  • 00010100LOAD):将一个地址的值加载(复制)到累加器中。
  • 00010101STORE): 存储(复制)累加器中的值到一个地址。
  • 00010110ADD):将存储在某一地址的数值加入到累加器中。
  • 00010111SUB):从累积器中减去储存在某一地址的数值。
  • 00011000GOTO):转到(跳到)一个计数器地址。
  • 00011001IFZERO):如果累加器为零,转到(跳到)一个计数器地址。
  • 10000000NOP):空操作,可以安全地忽略。

当处于 “编辑” 模式时,使用左右方向键选择操作码中的一个位,然后按空格键在关闭(0)和开启(1)之间翻转数值。当你完成编辑后,按回车键回到 “输入” 模式。

Toy CPU 输入模式屏幕

一个示例程序

我想通过输入一个简短的程序来探索 Toy CPU,将两个数值相加,并将结果存储在 Toy CPU 的内存中。实际上,这执行的是算术运算 A+B=C。要创建这个程序,你只需要几个操作码:

  • 00010100LOAD
  • 00010110ADD
  • 00010101STORE
  • 00000000STOP

LOADADDSTORE 指令需要一个内存地址,这个地址总是在下一个计数器的位置。例如,程序的前两条指令是:

计数器 0:00010100
计数器 1:某个内存地址,第一个值 A 存放在那里

计数器 0 中的指令是 LOAD 操作,计数器 1 中的值是你存储某个值的内存地址。这两条指令一起将内存中的数值复制到 Toy CPU 的累加器中,在那里你可以对该数值进行操作。

将一个数字 A 装入累加器后,你需要将数值 B 加到它上面。你可以用这两条指令来做:

计数器 2:00010110
计数器 3:存储第二个值 B 的内存地址

假设你把值 1A)装入累加器,然后把值 3B)加到它上面。现在累加器的值是 4。现在你需要用这两条指令把数值 4 复制到另一个内存地址(C):

计数器 4:00010101
计数器 5:一个内存地址(C),我们可以在那里保存新的值

把这两个值加在一起后,现在可以用这条指令结束程序:

计数器 6: 00000000

计数器 6 之后的任何指令都可以供程序作为存储内存使用。这意味着你可以用计数器 7 的内存来储存值 A,计数器 8 的内存来储存值 B ,计数器 9 的内存来储存值 C。你需要将这些分别输入到 Toy CPU 中:

计数器 7:00000001(1)
计数器 8:00000011(3)
计数器 9:00000000(0,以后会被覆盖)

在弄清了所有指令和 ABC 的内存位置后,现在可以将完整的程序输入到 Toy CPU 中。这个程序将数值 1 和 3 相加,得到 4:

计数器 0:00010100
计数器 1:00000111(7)
计数器 2:00010110
计数器 3:00001000(8)
计数器 4:00010101
计数器 5:00001001(9)
计数器 6:00000000
计数器 7:00000001(1)
计数器 8:00000011(3)
计数器 9:00000000(0,以后会被覆盖)

要运行程序,在 “输入” 模式下按下 R 键。Toy CPU 将在状态灯中显示 “RUN”(“运行” 模式),并从计数器 0 开始执行你的程序。

Toy CPU 有一个明显的延迟,所以你可以看到它执行程序中的每一步。随着程序的进行,你应该看到计数器从 00000000(0)移动到 00000110(6)。在计数器 1 之后,程序从内存位置 7 加载数值 1,累积器更新为 00000001(1)。在计数器 3 之后,程序将加数值 3,并更新累加器显示 00000100(4)。累加器将保持这种状态,直到程序在计数器 5 之后将数值存入内存位置 9,然后在计数器 6 结束。

在运行模式下的 Toy CPU

探索机器语言编程

你可以使用 Toy CPU 来创建其他程序,并进一步探索机器语言编程。通过用机器语言编写这些程序来测试你的创造力。

一个在累积器上闪灯的程序

你能点亮累加器上的右四位,然后是左四位,然后是所有的位吗?你可以用两种方法之一来写这个程序。

一种直接的方法是,从不同的内存地址加载三个数值,像这样:

计数器 0:LOAD
计数器 1:“右边”
计数器 2:LOAD
计数器 3:“左边”
计数器 4:LOAD
计数器 5:“所有”
计数器 6:STOP
计数器 7:00001111(“右边”)
计数器 8:11110000(“左边”)
计数器 9:11111111(“全部”)

写这个程序的另一种方法是尝试使用 NOTOR 二进制操作。这样可以得到一个更小的程序:

计数器 0:LOAD
计数器 1:“右边”
计数器 2:NOT
计数器 3:OR
计数器 4:“右边”
计数器 5:STOP
计数器 6:00001111(“右边”)

从一个数字开始倒数

你可以把 Toy CPU 作为一个倒数计时器。这个程序行使 IFZERO 测试,只有当累加器为零时,程序才会跳转到一个新的计数器:

计数器 0:LOAD
计数器 1:“初始值”
计数器 2:IFZERO(这也是倒计时的“开始”)
计数器 3:“结束”
计数器 4:SUB
计数器 5:“1”
计数器 6:GOTO
计数器 7:“开始”
计数器 8:STOP
计数器 9:00000111(“初始值”)
计数器 10:00000001(“1”)

Toy CPU 是学习机器语言的一个好方法。我在入门课程中使用了 Toy CPU,学生们说他们发现写第一个程序很困难,但写下一个程序就容易多了。学生们还表示,用这种方式编写程序其实很有趣,他们学到了很多关于计算机实际工作的知识。Toy CPU 既具有教育性,也很有趣味性!


via: https://opensource.com/article/23/1/learn-machine-language-retro-computer

作者:Jim Hall 选题:lkxed 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

让你的开源项目文档充满活力,从而吸引各种经验水平的用户。

开源软件项目通常拥有非常多样化的用户人群。有些用户非常擅长使用该系统,并且只需要很少的文档。对于这些实力派用户,文档只需要提供必要的提示,并且可以包含更多的技术信息,比如说在 Shell 中运行的命令行。有些用户可能只是初学者。这些用户需要更多的帮助来设置系统并学习如何使用它。

写一个同时适合这两个用户群体的文档是令人生畏的。网站文档需要在 “提供详细的技术信息” 和 “提供更多的概述和指导” 之间寻求一个平衡。这是一个很难找到的平衡。如果你的文档不能同时满足这两个用户人群,那么考虑一下另外一个选择 —— 动态文档。

探索在网页中添加一点 JavaScript 使用户可以选择自己想看的内容。

构建你的内容

你可以把例程添加的你的文档中需要同时满足 专家 expert 初学者 novice 的地方。在这个例程中,我们可以使用一个虚构的名为 AwesmeProject 的音乐播放器。

你可以用 HTML 编写一个简短的安装文档,通过 HTML 的 class 功能同时为专家和初学者提供操作指南。

例如,你可以用下面的代码来为专家定义一个段落:

<p class="expert reader">

这同时指派了 “专家类” 和 “读者类”。你可以用下面的代码来为初学者创建一个相同的段落。

<p class="novice reader">

完整的 HTML 文件同时包含初学者的段落和专家的段落。

<!DOCTYPE html>

<html lang="en">

<head>

<title>How to install the software</title>
</head>

<body>

<h1>How to install the software</h1>

<p>Thanks for installing AwesomeProject! With AwesomeProject,
you can manage your music collection like a wizard.</p>

<p>But first, we need to install it:</p>

<p class="expert reader">You can install AwesomeProject from
source. Download the tar file, extract it, then run:
<code>./configure ; make ; make install</code></p>

<p class="novice reader">AwesomeProject is available in
most Linux distributions. Check your graphical package manager and search for AwesomeProject to install it.</p>

</body>

</html>

例子中的 HTML 文档没有与之关联的样式表,所以浏览器中会显示所有的段落。

Image of html in black text.

我们可在文档中添加一些简单的样式来为 读者 reader 专家 expert 或者 初学者 novice 突出任何元素。为了使不同的文本更容易区分,让我们把读者类的背景颜色设置成米白色,专家类的字体颜色设置为深红色,初学者的字体颜色则设置为深蓝色。

<!DOCTYPE html>

<html lang="en">

<head>

<title>How to install the software</title>

<style>

.reader {
background-color: ghostwhite;
}

.expert {
color: darkred;
}

.novice {
color: darkblue;
}

</style>

</head>

<body>

<h1>How to install the software</h1>

当你在浏览器中查看这个网页时,这些样式有助于突出这两个段落。安装指导的所有段落都有一个米白色背景,因为他们都有 读者 reader 这个类。第一个段落的字体是深红色的,这是由 专家 expert 这个类定义的。第二个段落的字体是深蓝色的,则是由 初学者 novice 这个类定义的。

Image of html in red and black text.

添加 JavaScript 控件

这些类的应用,使你可以添加一些简单的 JavaScript 函数,只显示其中一个内容块。一个方法是,首先给所有的读者类元素设置 display:none 。这会将内容隐藏,使其不会在页面上显示。然后,用函数将你想显示的类元素设置为 display:block :

<script>
function readerview(audience) {
  var list, item;
  // hide all class="reader"
  list = document.getElementsByClassName("reader");
  for (item = 0; item < list.length; item++) {
    list[item].style.display = "none";
  }
  // show all class=audience
  list = document.getElementsByClassName(audience);
  for (item = 0; item < list.length; item++) {
    list[item].style.display = "block";
  }
}
</script>

要在 HTML 文档中使用这个 JavaScript,你可以吧这个功能附加到一个按钮上。由于 readerview 函数需要一个 听众 audience (这应该是相对那个虚拟音乐播放器来说的)作为参数,你可以使用你想查看的听众类别来调用这个函数,可以是 读者 reader 专家 expert 或者 初学者 novice

<!DOCTYPE html>
<html lang="en">
<head>
<title>How to install the software</title>
  <style>
    .reader {
    background-color: ghostwhite;
    }

    .expert {
    color: darkred;
    }

    .novice {
    color: darkblue;
    }
  </style>
</head>

<body>

<script>
function readerview(audience) {
  var list, item;

  // hide all class="reader"
  list = document.getElementsByClassName("reader");

  for (item = 0; item < list.length; item++) {
    list[item].style.display = "none";
  }

  // show all class=audience
  list = document.getElementsByClassName(audience);

  for (item = 0; item < list.length; item++) {
    list[item].style.display = "block";
  }
}
</script>

<h1>How to install the software</h1>

<nav>

<button onclick="readerview('novice')">view novice text</button>

<button onclick="readerview('expert')">view expert text</button>

</nav>

<p>Thanks for installing AwesomeProject! With AwesomeProject,
you can manage your music collection like a wizard.</p>

<p>But first, we need to install it:</p>
<p class="expert reader">You can install AwesomeProject from
source. Download the tar file, extract it, then run
<code>./configure ; make ; make install</code></p>

<p class="novice reader">AwesomeProject is available in
most Linux distributions. Check your graphical package
manager and search for AwesomeProject to install it.</p>

</body>
</html>

有了这些设置,用户可以在网页上选择他们想看的文本。

Image of window that allows you to select between novice and expert text.

点击任何一个按钮都将只显示用户想要阅读的文本。例如,如果你点击了 “ 阅读初学者内容 view novice text ” 按钮,你就只会看到蓝色段落。

Image showing blue text when you press the novice button.

点击 “ 阅读专家内容 view expert text ” 按钮,就会隐藏初学者文本,只显示红色的专家文本。

Image of red text after the expert button is clicked.

将此扩展到你的文档

如果你的项目需要你为不同的听众编写多个操作文档,你可以考虑使用这种方法,一次发布,多次阅读。为所有的用户编写一个文档,是每个人都能很容易的发现和分享你项目的文档。而你也不必同时维护尽在细节上有所不同的多个文档。


via: https://opensource.com/article/22/12/dynamic-documentation-javascript

作者:Jim Hall 选题:lkxed 译者:duoluoxiaosheng 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

为什么文本只能使用这些有限的颜色显示,为什么 FreeDOS 使用这些颜色和阴影,而不是其他颜色?答案就像技术中的许多事情一样,历史原因。

如果你仔细了解过 FreeDOS,你可能已经注意到文本使用有限的颜色范围 —— 16 种文本颜色和 8 种背景颜色。这类似于 Linux 显示文本颜色的方式 —— 你或许能够在 Linux 终端中更改 文本颜色,但你仍然只能使用 16 种文本颜色和 8 种背景颜色。

 title=

DOS 文本有 16 种颜色和 8 种背景颜色(吉姆·霍尔,CC-BY SA 4.0

为什么文本只能使用这些有限的颜色显示,为什么 FreeDOS 使用这些颜色和阴影,而不是其他颜色?

答案就像技术中的许多事情一样,历史原因。

PC 色彩的由来

为了解释为什么文本只有 16 种颜色,让我给你讲一个关于第一台 IBM 个人计算机的故事。这个故事的部分内容可能有些杜撰,但基本内容已经足够接近。

IBM 于 1981 年发布了 个人计算机 Personal Computer 5150(“IBM PC”)。该 PC 使用了一个简单的监视器屏幕,以绿色显示文本。由于此显示器仅适用于一种颜色,因此被称为 单色 monochrome (“IBM 5151 单色显示器”,搭载 IBM 单色显示适配器 Monochrome Display Adapter ,即 MDA)。

同年,IBM 发布了 PC 的更新版本,带来了惊人的技术成就 —— 颜色!新的 IBM 5153 彩色显示器依赖于新的 IBM 彩色图形适配器 Color Graphics Adapter (CGA)。正是由于这个原始的 CGA,所有的 DOS 文本都继承了它们的颜色。

但在我们讨论那一部分之前,我们首先需要了解一些关于颜色的东西。当我们谈论计算机屏幕上的颜色时,我们谈论的是混合 三原色(红色、绿色和蓝色)的不同值。你可以将不同级别(“亮度”)的红光、绿光和蓝光混合在一起,以创建几乎任何颜色。混合红色和蓝色光,你会得到洋红色。混合蓝色和绿色,你会得到青色或浅绿色。均匀地混合所有颜色,你会得到白色。没有任何浅色,你会看到黑色(没有颜色)。

 title=

混合红色、绿色和蓝色光以获得不同的颜色(吉姆·霍尔,CC-BY SA 4.0

IBM 5153 彩色显示器通过在 阴极射线管 cathode ray tube (CRT)上点亮微小的红色、绿色和蓝色光点来向用户呈现颜色。这些小点排列得非常紧密,并以红色、绿色和蓝色的三色点组成一个“像素”的模式排列。通过控制同时点亮哪些荧光点,IBM 5153 彩色显示器可以显示不同颜色的像素。

 title=

每个红色、绿色和蓝色三元组都是一个像素(吉姆·霍尔,CC-BY SA 4.0

顺便说一句,即使是现代显示器也使用这种红色、绿色和蓝色点的组合来表示颜色。现代计算机的不同之处在于,每个像素都使用红色、绿色和蓝色 LED 灯(通常并排排列),而不是微小的荧光点。计算机可以打开或关闭每个 LED 灯,以混合每个像素中的红色、绿色和蓝色。

 title=

每个红色、绿色和蓝色三元组都是一个像素(吉姆·霍尔,CC-BY SA 4.0

定义 CGA 颜色

IBM 工程师意识到他们可以通过混合红色、绿色和蓝色像素来显示多种颜色。在最简单的情况下,你可以假设单个像素中的每个红色、绿色和蓝色点要么“开”,要么“关”。正如任何计算机程序员都会告诉你的那样,你可以将“开”和“关”表示为二进制 —— 1(1 = 开)和 0(0 = 关)。

用 1 或 0 表示红色、绿色和蓝色意味着你可以组合多达八种颜色,从 000(红色、绿色和蓝色都关闭)到 111(红色、绿色和蓝色都打开)。请注意,位模式类似于“RGB”,因此 RGB=001 是蓝色的(只有蓝色是打开的),RGB=011 是青色的(绿色和蓝色都打开了):

颜色代码名称
 000
 001
 010绿
 011
 100
 101洋红
 110
 111

但这只是最简单的情况。一位特别聪明的 IBM 工程师意识到,只需再添加一点,你就可以将颜色数量从 8 种颜色增加到 16 种。因此,我们可以使用像 iRGB 这样的位模式,而不是像 RGB 这样的位模式。我们将把这个额外的“i”位称为“强度”位,因为如果我们将“强度”位设置为 1(开),那么我们将在全亮度下点亮红色、绿色和蓝色;如果“强度”位为 0(关闭),我们可以使用一些中级亮度。

有了这个简单的修复程序,现在 CGA 可以显示 16 种颜色!为了简单起见,IBM 工程师将高强度颜色称为常规颜色名称的“明亮”版本。因此,“红色”与“亮红色”配对,“洋红色”与“亮洋红色”配对。

颜色代码名称颜色代码名称
 0000 1000亮黑
 0001 1001亮蓝
 0010绿 1010亮绿
 0011 1011亮青
 0100 1100亮红
 0101洋红 1101亮洋红
 0110 1110亮黄
 0111 1111亮白

哦不,等等!这实际上不是十六种颜色。如果你注意到 iRGB=0000(黑色)和 iRGB=1000(亮黑色),它们都是相同的 黑色。没有颜色可以“亮”,所以它们都是普通的黑色。这意味着我们只有 15 种颜色,而不是我们希望的 16 种颜色。

但 IBM 有聪明的工程师为他们工作,他们意识到如何解决这个问题以获得 16 种颜色。IBM 实际上没有实现直接的 RGB 到 iRGB,而是实现了 iRGB 方案。随着这一变化,IBM 为每个光点设置了四个亮度级别:完全关闭、三分之一亮度、三分之二亮度和全亮度。如果“亮度”位被关闭,那么每个红色、绿色和蓝色光点将以三分之二的亮度点亮。如果你打开“亮度”位,RGB 颜色中的所有 0 都将以三分之一的亮度点亮,而所有 1 都将以全亮度点亮。

让我用另一种方式向你描述这一点,使用 Web 颜色代码表示。如果你熟悉 HTML 颜色,你可能知道你可以使用 #RGB 表示颜色,其中 RGB 表示红色、绿色和蓝色值的组合,每个值都在十六进制值 0 到 F 之间。因此,使用 IBM 修改后的 iRGB 定义,iRGB=0001 是 #00a(蓝色),iRGB=1001 是 #55f(亮蓝色),因为对于高亮度颜色,RGB=001 中的所有零点都以三分之一的亮度点亮(0 到 F 刻度上的“5”左右),RGB=001 中的所有零点都以三分之二的亮度点亮(0 到 F刻度上的“A”)。

颜色代码名称颜色代码名称
 0000 1000亮黑
 0001 1001亮蓝
 0010绿 1010亮绿
 0011 1011亮青
 0100 1100亮红
 0101洋红 1101亮洋红
 0110 1110亮黄
 0111 1111亮白

有了这些颜色,我们终于完成了!我们拥有从 iRGB=0000(黑色)到 iRGB=1111(亮白色)以及介于两者之间的所有颜色的全光谱。就像彩虹般的颜色,这很漂亮。

除了……不,等等,这里有问题!我们实际上还不能复制彩虹的所有颜色。我们在小学学到的方便的助记符是 ROYGBIV,它可以帮助我们记住彩虹的颜色有红色、橙色、黄色、绿色、蓝色、靛蓝和紫色。我们修改后的 iRGB 配色方案包括红色、黄色、绿色和蓝色——我们可以将其“伪造”为靛蓝和紫色,但是我们缺少橙色。遭了!

 title=

一条美丽的彩虹——不幸的是它含有橙色。(Paweł Fijałkowski,公共)

为了解决这个问题,聪明的 IBM 工程师对 RGB=110 做了最后的修复。高强度颜色(iRGB=1110)以全亮度点亮红色和绿色荧光粉点以产生黄色,但是在低亮度颜色(iRGB=0110)下,他们以三分之二的亮度点亮红色,以三分之一的亮度点亮绿色。这将 iRGB=0110 变成了橙色——尽管它后来被称为“棕色”,因为 IBM 不得不在某处弄乱标准名称。

颜色代码名称颜色代码名称
 0000 1000亮黑
 0001 1001亮蓝
 0010绿 1010亮绿
 0011 1011亮青
 0100 1100亮红
 0101洋红 1101亮洋红
 0110 1110
 0111 1111亮白

这就是 CGA 以及扩展的 DOS 获得十六种颜色的方式!如果你好奇,这也是为什么会有“亮黑色”的原因,即使它只是一种灰色阴影。

表示颜色(位和字节)

但是你可能想知道:为什么 DOS 可以显示 16 种文本颜色,却只能显示 8 种背景颜色?为此,我们需要快速了解计算机如何将颜色信息传递给 CGA 卡。

简而言之,CGA 卡希望将每个字符的文本颜色和背景颜色编码在一个字节数据包中,一共八位。那么八位是从哪里来的呢?

我们刚刚了解了 iRGB(四位)如何生成十六种颜色。文本颜色使用 iRGB ,四位,背景颜色仅限于八种低强度颜色(RGB,三位),加起来只有七位。丢失的第八位在哪里?

最后一个位可能是为 DOS 时代最重要的用户界面元素保留的 —— 闪烁文本。虽然闪烁的文本在如今可能很烦人,但在整个 1980 年代初期,闪烁的文本是表示错误消息等关键信息的友好方式。

将这个“闪烁”位添加到三个背景颜色位(RGB)和四个文本颜色位(iRGB)中会产生八个位或一个字节!计算机喜欢以完整字节为单位进行计数,这使其成为将颜色(和闪烁)信息传输到计算机的便捷方式。

因此,表示颜色(和闪烁)的完整字节是 Bbbbffff,其中 ffff 是文本颜色的 iRGB 位模式(从 0 到 15),bbb 是低强度的 RGB 位模式背景颜色(从 0 到 7),而 B 是“闪烁”位。

十六种文本颜色和八种背景颜色的限制一直持续到今天。当然,DOS 坚持使用这种颜色组合,但即使是像 GNOME 终端这样的 Linux 终端仿真器也仍然受限于 16 种文本颜色和 8 种背景颜色。当然,Linux 终端可能允许你更改使用的特定颜色,但你仍然限于十六种文本颜色和八种背景颜色。为此,你要感谢 DOS 和最初的 IBM PC。别客气!


via: https://opensource.com/article/21/6/freedos-sixteen-colors

作者:Jim Hall 选题:lujun9972 译者:gpchn 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Bash 有一些方便的自动化功能,可以让我在 Linux 上处理文件时更轻松。

通过 Bash 命令行进行自动化任务是极好的一种方式。不论你使用运行在服务器上的 Linux 进行管理日志文件或其他文件,还是你在个人电脑上整理文件以使桌面保持整洁,使用 Bash 的自动化功能会使你的工作变得更轻松。

自动执行文件任务:for

如果你对一堆文件要同时处理,并且对每个文件进行相同的操作,请使用 for 命令。该命令会遍历文件列表,并执行一个或多个命令。for 命令如下所示:

for 变量 in 列表
do
    命令
done

我在示例中添加了额外的空白和换行,来分开 for 命令中不同的部分。看起来好像无法在命令行中同时运行多个命令,不过你可以使用 ; 将所有命令放在同一行中,就像这样:

for 变量 in 列表 ; do 命令 ; done

让我们看看它的实际效果。我使用 for 命令来重命名一些文件。最近,我有一些截图,想要重命名。这些截图名称为 filemgr.pngterminal.png,我想将 screenshot 放在每个名称前。我可以使用 for 命令一次性将 30 个文件重命名。这是两个文件的示例:

$ ls
filemgr.png  terminal.png
$ for f in *.png ; do mv $f screenshot-$f ; done
$ ls
screenshot-filemgr.png  screenshot-terminal.png

for 命令使得在一系列文件中执行一种或多种操作变得容易。你可以用一些有意义的变量名,比如 imagescreenshot,或者你用示例中“缩写的”变量 f。当我在使用 for 循环写脚本的时候,会选择有意义的变量名。但是当我在命令行中使用 for,我通常会选择缩写变量名,比如 f 代表文件,d 代表目录等。

不论你选择怎样的变量名,请确保在引用变量时添加 $ 符号。这会将变量扩展为你正在处理的文件的名称。在 Bash 提示符下键入 help for 以了解有关 for 命令的更多信息。

按条件执行:if

当你需要对每个文件执行相同操作时,使用 for 循环遍历一些文件很有帮助。但是,如果你需要对某些文件做一些不同的事情怎么办?为此,你需要使用 if 语句进行条件执行。if 语句如下所示:

if 测试
then
    命令
fi

你也可以使用 ifelse 语句进行判断:

if 测试
then
    命令
else
    命令
fi

你可以使用 ifelifelse 语句来实现更复杂的程序。当我一次性需要自动处理很多文件时,我会在脚本中使用:

if 测试1
then
    命令
elif 测试2
then
    命令
elif 测试3
then
    命令
else
    命令
fi

if 命令可以让你进行各种判断,例如判断一个文件是否是一个文件,或者一个文件是否为空文件(零字节)。在命令行中输入 help test,可以立即查看使用 if 语句能够进行的各种测试。

例如,假设我想清理一个包含几十个文件的日志目录。日志管理中的一个常见任务是删除所有空日志文件,并压缩其他日志。解决这个问题的最简单方法是删除空文件。没有可以完全匹配的 if 测试,但是我们有 -s 选项来判断是否是一个文件,并且判断该文件不是空的(大小不为零)。这与我们想要的相反,但我们可以使用 ! 来否定测试,以判断某些内容不是文件或为空。

让我们用一个示例来看看这个过程。我创建了两个测试文件:一个是空的,另一个包含一些数据。我们可以使用 if 判断,如果文件为空打印消息 empty

$ ls
datafile  emptyfile
$ if [ ! -s datafile ] ; then echo "empty" ; fi
$ if [ ! -s emptyfile ] ; then echo "empty" ; fi
empty

我们可以将 iffor 命令结合起来,检查日志文件列表中的空文件并删除:

$ ls -l
total 20
-rw-rw-r--. 1 jhall jhall 2 Jul  1 01:02 log.1
-rw-rw-r--. 1 jhall jhall 2 Jul  2 01:02 log.2
-rw-rw-r--. 1 jhall jhall 2 Jul  3 01:02 log.3
-rw-rw-r--. 1 jhall jhall 0 Jul  4 01:02 log.4
-rw-rw-r--. 1 jhall jhall 2 Jul  5 01:02 log.5
-rw-rw-r--. 1 jhall jhall 0 Jul  6 01:02 log.6
-rw-rw-r--. 1 jhall jhall 2 Jul  7 01:02 log.7
$ for f in log.* ; do if [ ! -s $f ] ; then rm -v $f ; fi ; done
removed 'log.4'
removed 'log.6'
$ ls -l
total 20
-rw-rw-r--. 1 jhall jhall 2 Jul  1 01:02 log.1
-rw-rw-r--. 1 jhall jhall 2 Jul  2 01:02 log.2
-rw-rw-r--. 1 jhall jhall 2 Jul  3 01:02 log.3
-rw-rw-r--. 1 jhall jhall 2 Jul  5 01:02 log.5
-rw-rw-r--. 1 jhall jhall 2 Jul  7 01:02 log.7

使用 if 命令可以在需要时执行一些操作,使脚本变得智能。我经常会在脚本中使用 if,当我需要判断文件在我的系统上存在或不存在时,或者判断脚本正在检查的条目是文件或目录时。使用 if 使得脚本能够根据需要采取不同的操作。


via: https://opensource.com/article/22/7/use-bash-automate-tasks-linux

作者:Jim Hall 选题:lkxed 译者:Donkey-Hao 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

EPUB 文件是使用开放格式发布内容的好方法。

电子书提供了一种随时随地阅读书籍、杂志和其他内容的好方法。读者可以在长途飞行和乘坐火车时享受电子书打发时间。最流行的电子书文件格式是 EPUB 文件,它是“ 电子出版物 electronic publication ”的缩写。 EPUB 文件受到各种电子阅读器的支持,并且是当今电子书出版的有效标准。

EPUB 文件格式基于 XHTML 内容和 XML 元数据的开放标准,包含在 zip 存档中。由于一切都基于开放标准,我们可以使用通用工具来创建或检查 EPUB 文件。让我们探索一个 EPUB 文件以了解更多信息。《C 编程技巧和窍门指南》,于今年早些时候在 Opensource.com 上发布,提供 PDF 或 EPUB 格式。

因为 EPUB 文件是放在 zip 文件中的 XHTML 内容和 XML 元数据,所以你可以用 unzip 命令在命令行检查 EPUB:

$ unzip -l osdc_Jim-Hall_C-Programming-Tips.epub 
Archive: osdc_Jim-Hall_C-Programming-Tips.epub
Length Date Time Name
--------- ---------- ----- ----
20 06-23-2022 00:20 mimetype
8259 06-23-2022 00:20 OEBPS/styles/stylesheet.css
1659 06-23-2022 00:20 OEBPS/toc.xhtml
4460 06-23-2022 00:20 OEBPS/content.opf
44157 06-23-2022 00:20 OEBPS/sections/section0018.xhtml
1242 06-23-2022 00:20 OEBPS/sections/section0002.xhtml
22429 06-23-2022 00:20 OEBPS/sections/section0008.xhtml
[...]
9628 06-23-2022 00:20 OEBPS/sections/section0016.xhtml
748 06-23-2022 00:20 OEBPS/sections/section0001.xhtml
3370 06-23-2022 00:20 OEBPS/toc.ncx
8308 06-23-2022 00:21 OEBPS/images/image0011.png
6598 06-23-2022 00:21 OEBPS/images/image0009.png
[...]
14492 06-23-2022 00:21 OEBPS/images/image0005.png
239 06-23-2022 00:20 META-INF/container.xml
--------- -------
959201 41 files

这个 EPUB 包含很多文件,但其中大部分是内容。要了解 EPUB 文件是如何组合在一起的,请遵循电子书阅读器的流程:

1、电子书阅读器需要验证 EPUB 文件是否真的是 EPUB 文件。他们通过检查 EPUB 存档根目录中的 mimetype 文件来验证文件。该文件仅包含一行描述 EPUB 文件的 MIME 类型:

application/epub+zip

2、为了定位内容,电子书阅读器从 META-INF/container.xml 文件开始。这是一个简短的 XML 文档,指示在哪里可以找到内容。对于此 EPUB 文件,container.xml 文件如下所示:

<?xml version="1.0" encoding="UTF-8"?>
  <container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
    <rootfiles>
      <rootfile full-path="OEBPS/content.opf" media-type="application/oebps-package+xml"/>
  </rootfiles>
</container>

为了使 container.xml 文件更易于阅读,我将单行拆分为多行,并添加了一些间距来缩进每行。XML 文件并不关心新行和空格等额外的空白,因此这种额外的间距不会影响 XML 文件。

3、container.xml 文件表示 EPUB 的根从 OEBPS 目录中的 content.opf 文件开始。OPF 扩展名是因为 EPUB 基于 “ 开放打包格式 Open Packaging Format ”,但 content.opf 文件实际上只是另一个 XML 文件。

4、content.opf 文件包含一个完整的 EPUB 内容清单,以及一个有序的目录,以及查找每一章或每一节的引用。这个 EPUB 的 content.opf 文件很长,因此我将在此仅展示一小部分作为示例。

XML 数据包含在 <package> 块中,该块本身具有 <metadata> 块、<manifest> 数据和包含电子书目录的 <spine> 块:

<?xml version="1.0" encoding="UTF-8"?>
<package unique-identifier="unique-identifier" version="3.0" xmlns="http://www.idpf.org/2007/opf" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:opf="http://www.idpf.org/2007/opf">
  <metadata>
    <dc:identifier id="unique-identifier">osdc002</dc:identifier>
    <dc:title>Tips and Tricks for C Programming</dc:title>
    <dc:creator>Jim Hall</dc:creator>
    <dc:language>English</dc:language>
    <meta property="dcterms:modified">2022-06-23T12:09:13Z</meta>
    <meta content="LibreOffice/7.3.0.3$Linux_X86_64 LibreOffice_project/0f246aa12d0eee4a0f7adcefbf7c878fc2238db3 (libepubgen/0.1.1)" name="generator"/>
  </metadata>
  <manifest>
    ...
    <item href="sections/section0001.xhtml" id="section0001" media-type="application/xhtml+xml"/>
    <item href="images/image0003.png" id="image0003" media-type="image/png"/>
    <item href="styles/stylesheet.css" id="stylesheet.css" media-type="text/css"/>
    <item href="toc.ncx" id="toc.ncx" media-type="application/x-dtbncx+xml"/>
    ...
  </manifest>
  <spine toc="toc.ncx">
    <itemref idref="section0001"/>
    <itemref idref="section0002"/>
    <itemref idref="section0003"/>
    ...
  </spine>
</package>

你可以把数据匹配起来,看看在哪里可以找到每个部分。EPUB 阅读器就是这样做的。例如,目录中的第一项引用了 section0001,它在清单中被定义为位于 sections/section0001.xhtml 文件中。该文件的名称不需要与 idref 条目相同,但 LibreOffice Writer 的自动程序就是这样创建该文件的。(你可以在元数据中看到,这个 EPUB 是在 Linux 上用 LibreOffice 7.3.0.3 版本创建的,它可以将内容导出为 EPUB 文件。)

EPUB 格式

EPUB 文件是一种使用开放格式发布内容的好方法。EPUB 文件格式是 XML 元数据与 XHTML 内容,包含在一个 zip 文件内。虽然大多数技术作家使用工具来创建 EPUB 文件,因为 EPUB 是基于开放标准,意味着你可以使用其他方式创建自己的 EPUB 文件。


via: https://opensource.com/article/22/8/epub-file

作者:Jim Hall 选题:lkxed 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

使用 LaTeX 标记语言来撰写文档。

LaTeX 文件准备系统有一段有趣的历史。在 1968 年,程序员 Don Knuth 用一种老式印刷排版方式,撰写了他的第一本书《 计算机程序设计艺术 The Art of Computer Programming 》。当他在 1976 年出版第二版时,出版商已经转向现代照相排版技术。

Knuth 对新版本的外观不满意。他从程序员的角度解决问题,决定创建他自己的文字处理系统,这样以后他出版的书就可以以相同格式排版,拥有相同的外观。因此,Don Knuth 在 1978 年编写了第一版 TeX 。

几年后,Leslie Lamport 创建了一组宏定义,以便作者更容易编写复杂文档。Lamport 的宏定义扩展,即 LaTeX,有效地扩展了 TeX 能够轻松创建各种文档。例如,许多学术组织使用 LaTeX 出版期刊和论文集。

使用 LaTeX 编写文档

通过写一些短文就可以很容易掌握 LaTeX 基础。让我们从 Opensource.com 介绍页面借用一下内容,创建一个示例:

$ cat about.tex 
\documentclass{article}
\begin{document}

Opensource.com is a premier, daily publication focused on
open source and Linux tutorials, stories, and resources.

We're a diverse and inviting group, made up of staff
editors, Correspondents, contributors, and readers. We
value differences in skills, talents, backgrounds, and
experiences. There are a few different ways to get involved
as a reader or a writer.

\end{document}

类似其他文档格式程序, LaTeX 会将单词汇集起来,填充成段落 。这意味着你可以在段落中间添加新文本,而不用担心最终文档的段落参差不齐。只要你不在段落中添加空行, LaTeX 就会创建完全对齐的段落。当它找到一个空行时, LaTeX 会开启一个新段落。

LaTeX 需要一些定义文档的控制语句。任何 LaTeX 文档应当以“文档类别”声明开始。LaTeX 支持多种文档,包括书信、书籍和文章。例如,我使用 \documentclass{article} 设置类别为 “文章” 。

使用 \begin{document}\end{document} 声明来定义文本的开始和结束。如果你在 \begin{document} 前添加了文本,那么 LaTeX 会报错。在 \end{document} 之后的文本都会被忽略。

使用 LaTeX 的 latex 命令处理文档:

$ latex about.tex
This is pdfTeX, Version 3.141592653-2.6-1.40.22 (TeX Live 2021) (preloaded format=latex)
 restricted \write18 enabled.
entering extended mode
(./about.tex
LaTeX2e <2020-10-01> patch level 4
(/usr/share/texlive/texmf-dist/tex/latex/base/article.cls
Document Class: article 2020/04/10 v1.4m Standard LaTeX document class
(/usr/share/texlive/texmf-dist/tex/latex/base/size10.clo))
(/usr/share/texlive/texmf-dist/tex/latex/l3backend/l3backend-dvips.def)
No file about.aux.
[1] (./about.aux) )
Output written on about.dvi (1 page, 736 bytes).
Transcript written on about.log.

LaTeX 会输出许多文本,这样你就可以知道它在干什么。若你的文档包含错误, LaTeX 会报错并提示它可以做什么。大多数情况下,你可以在提示后输入 exit 来强制退出 LaTeX 。

如果用 LaTeX 成功生成一个文档,会生成一个带 .dvi 后缀的文件。DVI 表示 “ 设备无关 Device Independent ”,因为你可以使用不同的工具来生成其他格式。例如, dvipdf 程序可以将 DVI 文件转换为 PDF 文件。

$ dvipdf about.dvi

LaTeX output

添加列表

LaTeX 支持两种列表:一种以数字开头的 “枚举” 列表,一种 “逐项” 或 “项目符号” 列表。在第二段后添加一个简短的枚举列表,列出人们可以参与 Opensource.com 的方式:

\begin{enumerate}
\item Be a writer
\item Be a reader
\end{enumerate}

与在文档定义中添加 \begin\end 声明类似,你也需要在列表前后添加 \begin\end 声明。在列表中,每个项目以 \item 命令开始。当你用 LaTeX 处理该文档并转换为 PDF 格式后,你会看到该列表为数字列表:

LaTeX output

你也可以在列表中嵌套列表。这是一个优雅的功能,如果你需要在列表中为每个条目添加选项。例如,你可以为想要在 Opensource.com 中成为作者的人们提供一些不同的资源。嵌入列表使用单独的 \begin\end 声明。为了看起来方便,我在示例中添加了空行,但是 LaTeX 会忽略这些空行:

\begin{enumerate}
\item Be a writer

  \begin{itemize}
  \item Resources for writers
  \item Contributor Club
  \item Correspondent Program
  \end{itemize}

\item Be a reader
\end{enumerate}

作为嵌套列表,新列表嵌入在编号 1 的项目中,因为你在原先的 \item 声明之间添加了列表。你可以通过在 \end{enumerate} 语句前添加新列表,作为编号 2 项目的嵌套列表。

LaTeX output

章节和小节

你可以将冗长文章分成多个章节,这样更易于阅读。使用 \section{...} 语句在大括号内添加章节标题。例如,你可以在文档顶部添加一个标题为 “About Opensource.com” 的新章节:

$ head about.tex 
\documentclass{article}
\begin{document}

\section{About Opensource.com}

Opensource.com is a premier, daily publication focused on
open source and Linux tutorials, stories, and resources.

We're a diverse and inviting group, made up of staff
editors, Correspondents, contributors, and readers. We

article 文档类会在每个主要章节添加编号,并使字体变大来突出显示。

LaTeX output

你可以使用 \subsection{...} 命令来组织文档。就像 \section{...} 命令一样,在大括号中输入副标题名称。

$ head about.tex
\documentclass{article}
\begin{document}

\section{About Opensource.com}

Opensource.com is a premier, daily publication focused on
open source and Linux tutorials, stories, and resources.

\subsection{Welcome to the Opensource.com community}

LaTeX output

标题和作者

用于出版的科学类的文章需要标题、作者以及发表日期。LaTeX 提供了通过插入命令的方式来添加这些信息,然后使用单独的 \maketitle 命令生成文章的标题。

将 “About Us” 作为文章标题,作者为 “Opensource.com Editors”,发表日期为 “July 10, 2022” 。你必须在 \begin{document} 之后,文章内容前插入这些内容。

\title{About Us}
\author{Opensource.com Editors}
\date{July 10, 2022}
\maketitle

当你在生成文档时,LaTeX 会将标题、作者和日期添加到文章的顶部:

LaTeX output

着重强调

科学和其他技术类文章通常会突出术语和短语。 LaTeX 提供了几种可以在技术文档中使用的字体效果,包括强调文本(通常以斜体显示)、粗体文本和 小型大写字母 small caps

将短语“staff editors, Correspondents, contributors, and readers”放在斜体文本中,并将特定词“reader”和“writer”放在段落后面的强调文本中。你也可以将“skills, talents, backgrounds, and experiences”加粗。虽然这不是正确的样式设置方式,但你可以使用小型大写字母来键入 “Linux” 。

$ head -20 about.tex 
\documentclass{article}
\begin{document}

\title{About Us}
\author{Opensource.com Editors}
\date{July 10, 2022}
\maketitle

\section{About Opensource.com}

Opensource.com is a premier, daily publication focused on
open source and \textsc{Linux} tutorials, stories, and resources.

\subsection{Welcome to the Opensource.com community}

We're a diverse and inviting group, made up of \textit{staff
editors, Correspondents, contributors, and readers}. We
value differences in \textbf{skills, talents, backgrounds, and
experiences}. There are a few different ways to get involved
as a \emph{reader} or a \emph{writer}.

该示例展示了不同样式的文本的应用方法。当你需要强调时,使用 \emph{...} 命令,将强调主题放在大括号内。要以斜体、粗体或小型大写字母显示文本,使用 \text 命令的变体:\textit{...} 用于斜体,\textbf{...} 用于粗体,以及 \ textsc{...} 用于小型大写字母。LaTeX 支持许多其他方式来设置文本样式,这些样式有助于你编写科学技术类文章。

LaTeX output

使用 LaTeX

我只是介绍了使用 LaTeX 撰写科学技术文章的几种方式。你也可以在 LaTeX 中添加脚注,进行数学公式和方程的排版,取决于你的需求。你也可以通过阅读 Opensource.com 中的文章 《在 LaTeX 中创建文档的介绍》 ,了解使用 LaTeX 撰写科学技术文章的其他方式。


via: https://opensource.com/article/22/8/pdf-latex

作者:Jim Hall 选题:lkxed 译者:Donkey 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出