Author [EN] [PL] [ES] [PT] [IT] [DE] [FR] [NL] [TR] [SR] [AR] [RU] [ID] Topic: Theou Aegis' Rookie Guide To 6502 ASM Reading  (Read 17861 times)

0 Members and 1 Guest are viewing this topic.

Offline TheouAegis

  • Amateur Auteur of GMvania
  • Master Hunter
  • *****
  • Posts: 1860
  • Gender: Male
  • Awards The Retro Gamer: Has a heated passion for the oldschool VG Titles. The Great Defender will always defend the object of his or her fandom. Hack Master makes creations out of CV parts. (S)he makes Dr. Frankenstein proud.
    • GMvania Developer's Blog
    • Awards
  • Likes:
Theou Aegis' Rookie Guide To 6502 ASM Reading
« on: August 08, 2012, 05:02:59 AM »
+1
Since Inccubus is too busy to learn 6502 ASM (Nintendo Entertainment System programming), I'll write up this quick guide to ease the learning process. Bear in mind that the Super Nintendo Entertainment System used a similar system, albeit with a fourth register (Z).

First and foremost, you need to know the structure of 6502 ASM and the NES. There are three registers (four for the SNES): A, X, and Y (plus Z in the SNES). Each of these registers stores a single byte (8 bits). The A register, known as the Accumulator, is the primary register and is used in most operations. If you want to perform arithmetic operations, typically you use the Accumulator. The X and Y registers are often used for addressing, similar to indices in arrays in modern programming languages. You can also use the X and Y registers for temporarily storing values. The 6502 microprocessor also had a page file, which you could stack the accumulator into if you needed to change some addresses and then access their old values again.

The most important feature of the 6502 microprocessor is the status register. This is a single byte with each bit representing a specific status. This can apply to any of the registers or even any byte recently modified. Bit 7 (1000 0000) is set whenever a loaded or modified value is greater than $80. The reason for this is in any byte, bit 7 signifies negatives. So $80 thru $FF are negative values. Bit 6 is typically ignored. In most cases, you can ignore it. It handles overflows in twos complement maths, that is, it keeps track of when a value changes signs. Other bits can be ignored. However, bit 1 keeps track of when a value is set to 0 (hence it's called the Zero Bit), while bit 0 keeps track of when a value exceeds 255 or goes below 0, hence it is called the Carry Bit (since you'd carry a 1).

The most important link I can offer is this site which I use all the time myself:

http://www.obelisk.demon.co.uk/6502/reference.html

This will give you a detailed description of what each 6502 ASM command does. Note that if you just opened an NES ROM in a hex editor, you'd only see a series of byte values. The microprocessor reads each of those values and translates them into specific commands. That site will tell you what each of those commands are. Open a ROM in FCEUX and run the Debugger. It will translate the byte values into readable ASM.

Below is a list of what I feel are the most important commands. You will see these and work with these more than any other command, from my experience with Castlevania 3.


JSR
Jump to Sub-Routine
You will see this command often. It's similar to executing a script in Game Maker. It tells the processor to change its address temporarily to a different one. It has the byte value of $4C. The code %4C 10 80 would tell the processor to execute the ASM at address $8010. Note that addresses are lowest byte-highest byte.

RTS
Return from Sub-Routine
Think of this a the end of a script. This tells the processor to go back to the last JSR addressed. When viewing ROM data, the RTS command is like your girlfriend -- you'll miss it when you don't see it and feel so much joy whenever you do see it; I will explain why later.

JMP
Jump To Address
Often a JMP command signifies you are at the end of a certain series of code.  This can be a good thing in most cases, but many times it leads you off on wild tangents in coding, leaving you even more confused by the time you finally reach an RTS to the point you don't remember what you were reading.

LDA | LDX | LDY
Load Accumulator | X | Y
These functions set the Accumulator, X or Y to a specific value. Sometimes it's a direct value, like $00. Other times it's the value stored in a byte address. If $0000 is set to $C0, then LDA $0000 would set A to $C0. Keep in mind these functions can result in the Negative bit of the Status register being set if the value loaded is greater than 127.

STA | STX |STY
Store Accumulator | X Y
While the previous functions loaded values, these functions save them. You will be seeing a lot of these functions whenever you look through ASM.

PHA | PLA
Push | Pull Accumulator to Stack
The 6502 has a stack. You can dump values into it and then retrieve them. This is very useful if you, say, want to load $0542 to the Accumulator, change $0542, do some other stuff with the Accumulator, and then go back to working with that original value of $0542.

PHP
Push Processor
In most cases you won't need to worry about this. I haven't seen it. All it does is take store the Status register to the stack.

TXS | TSX
Transfer X to Stack | Transfer Stack to X
Same as above, only this pertains to the value stored in the X register. I haven't seen this outside of PPU handling.

TAX | TAY
Transfer Accumulator to X | Y
Similar to the Store functions, these functions save the value in the Accumulator to either the X or Y register. Very useful function.

TXA | TYA
Transfer X | Y to Accumulator
Similar to dumping a value in the stack, these functions let you not only retrieve the results of your meddling with the X or Y registers, they also let you use X and Y registers as little mini-stacks. Konami did that quite a bit in CV3.

SEC | CLC
Set Carry | Clear Carry
These are very important commands. The first sets the Carry bit of the Status register to 1, whilst the other sets it to 0. Setting the Carry lets you subtract two numbers directly. Clearing the Carry lets you add two numbers directly. Note that this only applies to the next Carry-intensive operation. So for example, the CLC before an ADC will only apply to that ADC, since the ADC will clear or set the Carry based on its own rules.

