[FINISHED] Read a SNES Button input for an ASM code? (EarthBound)

Started by ShadowOne333, April 13, 2015, 04:35:24 PM

Previous topic - Next topic

KingMike

Or delete the pointless header and forget that add/subtract 0x200 crap. :P
(but I suppose you're using an editor so that can't be done?)
"My watch says 30 chickens" Google, 2018

ShadowOne333

Quote from: KingMike on April 14, 2015, 07:18:53 PM
Or delete the pointless header and forget that add/subtract 0x200 crap. :P
(but I suppose you're using an editor so that can't be done?)
It's not really about using a headered ROM or not, rather that the tool itself takes a clean headered ROM as the base for the ROM addresses and coding. :P

chillyfeez

Honestly, I've tried ASM tools before... After a bit of experience with them, I find it easiest just to write the code directly into the ROM using Geiger's hex editor window. Of course, I keep this open in the background.

You can also try this program:
http://www.romhacking.net/utilities/395/

The only problem it has really is that it has trouble handling changes in the size of the accumulator and x,y registers. As long as your code doesn't use any SEP/REP #$10 or #$20, then the tool will work fine.

Nice thing about editing right in Geiger's is the instant debug capability, though.

Anyway, good luck!
Ongoing project: "Final Fantasy IV: A Threat From Within"

Latest Demo

ShadowOne333

Quote from: chillyfeez on April 14, 2015, 08:23:58 PM
Honestly, I've tried ASM tools before... After a bit of experience with them, I find it easiest just to write the code directly into the ROM using Geiger's hex editor window. Of course, I keep this open in the background.

You can also try this program:
http://www.romhacking.net/utilities/395/

The only problem it has really is that it has trouble handling changes in the size of the accumulator and x,y registers. As long as your code doesn't use any SEP/REP #$10 or #$20, then the tool will work fine.

Nice thing about editing right in Geiger's is the instant debug capability, though.

Anyway, good luck!
Wow, that spunds great!
Would there be any issues if I try to ASM hack over my already patched game? And if so, can I store the new ASM code in expanded memory from te 4MB expanded ROM?

chillyfeez

Quote from: ShadowOne333 on April 14, 2015, 11:28:17 PM
Wow, that spunds great!
Would there be any issues if I try to ASM hack over my already patched game? And if so, can I store the new ASM code in expanded memory from te 4MB expanded ROM?

I can't think of any reason why it wouldn't work. Remember, though, that if you're jumping to a subroutine that's that far away, yoru'll need to use JSL/RTL instead of JSR/RTS.
Ongoing project: "Final Fantasy IV: A Threat From Within"

Latest Demo

ShadowOne333

Quote from: chillyfeez on April 15, 2015, 12:35:23 AM
I can't think of any reason why it wouldn't work. Remember, though, that if you're jumping to a subroutine that's that far away, yoru'll need to use JSL/RTL instead of JSR/RTS.
Great!

So I just gave the ASM Developer Kit a try, and I can see the ASM code for the game.
Just to be sure, I double checked the available space in expanded area both in a HEX editor and in ASMDevKit.

In the HEX Editor, I have 0x2FF5F1 up to 0x3FFFFF free in the Patched ROM. So that leaves QUITE a big space to work with.
Taking a look at the ASM Code in ASMDevKit, I can see it starts from 0xC00000, so that means the DevKit is using SNES adddressing. Although, I would NOT have to subtract 0x200 from the SNES address since the already patched ROM doesn't have the 200 bytes from the header. So that means I only have to add C00000 to the ROM Address to work with the ASMDevKit

Converting the free addresses from PC to SNES, I got the following:
0x2FF5F1 (PC) => 0xEFF5F1 (SNES)
0x3FFFFF (PC) => 0xFFFFFF (SNES)

So far so good, I now have quite some space to work with, so that means I should not need JSL/RTL, right?
I can work simply with JSR/RTS.

Now the interesting part... The code.

The ASM Code is basically 3 columns in the DevKit.
First column is the SNES Address, second column is the HEX Code (I suppose) and the third column is the ASM command with its affected addresses/HEX values.

I can see in the second column that the first pair of nibbles is specific to the ASM Command.
Basically, I am taking this file as a reference:
https://www.dropbox.com/s/x0gku2wkeqw6x34/asm65816.ccs?dl=0

