|
|
|
@@ -0,0 +1,74 @@
|
|
|
|
|
/* func_800AA01C — 0x800AA01C..0x800AA0A0 (132 bytes).
|
|
|
|
|
*
|
|
|
|
|
* Original words:
|
|
|
|
|
* 27BDFFE8 addiu sp,sp,-24
|
|
|
|
|
* AFB00010 sw s0,16(sp)
|
|
|
|
|
* AFBF0014 sw ra,20(sp)
|
|
|
|
|
* 0C02A0B4 jal 0x800A82D0 the shared lookup
|
|
|
|
|
* 00A08021 _move s0,a1 (delay slot) keep the mode
|
|
|
|
|
* 00402021 move a0,v0 the looked-up object becomes the working pointer
|
|
|
|
|
* 10800015 beqz a0,0x800AA08C
|
|
|
|
|
* 00000000 _nop (delay slot)
|
|
|
|
|
* 16000006 bnez s0,0x800AA058 <- mode != 0
|
|
|
|
|
* 00000000 _nop
|
|
|
|
|
* 90820014 lbu v0,20(a0)
|
|
|
|
|
* 34420008 ori v0,v0,0x8
|
|
|
|
|
* 0802A821 j 0x800AA084
|
|
|
|
|
* 304200BF _andi v0,v0,0xBF (delay slot) clear bit 6
|
|
|
|
|
* 24020001 li v0,1 <- 0x800AA058, mode == 1 test
|
|
|
|
|
* 16020006 bne s0,v0,0x800AA078
|
|
|
|
|
* 00000000 _nop (delay slot)
|
|
|
|
|
* 90820014 lbu v0,20(a0) <- mode 1 arm
|
|
|
|
|
* 34420040 ori v0,v0,0x40 set bit 6
|
|
|
|
|
* 0802A821 j 0x800AA084
|
|
|
|
|
* 304200F7 _andi v0,v0,0xF7 (delay slot) clear bit 3
|
|
|
|
|
* 90820014 lbu v0,20(a0) <- 0x800AA078, the default arm
|
|
|
|
|
* 304200B7 andi v0,v0,0xB7 clear bits 6 and 3
|
|
|
|
|
* 0C02A0C4 jal 0x800A8310 <- 0x800AA084
|
|
|
|
|
* A0820014 _sb v0,20(a0) (delay slot) write the byte back
|
|
|
|
|
* 8FBF0014 lw ra,20(sp) <- 0x800AA08C
|
|
|
|
|
* 8FB00010 lw s0,16(sp)
|
|
|
|
|
* 27BD0018 addiu sp,sp,24
|
|
|
|
|
* 03E00008 jr ra
|
|
|
|
|
* 00000000 nop
|
|
|
|
|
*
|
|
|
|
|
* A three-way mode switch that edits two bits of a byte field in a looked-up object, then
|
|
|
|
|
* flushes. All three arms converge on one store and one flush, so this is a `switch`-like
|
|
|
|
|
* shape with a single tail rather than three returns.
|
|
|
|
|
*
|
|
|
|
|
* The masks are read-modify-write pairs, and the pairing is what identifies each arm:
|
|
|
|
|
* - mode 0 → `| 0x8` then `& ~0x40` — **sets** bit 3, **clears** bit 6;
|
|
|
|
|
* - mode 1 → `| 0x40` then `& ~0x8` — **sets** bit 6, **clears** bit 3;
|
|
|
|
|
* - anything else → `& 0xB7` — clears both.
|
|
|
|
|
* So the field encodes a two-state selection with a third "neither" case, and the two
|
|
|
|
|
* non-default arms are exact complements of each other. That is a strong structural signal the
|
|
|
|
|
* source was an `if`/`else if`/`else` chain rather than a switch with distinct bodies.
|
|
|
|
|
*
|
|
|
|
|
* The object pointer is moved into `a0` immediately after the lookup, so the flush receives
|
|
|
|
|
* the **looked-up object** — the opposite of the sibling row `0x800AAC44`, where the flush
|
|
|
|
|
* receives the routine's original `a0`. Two rows in one family that differ in exactly that
|
|
|
|
|
* respect, which is why the `move a0,v0` is worth checking on every row rather than assumed.
|
|
|
|
|
*
|
|
|
|
|
* LIMITS: the field offset 0x14 and the masks 0x08/0x40/0xB7 are read from the bytes. The
|
|
|
|
|
* comparison `bnez s0` then `li v0,1` / `bne s0,v0` shows the mode is compared against 0 and
|
|
|
|
|
* then 1, so the parameter is an index; its type is not otherwise constrained. The lookup is
|
|
|
|
|
* already registered in this project, corroborating the target.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int func_800A82D0(int a0);
|
|
|
|
|
void func_800A8310(int p);
|
|
|
|
|
|
|
|
|
|
void func_800AA01C(int a0, int mode)
|
|
|
|
|
{
|
|
|
|
|
char *p = (char *)func_800A82D0(a0);
|
|
|
|
|
|
|
|
|
|
if (p != 0) {
|
|
|
|
|
if (mode == 0)
|
|
|
|
|
p[0x14] = (p[0x14] | 0x08) & ~0x40;
|
|
|
|
|
else if (mode == 1)
|
|
|
|
|
p[0x14] = (p[0x14] | 0x40) & ~0x08;
|
|
|
|
|
else
|
|
|
|
|
p[0x14] = p[0x14] & ~0x48;
|
|
|
|
|
func_800A8310((int)p);
|
|
|
|
|
}
|
|
|
|
|
}
|