ADC
Add with Carry
This is how the 6502 handled basic arithmetic. Addition is handled by the formula A+M+c, where A is the Accumulator, M is the value referenced by the next byte address, and c is the Carry bit of the status register. When the sum is greater than 127, the Overflow bit is set. When the sum is over 255, the Carry bit is set. This means that unless the Carry bit is cleared in the next operation, if there is another ADC operation right afterward, it will be added to the next summation. Since the 6502 microprocessor uses bytes instead of words or Dwords, anything larger than $7F (127) is treated as a negative value. This means $01+$FF is the same as $01-$01. Sometimes a game programmer will do subtraction this way, but there is a function in 6502 ASM for that.

SBC
Subtract with Carry
This is how the 6502 handled subtraction. Be aware that here the Carry bit isn't subtracted from the difference, but rather the negation of the Carry bit. In other words, the formula is A-M-!c, or A-M-(1-c). This operation can be a tad confusing to work with. This time around, the Carry bit is set if the difference is logically greater than 0, otherwise it is cleared. In the case of $01-$02, the result would be $FF in byte format, but logically it's -1, so the Carry bit would be cleared.

INC | DEC
Increase | Decrease
You can directly alter byte addresses in the RAM. These functions will either add 1 to the byte value or subtract 1. In other words, INC $0043 will add 1 to whatever value is stored in the RAM at address $0043. Most games do this to count steps, which they then use to randomize actions.

INX | DEX
Increase X | Decrease X
Same as above. These functions let you change the values of the X register. This is frequently used in what are essentially FOR loops.

INY | DEY
Increase Y | Decrease Y
Same as above, but for the Y register. These are typically used for step counters similarly to the GM equivalent of timelines.

AND | ORA | EOR
Bitwise AND | Bitwise OR | Bitwise Exclusive OR
These are just your typical bitwise operations. They only apply to the Accumulator.

ASL | ROL
Arithmetic Shift Left | Rotate Left
I put these together because they are essentially the same. They can be performed on either the Accumulator or directly to an address in the RAM. An arithmetic shift left, as opposed to a normal bitwise shift to the left, transfers the setting of bit 7 to the Carry bit. For example, if the Accumulator has a value of $E0 and undergoes an arithmetic shift left, the Carry bit will be set, but if the Accumulator was $20 the Carry bit would be cleared. A rotation would replace bit 0 of the Accumulator or address with the value of the Carry bit before setting the Carry bit. Going back to the previous example, $E0 would shift to become $C0, but if the Carry bit was already set prior to that, then the value would become $C1; since $E0 was larger than $7F, its bit 7 was set and so therefor the Carry bit would be set as well. You can visualize the result of the Carry bit if you open Windows Calculator and run in Scientific mode's HEX setting. Leave it set to Qword or Dword and use the LSH button to perform a left shift on whatever you enter into the calculator. If bit 7 was set, the value would exceed $FF and the next highest nybble would be set to 1. That $E0 shifted to $C0 was really shifted to $1C0, but the 1 gets transferred to the Carry bit instead.

LSR | ROR
Logical Shift Right | Rotate Right
This is very similar to the left shift. This time, bit 0 is transferred to the Carry bit. In the rotation, the Carry bit's previous setting is transferred to bit 7 of the resulting value.

CMP | CPX | CPY
Compare Accumulator | Compare X | Compare Y
These are some of the most important functions in 6502 ASM. You use these functions to check the value of the specific register against either the value referenced in the RAM or a specific number. The Status register is then set accordingly. If you ever see a CPX call, there's a strong possibility that you are inside a loop. If you see INX CPX in that order, you are most assuredly looking at the end value of a loop. The equivalent of INX CPX $11 in a for loop is for(x=0;x<$11;x+=1). Needless to say, that pairing will be a site for sore eyes. Or a source of frustration, because oftentimes, if you see that while trying to hack a ROM, it means you missed the code you were actually looking for. These commands are basically A-M (or X-M or Y-M), so if the values are the same, the difference would be 0 and therefore the Zero bit would be set. This also means, just like in SBC calls, if difference between the Accumulator (or X register or Y register) results in a positive number, the Carry bit is set. In other words, if A>=M (or X>=M or Y>=M), the Carry bit is set. Likewise, if the difference is greater than 127, the Negative bit is set. Unlike SBC, the CMP operation does not return a value that cna be stored in the RAM.

BEQ | BNE
Branch if Equal | Branch if Not Equal
Branches are at the heart of games. These branch commands check the Zero bit in the Status register. If the Zero bit is set, a BEQ check passes. This is often used with CMP or DEX calls, since CMP is basically A-M.

BCS | BCC
Branch if Carry Set | Branch if Carry Clear
These check if the Carry bit was set or not.

BPL | BMI
Branch if Plus | Branch if Minus
These branches check if the Negative bit of the Status register is set.


So those are the most useful commands you'll come across (and also the majority of the commands). There are a few more, but you'll rarely encounter them. So now I'll discuss a couple nuances I didn't go into detail about above.

It is important to understand how JSR, JMP, BEQ, BNE, BCS, BCC, BPL, BMI, and so on, relate to each other. This can be very mind-boggling. Fortunately, FCEUX makes it hard to get lost, although if you do get lost you may have to play through the same part of the game again and again as you try to follow the code. A typical routine might look something like this:
Code: [Select]
0000 JSR $0006
0003 JMP $33C9
0006 LDX $034A
0009 INX
000A CPX #$03
000C BCC 0009
000F TXA
0010 RTS
0011 LDA #$00

In case you can't figure out what the code does at a glance (which is nothing), I will explain it. Address $0000 tells the processor to jump to the subroutine at address $0006. That subroutine tells it to load the value of address $034A of the RAM into the X register and then increase the X register. If after increasing it the Carry bit hasn't been set, the code tells it to branch back to address $0009, telling the processor to increase the X register again. If X gets increased to 3 or if it was already larger than 3 at the start of the subroutine, the value of the X register would then be transferred to the Accumulator and the code would leave that subroutine and go back to address $0003. Address $0011 is completely ignored in this situation, as address $0003 tells the processor to skip way ahead to another address. What this means for us is that basically this routine is over.

