So You Want to Crack & Train Commodore 64 Games Like a Pro 2

Posts: 11
Joined: Fri Jun 19, 2015 1:42 am
Location: Canada

So You Want to Crack & Train Commodore 64 Games Like a Pro 2

Post by mrnop »

2.5 Other Examples

You’re not ready to form your own training group just yet cowboy.

Sometimes you may encounter a program that doesn’t work with the steps I’ve shown to you. It could be the game is frozen with a cartridge, it could be that there are several ‘layers’ of crunched programs (eg. Someone used ECA Linker first and then crunched that file).

(((((( Pinball Power case study )))))))
Download 3d Pinball from Mastertronic

Now run the program until you see the flashing colours to indicate the game is loading.
Enter your MLM and do the search for where it sets location 1 to $37
H 0000 0800 a9 37 85 01
It finds it at $02ee
At $02ee we see:
:2ee a9 37 lda #$37
:2f0 85 01 sta $01
:2f2 85 c0 sta $c0
:2f4 a9 01 lda #$81
:02f6 8d 0d dc sta $dc0d
:02f9 ee fd 02 inc $02fd

So the loader is setting memory location 1 back to $37, good. It’s also putting $37 into location $c0. It then puts $81 into location $dc0d and then does what we call an INC which is INCREMENT (increases the value of whatever is in $02FD by one).
I don’t see a JMP to start the game…

But just for the heck of it, lets change the last line to JMP (jump) to itself.
In your MLM type:
A 02F9 jmp $02F9
This changes that INC command to now loop to itself. Exit the MLM and wait… once the colors stop flashing as part of the tape loader, we know it’s in the endless loop.

Type ALT-M to enter the monitor (or use your cartridge)
Same process…
1) Turn off the ROM by typing BANK 0 (or change location 1 to a $34 if using a cartridge)
2) Save the data using SP “pinball” 0800 ffff
Or with cartridge something like S “pinball” 08 0800 ffff
3) Reset the computer
4) Load in the raw data
In CCS you go into the MLM and type LP “pinball”
In a cartridge you’ll probably use L “pinball” 08 (and if it crashes it’s because you keep forgetting to turn ROM off by setting location 1 to $34 first)
5) Now we don’t know a start address though because there wasn’t one given… but type LIST. And we see it is indeed a program we can run. And there it is… our pinball game.

Now I can tell you that just by looking at the program, and seeing the screen go blank and noticing the large delay, that this is a crunched program. So we’ve managed to save the game that we ripped from tape – but it’s still crunched. ARGH!!!

Worry not my friend… what did I teach you?

Run the program and BEFORE the game begins but while the screen is blank, go into your MLM, search for the magic bytes
H 0000 0800 a9 37 85 01
It finds those exact bytes at location $01CF:

Disassemble $01cf and you’ll see :
$01e1 JSR 080D
$01e4 jsr $a68e
$01e7 jmp $a7ae

That first JSR is the one we want. How do I know this? Because it’s above $0800 so we know it’s game code, but also those two locations after it are in the $A000 range so we know it’s not game code. That’s BASIC ROM according to the memory map you have printed out and have taped to your wall like I told you to.

Now if you want to get technical – yes, $A68E could be game code as could $A7AE but because the code at $01CF changes the value of location 1 to $37 there is NO WAY that this can be game code. The value of location 1 says “I want the ROM guys to be in their houses, I’m not using them for RAM houses.”. Those two lines of code at $01e4 and $01e7 by the way, perform a simple “RUN” command in machine code.

Soooo again we assemble some code:
A 01E1 jmp $01E1
To create an endless loop, we now exit the MLM and wait… after a few moments we enter the MLM. We check that the PC is at $01e1 telling us it’s currently executing the endless loop.

Again we’re going to type in “BANK 0” (to turn on all RAM) and again we dump the memory to disk with:
SP “pinball2” 0800 ffff
We reset the C64, we go into the MLM, put in the BANK 0 command again and then
LP “pinball2”
Type ‘x’ to exit.

But how do we start the game?

Well every time we create an endless loop it’s because we removed the original JUMP (JMP) that started the game. So to play the game we have to again call that jump which we do from basic with the SYS command. It used to be JMP $080d did it not?

And we know that 080D is 0*4096, 8*256 and D which is 13. that gives us 2061. So a SYS2061 will let you know if you succeeded. I can tell you I’m looking at the game right now, playing… so it works under CCS64.

The file named “pinball2” is the full, raw, unpacked version of the game. It’s ready for training if you so desire.

2.6 Tips and Questions

First let’s clear up some things that may be confusing you.

We use “L” and “S” to load and save using a cartridge based machine code monitor. In CCS64 we use “LP”. Under CCS64 emulator we always type in BANK0 before saving and before loading in our final de-crunched game. Or if we’re using a cartridge, we always set location 1 to $34 before saving or loading the final de-crunched game.


Well if we save a game without typing Bank0 or setting location 1 to $34, we will save what’s in the ROM memory at $A000-$FFF because we didn’t tell the tenants of those ROM houses to leave for the afternoon while we borrow them for RAM purposes. And when it comes to loading in a game, any time you load something that is going into $A0000 or higher, if you haven’t turned on RAM (through the means I keep mentioning to you) then it causes a miserable crash. Want proof? Just start up your C64 and go into the MLM and fill $D000-$Dfff with this command: F D000 DFFF 22
Without setting location 1 to $34, you write to the ROM and it causes a crash.

So always remember to set your ROM banks to RAM when loading or saving. Maybe you can recall the old days when you tried to load in a 220+ block game that was clearly too large. Eventually the screen turned to garbage and it crashed – this was because the game loaded into $D000 and beyond. It was too large for the C64.

Another question you might have…. Why do I have to run the program first and then look for the magic bytes you keep telling me to look for? I tried searching for them before I ran the game and it finds them
H 0800 A000 A9 37 85 01

Yes, because the game has loaded into memory beginning at location $0801 but remember that a de-cruncher will copy itself to lower memory under $0800. So even if you did change a JMP to loop to itself
0840 JMP $1000 is changed to 0840 JMP $0840
It would CRASH because when it gets copied to under $0800 it’s going to look something like 0133 JMP $0840 instead of being “0133 JMP $0133” as it should be

2.7 Tetris “Hello Hacker” loader

If you feel confident in your skills, download Tetris.

Now by looking at the game, it has a loading picture so we’re not even going to bother looking for anything until we run the game and see the loading picture appear. We’d only be dumping the picture to disk if we proceeded at this point.

When the picture appears we’re going to enter the MLM and hunt for the bytes that set location 1 to $37

H 0000 0800 a9 37 85 01
Reveals nothing….

So let’s try a backup plan of searching for just the STA $01 (store “A” into location 1) portion of the code – which is the two op-codes : 85 01

TIP: A9 is the op-code for LOAD A with, and 85 is the op-code for STORE whatever is in A into a memory location. The numbers immediately after each of those is what we’re loading and storing with. So when you see a9 37, those go together in pairs. It says “LOAD A with $37” and the 85 01 says “Store A into location 1”.

So this isn’t good… we have three places where it found a STORE A into location 1.

$018D, $019C and $02AA

Let’s look at $018D with the disassembly of the code:
Type D 018D
Scroll down until you come to the end of the code and you see a JMP $02EB at location $01CD. $2EB isn’t likely to be the game as it’s below BASIC. Maybe it’s part of the loader.

