你可以帮助我们扩充关于该主题的更多信息。
本文旨在探讨Minecraft中计算系统的设计和实现。
第一章,建造一台计算机,这是一个关于如何在Minecraft中建造计算机以及如何扩展与改进的详细教程。不需要读者了解大量的计算机科学方面的知识,因为教程会对其进行解释,并且会深入研究。
第二章,红石计算机的规划,提供了在Minecraft中设计与理解一个红石计算机的基本概念。不需要读者了解大量的计算机科学方面的知识,因为教程会对其进行解释,并且会深入研究。
概述[]
计算机促进了人们通过编程与其交流的想法的实现。
这篇文章将会为在Minecraft中设计与建造计算机打下基础,假定读者相当熟悉传统红石电路并且有基本水平的计算机知识。
事实上在不了解计算机是如何工作的情况下是无法建造计算机的。此教程旨在解释你需要知道的所有内容,但也确实需要一点对计算机科学的了解。涉及的最深层次达到IGCSE[注 1] CS(计算机科学)。
为了在Minecraft中建造计算机能有一个良好的开始,你应该学习计算机科学。有非常多的资源与教程可以用来学习计算机科学,但是推荐观看Crash Course Computer Science作为入门课程,尤其是1-8节。尽管它不够透彻,但其内容可以作为你理解计算机的基础。
在Minecraft中,大多数计算机是由红石粉,红石火把以及红石中继器组成的,以引起黏性活塞或者红石灯的变化。它们被一系列的按钮,拉杆,压力板等等所控制。另外一些方法(本文没有涉及)利用了漏斗,矿车或船与红石。
应用[]
计算机与计算器的区别在于计算器在没有用户输入时无法连续执行复杂的指令,而计算机可以连续比较并处理指令来完成任务。计算机可以被用在很多方面,从创造一个智能房屋到运行一张冒险地图。但是,由于Minecraft对于计算机的限制(将会在后文说明),它们仍然只是个抽象概念,但也是理解CPU结构与嵌入式系统的良好工具。
由于Minecraft中的红石计算机非常慢并且臃肿,很难为它们找到实际应用。即使是最快的红石计算机也要花数秒来完成一次计算,还有着数千方块大的体积。因为命令方块的速度快且有着清晰且高级的指令,所以它们相比于红石计算机有很大的优势。
有一些Mod可以改变计算机的速度,比如TickrateChanger能改变游戏刻速率。
计算机的基础功能[]
计算机具有以下能力:
- 从有地址的内存中读写;
- 可以对内存的状态进行比较,并据此执行操作,包含了重写内存;
- 可以基于写在内存中的内容运行一个功能。我们把这样的内容叫作“程序+数据”,并将其编写为程序。
一个值得注意的例子是最基本的计算机概念,即图灵机,它能从一条无穷长的编码和指令中读取信息,以完成一个功能。
在Minecraft中设计和建造图灵机是可以实现的。然而,由于我们要设计更基本的一些东西,所以我们不讨论这个。
计算机的部件[]
一个现代计算机有五个基础部件。为了保证正常运行,并通过执行计算来处理数据,这些部件是必不可少的。
- 运算单元(ALU)(这不是必需的,但通常存在)
- 执行加减乘除(也有的计算机有例如移位的功能);
- 通过逻辑门进行布尔运算。
- 控制单元(CU)
- 执行接收到的指令;
- 控制其他单元。
- 数据存储器(Data Memory)
- 存储与返回数据。
- 指令存储器(Instruction Memory)
- 返回指令,并发送到CU;
- 可以被设置,但不需要向数据存储器那样频繁。
- 在冯诺依曼结构下是不必须的(指令将会和数据存储在一起)
- 输入/输出设备(I/O)
- 允许计算机与玩家和世界交互;
- 可以将信息输入到计算机(按下按钮,或通过阳光探测器);
- 可以从计算机中输出信息(通过红石灯、音符盒等)。
第一章 建造一台计算机[]
介绍与条件[]
红石的逻辑紧密地反映了二进制逻辑,即红石元件可以是激活或非激活的,可以被解释为0或1。在本教程中,我们将提到基础的二进制逻辑与众多简单的计算机科学术语。这里有一篇文章,很好地解释了二进制以及如何转化到二进制。请读计算机的结构一节,因为接下来我们对于计算机的设计是基于此的。
这一章会关注于知识的运用与红石的操作,以来创造一台简单的8位计算机,并且会解释如何建造以及它是如何工作的。
所有的内容被分为了理论与实践两部分。理论部分会深入解释会发生什么,而实践部分会说明如何在Minecraft中建造,它应该是什么样子,可能会提供存档。
- 我们将要建造的计算机(MASIC计算机)
- 步骤1:内存和地址解码器(理论) (未完成)
- 步骤1:内存和地址解码器(实践)
- 步骤2:构建算术逻辑单元(理论)
- 步骤2:构建算术逻辑单元(实践) (未完成)
- 步骤3:指令集和机器结构(理论)
- 步骤3:指令集和机器结构(实践) (未完成)
MASIC计算机[]
我们将在本教程中制作的计算机拥有8比特、16字节的内存。我们将要制作的输入输出系统是一个七段的数字显示器(以显示十六进制数字)和一个控制面板。
MASIC计算机旨在成为一种适合所有人的计算机,并且不专门从事一项任务,因此它可以通过读取其自己的内存进行完全编程(在第7节:指令集中进行了说明)。简单的I/O(输入/输出)非常适合多种用途,并且存储器大小足够。它以相当快的速度运行(由于其体积小)。
步骤1:内存和地址解码器(理论)[]
解码器将二进制的数字转换成十进制。例如,看着8位解码器,00点亮代表0的第一个灯,01点亮代表1的第二个灯,10点亮代表2的第三个灯,11点亮代表3的最后一个灯。
步骤1:内存和地址解码器(实践)[]
地址解码器[]
这是我们要构建的地址解码器的设计。
上面是一个简单的2位状态,因此它有两个输入(通过左右中继器)。输出是在图片最上方的红石线,当所有条件满足时会关闭。状态是红石信号输入是否会关闭上面的红石线; 如果是,则状态为红石输入。在上面例子中,必须是左侧输入为OFF(0),右侧输入为ON(1),才能关闭顶部的红石线。因此,它期望的状态为(OFF,ON)(即二进制01)。
这里,颜色为蓝色的方块应设为ON(1),以便停止激活顶部的红石线。一旦每一位都停止激活红石线,红石线就会关闭。
这些基本上是每条输入用一个或两个非门,多条汇入到一个或门,然后用非门后输出。
上面是一个8位状态,它期望8个输入的顺序正好是0000 1101。红石火把正好按照0000 1101的顺序放置,所以我们看到顶部的红石线熄灭。
现在,如果将它们中的多个放在一起,我们就可以用蓝色位以二进制数进行计数,以获得8位的所有255个状态。下面的一个是8位,并具有四个状态期望。查看右侧的图像以查看实际效果。现在,每个绿色输出可以是一个存储单元,如果我们继续以二进制进行计数,它将计到255。
输入为0000 0011(红石火把为输入),蓝色位与当前状态匹配时,绿色输出为ON。
- 0000 0000 - 第一个信号输出(在右侧的图像上)
- 0000 0001 - 第二个信号输出
- 0000 0010 - 第三个信号输出
- 0000 0011 - 第四个信号输出
因此,现在我们继续以二进制数进行计数,直到数到0000 1111并停止。现在我们应该有24(16)个状态期望值,这就说明我们完成了地址解码器。由于指令集的局限性,我们不会继续计数到1111 1111。
步骤2:构建算术逻辑单元(理论)[]
算术逻辑单元(ALU)会比较并执行二进制数的数学运算,并将结果与控制单元(CU,即计算机的中心组件)进行交互。本来也应与CPU交互,但它将与计算机本身一样大。许多教程都希望读者首先构建ALU,因此该主题在网上被广泛涉及。
我们将要构建的ALU可以在两个输入上执行四个重要操作,并返回正确的输出。A,B均为8位输入。
- A + B(A加上B)
- A >>(右移一位,与二进制除以2相同)
- << A(左移一位,与二进制乘以2相同)
- 非A(将A每位取反)
由于某些程序需要大量操作才能运行,因此计算机内部还可以有多个ALU,这些操作不依赖于先前的操作,因此可以进行线程化。 所以将它们委派给不同的ALU可以显着提高该程序的速度。
两个数字相加[]
在一个加法单元中,对于每个位(对于我们的计算机,我们需要四个,因此需要4位)都要有一个全加器。全加器将接收三个输入,每个输入可以是1或0。前两个将是用户的输入,第三个将是“进位”输入。“进位”输入是上一个全加器的输出,稍后将对此进行说明。全加器将输出两个结果:首先是输出,然后是“进位”输出,该输出作为输入发送到下一个全加器中,占一个位。 例如,希望将数字0101和1011相加。第一个全加器将把第一位的值1和1作为它们的两个输入(我们从右到左阅读)。由于这是第一次加法计算,因此没有“进位”输入。全加器将把1和1相加;它是0,并将进位的1送到下一个全加器的进位输入。下一个全加器将把0和1相加,进位输入为1。因此将0、1和1相加,得0,并且下一个进位输入为1。重新回顾二进制加法,应该能解决疑惑。
为了执行加法运算,所有ALU都需要多个全加器。每两位送入一个全加器,所有全加器互相连接,产生的输出是两个字节的和。每个全加器具有一个输入、一个输出、一个进位输入和一个进位输出,就像人执行9 + 1或01 + 01的加法操作一样。全加器由几个逻辑门构成,这可以通过二进制的命名法实现。 教程/算术逻辑给出了全加器和半加器的非常详细的介绍,目前,有一个构造方法的示意图。它提供四个输入/输出,应与其他加法器连接以创建一个单元。对于此示例,我们将在四位计算机中将四个加法器连接在一起,以便我们可以将所有四个位都取为输出。第一个加法器将缺少输入进位,这是因为之前的位(第一位)没有任何进位。输入进位将保持为零。第四加法器还将缺少输出进位,并且由于我们只能支持四个位,因此将忽略该输出。附加的第四个进位输出连接到溢出标志,表示无法完成该操作。这称为二进制溢出。
因此,基本上,进入Minecraft并构建一个完整的二进制加法器(如图所示)并将其连接起来。应该有八个输入和输出。尝试将拉杆和红石灯分别放在两端以测试。因此0010 + 0011应该算出0101(2 + 3 = 5,我们从右往左读)。
分数数字[]
计算机通过浮点算术的形式处理小数,仅在较大位计算机(16-64位)和需要使用小数的计算机中有用。浮点运算或高精度算法可以实现此目的。另一种更简单但效率较低的方法是为所有数字分配2的一个幂,以使它们和选定的2的幂“相乘”。玩家必须对每个数字都执行此操作,并假设单位1是你选择的2的幂的1倍。例如5 = 1012,因此5 × 23 = 1010002,即5(1012)向左移动了3位(1010002)。因此,现在,新系统中的值为1 × 23 = 10002,这将为0.12、0.012或0.0012留下表示方法;0.012 × 23 = 102。这将导致你的计算机设置更为复杂。
两个数字相减[]
数字减法非常简单。ALU首先必须更改第二个数字(即减数),将其从正数转换为负数。一个数取负,对应的二进制补码是反转原二进制数补码(0变为1,1变为0)后再加1。
示例:10减去9
1. 0000 1001 | (9的二进制补码,我们希望将它转换为-9) |
2. 1111 0110 | (将9取反,所有0变为1,1变为0) |
3. 1111 0111 | (加1,这是-9的二进制补码) |
4. 0000 1010 | (10的二进制补码) |
+ 1111 0111 | (加上-9的二进制补码) |
---- | |
0000 0001 | 结果(10 +(-9)= 1)(存在溢出,这仅意味着结果不是负数) |
这带来了带符号数字的复杂性,但这是二进制数的作用,用来将其指定为正数或负数。结果是负数还是正数由溢出标志确定。如果有溢出,则表示数字为正,否则为负。
为实现这一功能,可以让ALU做3个操作。 实现A减B的操作是:A SUB B
- NOT B (将B的每一位取反)
- B ADD 1 (将B自加1)
- A ADD B (将A加B的值存到A)
- RETURN A (A即为返回值)
两个数字相乘[]
乘法是重复的加法,因此最简单(但效率低下)是将A多次加到变量B上。
这是它的伪机代码:
操作:A * B
- C = 0
- (set C to) C ADD A (将C+A的值存入C)
- (set B to) B SUB 1 (将B自身减1)
- JUMP IF (B > 0) TO LINE 2 (如果B>0,回到第2行继续执行)
- RETURN C (C为返回值)
但是,有更高效的方法做乘法。一个好方法是将第一个数字重复位移到第二个数字中每个1的位置,然后求和。
下标2表示二进制,粗体表示十进制。
操作 | 说明 | ||||||
---|---|---|---|---|---|---|---|
1 | 1 | 代表3,注意有2个1 | |||||
× | 1 | 0 | 1 | 1 | 代表11 | ||
1 | 1 | 将112左移0位,因为二进制数10112的第1位是12 | |||||
+ | 1 | 1 | 0 | 将112左移1位,因为二进制数10112的第2位是12 | |||
+ | 1 | 1 | 0 | 0 | 0 | 将112左移3位,因为二进制数10112的第4位是12 | |
二进制数10112的第3位是02,因此不用加 | |||||||
1 | 0 | 0 | 0 | 0 | 1 | 结果为33 |
因此这对更大的数操作起来更高效。
操作:A * B
- C = 0
- D = 0
- JUMP IF (BIT (D) OF B == 0) TO LINE 5 (如果B的第D+1位是0,则跳到第5行)
- (Set C to) C ADD A (将C+A的值存入C)
- (Set D to) D ADD 1 (将D自加1)
- (Set A to) << A (将A左移1位)
- JUMP IF (D < LENGTH OF B) TO LINE 3 (如果D比B的位数小,即没有超出,则跳到第3行)
- RETURN C (C即为返回值)
不要忘记:
<< A (左移1位)等价于A * 2
>> A (右移1位)等价于A / 2
假如这些数字是唯一确定的或者CPU必须做大量的相似数字处理,可以考虑使用一个可查的穷举表来频繁调用乘法。因此,在极端情况下,你可以通过硬编码的方式来得到答案。
步骤2:构建算术逻辑单元(实践)[]
步骤3:指令集和机器结构(理论)[]
详细说明在第2章:指令集。我们可以创建自己的一套。
对于我们正在建造的MASIC计算机,有一个8位的系统,所以这意味着在堆栈内存的每个插槽上的每条指令都是8位。堆栈内存是存储任何信息并存储在RAM中的内存。所以将会有一个计数器,称为程序计数器,它每周期递增1次。循环是指CPU获取指令,解码指令(找出该指令是做什么的)和执行指令(执行它让它做的事情)。然后,通过递增程序计数器并在堆栈内存中读取位于该位置的信息,移动到下一个内存地址。
因此,堆栈内存中的每个字节都有8位供我们使用。有些指令需要一个地址,比如将内存加载到寄存器中,这样我们就可以对其执行操作。每条指令将被分成两部分,每部分4位。第一种是类型,类型将指定计算机必须做什么;地址将是我们将执行操作的值所在位置。
操作码/操作数
凭借这个4位数据组,我们可以有24种操作码,即代表16种不同的操作。我们的电脑将有两个寄存器,所以因此操作码的一位用于指定操作将在其上执行的寄存器,并在下文用x表示。
指令与内存将被放在同一位置,由于指令的地址部分只有四位,我们只能从1-16行引用内存,需要一些巧妙的编程来适应更大的程序。每个程序的内存也限制为16字节。值和指令本质上是相同的,因此,如果你编写一条指令将其存储到先前存储指令的行上,这将有效地用一个值覆盖该指令。
意外执行值是一个问题,因此必须设定并使用STOP命令来防止任何错误。这需要理解的东西太多了,所以自己去找找基础教程吧。另外,不要忘记为你的IGCSE同时使用客户端/服务器端和信息通信技术。
必要条件[]
本节将介绍计算机中常见的简单主题和组件。 看懂下文你需要:看懂计算机的部件和第2章中的信息及第3章中的概念,如ALU、RAM、寄存器和二进制操作。
MASIC指令集[]
因为这里的计算机是指令集的草稿,只有基本要素。这是基于其他汇编语言的,但经过更改以适应我们的体系结构。有两个寄存器,所以我们需要指令在两个寄存器上执行操作。
二进制码 | 操作码 | 效果注释 |
---|---|---|
0000 | LOAD R1 | 将地址加载到寄存器1中 |
0001 | STORE R1 | 将寄存器1的内容存储到该地址中 |
0010 | JUMP R1 IF | 如果寄存器1的值为0,则跳到地址一行 |
0011 | ADD R1 | 将地址处的内容添加到寄存器1 |
0100 | <<R1 | 在寄存器1中向左移位 |
0101 | NOT R1 | 按位非寄存器1(对寄存器1按位取反) |
0110 | JUMP OPERAND | 跳到第OPERAND行 |
0111 | STOP | 终止程序 |
1000 | LOAD R2 | 将地址加载到寄存器2中 |
1001 | STORE R2 | 将寄存器1的内容存储到该地址中 |
1010 | JUMP R2 IF | 如果寄存器2的值为0,则跳到地址一行 |
1011 | ADD R2 | 将地址处的内容添加到寄存器1 |
1100 | <<R2 | 在寄存器2中向左移位 |
1101 | NOT R2 | 按位非寄存器2(对寄存器2按位取反) |
1110 | OUT R1 | 输出寄存器1 |
1111 | OUT R2 | 输出寄存器2 |
解释:
1000 0011表示将地址3的数加载到R2寄存器,因为1000为加载到寄存器2中,0011为3。
这些可以在一个过程中进行,以便可以执行功能。
编写程序[]
下面这个程序能计算斐波那契数列:(0,1,1,2,3,5,8……)
地址 | 二进制指令 | 实际指令 | 注释 |
---|---|---|---|
0 | 0000 1110 | LOAD R1 14 | 将寄存器1设置为地址14的值 |
1 | 1000 1111 | LOAD R2 15 | 将寄存器2设置为地址15的值 |
2 | 1011 1110 | ADD R2 14 | 将地址14的值加入R2 |
3 | 1110 0000 | OUT R1 | 输出寄存器1 |
4 | 0001 1111 | STORE R1 15 | 将其放入地址15 |
5 | 1111 0000 | OUT R2 | 输出寄存器2 |
6 | 1001 1110 | STORE R1 14 | 将其放入地址14 |
7 | 0110 0000 | JUMP 0 | 回到地址0,重复执行 |
... | |||
14 | 0000 0001 | 1 | |
15 | 0000 0001 | 1 |
前面是低级汇编语言的一个例子。如果它是用高级语言编写的,比如C++,它看起来更像是这样:
#include <iostream>
using namespace std;
int main()
{
int n, t1 = 0, t2 = 1, nextTerm = 0;
cout << "Enter the number of terms: ";
cin >> n;
cout << "Fibonacci Series: ";
for (int i = 1; i <= n; ++i)
{
// Prints the first two terms.
if (i == 1)
{
cout << t1 << " ";
continue;
}
if (i == 2)
{
cout << t2 << " ";
continue;
}
nextTerm = t1 + t2;
t1 = t2;
t2 = nextTerm;
cout << nextTerm << " ";
}
return 0;
}
指令周期[]
指令集是低级汇编语言,所以我们希望将它更多地集成到硬件里。这围绕着“获取→解码→执行”循环(见上)在运行。 在CPU中,有4个重要寄存器:
- 程序计数器(PC)跟踪计算机目前运行到哪里;
- 存储地址寄存器(MAR)跟踪下一个内存地址的位置;
- 存储数据寄存器(MDR)跟踪当前内存在哪一个位置;
- 现行指令寄存器(CIR)跟踪哪条指令正在被执行;
- ALU累加器(ACC)跟踪ALU的输入和输出。
还有四个组件需要记住:地址解码器、内存、指令解码器和ALU。
- 获取(Fetch)
程序会获得下一条指令。
- PC将指令码发送到MAR;
- PC自己增加1,为下一条指令做准备;
- 地址解码器将地址解码,并从内存中获取相应地址中的信息;
- MDR接收到需要的信息。(在这个图片的例子中,如果MAR为0001,MDR会接收到“LOAD R1 1”,即将地址1的数据加载到寄存器R1中)
- 解码(Decode)
程序会识别获取到的是什么指令。
- CIR通过信息流,从MDR中接收到信息;
- 指令解码器将指令解码,知道接下来要做什么。
- 执行(Execute)
程序会执行这条指令。
- 在本图片例子中,程序接收到“LOAD R1 1”这条指令,指令解码器将这条指令拆分为操作码和操作对象。操作码是“LOAD R1”,操作对象是“1”。
- 操作对象被送到MAR,以便获取对应地址的信息。
- MDR接收到那个地址的信息。(在这个例子中,是同一行)
取决于指令,有四种情况可能发生。
- 如果指令是加(ADD),ACC就会被要求从信息流中获取信息,ALU会对其执行操作,再次输出到ACC。
- 如果指令是加载(LOAD),控制单元(CU)会将指令加载到寄存器中。
- 如果指令是存储(STORE),CU会将指定位置的值设置(SET)为内存中MAR指定的值。
- 如果指令是输出(OUT),CU会将指令送到外部输出设备。
- 重复(Repeat)
指令周期会重复进行,直到运行到停止(STOP)指令,或是用完内存。
步骤3:指令集和机器结构(实践)[]
第二章 红石计算机的规划[]
在Minecraft中,为了让你的红石计算机最适合你手头的任务,需要有以下三个主要的设计目标。而有些得失需要进行考虑,比如当计算机的规模更大时,运行就会更慢,因为中继器的数量会随着距离的增加而增加。越多的内存,意味着速度就越慢,体积越大。
- 紧凑程度
这台计算机的规模有多小?在Minecraft中,设计一台生存模式的计算机很大可能会强调这一点。所需的重复次数将随着大小的增加而增加。
- 内存大小
它能容纳多少的内存?它可以计算多少数字和字节?这对于大规模的计算机来说是很重要的,如那些可以完成更复杂算法和更大规模指令集的计算机(例如说,计算平方根或是三角学)内存大小或者位体系结构越大,计算机越复杂。
- 速度/性能
它能以多快的速度执行操作呢?它是否达到了最大优化的程度以执行工作呢?使用定制设计构建一个计算机可以将更多的任务和分配给硬件,从而大大地提升计算机的性能。这在现实世界中的某些超级计算机中表现得很明显,这些超级计算机可以非常高效地进行一些操作。Minecraft中的红石计算机速度是非常慢的,一些模组,例如TickrateChanger可以更改Minecraft游戏客户端的游戏刻速度,以提高计算机速度。
红石计算机可以像真实计算机一样运作,遵循计算机设计和硬件架构中的原则。有几个关键的设计决策会影响架构;你对计算机预期的大小和功能应该在构建部件之前具体确定。建造红石计算机需要理解这四个概念,并考虑最合适的方法,这对计算机是最实用的。
一些事情需要考虑:
- 执行模型(内存的计算机组织与程序存储和执行)
- 字节大小(信息的大小,你的红石计算机用这些执行命令)
- 命令集(红石计算机的一个结构)
- 内存大小(数据可以存储在内存中,可存储数据多少取决于内存大小)
有一个这里确定的选择将在你的计算机各种组件的设计中提供强有力的指导。
我们会在本章最后一节应用这些内容并计划构建CPU。这个CPU会在下一章建造。
执行模型[]
在内存中存储命令块的技术叫做程序,允许计算机同时执行各种不同的任务。由计算机来存储和检索这些程序使用的设备是计算机的执行模型。世界上最成功的两种执行模型,哈佛和冯·诺依曼,几乎在今天可用的100%的计算机上运行。
哈佛结构[]
哈佛结构物理分离检索组成活跃程序的指令的设备和程序在执行期间访问的数据访问器。
为使用哈佛结构的电脑编写的程序执行访问主存总线的任务最多可能快100%。但是要注意,哈佛结构的某些记忆电路体积会很大。
冯·诺依曼结构[]
冯·诺依曼结构使用一个两步的过程来执行命令。首先,加载包含下一个命令的内存,然后加载的新命令执行时被允许访问相同的内存,使用同一个内存的程序和数据推动元编程技术,像编译器和自修改代码。
冯·诺依曼体系结构是第一个提出的计算模型,几乎所有现实中的计算机都使用冯·诺依曼结构。
位数大小[]
位数大小是计算机物理尺寸的主要因素。 在Minecraft中,从1位一直到32位的机器已经被成功构建出来。 常见的位数大小的组合:
数据 | 指令 |
---|---|
4 | 8 |
8 | 8 |
8 | 16 |
16 | 16 |
数据字[]
计算机在任何特定时间可以操作的信息量被计算机的数据字大小代表了。
在数字二进制中,计算机的数据字大小(以位为单位)等于计算机主总线中通道的宽度或数目。
数据字通常表示整数,即编码为二进制数字模式的整数。
二进制整数编码可表示的最大容量是2数据字所占位数 - 1。
比如,有8位数据字大小的计算机在总线上有八个通道(一组导线和连接部件),因此,最多能计算到28 - 1 = 255。8位模式下计算超过255的数字是不可能的,因为计算255 + 1会带来额外的一个进位,这需要第9个位才能储存,于是发生了二进制溢出。最后结果返回一个不正确的0。
这个过程可以用下表形象描述:
1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 255 | ||
+ | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | |
= | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
一些常见的整数数据大小是:
最大可表示的数量 | 所需的位数 |
---|---|
1 = 21 - 1 | 1 |
7 = 23 - 1 | 3 |
15 = 24 - 1 | 4 |
255 = 28 - 1 | 8 |
65535 = 216 - 1 | 16 |
4294967295 = 232 - 1 | 32 |
数据字大小还决定了可以由计算机的ALU(算术和逻辑单元)处理的数字的最大大小。
命令字[]
计算机完成一条命令所需的数据量被计算机命令字大小代表。
计算机的命令字大小通常是其数据字大小的倍数,这有助于在程序执行期间检索命令时最小化存储偏移。
指令集[]
这里是一系列指令,控制单元(CU)能将它们解码,然后执行。
指令是计算机运行的基本功能。一些例子包括:
- 加、减、乘和除
- 从RAM/ROM/三级存储器中读写数据
- 从RAM中加载和卸载数据
- 分支到代码的其他部分
- 与寄存器比较
- 选择一个逻辑运算(与非、或非、非……)
指令可以被编码进RAM,从ROM中读取,或直接被拉杆或按钮激活。每条指令都有自己指定的二进制字符串(例如,0000可以代表从寄存器中读取数据,0001代表A与B相加,1011代表将RAM的数据保存到三级寄存器,等等),并且可能将自身的二进制编码转为十进制(将BIN码转为BCD码),再到十进制解码器,最后通过总线到ALU或寄存器。
构建CPU[]
所有的计算机系统都至少有一个处理单元。在运行时,处理单元执行存储在计算机内存中的指令。在计算机内部,有一个中央处理单元(简称CPU,不要与CPU内部的控制单元(CU)混淆),在现实生活中,这是一个非常小却很强大的组件,或多或少地充当计算机的大脑。
在Minecraft中,很难将其压缩到我们在现实生活中看到的规模,所以如果它看起来像是错误的,请不要担心。
在下一章中,我们将首先设计我们的4位中央处理单元,因为考虑到执行模型(CPU的通信和组织方法),这是我们计算机中最重要的事情,我们可以绘制计算机的构建图。
CPU遵循着一个循环,一共有四个步骤,获取、解码、执行和存储来执行指令。CPU首先从RAM获取指令,解码其含义(指令很可能是一个数字,CPU必须找出它是什么数字),一旦它理解了指令是什么,它将执行该操作。有时需要将数据放回存储器,因此它会存储数据。然后重复循环。
总线[]
CPU中共有五条总线,每条总线将信息从一个组件传送到下一个组件。总线是连接每个组件的红石通道。因为我们正在建造一台4位计算机,所以我们的总线只需要四个通道。这些是连接中央处理单元内部组件的红线和蓝线。请注意,蓝色总线少于四条线,这是因为它们不携带数据。由于总线只能单向传输数据(在Minecraft中,双向中继器以外的中继器只能单向工作),所以有两条总线将中央处理单元连接到外部计算机。
- 数据总线,将输入/输出设备或存储器信息与CU连接,以传输信息。指令同样通过数据总线传输。CU也可以通过数据总线传输给ALU。ALU不能通过此条总线传输,因为它只能单向传输。一旦数据由ALU接收,这条线路就会切断。
- 数据总线,专门用来将ALU的计算数据传回给CU。同样地,由于单向传输,CU不能将数据通过此线传输到ALU。但CU可将信息传回到存储单元,从而更改存储设备的值。
- 地址总线,CU通过这条线路传输存储器地址信息。这就是信息“居住”的地方。例如,CU请求获取位于地址0001的字节。CU通过地址总线,发送“0001”,接着RAM通过第一条数据总线返回地址里面的值。需要注意的是,此处的“0001”指的是内存中的地址,不是该地址中存储的值。
- 控制总线,CU通过这条总线与RAM交互。例如,一条线路告诉RAM,将对应字节处的内容设置为CU传输过来的值。另一条线路告诉RAM,从CU指定的地址处获取字节信息。
- 控制总线,连接到ALU,能接收来自ALU的信号。这些信号就是标记,可以指示错误信息。比如,CU要求ALU将15和1相加。假设这是一个4位机器,计算15 + 1会得到0,这就是二进制溢出(如上所述)。这时就会有个信号指示错误,ALU会通过这条总线告诉CU。CPU也可以将数据传送至ALU,让ALU对那个数据执行另一操作。
部件[]
控制单元(CU)能从ROM(只读内存)指令中取指令。对于其他计算机来说,这些指令是可以更改的,那就是存到RAM(随机存取存储器)中。但是对我们来说,我们要运行一个固定的程序,并不需要更改这些指令。这能大大简化工程,我们只需要ROM即可。然后控制单元会对指令解码,通常是将数字转换成可识别的操作。接着,控制单元执行操作,有时会根据指令要求,通过控制总线将结果存入RAM中。在执行过程中,控制单元也会接收ALU的执行信号。控制单元也能请求ALU执行操作(比如加减法)。具体交互操作请见上文总线一节。
算术逻辑单元(ALU)会执行控制单元发送的指令,处理二进制数,再将结果告诉控制单元。ALU能执行加法、减法,如果组合起来还能做乘法与整数除法(当然,进一步改造,也可做所有除法)。内部通常是逻辑门,用来处理逻辑值,比如非门、与非门。
现在我们可以选择一种总线设计,每一种都能达到一开始提到的三种关键设计目标,建成Minecraft中的计算机。
第三章 计算机的设计[]
指令集架构[]
状态[]
存储器是一系列的位。在Minecraft中,尽管曾经成功创造过32位和64位[1]计算机,但是通常情况下存储器有8位或16位。每一位都是两种可能的状态中的一种:开或关。而存储器就是一系列的开和关,用于完成特定的任务。
信号[]
现实世界计算机使用二进制,一系列的1和0。“1”表示“开”和“0”表示“关”。在Minecraft中,最好的体现是红石:有信号意味着“1”,没有信号表示“0”。然而,根据红石线到存储器的距离,“0”可以是从0到14的任何信号强度。你也可以设计,使“1”信号强度从1到15。
数[]
我们常规的十进制是一种以10为基数的数字系统。在电脑中使用的数制——二进制,则是以2为基数。为了比较两者,我们看一下两位数。在十进制中,左边的那一位是十位。在二进制中,则是“二位”。比如在十进制中,“10”表示“十”。而在二进制中,“10”表示“二”。有两种常用的十进制转二进制的方法:
1. 最高位优先:这种方法需要一点直觉。我们以42为例。首先我们找小于等于42的最大的2的幂(如32 = 25或65536 = 216)。在这个例子中,是32。然后我们用例子中的数字来减它,42-32=10。那么最左边的一位就是“1”。随后我们继续找下一个2的幂,看它是否小于等于当前的数字。对于这个例子来说,下一个是16,16大于10,所以接下来这一位是“0”。一直找下去直到数字为0为止。无论二的幂是小于还是等于当前的数字,都要减掉它,并且记下一位为“1”。否则下一位就是“0”。用表格来表示这一过程,就是
比较 | 计算 | 结果 |
---|---|---|
32<42 | 42-32=10 | 1 |
16>10 | 0 | |
8<10 | 10-8=2 | 1 |
4>2 | 0 | |
2=2 | 2-2=0 | 1 |
1>0 | 0 |
因此最终42的二进制表示就是“101010”。
2. 最低位优先:这个方法不需要记忆许多2的指数。相反,它重复将数字除以2,使用商作为下一个被除数,余数作为二进制位。但请记住,此方法从右到左写入二进制数,而不是像前一种方法一样从左到右写入。让我们重用我们的例子,42:
计算 | 余数 | 说明 |
---|---|---|
42/2=21 | 0 | 最右边的数字是0 |
21/2=10 | 1 | 向左边一位的数是1 |
10/2=5 | 0 | 向左边一位的数是0 |
5/2=2 | 1 | 向左边一位的数是1 |
2/2=1 | 0 | 向左边一位的数是0 |
1/2=0 | 1 | 向左边一位的数是1 |
商数为0,所以我们停止。 我们同样得到了二进制数“101010”,与之前相同。
归类[]
映射[]
数字[]
符号可以用红石灯或者活塞的推拉方块产生凸凹显示:
注:如果用红石灯的话不要用黄色的方块做面板,不然不容易分辨。
形式化[]
变量[]
变量是数字、字符串、字符(套)或布尔值(真/假)存储在RAM中的空间。例如,布尔值可以用来保存程序是否已经达到某种状态的信息。一个变量的以下信息需要保存:名称,类型(数字、字符串或布尔),和变量值。变量,顾名思义,改变。命令操作可以改变变量。在运行程序时创建变量,一旦程序结束,变量值会被从内存中删除。当一个程序重启,变量会被重置。在Minecraft中也是如此。
语义[]
数据[]
数据是计算机处理的信息,使用二进制表示。
机器架构[]
算术逻辑单元(简称ALU)[]
ALU是计算机最重要的组件之一,在现实生活和Minecraft中。首先,你必须选择你希望能够实现的功能。大多数时候,这些都是加法、减法和一组逻辑选项。
与,或,与非,或者你所喜欢的。你必须建立单位和所有你想要的逻辑门和数学函数和选择哪一个的输出显示。
(数据)总线[]
用总线允许你的计算机的组件相互通信。
一条总线可以通过使用创建红石布线连接你的计算机的运算器,随机储存器,只读储存器,中央处理器和寄存器在一起,这样他们就可以互相之间发送数据。通常是重要的计划,建立你的电脑的组件,否则你创建总线过长,或者更糟的是,没有空间来创建总线,在这种情况下,你可以删除的组件并重建一个适当的位置,或者使用像WorldEdit移动组件到其他地方。
存储[]
在Minecraft或是现实生活中有很多种方式存储数据。存储状态通常为二进制的,非开即关,可通过逻辑运算来执行。
在计算机中,有三种存储器。要知道,增加硬件的容量亦即增加尺寸,因此每一种存储器都有自己适合的速度与容量。
主存[]
主存,也为内存,对于CPU来说可以直接且最快访问。因此,在容量上它也通常很小。
寄存器与标志寄存器[]
最快的是存储在CPU中的内存。如下图,这些是寄存器和标志寄存器,它们几乎可以立即设置,并且不需要向其发送任何地址,因为每个寄存器中只能存储一个字节。
可以切换的红石位非常大,但可以在2刻内切换。这需要很大的空间,但非常适合缓存和寄存器。逻辑门(未显示)也需要红石来设置位,如图中所示,输入脉冲会导致位翻转。门会占用更多的空间。寄存器还可以利用锁定红石中继器和精确计时,这将在下面的RAM中解释。使用计算机时钟时,可能不需要建立寄存器。当数据在CU或ALU准备处理之前通过线路时,寄存器非常有用。它会将其保存到寄存器中,并等待CU或ALU执行其功能。
缓冲存储器[]
第二快的是缓冲存储器(缓存),将信息输入处理器。现实中,它被分为几个等级,每个等级都有独立的速度与能力[2]。和寄存器原因相同,它很有用。
随机存取存储器[]
第三快的是随机存取存储器(RAM),这比寄存器和缓存慢得多,是因为它有地址系统。它与三条总线(数据总线、控制总线和地址总线)连接。数据通过数据总线传送,无论是设置RAM中数据的值或是从RAM中获得信息。控制总线能控制RAM,要“获取”还是“设置”信息。地址总线能告诉RAM操作的是哪个字节。参见计算机的结构来更加深入了解。RAM非常有用,可以完全替代三级存储器(是因为它在Minecraft中不易失)。易失的意思是,在现实中,RAM断电时会丢失所有的信息,这在Minecraft中不会发生。因此RAM是存储信息的极佳方法。
在第一种情况下,RAM使用具有正确定时的锁定红石中继器。这需要一点规划,但非常节省空间。将总线转换为多条线路以锁定红石中继器也需要设置定时。这非常耗时,远远超过寄存器,但是,它非常紧凑且高效。地址总线(绿色部分)将以二进制方式解锁某个字节,由控制总线读取或设置(第二行,左侧)。
在大多数情况下,其易失性在Minecraft中不会生效,因此最简单的方法是使用D触发器并添加读写功能。如下图所示,它没有锁定中继器,而是使用D触发器,空间效率更低,但构建更简单。D触发器的工作原理或多或少类似于锁定的红石中继器,一个输入如果打开,将解锁,直到输入关闭,另一个将在解锁后将其设置。输出可以读取为一个位,使用与非门可以忽略它或将其放到总线上。这在第一章“建造一台计算机”中有详细介绍。
二级存储器[]
这相当于HDD和SSD。下面介绍一种非常紧凑的存储技术,要涉及到红石比较器,能够存储高达1KB的实际数据大小。
介绍视频请见此处。
三级存储器[]
第三,也是最后一点,是第三级内存,它需要大量的时间来读/写,牺牲速度但可以存储大量信息。在现实世界中,三级存储器使用的是一种老式的挂载内存的机制,而且现在也很少使用了。在Minecraft中,这种系统要用潜影盒来完成,潜影盒中的方块必须由排序系统进行排序,以表示某种形式的数据。由于这些工作需要大量的红石比较器和大量的时间,所以读/写速度相当慢。然而,利用某些mod可以加快游戏tick的速度并消除这个问题。这用于存储长期数据,这些数据需要在程序开始时加载。相当于一台真正的计算机的机械硬盘或固态硬盘。
机器状态[]
程序计数器[]
程序计数器用于告诉CPU应该运行哪行代码。在每个时钟周期,解码器将访问这个计数器来获取下一个要执行的命令。一些命令会比另一个访问不同的数据量,或无任何数据,因此解码器将按由下一个命令确定的量增加程序计数。计数器也被跳转命令用于控制程序流。
现实中,程序计数器自身并不是一个元器件,只是在其他寄存器旁边的一个寄存器。然而在Minecraft中,应该建造一个独立的寄存器,用来存储程序计数,这并不奇怪。
提示[]
- 你也可以使用一些像是WorldEdit的模组。
- 如果你在生存模式没有太多的红石中继器,你可以用两个红石火把代替。
- 利用颜色进行分区(例如用蓝色羊毛建造RAM(随机存取存储器),黄色羊毛建造ALU(算术逻辑部件运算器)等)。
- 结构方块对移动部件、组合多个部件来说很有用。然而,它只能通过命令获得。
参见[]
注释[]
- ↑ “International General Certificate of Secondary Education(国际普通中等教育证书)”的简称,剑桥全球测试的一部分。
|
语言