That file is the one CoilSnake/CCScript uses to compile the ROM with ASM code.
Going to the "command LDA" I can see something like "[A9 (other_stuff)]"
A9 is the first pair of nibbles appearing followed by the address it is loading.
Although, as I noticed, the next 2 pairs of nibbles after A9 is the address loaded by LDA in Little Endian.

Let's try the first line of code:
LDA     $7E9840

If I try to add that to the ASMDevKit in ROM address 0x2FF5F1 then I should have this as a line in DevKit:

EFF5F1:      A940987E     LDA  $7E9840

Is my theory correct so far?
So far I am using LDA, AND, CMP, RTS, BNE, STA and ORA for the code.
Although I am still unsure if the .CCS file I provided has the correct first byte for each command.
Most importantly, I don't know how I can add the label to the BNE code in DevKit nor how to direct it to another place to continue the code.

One last question...
Is there any problem if I simply put the ASM code in any random address in the ROM?
I want to use 0x2FF5F1 since I have a lot of free space from there onwards.

Thanks for the great help you guys have been providing me, seriously.
I've learned a LOT!

Oh and here's the code I want to implement in case anyone wants asks for it:

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

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

Well... A9 isn't the correct LDA code to use...
If you look at this page you'll see that SNES can actually use any one of fifteen different LDA opcodes, depending on the parameter.
A9 is LDA #constant. That is, if you want to load a specific (and always the same) value into A, you would use A9.
Example:
A9 05    LDA #$05

If you want to perform LDA $7E9840, you'll be using the AF opcode:
AF 40 98 7E

But, as I mentioned in an earlier post, usually the data bank is already set to 7E, so in most cases it is enough to write LDA $9840, for which you would use AD:
AD 40 98

I'm pretty sure the ASM dev kit will be able to look at your parameter(s) and choose the proper opcode to use.

As for whether you can use any random empty space - yes, but be aware of what is surrounding the empty space. If it is something you might edit later (and specifically if it is image or text data that would be a pain to move the entire section), you might regrt choosing to use that particular space. As long as you're sure you won't be using the space for anything else, though, have at it!
Ongoing project: "Final Fantasy IV: A Threat From Within"

Latest Demo

ShadowOne333

#27
Interesting.
So the ones with the assembler #constant is the one I should use but for the AND, CMP and ORA commands only?
29 for AND, C9 for CMP and 09 for ORA, and for both LDA and STA I should keep using the assembler addr (AD for LDA and 9D for STA).

The one that I still don't understand is BNE (and RTS).
Do I have to specify the address in which the new label starts?
Like specify the address of the LDA that begins the Runbutton label for example?

Let's see..
I will simply not take the first column (SNES address) into consideration for now since that might vary according to my SNES address in the ROM.:

AD4098    LDA             $9840            ; Load the data in RAM $9840
2901      AND             #$01             ; Check ONLY if the Burnt Status is loaded in RAM (#$01)
C901      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.
AD6600    LDA             $0066            ; Read the controller button bytes
2940      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)
AD4098    LDA             $9840            ; Load accumulator from RAM $9840
2901      AND             #$01             ; Turn all bits to zero except the lowest bit
094098    STA             $9840            ; Store in RAM $9840
????      RTS                              ; Return if button is not pressed

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

chillyfeez

Ah, branch functions...

In the actual assembly, the parameter is the amount of bytes the function will skip.
So, if you want to branch ten bytes if the accumulator is not zero:
BNE $0A

However, when using the ASM dev kit, you'll enter the offset to which you'll be jumping if the check returns true.
So, when using this tool (only), if, for example, your current offset is 04/8000 and you want to branch ten bytes if the accumulator is not zero:
BNE $800C
Because your code is two bytes long, and you're skipping 0A bytes.

It's a bit confusing to start out, but you'll get the hang of it.

As for RTS (and RTL), they have no parameters. When you use an RTS, the game will jump back to the opcode just after the JSR that brought you there in the first place.
Ongoing project: "Final Fantasy IV: A Threat From Within"

Latest Demo

ShadowOne333