01CD JMP $02EB

Well let’s disassemble that code at $02EB and see what it is…

Type D 02EB to see what we’re jumping to…

Okay at location $02EB we see a series of NOP’s. NOP is like a REM statement in BASIC, it means No Operation – do nothing. But as we follow those NOP’s we see that there’s no code after it, it’s all just BRK’s. It’s not so much the BRK I’m paying attention to as it is that the memory is all zeros. In other words, by seeing a series of zeros this code doesn’t make sense. What’s going to happen is that the game is going to JUMP to $02EB, it’s going to rip through those NOP’s in a hurry because they mean “do nothing” and it’s going to crash into those 00’s (BRK’s). It’s like a train heading down the track with a brick wall at the end…. Clearly those NOP’s are there for a reason and I know what it is. At some point the loader is going to try to trick us and put some code where those NOP’s are. It’s meant to trick us… but we know better because those NOP’s lead to a crash.

In fact if you scroll down beyond those zero bytes to $033C it looks like we have some text here… switch to the graphic view and see what it says
I 033C 03A0
“Wild Save © Interceptor Micros 1987 Written by Andrew Challis January 1987 Hello Hacker I Hope you have fun with this loader”

And back in the days before cartridges, I’m sure this game would have been a challenge. But using our CCS64 emulator we can fix this.

So we don’t know what’s going to happen with the code at $02EB we just know that it will probably change at the last minute in an attempt to trick us.

So let’s change the line that jumps to $02EB.
Type in: A 01CD JMP $01CD

Which will tell the loader to loop instead of going to that code that looks suspicious.

Wait until the loader stops…. And disassemble 02EB

AHA! It did change the code, those sneaky programmers…
The code is simple
LDA #$37
STA $01
LDA #$00
STA $D020
JMP $02A7

So it does set 1 back to $37 (turn ROM on) and changes the border to black (load A with zero, store A into $D020 the border colour). It then Jumps to $02A7

So we disassemble $02A7. I’ll spare you the code, but it basically turns off interrupts and then we see:
JSR $A659
JSR $A533

This brings us to the next “tip” on how to dump games. Any time you see JSR $A659 and JMP $A7AE this is like saying “RUN” in BASIC. In other words, this is going to be a BASIC program.

Just to confirm type
I 0800
And we see “2076 AUSTRO-COMP E1” at $0806 so this is going to be a program that has a line number

The tip I have for you is, when looking at loader code, also look at $0800 visually with the I command (I 0800) and see what’s there. MANY times it’s going to look like “(2064)” or “2061” or some other series of numbers. This could be an indication that this is a program that you just need to RUN. No need to worry about what the JMP is to start the game.

So we’re not going to bother with trying to figure out what the start address is, we’re going to treat this game like any other BASIC program and just save the memory to disk.

Switch out your ROM and save $0800-$ffff
Load it back in and type LIST – there’s a program there. Type RUN and it works! There’s a few glitches on the screen but for a beginner you did pretty well against this loader that was supposed to defeat a ‘hacker’

2.8 Vixen Loader

This time we’re going to try dumping the game Vixen to disk.

We know there’s a loading picture so we’re not going to bother looking for any code until after the loading photo appears.

We do our search
H 0000 0800 a9 37 85 01
No results

Let’s try our backup search:
H 0000 0800 85 01
Three results: $02B8 $03E7 $0431 $04A3

A little overwhelming I’m sure… but if we look at the line $042f we see that
042f LDA #$35
0431 STA $01
0433 JMP $0811

That JMP $0811 looks pretty good to me… let’s loop it.
A 0433 JMP $0433

We wait for the loader to stop, dump $0800 to $FFFF, restart the 64, turn off ROM, load the data back in and SYS 2065 (which is $0811, the address that was at loction $0433 in the loader)

You don’t need my help now. You’re a pro at dumping data. Incidentally the reason it put a #$35 into location 1 instead of a #$37 is most likely because the game uses memory under ROM ($A000).

2.9 Bosconian

Now this is a rather cool idea. In the tape era of the C64, games often took a long time to load. To make the long process easier, companies introduced music that played while the game loaded. This loader allowed you to play Space Invaders while the game loaded.

Download Bosconian from this link

Now run the game… when the border flashes, enter your MLM and search for these bytes:
H 0000 0800 a9 37 85 01
It doesn’t work… so there’s no code that says:
LDA #$37
STA $01

Let’s do the backup search…
H 0000 0800 85 01
and there’s three results: $0350 $036F and $03E8. If we scroll down nothing really jumps out as the game JMP (jump to start the game) however at $0428 we see the JMP $A7AE which as I mentioned in the game Tetris, is indicative of the game doing a “RUN” command.

Soooo…. Type
A $0428 jmp $0428
and exit the MLM to allow the loader to finish.

When the music stops, the flashing border stops and we enter the MLM, we see that the PC shows $0428 which is good (it’s stuck in our loop). Dump the game to disk, I’m not even going to tell you how at this point. You should know how.

Now turn off the ROM, and load the game back in. Knowing that it’s likely doing a RUN instead of a JMP, type LIST. And there you are…
1987 SYS2068 FOR ALL
it is indeed a RUN’able program.

But I suspect this is also going to be a crunched game…. So run it and then immediately enter the MLM and check for the usual bytes (H 0000 0800 a9 37 85 01)

A9 37 85 01 aren’t found but at $0368 we find the bytes 85 01 because your backup plan was to type : H 0000 0800 85 01

Disassemble $0368 and look at $0370, that has to be the game JMP.
0368 STA $01
036A LDA #$1B
036C STA $D011
036F CLI
0370 JMP $0810

Type in: A 0370 JMP $0370
This will prevent the execution of code at $0810 and force an endless loop when it reaches $0370 of the de-crunch routine.

Now you can dump this to disk. Now you might think that the game is good to go, but it isn’t because $0810 isn’t the start of the game, This game has been linked using ECA Linker. How do I know? Just from looking at the code – and from experience. You might not always be able to know when you’ve peeled the last layer of the de-crunching onion away until you get experience in looking through 6502 code.

But regardless, type RUN and immediately enter your MLM.

Search for a9 37 85 01 which are found at $0199
At $019E we see a JSR $7274
So in the MLM type: A 019E JMP $019E

Then dump the memory to disk again (Section A).

Now load the data back in… and since we know $7274 is 29300 in decimal we’re going to type
SYS 29300
And there you are….

Now you might wonder, how did I know that the game still had one final hurdle to go with one more crunch? Well when you see
1998 SYS (2064)
It’s more than likely ECA Linker that was used because of the line number being 1998. That’s your typical ECA Linker result.

2.10 Intros

Now there may come a time you want to dump a game to disk that has an intro before it. The intro might be from a crack group, but they didn’t train the game. You just want to get the raw game data so you can train the game.

Intros work like this: They load into memory at around $0800, in the beginning of the C64’s RAM. The game itself is loaded just AFTER the intro, piggybacked on top of it. For instance an intro might be placed at $0800 to $2000 and the game will be loaded into memory at $2000. When you press Space, the intro executes a small piece of code that copies the game from $2000 down to $0800 and runs it. Since this transfer has to occur in a way that the transfer routine isn’t overwritten, it usually occurs on the screen RAM which is why you often see characters incrementing (changing) on the screen after you exit an intro. It might also occur around the $033C area of memory which is the cassette buffer.

