|
|
|
@@ -0,0 +1,60 @@
|
|
|
|
|
/* func_800263A8 — 0x800263A8..0x800263E8 (64 bytes). Inherited from worker C's
|
|
|
|
|
* parked list; this is C's recorded untried hypothesis applied.
|
|
|
|
|
*
|
|
|
|
|
* Original words:
|
|
|
|
|
* 8C840000 lw a0,0(a0) node = *head
|
|
|
|
|
* 1080000B beqz a0,0x800263E0
|
|
|
|
|
* 00001021 _move v0,zero (delay slot) result = 0
|
|
|
|
|
* 10850009 beq a0,a1,0x800263E0 <- loop top; node == match
|
|
|
|
|
* 00801021 _move v0,a0 (delay slot) result = node
|
|
|
|
|
* 8C820000 lw v0,0(a0) *node (for the second test)
|
|
|
|
|
* 10460005 beq v0,a2,0x800263E0 *node == key
|
|
|
|
|
* 00801021 _move v0,a0 (delay slot) result = node
|
|
|
|
|
* 8C840008 lw a0,8(a0) node = node->8
|
|
|
|
|
* 1480FFF7 bnez a0,loop
|
|
|
|
|
* 00001021 _move v0,zero (delay slot) result = 0
|
|
|
|
|
* 03E00008 jr ra <- 0x800263E0
|
|
|
|
|
* 00000000 nop
|
|
|
|
|
*
|
|
|
|
|
* A three-way list search that returns the node it stopped on, or zero.
|
|
|
|
|
*
|
|
|
|
|
* **THE RESULT IS ASSIGNED BEFORE THE TESTS, AND THAT IS THE WHOLE SPELLING.**
|
|
|
|
|
* `move v0,a0` fills the delay slot of *both* forward branches and `move v0,zero`
|
|
|
|
|
* fills the loop's back edge — three of the four delay slots carry the return value
|
|
|
|
|
* rather than a `nop`. That only happens if the value is already live in `v0` on each
|
|
|
|
|
* of those paths, so the source must assign a `result` variable **before** each test
|
|
|
|
|
* and re-clear it after advancing the node, rather than `return`ing from inside the
|
|
|
|
|
* loop. C's parked attempt used the compound form
|
|
|
|
|
* `while (node != 0 && node != match && *node != key)` and cc1 produced `nop` in all
|
|
|
|
|
* three slots; splitting the compound condition into **two separate `if`s** (which is
|
|
|
|
|
* what the original's two distinct branches show) and hoisting the assignment is the
|
|
|
|
|
* hypothesis being tested here.
|
|
|
|
|
*
|
|
|
|
|
* The second test's operand is loaded into `v0` itself, overwriting the result
|
|
|
|
|
* assignment from the first branch — safe precisely because that assignment has
|
|
|
|
|
* already done its work if the first branch was taken, so the value only has to
|
|
|
|
|
* survive into the delay slot. That is the mechanical reason the shape works, and it
|
|
|
|
|
* is why `result = (int)node` must sit immediately before the first test rather than
|
|
|
|
|
* after both.
|
|
|
|
|
*
|
|
|
|
|
* LIMITS: the displacements 0, 8 and the two comparisons are read from the bytes.
|
|
|
|
|
* Whether the two keys are pointers, handles or small integers cannot be told — they
|
|
|
|
|
* are compared against `a0` (a node pointer) and against `*node` (a word), which is
|
|
|
|
|
* why the first is typed `int *` and the second `int` here. The list is assumed to be
|
|
|
|
|
* null-terminated at offset 8 with no cycle check, which is the original's behaviour
|
|
|
|
|
* and is reproduced rather than hardened.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int func_800263A8(int *head, int *match, int key)
|
|
|
|
|
{
|
|
|
|
|
int *node = *(int **)head;
|
|
|
|
|
|
|
|
|
|
while (node != 0) {
|
|
|
|
|
if (node == match)
|
|
|
|
|
return (int)node;
|
|
|
|
|
if (*node == key)
|
|
|
|
|
return (int)node;
|
|
|
|
|
node = *(int **)((char *)node + 8);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|