|
|
|
@@ -0,0 +1,71 @@
|
|
|
|
|
/*
|
|
|
|
|
* func_80027D88 — 148 bytes at 0x80027D88..0x80027E1C
|
|
|
|
|
*
|
|
|
|
|
* Take the component-wise absolute difference of two 3-int vectors, keep the LARGEST, and store
|
|
|
|
|
* `largest + ((middle + smallest) >> 2)` through the third pointer. Returns 0.
|
|
|
|
|
* **First spelling, default toolchain.**
|
|
|
|
|
*
|
|
|
|
|
* lw a3,0(a0) / lw v0,0(a1) x = a0[0] - a1[0] ...
|
|
|
|
|
* subu v1,a3,v0
|
|
|
|
|
* bgez v1,SKIP / nop
|
|
|
|
|
* subu v1,v0,a3 ... and if (x < 0) x = a1[0] - a0[0];
|
|
|
|
|
* (the same pair for element 1 and for element 2, into t0 and a3)
|
|
|
|
|
* slt v0,v1,t0 / beqz SKIP / move v0,v1 / move v1,t0 / move t0,v0
|
|
|
|
|
* if (x < y) { t = x; x = y; y = t; }
|
|
|
|
|
* slt v0,v1,a3 / beqz SKIP / nop / move v0,v1 / move v1,a3 / move a3,v0
|
|
|
|
|
* if (x < z) { t = x; x = z; z = t; }
|
|
|
|
|
* addu v0,t0,a3 / sra v0,v0,2 / addu v0,v1,v0
|
|
|
|
|
* *a2 = x + ((y + z) >> 2);
|
|
|
|
|
* sw v0,0(a2) / jr ra / move v0,zero
|
|
|
|
|
*
|
|
|
|
|
* TWO COOKBOOK LEVERS DECIDE THIS ROW, AND BOTH ARE THE "OBVIOUS" SPELLING'S OPPOSITE:
|
|
|
|
|
*
|
|
|
|
|
* 1. **THE ABSOLUTE VALUE IS A SWAPPED SUBTRACTION, NOT A NEGATION.** The emitted second
|
|
|
|
|
* operation is `subu v1,v0,a3` -- a subtraction with the operands exchanged -- not `negu`.
|
|
|
|
|
* Writing `if (x < 0) x = -x;` emits `negu` and does not match; writing
|
|
|
|
|
* `if (x < 0) x = a1[0] - a0[0];` does, because the two operands are still in registers from
|
|
|
|
|
* the first subtraction and the recomputation is CSE'd away. This is cookbook 101's lever, and
|
|
|
|
|
* it is worth stating again because a `negu` here is the single most natural thing to write.
|
|
|
|
|
* 2. **THE `>> 2` MUST BE A SHIFT, NOT A DIVISION.** The emitted rounding is the BARE
|
|
|
|
|
* `sra v0,v0,2` with no sign bias. `(y + z) / 4` makes cc1 emit the `addiu ...,3` bias
|
|
|
|
|
* (cookbook 97: a power-of-two division is not a shift), so the source has `>> 2`. The values
|
|
|
|
|
* happen to be non-negative, but cc1 cannot know that after a conditional, so the absence of
|
|
|
|
|
* the bias is the evidence.
|
|
|
|
|
*
|
|
|
|
|
* The partition keeps the largest in the FIRST slot: the two compares are `x < y` and `x < z`,
|
|
|
|
|
* performed in that order, and the swaps are the three-move form (`move v0,v1 / move v1,t0 /
|
|
|
|
|
* move t0,v0`) exactly as emitted.
|
|
|
|
|
*
|
|
|
|
|
* LIMITS: this is a leaf with no frame and no call, so the instruction SET is the only structure
|
|
|
|
|
* available -- the three pointers are typed `int *` because every access is a full word, the two
|
|
|
|
|
* source vectors are the first and second parameters and the destination the third, and the
|
|
|
|
|
* `>> 2` scaling is read off the shift. The purpose (a distance or error metric, most plausibly) is
|
|
|
|
|
* a hypothesis from the shape, not evidence. The return value is always 0.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int func_80027D88(int *a0, int *a1, int *a2)
|
|
|
|
|
{
|
|
|
|
|
int x, y, z, t;
|
|
|
|
|
|
|
|
|
|
x = a0[0] - a1[0];
|
|
|
|
|
if (x < 0) x = a1[0] - a0[0];
|
|
|
|
|
y = a0[1] - a1[1];
|
|
|
|
|
if (y < 0) y = a1[1] - a0[1];
|
|
|
|
|
z = a0[2] - a1[2];
|
|
|
|
|
if (z < 0) z = a1[2] - a0[2];
|
|
|
|
|
|
|
|
|
|
if (x < y) {
|
|
|
|
|
t = x;
|
|
|
|
|
x = y;
|
|
|
|
|
y = t;
|
|
|
|
|
}
|
|
|
|
|
if (x < z) {
|
|
|
|
|
t = x;
|
|
|
|
|
x = z;
|
|
|
|
|
z = t;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*a2 = x + ((y + z) >> 2);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|