Think of it this way: You have a pipe that runs vertically and it’s 5 feet tall. The bottom one foot of the pipe is empty and capped at the end. There’s a turn valve just above the one foot mark. The top four feet of the pipe is filled with water. In this analogy the empty bottom foot of the pipe is the C64 memory at $0800 with the intro. The top four feet of pipe with water is the game.

Now if you turn the valve, the water pours down into the bottom of the pipe. So too does the data pour down when you press space. But if you have the transfer routine anywhere in that ‘pipe’, it’s going to also be transferred down with the flow and it will crash. Copying the transfer routine to a safe area below BASIC makes it safe. What happens to the intro? All that data is overwritten which is why we can’t place a transfer routine in the intro.

Now trying to find the transfer routine can be tricky because there are hundreds of intros out there that all use different methods.

One way is to search for the routine that checks for when you press the spacebar and try to figure out where the transfer routine is. If you know machine code you’ll want to look for: JSR $FFE4 or LDA $DC01 which are two different methods used to check for a space bar press.

I’m going to recommend you look for the code where the transfer actually occurs.

Download Defender from ...

Run the game and watch the intro from Avatar. Now enter the MLM. We want to find the code where the intro transfer routine copies the data from where the game is (in memory above the intro memory) down to $0801.

So type
H 0800 5000 01 08
The $5000 is just a guess as to where the intro would end in memory. The 01 and 08 is representing $0801 (we write the bytes high byte first, low byte second) and I’m searching for $0801 because that’s most likely where the program will be copied to. Some intros copy down to $0800 but this is unnecessary because $0800 is always set to zero. If it’s other than zero, your program will not run.

We find two results at
$0C06 and 2D7F.

If we type D 2D60 and scroll down, we can see there’s no machine code there. This is a false positive. It could be data for something else.


If we type D 0C00 and scroll down we see:
0C00 LDX #$00
0C02 LDA $5700,x
0C05 STA $0801,x
0C08 inx
0C09 BNE $0c02
0C0B inc $07C4
0C0E inc $07c7

(I chose to disassemble $0C00 instead of $0C06 because it’s helpful to see what code is just before the search result you’re looking for. As evident in this example.)

What this code does is it sets X to zero (like in BASIC x=0)
It then LOADS $5700,x and stores what it finds at $5700 into $0801,x
then we increment X by one
if branch not equal (that is, if X hasn’t rolled back to zero) go to $0c02
This is like a BASIC loop:
x = 0
for I = 0 to 255
a = peek($5700) + x
poke ($0801) + x, a

Do you follow?

The game itself is found at $5700 and copies the first byte found at $5700 into $0801
X goes from a 0 to a 1, and then it loads $5701 and stores that location into $0802, then it copies $5702 into $0803 and so on.
the “,X” is like saying “+x”

Finally when x rolls around to zero (it will go up to 255 and reset to zero) then the $57 in $5700 will become a $58 and the $08 in STA $0801,x will become a $09, and so on.

If you don’t follow, just scroll down to $0C1B and that’s your jump to the game.

So we know what we’re looking for, but we also need to wait for the routine to copy itself to under BASIC (the safe zone). Exit the MLM and press Space. When you see the credits for the intro, then you’ll want to enter the MLM again. Note the changing characters on the bottom of the screen – that’s the two increments we saw at $0C0b and $0C0E incrementing the locations being copied in groups of 255 bytes at a time.

So to fix this, press SPACE and when the intro exits and you see characters changing on the bottom of the screen QUICKLY enter your MLM. The transfer routine is found on the screen (screen RAM) so your results won’t be below $0800 this time.

H 0400 0800 a9 37 85 01
Which will result in the code having been moved to $07D6 and you’ll want to loop the
07DB JMP $07DB

3.0 Training Concepts

There are different types of trainers including infinite lives, sprite collision, level skipping, infinite energy, etc.

Lives – involves preventing the decrementing of a number where the number is your number of lives.

Sprite collision – a register in the C64’s memory that indicates whether you’ve collided with another sprite or character on the screen. Stop that register from revealing a collision and you’re invincible.

Level skipping – hacking the game code to set a value, or jump to a location, that causes the game to skip a level.

Energy – The same concept as lives however energy may be a bar that gradually shrinks on the screen rather than an actual number (eg. XXXXXXXXXX where the X’s slowly get removed instead of “Energy: 50”)

The easiest way of training is to use an Action Replay Cartridge that has a trainer built into it. It works by asking you the number of lives that you currently have (eg. 3). You then exit the cartridge, lose a life and enter the cartridge again. The cartridge will then check all memory locations that used to contain a 3 but now contain a 2. If you have such a cartridge, you should have little difficulty finding the code that removes a life.

In a game, you will always start with a certain number of lives, on a certain level and with a certain amount of energy. This is done by loading the number necessary and storing it into a memory location. You could theoretically write a game and save it with a memory location already set to the number 3 (for 3 lives) instead of loading a 3 and storing it into a location, but once you died and tried to play the game a second time, the memory location would still hold a 0 (zero). For this reason the game needs to initialize the memory locations before the game begins – each time and every time.

For example if the makers of the arcade game Galaga didn’t initialize the memory location that held your lives, with a “3” each new game, you’d have to turn off the machine and turn it back on so it would load up with that “3” in the right memory location. So memory locations will always need to be initialized instead of just being loaded from memory (or disk) with the number of ships already in the designated memory location.

The concept behind this is that you know how many lives you begin with and you look for code in the game where it loads that number and stores it into a location. You then check those locations after losing a life to see which of them has decremented in number. The con of this approach is that you could have dozens of memory locations that are loaded with your number of lives, meaning you have to check them all.

3.1 Method 1 – The Screen RAM Method

In this method we choose not to look for the code where your lives are set and decremented. Instead we look at screen memory to see what location on the screen displays your number of ships.

PRO: Easy to find usually and if the game reads the number of lives from the screen memory, it’s easy to change.
CON: The location might just reflect the value of a secondary location that is your number of ships. Example: Location 2 might hold your number of ships, but the game copies what’s in location 2 and puts it on the screen. The actual screen location has no bearing on the actual number of ships. Location 2 does.

Download Galaga from at the following link:


Type I 0800 and scroll down and down until you find what looks like screen memory. I’ll give you a hint, it’s at $8000 instead of $0400.

If you look at around $8360 you can see that your number of lives shows as a ‘4’ in location $839D. You can change that 4 to a 9 if you want, and you have 9 lives. It works! How about a more permanent method though? We know that location $839D is showing our number of ships.

We’ll search the game for the command:
DEC $829D
This means decrement whatever is in location $839D. The command is much the same as we’ve been doing:
H 0800 C000 CE 9D 83
CE means Decrement, 9d and 83 is $839D where the high byte goes first (the first two digits of the memory location are typed last, the last two digits of the location are typed first)


And with our command, we find code where this is decremented at $1FE7.

If you look at line $1FE7 it says DEC $839D. So if we change this to do something other than reduce the value of our ships from the screen memory, we’ll have cheated the game. How about that NOP command? We know NOP does nothing at all (it means “No Operation”)

On a new line type:
A 1FE7 NOP and press return
Then enter two more NOP’s, the MLM should automatically advance to the next memory location so you don’t have to type the “A”ssessemble command each time. When done, press enter.

Exit the MLM and continue to play the game…. And it worked! You’ve just trained your first game.