#29
So ASMDevKit doesn't use the amount of bytes jumped but rather the address to which it jumps.
Although, I THINK ASMDevKit still takes into account what bytes have been jumped with the D0XX value, and BNE $XXXX only specifies what address it should jump to. Although, i don't know if I just write let's say BNE $F5FB, the "D0XX" part would be automatically changed to match the jump to that address from the corresponding address.

I will take 0x2FF5F1 as reference and the starting point for the first LDA (AD4098) (which for the ASM code should be 0xEFF5F1).

Here goes nothing then:

AD4098    LDA             $9840            ; Load the data in RAM $9840, (0xEFF5F1-F3)
2901      AND             #$01             ; Check ONLY if the Burnt Status is loaded in RAM (#$01), (0xEFF5F4-F5)
C901      CMP             #$01             ; Is the current status Burnt?, (0xEFF5F6-F7)
D001      BNE             $F5FB            ; If the status is not Burnt, go to RunButton subroutine, (0xEFF5F8-F9)
60        RTS                              ; Return if status is Burnt, (0xEFF5FA)

RunButton:                        ; If condition is NOT burnt, run the following code, not taken into consideration for ASMDevKit
AD6600    LDA             $0066            ; Read the controller button bytes, (0xEFF5FB-FD)
2940      AND             #$40             ; Ignore any other buttons that might be pressed and check ONLY if Y is being pressed, (0xEFF5FE-FF)
D009      BNE             $F60B            ; If Y Button is being pressed, go to set status to #$03 (Skip Sandwich), (0xEFF600-01)
AD4098    LDA             $9840            ; Load accumulator from RAM $9840, (0xEFF602-04)
2901      AND             #$01             ; Turn all bits to zero except the lowest bit, (0xEFF605-06)
8D4098    STA             $9840            ; Store in RAM $9840, (0xEFF607-09)
60        RTS                              ; Return if button is not pressed, (0xEFF60A)

RunStat:                         ; If Button Y is being pressed, load the following code, not taken into consideration for ASMDevKit.
AD4098    LDA             $9840            ; Load RAM Address in charge of the character status, (0xEFF60B-0D)
0903      ORA             #$03             ; OR statement. Set the status to Skip Sandwich (Bits 0000 0011), (0xEFF60E-0F)
8D4098    STA             $9840            ; Store status in RAM as Skip Sandwich, (0xEFF610-12)
60        RTS                              ; Return after code has been executed, (0xEF613)


For the BNE jumps, I am simply assuming that ALL the code is put up together next to each other (without the label names of course), so that way all the code would be consecutive in the addresses starting from 0xEFF5F1 to 0xEF613.
I also specified what addresses each line of ASM code would take to better illustrate the jumps (and hopefully I got them right).

Is the first byte per-command correct?

This would be the code in ASMDevKit:

chillyfeez

Mmm... your code looks great...
The Dev Kit is screwing it up by assuming you have a 16-bit accumulator (hence "AND #$0001" instead of "AND #$01"). that will almost certainly cause a crash.
Unless of course the Accumulator actually is 16 bits, but usually it's 8 by default.
The Dev Kit does have a toggle button for 8/16 bit status, but in my experience it doesn't work very reliably...
Ongoing project: "Final Fantasy IV: A Threat From Within"

Latest Demo

KingMike

Quote from: ShadowOne333 on April 15, 2015, 11:08:14 AM

So far so good, I now have quite some space to work with, so that means I should not need JSL/RTL, right?
I can work simply with JSR/RTS.
JSL/RTL is needed when you jump to a subroutine outside the current bank. (JSL uses a 3-byte address)
JSR/RTS is used when you jump within the current bank. (JSR uses a 2-byte address. Do not mix them up or you will crash the game)
"My watch says 30 chickens" Google, 2018

ShadowOne333

Ok so I made sure the code stays as a 8-bit accumulator and they now show up properly.
I even verified them manually in a HEX editor.

This is the code:


Weird thing is...
I load up the ROM with the code added in that address, but the Y Button does nothing at all. XD

I also tried changing LDA $0066 to $0076 and $0078 (since those two also change from 00 to 40 upon button input) but I get the same result.

chillyfeez

Umm... Is the game actually running the code?
We never discussed this formally, but you have to direct the game to actually jump to the code you're writing.
Ongoing project: "Final Fantasy IV: A Threat From Within"

Latest Demo

