修理雅达利2600的外星人

2020-05-16 11:06:44

还记得我们在第一个版本中使用的1013处的死代码吗?这只是计时码,在那里浪费一些周期,这样所有的东西都画在正确的地方。我们最终并不需要它来修复我们的失败(尽管我们还是把它留在那里了),所以我们可以自由地使用它来做其他的事情,只要我们谨慎选择时机的话。我们要更改的代码从1060开始,分支到1022的代码。这就把我们放在1013的无用代码的正下方。我们可以从那里截取几个字节,然后将跳转调整到1074。

首先,我们将在1060终止WSYNC,并将跳转到1022(在1074)之前的所有内容上移两个字节。我们将在1022将CPX移动到我们在1072创建的洞中,从而:

1060:LDA$87;3;A5 87 1062:STA GRP0;3;85 1B 1064:LDA$88;3;A5 88 1066:STA COLUP0;3;85 06 1068:TXA;2;8A 1069:Tay;2;A8 106A:LDA$BA,Y;5;B1 BA 106C:STA PF1;3;85 0E 106E:LDA$BC,Y;5;B1 BC 1070:

接下来,我们将通过将101F处的跳转移动到101B来从死代码中获取4个字节。然后我们将更新我们在1074处的跳跃(过去指向1022)以指向101E。

101B:JMP F04B;3;4C 4B F0 1074:JMP F01E;3;4C 1E F0。

现在我们可以为我们的例程清除一个10字节的位置。将PHP从1024移至101E。(我们可以不受影响,因为JMP不影响任何旗帜。)。现在,将从1025的CPX到102E的TXA的所有内容向下移动一个字节,覆盖102F的无用SEC。

101B:JMP F04B;3;4C 4B F0 101E:PHP;3;08 101F:CPX$8B;3;E4 8B 1021:BNE+6;2;D0 06 1023:位CXP1FB;2;24 13 1025:BVS+2;2;70 02 1027:STA CXCLR;3;85 2C 1029:CPX$9E;3;E4 9E 102B:PHP;3;

如果我们现在试这个游戏,它会崩溃的。内核入口点位于102e,现在位于指令中间!CPU会认为我们正在尝试STX$8A,谁知道之后还会发生什么!我们需要将入口点向下调整一个字节。它埋在18F3的一张桌子里。该地址在16A0处加载并推送到堆栈上。我们使用16b5的RTS跳到内核。RTS指令在设置PC时将地址从堆栈中弹出,但在进程中莫名其妙地加1。这意味着我们需要在我们想要的地址之前指定一个1字节的地址。这太奇怪了。我们跳到我们的新地址..。通过指定我们的旧版本。

现在,如果我们用NOP';s替换从1013到101a的8个字节(EA,使用最多16个周期),我们就可以试用我们的新内核了。快速测试表明,我们显然还没有做完。E.T.仍然在屏幕右侧失去分辨率!还记得那个没用的SEC吗?我们需要它将GRP1的更新延迟到1034,直到我们完成游戏区域的绘制。修复很简单,只需将其与WYSNC调换(电话号码为1036)即可。

1034:STA WSYNC;3;85 02 1036:STY组1;3;84 1C。

那好多了,但我们还没有做完。埃利奥特仍然不能让外星人复活,我们仍然需要用脚触摸手机部件,这意味着我们仍然需要把井里的物体推下几根绳子。我们还引入了一个新问题:外星技术在着陆或起飞时并不以飞船为中心。至少那个很容易修好。我们需要做的就是调整飞船着陆(0B40)和起飞(07ED)的起始位置:

0B40:LDA#$EF;2;A9 EF 07ED:SBC#$04;2;E9 04。

那么,为什么埃利奥特能够重振外星人呢?为了让外星人复活,埃利奥特需要摸他。不幸的是,外星人已经出局了,精灵比普通的外星人要短得多,并且我们在最后一条线下面清除了碰撞锁闩。埃利奥特被安置在哪里并不重要,我们选通CXCLR后,他将永远不会碰外星人,因为他将没有什么可碰的。

我们可以做几件事。当我们计算位置以清除防撞闩锁时,不是加7,我们可以做明智的事情,添加E.T.的高度和位置,然后减去2,然后将其存储在8B中。那样的话,我们就可以让埃利奥特来品尝短得多的E.T.雪碧了。不幸的是,这是行不通的。即使我们将计算E.T.英尺上方位置的代码移到0BF6处的10个空闲字节(无论如何我们都会这样做),我们也需要8个字节来计算数学,需要3个字节才能跳到0CAB。那是11个字节。我们的运气不会持久,因为似乎没有任何无用的字节可供备用。尽管如此,还是让我们改变一下常规,为我们酷炫的新功能做准备吧。