3.2 Galaxia – Screen RAM Method

Let’s now download Galaxia from at the following link:

This time we see we don’t have a number representing our ships, but we see actual ships at the top right of the screen. Again we’re going to enter the MLM and search for the screen RAM. I’ll give you a hint: It’s at normal screen RAM - $0400.

Look at $041F by typing I 041F
In bytes we’d see: 30 31 20 4F 20 4F
In text mode we’ll see: 01 O O
The 30, 31 is equal to the numbers “0” and “1” and the two “O”’s are the ships shown on the screen.

Now by counting the number of memory locations over we see that $0422 is where our first ship is shown and $0424 is where the second ship is shown. Now it’s not going to decrement a memory location because this isn’t a number value. It’s two characters on the screen. What I mean is, we’re not looking for a “3” lives than becomes a “2” then a “1” and then Game Over. We’re looking for a routine that places ships on the screen – tiny ships that indicate your number of lives.

I suggest searching for $0422 and $0424 to see if we find any hits.
H 0800 C000 22 04
0A3D and $0A6C are found!

Disassemble at $0A38 to see what it’s doing:
0A38 LDX #$00
0A3A LDA #$20
0A3C STA $0422,X
0A40 CPX #$06
0A42 BNE $0A3C

This isn’t what we want – but it’s close. It loads X with zero, loads A with a value of $20 which is 32 in decimal (32 is also ASCII for space). It stores the space in $0422,X and increments X until it becomes 6. So this basically erases the ship count from the top of the screen.

Now the second result is of interest to us. Great interest!

We’ll disassemble a little bit before our result…

D 0A63
0A63 LDX #$00
0A65 CPX $50
0A67 BEQ $0A70
0A69 LDA #$4F
0A6B STA $0422,X
0A70 CPX $50
0A72 BNE $0A65

What this does is as follows. It loads X with zero (in BASIC it would be x = 0)

It then compares X to $50. Now there’s something to take note of and that’s that there is no number sign before the $50. When you are loading or comparing numbers there’s two ways to do it. You can load the actual number or you can load a memory location.

For example:
LDA #$00
STA $0400
Is not the same as
LDA $00
STA $0400

The first example will load A with the actual number 0 and store it at $0400. The second example will load whatever is in the actual memory location $00. When we use the #$ prefix we mean “I want you to use this actual number”. So when we do a CPX #$50 we’re saying, “compare X to see if it’s $50 in hex” whereas if we did a CPX $50 we’d be saying, “compare X to whatever is in LOCATION 50”

Getting back to our example.
- LOAD X with zero
-compare X to whatever is in location $50
- if X = whatever is in location $50 (branch equal) to $0A70
- otherwise load A with #$4F and store A at $0422,X
- increase X once
- increase X again
- compare X to location $50, if not equal go back to $0A65

What this is doing is it is loading A with #$4F and placing it on the screen, it is then incrementing X by 2 (inx and inx) which effectively places another ship on the screen double spaced from the last ship. We also know by viewing the screen RAM that #$4F is what’s being used to show a picture of a ship on the top of the screen.

So say you have two ships remaining – it will load X with zero, it will compare X with location $50 which holds a value of 4. Since X is not equal (0 Is not equal to 4) then it goes to line $0A69 to place ONE ship on the screen. It then executes the two INX commands at $0A6E which increases X to a value of 2 (because X was set to zero on line $0A63). Essentially for every ship that you have, location $50 will hold a value of 2. So if you had 3 ships, location $50 would be equal to 6. I know this only because I checked the value of location $50 when the game first began and it was a value of 4 yet I only had 2 lives.

This simple loop routine draws a ship for each life you have. I deduced that location $50 was responsible for holding the number of lives because it is the only comparing being done in this loop to indicate when we should stop displaying ships.

To cheat the game we’re going to look for where it DECREMENTS location $50. Now since we know $50 holds DOUBLE your actual lives (eg. One ship means location $50 is equal to a 2) we’re probably going to be looking for TWO decrements.

The command to search for is
H 0800 C000 C6 50
This is because C6 means ‘decrement’ and of course the 50 is the memory location we’d want to see being decremented.

To confirm that these are the bytes though, try this test. Assemble the command “decrement 50” or “DEC $50” into location $0100 by typing:
A 0100 DEC $50 (and press return)
You’ll notice that it now says “C6 50” which are the “op-codes” or operational codes that we want to find.

And we do find results at locations $1b97 and $1b99. Coincidentally 1b97 and 1b99 and right after one another, so I was right about the ‘double decrement’.

Type D 1B97 and you’ll see:
1B97 DEC $50
1B99 DEC $50
1B9B LDA $50

So this routine is the one that decrements our lives. Let’s change those two decrements to our “NOP” (no operation, do nothing at all)

A 1B97 NOP (press return)
And continue to type in three more NOP’s. It’s important not to put in too many or too few NOP’s. Exit and test the game… and it works! Infinite lives!

TIP: Why did the game decrement location $50 and then compare location $50 to a value of #$FE as seen in line $1B9D?
Ships: 3 – value of $50 = 4
Ships: 2 – value of $50 = 2
Ships: 1 – value of $50 = 0
When on your final ship it would decrement (subtract) 2 from location $50 which would be equal to a zero causing it to wrap around. 0 minus one=$FF. $FF minus one = $FE. Memory locations wrap when you add or subtract beyond 0.

3.3 Method 2 – The Load and Store Method

There are times where the screen memory method won’t work.

Download Kong Strikes Back from this link:

Now this game can actually be cheated using the screen RAM method if you look at $4400 and onward, but I’d like to see you advance your skills to do it using a more advanced approach.

Play the game… immediately you’ll notice that you begin the game with 4 Bombs and 4 Lives.

So enter the MLM and let’s try to find where those memory locations are initialized through the LDA #$04 and STA $xxxx commands (where $xxxx could be anything).

H 0000 A000 A9 04
We find 12 results
The first one is at $223D

So let’s see what it does with that value of 4.
D 223D
223D LDA #$04
223F STA $23DC

Now let’s look at location 23DC using the M command:
M 23DC
In my game, location $23DC is showing a zero value and I’ve still got 4 lives. So this isn’t the right location. The second result at $2267 also stores the number 4 into location $23DC so this is wrong too.

The third result at $260E stores the number 4 into $D839 which is screen colour RAM. This is also wrong.

The fourth result at $2B69 looks like this:
2B69 LDA #$04
2B6B STA $2517
And currently location 2517 holds a 4… so I’ll lose a life. No, that didn’t work… but I noticed that when I used a bomb in the game that the value in location $2517 went down to a 03.

So let’s find where it decrements location $2517.

TIP: Can’t remember the op-codes you’re looking for? Just assesmble the actual code you’re looking for, into a location such as $0100.
A 0100 DEC $2517
And press return. You’ll now see your op-codes are: C6 17 and 25.

H 0800 A000 CE 17 25
location $2FDE is the result

2FDE DEC $2517
2FE1 LDA $2517
2FE5 ADC #$30
2FE7 STA $47D1

If you decided to use the screen RAM method I taught you, and you tried searching for location $47D1 which also held the number of bombs in screen RAM, you should also have found this routine. The routine decrements a life, it then loads whatever is in $2517 and then it adds #$30 to it and stores it in $47D1. What this does is it takes the number of lives (1 to 4) and adds an ASCII value of “0” (#$30) to it so that it can be displayed on the screen as a number. Values of 1-4 are not ASCII numbers so the addition is necessary. (eg. #$31 = ASCII “1”, #$32 = ASCII “2”)

