Tracing the Branch Calls of Using a Stat-Up Item(FE7)


Okay, I think Blazer had been able to add item effect before(but I couldn’t find his documentation on it)(Edit: found it: [FE7] Blazer's Notes on Custom Items It’s not exactly the thing I need(I need the code for the actual effect of dragonshield, so I’m still going to keep going) and I figure a more thorough routine-by-routine analysis never hurt), and I don’t know how much of this is known as the forum is still budding and other places’ organization is… bleh. But it’s not something I’ve found by digging through some of my commonly referenced docs so here goes.

This was in an effort to figure out how to make the popup window for like Energy Ring, Dragonshield, etc. So to start, I started from the vulnerary heal code. I traced that back to a subroutine at 802D004. In short, it seems to

  • load the item in question
  • I assume it loads the item effect byte, or maybe that’s just what it loads in the first part(it doesn’t help that they’re just equal to the item ID)
  • It preforms a hard check to make sure the effect ID - 0x4A(Heal) is less than 0x50; meaning that the effect ID is less than 0x9A(Pointless check?) – Also forces the item effect to be greater than 4A, which Blazer’s expansion had to target.

Okay, but here’s the interesting part. It takes the effect ID(after subtracting 4A) and left shifts it by 4 and adds it to a pointer to 802D04C. I believe this to be the pointer table for item effects.(lolwin for functional pointers) This is reinforced by how the first four point to the same routine. (They would be for Heal, Mend, Recover, and Physic. I assume they just check the effect id again or something).

I’m going to try messing with the values and peeking at subroutines, but I wanted to document this first. Will bring updates as they come.

Interesting. The stat up items all point to the same subroutine – 0E D2 02 08 (or read with little endian, 0802D20E)

On the subroutine at 0802D20E --(this is eliwood using a dragonshield)
-r0 is the offset of the subroutine
-r1 is 0802D04C, the offset to the pointers to the subroutines
-r2-r4 are 0
-r5 is 020251F4(This is important because the first line is mov r0, r5)(The data here is 74 33 B9 08-- 08B93374) That’s a 0x19, but I’m assuming the place it points to changes.
-Then it branches to 0x802CE60

On the routine at 802CE60 –

  • registers as before, first line(after pushes) r7 := r0
  • Loads 0x203A85C into r4…
  • Loads [r4, #0xC] into r0, so effectively r0 := *(0x203A868). Data there looks like 01 02 10 03 04 17 00 00 00 00 01, then 00’s. 01 is Eliwood, uh… (Confirmed by experimentation – 0203A86B contains the deployment number of the last selected unit. Deployment number, not character ID) Funfact: 0203A86C contains the number of spaces that they moved(that turn). 0204A86E contains the position in inventory of the last selected item.
  • Branches to #0x8018D0C (Loads pointer to Eliwood’s deployed data(at 0202BD50) into r0. r1 and r2 are clobbered)
  • Loads [r4, #0x12] into r1. Still have no idea what that is? but this loads 00 in my case. (Oh wait, it’s the position in inventory of last selected item. So the item we’re using)
  • Gets pointer to the item in inventory of deployed unit data… Loads it into r6.
  • loads 0203A470 into r1. Not sure what this is as of yet but it looks like B0 DA BD 08 00 00 00 00 01 FF 00 87…
  • adds 6F to it, so r1 := 0203A4DF…?
  • loads FF to r2 and stores that into [r1]. Okay, so we wrote an FF to a particular spot in RAM…?
  • load [r4, #0x12] into r1. Again, the position in inventory of the item being used
  • Branch to 0x802CD28
  • r0 now has Stat Bonuses Pointer(for emblem seal and such?)
  • Loads 0x12th byte of eliwood’s deployment data… max HP…
  • Loads the dereference of the stat bonuses into r3. Dragonshield has no stat bonuses.
  • I see, it’s finding maxHP with stat bonus(you can stat bonus max HP?)
  • Writes it back as-- oh crap, I see how it is. The stat bonuses of the stat up items are what it gives as a bonus. That is so cool.
  • Repeats for every other stat
  • Branches to 0x8017C64(Checks max stats)
  • moves r4 to r0 and r7 to r1(0? Inventory slot position, I think?), then braches to 801842C
  • loads 203A470 to r0(idk). Checks the 6F-th byte(arthimetic) of that so see if it’s less than 0(it’s -1 for me, so yes)
  • pops 'n returns to r0(802F31B)

On the routine at 8018D0C --(Note we still haven’t used the pointer to effect subroutines or loaded item effect anywhere…) 'Get the nth deployed unit"

  • Loads 8B92EB0 into r2… Seems to be a pointer table? 00 00 00 00 50 BD 02 02. wait, 0202BD50? That’s the first deployed unit. This pointer table’s nth entry(1-aligned) points to the nth deployed unit(I think I saw this while doing another hack before). Then gets the LO Byte of r0… multiplies it by 4… adds to r2…
  • Okay, so it’s loading a pointer to Eliwood’s deployed data in RAM. Loads it into r0 and returns.

On the routine at 802CD28 --“Get the inventory item data from given character and inventory position”

  • Basically, get the item data at the r1th inventory position of the character data at r0. Then branches to 80171B4(agghhhh branches everywhere). So gets the item ID of the inventory position of the item used. Legit.
  • Hard check against item id #0x88?(Afa’s Drops) In any case, jumps to 802CD60 if not afa’s drops(Guess they’re that special.)
  • Moves r6(the whole item data with uses and all) and branches to 80173E8

On the routine at 80171B4 – “Get item ID from inventory item data”

  • Simple one here, takes the LO Byte of r0, so the item ID

On the routine at 80173E8 – “Find Stat Bonus”

  • Grabs the LO byte of r0
  • performs the operation r1 = 36(decimal)*r0. Loads r0 = 8BE222C(Item data table), and adds that into r1.
  • So r1 is now pointer to the item being used. Loads the #0x0C double word of the item data(agh I don’t have my nightmare modules handy to tell me what that is, but it’s 08C98FE0 here – another pointer?)(I looked it up, it’s the Stat Bonuses Pointer.)
  • Returns

On the routine at 8017C64 – “Check Max Stats”

  • “ldsb” load signed byte from [r4, r1] into r1. r4 is eliwood’s deployed data, r1 is 12(a constant, loaded the line above). So, Max HP. Load 0xB into r2… the byte before turn status. Seems to be 0x1
  • Ands that thing with 0xC0… if not equal to 80 branch down to 8017C7C (is this checking for Canto?)
  • Compares r1(maxHP) to 3C(that’s 60)(this is at 8017C7C; if you want to increase max HP this is somewhere you have to change)
  • If less than, branch down to 8017C90
  • Loads halfword starting from 0x4 of Eliwood’s deployment data – that’s class(I think it’s checking against caps). Goes through and checks all the caps and stores the caps into his data if his temporary stats exceed it.
  • Pops with r0 = 0802CDBD and returns (Was stat bonuses pointer)

On the routine at 801842C

  • pushes r4, r5, r14
  • Takes the (2*r1+1E)th byte of the character data(no yeah, inventory item slot data, by which I mean the durability and ID)
  • checks to see if it’s nonzero? If not branches to 8016730
  • stores the return value into *r4, the item slot.
  • moves the pointer to his data into r0 again and branches to 8017688
  • pops r4, r5 and then r0, then branches with exchange to r0(802CDC4)

On the routine at 8016730

  • gets the 8th byte of the specified item from the rom’s item data… “Weapon Ability 1” Checks the third bit(Ands it with 8)(Indestructible)
  • if not then loads FFFFFF00 into r0(this is -1<<2) and adds it to the item data(effectively decreasing durability by one)
  • Is r2<=FF? (i.e. is the durability byte 0?)
  • if so, return 0(not charting out the if not today, sorry folks)(presumably it just returns the item with 1less use in that base)

On the routine at 8017688 –
-Ugh stack manipulations. add sp, -#0xC

  • moves sp to r2.
  • Uh, loads a pointer to the first item slot, again. (into r4)
  • is the item 0? I.e. was it used up?
  • if so, branch to 080176A6. Store back 0?
  • loop over the inventory doing this until you get to slot 0x4(note- inventory IS 0 aligned)
  • I think we just stuck the inventory onto the stack.
  • somethin somethin pop stuff, bx r0

On the routine we somehow ended up at 0802CDC4???

  • moves r6(the item data BEFORE use) to r0 and branches to 80171B4(Get item id)
  • subtracts 5A from the item ID and compares it it to #0x8(hero crest or higher)
  • branches if hero crest or higher.
  • r0 := 4*(itemID-5A(first promote item))+0802CDE0(whose data looks like 14 CE 02 08 44 CE 02 08 etc; it’s a pointer item for stat up items)
  • branch to that location. I have dragonshield so it’s at 0802CE1A

On the dragonshield-specific routine at 0802CE1A —

  • load #714(what??? I think this is the text code for “Def increased” or something? goes to check Yup, confirmed.) to r5, branch to 802CE54(They all seem to branch there). Moves the code to r0 and pops r4-47, then r1, then bx r1. r1 is 0802CE87

On the stat-boosting-item-common-code at 0802CE87 —

  • shoves the text code into r5 and loads r0:=202BBF8(then adds 41, so 0202BC39. Interestingly, this is 21 bytes after the tactician name). Load the byte, then lsl by x1E, i.e. get only the rightmost 2 bits.
  • Is it 0? if less than 0(i.e. the 2nd bit was a 1) then branch. For me, it is not.
  • Load #37A into 0. (This isn’t text; that’s “Read the compatibility of units” which I doubt is related.)
  • branch 80BE594
  • move r6 into r0, that’s the item with durability before use.
  • branch 08017400
  • move r0 to r4(r0 is the thing we got, 5E(which is weird)(It might be staff/use effect?)
  • move r5(text id) into r0, branch to 8012C60
  • shift some registers around, brach 801F100(which does more stack manipulations aggggh)(But it seems to be doing something with the item code, at least, I think)
  • pops+ branches to r0, which is 0802D215(Back up to the 2nd subroutine, I think?)

On the routine at 080BE594, and 80BECC8 —

  • push r14
  • put the bytes we loaded into r0 into the top word(lsl 0x10)
  • load #0x869D668 into r2(the crap?)
  • load #0x869D6E0 into r1
  • lsr’s r0 by 0xD(the crap) it is now 1BD0.
  • add it to r1(into r0)…? and load the halfword at [r0, 4] into r3… what. I am lost. Anyway, r3 is 7.
  • r1 = 12*r3 + r2. whatever r3 is, its stuct size is 12 bytes.
  • load into r2 from [r1], and into r1 from [r0], uh, I’m lost, but r1 is 8AE4A60(which I think is text? Big “I think”.) and r2 is 3005CE0. move r2 to r0 then branch to 80BECC8(which I am keeping my notes here)
  • Now it’s loading stuff from 0x68736D53??? That’s in the VRAM; I’m certain this is drawing the box that the text goes on.
    -Does stuff I don’t care to analyze(it overwrote r5 anyway), then pops registers and bx r0(which is 80BE5B5, right where we left off)
  • pop r0, bx r0(which is 0802CE9B, where we left off before)

On the routine at 08017400 —

  • is r0 (no item; useless check?) branch if it is.
  • get the item ID of it, load its data from the rom, get byte 1D(IDK? 5E for dragonshield)
  • unconditional branch down to 8017420…
  • return(r14 being 0802CEA1)

On the routine at 8012C60 –

  • Loads 202B5B4 to r6 – seems to be a temporary place to store text?(Playing around with text makes it look like the id of the text last displayed)
  • Is it already being displayed? If so, branch(it’s not)
  • make r0 into r5<<2(so, it’s going to do a pointer lookup)
  • r1 is 8B808AC + r0(so, r5 pointers after)
  • load the pointer to r0.
  • load 202A5B4 to r4, and put that into r1.
  • Branch 8004364(I’m not documenting this)
  • put r5(text code to be displayed) into [r6]
  • put 202A5B4(was r4) into r0, pop stuff, bx r1(which is 0802CEA9(which is where we left off))

On the routine at 802F31A —

  • here’s a short one, just pops r4,r5 and r1 and bx r1(which is 800491B)
  • Stuff happens, we end up at 8000AF2, we jump up 2 commands and the thing is actually drawn by 8000AEE(Which calls the subroutine at 80019D4)

Making text info boxes on items (e.g. "Weapon Level increased")
Stats booster Items Module
Stats booster Items Module

Okay, so at this point I’m thinking I MIGHT be able to draw the stat up box by calling 0802CE94 with r5 being the text I want, but I’m not sure (not am I able to test it right now, but I’m also fairly certain it would crash :P).

Probably 802CE60 instead because that’s where it pushes to the stack… But that wants to be an actual item use.

I’ll go observe what the registers being pushed are so we can push them in my asmc routine and skip their item use deduction thing.
their command is push {r4-r7, r14}
r4: 00000001
r5: 0202511C (–> 74 33 B9 08(–>19))
r6: 00000000
r7: 03007DD8(–> F8 57 B8 08(–>???))
r14: 0802D215

So I don’t know what will happen; it’ll probably crash. Ideally, it’ll draw something on the screen before it does.

Guys, I suck at graphics halp plz :.


“- I see, it’s finding maxHP with stat bonus(you can stat bonus max HP?)”

By stat bonus, you mean the boosts given by equipped(generally legendary) weapons, right? Yeah, you can give them a HP bonus. The Uber Spear Vaida uses gives it. (Having a lance using unit with over 60 HP is fun)

The Communal To-Do List

If you’re trying to draw that box…

[quote]Text tilemap written to BG0 - 0600:6000

Text tiles written to tiles 128-160ish on CHARBLOCK_0 base? More testing required
address 0600:1000

Box written to BG1 - 0600:6800
Box tiles written to tile 1

Break on 0600:6000

routine at 080016EC - Not messing with this
Calls CPUFastSet (memcpy) with argument src=0202:2C60, dst=0600:6000 and len=0x200
Probably copying BG0 tilemap data into VRAM
This is definitely the case, as it gets called everytime the BG0 tilemap gets changed

080016EC called in series of function calls - 08015274
080011B0 - Setting the graphics mode by altering a bunch of stuff in IOregs[]
080016EC - CPUFastSet the tilemap
08003178 - Does a bunch of other stuff, then CPUFastSet 0x20 bytes from 0x080C8E24 to 06005F80
080032E0 - CPUFastSet the OAMData
080BE588 - One of these is not like the others
Does some really weird stuff that I don’t follow

After this, bx r0 goes to IRAM code then returns to mainloop for a bit… I’m more than a little

080019D4 - Repeated calls of this routine appears to set up the tilemap, then display it. I’m confused.

Break on 0600:1000

08005AD4 - A bunch of graphics routines

Routine at 080054E0 - Also not touching this
store 00 into SP-[2,4]
bx to 0800587D
ldrb r4, [r4, #0x4] @ lol
lsl r2, r4, #0x4 @ looks like a really fast way to get a numeric value
CPUFastSet(src=sp, dst=0600:1000, len=01000140)
…hasn’t actually written any data. Maybe just clearing it in preparation for whatever comes next?

0800587D -
ldrb r2, [r0, #0x4]
ldrb r3, [r0, #0x6]
mov r1, r2
mul r1, r3 @ ???

ldr r0, =0x02028D70
ldr r0, [r0] @ 0600:1000 - Tile bitmap location
add r0, r1, lsl #0x2 @ probably gets location of specific graphic tile

08005719 - CreateTextTiles(?, ptr textLoc)
Loads the relevant font tiles to CHARBLOCK_0 VRAM to display the text at textLoc

08005590 - ???

This looks to be stat-item specific
0801F100(ptr ?, u8 iconID) - Called after 08012C60
Loads relevant graphics into VRAM, not sure what else it does
Also appears to load the tilemap into WRAM to be copied into VRAM later
- tilemap WRAM at 02022EAC

08012C60(u16 textID) -
Loads text data from index textID and stores it into WRAM. Returns storage location.

At this point, I’m almost convinced it might be easier to try homebrewing it onto the screen instead

doc link

My head’s spinning, I spent several hours yesterday staring at tonc and no$debugger and couldn’t actually make out anything else. I think I might be able to get it to draw the box just by setting some of the IO registers and copying the map data into VRAM (the former being what’s giving me the most trouble)