ShadowOne333

Quote from: chillyfeez on April 16, 2015, 11:56:34 AM
Umm... Is the game actually running the code?
We never discussed this formally, but you have to direct the game to actually jump to the code you're writing.
Mmmm I do not know if the code is indeed running or not.
How can I specify the game to jump to that code I added?

STARWIN

You need to find existing code that is executed. And it must be executed either 1. exactly in the state when you want your code to be executed or 2. more often, and you check whether you want to execute or not in your code. Then rearrange suitable parts of the existing code to your new space, and replace that with a jump to your space. "rewiring"

chillyfeez

In slightly more lay terms, ShadowOne, what you want to do is:
in Geiger's, get the game to a point where you would want your code to run live (so, in this case, when you're in a town or dungeon, just standing there).
Click "step in" and observe the offset of whatever code is running at that time.
Then click run.
Wait a second or two, then click step in again.
Chances are the offset will be relatively close to the first time, but if not, do it a few more times.
What you're looking for is any string of code that runs continually and constantly while in "walking around town/dungeon" mode.
You probably don't want to do this in a location where there are things wandering around that might try to attack you, as they may cause the game to run code that is unique to that situation, and you don't want "press Y to run" to work only when monsters are trying to attack you (although that would put an interesting spin on it  ;)).
Now, once you've found said string, you'll want to find an opcode (or a couple of opcodes) that can be replaced with a subroutine jump. most likely, what you will find will be in a different program bank than the custom assembly you wrote, so you'll need a JSL (and not a JSR), meaning you'll need to replace at least four bytes with this jump (and if you use a JSL, you'll need to replace all of the RTSs in your custom assembly with RTLs). The bytes you'll want to replace will be opcodes that don't depend specifically on where they are in the program bank, because you'll be jamming them into the beginning (or the end) of your custom assembly. The easiest ones to use are a "LDA #constant" and "STA" coupling, but others are usable, too. Avoid replacing branch codes (BNE, BEQ, BCC, BCS, BRA), because they won't work right if you move them (the parameter is relative to the current offset).

When the game is running, there's always a string of code running. When conditions don't change, often the same code is looped over and over, simply to maintain the "nothing is happening" status.
You want to take that constantly looping string of code and, essentially, splice your custom assembly in.
Don't worry about the fact that you're momentarily interrupting the game's normal processes, it will take approximately one bajillionth of a second to run through the extra stuff you're putting in.

Again, I know that's a mouthful. Questions about any of it?
Ongoing project: "Final Fantasy IV: A Threat From Within"

Latest Demo

ShadowOne333

F*ck, I didn't take this into consideration.

You see, the HEX value for the Button Y input is 40 in RAM addresses 7E0066, 7E0076 and 7E0078... right?
Ok. That's the value for JUST the Y Button, I completely forgot about the D-Pad inputs.

THE VALUE CHANGES WITH EACH D-PAD INPUT! Dx
I forgot that hahaha damnit.

Ok so these are the values for each one of the 8 possible directions in game.

Y + Up = #$48
Y + Down = #$44
Y + Left = #$42
Y +Right = #$41

Y + (Up + Left) = #$4A
Y + (Up + Right) = #$49
Y + (Down + Left) = #$46
Y + (Down + Right) = #$45

Might be the reason why I am not getting any output at all, since whenever I press a direction AND the Y Button together, the value is not 40 anymore...
How could I've overlooked that?!
So with those values now on the fly, what changes are needed in the code?

Also, thank you both STARWIN and chillyfeez for your replies.
Unfortunately, I am completely lost there.
I opened up Debugger and everything, but whenever I click "Step into" I get a completely different ASM command, the offset varies everytime, although not by much (i.e. around 0x200 maximum, as an example 0x05E00-0x0600 as change between opcodes)
My guess is that maybe this is caused due to the music looping, which might be the reason why it is constantly loading opcodes and changing addresses within a 0x200 range.

I am struggling a lot to make this work, I think I might start offering PayPal money for whoever gets it right. :P

chillyfeez

I'm not interested in money for this (I don't even have a PayPal account), but I am interested in helping you realize your goal.

So... First thing to do is figure out if your code is running at all, so set a breakpoint in Geiger's to EFF641 (as far as I can see, the beginning of your custom ASM), and check the "execute" checkbox.
Then Run.
If the game pauses immediately, then your code is running.
If the game does not pause, then your code is not running.