Let’s make infinite bombs…
type: A 2FDE NOP (press return and type in two more NOP commands)

and we’ve just created infinite bombs. Now let’s find the lives….
For the lives I’m going to use the screen RAM method which would be $47DB. I’m choosing this because I don’t readily see where it is putting the lives. So let’s search for anything decreases the location $47DB

H 0800 A000 CE DB 47 (remember high byte first)
results: $2DB3 and $3AEF

Overwrite the $2DB3 DEC $47DB by typing:
A 2DB3 NOP (and two more NOP’s)

We put three NOP’s because the command DEC $47DB uses three op-codes (CE DB and 47). And there we are… infinite lives! I might suggest doing the same for the other decrement found at $3AEF just to be safe.

3.4 Infinite Time

Now we also see that there’s a countdown timer for Kong Strikes Back. Let’s see if we can find that too. Type “I 4400” and scroll down until you find the timer numbers in the screen RAM (which ordinarily would start at $0400 but not in this case)

And we see the timer numbers are found at location $47E3 through to $47E6.

Let’s try to find where it’s decrementing those numbers…. Going back to the screen RAM method.

I am typing:
H 0000 A000 e3 47
H 0000 A000 e6 47
I tried searching for anything to do with the first and also the last numbers of the timer ($47e3 and $47e6) because it subtracts from the last number, right?

Now here’s a tip…. Maybe the game allows for even MORE time than we can see on the screen. Let’s just try searching for $47E2 which is one character to the LEFT of the four digit time on the screen memory.

H 0000 A000 E2 47
Results: $29F8 and $2A06

Now we’re getting into some code that I’m just not able to teach you – you need to have some understanding of machine language to be a successful game trainer. But if you look at line $29EE it loads A with $29EA. At line $29F4 and $29F5 it adds a “0” to the number and stores the number at line $29F7 into $47E2, X. So this is definitely our timer. What it’s doing is converting whatever is in location $29EA into a four digit number.

I suspect $29EA will be our timer. We’ll search for a decrement of that location:
H 0000 A000 CE EA 29
Nope, there is no DEC $29EA to be found.

Since we can’t find a DEC-rement command let’s see if we can find anything to do with location $29EA.
H 0000 A000 EA 29
$2087, $29EF, $2A1D, $2A23, $2A39 and $2BB0.

$2086 LDA $29EA
$2089 STA $266B
This does nothing more than copies the location $29ea and puts it into $266b. Move to the next result….

Beginning around $29EE we can see this just puts the numbers onto the screen – we already know this. Next result…

2A1C LDA $29EA
2A20 SBC #$01
2A22 STA $29EA

Can you figure out what this is doing? It loads $29EA into A, it then subtracts using the SBC command where the #$01 means “subtract 1” and then it stores it back into the same location. This is exactly what we want. It didn’t use the DEC command at all, and this is another thing you’ll need to know – how to search for the SBC command.

How can we make it infinite time? There are a few methods…

Method A: Change the 1 to a 0 so it reads “SBC #$00” and subtracts nothing at all.
Method B: NOP out the subtraction code
Method C: NOP out the final STA $29EA so that it subtracts but doesn’t store it back.

To do this:
Method A:
A 2A20 SBC #$00
Method B:
A 2A20 NOP

Method C:
A 2A22 NOP

Unfortunately methods A and B won’t work because at line $227F the game it replaces the “1” back in at the “SBC #$01” command. If we were to put it to “SBC #$00” then it would revert back. If we changed it to NOP’s, one NOP would be changed to the ORA command when that “1” was put back.

To solve this problem let’s just go to line $2A22 and change the STA $29EA to LDA $29EA. So after it loads $29EA and subtracts 1 through the SBC command, instead of putting the reduced value back into $29EA it will just re-load $29EA.

A 2A22 LDA $29EA
now this doesn’t quite work yet because there is a second location that holds our time… go to line $2A31 and change the STA $29EB to a LDA $29EB. Now it works 100%.

In basic this would be like saying:
10 X = 3 : REM Number of lives
20 X = X – 1 : REM reduce life
30 X = 3

3.5 Dukes of Hazzard – Sprite Collision

In this case study we’re going to introduce you to sprite collision. When two or more sprites touch on the screen, a register in the C64 changes. The register is $D01E.

There are eight allowable sprites on the screen at any one time. Location $D01E will read the sprites that have collided – It does this through the use of bits. Since we have eight sprites and only one register to read, it’s somehow necessary to break down the result into a way of determining which of the sprites have collided. We do this through assigning the sprites bit values.

The values are 1,2,4,8,16,32,64 and 128. Sprite 1 would hold a value of 1, sprite 2 would hold a value of 2, sprite 3 would hold a value of 4 and so on. So if we have the very FIRST sprite and the very LAST sprite touching, the value of $D01E would be 129. It would be 129 because it adds up the bits of the sprites that are touching (1 and 128). If all 8 sprites were touching one another, we’d have a value of 255 in $D01E.

So if we want to create” invincibility” in a game, we can often do so by changing the line of code that loads the register $D01E and changing it to load a zero instead. Zero to indicate no sprites are touching.

Download and run Dukes of Hazzard from this link.

Play the game and notice that you lose a life if you collide with other cars, motorcycles, etc.

If the game uses a straightforward LDA $D01E, this can be one of the easiest cheats to make. To accomplish this we look for the bytes: AD 1E D0 which means

We find two results. The first one is at $203E

203E LDA $D01E
2041 LDA $D01F

Why did the game load the register but not do anything with it? It loads $D01F right afterward. This would be like typing in BASIC:
A = peek(53278)
A = peek(53279)

The reason it, and many other games, does this is because the register $D01E holds the value of a collision until you read it in. So it’s necessary at the start of a game to read the registers to “clear them out” and reset them to zero. This isn’t what we’re looking for but I can almost guarantee you the second result is.

The second result is found at $5C3E

5C3E LDA $D01E
5C41 STA $3B03

So location $3B03 will hold the result of any sprite collisions. To gain invincibility we want to change line $5C3E. Now this line holds three op-codes because it uses up three bytes: AD for the LOAD (LDA$) command, and $1E and $D0 for the $D01E. So we’ll need to replace it with THREE op-codes.

A 5C3E LDA #$00
5C40 NOP

We need that NOP in there because LDA #$00 uses only two op-codes. So if we didn’t include the NOP, the game will crash.