That's just what it means most of the time, but that's not always the case. Consider the following situation:
Code: [Select]
0000 JSR $0012
0003 JSR $0014
0006 BPL $0120
0008 JSR $0017
000B BCC $001B
000D BCS $0120
000F LDX $0224
0012 DEX
0013 RTS
0014 TXA
0015 JMP $001A
0017 ROL
0018 ROL
0019 RTS
001A TAY
001B RTS

Now what's going on? First, we enter the subroutine at address $0012. This subroutine tells the processor to load the value of address $0224 in the RAM into the X register and then decrease the X register by 1. It then exits that subroutine and reads address $0003. That tells the processor to go to address $0014, which transfers the X register's contents into the accumulator. We encounter a JMP command next. It would seem the routine is over. However, if we follow the JMP call to address $001A like it tells the processor to, we see that the Accumulator gets transferred to the Y register and then the subroutine is exited.

Wait, what subroutine? Didn't we just end the current routine with the JMP command? Not quite. In most cases, we can think of a JMP command as ending the current routine or subroutine as far as we're concerned. You will almost always be inside a routine, but most of that routine's subroutines will be meaningless for you. A JMP command typically signifies the end of a meaningful subroutine. However in this case, the RTS call at $001B will send the processor all the way back to $0006, which checks if bit 7 of the Y is set. We are now technically out of the current subroutine, but if the Negative bit was set and that branch check failed, the processor would read $0008 and enter another subroutine at $0017. After rotating the Accumulator twice to the left, the subroutine is exited and the Carry bit is checked. There are two conflicting branches, so we know it's finished.


You may have noticed the addresses weren't incremented evenly. Each function is called by various means. Technically 6502 ASM is nothing but a series of bytes. For example, the function JSR $F35A is actually $4C5AF3. No joke! Okay, so that took up three bytes in the RAM, thus the next address is 3 higher. That was obvious enough, but what about the other JSR in the code that only takes up two bytes? There are numerous types of function calls in 6502 ASM:

Implicit
This is the simplest form. One byte determines the entire process. Calls like RTS, TAY or SEC use implicit addressing. An offshoot of this is Accumulator addressing. For functions like ROL and ASL, there are special byte values for when the Accumulator is the target.

Immediate
This form requires two bytes. The first byte is the call and the second byte is an absolute value. CMP #$03 uses immediate addressing.

Zero Page
The 6502 processor has what's known as the Zero Page. All the addresses in the RAM $0FF or lower belong in what is known as the Zero Page. These can be referenced in a single byte. JMP $001C could be called using zero page addressing.

Relative
Branches use this. This kind of addressing adds the specified value to the current address, $80 and above function as -1 to -128.

Absolute
Not always a good idea to use this, as it takes up three bytes. This refers to a full 16-bit address in the RAM.

Indirect
Only the JMP command uses this. It requires only two bytes, whereby the byte is a Zero Page address which is read along with the next lowest address to retrieve the target address. In other words, if $0000=#$04 and $0001=#$F0, then JMP ($00) would send the processor to address $F004.

Indexed
The other addressing modes can be indexed. This means the value of the X register or Y register is added to the address retrieved by the other addressing modes. In the previous example, if the X register was set to $05, then instead of jumping to $F004 it would jump to $F009.


On the next installment of Theou Aegis' Rookie Guide to 6502 ASM Reading, I will give you a brief tour of FCEUX's Debugger and get started on actual ASM reading lessons. You'll soon be hacking game code in no time!


omg it's so late at night...
« Last Edit: August 08, 2012, 10:51:55 PM by TheouAegis »
Your mom has had more floppies put in her than a Commodore 64!


Follow my lack of progress on my game at my blog:
http://gmvania.blogspot.com

Offline Inccubus

  • Wannabe Great Old One
  • Master Hunter
  • *****
  • Posts: 3265
  • Gender: Male
  • Warrior
  • Awards The Retro Gamer: Has a heated passion for the oldschool VG Titles. SuperOld Dungeonite: Members who have been around since the oldOLD days. Permanent Resident: Seems to always be around to post/reply.
    • Awards
  • Favorite Game: Vampire Killer (MSX)
  • Likes:
Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
« Reply #1 on: August 09, 2012, 12:52:56 AM »
0
Nice. That's a good refresher right there. Thanks.

BTW, here's that thing I'm working on A small edit of the translation patch of Akumajou Densetsu:

Mock Up:


Currently in-game:


As you can see it still needs to have some tiles moved around and some tile attributes to be changed.
« Last Edit: August 09, 2012, 03:39:47 PM by Inccubus »
"Stuff and things."

Offline TheouAegis

  • Amateur Auteur of GMvania
  • Master Hunter
  • *****
  • Posts: 1860
  • Gender: Male
  • Awards The Retro Gamer: Has a heated passion for the oldschool VG Titles. The Great Defender will always defend the object of his or her fandom. Hack Master makes creations out of CV parts. (S)he makes Dr. Frankenstein proud.
    • GMvania Developer's Blog
    • Awards
  • Likes:
Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
« Reply #2 on: August 09, 2012, 04:28:57 PM »
0
If you ever wondered why NES games used TSAs, I read a good explanation somewhere that pointed out programming by 8x8 tiles takes 4x as much memory as programming by 16x16 tiles and 16x as much as programming by 32x32 tiles. TSAs save on ROM size and allow you to dump more into the RAM and PPU.

It wasn't even an article on the NES. I just was reading it and was like, "OMG so that's the logic behind them!"
Your mom has had more floppies put in her than a Commodore 64!


Follow my lack of progress on my game at my blog:
http://gmvania.blogspot.com

