迷失的编程方法:Commodore 64 Basic

2020-11-07 11:19:13

在这篇互动文章中,我们将在浏览器中使用Commodore 64 Basic构建一个突围游戏。这是一个有趣的编程技巧,但它有相当深厚的理论背景。听我解释。

我相信,当编程比我们使用的特定编程语言更重要时,我们如何与编程环境交互。

这从来没有被广泛研究过,我们可以从过去的系统中学到一些有趣的东西,包括准将64 Basic。

我们应该回顾历史,重现过去的编程经验,以便向他们学习,遵循科学史学家张夏硕(Hasok Chang)所称的完全科学的方法。

仅仅阅读有关互动的内容是不够的。要了解互动是如何运作的,你需要亲身体验,至少是在有限的形式下。这最好是用一篇互动的文章来完成。

这是一篇交互式文章,记录了Commodore 64 Basic编程的一些有趣方面。不过,我并不是要创造一个精确的准将64模拟器。重点是展示一些我们可以在未来的编程系统中学习的东西。

我们将从Hello World示例开始,看看事情是如何工作的,然后我们将构建一个小型突围游戏。这说明了入门是多么容易,环境如何支持学习,以及Commmodore 64基本交互模式如何让我们以一种与现代编程环境截然不同的方式逐步构建程序。

当准将64启动时,一个来自Basic的欢迎屏幕等待着你。即使您只想使用它来玩游戏,您也可以从编程环境开始。这告诉您,您也可以成为一名程序员,您当然不需要下载千兆字节的工具,也不需要等待数小时才能安装XCode或Visual Studio。

让我们遵循传统,让Basic为我们问好世界。要执行此操作,请键入以下命令,或者如果您很懒,只需单击该按钮即可。

对于一个现代程序员来说,从启动机器到打印Hello world所需的时间是如此之少,这是令人惊讶的。您不仅可以从编程环境开始,还不需要编写任何类、静态方法和编译程序。

显而易见的下一步是在无限循环中打印Hello world。以前,我们输入一个命令并由Basic执行它,但现在我们需要创建一个程序。为此,我们在代码前加上行号:

这又是一种巧妙的做法。Basic会保存程序的行列表,当您键入以数字开头的行时,它会将其插入到正确的位置。您可以使用相同的提示符来运行命令和编辑程序。要现在运行该程序,只需键入run:

如果您运行的程序带有类似这样的无限循环,您可以使用Ctrl+C或Command+C来停止它。如果您想要查看您输入的程序,您可以输入list命令。

在Basic中,只需几行代码就可以实现许多巧妙的技巧。这种入门的简便性使它成为一个有趣的编程环境。如果你在电脑杂志上发现了有趣的黑客攻击,你可以把它输入控制台,然后马上运行它。

你必须从纸质杂志上复制代码这一事实听起来很麻烦,但它具有教育意义。它使可以以这种方式分发的样本保持在相当小的范围内,并且让您在输入代码时考虑代码。

要亲自体验这一点,您应该尝试在控制台中输入以下三行程序!它会产生一个著名的迷宫。这依赖于特殊的准将字符代码:147清除屏幕,205和206是反斜杠和横跨整个字符大小的斜杠。

要打造我们的突围游戏,我们可以循序渐进。这是编程环境的另一个很好的特性。我们想创造一个从墙上弹起的球,但让我们从一个向右移动的球开始吧。

我们只会做很少的规划。用游戏状态初始化变量的代码从第1000行开始,处理球移动的代码从2000开始。我们还将首先清除屏幕并使用DELETE删除所有先前的迷宫和Hello World代码。

打印CHR$(147);删除1000REM状态初始化1010X=02000 REM球移动2010 Pke X CHR$(32)2020 X=X+12030 Pke X CHR$(209)2040Goto 2000Run。

要在特定位置绘制一个球,我们使用poke,它将一个值写入内存位置。这里,代表屏幕的内存部分从偏移量0开始。我们首先使用空格(字符代码32)擦除前一个球,然后画一个球(字符代码209)。

要让球从墙上弹起,我们需要检查它何时到达屏幕一侧,并反转它移动的方向。要做到这一点,我们需要保持更多的状态。我们使用变量X来保持X坐标。现在,我们为Y坐标添加Y,并为方向(+1或-1)添加DX、DY。为了清楚地看到我们的代码,让我们首先清除屏幕并列出我们当前的代码。

我们紧跟在初始化代码中的编辑之后,在球移动代码中进行编辑(从第2000行开始),以检查与屏幕左侧和右侧的冲突:

2010Poke((Y*40)+X)CHR$(32)2020 X=X+DX2030 Y=Y+DY2040如果X=40,则DX=-12050如果X=40,则X=382060;如果X<;0,则DX=12070如果X<;0,则X=12200 Pke((Y*40)+X)CHR$(209)2210转至2000Run。