You see the original bytes were:
53ce – AD 1E D0 (or LDA $D01E)
we changed them to
5c3e – a9 00 ea (or LDA #$00, NOP)
had we not put the NOP in, the three bytes would have been:
53ce – a9 00 d0
And after the game processed the LDA #$00, that $d0 command stands for BNE (branch not equal) and we’d be sending the program to execute some area of memory that it’s not supposed to. Remember op-codes are commands and the 64 will execute whatever comes after the LDA #$00 command – so if you change a three byte command to a two byte command, that third byte is going to be treated as a command when it’s not supposed to be one. In this case the $d0 is a left-over from the $d01e in the old LDA $D01E command.

Anyway I’ll stop blabbering… the game now allows you to be invincible to other vehicles.

Run the game and play it. You see that we start with 3 cars. So let’s try to find the lives. I first start by typing:

H 0800 A000 A9 03
this is the LOAD #$03 command in 6502. A9 is LOAD A and 3 of course is the value to load.

If you don’t know the specific op-codes to search for, again, try assembling the code into memory at around $0100 to confirm.

A 0100 LDA #$03
To which when you press return it will place the op-codes in automatically (A9 and 03).

Now I’ve checked the results (there are 6 of them) and none appear to store a value of 5 into any particular location. Some of the results are for screen colour.

3.6 Dukes of Hazzard – Character Collision

Just as $D01E is to sprites colliding with one another, $D01F is a register to indicate when a sprite has touched a character on the screen. Sprites and characters can overlap and often to.

After you’ve created invincibility for yourself you’ll notice that the helicopter dropping ammunition on your car will still cause you to lose a life. This is because what is being dropped from the helicopter isn’t a sprite, it’s a character.

Much like steps taken for finding sprite collision, we search for:
H 0000 A000 AD 1F D0
Those three op-codes mean LDA $D01F

Two results: $2041 and $5C44. The first result doesn’t appear to do much, I suspect it’s clearing out the register because like $D01E, it holds its values until you read them and then clears them out.

The second result at $5c44:
5c44 LDA $D01F
5c47 STA $3B04
Looks good… it’s storing the result of any collision into a location that it can check later. So let’s replace that line of code. Remember it’s three bytes (ad 1f d0) so it needs to be replaced with three bytes.

A 5C44 LDA #$00
5C46 NOP
This will place a value of zero into the memory location $3b04 that is being used to check for any collisions with the helicopter’s dropping things on you.

Exit and resume the game… and you’ve now got complete invincibility!

Just for fun, download The Machine which is a racing car game. It can be found at this link:

Enter the MLM and search for LDA $D01E using the command
H 0800 A000 ad 1e d0
There are many results but most of them load the register and do nothing with the result. The one at line $8400 however does some AND’ing which is a way of isolating which sprite has collided. AND is often used to isolate which bits are turned on.

So now you’ll type
A 8400 LDA #$00
And add the extra NOP at $8402 and now you can drive right through other vehicles.

Sometimes you want sprite collision to a degree, especially if you’re trying to destroy other enemies. So you’ll have to learn how to fine tune your skills and learn how games will use the AND command.

Download my C64 software here

Posts: 11
Joined: Fri Jun 19, 2015 1:42 am
Location: Canada

Re: So You Want to Crack & Train Commodore 64 Games Like a P

Post by mrnop »

3.7 Lightforce – Infinite Lives

Download Lightforce (the game with the cool music) from this link:

Notice that you begin with 5 lives. So naturally we want to look for the command:
LDA #$05
in this case there are only two results: You already should know the command to search for this, I think at this point I don’t need to tell you any more but I will one last time -> A9 05 which represents LDA #$05

One result is found at:
1A3E LDA #$05
1A40 STA $01
Right away we know this isn’t what we’re looking for because location 1 is the location we use to turn on and off the ROM when we want to use those ‘houses’ for RAM.

The second result is found at:
2D1B LDA #$05
2D1D STA $081C

And by playing the game and losing a life, so we’re down to four ships, we enter the MLM and verify that location $081C holds a value of 4.

M 081C 081C


And how will we lose a life? Most likely through a DEC (decrement) of that location. So we search for the bytes: CE 1C 08 (dec $081c)

And voila, location $379B is the line we want to change. So we’ll change it to three NOP commands. You know how by this point:
A 379B NOP (press return and type two more NOP’s)

There you are, infinite lives!

3.8 Toy Bizarre

Download Toy Bizarre from this link:

You begin with four lives so we search for:
A9 04
which returns many results. Most of the time it’s storing the value of ‘4’ in zero page memory ($0000-$0100) and when I check the values of the locations it stored the 4 in, they are no longer holding a value of 4. So these aren’t the right ones.

However when we get to the $2F0B:
$2F0B LDA #$04
$2F0D STA $17

We find that location $17 changes to a 3 when we lose a life and now have 3 guys.

We want to search for the DECREMENT $17 command. It’s not the same as earlier examples though.

Op-Code quick tip:
DEC $C000 = op-codes CE 00 C0
DEC $C0 = op-codes C6 C0

Notice that $CE is used for decrementing four digit memory locations (that is, above zero page) while $C6 is used for decrementing zero page locations (below $0100).

And a result for H 0800 A000 c6 17 is found at $3033. Now we’re NOT going to NOP out that because the next line of code reads:
3035 BPL $3062

When you decrement or increment a location, you can then execute a BPL command to branch off if the value is anything but zero. So in this case once it decremented location $17 it would call the BPL to go to location $3062 is location $17 was anything but zero (you still had lives left).

What this did originally was branch off to $3062 if you still had lives left. Knowing the line before was LDA $17 if we change it to two NOP’s it will look like this:
BPL $3062
Which means… do nothing, but branch to $3062 if the result is above zero. But as we haven’t loaded anything into A, this could be catastrophic. It means that we’re relying on whatever is in the “A” register instead of location $17. Who knows what that could be…

Best course of action just change the DEC $17 to a LDA $17. Instead of decrementing, it will just re-load your number of lives.

A 3033 LDA $17

and you’ve got infinite lives!

3.9 Ghosts and Goblins 1994

Download Ghosts and Goblins 1994 from this link:

Now you see four lives at the bottom but you actually have five (including the life you begin with). So let’s look for the code LDA #$05. You should know how by now and you’ll find a result at $087E

087E LDA #$05
0880 STA $359A

And it so happens by viewing memory for location $359A that it equals the number of lives we have even after we lose a life. So for infinite lives we search for:
CE 9A 35
(which translates to DEC $359A)

We find a result at $0936 so we’ll change just one byte. At $0936 we see a $CE which means Decrement. Type:
M 0936 0936
And cursor up and just change the CE to an AD. This changes the DECrement to a LOAD instead. Much easier than typing : A 0936 LDA $359A

There’s infinite lives for you!

Once you become comfortable with knowing op-codes you can just type over top of the old op-codes rather than assembling new code over top of the old code.

Now as for infinite time… we know that you lose a life at around line $0936 right? And we know that the time is reset when you lose a life.

Look at line $0936
0936 DEC $359A
0939 BEQ $093E
093B JMP $08B0
093E LDA #$01

This says:
decrement a life, is it EQUAL (to zero) then jump to $093E otherwise jump to $08b0. Now when you DECrement a location you can perform a BEQ (branch if equal). And if the result of the location you just decremented is equal to zero, it will branch off. So we know that if you lose a life and it’s your last life, you go to line $093e. Otherwise you JUMP to $08B0. For clarification on BEQ and BNE see Section B for reference.

Let’s look at $08B0 to see if we can find the time…

Look closely at $08BF
08BF LDA #$02
08C1 STA $359B
08C4 LDA #$59
08C6 STA $359C

Doesn’t loading a 2 and loading a 59 seem awfully coincidental with our beginning time of 2:59 minutes? Hell yes.
So we’re going to look for CE 9C 35 (DEC $359C)

H 0800 A000 CE 9C 35
Three results: 0D94, 0ECD and 0EE6. However if we set all three of these to LDA $359C instead of leaving them as DEC(rement), the timer still counts down. Is this not the countdown timer location?

Let’s try another approach before we give up. We know that you can also use the SBC command to Subtract, which would work for a countdown timer. So let’s look for that code. But first we need to know what op-codes to look for.

So let’s type in the code at a ‘safe’ location which will be $0400 (screen memory).
The commands will be
SBC #$01
STA $359C

This code subtracts 1 and stores the result in $359C

So we type
A 0400 SBC #$01 (press return)
0402 STA $359C

And it shows us the bytes to look for are: E9 01 8D 9C 35
e9 01 = sbc #$01
8d 9c 35 = sta $359c

and now with high hopes we type:
H 0800 A000 e9 01 8d 9c 35
which it finds at $0F3C
so we change line 0F3C to SBC #$00 which we can do two ways:
A 0F3C SBC #$00
Or type in M 0F3C and move the cursor over the E9 01 and change the E9 01 to E9 00.

Exit and resume the game… did it work? Yes it did!

TIP: When working with countdown timers it’s important to keep in mind that some games will subtract your time when you complete a level, and award you points. If you’ve set the game for infinite time, this COULD cause an endless loop as the time never counts down to zero.

TIP 2: In time you’ll come to learn the op-codes by memory. You’ll know that e9 01 8d can by used with the H command in your MLM if you add onto them the high and low bytes to those numbers, of the location you’re interested in. In this case it was $359C.

4.0 Whittling Method

Unfortunately due to personal issues in my life, I’m unable to dedicate more time to complete this guide from this point on. However the guide is 99% complered.

The last method I want to show you is the ‘whittle’ method. While not an advanced technique, it can certainly work for the novice person.

The whittle method works like this…. You load and run your game, making note of how many lives you have (or energy, or weapon, or time, etc.)

Step 1: Save the memory into two or more large files.
SP “A” 0000 0100
SP “B” 0100 0800
SP “C” 0800 A000
SP “D” A000 FFFF

We have the zero page saved as file A, we have $0100 to $0800 as file B, and then two large chunks of memory in files C and D.

You could also save the files into two files only:
SP “A” 0000 5000
SP “B” 5000 FFFF

It doesn’t matter – just save all the memory into large chunks. It doesn’t matter how many files you choose or what start and ending addresses you use. Just save ALL the memory to disk.

Step 2: Press F12 to freeze the game as it is.

Step 3: Lose a life, or change weapon, or lose energy or complete the level, etc. You want to decrease or alter the characteristic you’re trying to train.

Step 4: Load back in the files, one at a time and see if your energy/lives/etc has been restored.

For example, you’d load R-Type, Gryzor, Pac Man, Blue Max, etc.
As the game begins, you’ll save the memory into large chunks as per Step 1. Let’s say you have four files: A, B, C and D.

You’ll lose a life or drain some energy or lose some time. You’ll then press F12 to save the game in this state.

You’ll then enter the MLM and type:
LP “A”
Type X to exit the MLM

Step 5: Examine the energy or lives to see if they’ve gone back to their original values.

If not, repeat step 4 but load in file B this time… and repeat for files C, D, etc.

Step 6: Once you’ve loaded in a file and your lives go back to what were before (that is, your energy is restored or your lives went back to 3 instead of 2, etc.) you know which file contains the memory location you want.

Remember to only load one file at a time and then exit the MLM to check if that files worked. Don’t load them all in at once.

Step 7: Once you’ve found the right file, you ‘whittle’ the memory into even more sections. You’ll then press F11 to restore the game back to the original number of lives/energy/weapon/time/etc and repeat the process.

Putting it into practice:
Just to demonstrate what I mean, in case you don’t follow. We’re going to use Blue Max as a demonstration. I’ve loaded and started Blue Max and am now going to save the memory into two files.

SP “A” 0800 5000
SP “B” 5000 FFFF (making sure ROM is turned off)

I pres F12 to capture the game with my fuel remaining set to 199 (full time remaining).

I play the game and watch the fuel drop down to 195 (or whatever).

Now I enter the MLM and begin to load in the files, one by one to see which one resets my fuel back to 199.

LP “A”
and it works… the time has been reset back to 199. So somewhere between $0800 and $5000 is my time. We didn’t need to load file ‘B’.

Now I press F11 to restore my freeze (putting the game back as it was when I saved the memory). Now I whittle down the memory into even more files:

SP “A” 0800 1000
SP “B” 1000 2000
SP “C” 2000 3000
SP “D” 3000 4000
SP “E” 4000 5000

And I again load in the files one by one…. And make note of the fuel remaining in Blue Max. As it turns out, file E works. When I load in file E, the Fuel jumps back up to it’s initial value.

Now we whittle down that memory into even more files. Press F11 to restore the game back to how it was when you started and whittle down that bank of memory even more.

SP “A” 4000 4800
SP “B” 4800 5000

It turns out when you exit the MLM, drain a bit of fuel and load in file A, the Fuel goes back up again.

So we’ve narrowed down the memory to $0800-$5000, then narrowed it down to $4000-5000 and then to $4000-4800.

Now let’s whittle it down even more…
SP “A” 4000 4100
SP “B” 4100 4200
SP “C” 4200 4300
SP “E” 4400 4500
SP “F” 4500 4600
SP “G” 4600 4700
SP “H” 4700 4800

As it happens, the first file works… no need to load any more files to see which one works! So we know that somewhere between $4000 and $4100 is the timer.

Now I break down the files even more…
SP “A” 4000 4080
SP “B” 4080 4100

I press F11 to load the frozen game, and load in file A… it works.

So now I want to narrow down $4000 to $4080.

SP “A” 4000 4040
SP “B” 4040 4080

Now I load in file A and the fuel hasn’t changed. So I load file “B” and notice the fuel jumps back up in value.

So somewhere between 4040 and 4080 is the fuel!!!

SP “A” 4040 4050
SP “B” 4050 4060
SP “C” 4060 4070
SP “D” 4070 4080

I load file A, nothing happens… I load file B, nothing. However file C changes the fuel back to it’s original value.

$4060-$4070 holds the fuel.

Now you can do a few things here.
1) You can save the memory to disk and compare it to what’s in $4060-$4070.
You’d do this by typing:
SP “A” 4060 4070
then you’d load in file “A” into memory, loading it into memory $c000
LP “A” C000
then you’d use the C (compare) command to compare what values are different
C 4060 4070 c000
This compares the original memory from $4060 to $4070 with the file you loaded into memory at $c000 ($c000 may be used by the game, so you’ll want to choose an area that looks relatively unused). The results are: 4061, 4062, 4065, 4066, 4067 so we know those locations are changing as we are expending fuel.

