Nugget
Loading...
Searching...
No Matches
raster-expected-phase12.h
Go to the documentation of this file.
1/*
2
3MIT License
4
5Copyright (c) 2026 PCSX-Redux authors
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to deal
9in the Software without restriction, including without limitation the rights
10to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in all
15copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23SOFTWARE.
24
25*/
26
27#pragma once
28
29// Phase-12 expected ABR blend values. Predictions below use the
30// documented psx-spx 8-bit-space formulas:
31// ABR=0: out8 = (B8 + F8) / 2
32// ABR=1: out8 = min(B8 + F8, 255)
33// ABR=2: out8 = max(B8 - F8, 0)
34// ABR=3: out8 = min(B8 + F8/4, 255)
35// out5 = out8 >> 3. B8 = B5 << 3 (re-expanded from 5-bit). F8 = F5
36// << 3 (the command-color is sent at 5-bit-boundary alignment by
37// cmdR5() so F8 = F5 * 8 exactly).
38//
39// All placeholders HW_TODO; first hardware run captures truth.
40
41#include "raster-helpers.h"
42
43// VRAM 5:5:5 helper for R-only outputs: vram555(R, 0, 0) = R.
44#define VR(r5) ((uint16_t)((r5) & 0x1fu))
45
46// ============================================================================
47// ABR=0 (B/2 + F/2): out5 = ((B5+F5) >> 1) approximately
48// At B5=16, F5=16: out8 = (128+128)/2 = 128 -> out5=16 = 0x10
49// At B5=16, F5=31: out8 = (128+248)/2 = 188 -> out5=23 = 0x17
50// ============================================================================
51
52#define ABR0_B00_F00 VR(0) /* (0+0)/2 = 0 -> R5=0 */
53#define ABR0_B00_F16 VR(8) /* (0+128)/2 = 64 -> R5=8 */
54#define ABR0_B00_F31 VR(15) /* (0+248)/2 = 124 -> R5=15 */
55#define ABR0_B16_F00 VR(8) /* (128+0)/2 = 64 -> R5=8 */
56#define ABR0_B16_F16 VR(16) /* (128+128)/2 = 128 -> R5=16 */
57#define ABR0_B16_F31 VR(23) /* (128+248)/2 = 188 -> R5=23 */
58#define ABR0_B31_F00 VR(15) /* (248+0)/2 = 124 -> R5=15 */
59#define ABR0_B31_F16 VR(23) /* (248+128)/2 = 188 -> R5=23 */
60#define ABR0_B31_F31 VR(31) /* (248+248)/2 = 248 -> R5=31 */
61
62// ============================================================================
63// ABR=1 (B + F, clamped): out8 = min(B8+F8, 255)
64// ============================================================================
65
66#define ABR1_B00_F00 VR(0)
67#define ABR1_B00_F16 VR(16) /* 0+128=128 -> R5=16 */
68#define ABR1_B00_F31 VR(31) /* 0+248=248 -> R5=31 */
69#define ABR1_B16_F00 VR(16)
70#define ABR1_B16_F16 VR(31) /* 128+128=256 -> clamp 255 -> R5=31 */
71#define ABR1_B16_F31 VR(31) /* 128+248=376 -> clamp -> R5=31 */
72#define ABR1_B31_F00 VR(31)
73#define ABR1_B31_F16 VR(31)
74#define ABR1_B31_F31 VR(31) /* sat */
75
76// ============================================================================
77// ABR=2 (B - F, clamped at 0): out8 = max(B8-F8, 0)
78// ============================================================================
79
80#define ABR2_B00_F00 VR(0)
81#define ABR2_B00_F16 VR(0) /* 0-128=-128 -> clamp 0 */
82#define ABR2_B00_F31 VR(0)
83#define ABR2_B16_F00 VR(16) /* 128-0=128 -> R5=16 */
84#define ABR2_B16_F16 VR(0) /* 128-128=0 -> R5=0 */
85#define ABR2_B16_F31 VR(0) /* 128-248=-120 -> 0 */
86#define ABR2_B31_F00 VR(31)
87#define ABR2_B31_F16 VR(15) /* 248-128=120 -> R5=15 */
88#define ABR2_B31_F31 VR(0) /* 248-248=0 */
89
90// ============================================================================
91// ABR=3 (B + F/4, clamped): out8 = min(B8 + (F8 >> 2), 255)
92// ============================================================================
93
94#define ABR3_B00_F00 VR(0)
95#define ABR3_B00_F16 VR(4) /* 0 + 128/4 = 32 -> R5=4 */
96#define ABR3_B00_F31 VR(7) /* 0 + 248/4 = 62 -> R5=7 */
97#define ABR3_B16_F00 VR(16)
98#define ABR3_B16_F16 VR(20) /* 128 + 32 = 160 -> R5=20 */
99#define ABR3_B16_F31 VR(23) /* 128 + 62 = 190 -> R5=23 */
100#define ABR3_B31_F00 VR(31)
101#define ABR3_B31_F16 VR(31) /* 248 + 32 = 280 -> clamp 255 -> R5=31 */
102#define ABR3_B31_F31 VR(31) /* sat */
103
104// ============================================================================
105// ABR_PRIM: same blend math across quad / rect / line at (B=16, F=16)
106// ============================================================================
107
108#define ABR0_PRIM_QUAD ABR0_B16_F16
109#define ABR1_PRIM_QUAD ABR1_B16_F16
110#define ABR2_PRIM_QUAD ABR2_B16_F16
111#define ABR3_PRIM_QUAD ABR3_B16_F16
112
113#define ABR0_PRIM_RECT ABR0_B16_F16
114#define ABR1_PRIM_RECT ABR1_B16_F16
115#define ABR2_PRIM_RECT ABR2_B16_F16
116#define ABR3_PRIM_RECT ABR3_B16_F16
117
118#define ABR0_PRIM_LINE ABR0_B16_F16
119#define ABR1_PRIM_LINE ABR1_B16_F16
120#define ABR2_PRIM_LINE ABR2_B16_F16
121#define ABR3_PRIM_LINE ABR3_B16_F16
122
123// ============================================================================
124// ABR_TEX_MASKED: textured tri sampling CLUT4[4] = vram555(4, 27, 0) | 0x8000.
125// F = (R=4, G=27, B=0) after modulation (neutral). B = (R=16, G=0, B=0).
126// R-channel blend math for each ABR:
127// ABR=0: (B_R + F_R)/2 = (16+4)/2 = 10 -> R5=1 (in 8-bit: (128+32)/2 = 80 -> R5=10)
128// Actually F8=4*8=32, B8=16*8=128. (128+32)/2 = 80 -> R5 = 80>>3 = 10.
129// ABR=1: 128+32 = 160 -> R5=20.
130// ABR=2: 128-32 = 96 -> R5=12.
131// ABR=3: 128 + 32/4 = 136 -> R5=17.
132// G-channel: B_G=0, F_G=27*8=216.
133// ABR=0: (0+216)/2 = 108 -> G5=13. Output bits: 13<<5 = 0x01A0.
134// Hmm, but B=16 in our pre-fill is R-only (G8=0, B8=0). So the G/B
135// channels start at 0. The texture brings G=27 and B=0 into the
136// mix. Predict each channel separately and compose. This gets
137// complex; HW_TODO placeholders + capture truth.
138// ============================================================================
139
140// HARDWARE FINDING (verified 2026-05-16): the texel's bit-15 mask is
141// preserved into VRAM even WITHOUT E6 set-mask enabled. Texture-side
142// mask-bit propagates through the semi-trans blend - hardware writes
143// the blended value with bit-15 = (mask bit of sampled texel). All
144// four textured semi-trans outputs below carry the 0x8000 mask bit.
145// The R/G/B channel math matches the documented blend formulas:
146// ABR=0: B/2 + F/2 (R=10, G=13, B=0)
147// ABR=1: B + F (R=20, G=27, B=0)
148// ABR=2: B - F (R=12, G=0, B=0)
149// ABR=3: B + F/4 (R=17, G=6, B=0)
150// (B=R5=16 / G5=0 / B5=0 background; F=R5=4 / G5=27 / B5=0 texel.)
151#define ABR0_TEX_MASKED 0x81aau /* (R=10, G=13, B=0) | mask */
152#define ABR1_TEX_MASKED 0x8374u /* (R=20, G=27, B=0) | mask */
153#define ABR2_TEX_MASKED 0x800cu /* (R=12, G=0, B=0) | mask */
154#define ABR3_TEX_MASKED 0x80d1u /* (R=17, G=6, B=0) | mask */
155
156// ============================================================================
157// ABR_SETMASK: blended value with bit-15 OR'd in.
158// (B16, F16, ABR=*) blend per above, then | 0x8000.
159// ============================================================================
160
161#define ABR0_SETMASK (VR(16) | 0x8000u) /* 0x10 | 0x8000 = 0x8010 */
162#define ABR1_SETMASK (VR(31) | 0x8000u) /* sat */
163#define ABR2_SETMASK (VR(0) | 0x8000u)
164#define ABR3_SETMASK (VR(20) | 0x8000u)
165
166// ============================================================================
167// ABR_CHECKMASK: pre-fill has bit-15 set, so writes are skipped. The
168// pre-fill value (B_R = 16, with bit-15 set) survives untouched.
169// ============================================================================
170
171#define ABR0_CHECKMASK (VR(16) | 0x8000u)
172#define ABR1_CHECKMASK (VR(16) | 0x8000u)
173#define ABR2_CHECKMASK (VR(16) | 0x8000u)
174#define ABR3_CHECKMASK (VR(16) | 0x8000u)