我们知道需要为顶部和底部添加支票,所以我们保留了一些空行。检查代码在2070结束,抽球在2200行。我们只需要填上剩下的支票:

2080年如果Y=25,则DY=-12090;如果Y=25,则Y=232100;如果Y<;0,则DY=12110;如果Y<;0,则Y=2Run。

为什么球还在从左向右再向后移动?我们添加了校验码,但忘了更改DY。这很容易修复:

这就是我们想要的,一个从墙上弹起的球!但让我反思一下我们是如何创建这个项目的。我们从一个移动的球开始,然后分两步增加弹跳。Commodore 64 Basic的编程模型使这一点变得非常简单。

这取决于两件事。首先,由于代码是一个简单的命令性命令列表,寻址(使用行号)使编辑程序比编辑由复杂的组合表达式组成的程序要容易得多。我们必须聪明地处理行号,才能有空间插入代码,但这只是一个很小的代价。其次,控制台的双重用途(既是编辑器又是REPL)意味着交互非常简单。

这款游戏机还有更多的功能。您可以使用它来运行一次性测试,比如Print(Y*40)+X来测试您的计算。您也可以直接修改状态,然后跳到程序中间。例如,我们可以将球移动到右下角,并通过跳过初始化代码让程序从那里运行:

在我们把这个程序变成一个真正的游戏之前,我们需要弄清楚如何处理来自键盘的输入。如果您有一个字符串变量K$,则可以使用GET$K$从缓冲区读取密钥。如果缓冲区中没有键,这将返回一个空字符串,因此我们需要编写一个等待键的循环。在实际程序开始之前,我们可以在第10-50行执行此操作:

打印完字符的PETSCII代码后,我们显式地停止程序,这样它就不会继续进入我们的球弹跳代码。现在尝试使用Run命令重复运行该程序,以获取向上和向下箭头的关键代码!解释器不支持所有键,但它可以处理箭头、空格和字母数字字符。

我们需要向上键(145)和向下键(17)的密钥代码。现在我们知道了这一点,我们可以删除我们的测试代码:

既然我们已经了解了如何处理输入,我们就可以在游戏中添加一把桨了。它将位于屏幕的左侧,并使用向上键和向下键进行移动。我们可以独立于到目前为止已经编写的所有代码来编写代码,在初始化代码中添加1050行,并在2500处开始划桨移动代码。

逻辑很简单。我们读取一把钥匙并得到它的代码。如果键向上或向下,我们会递增或递减球拍的位置。然后,我们使用POKE绘制划桨,还在前后画了一个空格来擦除之前的状态。仿真器不支持循环,所以我们只需要一个接一个地绘制5个竖线字符。

1050P=102500 REM移动A球拍2510 K=02530,如果K$<;&>34;";";则获得$2540 K;;";";;2520 K=02530,如果K$<;>;";";则获得$2540;则K=Asc(K$)2550如果K=145,则P=P-12560如果K=17,则P=P+12570 poke((P-1)*40)chr$(32)2571 poke((P+0)*40)chr$(182)2572 poke((P+1)*40)chr$(182)2573 poke((P+2)*40)chr$(182)2574 poke((P+3)*40)chr。

无论您是否遵循了前面的章节,您都可以运行此命令。如果您这样做了,输入run将启动弹跳球,因此,我们手动初始化P并直接跳到球拍处理代码。

为了尽快让某些东西运行,我们没有添加任何检查以确保PATE不会在屏幕之外运行。如果发生这种情况,程序会立即停止,您需要使用Ctrl+C组合键终止它。要解决这个问题,我们只需要在不在开始/结束位置时在桨周围画空格,并确保P在0到20之间。我们非常明智地使用10的倍数作为行号,所以我们有空间在2560到2570之间插入此代码。

如果P&>0,则戳((P-1)*40)chr$(32)2576如果P<;20,则戳((P+5)*40)chr$(32)2561如果P&>0,则P=02562;如果P<;20,则P=20GOTO 2500。

我们在很大程度上独立构建了游戏的两个部分。弹跳球的代码在2000到2500行之间,我们可以通过输入run或goto 2000来启动它。在那之后,划板的代码就开始了,我们可以使用GOTO 2500来运行它。最后一步是把这两个部分连接起来!如果您跳过了本教程的一部分,以下内容可让您重新加载所有代码。

现在运行List时,整个代码太长,无法显示在屏幕上。如果您想查看它,则需要运行list-2500来查看第2500行之前的所有内容,或者运行list 2500来查看2500行之后的所有内容。