I can tell you that $4060 holds $1e which is the bombs (30 bombs)
I can tell you that $4061 holds the hex value of the fuel remaining.

I found this by changing the values of $4060 and $4061 to $ff just to see if the time would change – and it did (as did the bombs). Setting $4060 to $ff changes your bomb value. Setting $4061 to $ff sets your fuel to 255.

So to create infinite time we’ll look for DEC $4061
S 0800 FFFF CE 61 40

And there you have it…. Location 4605 is where the infinite fuel can be found.

What we did was broke down the memory into large chunks, then depending on which file contained the memory that held our item being drained (lives, energy, etc.) we broke down that memory into even more files. Eventually we ‘whittle’ the memory down into small enough areas that we can narrow down the location.

This can also work when trying to find a location that ‘completes’ a level (for level skipping). Note that when you use this method, you’re liable to change things in the game such as background graphics, your location on the screen, etc. and it’s not always pretty. This is why you have the F12/F11 to load and freeze the game back to normal.

Thanks for taking the time to read this file!

Section A – Additional Help on Op-Codes

Let’s go back to the game 1942 that you loaded but didn’t run it yet. Go into your MLM and type:
D 080D
This means “Hey I want you to disassemble the code at location $080D.” Disassemble means show me in words that I can understand, in English, what you’re doing.