Offline Inccubus

  • Wannabe Great Old One
  • Master Hunter
  • *****
  • Posts: 3265
  • Gender: Male
  • Warrior
  • Awards The Retro Gamer: Has a heated passion for the oldschool VG Titles. SuperOld Dungeonite: Members who have been around since the oldOLD days. Permanent Resident: Seems to always be around to post/reply.
    • Awards
  • Favorite Game: Vampire Killer (MSX)
  • Likes:
Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
« Reply #3 on: August 09, 2012, 06:30:32 PM »
0
Yeah, I remember reading something like that.
"Stuff and things."

Offline TheouAegis

  • Amateur Auteur of GMvania
  • Master Hunter
  • *****
  • Posts: 1860
  • Gender: Male
  • Awards The Retro Gamer: Has a heated passion for the oldschool VG Titles. The Great Defender will always defend the object of his or her fandom. Hack Master makes creations out of CV parts. (S)he makes Dr. Frankenstein proud.
    • GMvania Developer's Blog
    • Awards
  • Likes:
Theou Aegis' Rookie Guide To 6502 ASM Reading, Pt. 2
« Reply #4 on: August 09, 2012, 10:20:09 PM »
+1
There is a lot of information stored in one little NES ROM cart. You can't just go diving into it headfirst. You need a place to start. If you wander into a labyrinth and forget how you got in, you may never get out.

Open FCEUX.exe and load up a ROM file. For this example, we will go with Castlevania III. It doesn't matter much which regional version you use, or it shouldn't, but for sake of argument, this tutorial will use the US version, so you should too. First thing is first, we need to decide what to look for. For starters, let's look for something simple. You might think Trevor's horizontal movement would be simple enough -- left makes him move left 1px, right makes him move right 1px. Well, Trevor's horizontal code is actually one of the more complex codes out there, but what the heck. Somewhere in the RAM is Trevor's x-coordinate. We need to find this first. That's what the Cheat Search is for.

Load up the Cheats function in FCEUX. Make sure Trevor is at the very beginning of the stage, then click the Reset button in the Cheats function. The right side will load the RAM. Do you know Trevor's X coordinate? possibly not unless you want to count pixels (in other words, that's a feasible starting point, but it's not the path we'll take). You'll want to clear all the bytes in the RAM that are changing, since Trevor is obviously standing still, meaning his x-coordinate isn't one of those changing variables. Make sure the Pause When Active box is unchecked and then repeatedly click the Equal button for 30 or 45 seconds. You should have weeded out a few thousand bytes. This will make your next job a little easier. Check the Pause When Active box again then click on the game window. Move Trevor right one step (or two, since it's hard to control sometimes). Since x-values get larger going right, click the Greater Than button. Move Trevor right again and click the Greater Than button once more. This next time, move Trevor left and then click the Less Than button.

If you did it right, you should have narrowed it down to 2 bytes. You need to understand how the RAM is typically used. Bytes $0000 thru $00FF are in what is known as Zero Page. This is used mostly for temporary housing of variables. If you watch the RAM in FCEUX's built-in Hex Editor, you will see some Zero Page bytes that don't change from step to step. It's very likely they are indeed changing, but at a rate so fast that you can't see the changes. Bytes $0100 thru $01FF are the stack (see PLA|PHA|PHP in previous post). Bytes $0200 thru $07FF are the RAM, which is then mirrored in bytes $0800 thru $1FFF. The Zero Page will be important later, but all you need to worry about is the RAM ($0200-$07FF). Your Cheats search should have yielded one result in the Zero Page and one in the RAM. Move Trevor right one more time. Did the byte in the RAM address increase? If so, that's the byte we are looking for. You should have $0438.

Write down that byte value and make a note of it being Trevor's X. Now open the Debugger and click Add.... Enter $0438 in the first box. Next are three options -- Read, Write, and Execute. Reading is for processor calls such as LDA, LDX, LDY, INC, DEC, ASR, LSR, ADC, SBC, and so forth. Writing is for processor calls such as STA, STX, and STA. Execution is only for bytes in the PRG-ROM, which is located in bytes $8000 thru $FFFF. Right now we're interested in anything pertaining to Trevor's x-coordinate, so select Read and Write and leave Execute unchecked. You should now see on the right side of the Debugger

$0438 ECRW-

The E stands for "executing", the C stands for "CPU" (which is where the RAM is located), R stands for "read" and W stands for "write". The blank would be X if "execute" was selected.

As soon as you did that, the game should have stopped. Look on the left side of the Debugger. The very first line should contain the $0438 somewhere. Unfortunately, x- and y-coordinates are read each step, usually multiple times, for every object. This makes tracing x- and y-coordinates a major hassle. If you click Run over and over, you'll notice the same PRG-ROM addresses keep getting stopped. One of them is very important, but for now we'll disable them. The easiest way is to simply disable reading of $0438, but there is a better alternative.

Click once on the $0438 ECRW- and click the Edit button. Under "Condition" type

P!=#8035 && P!=#FCE4

This will prevent the game from stopping whenever the PC (abbreviated in the code to P) is not equal to $8035 or $FCE4. Note that the pound sign is used instead of a dollar sign. Dollar signs are for relative addressing -- putting P!=$8035 would tell FCEUX to keep running if the PC was at the byte with the same address as the value stored in byte $8035. So the # sign is used for absolute numerical values.

Click Run and the game should continue as normal. Move Trevor left a few steps. Did the program pause as soon as you tried to move Trevor? Look at the Debugger (it should have been brought to the front). Hopefully, you should see this in the Debugger:

Code: [Select]
0E:8758:AD 38 04  LDA $0438 = #$4A
0E:875B:10 05     BPL $8762
0E:875D:A9 01     LDA #$01
0E:875F:85 65     STA $0065 = #$02
0E:8761:60        RTS
0E:8762:AD C4 04  LDA $04C4 = #$00
0E:8765:18        CLC
0E:8766:6D 09 05  ADC $0509 = #$00
0E:8769:8D C4 04  STA $04C4 = #$00
0E:876C:AD 38 04  LDA $0438 = #$4A
0E:876F:6D F2 04  ADC $04F2 = #$01
0E:8772:8D 38 04  STA $0438 = #$4A

Let's assume you don't know anything about ASM programming (which of course you do because you've all been reading my threads). We'll break down each line.

1) 0E:8758 is simply the address in the RAM (0E is the page, but don't worry about that). The next numbers, AD 38 04 are the actual ASM values. $AD is the ASM for Absolute LDA. $38 04 is RAM address whose value is to be loaded into the Accumulator. Remember, the bytes are switched, so $38 04 refers to address $0438. After that we see FCEUX's translation of the ASM into a readable format. The last value, #$4A is the value stored in byte $0438. In other words, it's Trevor's x-coordinate (don't worry if yours isn't #$4A).

2) BPL $8762 tells the processor to check if the value loaded into the Accumulator is between #$00 and #$7F. If that is the case, the code will jump to address $8762.

3) If the BPL above was false -- that is, if the value of the Accumulator was #$80 or greater -- this line would be executed. LDA #$01 simply sets the value of the Accumulator to #$01.