要链接这两个部分,我们需要进行几次编辑。首先,我们将清除初始化代码末尾的屏幕,并更新弹跳,使球保持在球拍的右侧。其次,我们将把这两个部分联系起来。为此,我们通过从球移动代码的末尾删除GoTo 2000来合并这两个循环。只有在更新了2580线上的球拍之后,我们才会跳到2000线。

这看起来像是一场比赛,但我们遗漏了最后一个关键部分。我们需要添加一个检查,以检测球在从屏幕左侧反弹时没有击中球拍。我们在2030和2040行之间插入这段代码,也就是在更新球位置之后。

2031年如果(X=0)和(Y<;P),则转到30002032 IF(X=0)和(Y&>;(P+4)),然后转到30003000 STOPRUN。

当球在球拍下方或上方时,代码跳转到3000行,该行使用STOP命令终止程序。为了让游戏更好看一些,我们用一个漂亮的游戏结束效果来代替它。如果你正在输入这段代码,你也可以在最后运行GoTo 3000,在代码上只调试游戏,而不必玩游戏。

3000REM游戏OVER3010打印克朗$(147);3020S=03030 S=S+13040打印游戏结束##34;3050如果S<;25,则转到30303060打印克朗$(147)运行。

就是这样!如果您遵循了本文的各个步骤,那么现在您已经用不到50行代码创建了一个简单的Breakout游戏。这篇文章的目的是让你体验编程过程的感觉,但是如果你跳过一些部分,你可能会欺骗并加载下面的整个游戏。完成此操作后,使用Run运行它,或使用List查看完整代码。

我在开始这篇文章时提出,我们如何与编程环境交互比使用的语言更重要。计算机科学非常擅长谈论编程语言,但它传统上很少谈及交互。

本文的重点是指出与Commodore 64 Basic交互的一些有趣的方面。这是一个非常简单的环境,但有一些宝贵的经验教训:

开始编程很容易。系统引导进入编程环境,使其成为与机器交互的主要方式。从磁带加载并运行游戏涉及到与编程相同的交互。

这很容易学。许多程序在纸质杂志上以代码清单的形式出现,这可能是偶然的美德,但这意味着它们很短,你可以在复制的过程中学习。

只有一种互动。您可以在控制台中键入命令,但这既是立即运行命令的一种方式(REPL),也是构建程序(编辑器)的一种方式。

你会得到一个简单而灵活的工作空间。我们独立创建了游戏的两个部分,然后通过编辑两个GoTo将它们连接在一起。它们也很容易独立测试,因为我们可以在REPL模式下设置变量,并直接跳到代码中间。

黑客访问额外功能。我只是部分演示了这一点,但是POKE(和SYS)让您可以访问系统的许多额外功能。常见的事情很简单,但好奇的黑客会得到奖励。

我并不是建议我们扔掉使用Visual Studio Code的打字稿或使用Eclipse的Java打字稿,而是回到Commodore 64和Basic。然而,现代编程环境,即使使用REPL或实时重载,也肯定可以学到一些技巧。

也许最重要的一点是,语言应该与编程交互一起设计。例如,BASIC中的行号不仅仅是穷人的循环。它们允许与系统进行许多有趣的交互。

本文还试图提出一个方法论观点。一种天真的观点认为,科学是一项进步的事业,是一点一点地逐步提高我们的知识。正如历史学家和科学哲学家指出的那样,实际的科学要复杂得多。

科学中的多元化意味着有时可能会有多个被接受但相互矛盾的理论,这些理论仍然可以有效地交流思想。科学革命意味着,有时,基本假设的革命性转变会使过去的知识失效。

尽管我们有时不愿称计算机科学为一门科学,但我们仍然相信其直截了当的进步本质这一天真的想法。这种观点暗示,我们现在的知识囊括了过去所有好的想法,而被遗忘的想法则因为它们根本就不好而丢失了。物理等科学并非如此,计算机科学更是如此。

这是什么意思?如果我们承认过去的科学思想可能有某些东西丢失了,那么就有必要重建这些思想,把它们放在现代的背景下,看看它们如何为当前的发展做出贡献。科学史学家张夏硕(Hasok Chang)称这种方法为互补科学。

互补的计算机科学正是我在这篇文章中想要做的。我认为,关于如何与编程环境交互的有趣想法已经丢失,很大程度上是因为编程研究变得如此专注于语言(可能是在ALGOL的影响下)。这篇文章试图重现一些有趣的过去的想法。我选择从准将64 Basic开始是一个相当务实的选择。过去还有更有趣的编程系统,但这一个相对容易重现。

谈论编程交互也比谈论编程语言更难,因为一种语言很容易在纸质报纸上进行正式描述。要理解互动,你需要体验它。这就是为什么这篇文章是交互式的,也是我鼓励读者玩弄环境的原因,至少是通过复制一些代码来实现的。

换句话说,我们需要改变我们谈论的内容和谈论它的方式,因为媒介就是信息。