You will see:
080D A9 93 LDA #$93
080F 20 D2 FF JSR $FFD2

To make sense of this, the first column is the memory location. In this case $080D is the first line, $080F is the memory location on the second line. The next two columns are the op-codes. The op-codes are the actual values in hex of what’s in memory.

Basically if you were to go to a new line and type:
M 080D 080F
You will see
A9 93 20 D2 FF A9 8E

Notice that the numbers are the same and the ones when we typed D 080D. (A9, 93, 20, D2, FF, etc) To explain it so that you’ll understand it… the M command shows us only the values of what’s in memory – the actual numbers. If you type POKE 1024, 42 to put a star in the top left of the screen and you then went into your MLM and typed
M 0400 0400
It would show something like:
:0400 2A 20 20 20 20 20 20 20
Which means the first number ($2A) is what’s at location $0400, the second ($20) is what’s in memory at location $0401, $0402 and so on. If you cursor up and replace that 2A with a 2C and exit the MLM, that asterix has now become a comma. Why? Because we overwrote what was in memory at $0400.

In a machine language monitor the M command displays the beginning memory location in the first column follow by what is in those next 8 memory locations. This goes on for every column that you see – which is why the values in the first row increment by 8. You’re seeing the memory in rows of 8 bytes.

The D command will show us a disassembly of what those numbers mean. The op-codes are the actual numbers of what’s in those locations. The far right is the actual 6502 commands.

So then…. We’ve typed in
D 080D
After the game 1942 was loaded, but not run yet.

We see:
080D A9 93 LDA #$93
080F 20 D2 FF JSR $FFD2
0812 A9 8E LDA #$8E
0814 20 D2 FF JSR $FFD2

What we can make of this is that location $080D has an $A9 in it (169 decimal), location $080E has a $93 in it, location $080F has a $20 in it. We know if we type
M 080D 0812 that we see those numbers in those memory locations. Here in the Disassembly (typing D 080D in your MLM) we not only see those same numbers but their actual meaning to the C64’s 6502 processor.

At location $080D we’re LOADing a $93
At location $080F we’re JSR’ing to FFD2 (in Basic this would be GOSUB)
At location $0812 once the computer returns from the GOSUB to FFD2 at the line above it again LOADs a value of $8E and again JSR’s (Jump subroutine) to $FFD2.

Just for fun try typing…
M 080D 0810
You see
:080D A9 93 20 D2 FF A9 8E 20
:0815 D2 FF A9 00 8D 20 D0 A9

Scroll up and change the 93 in the first row to a 42 and press return. Get to a new line with nothing on it and type again
D 080D
And we see
080D A9 42 LDA #$42

So the original code was
080D A9 93 LDA #$93
Has now become
080D A9 42 LDA #$42
Because we changed one value. And here we see the impact of changing that value. It Loads a $42 instead of a $93. The D and M commands in a MLM are showing you the same data except the D is to show you the actual 6502 commands and the M is just a memory dump without any explanation as to what the commands are.

Section B – BNE/BEQ

BNE means Branch if Not Equal. BEQ means Branch if Equal.

It works like this:
LDA $0400
CMP #$2A
BNE $xxxx

10 a = peek(1024)
20 if a <> 42 then 100
30 end
100 print “a does not equal a *”

The above code loads $0400 into A (the Accumulator as it’s called). The next line says COMPARE what’s in A to #$2A. The next line says BRANCH if NOT EQUAL to xxxx. So if you put an asterix in the top corner of the screen (location $0400, and the ASCII code for an asterix is #$2a) it will fall through to the RTS (return from subroutine). Basically is there is an asterix in the top left of the screen it will just return to the Ready prompt if you called this actual code from memory.

If there ISN’T an asterix in the corner of the screen it will branch off to xxxx because it’s a “branch NOT equal”. That is, what you had loaded into A and compared to a value of #$2A was not equal to one another.

In comparison….
LDA $0400
CMP #$20
BEQ xxxxx

10 a = peek(1024)
20 if a = 42 then 100
30 end
100 print “a equals a *”

The above code loads whatever is located in the top left corner of the screen (location $0400 also known as location 1024 decimal). It compares what it just loaded with #$20 (which is a value of 32 – or – an ASCII space). If there’s a blank space in the top left of the screen it is a BRANCH EQUAL because the results are equal. What you loaded into A (location $0400) is equal to what you compared.

In machine code that would be a BRANCH EQUAL because it branched off with the result being equal to what you compared it to.

Section C – Final Comments

- If your game shows a character in the bottom right of the screen that changes colour during de-crunching, this is likely Exomizer Cruncher. Replace your search bytes a9 37 85 01 with C6 01 which translates to “DEC $01”. This should reveal the final JMP$ you’re looking for.

- Often times you can change the final JMP to a JMP $A474 instead of creating a loop. This will jump to the READY. Prompt where you can save the raw data (or type LIST to see if it’s a BASIC program underneath the program being de-crunched. In the case of Defender for example you can change the JMP $3000 to JMP $A474.

- In CCS64 you can type ?$xxxx where xxx is an address, and it will show you the decimal value (eg. ?$C000 will result in 49152)

- You can convert decimal to hex by typing: ?#xx where xx is a number (either 2 or four digit hex)

- If you’re trying to isolate part of a game where you think you’re losing a life, I like to change a line of code where there’s three bytes (example: LDA $4000) to INC $D020. Then I resume the game – if the border colour changes colour because of the INCREMENT BORDER COLOUR command I just put in, I know I’m in the right place.

- Sometimes you’ll notice that I tell you to replace the DEC command with three NOPS and other times I told you to replace it with a LDA command. Why is this? Well sometimes you can get away with using NOP commands if the code that comes after your initial DECrement code has nothing to with the decrement.

This would be a safe place to use NOP’s:
DEC $45
LDA $50
STA $66
Replacing the DEC $45 will have no effect on the code after it.

However in a case like this:
DEC $45
BPL $C050
LDA $50
STA $66
That BPL command relies on the decrement $45 to make a judgement call on whether to branch off or not. If you put in NOP’s then the BPL command would rely on whatever code came BEFORE your NOP’s.

Lastly, you should know that it's not always going to be the "A" that is used for loading and storing. There are in fact three registers: A, X and Y. These are much like variables in BASIC.
Y = 44

A is used primarily for math (this is why it's called the Accumulator) whereas X and Y are used secondarily. For training purposes though you shouldn't rely solely upon looking for LDA and STA. There are also LDX, STX and LDY, STY. The examples I've provided you with should be enough to get started on training. Once you're comfortable with this tutorial, it's just a matter of also searching for A2 and A0 instead of A9 (A0 = LDY and A2 = LDX)

Tutorial written June 2015 by Mr. NOP (Canada)
Creator of Oil's Well and Ladybug for Commodore PET
nopsoftware at hotmail . com
Download my C64 software here

Post Reply Previous topicNext topic

Who is online

Users browsing this forum: No registered users and 1 guest