通过我的复古计算机程序学习编码

我编写了一个名为 Toy CPU 的教育用复古计算机,以便我的学生可以学习机器语言。
5 位读者喜欢这篇文章。
Old UNIX computer

Opensource.com

我兼职教授大学课程,包括一门关于通用计算主题的课程,对所有专业的学生开放。这是一门入门课程,旨在向学生介绍技术的工作原理,消除围绕计算的神秘感。

虽然不是计算机科学课程,但这门课程的一个章节涵盖了计算机编程。我通常以非常抽象的方式谈论编程,以免让听众感到困惑。但是今年,我希望我的学生以“老派”的方式进行一些“动手”编程。与此同时,我想保持简单,以便每个人都能跟上。

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

机器语言编程

早期的个人电脑,如 Apple II (1977)、TRS-80 (1977) 和 IBM PC (1981),允许用户通过键盘输入程序,并在屏幕上显示结果。但计算机并非总是配备屏幕和键盘。

Altair 8800 和 IMSAI 8080(均产于 1975 年)需要用户使用面板上的“开关和指示灯”输入程序。您可以使用一组开关以机器语言输入指令,机器将使用 LED 灯点亮每个二进制指令的 1 和 0。

Image of an Altair 8800 Computer.

(Cromemco, CC BY-NC-SA 3.0)

对这些早期机器进行编程需要了解机器语言指令,称为操作码(opcodes),是 operation codes(操作代码)的缩写,用于执行基本操作,例如将两个数字相加或将值存储到计算机的内存中。我想向我的学生展示程序员如何手动输入一系列指令和内存地址,使用开关和指示灯。

但是,在课堂上使用真正的 Altair 8800 会造成太大的负担。我需要一些简单的东西,任何初学者水平的学生都可以掌握。理想情况下,我希望找到一款简单的“业余爱好”复古计算机,其工作方式与 Altair 8800 类似,但我找不到价格低于 100 美元的合适的“类 Altair”设备。我找到了一些“Altair”软件模拟器,但它们忠实地再现了 Altair 8800 操作码,这对于我的需求来说太多了。

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

Toy CPU 编程

Toy CPU 是一款非常简单的复古计算机。Toy CPU 仅有 256 字节的内存和一个最小的指令集,旨在简化操作,同时复制“开关和指示灯”编程模型。该界面模仿 Altair 8800,带有一系列八个 LED,用于计数器(程序的“行号”)、指令、累加器(用于临时数据的内部内存)和状态。

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

Image of start screen for the toy cpu.

(Jim Hall, CC BY-SA 4.0)

 

当 Toy CPU 准备好让您输入程序时,它会通过状态指示灯指示 INP(“输入”模式),并从程序中的计数器 0 开始。Toy CPU 的程序始终从计数器 0 开始。

在“输入”模式下,使用向上和向下箭头键显示不同的程序计数器,然后按 Enter 键编辑当前计数器处的指令。当您进入“编辑”模式时,Toy CPU 会在状态指示灯上显示 EDT(“编辑”模式)。

Image of the toy CPU editing screen.

(Jim Hall, CC BY-SA 4.0)

Toy CPU 有一张“粘贴”在显示器正面的速查表。其中列出了 Toy CPU 可以处理的不同操作码

  • 00000000 (STOP):停止程序执行。

  • 00000001 (RIGHT):将累加器中的位向右移动一位。值 00000010 变为 00000001,00000001 变为 00000000。

  • 00000010 (LEFT):将累加器中的位向左移动一位。值 01000000 变为 10000000,10000000 变为 00000000。

  • 00001111 (NOT):对累加器进行二进制 NOT 运算。例如,值 10001000 变为 01110111。

  • 00010001 (AND):将累加器与存储在地址中的值进行二进制 AND 运算。地址存储在下一个计数器中。

  • 00010010 (OR):将累加器与存储在地址中的值进行二进制 OR 运算。

  • 00010011 (XOR):将累加器与存储在地址中的值进行二进制 XOR(“异或”)运算。

  • 00010100 (LOAD):将值从地址加载(复制)到累加器中。

  • 00010101 (STORE):将累加器中的值存储(复制)到地址中。

  • 00010110 (ADD):将存储在地址中的值添加到累加器中。

  • 00010111 (SUB):从累加器中减去存储在地址中的值。

  • 00011000 (GOTO):跳转到计数器地址。

  • 00011001 (IFZERO):如果累加器为零,则跳转到计数器地址。

  • 10000000 (NOP):空操作;安全忽略。

在“编辑”模式下,使用向左和向右箭头键选择操作码中的一位,然后按 空格键 在关闭 (0) 和打开 (1) 之间翻转值。编辑完成后,按 Enter 键返回“输入”模式。

