News: 11 March 2016 - Forum Rules
Current Moderators - DarkSol, KingMike, MathOnNapkins, Azkadellia, Danke

Author Topic: [FINISHED] Read a SNES Button input for an ASM code? (EarthBound)  (Read 15721 times)

ShadowOne333

  • Hero Member
  • *****
  • Posts: 1509
    • View Profile
Good day, RomHacking community.

Right now I am doing my baby steps into the ASM world.
Since the code I want to create is not that much of a work that I know, i don't need to get too much into it.

Basically, I am trying to implement some sort of IF statement into an EarthBound ROM to make use of the Y button in-game.
It should be something like:

If Button Y is pressed
then change character status to Skip Sandwich (which is the state for running in-game).

The status is being loaded from RAM as follows:
*009840-009840 (000001) = Status (00 = Normal, 01 = Burnt, 03 = Skip Sandwich)
(http://datacrystal.romhacking.net/wiki/EarthBound:RAM_map)

Thanks to some users over at SSRG.org, they provided me with these two links:
http://wiki.superfamicom.org/snes/show/ASM+Tutorial+Part+1
http://wiki.superfamicom.org/snes/show/Controllers#joypads

Out of what I could gather from there, I put these code together:
Code: [Select]
LDA             $4016              ; Load joypad's value to accumulator.
 CMP             #$4000             ; Is Button Y being pressed?
 BEQ             RunButton          ; Branch to RunButton label.
 RTS                                ; Return if Button Y is not being pressed. End code.
 
 RunButton:                         ; If Button Y is being pressed, load the following code.
 LDA             #$03               ; Set to Skip Sandwich status
 STA             $009840            ; Set as status.
 RTS                                ; Return after code has been executed.

Now the part that I don't fully understand is that of the button reading.
I want the Button Y from the SNES controller to be read, so I can start the IF statement and branch to the RunButton subroutine, and that part basically changes the current status of the character to the Skip Sandwich status so I can Run while I press said button.

Anyone can help me out on that regard?
And if the code is wrong or could be better implemented, please let me know, I'm open to suggestions!

PS: I also want to add some sort of AND statement in which IF button Y is pressed AND Status is not 01 (burnt) then branch.
Any help would be greatly appreciated!
« Last Edit: April 22, 2015, 01:11:26 pm by ShadowOne333 »

Disch

  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: Read a SNES Button input for an ASM code? (EarthBound)
« Reply #1 on: April 13, 2015, 04:45:53 pm »
Your code is not correct.  Reading from $4016 isn't so straightforward because you have to read each button press serially (one bit at a time).

But nevermind that, because you should not read from $4016 directly anyway.  Most if not all games will poll $4016 during a common routine in the frame, then will store the overall key state in RAM somewhere.  All you'd need to do for your hack is read from that area in RAM, rather than reading from $4016.

Set a breakpoint on $4016 reads in the original game, and examine the code that does it.  It probably shifts the low bit into some place in memory... like:

Code: [Select]
LDA $4016
LSR A
ASL some_mem

'some_mem' is the variable you're looking for.  You can just use that instead of using $4016.


KingMike

  • Forum Moderator
  • Hero Member
  • *****
  • Posts: 7017
  • *sigh* A changed avatar. Big deal.
    • View Profile
Re: Read a SNES Button input for an ASM code? (EarthBound)
« Reply #2 on: April 13, 2015, 05:30:19 pm »
There's an SNES game that USES $4016/4017?

Most every game I've seen has used the auto-poll registers ($4218-421F, though usually only $4218-421B are used, 2 bytes each for players 1 and 2).
Still that last thing Disch said is the important thing. Look for where that data gets shifted into RAM, and look at that RAM address.
(even games that use $4218 will usually copy to RAM. I hear there is in reality a slight delay when reading the register for the input to be processed, but emulators won't care.
Also, copying to RAM allows games to filter the data, to detect buttons PUSHED from buttons HELD. For the former, even if you mean to push a button, it will probably take you more than one frame (thus more than one input reading cycle) to lift your finger off the button, so a good game should be able to detect that.)
"My watch says 30 chickens" Google, 2018

ShadowOne333

  • Hero Member
  • *****
  • Posts: 1509
    • View Profile
Re: Read a SNES Button input for an ASM code? (EarthBound)
« Reply #3 on: April 13, 2015, 05:34:22 pm »
$4016 is RAM address right?
I suppose you mean that I should open up Earthbound in Snes9x Debugger, set the breakpoint to that RAM Address and then see where the shift occurs in memory after the button press... right?

KingMike:
I did read that $4218-4219 are used for Player 1 while 421A and 421B are used for Player 2, but what byte corresponds to what button?
#$4219 should have $40 in it in order to mimic a Button Y press, doesn't it?
« Last Edit: April 13, 2015, 05:41:22 pm by ShadowOne333 »

Disch

  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: Read a SNES Button input for an ASM code? (EarthBound)
« Reply #4 on: April 13, 2015, 06:10:03 pm »
@KingMike:

Whoops!  Yeah you're right, I forgot all about the auto-polling regs on the SNES.  I was thinking of NES.

STARWIN

  • Sr. Member
  • ****
  • Posts: 452
    • View Profile
Re: Read a SNES Button input for an ASM code? (EarthBound)
« Reply #5 on: April 13, 2015, 08:29:31 pm »
#$4219 should have $40 in it in order to mimic a Button Y press, doesn't it?

Something like that, yes. But the game already reads those to various places. If you have realtime RAM view open, you usually see some of the RAM spots where it stores keystate. At simplest you just want to insert your code somewhere in the game loop and have it read from a place that looks like the correct place.  :D

Hopefully the game reacts nicely into that player state being manipulated. Also, do you want to not run when you stop pressing somehow? If yes, your code logic is not sufficient for that yet, it just makes you run until natural game logic unsets running. One small problem is in finding a suitable spot to store the code, but maybe not such a big problem for SNES.

chillyfeez

  • Hero Member
  • *****
  • Posts: 824
    • View Profile
Re: Read a SNES Button input for an ASM code? (EarthBound)
« Reply #6 on: April 13, 2015, 08:58:47 pm »
Here's my suggestion, ShadowOne333, since setting a breakpoint to read the input registers can sometimes be messy business:
Open the game in Geiger's S9X Debugger.
Click "show hex" and view RAM, beginning at address 0000. Increase the size of the window to 0x10 lines (so you're viewing 0000 through 00FF). Take a second to observe what's going on, specifically the bytes that are not changing. Then, press Y and see if anything changes. If not, then view the next 0x10 lines (0100 through 01FF).
After a few tries, you'll find where EarthBound logs input pretty quickly. It'll be somewhere within the first 0x200 lines of RAM, so at most you'll be doing this 20 times.

Make sure you're doing this when the ga!e is at a point at which you want this Y button business to actually be happening, because where in RAM the game is logging input can change situationally - if you want it to happen when you're wandering around towns and dungeons, don't perform these tests in battle, for instance.
Ongoing project: "Final Fantasy IV: A Threat From Within"

Latest Demo

ShadowOne333

  • Hero Member
  • *****
  • Posts: 1509
    • View Profile
Re: Read a SNES Button input for an ASM code? (EarthBound)
« Reply #7 on: April 13, 2015, 09:19:29 pm »
Here's my suggestion, ShadowOne333, since setting a breakpoint to read the input registers can sometimes be messy business:
Open the game in Geiger's S9X Debugger.
Click "show hex" and view RAM, beginning at address 0000. Increase the size of the window to 0x10 lines (so you're viewing 0000 through 00FF). Take a second to observe what's going on, specifically the bytes that are not changing. Then, press Y and see if anything changes. If not, then view the next 0x10 lines (0100 through 01FF).
After a few tries, you'll find where EarthBound logs input pretty quickly. It'll be somewhere within the first 0x200 lines of RAM, so at most you'll be doing this 20 times.

Make sure you're doing this when the ga!e is at a point at which you want this Y button business to actually be happening, because where in RAM the game is logging input can change situationally - if you want it to happen when you're wandering around towns and dungeons, don't perform these tests in battle, for instance.

Thanks!
I think I found it!

When I set the range from 0000 to 0200, I get the following changes upon Button Y input.
I can tell I found the RAM address because the neighbor addresses change too with other button inputs, like Up, Down, X, etc.

Whenever I press the "y" button these addresses change:

0x0066 => Changes from 00 to 40
0x0076 => Changes from 00 to 40
0x0078 => Changes from 00 to 40

I also found another address that changes somewhat oddly:
0x006A => If I let the button Y held, it starts as 00 then switches between 00 and 40 like every second or two.

Oh and yes, I know exactly when I want the Y Button Run action to occur (outside of battle) so anywhere like in towns, houses, dungeons, etc should be good enough.

So now that I found those 3 addresses (four if I count the other one that switches when being held), what do I do with the ASM code? O.O

LDA $0066 or something similar for the button input instead of the LDA $4016 I used previously?

PS: I also changed $9840 address value from 00 to 03 to test out the Skip Sandwich status, and Ness walks fast by default!
So that one should be a no brainer when doing the code. Although, I gotta figure out a way so that the Burnt status (01) doesn't collide with the Skip Sandwich Status (03) to avoid the odd Failed Teleportation bug.
« Last Edit: April 13, 2015, 09:52:47 pm by ShadowOne333 »

chillyfeez

  • Hero Member
  • *****
  • Posts: 824
    • View Profile
Re: Read a SNES Button input for an ASM code? (EarthBound)
« Reply #8 on: April 13, 2015, 09:54:56 pm »
You definitely don't want 6A. It's likely being used by other lines of code as a placeholder.
Since you have three likely candidates, you probably want to be sure which one is "the one."
Here's a couple things to try:

Press other buttons - two (likely adjacent) bytes of RAM will be used to log input from the 12 different buttons on the controller. Depending on which button you press, you'll see one of two bytes read 01, 02, 04, 08, 10, 20, 40 or 80. These are hex representations of one each of the eight bits in each byte, and each bit logs a different controller button.

Also, slow the emulation down (press "-" and Geiger's will display the % of normal processing speed) to about 4%. Then press and hold Y. Make sure the piece of RAM you want to read isn't changing as you hold the button (doing a very fast version of what 6A is doing, which will be more easily observed when the emulation is slowed down). You want to use a byte that stays constant as the button is held. Also, see if any one of these bytes changes before the others. Chances are, one of them is being read and copied to the other two. Best to use the original in this situation (but not in all situations).

Once you've figured out which byte of RAM to read, your ASM will look something like this:
(Let's assume for the sake of example that it ends up being 0066)
LDA $0066 ;read the controller button byte
AND #$40 ;ignore any other buttons that might be pressed and check only for if Y is being pressed
BEQ $XX ;if Y is not being pressed, skip the following XX bytes of code (therefore, if Y is being pressed, run the code that follows)

Does that all make sense?
Ongoing project: "Final Fantasy IV: A Threat From Within"

Latest Demo

ShadowOne333

  • Hero Member
  • *****
  • Posts: 1509
    • View Profile
Re: Read a SNES Button input for an ASM code? (EarthBound)
« Reply #9 on: April 13, 2015, 10:07:12 pm »
Well I did slow down the emulation to 2% and all of the three main addresses I mentioned stay put at a constant 40 value.
I also found 6E is doing the same as 6A, so that is a no go.

I let the button pressed for like 30 seconds or a minute and they stayed constant at 40. So that means any of those 3 can be used for the LDA code?

Yes that actually makes sense.
You are basically saying that If the value in $66 is the HEX value 40, then it should run to the code below, which should be the LDA $9840 and STA #$03, right?

A guy over at SSRG forums told me to use CMP for the HEX value in the button input, so that it should be comparing the value in that address, and when that value jumps to 40, then it should do the branch.

One other thing, how can I specify in ASM code that I want the 03 value in 9840 to be only set when I press the button, but when I let it go, it should go back to the previous state? I don't want the button press to let the value there forever.
Also, how can make it so that if the value in 9840 is already 01 (Burnt) then the branch should not occur? (Therefore not applying the Run button code if the Burnt status is in place to avoid a certain bug with sprites).

I think that (in my theory) if it can be established that the status should only be 03 when I press the button and it goes back to the previous state whenever I let it go, there should be no problems (I guess XD)

Many thanks for your help, and sorry if I overwhelm you with questions.

PS: This would be the code so far:
Code: [Select]
LDA              $0066            ; Read the controller button byte
 AND             #$40             ; Ignore any other buttons that might be pressed and check only for if Y is being pressed
 BEQ             RunButton:       ; If Y Button is being pressed, run the code from the RunButton Subroutine
 RTS                              ; Return if condition is not met

 RunButton:                       ; If Button Y is being pressed, load the following code.
 LDA             $009840          ; Load the data in RAM $9840
 CMP             #$01             ; Is the current status Burnt?
 BEQ             RunStat:         ; If the status is not Burnt, set status to #$03 (Skip Sandwich)
 RTS                              ; Return if status is Burnt

 RunStat:                         ; If condition is NOT burnt, load the following code.
 LDA             #$03             ; Set to Skip Sandwich status
 STA             $009840          ; Set as status.
 RTS                              ; Return after code has been executed.
« Last Edit: April 13, 2015, 10:37:49 pm by ShadowOne333 »

chillyfeez

  • Hero Member
  • *****
  • Posts: 824
    • View Profile
Re: Read a SNES Button input for an ASM code? (EarthBound)
« Reply #10 on: April 13, 2015, 11:21:38 pm »
Quote
So that means any of those 3 can be used for the LDA code?
Yep, pretty much.

Quote
You are basically saying that If the value in $66 is the HEX value 40, then it should run to the code below, which should be the LDA $9840 and STA #$03, right?
Well, you've got the opcodes reversed, but yeah.

Quote
A guy over at SSRG forums told me to use CMP for the HEX value in the button input, so that it should be comparing the value in that address, and when that value jumps to 40, then it should do the branch.
That'd likely work, too. SNES ASM has so few actual functions that there are often multiple ways to derive the same result.

Quote
One other thing, how can I specify in ASM code that I want the 03 value in 9840 to be only set when I press the button, but when I let it go, it should go back to the previous state? I don't want the button press to let the value there forever.
Also, how can make it so that if the value in 9840 is already 01 (Burnt) then the branch should not occur?
You're simply going to put in another if/then to handle that. Within the RunButton code, you'll put in a check for burnt status (which will look very similar for the Y button check)... Looking at your code, you've done that already... Sort of.

Let's look at your code now...
Code: [Select]
LDA              $0066            ; Read the controller button byte
 AND             #$40             ; Ignore any other buttons that might be pressed and check only for if Y is being pressed
 BEQ             RunButton:       ; If Y Button is being pressed, run the code from the RunButton Subroutine
 RTS                              ; Return if condition is not met
You want a BNE there instead of a BEQ.
If no CMP precedes BEQ, then the SNES treats it as though you're CMPing zero.
So, you're code says, "Load button register; ignore all buttons except Y; if Y is not being pressed, branch to RunButton."

Code: [Select]
RunButton:                       ; If Button Y is being pressed, load the following code.
 LDA             $009840          ; Load the data in RAM $9840
 CMP             #$01             ; Is the current status Burnt?
 BEQ             RunStat:         ; If the status is not Burnt, set status to #$03 (Skip Sandwich)
 RTS                              ; Return if status is Burnt
Question (because I don't know): does $9840 allow multiple statuses to be set? If so, then you'll want to include an AND #$01 before the CMP #01, or else the check will fail unless only burnt status is set.
Also, again, I think you want a BNE instead of a BEQ.

Code: [Select]
RunStat:                         ; If condition is NOT burnt, load the following code.
 LDA             #$03             ; Set to Skip Sandwich status
 STA             $009840          ; Set as status.
 RTS                              ; Return after code has been executed.
Again, if 9840 allows multiple statuses to be set, then you'll want to change this like so:
LDA $9840
ORA #$03
STA $9840
RTS

This way, you'll add running status to any already existing statuses.

 :woot!:
Ongoing project: "Final Fantasy IV: A Threat From Within"

Latest Demo

ShadowOne333

  • Hero Member
  • *****
  • Posts: 1509
    • View Profile
Re: Read a SNES Button input for an ASM code? (EarthBound)
« Reply #11 on: April 13, 2015, 11:41:20 pm »
Good, good!
I am understading quite well your explanations!

Regarding your question...
Yes. RAM Address $9840 does permit one of three possible 'statuses' to be set.
00 = Normal Status: This is the default status for Ness and other party members, which could be defined as "normal walking".
01 = Burnt Status: This status is only loaded when Ness fails a Teleportation, in which case this status is being taken as a reference to load a sprite group for 3 seconds or so.
03 = Skip Sandwich Status: Basically, running for the character. This status is activate once the character uses a Skip Sandwich, which side effect is to make the character faster than the Normal Status.

So yeah, there are three possible status, and the only one that is creating issues is the Burnt status with the Skip Sandwich status.
You see, with the current Run Patch that is available, you can press the Y Button to run and everything, but when you fail a teleportation, the Burnt Sprite loads for a split second and then switches to that of a Walking animation (but the characters stay in place, you can't move), and the correct effect should be the Burnt Sprite being displayed for 3 or so seconds until you can move again.

Thanks to your suggestions, the code would be as follows:

Code: [Select]
LDA              $0066           ; Read the controller button byte
 AND             #$40             ; Ignore any other buttons that might be pressed and check only for if Y is being pressed
 BNE             RunButton:       ; If Y Button is being pressed, run the code from the RunButton Subroutine
 RTS                              ; Return if condition is not met

 RunButton:                       ; If Button Y is being pressed, load the following code.
 LDA             $009840          ; Load the data in RAM $9840
 AND             #$01             ; Check if ONLY the Burnt Status is being loaded
 CMP             #$01             ; Is the current status Burnt?
 BNE             RunStat:         ; If the status is not Burnt, set status to #$03 (Skip Sandwich)
 RTS                              ; Return if status is Burnt

 RunStat:                         ; If condition is NOT burnt, load the following code.
 LDA             $9840            ; Load RAM Address in charge of the character status
 ORA             #$03             ; OR statement. If one of the two is true, it will set the status to Skip Sandwich
 STA             $009840          ; Set status as Skip Sandwich.
 RTS                              ; Return after code has been executed.

What other value is being taken into consideration for the ORA command?
Is that good so far?

Would the status return to its previous value if I let go of the Y Button with this code?

Seriously, many thanks.
It's nice to finally be learning some ASM, even though I am a complete noob at it. :P
« Last Edit: April 13, 2015, 11:46:40 pm by ShadowOne333 »

chillyfeez

  • Hero Member
  • *****
  • Posts: 824
    • View Profile
Re: Read a SNES Button input for an ASM code? (EarthBound)
« Reply #12 on: April 14, 2015, 10:11:41 am »
Oh, actually, what I meant was, if 9840 stores other statuses besides Burnt and Skip Sandwich (like, for example, Mushroom or Diamond Head status) then you'll need to make the code ignore them with the AND opcode. But if the only possible values are 00, 01 and 03, then you can ignore what I said about using the AND.

If letting go of Y is going to cancel running, you'll have to write separate code for that - effectively the opposite of the code that turns running on.

So, here:
Code: [Select]
BNE             RunButton:       ; If Y Button is being pressed, run the code from the RunButton Subroutine
 RTS                              ; Return if condition is not met
Instead of just an RTS, you'll need to put in the part about cancelling running.
STZ $9840 won't be enough, because then you'd be cancelling burnt status, too (probably not the end of the world, but also not the desired effect).
You'll need to use an AND to only turn off the relevant bits in the value.
Do you know how AND and ORA work, exactly?
Quick tutorial (apologies if you already know this):
Every hex byte is a two character representation of 8 bits...
00 = 00000000
01 = 00000001
02 = 00000010
03 = 00000011
04 = 00000100
etc, but that's enough for this explanation.
The ORA Operation essentially combines the set bits (1's, as opposed to 0's) of the accumulator (A) and the given value.
So if A is 04 (00000100) and you ORA #$03 (00000011), the resulting A will be 07 (00000111). The ORA combined the turned-on bits of the two values. This isn't the same as addition. 01 ORA #$01 will result in 01, because the operation is simply turning on the lowest bit if it's not already on.
ORA is generally used to set certain bits in a byte while leaving others alone.
Now, AND is sort of the opposite. When you AND two values, the result will be a value that has only the turned-on bits of the two original values.
So,
01 AND #$01 = 01
But,
02 AND #$03 = 02, because the two only have the second bit in common
Also,
04 AND #$03 = 00, because the two have no bits in common.

So... You want an operation that says, "load A; if the running bit is set, then turn it off, but don't touch the burnt bit."

That'll be
LDA $9840
AND #$01; turn all bits to zero except the lowest bit
STA $9840

This will take any value in 9840 (if what you're saying is true, then the only possibilities are 00, 01 and 03 under normal circumstances), and cancel out anything except the burnt bit.

... Wait. That won't work. Are you sure running is 03 and not 02? If running is 03, then that means the value for running (00000011) necessarily includes the value for being burnt (00000001). That means the code I just wrote above would make letting go of Y always result in being burnt (pressing Y turns the bottom two bits on, but letting go of Y only turns the second-lowest bit off).

Okay...

So, you know that burnt check?
Code: [Select]
LDA             $009840          ; Load the data in RAM $9840
 AND             #$01             ; Check if ONLY the Burnt Status is being loaded
 CMP             #$01             ; Is the current status Burnt?
 BNE             RunStat:         ; If the status is not Burnt, set status to #$03 (Skip Sandwich)
 RTS                              ; Return if status is Burnt

That's going to have to come before the Y button check, hence the game will ignore any Y button activity if and only if current status is burnt.

Does that make sense?
Sorry it took so long to get back to you on this, and sorry that this response was kind of a stream of consciousness.
I hope it helps.
Ongoing project: "Final Fantasy IV: A Threat From Within"

Latest Demo

ShadowOne333

  • Hero Member
  • *****
  • Posts: 1509
    • View Profile
Re: Read a SNES Button input for an ASM code? (EarthBound)
« Reply #13 on: April 14, 2015, 11:38:19 am »
Let's see if I got it right.

ORA example:
A 0101
ORA 1000
Result = 1101
Another one would be 1000 and 0001, ORA will give as result 1001.

AND example:
A 1000
AND 0001
Result would be 0000 since there is no bit in common, unlike 1100 and 0101 would end up in 0100 (Only 3rd bit in common)

Yes, I'm sure that the status for running (Skip Sandwich) is indeed 03.
I changed $9840 manually in Snes9X Debugger from 00 to 03, and Ness began to walk fast (run basically) without pressing anything. Funny thing is that I actually tried 01 too and that did nothing.
Although... I have to try changing the value when I am performing the Failed Teleportation, since that is the one that loads up the Burnt State to see what happens with 00, 01 and 03 values and the Burnt Sprites.

With the change you suggested:

Code: [Select]
LDA              $009840          ; Load the data in RAM $9840
 AND             #$01             ; Check ONLY if the Burnt Status is loaded in RAM (#$01)
 CMP             #$01             ; Is the current status Burnt?
 BNE             RunButton:       ; If the status is not Burnt, go to RunButton subroutine
 RTS                              ; Return if status is Burnt

 RunButton:                       ; If condition is NOT burnt, run the following code.
 LDA             $0066            ; Read the controller button bytes
 AND             #$40             ; Ignore any other buttons that might be pressed and check ONLY if Y is being pressed
 BNE             RunStat:         ; If Y Button is being pressed, go to set status to #$03 (Skip Sandwich)
 RTS                              ; Return if button is not pressed

 RunStat:                         ; If Button Y is being pressed, load the following code.
 LDA             $009840          ; Load RAM Address in charge of the character status
 ORA             #$03             ; OR statement. Set the status to Skip Sandwich (Bits 0000 0011)
 STA             $009840          ; Set status as Skip Sandwich.
 RTS                              ; Return after code has been executed.

It does make sense to me.
Seems like a more feasible way to do it, that way if we have the Burnt condition it will not even enter the code, avoiding any conflict at all.

I will do some testing with the RAM address $9840 and each one of the 3 HEX values with the Failed Teleportation to see how the game reacts to see if the code would apply like it is or if any modifications are needed to the code logic.

Thanks again for your help!
« Last Edit: April 14, 2015, 12:47:29 pm by ShadowOne333 »

KingMike

  • Forum Moderator
  • Hero Member
  • *****
  • Posts: 7017
  • *sigh* A changed avatar. Big deal.
    • View Profile
Re: Read a SNES Button input for an ASM code? (EarthBound)
« Reply #14 on: April 14, 2015, 12:23:34 pm »
As to using AND or CMP to check button status...
AND effectively allows it to check if a button is pressed, regardless of any other buttons pressed (as AND masks out the rest of the buttons)
A CMP will only work if the desired button is pressed AND NO OTHERS.

You decide which is better.
"My watch says 30 chickens" Google, 2018

mopoz

  • Jr. Member
  • **
  • Posts: 69
  • Russia, Vodka and Bears
    • View Profile
Re: Read a SNES Button input for an ASM code? (EarthBound)
« Reply #15 on: April 14, 2015, 12:42:12 pm »
Code: [Select]
LDA             $9840            ; Load RAM Address in charge of the character status
STA             $009840   

STA $009840  -Store in ROM  :o

LDA             $9840 - Load data in ROM with bank (need php
                                                                                       sep #$20
                                                                                       lda #$7e
                                                                                       pha
                                                                                       plb
                                                                                       plp
 or 009840 (0x1840 pc or $C09840 HiRom)
Processor at this address will be used ROM bus, but not the RAM

In ram $7E9840
Skate-punk rulezzzz

chillyfeez

  • Hero Member
  • *****
  • Posts: 824
    • View Profile
Re: Read a SNES Button input for an ASM code? (EarthBound)
« Reply #16 on: April 14, 2015, 12:56:14 pm »
Good point, mopoz. I wasn't paying attention to that.
Though in most cases (in my experience) the data bank will already be set to 7E, so STA $9840 is usually enough (but definitely do not say STA $009840).

The rest of your code looks good, as far as I can see, ShadowOne, except you left out the part where running is turned off when Y is released.
Ongoing project: "Final Fantasy IV: A Threat From Within"

Latest Demo

ShadowOne333

  • Hero Member
  • *****
  • Posts: 1509
    • View Profile
Re: Read a SNES Button input for an ASM code? (EarthBound)
« Reply #17 on: April 14, 2015, 01:17:41 pm »
Damn I forgot about that part.
So basically it's this part:

LDA $9840
AND #$01; turn all bits to zero except the lowest bit
STA $9840

Right?
Should this part go inside the RunButton Subroutine or the RunStat one?
If my guess is right, then that part should go after the BNE in RunButton, so if the button is not pressed, it should run that code instead like this:

Code: [Select]
RunButton:                        ; If condition is NOT burnt, run the following code.
 LDA             $0066            ; Read the controller button bytes
 AND             #$40             ; Ignore any other buttons that might be pressed and check ONLY if Y is being pressed
 BNE             RunStat:         ; If Y Button is being pressed, go to set status to #$03 (Skip Sandwich)
 LDA             $9840            ; Load accumulator from RAM $9840
 AND             #$01             ; Turn all bits to zero except the lowest bit
 STA             $9840            ; Store in RAM $9840
 RTS                              ; Return if button is not pressed

Please, let me know what you think of it.

With mopoz corrections, it should be like this (Still doesn't include Y button released):

Code: [Select]
LDA              $9840           ; Load the data in RAM $9840
 AND             #$01             ; Check ONLY if the Burnt Status is loaded in RAM (#$01)
 CMP             #$01             ; Is the current status Burnt?
 BNE             RunButton:       ; If the status is not Burnt, go to RunButton subroutine
 RTS                              ; Return if status is Burnt

 RunButton:                       ; If condition is NOT burnt, run the following code.
 LDA             $0066            ; Read the controller button bytes
 AND             #$40             ; Ignore any other buttons that might be pressed and check ONLY if Y is being pressed
 BNE             RunStat:         ; If Y Button is being pressed, go to set status to #$03 (Skip Sandwich)
 RTS                              ; Return if button is not pressed

 RunStat:                         ; If Button Y is being pressed, load the following code.
 LDA             $9840            ; Load RAM Address in charge of the character status
 ORA             #$03             ; OR statement. Set the status to Skip Sandwich (Bits 0000 0011)
 STA             $9840            ; Store status in RAM as Skip Sandwich
 RTS                              ; Return after code has been executed.

chillyfeez

  • Hero Member
  • *****
  • Posts: 824
    • View Profile
Re: Read a SNES Button input for an ASM code? (EarthBound)
« Reply #18 on: April 14, 2015, 02:22:41 pm »
Looks good.
Of course, sometimes things look fine, but once you write them in and run them you can get an unexpected result. As far as I can tell, this should work, but don't get discouraged if it's not perfect on the first try. That's what debugging is for.
 ;)
Ongoing project: "Final Fantasy IV: A Threat From Within"

Latest Demo

ShadowOne333

  • Hero Member
  • *****
  • Posts: 1509
    • View Profile
Re: Read a SNES Button input for an ASM code? (EarthBound)
« Reply #19 on: April 14, 2015, 05:50:04 pm »
I will do just that!
Although... I have the convert the code first into the format the tool uses to compile the ROM with this code.
If I try to implement the ASM code as it is, it will give me an error during compilation.

You can check out my source files here:
http://local-static0.forum-files.fobby.net/forum_attachments/0038/7970/MaternalBound_Omega_-_CoilSnake_Source.rar

In there, you can go to "ccscript -> asm65816.ccs" to verify the commands the tool uses so that the ROM can compile properly or go to "ccscript -> asm_code.ccs" to check out some examples for LDA and ADC.

In the "asm_code.ccs" example, you can see some ROM[0xXXXXXX] command followed by the ASM command.
The 0xXXXXXX address is basically the ROM address in SNES address, which is PC address + 0xBFFE00.

Basically, to convert PC address to SNES address one should add 0xC00000 and then subtract 0x200 (or just add 0xBFFE00 like I do. XD) and viceversa.
So let's say it has ROM[0xC52632], that's the SNES address, to convert it to PC address one should subtract 0xBFFE00 from the SNES address, making the PC address 0x052832.

I got a little way ahead of myself... Didn't I? XD

EDIT:
Ok so I just screwed arround with the status after getting the Teleport PSI and got this:
00: Game plays normally. This is the default status.
01: Burnt status. Whenever I activate this, the character sprites change to the Burnt sprites and I can walk around without the sprites changing at all, quite odd indeed.
03: Run state. The characters walk faster than usual.

One thing to notice though, if I have 03 or 01 enabled in RAM and I fail a teleportation, the status changes back to 00 after the 3 sec hiatus of the failed teleportation.
« Last Edit: April 14, 2015, 07:05:57 pm by ShadowOne333 »