Alright, I'm back. I actually forgot to do the whole breakpoint thing with the JP meter. I need to ask something before do so, and I can't believe I haven't asked this earlier: What is the second address mean at the bottom right screen? What is that bottom right screen? I may need to revise the way I document things if it turns out I've been misinterpreting what it means.
This is what's called the "stack".
As I'd mentioned, there are 16 registers - hands of the octopus. And memory is where things go when you don't need them in your hand right now. Stack is just memory, but it's a "special" kind of memory just by convention. There's nothing actually special about it, except the way game code uses it.
Everything I'm talking about now is a "convention". I mention this because a programmer can do whatever they want. If some developer working on Lufia decided to hand-write some assembly for an important function of the game, they could've not followed these conventions at all. But for 95%+ of code, they'll be followed.
So, imagine a function call. Let's take something simple, like "a = pow(b, c)". This function takes b to the power of c, and returns it in a. There's no single instruction for it (like "pow r0, r1, r2"), it requires a bunch of instructions.
The assembly for pow() would be "bl 8123456h" (address is an example.)
But, how does it know which things to multiply and where to put the result? Convention.
We call the inputs to a function "arguments" or "parameters". They should be r0, r1, r2, and r3. In this case, there are only two, so r0 and r1. If there are more than 4, it uses the stack but don't worry about this for now.
The result is then returned in r0. So now we know pow() would look like this:
mov r0,b
mov r1,c
bl pow
mov a,r0
But, what if you had something important in r0 already? Or what if you had important things in other registers? This is just a jump, really, a "Branch and Link". If pow makes a bunch of changes to registers, you'll lose their values.
This is where the stack comes in. You save the registers you care about on the stack. Here's the convention:
r0-r3: Arguments / return values.
r4-r11: "Callee saved". Callee means the function itself. If pow() wants to use these registers, it must make sure it saves them and puts them back the way it found them.
r12: "Callee saved". This is a special temporary register.
r13: Stack pointer. This is the address of the stack (more on that in a bit.)
r14: Link register, technically callee saved. This is the calling code to return to.
r15: Program counter. Code address to execute soon.
Let's say pow is complicated. You might see this code in it:
push r4-r7,r14
... code for pow(r0, positive r1) and pow(r0, negative r1) ...
pop r4-r7,r15
In many ways, "push" is like str. In fact, it's technically stm (store multiple.) It does the following:
* Decreases the value of sp (aka r13) by enough for the registers passed. In this case, 20 bytes.
* Supports ranges for r0-r7 only, and can specially do r14. If you want to push r8, you'd move it to another register first.
* Stores each register in that space, i.e. "str r4,[sp,0]" and "str r14,[sp,16]".
The pop instruction is similar, in reverse. It's like ldr, where it reads from the stack.
So now finally to your screenshot:
https://cdn.discordapp.com/attachments/769286995779780689/771367803067432970/unknown.png03007C00 is the current value of sp. It started as a higher value, and goes downward as more functions are called, then back upward when they return.
If you notice 03007C28 says "Pushed r7". That means this was stored by "push r7", or actually a "push r4-r7" if you look further down. The extra r5, r6, r7 are probably actually r8-r10 moved to those registers. This is very common.
The "Return from Lxx_8001a5Ch" is the r14 pushed on the stack.
"Allocated" means that the stack value was changed without using push. For example:
push r4-r7,r14
mov r4,r8
mov r5,r9
mov r6,r10
push r4-r6
sub sp,sp,16
You would do this if you needed a quick "scratchpad" to save registers while running numbers. Maybe your math formula is complicated, and using all of r0-r10 (11 registers) isn't enough. So you scribble some things down into that allocated memory, temporarily.
Very important things to understand about stack:
* It's temporary. As soon as pow() is done, another function will use these addresses and overwrite it.
* The address might change. pow() might be called by different people - some might allocate more or less stack. So the addresses pow() sees for sp are not consistent.
The second column there, like on the left, is the value stored at that address. I'm really burying the lead here, but the above is important context for this fact.
If you see an important/useful value on the bottom right, the address is still not safe to say "this is where the game stores JP". Even a hack you apply to another function might change that address. It's just a temporary scratch location.
Fair point, I just came to that conclusion because expanding the ROM itself didn't cross my mind, mostly because I have no clue how I would do that. It's probably really simple to expand it to 16 MB, but in order to make any proper use of the new space, I'd need to do a bunch of things that are beyond my current level of knowledge. A daunting task, indeed. No idea what you mean by "trimming" a ROM. Wouldn't that require recompiling the ROM, thus necessitating a disassembly of the game?
You can basically just make the file larger, like using a hex editor or with ".org" in armips.
https://github.com/Kingcom/armips#set-the-output-position-[Unknown]