002A:JMP$BBF6;3;4C F6 BB 0BF6:LDA$9C;3;A5 9C 0BF8:ADC 07;3;69 07 0BFA:STA$F6;3;85 F6 0BFC:JMP$BCAB;3;4C AB BC。

(等一下!当我们的新内核期望将结果存储在8B时,为什么我们要将结果存储在F6?这是有原因的,我们很快就会看到。)

我们可以交替检查E.T.是否已昏迷,并跳过选通CXCLR。同样的技巧也适用于手机部件,通过检查E.T.是否处于水井中。当然,我们绝对不可能在1023做其他检查的同一地点做这件事。这应该不是问题,因为我们真的只需要做一次这些检查,因为它们不依赖于外星人的垂直位置。那很好,但是我们怎么跳过清除防撞门闩呢?连一张额外的支票都放不下。

我们可以修改我们的计算值以指向其他位置。将其设置为0将是理想的,但是这个过程又是什么理想呢?我们还需要一个地方装满我们的支票。一次检查通常要花费我们整整4个字节,其中两个用于设置标志,另外两个用于分支。这会使我们看起来至少需要10个字节(另外2个字节等于08B)。当然,我们没有10个可用字节加上1或3个额外的字节用于返回。不是任何地方。

现在,我们在1013处有8个字节,用于计时,只是恳求在我们的内核中使用。问题是,我们不仅需要将我们的例程压缩到8个字节,我们还必须消耗12到16个周期(不多也不少)来保持我们的时间正确。

D9通常是0,但当我们在井上盘旋时,D9设置为40,当我们在井底时,D9设置为20,当我们落入井中时,D9设置为80。

当E.T.的垂直位置为0时,他永远不会收集任何东西,也不会掉进井里。

我们仅在母船起飞或着陆时在第一条扫描线上绘制GRP0。

这意味着,出于所有实际目的,我们在1013的定时代码将始终在第一个扫描线上调用。

我们能用这个做什么呢?将D9和E3添加到8B通常不会执行任何操作。如果我们在井底,D9将被设置为20,E3将被设置为0(如果E.T.是健康的),C0(如果不是),这意味着8B将在10(+16)或50(+80)左右,在任何情况下,都远远超出我们关心的区域。(它将远远高于东部时间或低于游乐区)。如果我们不在井中,D9将为0,并且将C0(-64)加到任何可能的E.T.位置将始终得到FA(-6)到C0(-64)范围内的值。

这太棒了。我们可以在8个字节和恰好12个周期内完成该操作(这是我们需要刻录的最小值)。唯一的问题是我们的小例程会被调用不止一次。我们不能一直修改8B,然后抱着最好的希望。简单的解决方案是将计算值的原始副本保存在不同的位置,使用该值运行我们的例程,并将结果存储在8B中。(这就是当我们移动例程来计算我们想要选通CXCLR的位置时,我们将计算值存储在F6而不是8B中的原因。)。我们的小套路看起来像这样:

1013:ORA$D9;3;05 D9 1015:ADC$E3;3;65 E3 1017:ADC$F6;3;65 F6 1019:STA$8B;3;85 8B。

为什么是ORA而不是LDA?此时,A将始终为0,因此效果将是相同的。由于代码中散布着一些不必要的隐晦的东西(比如美国证券交易委员会就像一个NOP),它似乎符合华为的风格。

更新:AtariAge用户iesposta注意到,如果E.T.同时触摸一口井和一块糖果,他就有可能掉进井里。这只发生在一个地方(屏幕上有V&34;形状的井),你必须排得近乎完美。我们可以通过把糖果稍微往下推一点来防止这种情况的发生。

我们成功了!除了我们想要做的改变外,这款游戏现在几乎和原版完全一样了。血迹是唯一的例外,但为了..。

正如承诺的那样,我们将使我们的困难解决成为可选的。熟练的外星人将不再需要放弃我们急需的改变来换取更具挑战性的游戏。这两个难度开关都已使用,但B&;W/Color开关未使用。我们要做的就是找个地方放我们的例行公事。

我们需要首先修改旧的困难修复,以便从内存中的一个字节读取,而不是显式的1或0。这是最容易的部分。我们将无缘无故地使用F8。

0707:LDY$F8;3;A4 F8 071B:LDY$F8;3;A4 F8 0685:LDY$F8;3;A4 F8

在SWCHB中,颜色开关的状态为位3。我们需要将其加载到内存中,关闭位3的掩码,并对照该位检查状态。当然,我们没有足够的空间来做所有这些事情并将其储存起来。对于显而易见的例程,我们需要13个字节,如果我们神奇地找到了一个不需要劫持跳跃的地方来填充我们的例程。这是不会发生的。