4) If the BPL was false, after setting the Accumulator to #$01, STA $0065 saves the Accumulator's value to byte $0065. What is $0065 for? That's for you to find out.

5) Yay! An RTS call! You can either follow it (click Step Into until you reach the RTS then click Step Into once more to see where it takes you) or ignore it, since it's obviously not going to help us right now.

6) Notice now we're at the address referred to in line 2 by BPL $8762. I will explain why this branch was so important very soon. This address tells the processor to load the value of $04C4 into the Accumulator. Simple stuff.

7) Good old CLC. When you see this, you know an ADC is coming up, probably. Sometimes you will see it before a bitwise function, too.

8) Sure enough, here's the ADC we expected. This adds the value of $0509 to the Accumulator's current value. In other words, this sets the Accumulator to $04C4+$0509.

9) Now the Accumulator is put back into byte $04C4. Basically, the game had just added $0509 to $04C4 directly. It is roundabout, but this is how you add two bytes in the RAM together.

10) Now the x-coordinate is loaded into the Accumulator again. That's a good sign, since we want to follow the x-coordinate anyway.

11) The value of address $04F2 is added to the Accumulator now. Notice $04F2 is set to #$01. Coincidentally, that's how many pixels Trevor moved each step. Could this mean...

12) Yep, the sum is added back into Trevor's x-coordinate. So this was obviously the subroutine to increase Trevor's x-coordinate.

If xold+($04F2)=xnew, then it is safe to say $04F2 is Trevor's hspeed (using Game Maker terminology). But what were those other two byte addresses about? (Remember, we're pretending you don't know.)

Let's look again at that subroutine, simplified a bit.
Code: [Select]
LDA $04C4
CLC
ADC $0509
STA $04C4
LDA $0438
ADC $04F2
STA $0438

First of all, since LDA will never affect the Carry bit of the Status register, it doesn't matter if the CLC call is placed before or after the LDA call. So let's move it up and see what we get.
Code: [Select]
CLC
LDA $04C4
ADC $0509
STA $04C4

LDA $0438
ADC $04F2
STA $0438

Do you see a pattern? We have two simple addition routines. The only difference is one has the Carry bit cleared before the addition. Remember that ADC is A+M+c. If the Carry bit is cleared prior, then the first routine is

$04C4+=$0509

The Carry bit isn't cleared before the next addition routine, so that means if the sum of the previous routine is greater than 255, the carry bit will be set and 1 will be added to the next routine.

But we're overlooking something important here. Both $04C4 and $0509 are set to #$00. No real addition took place here. So what gives? Obviously we are looking at an important subroutine, it just wasn't put to use in this situation. Write down the subroutine and make a note of it.

Now, why was it so important to check if Trevor's x-coordinate was positive? Isn't it always? The answer is no. Whenever an x-coordinate is on the left half of the screen, it's technically positive since it is less than #$80; if it is on the right half of the screen, it is technically negative since it is greater than #$7F. Yes, this can get confusing, but that's just how it is. So why does this matter, though? For now, disable the Debugger check by double-clicking on $0438 ECRW- to set it to $0438 -CRW- and then click Run. Go back to the game and move Trevor right and keep moving him right. Notice as soon as he reaches the middle of the screen he stops moving right. What is the value of $0438 at the middle of the screen? It's #$80! Trevor's x-coordinate is no longer positive, thus the code we saw earlier is never executed.

How does Trevor move left, then? We already know the formula to set Trevor's x-coordinate. We also know he can't be in the middle of the screen. So first off, move him left or right until he can actually move on the screen again. We know how $0438 gets set, so now we need to know how $04F2 get set. Actually you don't, but this is a tutorial, so just go along with it.

Load up the Debugger and add a new entry for $04F2 EC-W-. We don't need to read it, just write to it. Now go back to the game and move Trevor left. The Debugger will pop up and pause the game.
Code: [Select]
0E:960D:8D F2 04  STA $04F2 = #$01
0E:9610:8D 09 05  STA $0509 = #$00
0E:9613:A9 04     LDA #$04
0E:9615:8D 65 05  STA $0565 = #$02
0E:9618:A9 00     LDA #$00
0E:961A:4C 4A EF  JMP $EF4A

We hit a JMP command. That's a bad sign. We also don't know what $04F2 is being set to. Sure, the A register says the Accumulator has a value of #$00, but how'd it get that value? You need to scroll the code up. Do so slowly. FCEUX will usually display garbage code first and then sort it out as the rest of the code is revealed. Be warned: bytes in the PRG-ROM addresses are often garbage (they're actually specific values hard-coded into the program) that can trick FCEUX.
Code: [Select]
0E:960B:A9 00     LDA #$00
0E:960D:8D F2 04  STA $04F2 = #$01

