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.
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 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
, thought it will actually never return.
So, the first tries would be:
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.
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 were that at first the attempts to hide the loader inners 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 this kind 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 diminishing 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 for a subroutine. In BASIC, you would call a subroutine with GOSUB
and return from its end with RETURN
. In asssembly, the same pair is CALL/RET.
If you had a 201, then it would effectively mean that a subroutine (say subtracting one to 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 was transformed to:
9950
9960 return
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With