[ASM] Tips On Looping Through Deployed Units

Since deployed units aren’t stored in linear order (e.g. the “Other” Units are stored after “Enemy” Units though their deployment numbers are before (0x40’s, vs 0x80’s)). This is also the method the game uses to get the nth deployed character.

FE7 - 0x08B92EB0
FE8 - 0x0859A5D0

The nth pointer is a pointer to the nth deployed unit. Note that since the deployment table is 1-aligned, the 0th pointer is 0x00000000. So instead, keep a loop counter and load the nth pointer.

loop through
(0x00-0x3F) for players
(0x40-0x7F) for other
(0x80-0xBF) for enemies
(0xC0-0xC5) for 4th-player.

Just continue looping if you hit a 0-pointer.

1 Like

it’s probably a good thing to note that the index in that array corresponds to the value at 0xB in the char struct

the upper nybble of said byte determines allegiance

Also, I think the slots in game are 0x48-aligned, so it should be safe to just loop through the strict ram location.
But this is still the technically safer and/or better way, so just advocating it!

it’s a standard word-aligned struct, so they’re all just 0x48 bytes of data

looping through that way is perfectly fine if you’re only checking one faction

Well, I mean that the enemy faction will be hit if you keep looping through 0x48 increments from the player units. But it seems a bit more fickle/easy to break. So yeah, I’d go with this method.

if you just terminate on a 00 character pointer like the game does you won’t hit hit the enemy units ever

i’ve actually seen almost 50/50 usage of the +0x48 method and using the pointer table

in fe8 i’ve run into weird issues where there are units just separated at random but that’s a pretty edge case

I stopped the 00 termination because dead characters get their character pointer wipes, so if a character in the middle of your data dies, . . . Your loop is broken.
It’s more secure to loop through a constant 0x3F slots.

Copy-paste code for looking through deployed units for one with character ID given in r0.

.thumb
.org 0x0

push {r4, r14}
mov r0, #0xFF					@Character to look for
ldr r1, Unit_Pointers
mov r4, #0x0
look_for_unit__asdfgh:			@Name mangling for copy-pasta-bility
	lsl r2, r4, #0x2
	ldr r3, [r1, r2]
	cmp r3, #0x0 				@Is the slot valid?
	beq loop__asdfgh 			@Nope
	ldr r2, [r3] 				@is there a character here?
	cmp r2, #0x0
	beq loop__asdfgh @Nope
	ldrb r2, [r2, #0x4] 		@Character number
	cmp r0, r2 					@Is it the character we are looking for?
	beq Perform_Action__asdfgh
loop__asdfgh:
	add r4, #0x1
	cmp r4, #0x3F 				@Have we checked 0x3F units?
	bgt end						@The unit we were looking for is not deployed.
	b look_for_unit__asdfgh
	
Perform_Action__asdfgh:
	mov r0, r3
	@r0 = &Unit with the id of initial r0.
	
end:
	@things might have to be done here, such as making it return false or something like that.
	
	pop {r4}
	pop {r1}
	bx r1
	
.align
Unit_Pointers:
	.long 0x08B92EB0

Edit: Something I use for assembly conditionals more is something like:

returnFalse:
	mov r0, #0x0
end:
	
	pop {r4}
	pop {r1}
	bx r1

and instead of the branch to end in the 0x3F player unit check, I’d branch to returnFalse.

08018D0C - subroutine that gives the r0th unit. [FE7]

I always just looped through the WRAM and ignored NULL characters while still iterating the full 0x3E times (I think it’s actually 0x3E)

I mean, those references you’re talking about are in the ROM. You are basically just calculating them on the fly without any sacrifice to speed or memory usage. They certainly aren’t going to change

Right, but it helps if you also need to loop through the enemy or other units, since those aren’t stored linear in the right order in RAM.

(Also, don’t tell me what not to change about my ROM :P)

but then you have the same situation where you already know where

what

is this for looping through the entire cast of units regardless of allegiance with a single loop? I guess that would be a good reason to bother with the pointers but then like

why would you be doing that to begin with

I would sooner have my 3 loops (read: 3 loops of one loop) and allow for some customization based on allegiance (odds are I would want it)

yeah too late

your choices are do what I say or replace FEditor from scratch :smiley:

There are also pointers for that. Also, this way you don’t need to keep around a 0x48 for adding to. Also, robustness. And readability (just comment @gets the nth deployed unit).

or you could like

not do that

and use all those lousy pointers in the ROM as more free space

Well, or not, since the in-game methods use those pointers anyway. And changing that would be… really difficult.

Edit: Fixed a typo in my code… oooooops. It was on the “is there a character here” line. Load from [r3], not [r5]

There are in-game getters. I’ll post them here just so I don’t need to find them every time.

params: r0 = deployment number
returns: r0 = pointer to that character’s data in RAM.

(todo, as I don’t have their locations lol oops)
FE8 - 0x08019430

FE7:

08018D0C
Parameter: r0 = deployment number
Return: r0 = pointer to unit struct in RAM

08017D34
Parameter: r0 = character number
Return: r0 = pointer to unit struct in RAM

FE8:

08019430
Parameter: r0 = deployment number
Return: r0 = pointer to unit struct in RAM

0801829C
Parameter: r0 = character number
Return: r0 = pointer to unit struct in RAM

FE6:

0801860C
Parameter: r0 = deployment number
Return: r0 = pointer to unit struct in RAM

08017ABC
Parameter: r0 = character number
Return: r0 = pointer to unit struct in RAM

3 Likes

Oh ok I guess that saves me some work/debugging