Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

POKE in ZX Spectrum

Tags:

zxspectrum

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.

like image 943
simmons Avatar asked Jun 04 '16 08:06

simmons


2 Answers

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:

  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 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
like image 71
Baltasarq Avatar answered Oct 09 '22 12:10

Baltasarq


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.

like image 21
Duefectu Avatar answered Oct 09 '22 11:10

Duefectu