ZX Spectrum中的POKE

12
我正在使用一台旧的ZX Spectrum 48k玩耍,想知道如何输入POKE codes。您需要通过磁带加载游戏,然后在某种程度上打破程序,键入POKE语句,然后重新启动程序吗?我已经搜索了很多但是没有找到确切的方法,所以任何关于此事的线索将不胜感激。
5个回答

11

首先,PEEK和POKE的意义:

10 let x = PEEK 40000: REM returns (reads) the value (0-255) in position 40000
20 POKE 40000, 201: REM writes the 201 value in position 40000

大多数程序加载一个名为“loader”的小BASIC程序。它类似于:

10 cls
20 print "Loading AWESOME GAME!!!"
20 load "" screen$
30 load "" code 40000
40 randomize usr 40000

这段代码的意思很明显:加载一个屏幕演示(第20行),以在汇编程序(游戏本身)加载时保持用户的娱乐,最后启动游戏(第40行)。

关于第40行,“usr 40000”是调用地址为40000处的汇编表达式。指令“Randomize”只是初始化随机种子,供“rnd”使用,但它实际上永远不会返回。

因此,首先尝试的方法是:

  1. 按下“break”键(相当于Ctrl+C),输入“list”,并将poke放在第35行,即在程序加载后但尚未执行时。

  2. 不要键入“load \\"\\"”以启动游戏,而要键入“merge \\"\\"”(这也可以用来将存储器中的基本程序与磁带中的程序合并)。该过程将在执行加载器之前停止。当加载器包含禁用BREAK的poke指令时,这非常有用。

这些方法的问题在于,一开始隐藏加载器内部的尝试是幼稚的(例如,在第10行包括“PAPER 0: INK 0”指令或类似于此的内容,使所有东西暂时消失),但很快就会变得复杂得多,一直到实际上成为包含在REM指令中的汇编程序。

下一步是分析基本加载器之后加载的汇编代码的头文件,确定转储地址和代码长度,并创建自己的加载器,其中可以包含所需的poke指令。许多杂志分发这些类型的加载器,旨在在原始加载器之前加载(加载器查找特定块,绕过原始基本加载器)。

因此,开发人员决定在磁带中不包括汇编块的头信息,以及保护加载器。或包括一个只加载替换ROM中加载器的汇编程序的加载器,使用不同的速度,无头信息等。或包括一个加载头部块的加载器,其中包括演示屏幕和游戏代码。

然后出现了一些特殊的硬件,比如Multiface-1。通过阅读Multiface-1手册,您可以看到如何通过按下红色按钮(触发非屏蔽中断)调用multiface的软件(包含在外围硬件的ROM中),会显示一个菜单,允许您保存此时的内存(并且保存的代码将不受任何保护,因此打开了创建自己的loader并使用poke的路径),甚至检查(PEEK)指定地址的当前值并直接输入POKE(通过这种方式,您可以找到那些减少生命的例程的开头,例如,在游戏中失去一条命的程序)。 POKE指令通常是这样的(这是一种简化形式):POKE addr,0POKE addr,201。数字addr是减少可用生命数量或检测与敌人冲突的例行程序的开始。
代码0是汇编NOP(无操作)指令。在NOP期间,CPU不执行任何操作。
代码201C9是汇编RET(返回)指令,表示从子程序返回。在BASIC中,您可以使用GOSUB调用子程序,并使用RETURN从其末尾返回。在汇编语言中,相同的程序对是CALL/RET
如果您有一个201,那么它实际上意味着一个子程序(例如减去一个生命),如下所示:
9950 let lives = lives - 1
9960 return

被转换成了:

9950 return
9960 return

如果您的值为0,则会将相同的例程转换为:

9950
9960 return

1
我有一个多面板扩展卡,绝对是个改变游戏规则的东西,特别是在+3上——你可以将RAM转储到磁盘并恢复状态(就像现代PC上的休眠功能)。 - John Hunt

3
在ZX Spectrum杂志上打印的POKE代码通常需要您拥有插件硬件设备(例如Multiface)。游戏加载后,您可以按下Multiface按钮来暂停游戏,输入POKE,然后返回到游戏中。
如果没有特殊设备,您需要使用其他答案所述的加载程序。您需要加载初始的小型加载程序,然后BREAK进入代码。如果幸运的话,代码将使用RANDOMIZE USR调用执行一些简单的操作来加载其余的游戏,然后执行实际的机器码游戏。在这种情况下,您可以修改加载程序,使其在游戏加载后但在游戏开始之前进行POKES。
然而,许多游戏使这很困难,因为它们包含自定义加载器代码。这通常是嵌入到小型BASIC程序中的机器码,以REM语句的形式编写。机器码将加载游戏并执行它,并且因为它们从不将控制权返回给BASIC代码,所以没有机会输入POKE。如果您足够专注,可以尝试修改机器码,使其要么将控制权返回给BASIC以便您可以POKE,要么通过机器码调用执行POKE。这很困难,因为如果我记得正确,编辑器会混淆包含非可打印字符的REM语句中的行。有一些软件工具(如RoyBot)可以帮助您修改内存中的代码。
一些游戏开发人员为防止游戏黑客而做出了非常疯狂的事情,例如实现加载器代码,其在执行时实际上覆盖了自己的代码。

2
大多数 Spectrum 程序使用两步过程来启动游戏:
  1. 加载并运行一个小的 BASIC 程序
  2. 该小的 BASIC 程序然后加载更长的机器码,然后跳转到机器码的入口点(例如 RANDOMIZE USR 28455)。
如果你能在这些步骤之间停止,你可以用 POKE 来进行操作(增加生命值等),然后使用 RANDOMIZE USR 28455 启动机器码,假设你已经找到了正确的地址。
一旦机器程序正在运行,通常就没有办法停止它并返回到 BASIC 解释器。除非机器程序提供了某种明确的(或无意的)方法来这样做。

值得注意的是,有时加载程序会包括嵌入在REM语句中的自定义加载器代码。在这种情况下,机器码将在游戏的其余部分加载之前执行,并且控制权永远不会返回到BASIC,因此在这种情况下没有机会执行POKE操作。 - Tom

1
我记得很久以前... 当一个 Spectrum 游戏加载时,它最初会加载一个小的加载程序并运行它,磁带继续播放并加载大部分程序。加载程序中的最后一条命令然后发出 poke 命令调用已加载的所有内容并启动游戏。因此,据我记得,您必须在加载程序加载完毕后暂停磁带,并停止代码行自动发出最后一个 poke,然后继续。然后,一旦主体程序加载完成,您需要从命令行发出 poke 命令,然后发出原始的 poke 命令来启动游戏。加载程序将在第一组红色和蓝色线之后加载,接着是非常短的黄色和蓝色线(据我记得在这一点上打印程序名称)。停止磁带,按 Break 键,然后按 List 查看代码。祝好运,问题不错!

1
作为找到正确poke的一种解决方法,加载并中断程序后,您可以搜索诸如以下命令:
LD A,3

在一款拥有3条生命的游戏中,该命令的十六进制代码为:
3E 03 -> in hex
62 3  -> in decimal

搜索这些数据并将03更改为255,例如(255是允许的最大值)。然后进行测试。

2
值得一提的是,寻找一个 DEC(HL) 并用 NOP 覆盖它通常会给你无限生命。我认为在《疯狂矿工》中找到正确的 DEC(HL) 是整个代码中的第六个。 - bodgesoc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接