Castlevania Dungeon Forums

The Castlevania Dungeon Forums => Fan Stuff => Topic started by: TheouAegis on August 08, 2012, 10:02:59 AM

Title: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on August 08, 2012, 10:02:59 AM
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 (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...
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Inccubus on August 09, 2012, 05:52:56 AM
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:
(https://castlevaniadungeon.net/forums/proxy.php?request=http%3A%2F%2Fi256.photobucket.com%2Falbums%2Fhh189%2Fkoala_knight%2FNES%2520Graphic%2520Edits%2FNewTitleGraphicXXfinalpalettemap.png&hash=3b314bd1914061a872889e7c7e8d836b338e4369)

Currently in-game:
(https://castlevaniadungeon.net/forums/proxy.php?request=http%3A%2F%2Fi256.photobucket.com%2Falbums%2Fhh189%2Fkoala_knight%2FNES%2520Graphic%2520Edits%2FCastlevaniaIII-DemonCastleLegendTNewTitle-3.png&hash=f217160fa05425f55bfc4998124b1bc4ef93dcd5)

As you can see it still needs to have some tiles moved around and some tile attributes to be changed.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on August 09, 2012, 09:28:57 PM
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!"
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Inccubus on August 09, 2012, 11:30:32 PM
Yeah, I remember reading something like that.
Title: Theou Aegis' Rookie Guide To 6502 ASM Reading, Pt. 2
Post by: TheouAegis on August 10, 2012, 03:20:09 AM
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.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Inccubus on August 10, 2012, 06:33:11 AM
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.

(https://castlevaniadungeon.net/forums/proxy.php?request=http%3A%2F%2Fi256.photobucket.com%2Falbums%2Fhh189%2Fkoala_knight%2FNES%2520Graphic%2520Edits%2FCastlevaniaIII-DemonCastleLegendTNewTitle-4.png&hash=88acbf55a585951784e61a86df51c0784ac9246a)
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on August 10, 2012, 09:59:49 AM
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
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Inccubus on August 10, 2012, 04:37:54 PM
Actually, I'm working off the Akumajou Densetsu translated ROM. So, no need to edit the church or prologue. ;)
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on August 12, 2012, 04:37:09 PM
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...
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Inccubus on August 13, 2012, 04:27:54 AM
Sweet. I've been finding some title screen and intro sequence info, of course.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on August 14, 2012, 10:19:56 PM
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
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Inccubus on August 15, 2012, 03:14:29 AM
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.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on August 24, 2012, 01:05:49 PM
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?
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Inccubus on August 24, 2012, 02:43:29 PM
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.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on August 25, 2012, 09:58:15 PM
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.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Inccubus on August 26, 2012, 04:34:20 AM
Hells yeah!!  ;D
That's like the most important thing that the CV3 hacking community needs.
Having control of how the stages are laid out and how long they are will allow a lot of creative stage design for new hacks.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on August 26, 2012, 12:49:57 PM
Ugh. I have my work cut out for me today...

So it turns out the tile maps aren't predefined in the ROM. That's a good thing, since it keeps ROM size very, very low. Unfortunately, that means I have to study the stage generation script and figure out how the tile maps are generated. I hain't excited about it because at the same time the name tables and PPU are being written to and they just cause more numbers to get tossed around. OH well, I already have my theory on how it's done (a very small, specific subset of tiles), so it shouldn't be too rough .
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on August 27, 2012, 10:58:47 AM
Ok, I really do have multiple threads about the same thing. :D

Like Ippolito Rosellini to Jean-Francois Champollion and like Champollion to Thomas Young, building upon the efforts of those before me, I think I have stumbled on part of the level map for CV3.

When I woke up this morning I looked at my screen and noticed a pattern in the hex editor that looked vaguely familiar, so I went back to my notes and saw a similar pattern there. Delving further, I looked in the PRG-ROM for what I assumed was a RAM offset that I was looking at. I traced that PRG-ROM address back to its corresponding address in the ROM and noticed I had that same address in my notes -- the scrolling definitions for vertical stages!

This in and of itself isn't that great of a discovery because there's one significant problem: Konami didn't code a simple level map, it's more like a cyphered treasure map. I still need to decipher it. The problem is how rooms are laid out. You have three variables: stage, block, and room. When you walk through a door, block gets increased by 1. When you beat a boss then walk down the next stairs, stage gets increased. The room variable isn't so simple. If you walk UP stairs, room gets increased by 1. If you walk through a door, it gets set according to some variable that gets loaded into the memory, which in turn also sets another variable that I have yet to figure out what it does. If you walk DOWN stairs, room gets decreased by 1, but if room was already 0 it gets set to 1. Problem is, many rooms with downward stairs aren't set like that and so that little function isn't needed most of the time, I think. And I still haven't figured out how that stair in the Black Forest warps you from block 1 to block 2.

So what this all means is the values stored at the specific addresses holding the vertical scroll values, which themselves appear to be in a sequential format, aren't referenced sequentially but instead by a roundabout means which suggests that the actual level map is handled similarly. I found another map right above the vertical scroll map that I will have to look at this week.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Inccubus on August 27, 2012, 12:11:19 PM
I'm fairly confident that the funky forest stairs are hard coded since there's nothing else like them anywhere in the rest of the game that I know of.

On the subject of the convoluted shit-storm they decided to use for level layouts... That is exactly why no one has ever done a really extensive hack of CV3. There was one hack for CV1 that was able to mess with the layouts a little, but that's the farthest anyone ever got to my knowledge.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on September 01, 2012, 06:26:03 PM
Almost done cracking the doors. It took me a while because it wasn't registering in my mind what RAM addresses $0053 and $0054 were. Once I figured out that was view_xview, I cracked the door code. The author of reVamp said you can't change which side of the room the doors are on. Not true. It's as simple as 0 for left and 1 for right. I changed the door in Stage 1-01:0 (starting room) to the left side of the room.

Before you get too giddy with anticipation, it's not quite so simple. Konami stunted game modding by making their code somewhat efficient. There are a very limited number of doors allowed. And you can't have doors on both sides of the room, only on one side. This isn't Simon's Quest.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Inccubus on September 02, 2012, 02:12:34 AM
It's not surprising that it's set up like that. After all if it wasn't there would be no need for the duplicate rooms in the City Clocktower. BTW, I call it the 'City Clocktower' because it isn't the traditional one within the castle and it's on the outskirts of the town. That never made sense to me. Why would you build a clocktower OUTSIDE your city limits?

Anyway, good job on that, dude. The more actual code you figure out, the better your engine will be. Right now I'm developing a platformer engine based on a document I found that details the physics code of SMB1 & SMB3. I hope to pretty much blow every other GM SMB Engine outta the water with this, plus I'll probably end up using it for many of my own projects, too.
Title: Theou Aegis' Rookie Guide To 6502 ASM Reading, Parte Trois
Post by: TheouAegis on September 02, 2012, 04:30:58 AM
In the last lesson, we isolated the Trevor's horizontal and vertical movement codes. We will pick up where we left off -- isolating changes in address $0520.

Disable all breakpoints and run the game so Trevor finishes his jump. Change the $041C ECRW- breakpoint to instead watch $0520. Delete the conditions, since they won't apply to $0520. Enable the breakpoint and make Trevor jump. The debugger immediately shows the following result:

Code: [Select]
0E:98E6:9D 20 05  STA $0520,X @ $0520 = #$00
0E:98E9:FE D8 05  INC $05D8,X @ $05D8 = #$09
0E:98EC:60        RTS

That's a bit short and doesn't tell us much. Well, not yet. First, look at the registers. The Accumulator is set to #$FB, X is #$00 and Y is #$09.

First, let's analyze the Accumulator. Its value is greater than #$80. The Negative bit in the status register isn't set currently, but we know that #$FB is either 251 or -5, depending on whether it's handled as an unsigned or signed byte. However, even if it's treated as an unsigned byte, the Negative bit should still be set. It's not, so that means something important happened just prior to $98E6 was handled.

There is another clue that something significant just occurred. The Y register is set. We can expect the X register to be set (even though here it isn't) because that typically handles RAM offsets for instances (Trevor is X:#$00, enemy 1 is X:#$01, enemy 2 is X:#$02, and so on). The Y register is typically only set when temporarily housing accumulator values or handling offsets in the PRG-ROM.

We need to see more of the PRG-ROM if we want to find any significant code that lead up to this breakpoint. Scrolling up through the debugger to the next RTS call reveals the following:

Code: [Select]
0E:98C6:AD 65 05  LDA $0565 = #$08
0E:98C9:C9 26     CMP #$26
0E:98CB:F0 CC     BEQ $9899
0E:98CD:A2 00     LDX #$00
0E:98CF:A9 00     LDA #$00
0E:98D1:9D 37 05  STA $0537,X @ $0537 = #$00
0E:98D4:9D DB 04  STA $04DB,X @ $04DB = #$00
0E:98D7:BC D8 05  LDY $05D8,X @ $05D8 = #$09
0E:98DA:BD C1 05  LDA $05C1,X @ $05C1 = #$00
0E:98DD:D0 1C     BNE $98FB
0E:98DF:B9 7B 98  LDA $987B,Y @ $9884 = #$FB
0E:98E2:C9 81     CMP #$81
0E:98E4:F0 07     BEQ $98ED
0E:98E6:9D 20 05  STA $0520,X @ $0520 = #$00
0E:98E9:FE D8 05  INC $05D8,X @ $05D8 = #$09
0E:98EC:60        RTS

(NOTE: If you get a code that instead contains the routine STA $006B, it means you are in a vertical room. Move to a horizontal room and then try again.)

The first few lines is a simple comparison checking if $0565=#$26. We won't worry about it here, but this checks if Trevor's sprite is his knocked-back sprite (for when he's wounded). The next few lines simply set X to refer to Trevor's offsets and then initialize a few RAM addresses.

The Y register gets set to the value of $05D8. For now it's an obscure byte that gets increased at the end of this subroutine. You'll eventually find out it's the fall height counter used to determine if Trevor lands nimbly or awkwardly (characterized by a momentary stun upon landing).

The next line checks an unknown address, $05C1, and skips the rest of this code if the byte has been set. This is actually a very important address and we will return to it later. Anything that causes the game to skip an entire segment of code is probably worth looking into.

Now we're where we want to be. Notice the next line refers to an address located in the PRG-ROM with the Y register as an offset. We still don't know why Y is set to 9, but that's not important right now. These types of calls tell us a lot of relevant information beyond the scope of our current task, so let's analyze the one at hand.

LDA $987B,Y @ $9884 = #$FB

This code sets the Accumulator to the value at $9884 in the current bank, which is #$FB. This means $9884 doesn't actually contain any routine calls, it just stores a constant. How many other addresses around it store constants? According to this line, the list of this particular class of constants starts at $987B. The next line in the routine checks if the value loaded into the Accumulator is #$81, so we should look at the PRG-ROM range starting from $987B for any address with the value #$81.

Code: [Select]
0E:987B:80        UNDEFINED
0E:987C:FA        UNDEFINED
0E:987D:FA        UNDEFINED
0E:987E:FA        UNDEFINED
0E:987F:FA        UNDEFINED
0E:9880:FA        UNDEFINED
0E:9881:FB        UNDEFINED
0E:9882:FB        UNDEFINED
0E:9883:FB        UNDEFINED
0E:9884:FB        UNDEFINED
0E:9885:FB        UNDEFINED
0E:9886:FD FD FD  SBC $FDFD,X @ $FDFD = #$B1
0E:9889:FD FD FE  SBC $FEFD,X @ $FEFD = #$B6
0E:988C:FE FE FF  INC $FFFE,X @ $FFFE = #$F5
0E:988F:FF        UNDEFINED
0E:9890:FF        UNDEFINED
0E:9891:FF        UNDEFINED
0E:9892:00        BRK
0E:9893:FF        UNDEFINED
0E:9894:00        BRK
0E:9895:00        BRK
0E:9896:00        BRK
0E:9897:00        BRK
0E:9898:81

Whew! That's a big list and much of it is just repeated values. You're about to learn why I've called some of Konami's coding very inefficient.

If you actually measure out Trevor's movement in the game when he jumps, you'll discover his y-coordinate changes by -5, then -3, then -2, then -1, then 0, then -1, then 0 again, then it essentially reverses. Looking at $9884, we in fact see that same pattern. We've just discovered Trevor's vspeed for when he jumps. You'll notice there is no gravity or complex trigonometric function to calculate his vspeed. It's just a simple array of constants taking up a big (relatively speaking) chunk of memory.

There is one more useful tidbit we can garner from this section of code. Hold your mouse over the empty grey bar on the far left side of the Debugger near $987C. At the bottom you will see the word "Offset". The large hexadecimal number after that is the identical ROM address. In other words, if you want to permanently change Trevor's jumping pattern, that is the address in the ROM you would need to edit (but don't edit $987B or $9898, as the game uses those as specific markers).

The fall height gets increased at the end of the subroutine, which we can surmise will be applied to the Y register in order to increase the PRG-ROM offset for Trevor's vspeed. Click Run to skip ahead to the next reference of $0520. It's the vertical movement subroutine. We've already dealt with that, so click Run once again. As expected, it's the code we just analyzed. So now we know how Trevor jumps.

But what about that address we neglected earlier, $05C1? Let Trevor finish his jump routine, then change the breakpoint to watch for $05C1. Now make Trevor jump again.

Code: [Select]
0E:9624:A9 08     LDA #$08
0E:9626:8D 65 05  STA $0565 = #$08
0E:9629:A5 2A     LDA $002A = #$80
0E:962B:85 10     STA $0010 = #$80
0E:962D:A2 00     LDX #$00
0E:962F:A9 16     LDA #$16
0E:9631:9D 00 04  STA $0400,X @ $0400 = #$16
0E:9634:A9 00     LDA #$00
0E:9636:9D C1 05  STA $05C1,X @ $05C1 = #$01
0E:9639:A9 09     LDA #$09
0E:963B:9D D8 05  STA $05D8,X @ $05D8 = #$07
0E:963E:A5 10     LDA $0010 = #$80
0E:9640:4A        LSR
0E:9641:B0 08     BCS $964B
0E:9643:4A        LSR
0E:9644:B0 10     BCS $9656
0E:9646:A9 00     LDA #$00
0E:9648:A8        TAY
0E:9649:F0 14     BEQ $965F
0E:964B:A9 00     LDA #$00
0E:964D:9D A8 04  STA $04A8,X @ $04A8 = #$00
0E:9650:A9 01     LDA #$01
0E:9652:A0 00     LDY #$00
0E:9654:F0 09     BEQ $965F
0E:9656:A9 01     LDA #$01
0E:9658:9D A8 04  STA $04A8,X @ $04A8 = #$00
0E:965B:A9 FF     LDA #$FF
0E:965D:A0 00     LDY #$00
0E:965F:9D F2 04  STA $04F2,X @ $04F2 = #$00
0E:9662:98        TYA
0E:9663:9D 09 05  STA $0509,X @ $0509 = #$00
0E:9666:60        RTS

I went ahead and scrolled up into the earlier code for education's sake. At $9636 the game clears $05C1. The next line sets $05D8, which I said was the fall height, to #$09.

The next line loads the value of $0010 into the Accumulator. That's a kind of Zero Page reference I call Hacker Hell. Some addresses in the Zero Page store useful values, such as timers, hearts, score, and whatnot. Byte $0010 is not one of those useful addresses. It's still a useful address, just not out of context. No matter where you are in the PRG-ROM, some addresses in the Zero Page will hold the same value, but not this one. When dealing with the Zero Page, it's helpful to have a notepad handy so you can keep track of which values are loaded into it. Sometimes a PRG-ROM offset is loaded into the Zero Page, while other times it's something specific like the current instance's bounding box.

Back on track, we know what happens when $05C1 is #$00. We want to know when it changes and what happens in such cases. Change the breakpoint to $05C1 EC-W- and then run the game until the next breakpoint. Eventually you should reach this point:

Code: [Select]
0E:98ED:A9 00     LDA #$00
0E:98EF:9D 20 05  STA $0520,X @ $0520 = #$00
0E:98F2:DE D8 05  DEC $05D8,X @ $05D8 = #$1C
0E:98F5:A9 01     LDA #$01
0E:98F7:9D C1 05  STA $05C1,X @ $05C1 = #$00
0E:98FA:60        RTS

Short, sweet, and to the point. This is a great subroutine! Wait a second, though. Some of it looks a little familiar. Once again we're amending $05D8, but whereas last time the code increased it, this time it decreased it. Also, the address at the start of the subroutine looks familiar. If we scroll up a little farther, we'll find the branch call that sent us to this subroutine:

Code: [Select]
0E:98DF:B9 7B 98  LDA $987B,Y @ $9898 = #$81
0E:98E2:C9 81     CMP #$81
0E:98E4:F0 07     BEQ $98ED

That's the branching conditional from our very first code when we were tracing $0520! Now we know what the purpose of that #$81 was -- to set $05C1. Remember that first routine branched out if $05C1 was set, so change the breakpoint back to $05C1 ECRW- and click Run to jump straight to said conditional. Click Step Into a couple times to follow the branch.

Code: [Select]
0E:98FB:B9 7B 98  LDA $987B,Y @ $9897 = #$00
0E:98FE:C9 80     CMP #$80
0E:9900:F0 0C     BEQ $990E
0E:9902:DE D8 05  DEC $05D8,X @ $05D8 = #$1C
0E:9905:49 FF     EOR #$FF
0E:9907:18        CLC
0E:9908:69 01     ADC #$01
0E:990A:9D 20 05  STA $0520,X @ $0520 = #$00
0E:990D:60        RTS

This is a nice little subroutine for learning. We have our vspeed reference offset first. This time it branches on #$80. Looking back at Trevor's vspeed list, that's at $987B, or when $05D8 equals #$00. Said variable gets decreased, so we know eventually it will indeed likely reach #$00.

Now Konami does something fun. Well, it's fun if you don't know squat about working with bytes. They take Trevor's vspeed and EOR it with $FF. In other words, they invert it. Since his vspeed according to that array is always negative, this makes it positive. However, the result is 1 off from what you'd expect -- #$FB^#$FF does not equal #$05, but #$04. So they add #$01. This is how you retrieve the absolute value of a signed byte, but in this case it's just a straight inversion of vspeed. (Learning how to find the absolute value, though, helped immensely in cracking collision subroutines).



That's it for today's lesson.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on September 03, 2012, 02:30:49 AM
^
^
^
^

Enjoy the nice third part of my ASM reading/cracking tutorial above.

That said, I'm closer to cracking the room map. It's still very convoluted and driving me nuts. So many variables crammed so close together. Some look like RAM offsets and then end up being compressed data points. Other times it's not only a RAM offset but compressed data points as well. Figuring out those compressed data points is a significant part of the construct, though. And of course it all depends on four (I upped the count last night) tiny little variables:

Stage
Block
Subroom
Screen

Some codes will literally lead you through four different RAM offsets based on each of those.

LDA stage
ASL
TAY
LDA offset1,y
STA offset1a
INY
LDA offset1,y
STA offset1b
LDA block
ASL
TAY
LDA (offset1a),y
STA offset2a
INY
LDA (offset1a),y
STA offset2b
LDA subroom
ASL
TAY
LDA (offset2a),y
STA offset3a
INY
LDA (offset2a),y
STA offset3b
LDY screen
INY
INY
LDA (offset3a),y


In the words of Egon Spengler:
"Your mother!"
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Inccubus on September 03, 2012, 05:46:51 PM
LOL. And that is why we've never had realyy good CV hacks, only good fangames. ;)
Title: Adventures in NES Hacking (or How I Learned to Love the PC)
Post by: TheouAegis on September 06, 2012, 12:04:27 AM
Adventures In NES Hacking
or How I Learned to Love the PC


Ok. The solids definitions are located at address XXXX. How did it get to that address? Let's scroll up. ... Hm. It's based on a single variable. Do I have a note for that one yet? ... Damn, no. Ok, let's scroll up and look for that variable getting set. Ok, dead-end. Search the RAM for the nearest reference to that dead-end. ... Success! Let's see where that address takes me. ... Crap, JSR hell. Fine, let's follow that JSR. ... Le'see, variables, variables, more variables. None of them the one I want. Ok then, breakpoint time! Set the breakpoint for that variable to get writ. And... run. Ok, gotta get to a new room. Walk up the stairs. Doo dee doo de doo. Yay! Variable writ! Hm. What lead up to that value? Ok, load the value from an offset RAM address. ... Wait. ...
LDA stage.
ASL.
TAY.

... Crap.

LDA SomeBigNumber,Y
STA $000A.

Oh hell no.

INY
LDA SomeBigNumber,Y.
STA $000B.

Oh god no, please no.

LDA block
ASL
TAY.
LDA $000A,Y.

Fffff---.

STA $0008.
LDA $000A,Y.

FFFFFUUUUUUUUU----!

STA $0009.
LDA room

YOUR MOTHER!

ASL
TAY
LDA $0008,Y

*cries*

BMI distantOffset

Huh? Ooh! Progress! Let's goto this offset now... ... ...

LDA stage
ASL
TYA

FUCK YOU AND YOUR WEE DOGGY TOO!
*makes dinner*
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Inccubus on September 06, 2012, 08:17:39 AM
O_O

And that's why I never found the tile properties for the title screen.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on September 06, 2012, 02:49:50 PM
I did eventually figure out what said-variable was. I had to go to NESdev wiki to figure it out.

When a value is copied/stored to addresses $D000 thru $D003 or $E000 thru $E003 or $2007 or some other odd values that are unusually high, those are PPU references. $D000 thru $D003 are for the left side of the PPU (typically sprites), while $E000 thru $E003 are for the right side of the PPU, typically background tiles.

So yeah, figured out I was looking at the CHR select values. You can change those in reVamp.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Inccubus on September 06, 2012, 03:20:09 PM
Nice. Good info to have.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on September 09, 2012, 10:04:54 PM
Another useful feature in FCEUX is the Trace Logger. When you're using the Debugger, you might have a hunch that some particular RAM address is responsible for some outcome, but when you try to set a break point for that address, you either get too many results or you're forced to scroll through page after page of jumps and branches. That's where the Trace Logger can help out. It has other uses, but it can make looking at pages of code much easier because it will only list the PRG-ROM addresses that are actually handled. Your mind won't go numb from all the extra code on the screen -- what you see in the trace log is the only code you actually need.

Load up the Debugger and set a break point to pause the code roughly where you want it. If you are going through a lot of breaks with that particular address, typically you want to find the first break after sprites get updated (easy to do if you have something that actually changes sprites or position every step on the screen at the time). Now load up the Trace Logger. Click the radio for logging to a file. Click the Browse button and choose a location and name for the file. Click Start Logging. Go back to the Debugger and click Run. The Trace Logger will record up to 10000 lines of code or until the next break point. When you reach the break point, click Stop Logging. Locate the log file you created and open it in a word processor other than NotePad. You can now view all the code that was processed without the clutter of the code that was branched or jumped over. Look through the log file and hopefully the code you want will be in it. If you don't want the registers included on the right side, you can turn those off in the Trace Logger before recording. Personally I like having that information readily accessible.

Sample Trace Log (actual code)
Code: [Select]
$936C:A9 02     LDA #$02                   A:00 X:16 Y:00 S:F3 P:nvUbdiZc
$936E:85 62     STA $0062 = #$02           A:02 X:16 Y:00 S:F3 P:nvUbdizc
$9370:AC 65 05  LDY $0565 = #$08           A:02 X:16 Y:00 S:F3 P:nvUbdizc
$9373:20 14 E8  JSR $E814                  A:02 X:16 Y:08 S:F3 P:nvUbdizc
$E814:C8        INY                        A:02 X:16 Y:08 S:F1 P:nvUbdizc
$E815:68        PLA                        A:02 X:16 Y:09 S:F1 P:nvUbdizc
$E816:85 00     STA $0000 = #$9C           A:75 X:16 Y:09 S:F2 P:nvUbdizc
$E818:68        PLA                        A:75 X:16 Y:09 S:F2 P:nvUbdizc
$E819:85 01     STA $0001 = #$F3           A:93 X:16 Y:09 S:F3 P:NvUbdizc
$E81B:B1 00     LDA ($00),Y @ $937E = #$77 A:93 X:16 Y:09 S:F3 P:NvUbdizc
$E81D:85 02     STA $0002 = #$31           A:77 X:16 Y:09 S:F3 P:nvUbdizc
$E81F:C8        INY                        A:77 X:16 Y:09 S:F3 P:nvUbdizc
$E820:B1 00     LDA ($00),Y @ $937F = #$97 A:77 X:16 Y:0A S:F3 P:nvUbdizc
$E822:85 03     STA $0003 = #$F5           A:97 X:16 Y:0A S:F3 P:NvUbdizc
$E824:6C 02 00  JMP ($0002) = $9777        A:97 X:16 Y:0A S:F3 P:NvUbdizc
$9777:A5 28     LDA $0028 = #$00           A:97 X:16 Y:0A S:F3 P:NvUbdizc
$9779:29 40     AND #$40                   A:00 X:16 Y:0A S:F3 P:nvUbdiZc
$977B:F0 26     BEQ $97A3                  A:00 X:16 Y:0A S:F3 P:nvUbdiZc
$97A3:20 95 83  JSR $8395                  A:00 X:16 Y:0A S:F3 P:nvUbdiZc
$8395:A5 7D     LDA $007D = #$00           A:00 X:16 Y:0A S:F1 P:nvUbdiZc
$8397:D0 0F     BNE $83A8                  A:00 X:16 Y:0A S:F1 P:nvUbdiZc
$8399:A2 F8     LDX #$F8                   A:00 X:16 Y:0A S:F1 P:nvUbdiZc
$839B:A9 00     LDA #$00                   A:00 X:F8 Y:0A S:F1 P:NvUbdizc
$839D:20 DE FC  JSR $FCDE                  A:00 X:F8 Y:0A S:F1 P:nvUbdiZc

And yes, that mess of code was actually useful for me. I was trying to figure out how to get to $8395, which is only when Trevor jumps. The break point was on $0565 ($565==#8). Since I didn't know which break point actually branched to where I needed to be, I started it farther back (you'll notice this excerpt doesn't start with a $0565 break point) then just stepped through the code until I got to $839D (which leads to a generic code checking for collisions with solid tiles). My previous attempts at tracing it got me nowhere because I kept overlooking all that relative addressing from $E814 onward.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on September 26, 2012, 03:49:46 AM
I may have mentioned this earlier in this thread, but I feel I cannot stress enough the importance of pattern recognition when reading ASM, either as convenient acronyms or straight byte values. The important thing to remember is all address references in an NES game's ROM are indirect, even when by definition they're direct references. To clarify, in ASM a direct reference would be something like:

0E:8122 JSR $B332

What this looks like it's saying is "jump to the subroutine at address $B332 in bank 0x0E" and that is indeed what it is saying, but only insofar as the RAM is concerned. This does you little good when you're trying to work with the ROM unless the current RAM bank is 0x02 (the RAM addresses in bank 0x02 correspond directly to the same ROM addresses).

First you need to figure out which ROM address corresponds to 0x0E:B332. The easiest way is to locate it in the Debugger, hold the mouse to the left of the 0E, and look at the ROM offset that pops up at the bottom of the Debugger. Or you take the bank and multiply it by $4000, subtract $8000, then add the RAM address (in other words, [bank-2] * $4000 + address). However, NES games have a header in the ROM 16 bytes long, so you will need to add $10 to find the real ROM address.

Now, going back to what I said about pattern recognition. Suppose you're skimming through the byte values of the ROM and you see something like this:
0x029290 02 02 02 02 02 02 01 01 01 02 06 01 01 02 02 02
0x0292A0 02 02 06 10 0A 02 02 20 03 02 01 02 02 02 02 02
0x0292B0 06 06 7F 02 02 02 02 02 02 02 02 02 02 02 CC 92
0x0292C0 D4 92 E0 92 EA 92 F4 92 FA 92 02 93 08 93 16 93
0x0292D0 F4 9A F8 9A 06 9B 0C 9B 12 9B 1A 9B 20 93 22 93
0x0292E0 2A 93 2E 93 30 93 36 93 3C 93 42 93 48 93 4E 93
0x0292F0 54 93 58 93 5A 93 5E 93 64 93 68 93 6E 93 72 93

Odds are your eyes were drawn immediately to the string of repeating $02 values. Without analyzing the surrounding values and without looking up the referring ASM that maps to those bytes, we can at least surmise that the first half of this code snippet is a definition table. In other words, Each of those $02 or $01 or $06 most likely defines a specific variable.

That's one form of pattern recognition that's useful in reading ASM, but there is an even more important form. Look at the second half of that code snippet. Do you see the pattern there? Every other byte is $92 or $93 or $9A or $9B, while the bytes in between are different but with each one greater than the previous. Being able to spot these kinds of patterns will making ROM hacking much easier. These are what I refer to as map tables or index tables. Each pair of bytes is actually a direct RAM reference, used to redirect the CPU to a different address in the RAM.

But this code wasn't read from the RAM, it was read from the ROM. To see where each index maps to, you'll need to calculate the ROM offset. You can take a shortcut in this situation, though. The addresses referenced in a map table will almost always be within $1000 bytes from the current ROM address. If you pay close attention, you'll notice in this particular situation, the ROM address contains $92 just like some of the values in the map table. That's a good sign that this map table doesn't reference very far ahead.

Reading a map table is actually quite simple. Find the lowest byte that would be part of the map table. There are a lot of alternating $92, so locate the first occurrence of $92. Look farther back for a $91. There are none in this case, so the first occurrence of $92 is the very beginning of this map table. Look up the value of the byte immediately before it. In this case, it's $CC. This means the map points to RAM address $92CC of the same bank (I don't think you can cross-reference banks, except for bank 0F, which is always loaded).

To find out which bank that actually is, divide the current ROM address by $4000. The result is $A, so the current bank is 0x0A. This is one of those special banks that lets you basically read the RAM addresses as though they were ROM addresses (offset by $20000 of course). Therefore the map indexes us to $292CC. But wait! You need to account for the ROM header, so add $10. Therefore, the first index in the map table points us to $292DC. As it turns out, that address is within this same snippet of code (which one would logically expect). Looking at that value and the values around it, clearly $292DC is the beginning of another map table indexing to $29330.

Every game handles map tables differently. Figuring out how the programmers cross-indexed their map tables will greatly help you locate valuable data in a ROM.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Lelygax on September 26, 2012, 11:55:50 AM
I never post here because I cant understand these things, but you are doing a good job, it will be helpful for someone that knows how to hack games :)
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on September 26, 2012, 02:16:07 PM
I never hacked a game in my life until about three or four months ago.I learned how to read ROM data and hack NES games because reVamp had too many limitations and I couldn't figure out why movements in CV3 were so random. So I self-taught myself how to read ASM and have been learning how Konami's R&D team made CV3. Now I can look at something in the game and make a pretty good guess as to how they did something.

Finding the code is still the hard part, hence that last post.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Gunlord on October 09, 2012, 01:27:02 AM
You gotta be smart to understand all dis stuff...I dun get it T_T
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Inccubus on October 10, 2012, 07:48:16 AM
I'm convinced that the computer engineers that came up with ASM all had a neurochemical imbalance that was itself engineered by the Devil. Therefore, ASseMbly language is the spawn of Lucifer.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on October 11, 2012, 01:25:03 AM
ASM are the first three letters in the demon Asmodeus' name.  :o

I'm not smart. ... Ok, I'm smarter than a lot of people, but I'm not smart-smart. Behold the power of ejoocayshuN!
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Lelygax on October 11, 2012, 12:12:04 PM
"ejoocayshuN" means "education" right? lol
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on October 12, 2012, 02:40:23 AM
Once you become a little more comfortable reading ASM, you can start learning how some of your favorite games did what they did. Everyone knows I'm working on CV3, but tonight I just now took a glance at Bionic Commando. I see requests for "rope physics" in the Game Maker forums, but the simplest rope physics were right there on the NES. ... It's actually not that simple. It's kinda hard-coded, but in a sensible way. At a very quick glance, it would seem to me that it stores how many chains of the arm gets extended then checks the x of the hook part against the player and then moves the player that way. But like I said, it was at a very, very brief glance. I didn't like Bionic Commando's coding. Compared to CV3's, it seemed cruder. While CV3 has some coding issues that I've mentioned before, Bionic Commando's issues just at a glance are it uses too many indirect references. EEEEVVVVERYTHING is a variable referenced via indexing. And one value can be assigned to three or four different variables. What I do like though is it's very, very easy to find the tile map. You scroll through the hex editor and then BAM! It's right there, plain as day when you stumble upon it. ... You'd have to actually see it to understand how glaringly obvious it is. I was just like, "scroll down to find x, scroll down to find x, scroll do-- Oh. Gee, that must be the tile map!" But as for actually finding the player's x coordinate, nope, that wasn't easy. In CV3, finding variables is a cinch.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Inccubus on October 13, 2012, 01:14:36 AM
The rope physics in BC would be good to know and I'm sure a whole lot of people would appreciate having that info besides myself.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on October 14, 2012, 01:20:01 PM
Well you'll all just have to wait on that or do it yourselves. I have the same problem hacking BC that I do trying to help people on the GMC forums -- I can't understand very well coding methods that I'm not familiar with. Studying CV3's code then jumping into BC's code is like going from reading Steinbeck to reading Shakespeare. ... Not implying that BC's coding is Shakespearean, just saying the styles are so different that it's hard to easily read one and then the other. Seriously though, BC may as well be like a play -- there's literally page after page after page of straight JSR commands. When I first saw it I was like, "WTF?!" Although to be fair, I've done my share of complaining about CV3's address indexing.

Also, if you want to make a game as close to the original as possible, use some sensibility. The spiders in CV3 are SLOW. Each piece of thread is an object. The spider creates the first thread and stores the index in a byte, then the thread stores the spider's index in a byte and creates a thread of its own and stores that index. It's one convoluted stream of parent-child object relations. I opted out in favor of just drawing sprites, since the threads have their "illusion" flag set.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Inccubus on October 17, 2012, 04:49:18 AM
OK. That is convoluted, but then again, it is very limited hardware we're talking about. When recreating that sort of thing in a modern video game SDK it's better to ignore the technicality of how the original did it and just try to get it as close as possible. I've been recreating the SMB physics for about a week now and I keep running into problems with either the background layers or the player sprite being jittery depending on how I do certain things. I'm sure this wouldn't be an issue at all if I wasn't trying to use the exact hexadecimal values from the original and just settled for rounder numbers.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on October 22, 2012, 01:44:16 AM
Doubt that would help with the jittering.

You're remembering to truncate to single bytes, right? Nothing can be greater than 255 in an NES game, remember. SNES may give you a little more leeway, but you'd still need to watch your truncations.

Did you do what I've been doing and handle the carry bit manually? I mean, if you really want to use fractions, you'd take the fractional speed denominator value, divide it by 256, then add the integral speed. So if you have horz_2=$40 and horz_3=$01, hspeed would be $01+$40/$100 or $140/$100 or 5/4. In GM8, at least on my computers, that suffices to prevent jitteriness (I never use decimals because that's always caused jitters for me). Gravity, friction, and other acceleration is almost always fractional and follows the same rule. If you apply a gravity of $10 each step (as CV3 does with Living Armors and zombies), you'd set gravity to $10/$100 or 1/16.

Actually I'm veering away from the carry bit. In most situations it's not necessary. The only time you really need it is when dealing with bitwise rotations. For adding and subtracting, you can just compare the previous result to what was added. So if x+$10 < $10 would mean the carry bit was set, so you'd use y+=x+$10<$10 to increase y only when x+$10 is greater than $FF. For subtraction, you'd use y+=x-$10&$FF<$FF-$10. (Edit: Had to reverse the sign. Carry bit gets set when the subtraction doesn't cause an overflow. That's why CMP->BCC is used for less-than conditionals.)

Also, I'm feeling really happy with myself because while working on the Axe Knights (god, I hate their actual code) I thought up some codes for boomerangs. I had an idea for a boomerang that simply rises up if thrown from a crouch or down when thrown from a standing position so it still returns to the enemy that threw it (like the Quick Boomerang in MM2). I also had an idea for a boomerange that would be thrown downward and then arches back up subtly. I am quite pleased with that idea. :D

I'm kinda interested in the programming methods used by NR&D1 and NEAD, especially NR&D1 though. Think it'd be interesting to see how pioneers of console gaming used to program. Konami's "phase" system in CV3 is kinda hard to read even with a GM translation, but I like it. Logically, it's fairly sound -- increase a variable and only process the code defined by that variable. Even though all the code still gets dumped into the RAM, the CPU skips most of it. I think it also makes AI easier to interpret once you get past the messiness of it all.

To illustrate this, here's my translated Axe Knight code:
(click to show/hide)

As much as I hate it, it's one of the simpler codes. Compare that to something as deceptively simple as a fuzzy:
(click to show/hide)

And then there's my favorite so far, the owls.
(click to show/hide)

OMG BRAINSTORM! DISCUS SKELETON! He throws it down toward the ground and it bounces up then arches back toward the ground (or off the screen, whichever). Like skipping a Frisbee. I could so totally program that in CV3's style!
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Inccubus on October 22, 2012, 05:12:36 AM
You're remembering to truncate to single bytes, right? Nothing can be greater than 255 in an NES game, remember. SNES may give you a little more leeway, but you'd still need to watch your truncations.

I'm handling each of 4 bytes individually.


Did you do what I've been doing and handle the carry bit manually? I mean, if you really want to use fractions, you'd take the fractional speed denominator value, divide it by 256, then add the integral speed. So if you have horz_2=$40 and horz_3=$01, hspeed would be $01+$40/$100 or $140/$100 or 5/4. In GM8, at least on my computers, that suffices to prevent jitteriness (I never use decimals because that's always caused jitters for me). Gravity, friction, and other acceleration is almost always fractional and follows the same rule. If you apply a gravity of $10 each step (as CV3 does with Living Armors and zombies), you'd set gravity to $10/$100 or 1/16.

Actually I'm veering away from the carry bit. In most situations it's not necessary. The only time you really need it is when dealing with bitwise rotations. For adding and subtracting, you can just compare the previous result to what was added. So if x+$10 < $10 would mean the carry bit was set, so you'd use y+=x+$10<$10 to increase y only when x+$10 is greater than $FF. For subtraction, you'd use y+=x-$10&$FF<$FF-$10. (Edit: Had to reverse the sign. Carry bit gets set when the subtraction doesn't cause an overflow. That's why CMP->BCC is used for less-than conditionals.)

Completely ignoring the carry bit since I'm using only the hex values and not the original code. Someone else interpreted the code and determined the exact values and explained how those values interact under different circumstances.


Also, I'm feeling really happy with myself because while working on the Axe Knights (god, I hate their actual code) I thought up some codes for boomerangs. I had an idea for a boomerang that simply rises up if thrown from a crouch or down when thrown from a standing position so it still returns to the enemy that threw it (like the Quick Boomerang in MM2). I also had an idea for a boomerange that would be thrown downward and then arches back up subtly. I am quite pleased with that idea. :D

Cool beans!


I'm kinda interested in the programming methods used by NR&D1 and NEAD, especially NR&D1 though. Think it'd be interesting to see how pioneers of console gaming used to program. Konami's "phase" system in CV3 is kinda hard to read even with a GM translation, but I like it. Logically, it's fairly sound -- increase a variable and only process the code defined by that variable. Even though all the code still gets dumped into the RAM, the CPU skips most of it. I think it also makes AI easier to interpret once you get past the messiness of it all.

I would too. That sort of info could be invaluable.


OMG BRAINSTORM! DISCUS SKELETON! He throws it down toward the ground and it bounces up then arches back toward the ground (or off the screen, whichever). Like skipping a Frisbee. I could so totally program that in CV3's style!

Kinda like the Jack o' Bones enemy from SotN? That would be cool.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on October 22, 2012, 10:25:28 AM
Your "exact values" could be off. I'd double check that or get a second opinion.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Inccubus on October 22, 2012, 11:47:48 PM
Your "exact values" could be off. I'd double check that or get a second opinion.

Well, so far they seem to be pretty damn close, I really just think my execution is just a bit off.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on October 25, 2012, 09:25:04 PM
Actually, without even looking at BC's code, I think I can surmise how they did it or at least the general idea of how to do similar whip mechanics.

The lowest point of the swing will be at (hook.x,hook.y+arm_length). The speed of the swing is derived from how far from hook.x Ladd is at the time he starts swinging. To make sure he can swing, Ladd jumps up a height relative to the length of the arm. Gravity and horizontal acceleration are applied toward hook.x. When Ladd.x==hook.x, vertical speed is set upward retaining the gravity and horizontal speed is set away from hook.x and friction is reversed. At the highest point of the swing relative to the whip length, horizontal and vertical speeds are reset to 0 while gravity and "friction" dictate movement back toward the lowest point of the swing. To increase the swing, pressing right would add to the "friction" and left would subtract from the "friction", so pressing right while you're swinging to the left would reduce the speed, whereas pressing right while swinging to the right would increase the speed.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Omegasigma on October 26, 2012, 12:36:06 AM
im glad ive stuck to 3D programming >.> 2D looks scary on a console
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on October 26, 2012, 01:25:24 AM
I had a derp moment just now.

Here's an easier way to convert NES hex-fractions to normal fractions: If the rate of acceleration only occupies the upper nybble (e.g., $10, $40, $80, $C0), just take that upper nybble and divide it by 16. There's your fraction. So the following:

CLC
LDA horz_1
ADC horz_2 @ horz_2=$40
STA horz_1
LDA x
ADC horz_3 @ horz_3=$01
STA x

That's basically saying hspeed=1+4/16
$01 + ($40>>4) / 16

I feel sheepish. Baah.

For when the lower nybble is set as well...

$14 = ((1*16)+4) / 256

But it's just as easy to use hex and tell base 10 to piss off.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on November 18, 2012, 01:57:21 AM
Couldn't remember which topic I was posting this crap in, so I'll post here to bump my thread.  :-X

So I was transcribing the code for the water splashes fishmen and frogs made in CV3. I'm guessing the same code is probably used for the rubble from a broken wall. Well, as it turns out, Konami's programmers made another typo there as well.

To recapitulate, CV3 has four main variables pertaining to movement: the $04F2,X range pertaining to the integral horizontal speed, the $0509,X range pertaining to the fractional horizontal speed, the $0520,X range pertaining to the integral vertical speed, and the $0537,X range pertaining to the fractional vertical speed. Objects will frequently clear these out and then set them when needed. Sometimes all four are set even if they're not actually set to anything just because NES programmers frequently used simple data structures and it's often worth the extra cycles to save ROM space.

The code for the water splashes (and like I said, maybe the wall rubble as I haven't analyzed that yet) doesn't set $0509,X! Instead, where you would expect to see $0509,X you instead see $0537,X! How do I know that's a typo? Three lines down, $0537,X is set again to a different value! What's more, when $0537,X gets set and the index for the data structure is 7, the value gets set to #$81! That's a nonsense value never used for movement values!


I'm probably the only person on these forums that actually cares and perhaps some people are even getting annoyed with my finds, but I don't care. This is my thread and it pertains to Castlevania modding, so it's fucking relevant.  ;D
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Inccubus on November 19, 2012, 05:09:31 PM
Couldn't remember which topic I was posting this crap in, so I'll post here to bump my thread.  :-X

So I was transcribing the code for the water splashes fishmen and frogs made in CV3. I'm guessing the same code is probably used for the rubble from a broken wall. Well, as it turns out, Konami's programmers made another typo there as well.

To recapitulate, CV3 has four main variables pertaining to movement: the $04F2,X range pertaining to the integral horizontal speed, the $0509,X range pertaining to the fractional horizontal speed, the $0520,X range pertaining to the integral vertical speed, and the $0537,X range pertaining to the fractional vertical speed. Objects will frequently clear these out and then set them when needed. Sometimes all four are set even if they're not actually set to anything just because NES programmers frequently used simple data structures and it's often worth the extra cycles to save ROM space.

The code for the water splashes (and like I said, maybe the wall rubble as I haven't analyzed that yet) doesn't set $0509,X! Instead, where you would expect to see $0509,X you instead see $0537,X! How do I know that's a typo? Three lines down, $0537,X is set again to a different value! What's more, when $0537,X gets set and the index for the data structure is 7, the value gets set to #$81! That's a nonsense value never used for movement values!


I'm probably the only person on these forums that actually cares and perhaps some people are even getting annoyed with my finds, but I don't care. This is my thread and it pertains to Castlevania modding, so it's fucking relevant.  ;D

I love these things. Every time you find a bug like that I add it to my list of things to add to my title screen hack.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on November 20, 2012, 02:22:20 PM
Speaking of hacking, I just realized the big-grin smiley here is the same one used by Ed in Cowboy Bebop.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: Inccubus on November 21, 2012, 12:55:13 AM
Ah, Ed. I imagine that my my own daughter will be just like Ed some day when I get around to that.
Title: Re: Theou Aegis' Rookie Guide To 6502 ASM Reading
Post by: TheouAegis on May 27, 2013, 04:31:15 AM
Ah, the threads of our pasts return to haunt us!

This post actually has less to do with 6502 ASM and more to do with just understanding one of the binary functions that has been boggling me ever since I started studying NES coding. I never took the time to actually try to understand the purpose of bitwise rotations (ROL and ROR). A bitwise shift is easy enough to understand: shift the bits left and you multiply the value by a power of 2; shift the bits right and you divide the value by a power of 2; shift four times and you move nybbles around. But what is the point of a bitwise rotation and why is such a function not included in Game Maker?

Over time I've come to realize the difference between 8-bit and 16-bit programming. There isn't much of one: some actions take twice as long in 8-bit than in 16-bit because numbers larger than 255 take up more bits than can be handled in a single call. While working with CV3's code, I noticed certain values seemed to essentially be 16-bit values. For example, RAM offsets $0053 and $0054 are the coordinates of the left edge of the view in the room with the upper byte written last; in other words, although it appears in the RAM as $53$54, it is meant to be read as $54$53. A combined value of $82$01 can thus be interpreted as $0182. Yes, I just wrote two 8-bit values as one 16-bit value. By the way, that number would translate to the left edge of the view is at x:386, which would mean Trevor was at x:514 in the room.

A problem arises when working with 16-bit values in an 8-bit environment, namely how to manipulate both bytes at the same time. Consider a bitwise shift to the left. You can't simply shift both bytes equally:

Intended 16-bit LSR:
$0182 << 4 = $1820
False 8-bit LSR:
[$01$82] << 4 = $10$20

How do we get $18$20 from $01$82? The answer is with a rotation. Bitwise shifts on 8-bit and 16-bit hardware set the carry bit of the status byte depending on if the bit that gets dropped during a shift was set or not. Here's a refresher on how bitwise shifts work:

In a logical shift to the right (LSR), the lowest bit is dropped. The highest bit will be void and gets filled in with a 0. Thus 1001>>1 creates X100 (1); X is replaced with 0 and (1) is transferred to the carry bit. In an arithmetic shift to the left (ASL), the highest bit is dropped and the lowest bit will be void. Thus 1001<<1 creates (1) 001X; X is replaced with 0 and (1) is transferred to the carry bit.

Now that's out of the way, a rotation is simply a shift, but instead of filling the void with 0, it fills the void with the carry bit.  So let's look at that value from before, but this time as bits.

0000 0001   1000 0010
$01         $82


The byte you shift first depends on where you are shifting to. This could be confusing on the NES because of the reverse order, so it helps to write it out in 16-bit order as I did here. If you are shifting to the left, you start with the lowest byte ($82); if you are shifting to the right, you start with the highest byte ($01). In this example we want to shift to the left 4 times, so we start with the lowest byte.

0000 0001 (1) 0000 0100


We now perform a rotation in the same direction on the other byte.

0000 001(1)   0000 0100


Do this three more times per our previous example.


0000 0011 (0) 0000 1000
0000 011(0)   0000 1000
0000 0110 (0) 0001 0000
0000 110(0)   0001 0000
0000 1100 (0) 0010 0000
0001 100(0)   0010 0000 ($18$20)


As you can see, that required twice as many steps as a simple 16-bit shift to the left. That's how 16-bit systems are faster than 8-bit systems, even though they can seem to run just as slowly in a poorly programmed game. Game Maker already handles enough bytes, so it has no need for a rotation function.

What this means for me is I can possibly now go back and rewrite my password generator so it's twice as fast and possibly rewrite some code in my current engine to be faster. I've already converted hspd and vspd to 16-bit values, now I just have to work on other 16-bit values (things like score and time I've been handling in 32-bit from the get-go).