POKE in ZX Spectrum
Asked Answered
A

5

13

I'm playing with an old ZX Spectrum 48k and I'm wondering how exactly it is possible to enter POKE codes.

You load a game with a tape - then somehow break out of the program, type in the POKE statements, and start running the program again?

I've done a lot of searching on this but haven't been able to find exactly how this is done so any leads on this would be greatly appreciated.

Amerind answered 4/6, 2016 at 8:44 Comment(0)
I
11

First of all, the meaning of PEEK and 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

Most programs loaded a small BASIC program called a loader. It was something like:

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

The meaning should be straightforward: load a screen presentation (line 20) to keep the user entertained while the assembler program (the game itself) loads (line 30), and finally launch the game (line 40).

About line 40, usr 40000 is the expression that does the trick, calling assembly at position 40000. The instruction Randomize just initializes the random seed used by rnd, though it will actually never return.

So, the first tries would be:

  1. Press "break" (more or less equivalent to Ctrl+C), enter list, and put the pokes in line 35, i.e., once the program has been loaded but it has not been executed yet.

  2. Instead of typing load "" in order to launch the game, type merge "" (this was used to combine the basic program in memory with the one in tape). The process will stop before executing the loader. This is useful when the loader included a poke instruction that disabled BREAK.

The problem with these methods was that at first the attempts to hide the loader innards were naive, (such as including a PAPER 0: INK 0 instruction or something like that at line 10, making everything temporarily invisible), but soon they would get a lot more complex, up to the point to actually be an assembler program included in REM instructions.

The next step was to analyze the headers of the assembly code loaded after the basic loader, conclude the dump address and the length of the code, and create your own loader in which you could include the poke instructions you wanted. Many magazines distributed these kinds of loaders, which were intended to be loaded before the original one (the loader looked for specific blocks, bypassing the original basic loader).

So then developers decided to include the assembly blocks in tape without the headers, as well as protecting the loader. Or including a loader that just loads an assembly program that substitutes the loader in ROM, using different speeds, without header information, etc. Or including a loader that loads a headless block including the presentation screen and the code for the game.

And then special hardware such as the Multiface-1 appeared. Reading the Multiface-1 manual you can see how invoking multiface's software (included in the peripheral hardware's ROM) by pressing a red button (which provoked a NMI (not masked interruption), a menu was shown allowing you to save the memory at that point (and the saved code would be free of any protection, thus opening the path to create your own loader with pokes), or even examine (PEEK) current values at specific addresses in memory and enter POKE's directly (with which you could find the beginning of those routines, for example, that diminish your lives in one).

The POKE's instructions were usually of the kind (this is a simplification): POKE addr, 0 or POKE addr, 201. The number addr was the beginning of a routine decreasing the number of lives available, or detecting the clash with an enemy.

The code 0 is the assembly NOP (no operation) instruction. During a NOP, the CPU does nothing.

The code 201 or C9 is the assembly RET (return) instruction, which means to return from a subroutine. In BASIC, you would call a subroutine with GOSUB and return from its end with RETURN. In assembly, the same pair is CALL/RET.

If you had a 201, then it would effectively mean that a subroutine (say subtracting one from your lives) such as:

9950 let lives = lives - 1
9960 return

was transformed to:

9950 return
9960 return

If you had a 0 value the same routine it was transformed to:

9950
9960 return
Ivy answered 7/6, 2018 at 8:4 Comment(1)
I had a multiface.. absolute game changer, especially on the +3 - meant you could dump the ram to disk and restore state (like hibernate on a modern PC)Sherillsherilyn
D
4

The POKE codes printed in ZX Spectrum magazines usually expected you to have a plug-in hardware device (e.g. Multiface). Once the game loaded, you could press the Multiface button to halt the game, enter the POKEs, and then return back to the game.

Without a special device, you need to play with the loader programs, as described by the other answers. You need to load the initial small loader program and then BREAK into the code. If you're lucky, the code will do something simple with loading in the rest of the game and then execute the actual machine code game using the RANDOMIZE USR call. In this case you can modify the loader BASIC program to do the POKES after the game has loaded but before the game is started.

However, many games make this hard because they include custom loader code. This is often written in machine code embedded into the small BASIC program in REM statements. The machine code will load the game and execute it, and because they never return control to the BASIC code, there is no opportunity to enter the POKEs. If you are dedicated enough, you could try modifying the machine code to either return control back to BASIC so you can POKE away, or else perform the POKEs via machine code calls. This is quite hard, because if I remember rightly, the editor used to scramble lines containing non printable characters in the REM statements. There were software tools like RoyBot that could help you with modifying code in memory.

Some game developers did really crazy stuff to prevent game hacking, such as implementing loader code that actually overwrote its own code while it was being executed

Despain answered 22/5, 2017 at 16:0 Comment(0)
C
2

Most Spectrum programs use a two step process to start a game:

  1. Load and run a small BASIC program
  2. That small BASIC program then loads much longer machine code and then jumps to the machine code's entry point (e.g. RANDOMIZE USR 28455).

If you can manage to stop between those steps, you can POKE around (to increase the number of lives, ...) and then start the machine code with RANDOMIZE USR 28455, assuming you somehow found out the correct address.

Once a machine program is running there is usually no way to stop it and get back to the BASIC interpreter. Unless the machine program provides some explicit (or inadvertent) way to do so.

Crewelwork answered 4/6, 2016 at 9:53 Comment(1)
Worth noting that sometimes the loader program would include custom loader code embedded as machine code in REM statements. In this case the machine code would be executed before the rest of the game is loaded, and control never returned back to BASIC so there is no opportunity to execute POKEs in that case.Despain
T
1

As I recall from a long time ago.... When a Spectrum game loads, it initially loads in a small loader program, and runs that, the tape continues and the bulk of the program is loaded in. The last command in the loader program then issues a poke command which calls everything loaded and starts the game. So, as I remember, you have to pause the tape once the loader program has loaded, and stop the line of code from automatically issuing the final poke, then continues. Then once the bulk has loaded, you issue your poke from the command line, and then the original poke to start the game. The loader program will be loaded after the first set of red and blue lines, followed with the very short yellow and blue lines on the screen (as I recall it prints the name of the program found at this point). Stop the tape, press Break, then List to see the code. Best of luck and great question!

Tetrachord answered 4/6, 2016 at 9:38 Comment(0)
I
1

As a workarround to found the right poke, and after loaded and BREAK the program, you can search for commands like:

LD A,3

In a game with 3 lives at the start. The code in HEX for this command is:

3E 03 -> in hex
62 3  -> in decimal

Search this data and change the 03 to 255 for example (255 is the max allowebd value). Then test it.

Illogicality answered 15/2, 2021 at 15:52 Comment(1)
FWIW looking for a DEC(HL) and overwriting it with a NOP would often give you infinite lives. I think I found the right DEC(HL) in Manic Miner was the sixth of ten in the entire code.Longueur

© 2022 - 2024 — McMap. All rights reserved.