Image of the toy CPU input mode screen.

(Jim Hall, CC BY-SA 4.0)

示例程序

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

  • 00010100 (LOAD):将值从地址加载(复制)到累加器中。

  • 00010110 (ADD):将存储在地址中的值添加到累加器中。

  • 00010101 (STORE):将累加器中的值存储(复制)到地址中。

  • 00000000 (STOP):停止程序执行。

LOADADDSTORE 指令需要一个内存地址,该地址始终位于下一个计数器位置。例如,程序的前两条指令是

counter 0: 00010100
counter 1: some memory address where the first value A is stored

计数器 0 中的指令是 LOAD 操作,计数器 1 中的值是您存储某个值的内存地址。这两个指令一起将内存中的值复制到 Toy 的累加器中,您可以在其中处理该值。

将数字 A 加载到累加器后,您需要将值 B 添加到其中。您可以使用以下两条指令执行此操作

counter 2: 00010110
counter 3: a memory address where the second value B is stored

假设您将值 1 (A) 加载到累加器中,然后将值 3 (B) 添加到其中。累加器现在的值将为 4。现在您需要使用以下两条指令将值 4 复制到另一个内存地址 (C)

counter 4: 00010101
counter 5: a memory address (C) where we can save the new value

将两个值相加后,您现在可以使用以下指令结束程序

counter 6: 00000000

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

counter 7: 00000001 (1)
counter 8: 00000011 (3)
counter 9: 00000000 (0, will be overwritten later)

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

counter 0: 00010100
counter 1: 00000111 (7)
counter 2: 00010110
counter 3: 00001000 (8)
counter 4: 00010101
counter 5: 00001001 (9)
counter 6: 00000000
counter 7: 00000001 (1)
counter 8: 00000011 (3)
counter 9: 00000000 (0, will be overwritten later)

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

Toy 内置了明显的延迟,因此您可以观看 Toy 执行程序中的每个步骤。您应该看到计数器从 00000000 (0) 移动到 00000110 (6),程序不断进行。在计数器 1 之后,程序从内存位置 7 加载值 1,累加器更新为 00000001 (1)。在计数器 3 之后,程序将添加值 3 并更新累加器以显示 00000100 (4)。累加器将保持该值,直到程序在计数器 5 之后将该值存储到内存位置 9,然后在计数器 6 处结束。

Image of the Toy in RUN mode.

(Jim Hall, CC BY-SA 4.0)

探索机器语言编程

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

一个闪烁累加器上的指示灯的程序

您能否点亮累加器上的右边四位,然后点亮左边四位,然后点亮所有位?您可以使用以下两种方法之一编写此程序

一种直接的方法是从不同的内存地址加载三个值,如下所示

counter 0: LOAD
counter 1: "right"
counter 2: LOAD
counter 3: "left"
counter 4: LOAD
counter 5: "all"
counter 6: STOP
counter 7: 00001111 ("right")
counter 8: 11110000 ("left")
counter 9: 11111111 ("all")

编写此程序的另一种方法是尝试使用 NOT 和 OR 二进制运算。这将产生一个更小的程序

counter 0: LOAD
counter 1: "right"
counter 2: NOT
counter 3: OR
counter 4: "right"
counter 5: STOP
counter 6: 00001111 ("right")

从一个数字倒数

您可以将 Toy 用作倒计时计时器。此程序练习 IFZERO 测试,该测试仅在累加器为零时才将程序跳转到新的计数器

counter 0: LOAD
counter 1: "initial value"
counter 2: IFZERO (this is also the "start" of the countdown)
counter 3: "end"
counter 4: SUB
counter 5: "one"
counter 6: GOTO
counter 7: "start"
counter 8: STOP
counter 9: 00000111 ("initial value")
counter 10: 00000001 ("one")

Toy CPU 是学习机器语言的好方法。我在我的入门课程中使用了 Toy CPU,学生们说他们发现编写第一个程序很困难,但是编写下一个程序就容易多了。学生们还评论说,以这种方式编写程序实际上很有趣,并且他们学到了很多关于计算机实际工作原理的知识。Toy CPU 既有教育意义又有趣!

标签
photo of Jim Hall
Jim Hall 是一位开源软件倡导者和开发者,以 GNOME 的可用性测试以及作为 FreeDOS 的创始人兼项目协调员而闻名。

1 条评论

在我年轻的时候,我拥有纸板图解计算辅助设备 (CARDIAC)。这真是一种亲身体验学习计算机工作原理以及机器级编程的方式。

© . All rights reserved.