【发布时间】:2016-06-04 08:44:44
【问题描述】:
我正在玩旧的 ZX Spectrum 48k,我想知道如何准确地输入 POKE codes。
您使用磁带加载游戏 - 然后以某种方式中断 POKE 语句中的程序类型并再次开始运行程序?
我已经对此进行了很多搜索,但无法准确找到这是如何完成的,因此我们将不胜感激任何线索。
【问题讨论】:
标签: zxspectrum
我正在玩旧的 ZX Spectrum 48k,我想知道如何准确地输入 POKE codes。
您使用磁带加载游戏 - 然后以某种方式中断 POKE 语句中的程序类型并再次开始运行程序?
我已经对此进行了很多搜索,但无法准确找到这是如何完成的,因此我们将不胜感激任何线索。
【问题讨论】:
标签: zxspectrum
首先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 行)以在加载汇编程序(游戏本身)(第 30 行)时让用户保持娱乐,最后启动游戏(第 40 行)。
关于第 40 行,usr 40000 是发挥作用的表达式,它在位置 40000 处调用程序集。指令 Randomize 只是初始化 rnd 使用的随机种子,以为它实际上永远不会返回。
所以,第一次尝试是:
按“break”(或多或少相当于 Ctrl+C),输入list,然后将戳放在第 35 行,即一旦程序已加载但尚未执行。
不要键入load "" 来启动游戏,而是键入merge ""(这用于将内存中的基本程序与磁带中的程序结合起来)。该进程将在执行加载程序之前停止。这在加载程序包含禁用 BREAK 的 poke 指令时很有用。
这些方法的问题在于,起初隐藏加载器内部的尝试是幼稚的(例如在第 10 行包含 PAPER 0: INK 0 指令或类似的东西,使所有内容暂时不可见),但很快他们会变得更加复杂,直到实际上是包含在 REM 指令中的汇编程序。
下一步是分析在基本加载程序之后加载的汇编代码的标头,得出转储地址和代码长度,并创建您自己的加载程序,您可以在其中包含您想要的poke 指令。许多杂志发行了这种装载机,它们的目的是在原来的装载机之前装载(装载机寻找特定的块,绕过原来的基本装载机)。
因此,开发人员决定将组装块包含在没有标题的磁带中,并保护加载程序。或者包括一个加载程序,它只加载一个汇编程序,该程序替代 ROM 中的加载程序,使用不同的速度,没有标题信息等。或者包括一个加载无头块的加载程序,包括演示屏幕和游戏代码。
然后出现了Multiface-1等特殊硬件。阅读Multiface-1 manual,您可以看到如何通过按下红色按钮(引发 NMI(非屏蔽中断))调用 multiface 的软件(包含在外围硬件的 ROM 中),此时显示的菜单允许您保存内存(并且保存的代码将没有任何保护,从而打开了创建自己的加载程序的路径),甚至可以检查(PEEK)内存中特定地址的当前值并直接输入POKE(其中例如,你可以找到这些例程的开始,这些例程会减少你的生命)。
POKE 的指令通常是这样的(这是一种简化):POKE addr, 0 或 POKE addr, 201。 addr 数字是减少可用生命数量或检测与敌人冲突的例行程序的开始。
代码0是汇编NOP(无操作)指令。在 NOP 期间,CPU 什么都不做。
代码201或C9是汇编RET(返回)指令,意思是返回一个子程序。在 BASIC 中,您将使用 GOSUB 调用子程序并使用 RETURN 从其末尾返回。在汇编中,同一对是 CALL/RET。
如果你有一个 201,那么它实际上意味着一个子程序(比如从你的生命中减去一个),例如:
9950 let lives = lives - 1
9960 return
被转化为:
9950 return
9960 return
如果您有一个 0 值,则相同的例程将转换为:
9950
9960 return
【讨论】:
印在 ZX Spectrum 杂志上的 POKE 代码通常要求您拥有一个插件硬件设备(例如 Multiface)。游戏加载完成后,您可以按 Multiface 按钮停止游戏,输入 POKE,然后返回游戏。
没有特殊设备,您需要使用加载程序,如其他答案所述。您需要加载初始的小型加载程序,然后 BREAK 进入代码。如果幸运的话,代码将在游戏的其余部分加载,然后使用 RANDOMIZE USR 调用执行实际的机器代码游戏。在这种情况下,您可以修改加载器 BASIC 程序,使其在游戏加载后但在游戏开始之前执行 POKES。
但是,许多游戏都很难做到这一点,因为它们包含自定义加载程序代码。这通常是用嵌入到 REM 语句中的小型 BASIC 程序中的机器代码编写的。机器代码将加载游戏并执行它,因为它们永远不会将控制权返回给 BASIC 代码,所以没有机会进入 POKE。如果您足够专注,您可以尝试修改机器代码以将控制权返回给 BASIC,以便您可以 POKE 离开,或者通过机器代码调用执行 POKE。这很难,因为如果我没记错的话,编辑器曾经在 REM 语句中打乱包含不可打印字符的行。有像 RoyBot 这样的软件工具可以帮助您修改内存中的代码。
一些游戏开发者做了一些非常疯狂的事情来防止游戏被黑客入侵,例如实现加载器代码,实际上在执行时覆盖了自己的代码
【讨论】:
大多数 Spectrum 程序使用两步流程来开始游戏:
RANDOMIZE USR 28455)。如果你能设法在这些步骤之间停下来,你可以 POKE 左右(以增加生命的数量,...)然后用 RANDOMIZE USR 28455 启动机器代码,假设你以某种方式找到了正确的地址.
一旦机器程序运行,通常无法停止它并返回 BASIC 解释器。除非机器程序提供了一些明确(或无意)的方式来这样做。
【讨论】:
我记得很久以前......当一个 Spectrum 游戏加载时,它最初会加载一个小的加载程序,然后运行它,磁带继续,大部分程序被加载。最后一个命令然后在加载程序中发出一个 poke 命令,该命令调用所有加载的内容并开始游戏。所以,我记得,一旦加载程序加载,您必须暂停磁带,并停止代码行自动发出最终的 poke,然后继续。然后,一旦批量加载,您就可以从命令行发出您的 poke,然后是原始 poke 以开始游戏。加载程序将在第一组红线和蓝线之后加载,然后是屏幕上非常短的黄线和蓝线(我记得它会打印此时找到的程序的名称)。停止磁带,按 Break,然后按 List 以查看代码。祝你好运和好问题!
【讨论】:
作为找到正确 poke 的解决方法,在加载并中断程序后,您可以搜索以下命令:
LD A,3
在开始时有 3 条生命的游戏中。该命令的十六进制代码为:
3E 03 -> in hex
62 3 -> in decimal
搜索此数据并将 03 更改为 255(例如,255 是最大允许值)。然后测试一下。
【讨论】: