re: the game blends in the ENCODED space -- no gamma render target, anywhere
The Port asks whether the blend is evaluated on sRGB-encoded values or linearised and re-encoded. It is answered by one register field I already log, and the answer is the same on every draw of two full captures. RB_COLOR_INFO.color_format is k_8_8_8_8 (0) on 2402/2402 splash draws and 33779/33791 draws of the boot-through-title capture; the other 12 are k_32_FLOAT and k_16_16_FLOAT and are not colour passes. k_8_8_8_8_GAMMA (1) appears ZERO times, and color_exp_bias is 0 everywhere so nothing stands in for a gamma either. The mechanism is Canary's own source, not our inference: k_8_8_8_8_GAMMA is the ONLY colour format around which a piecewise-linear gamma<->linear conversion is applied (spirv_shader_translator.h:510 PWLGammaToLinear / LinearToPWLGamma, render_target_cache.h:720, dxbc_shader_translator_om.cc). With k_8_8_8_8 there is none, so the blender operates on the stored values as they are. So a renderer that linearises before blending and re-encodes after is doing a different operation -- and the difference is gamma-shaped and exactly zero on unblended pixels, which is the divergence signature the port reports. Also renamed my units-per-second pre-registration off the 'h4' prefix: the Port's BLOCKED.md numbers this blend-space question H4 and two different H4s in one corpus is how a citation goes wrong.
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
# The pad read path — what the game asks the console for
|
||||
|
||||
**Status: ✅ decoded from the image** for the *driver* half; ❔ **not yet decoded**
|
||||
for which button bits the menus test. Instrument: ⟨image⟩ — the executable's own
|
||||
**Status: ✅ decoded from the image** for the driver, for the field set, and for
|
||||
the complete button set `C_PAD_DECODER` tests; ❔ **not decoded** for which of
|
||||
those each individual screen acts on, or for the decoder's own output bit
|
||||
numbering. Instrument: ⟨image⟩ — the executable's own
|
||||
bytes, with the database used only as an index and every load verified against
|
||||
the file. 2026-09-01.
|
||||
|
||||
@@ -87,10 +89,88 @@ title. A menu that responds to a **press** is likely reading the queue; anything
|
||||
that responds to a **hold** must be reading the polled state. Which of the two
|
||||
each menu action uses is not decoded.
|
||||
|
||||
## ❔ Not decoded — which bits each screen tests
|
||||
## ✅ Which buttons the decoder actually tests — the complete set
|
||||
|
||||
The consumer side is open. Two named footholds exist for it, from the image's own
|
||||
strings:
|
||||
**`C_PAD_DECODER` is the game's own name for the layer between `wButtons` and
|
||||
the menus**, from the executable's own Shift-JIS trace strings. Its constructor is
|
||||
`sub_8220B610` (684-byte object, `memset` then a block of configuration
|
||||
constants) and its update is **`sub_8220B8C0`**, 1 400 instructions immediately
|
||||
after it. The construction trace `addi r3,r11,21840 → 0x820A5550` is
|
||||
`C_PAD_DECODER 初期化`, verified against the image.
|
||||
|
||||
**It reads a 32-bit word at `+12` of the `C_PAD_RINGBUF` (`this+76`) and masks
|
||||
its low 16 bits directly. There is no shift and no remap on the way in** — the
|
||||
bit positions are XINPUT's own:
|
||||
|
||||
```
|
||||
0x8220C550 lwz r10, 76(r31) ; this+76 = the C_PAD_RINGBUF
|
||||
0x8220C554 lwz r10, 12(r10) ; +12 = the button word
|
||||
0x8220C558 rlwinm r10, r10, 0,16,19 ; mask 0xF000 = A|B|X|Y -> "any face button"
|
||||
```
|
||||
|
||||
Every mask the update applies, from
|
||||
[`data/input-decoder-masks.txt`](data/input-decoder-masks.txt):
|
||||
|
||||
| bit | button | tested individually? |
|
||||
|---|---|---|
|
||||
| `0x0001` | D-pad **Up** | ✅ ×21 |
|
||||
| `0x0002` | D-pad **Down** | ✅ ×2 |
|
||||
| `0x0004` | D-pad **Left** | ✅ ×3 |
|
||||
| `0x0008` | D-pad **Right** | ✅ ×1 |
|
||||
| `0x0010` | **START** | ✅ ×5 |
|
||||
| `0x0020` | **BACK** | ✅ ×4 |
|
||||
| `0x0040` | **Left thumb click** | ✅ ×2 |
|
||||
| `0x0080` | **Right thumb click** | ✅ ×2 |
|
||||
| `0x0100` | **Left shoulder** | 🔴 **never** |
|
||||
| `0x0200` | **Right shoulder** | 🔴 **never** |
|
||||
| `0x0400` | (unnamed) | ✅ ×1 |
|
||||
| `0x0800` | (unnamed) | 🔴 never |
|
||||
| `0x1000` | **Ⓐ** | ✅ ×2 |
|
||||
| `0x2000` | **Ⓑ** | ✅ ×1 |
|
||||
| `0x4000` | **Ⓧ** | ✅ ×1 |
|
||||
| `0x8000` | **Ⓨ** | ✅ ×1 |
|
||||
|
||||
and the **group** masks, which are what identify these as buttons rather than as
|
||||
coincidental bit constants — no other quantity in a program produces exactly
|
||||
these groupings:
|
||||
|
||||
| mask | meaning | ×|
|
||||
|---|---|---|
|
||||
| `0x000F` | the whole D-pad | 1 |
|
||||
| `0x0003` | Up \| Down | 1 |
|
||||
| `0x0030` | START \| BACK | 1 |
|
||||
| `0x0060` | BACK \| left thumb | 1 |
|
||||
| `0x00FF` | D-pad + START + BACK + both thumbs | 5 |
|
||||
| `0xE000` | Ⓑ \| Ⓧ \| Ⓨ | **18** |
|
||||
| `0xF000` | Ⓐ \| Ⓑ \| Ⓧ \| Ⓨ — *"any face button"* | 1 |
|
||||
|
||||
**Verified 7/7 against `/image/sylpheed.pe`**, database used only as an index.
|
||||
|
||||
### 🔴 The one negative, and it is the useful part
|
||||
|
||||
**The shoulder buttons are the only pad inputs the decoder never tests.**
|
||||
`0x0100` and `0x0200` appear in this function **only** as `ori` — the decoder
|
||||
*setting* bits in its own output word (`0x8220C334`, `0x8220C48C`), never
|
||||
`andi.`/`rlwinm` reading them. Same for `0x0800`.
|
||||
|
||||
So on the evidence of this layer: **LB and RB are not menu inputs.** A binding
|
||||
table that maps them to anything is mapping them to nothing.
|
||||
|
||||
⚠️ **Reach.** This is `C_PAD_DECODER`, one layer, and it is the layer the *menus*
|
||||
sit above — but gameplay code may read `wButtons` by another route, and this says
|
||||
nothing about that. It is also not per-screen: it is the set the decoder
|
||||
distinguishes, and a given screen may act on a subset.
|
||||
|
||||
📌 **And the decoder emits its own bit assignment.** The `ori 0x100 / 0x200 /
|
||||
0x400 / 0x800` sites build an output word whose bit positions are **not**
|
||||
XINPUT's. Anything downstream reads *that* word, so a table found later in menu
|
||||
code will be in the decoder's numbering and not the console's. Mapping the two is
|
||||
not done and is the next read.
|
||||
|
||||
## ❔ Still not decoded — which bits each SCREEN acts on
|
||||
|
||||
The decoder's set is above. What remains is the layer past it. The footholds, from
|
||||
the image's own strings:
|
||||
|
||||
```
|
||||
0x820A5550 'C_PAD_DECODER 初期化' referenced from sub_8220B610
|
||||
|
||||
Reference in New Issue
Block a user