我正在玩旧的 ZX Spectrum 48k,我想知道如何准确地输入POKE 代码。
您用磁带加载游戏 - 然后以某种方式打破 POKE 语句中的程序类型并再次开始运行程序?
我已经对此进行了大量搜索,但无法准确找到这是如何完成的,因此将不胜感激任何线索。
我正在玩旧的 ZX Spectrum 48k,我想知道如何准确地输入POKE 代码。
您用磁带加载游戏 - 然后以某种方式打破 POKE 语句中的程序类型并再次开始运行程序?
我已经对此进行了大量搜索,但无法准确找到这是如何完成的,因此将不胜感激任何线索。
首先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
,然后将 pokes 放在第 35 行,即,一旦程序已加载但尚未执行。
load ""
不是为了启动游戏而打字,而是键入merge ""
(这是用来将内存中的基本程序与磁带中的程序结合起来的)。该进程将在执行加载程序之前停止。poke
当加载程序包含禁用 BREAK的指令时,这很有用。
这些方法的问题在于,起初隐藏加载器内部的尝试是幼稚的(例如在第 10 行包含 PAPER 0: INK 0 指令或类似的东西,使所有内容暂时不可见),但很快他们就会得到一个更复杂,直到实际上是包含在REM
指令中的汇编程序。
下一步是分析在基本加载器之后加载的汇编代码的标头,得出转储地址和代码长度,并创建您自己的加载器,您可以在其中包含poke
您想要的指令。许多杂志发行了这种装载机,它们打算在原来的装载机之前装载(装载机寻找特定的块,绕过原来的基本装载机)。
因此,开发人员决定将组装块包含在没有标题的磁带中,并保护加载器。或者包括一个加载程序,它只加载一个替代 ROM 中加载程序的汇编程序,使用不同的速度,没有头信息等。或者包括一个加载无头块的加载程序,包括演示屏幕和游戏代码。
然后出现了Multiface-1等特殊硬件。阅读Multiface-1 手册,您可以看到如何通过按下红色按钮(引发 NMI(非屏蔽中断))调用 multiface 的软件(包含在外围硬件的 ROM 中),此时显示的菜单允许您保存内存(并且保存的代码将没有任何保护,因此打开了使用戳创建自己的加载程序的路径),甚至检查 ( PEEK
) 内存中特定地址的当前值并POKE
直接输入 's (您可以通过它找到开头例如,那些会减少你的生活的例行公事)。
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 以开始游戏。加载程序将在第一组红线和蓝线之后加载,然后是屏幕上非常短的黄线和蓝线(我记得它会打印此时找到的程序的名称)。停止磁带,按 Break,然后按 List 以查看代码。
作为找到正确 poke 的解决方法,在加载并中断程序后,您可以搜索以下命令:
LD A,3
在开始时有 3 条生命的游戏中。此命令的十六进制代码是:
3E 03 -> in hex
62 3 -> in decimal
例如,搜索此数据并将 03 更改为 255(255 是最大允许值)。然后测试它。