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:
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 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