If your code is running, then the problem is probably that the code isn't working right (unlikely - it looks right to me, and the AND #$40 is there specifically to make it so that it doesn't matter if other buttons are being pressed).

More likely is that the code is not running at all. If you just picked some random space in which to place the new code, without an instruction to jump to that place, then the game has no way of knowing that it should try to read that code.

Quote from: ShadowOne333 on April 16, 2015, 08:26:22 PM
I opened up Debugger and everything, but whenever I click "Step into" I get a completely different ASM command, the offset varies everytime, although not by much (i.e. around 0x200 maximum, as an example 0x05E00-0x0600 as change between opcodes)
My guess is that maybe this is caused due to the music looping, which might be the reason why it is constantly loading opcodes and changing addresses within a 0x200 range.

No, it's almost certainly not the music looping. You have to really dig to find the assembly that handles playing music or displaying images. What you're talking about is exactly what I was trying to get you to.

When Ness is just standing there, there will be code constantly running, tracking and controlling all sorts of stuff, like where the NPCs are, whether you're pressing the d-pad... Whatever. All that goes on in the game needs to be refreshed constantly in order to keep the game running.

What you need to do is take a piece of that constantly looping code and... Sort of build a railroad switch track (so to speak) out of there to the stuff that you wrote, then when it's done running your code, you want it to jump back to the stuff it was doing before.

So, here's what I want you to do...
Hit "step in" again. Hopefully, you'll get something within that 0x200 byte range you're talking about.
Whatever offset comes up, set a breakpoint for when the game executes that offset.
Then Run.
It should pause instantly, and the debug window will display that offset and whatever opcode is there.
Run again and you should get the same result.

Confirm for me that you understand those instructions and that you're getting those results, and we'll move on from there.

Don't worry, as long as you have the patience, we'll get there.
:thumbsup:
Ongoing project: "Final Fantasy IV: A Threat From Within"

Latest Demo

ShadowOne333

Alrighty then!

So first things first:

I did what you told me about the Breakpoint.
I set it to EFF641 and it doesn't stop at any moment, so the code is not running at all.

I did some more experimentation with the Breakpoint (since you just told me how it works).
Basically, I set the address to 7E9840 and checked "Write", so basically the game should stop whenever it tried to write any value into 7E9840, being it 00, 01, or 03 which should be the only possible status.
It worked as I thought.

If I fail a teleportation, the game stops and the debugger shows up this:
$C0/EA0E 8D 40 98    STA $9840  [$7E:9840]   A:0001 X:003A Y:0000 P:envMxdIzc
$C0/EA34 9C 40 98    STZ $9840  [$7E:9840]   A:0000 X:00B4 Y:0001 P:envMxdIZC


The first STA command occurs when you hit a wall but that happens just before the Burnt Sprites load up, after the sprites load, the game runs until the 3 sec pass and then STZ enables the normal sprites once again.
Upon reading a little bit about STZ, basically it stores Zero into the address, so that's what sets the normal status back again (00).

Did the same for the Skip Sandwich.
Bought two and tested out the output by the debugger. As soon as I use one I get the following:
$C0/76E5 9D 00 00    STA $0000,x[$7E:9840]   A:0003 X:9840 Y:0258 P:envMxdIzc
$C0/76A6 9C 40 98    STZ $9840  [$7E:9840]   A:01E0 X:9E3C Y:9E3E P:envMxdIzC


So those two lines are the ones that enable the sprinting in game.
My guess would be to change the address that gets loaded, so that it loads 0066 instead and compares it to one of the 4X variations for Y and a Directional input to hopefuly make it work.

Here are the snippets of code from the addresses the breakpoint gives:




As for your request:
I did that.
I got into a random cave with no enemies with only my party members, hit "Step Into" and got this:
$80/94E6 8D 58 0A    STA $0A58  [$7E:0A58]   A:FFFF X:0036 Y:0008 P:eNvmxdIzc

So I set a Breakpoint for 8094E6 and checked in "Exec", after that I clicked on "Run" and the game stopped immediately.
I hit Run again, and it stopped once again, then again, and again...
I got the same offset and opcode with that address.