Whenever you see LDA #$00, you are almost always looking at a byte initialization routine. In other words, ignore it. Later on we will see how these can be useful for hacking, but for now click Run to find the next STA $0F42 call.

What the deuce? The game resumed running as normal again! The reason is simple: Trevor has an onKeyPress event, basically. Now that he's already facing left, try moving left again. It happened again! If we trace $0438 again, we'll see that $04F2 isn't #$00 but #$FF. How do we get Trevor to keep moving left while the Debugger is up? Cheat.

Open the Cheats function and reset it again. You don't need to both with clearing junk bytes this time. Move Trevor right and while holding down the directional button, click on the Cheats window to pause the game. Now click Not Equal. Click on the game window and let go of the directional key so Trevor stops. Go back to the Cheats and click Not Equal again. Now click on the game window then go back to the Cheats. This time click Equal. Hopefully the list will have been narrowed down to a very small number, ideally 2 entries. Press the directional buttons (including UP and DOWN) and watch the variables. Note the ones that change. It should be $0028 and $00FA. Zero Page entries. It makes sense, if you think about it. Set $0028 to #$02 (the value it is set to when you press LEFT) then tell the Debugger to watch $04F2 again.

It stopped instantly. We found the right byte for handling the controller, but it's still setting $04F2 to #$00. Click Run and see if anything different happens. Sure enough, the Debugger jumps to another address and stops. This time the Accumulator is set to #$FF. Time to find out where that got set:
Code: [Select]
0E:9727:A9 FF     LDA #$FF
0E:9729:A0 00     LDY #$00
0E:972B:8C 09 05  STY $0509 = #$00
0E:972E:8D F2 04  STA $04F2 = #$00
0E:9731:4C 83 96  JMP $9683

This is a fun little method Konami uses once in a while. Codes like these suggest there were multiple programmers. There was no need to load two registers. For people like me, this makes reading ASM fun. The above code could have just as easily been performed with
Code: [Select]
LDA #$00
STA $0509
LDA #$FF
STA $04F2

Either way, the code is straightforward. When LEFT is pressed, set $04F2 and $0509 to #$00; when LEFT is held down, set $0438 to #$FF. Of course it's not as clean cut as that. This only applies when Trevor is on the ground. How does the game detect that? Some day you will find out, but for now you are not ready.

Instead, let's try to trace Trevor's y-coordinate. Open the Cheats function and reset it. Make Trevor jump then quickly pause the game by clicking on the Cheats function. Since y-values increase as you move down the screen, we need to check when Trevor's y-coordinate is less than it was previously, so click Less Than. Go back to the game then quickly back to the Cheats function. Click Less Than again. Be careful, Trevor will stop at the peak of the jump and then move down again. As soon as you see him reach the apex, don't do any searches until he starts moving down, then use Greater Than until he lands on the ground. Now make Trevor jump again and find the byte or byte that most likely is his y-coordinate. You should have narrowed it down to $041C.

Go back to the Debugger and delete the old breakpoints. Add $041C ECRW- to the list. Prevent breaking at PC addresses $8030, $8BDB, and $FCEA  (just like when we had to prevent unwanted breakpoints while watching $0438). Make Trevor jump again. The Debugger should pause the game again.
Code: [Select]
0E:97CF:AD 1C 04  LDA $041C = #$C0
0E:97D2:85 06     STA $0006 = #$9D
0E:97D4:20 4F 98  JSR $984F
0E:97D7:AD C1 05  LDA $05C1 = #$00
0E:97DA:F0 65     BEQ $9841
0E:97DC:A2 10     LDX #$10
0E:97DE:A9 FB     LDA #$FB
0E:97E0:20 DD FC  JSR $FCDD
0E:97E3:F0 0C     BEQ $97F1
0E:97E5:A2 08     LDX #$08
0E:97E7:A9 FB     LDA #$FB
0E:97E9:20 FF 83  JSR $83FF
0E:97EC:F0 30     BEQ $981E
0E:97EE:4C 41 98  JMP $9841

Look closely at that code. Notice all the JSR calls? See the addresses they all go to? One of them is repeated. Not only that, it's also very close to one of the addresses we told the Debugger to allow. It's safe to skip this code, but let's follow it through the first JSR routine anyway and see where it takes us:
Code: [Select]
0E:984F:AD 1C 04  LDA $041C = #$C0
0E:9852:C9 08     CMP #$08
0E:9854:B0 11     BCS $9867
0E:9856:AD C1 05  LDA $05C1 = #$00
0E:9859:D0 0C     BNE $9867
0E:985B:AD 65 05  LDA $0565 = #$08
0E:985E:C9 26     CMP #$26
0E:9860:F0 08     BEQ $986A
0E:9862:A2 00     LDX #$00
0E:9864:20 4F 97  JSR $974F
0E:9867:4C BF 98  JMP $98BF

The first two lines load the y-coordinate into the Accumulator and then compare it to #$08. If that sets off any alarms in your head, make a note oft he addresses so you can refer to them later. If Trevor is above #$08, all that code is executed (A CMP M is the same as A-M, so the Carry bit would be cleared if A was less than M, thus forcing the processor to skip the BPL call). None of this code seems ideal to what we're looking for, so click Run again to find the next reference to $041C:
Code: [Select]
0E:8716:AD DB 04  LDA $04DB = #$00
0E:8719:18        CLC
0E:871A:6D 37 05  ADC $0537 = #$00
0E:871D:8D DB 04  STA $04DB = #$00
0E:8720:AD 1C 04  LDA $041C = #$C0
0E:8723:6D 20 05  ADC $0520 = #$FB
0E:8726:8D 1C 04  STA $041C = #$C0
0E:8729:60        RTS

