phase11: merge 48 + gtemac lwc2/swc2 + cookbook 159-161 — 585 bodies / 594 regions
Worker D's 0x800F3E18 (88 B) -- THE FIRST GTE/COP2 ROW MATCHED IN THIS PROJECT. 159: lwc2/swc2 move a word straight between MEMORY and COP2, unlike mtc2/mfc2 which move between a GPR and COP2. The row uses lwc2 $9/$10/$11 and swc2 $25/$26/$27, so a row can use the IR/MAC registers WITHOUT the IR/MAC macros. Added gte_lwc2IR1/2/3 and gte_swc2MAC1/2/3. 160: register variables PIN the COP2 operand registers -- worker D's entire residual was that cc1 chose its own cfc2/mfc2 destinations. The GTE analogue of the named-locals family: an inline-asm row's residual is usually the operand REGISTERS, not the sequence. 161 IS A CORRECTION TO THE COORDINATOR'S OWN ADVICE. I broadcast the command-field values with their occurrence counts as if they were a lookup table. They are a DISTRIBUTION, not a per-row answer: worker D wrote 0x178000c (counted 51x) into the row and it came out ONE BYTE wrong; the correct field is 0x170000c. The low bits carry the shift/matrix/vector selectors, so two commands differing only there are different instructions. Read the field off the ORIGINAL WORD.
This commit is contained in:
@@ -480,6 +480,7 @@
|
||||
0x800F3140 0x800F3160 src/func_800F3140.c
|
||||
0x800F3160 0x800F316C src/func_800F3160.c maspsx=off
|
||||
0x800F3A00 0x800F3A24 src/func_800F3A00.c
|
||||
0x800F3E18 0x800F3E70 src/func_800F3E18.c
|
||||
0x800F3E70 0x800F3E88 src/func_800F3E70.c
|
||||
0x800F4098 0x800F4100 src/func_800F4098.c maspsx=epilogue
|
||||
0x800F42AC 0x800F430C src/func_800F42AC.c maspsx=epilogue
|
||||
|
||||
|
@@ -2597,3 +2597,38 @@ is sign-extended **in the jump delay slot**.
|
||||
> **When one function reads the same field BOTH ways, the two views are deliberate.** Do not
|
||||
> "simplify" them to one type — the mixed `lh`/`lhu` pair over one pointer is the evidence that the
|
||||
> source declared two views.
|
||||
|
||||
### 159. `lwc2`/`swc2` are NOT `mtc2`/`mfc2` — a row can use IR/MAC without the IR/MAC macros (worker D)
|
||||
|
||||
`lwc2`/`swc2` move a word **straight between memory and a COP2 register**; `mtc2`/`mfc2` move between
|
||||
a **GPR and COP2**. Worker D's `0x800F3E18` — **the first GTE row matched in this project** — uses
|
||||
`lwc2 $9/$10/$11` and `swc2 $25/$26/$27`, so **the IR/MAC registers can be used without the IR/MAC
|
||||
macros.** `gte_lwc2IR1/2/3` and `gte_swc2MAC1/2/3` are now in `include/gtemac.h`.
|
||||
|
||||
### 160. Register variables PIN the COP2 operand registers — the GTE analogue of named locals (worker D)
|
||||
|
||||
Worker D's **entire residual** on `0x800F3E18` was that cc1 chose its own `cfc2`/`mfc2`
|
||||
destinations. `register int r13 __asm__("$13");` with `"=r"(r13)` forces `cfc2 $13,...`, and the same
|
||||
for `$8/$9/$10` on the matrix loads. Without them the candidate loads `a0[0..2]` in a different order
|
||||
into `v0/v1/a0` and comes out one instruction long.
|
||||
|
||||
> **An inline-asm row's residual is usually the operand REGISTERS, not the instruction sequence, and
|
||||
> register variables are the lever.** This is the GTE analogue of the named-locals family.
|
||||
|
||||
### 161. *** READ THE COMMAND FIELD OFF THE ORIGINAL WORD, NOT OFF A TABLE ***
|
||||
|
||||
**A correction to the coordinator's own advice, from worker D.** I broadcast the command-field values
|
||||
`0x486012`/`0x49E012`/`0x41E012` with their occurrence counts as if they were a lookup table. **They
|
||||
are a distribution, not a per-row answer.**
|
||||
|
||||
Worker D wrote `0x178000c` — the value I counted **51×** across the binary — into `0x800F3E18`, and
|
||||
**the row came out exactly ONE BYTE wrong.** The correct field for that row is **`0x170000c`**.
|
||||
|
||||
**The low bits carry the shift / matrix / vector selectors, so two commands that differ only there
|
||||
are DIFFERENT instructions** — and a one-byte residual is invisible without a raw-word diff.
|
||||
|
||||
> **Read the command field off the ORIGINAL WORD for the row you are working.** A count tells you a
|
||||
> field is *common*, not that it is *right*.
|
||||
|
||||
The save/restore of `$0/$2/$4` around the command on that row is also real — **the caller's matrix
|
||||
must survive**, so the `cfc2`/`ctc2` pair is not decorative.
|
||||
|
||||
+30
-1
@@ -139,6 +139,28 @@
|
||||
#define gte_ldMAC2(v) __asm__ volatile ("mtc2 %0,$26" : : "r"(v))
|
||||
#define gte_ldMAC3(v) __asm__ volatile ("mtc2 %0,$27" : : "r"(v))
|
||||
|
||||
/* --- memory <-> COP2 ($9-$11, $25-$27) ----------------------------------- */
|
||||
|
||||
/* `lwc2`/`swc2` move a word straight between MEMORY and a COP2 register; they are NOT
|
||||
* `mtc2`/`mfc2`, which move between a GPR and COP2. Worker D's 0x800F3E18 (the first
|
||||
* GTE row matched in this project) uses `lwc2 $9/$10/$11` and `swc2 $25/$26/$27`, so a
|
||||
* row can use the IR/MAC registers WITHOUT using the IR/MAC macros above. These take an
|
||||
* address in a register operand.
|
||||
*
|
||||
* Proved by 0x800F3E18. */
|
||||
#define gte_lwc2IR1(p) __asm__ volatile ("lwc2 $9, 0(%0)" : : "r"(p))
|
||||
#define gte_lwc2IR2(p) __asm__ volatile ("lwc2 $10, 0(%0)" : : "r"(p))
|
||||
#define gte_lwc2IR3(p) __asm__ volatile ("lwc2 $11, 0(%0)" : : "r"(p))
|
||||
#define gte_swc2MAC1(p) __asm__ volatile ("swc2 $25, 0(%0)" : : "r"(p))
|
||||
#define gte_swc2MAC2(p) __asm__ volatile ("swc2 $26, 0(%0)" : : "r"(p))
|
||||
#define gte_swc2MAC3(p) __asm__ volatile ("swc2 $27, 0(%0)" : : "r"(p))
|
||||
|
||||
/* Register variables PIN the COP2 operand registers. Worker D's whole residual on
|
||||
* 0x800F3E18 was that cc1 chose its own `cfc2`/`mfc2` destinations; binding the
|
||||
* destination with `register int r13 __asm__("$13");` and passing `"=r"(r13)` forces it.
|
||||
* This is the GTE analogue of the named-locals family: an inline-asm row's residual is
|
||||
* usually the operand REGISTERS, not the instruction sequence. */
|
||||
|
||||
/* --- commands ------------------------------------------------------------ */
|
||||
|
||||
/* The 25-bit COP2 command field is written as `cop2 0x...`, which GNU as
|
||||
@@ -159,7 +181,14 @@
|
||||
* 0x41E012 x4 -- worker A's 0x800F3C60
|
||||
* The low six bits are the GTE command number (0x12 = MVMVA in all three); the
|
||||
* upper bits select the matrix / vector / translation / shift fields. Do NOT
|
||||
* name them semantically without evidence -- name them by field value. */
|
||||
* name them semantically without evidence -- name them by field value.
|
||||
*
|
||||
* *** THOSE COUNTS ARE A DISTRIBUTION, NOT A PER-ROW LOOKUP. *** Worker D wrote
|
||||
* `0x178000c` (the value counted 51x) into 0x800F3E18 and the row came out ONE BYTE
|
||||
* wrong: the correct field for that row is `0x170000c`. The low bits carry the
|
||||
* shift / matrix / vector selectors, so two commands that differ only there are
|
||||
* DIFFERENT instructions and a one-byte residual is invisible without a raw-word
|
||||
* diff. READ THE COMMAND FIELD OFF THE ORIGINAL WORD, never off this list. */
|
||||
#define gte_cmd(field) __asm__ volatile ("cop2 " #field)
|
||||
|
||||
/* --- BIOS call stubs ----------------------------------------------------- */
|
||||
|
||||
Reference in New Issue
Block a user