我能管理的最小例程是这样工作的:读取SWCHB,关闭位3的掩码,右移三次,存储结果。这将占用10个字节,不包括返回跳转。即使我们有10个字节,这也意味着在默认情况下困难修复将是关闭的(开关设置为COLOR),这并不是最佳的。

我们看起来是10字节,但实际上是8字节,在0FF0处是空闲的,我们过去使用9字节例程来计算位置以清除冲突锁存器。(我不知道我们是怎么逃脱惩罚的。)。我们还有8个字节来设置M0';在0B4D的血腥结局场景的水平和垂直位置。如果我们从一个小组换到另一个小组,可能就足够了。

我们可能需要使用这些字节中的6个来进行跳跃,一次是在我们的开放区域之间切换,一次是完成我们劫持的跳跃。幸运的是,就在我们位于0FF0的8个空闲字节之前的例程以RTS结束。事实证明,这就是减少外星人能量的例行公事。这是一点好运气,对我们解决困难来说也是一场不错的比赛。让事情变得更好的是,在我们另外8个字节的末尾有一个RTS(它设置了M0;#39;的位置)。有了16个字节和现成的返回值,我们就可以执行我们的例程,交换Color和B&;W设置的功能(这样颜色很容易),所有这些都有一个字节可用。

我们将在0FEF处重写RTS,以允许例程继续进入我们的例程,抓取SWCHB,并在跳转之前关闭位3的掩码。我们可以在这里做一次LSR,但我们会把它留到跳跃之后,以便在0B55与现有的RTS进行常规对阵。

0FEF:LDA SWCHB;4;AD 82 02 0FF2:AND#$08;2;29 08 0FF4:JMP$BB4E;6;4C 4E BB。

我们需要使用一个字节来结束运行旧例程的检查,这样我们的例程才不会意外运行。这就是为什么我们将在0B4E而不是0B4D进入第二部分。

0B4D:RTS;6;60 0B4E:LSR;2;4A 0B4F:LSR;2;4A 0B50:LSR;2;4A 0B51:EOR#$01;2;49 01 0B53:STA$F8;3;85 F8

如果B&;W/Color开关处于颜色位置,则SWCHB的位3为1,否则为0。0B51处的EOR将反转,因此当开关设置为Color而不是1时,我们将0存储在F8中。

外星人有个臭名昭著的臭名昭著的地方。使游戏几乎无法玩的错误。这根本不是真的。实际上并没有那么多的错误,而且似乎只有一个会影响正常的游戏。

有几个经常被引用的错误,好吧,根本就不是错误。在我们开始之前,让我们澄清一下最常见的非bug:

误区:一些游戏状态变量在开始新游戏时没有清除,科学家和联邦调查局特工在模式3下开始新游戏后的出现就证明了这一点。

这根本不是真的。不管游戏模式如何,科学家和联邦调查局特工总是会出现。在每种模式下,都会显示所有人返回各自的建筑--包括模式3,甚至在开机后也是如此。当然,不同的是,在模式3中,他们永远不会离开。

这只是对两个计时器所代表的意义的误解。计时器需要64个滴答声才能完成。每8个刻度,就有一段消失。第二个定时器是第一个定时器最后一部分的特写镜头。它每勾选一次,就会勾选出它的八个部分中的一个。这就好像第一个计时器是分钟,第二个计时器是秒。实际上只有一个64刻度的计时器,当我们走到最后(最后一分钟&34;)时,我们只会看到更高精度的(";秒&34;)视图。

谣言:埃利奥特能唤醒E.T.的次数是错误的/可以被利用来获得和额外的复活。

这根本不是真的。手册规定埃利奥特每场比赛可以和外星人合并三次。这是正确的。它还说,然而,在一轮比赛中,外星人可能会遇到藏在井底的枯萎的花朵。如果外星人正在复活花朵,埃利奥特就有能力与外星人多融合一次。";这也是正确的。正如您从描述中预期的那样,额外的合并/生命会累积起来,尽管手册中并没有明确说明这一点。

简而言之,没有与Elliott可以唤醒E.T.的次数相关的错误。(无论如何,在任何正常的游戏情况下都不会。如果你积累了127条以上的生命,埃利奥特就不会复活外星人。我怀疑这种情况从未发生过!)。

错误1:在困难模式中,当飞船着陆时,埃利奥特被允许出现在屏幕上,飞船粉碎了埃利奥特。

发生此错误的原因是,在将船的位置设置为开始着陆动画后,船的位置立即被埃利奥特的位置覆盖。修复很简单,只需跳过在我们开始初始化船舶登陆序列之后更新当前屏幕上对象位置的代码。

错误二:当你离开右边的森林时,和离开左边的城市时,你总是掉进一口井里。

这也是一个很容易解决的问题。当从城市屏幕向左退出时,坠落修复会自动修复错误。对于森林,我们只需要在下一个屏幕上稍微调整一下外星人的开始位置。玩家将向右推操纵杆,所以我们只需将E.T.稍微向上和向右移动一点,以避免立即掉入屏幕上有八个凹坑的顶部中心井中。E.T.的起始位置是从表中读取的,因此我们只需要更新几个值:

我们将把E.T.向左移动16个单位,并从原来的位置向上移动3个单位。

这在很大程度上是始终如一的,但它非常令人困惑。它甚至连手册都比不上。然而,还有一些更严重的问题。收集超过31块糖果会导致错误,通常会导致著名的忍者外星人错误。首发能量惩罚和糖果奖金也是不正确的。

得分代码还决定着其他事情,比如E.T.在开始一场新游戏时的精力,所以我们需要小心做出剧烈的改变,以确保我们不会不小心打破任何东西。当然,代码也是一团糟,所以我们将做一些相当戏剧性的改变。

现在,糖果外星人带上船的积分,超过一定数量的糖果的奖励积分,以及能源惩罚都是通过查表来决定的。通常,查找表用于节省ROM空间、时间或两者兼而有之,以避免复杂的计算或提供更准确的计算。这些桌子只是浪费空间,因为时间在这里不是一个重要的因素。更糟糕的是,只有外星人的糖果得分正确!我们将删除这些糟糕的表,并更好地利用这些字节。

目前,计分遵循以下顺序:结束动画开始后,每单位剩余能量给你1分,然后每一块糖果带到船上就给你490分。然后,动画中咀嚼糖果的部分开始,埃利奥特持有的每一颗糖果将为你净赚770分;在动画播放期间,一次一个。当新一轮开始时,你收集的糖果超过一定数量(21颗,而不是手册中规定的31颗)就会得到加分,能量惩罚决定了你这一轮的开始能量。

我们要改变这一点。我们将以同样的方式开始,每单位剩余能量给1分。然后我们将把外星人的能量设置为9999。随着咀嚼动画的运行,我们将扣除惩罚的能量,为收集的糖果增加一定数量的加分,并将收集的糖果的正常490或770分添加到您的分数中。

能量惩罚规则还会在新游戏开始时设置外星人的能量。我们需要记住,新游戏开始时也会调用开始新一轮的代码,并添加一些代码以确保E.T.在新游戏开始时的启动能量设置为9999。

修正评分代码也会修正忍者外星人的错误。由于它已经成为一个流行的复活节彩蛋,如果不是故意的话,我们将把它作为一个有意的效果添加到游戏中。(当然,没有其他奇怪的文物)。

既然我们正在修改分数,那么让分数与手册相匹配是有意义的。这意味着我们还需要对一轮中能找到多少糖果做一个小小的改变,这样你就可以有意义地收集超过31块。从技术上讲,这里有一个错误,因为显示哪些井屏幕上目前有糖果的旗帜永远不会被清除。在这一轮中可以找到多少额外糖果的计数器通常不会达到零。我们会把这个放在一边,因为它给你能找到的糖果数量增加了一点伪随机性。

等。什么?与一轮比赛中出现的糖果数量相比,有效地有+4或-3的潜力。别忘了上一轮落在地上的糖果没有被清理干净,上一轮留在地上的糖果可能多达四块。那些不会从柜台中扣除的糖果,实际上增加了多达四块的潜在糖果数量。-3有点复杂。当糖果出现时,它会出现在每口井的屏幕上。DC处的值减去将糖果添加到每个屏幕所需的糖果数量。(如果所有四个屏幕都需要糖果,则DC减少四个。如果三个屏幕需要糖果,则DC减少三个,以此类推。)。根据DC的说法,如果没有足够的糖果放在每个屏幕上,那么就不会添加糖果。这意味着DC可以在3、2或1处卡住。(当DC小于4时,如果所有四个井屏幕都需要糖果,则不添加糖果。如果三个屏幕在DC小于3时需要糖果,则不会添加糖果,以此类推。)。如果你想最大限度地增加你能收集到的糖果数量,那么一次只收集一颗糖果,当你接近最大值时,让糖果在中间补充。

不作进一步的阐述,让我们开始吧。我们要做的第一件事是对我们完成一轮后立即运行的代码进行更改。我们感兴趣的代码初始化下一轮剩余糖果的计数器,并将收集到的糖果合计(Elliott手中的糖果加上Wha

..