A RTS call! We're inside a subroutine. Not only that, that code looks somewhat familiar...
Code: [Select]
LDA
CLC
ADC
STA
LDA
ADC
STA

It's the same structure as the horizontal movement code! We can thus draw up a side-by-side comparison:

x4C4 : y4DB :: ?
x509 : y537 :: ?
x4F2 : y520 :: speed
x438 : y41C :: coordinates

If you keep clicking Run until you get to this code again, you'll notice just like before the first two bytes are empty. Clearly that code structure is very important.

In other words, memorize it and learn to look for it in every game you attempt to hack.

You'll notice $0520 gets changed frequently, so we should look into how that is being handled.
« Last Edit: August 09, 2012, 10:29:16 PM by TheouAegis »
Your mom has had more floppies put in her than a Commodore 64!


Follow my lack of progress on my game at my blog:
http://gmvania.blogspot.com

Offline Inccubus

  • Wannabe Great Old One
  • Master Hunter
  • *****
  • Posts: 3265
  • Gender: Male
  • Warrior
  • Awards The Retro Gamer: Has a heated passion for the oldschool VG Titles. SuperOld Dungeonite: Members who have been around since the oldOLD days. Permanent Resident: Seems to always be around to post/reply.
    • Awards
  • Favorite Game: Vampire Killer (MSX)
  • Likes:
Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
« Reply #5 on: August 10, 2012, 01:33:11 AM »
0
I could use some help finding the tile data for the title screen.

EDIT: Never mind. I got it. The tile drawing routine here is very different from the Megaman games I've edited. Seems it loads tile map data and tile attribute data separately and that they are stored separately in the rom. Where as in Megaman 5 all of it is kept together in one table along with mirroring data and loaded together.

EDIT 2:

OK. So I loaded up the game and paused the emulation on the title screen. Looking at the PPU data in the HEX editor in FCEUX I found the values of part of the tiles on the title screen and made a write break point for the addresses of the tiles data I found. Then I rebooted and went back to the title screen. It stopped on the tile loading routine when it got to the addresses in the PPU I marked. Looking at the code in the debugger I looked at the rom addresses that data was being loaded from and sure enough I found the tile data on the ROM. It was a bit of a hassle to rearrange the tiles because of how the data is stored. It loads in two different ways depending on if it will repeated load the same tile value or a series of different tiles. Each entry consists of two parts; first the number of bytes to load and then the value(s) of those bytes. $00 - $7f repeats the next byte value a number of times equal to itself. $80 - $FF loads a number of following values equal to itself -$80.
So...
$07DD instructs the routine to load tile value $DD seven times.
And...
$8401020304 instructs the routine to load tile data $01, $02, $03, and then $04.

That said, I got the new tile map for my patch done... although I accidentally over wrote the new palette data.

« Last Edit: August 10, 2012, 04:21:53 AM by Inccubus »
"Stuff and things."

Offline TheouAegis

  • Amateur Auteur of GMvania
  • Master Hunter
  • *****
  • Posts: 1860
  • Gender: Male
  • Awards The Retro Gamer: Has a heated passion for the oldschool VG Titles. The Great Defender will always defend the object of his or her fandom. Hack Master makes creations out of CV parts. (S)he makes Dr. Frankenstein proud.
    • GMvania Developer's Blog
    • Awards
  • Likes:
Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
« Reply #6 on: August 10, 2012, 04:59:49 AM »
0
Back yo' shit up, yo!
*shakes head in dismay*
tsk tsk tsk

I never looked in the title screen data. Level design data is much easier I think. Each TSA has a byte value, 256 TSA max. This serves as a map to the actual tile layout. But using reVamp is much easiser for that kind of stuff anyway. Good job on editing the title screen (was prehacked in Stake but not reVamp). Now you need ot edit the church and prologue. :D
Your mom has had more floppies put in her than a Commodore 64!


Follow my lack of progress on my game at my blog:
http://gmvania.blogspot.com

Offline Inccubus

  • Wannabe Great Old One
  • Master Hunter
  • *****
  • Posts: 3265
  • Gender: Male
  • Warrior
  • Awards The Retro Gamer: Has a heated passion for the oldschool VG Titles. SuperOld Dungeonite: Members who have been around since the oldOLD days. Permanent Resident: Seems to always be around to post/reply.
    • Awards
  • Favorite Game: Vampire Killer (MSX)
  • Likes:
Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
« Reply #7 on: August 10, 2012, 11:37:54 AM »
0
Actually, I'm working off the Akumajou Densetsu translated ROM. So, no need to edit the church or prologue. ;)
"Stuff and things."

Offline TheouAegis

  • Amateur Auteur of GMvania
  • Master Hunter
  • *****
  • Posts: 1860
  • Gender: Male
  • Awards The Retro Gamer: Has a heated passion for the oldschool VG Titles. The Great Defender will always defend the object of his or her fandom. Hack Master makes creations out of CV parts. (S)he makes Dr. Frankenstein proud.
    • GMvania Developer's Blog
    • Awards
  • Likes:
Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
« Reply #8 on: August 12, 2012, 11:37:09 AM »
0
I feel like I have the same topic in two different pots...

Before I continue with my long-ass-winded tutorial, I figured I'd share a little blog update I've been working on. Anyone interested in the mechanics for CV3 should check out my blog (check my forum sig for the link). I've included numerous RAM addresses and will eventually include ROM addresses (the code stored in the cart itself) as well> I just realized one of my entries I listed where to find a value in the RAM, but it'd be more useful to list the ROM address (which you can calculate from the RAM address if you read the FCEUX help file). Anyway, some values so far included in the list I've compiled (which I will be updating quite often):

Controller I/O (how buttons are read, useful if you need to keep a button pressed)
Item drop codes
Movement variables (should be complete)
Trevor's stats (in RAM)
Enemy stats (in RAM)
Enemy random drop generation (!!)  ;D
Stage information (don't mess with it in-game, though)
Palette entries
Step counter / randomizer
and much more...
« Last Edit: August 12, 2012, 11:38:41 AM by TheouAegis »
Your mom has had more floppies put in her than a Commodore 64!


Follow my lack of progress on my game at my blog:
http://gmvania.blogspot.com

Offline Inccubus

  • Wannabe Great Old One
  • Master Hunter
  • *****
  • Posts: 3265
  • Gender: Male
  • Warrior
  • Awards The Retro Gamer: Has a heated passion for the oldschool VG Titles. SuperOld Dungeonite: Members who have been around since the oldOLD days. Permanent Resident: Seems to always be around to post/reply.
    • Awards
  • Favorite Game: Vampire Killer (MSX)
  • Likes:
Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
« Reply #9 on: August 12, 2012, 11:27:54 PM »
0
Sweet. I've been finding some title screen and intro sequence info, of course.
"Stuff and things."

Offline TheouAegis

  • Amateur Auteur of GMvania
  • Master Hunter
  • *****
  • Posts: 1860
  • Gender: Male
  • Awards The Retro Gamer: Has a heated passion for the oldschool VG Titles. The Great Defender will always defend the object of his or her fandom. Hack Master makes creations out of CV parts. (S)he makes Dr. Frankenstein proud.
    • GMvania Developer's Blog
    • Awards
  • Likes:
Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
« Reply #10 on: August 14, 2012, 05:19:56 PM »
0
Added the tile map last night.
Still need to work out. Still need to work out what the difference between $5 and $7 is (I'm guessing they were intended to do different damage, but that doesn't happen in the Jap version so I'll have to check the US version). Also need to check what $8 through $B actually do, if anything, since $C causes a cycle to $F, which appears to be the same as $4 or whatever, only it also causes the game to crash half the time.

Still, I am totally stoked that I figured out the tile map.  ;D
Your mom has had more floppies put in her than a Commodore 64!


Follow my lack of progress on my game at my blog:
http://gmvania.blogspot.com

Offline Inccubus

  • Wannabe Great Old One
  • Master Hunter
  • *****
  • Posts: 3265
  • Gender: Male
  • Warrior
  • Awards The Retro Gamer: Has a heated passion for the oldschool VG Titles. SuperOld Dungeonite: Members who have been around since the oldOLD days. Permanent Resident: Seems to always be around to post/reply.
    • Awards
  • Favorite Game: Vampire Killer (MSX)
  • Likes:
Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
« Reply #11 on: August 14, 2012, 10:14:29 PM »
0
Sweet. ReVamp doesn't have tile attributes, right? Would be great to add some of this info to that editor. Speaking of which... have you ever looked into how the room data is set up? It would be fantastic if you could crack that nut. That would greatly improve the hacking community's ability to make really kick-ass CV3 hacks.
"Stuff and things."

Offline TheouAegis

  • Amateur Auteur of GMvania
  • Master Hunter
  • *****
  • Posts: 1860
  • Gender: Male
  • Awards The Retro Gamer: Has a heated passion for the oldschool VG Titles. The Great Defender will always defend the object of his or her fandom. Hack Master makes creations out of CV parts. (S)he makes Dr. Frankenstein proud.
    • GMvania Developer's Blog
    • Awards
  • Likes:
Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
« Reply #12 on: August 24, 2012, 08:05:49 AM »
0
What room data exactly? I found the solid tile map, as I said. I should be able to easily track down the room orientations by watching the RAM hashes for the room orientation byte and then tracing it back to the PRG-ROM and then tracing that back to the ROM itself. Simple stuff. I haven't traced stairs yet. It should be easy enough if I sat down and tried. Would just have to find a stair, save the state, tweak the stair in reVamp, save to another state, then RAM hash my way down to the correct addresses (X, Y, and direction), then trace it back to the PRG-ROM and then to the ROM itself. So what else?
Your mom has had more floppies put in her than a Commodore 64!


Follow my lack of progress on my game at my blog:
http://gmvania.blogspot.com

Offline Inccubus

  • Wannabe Great Old One
  • Master Hunter
  • *****
  • Posts: 3265
  • Gender: Male
  • Warrior
  • Awards The Retro Gamer: Has a heated passion for the oldschool VG Titles. SuperOld Dungeonite: Members who have been around since the oldOLD days. Permanent Resident: Seems to always be around to post/reply.
    • Awards
  • Favorite Game: Vampire Killer (MSX)
  • Likes:
Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
« Reply #13 on: August 24, 2012, 09:43:29 AM »
0
I'd like to know everything about how the rooms are laid out so I can modify them as I see fit. Plus if there was a way to change the size of a stage, that would be even better.
"Stuff and things."

Offline TheouAegis

  • Amateur Auteur of GMvania
  • Master Hunter
  • *****
  • Posts: 1860
  • Gender: Male
  • Awards The Retro Gamer: Has a heated passion for the oldschool VG Titles. The Great Defender will always defend the object of his or her fandom. Hack Master makes creations out of CV parts. (S)he makes Dr. Frankenstein proud.
    • GMvania Developer's Blog
    • Awards
  • Likes:
Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
« Reply #14 on: August 25, 2012, 04:58:15 PM »
0
Plus if there was a way to change the size of a stage, that would be even better.

Done. Go back to the blog entry where I'm keeping track of different RAM addresses. I've been working on the ROM addresses today since the guy that made reVamp wasn't kind enough to share all his notes.
Your mom has had more floppies put in her than a Commodore 64!


Follow my lack of progress on my game at my blog:
http://gmvania.blogspot.com

Tags:
 

anything