1/*****************************************************************************\
2 Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3 This file is licensed under the Snes9x License.
4 For further information, consult the LICENSE file in the root directory.
5\*****************************************************************************/
6
7#include "snes9x.h"
8#include "memmap.h"
9
10static uint8 region_map[6][6] =
11{
12 { 0, 0x01, 0x03, 0x07, 0x0f, 0x1f },
13 { 0, 0, 0x02, 0x06, 0x0e, 0x1e },
14 { 0, 0, 0, 0x04, 0x0c, 0x1c },
15 { 0, 0, 0, 0, 0x08, 0x18 },
16 { 0, 0, 0, 0, 0, 0x10 }
17};
18
19static inline uint8 CalcWindowMask (int, uint8, uint8);
20static inline void StoreWindowRegions (uint8, struct ClipData *, int, int16 *, uint8 *, bool8, bool8 s = FALSE);
21
22
23static inline uint8 CalcWindowMask (int i, uint8 W1, uint8 W2)
24{
25 if (!PPU.ClipWindow1Enable[i])
26 {
27 if (!PPU.ClipWindow2Enable[i])
28 return (0);
29 else
30 {
31 if (!PPU.ClipWindow2Inside[i])
32 return (~W2);
33 return (W2);
34 }
35 }
36 else
37 {
38 if (!PPU.ClipWindow2Enable[i])
39 {
40 if (!PPU.ClipWindow1Inside[i])
41 return (~W1);
42 return (W1);
43 }
44 else
45 {
46 if (!PPU.ClipWindow1Inside[i])
47 W1 = ~W1;
48 if (!PPU.ClipWindow2Inside[i])
49 W2 = ~W2;
50
51 switch (PPU.ClipWindowOverlapLogic[i])
52 {
53 case 0: // OR
54 return (W1 | W2);
55
56 case 1: // AND
57 return (W1 & W2);
58
59 case 2: // XOR
60 return (W1 ^ W2);
61
62 case 3: // XNOR
63 return (~(W1 ^ W2));
64 }
65 }
66 }
67
68 // Never get here
69 return (0);
70}
71
72static inline void StoreWindowRegions (uint8 Mask, struct ClipData *Clip, int n_regions, int16 *windows, uint8 *drawing_modes, bool8 sub, bool8 StoreMode0)
73{
74 int ct = 0;
75
76 for (int j = 0; j < n_regions; j++)
77 {
78 int DrawMode = drawing_modes[j];
79 if (sub)
80 DrawMode |= 1;
81 if (Mask & (1 << j))
82 DrawMode = 0;
83
84 if (!StoreMode0 && !DrawMode)
85 continue;
86
87 if (ct > 0 && Clip->Right[ct - 1] == windows[j] && Clip->DrawMode[ct - 1] == DrawMode)
88 Clip->Right[ct - 1] = windows[j + 1]; // This region borders with and has the same drawing mode as the previous region: merge them.
89 else
90 {
91 // Add a new region to the BG
92 Clip->Left[ct] = windows[j];
93 Clip->Right[ct] = windows[j + 1];
94 Clip->DrawMode[ct] = DrawMode;
95 ct++;
96 }
97 }
98
99 Clip->Count = ct;
100}
101
102void S9xComputeClipWindows (void)
103{
104 int16 windows[6] = { 0, 256, 256, 256, 256, 256 };
105 uint8 drawing_modes[5] = { 0, 0, 0, 0, 0 };
106 int n_regions = 1;
107 int i, j;
108
109 // Calculate window regions. We have at most 5 regions, because we have 6 control points
110 // (screen edges, window 1 left & right, and window 2 left & right).
111
112 if (PPU.Window1Left <= PPU.Window1Right)
113 {
114 if (PPU.Window1Left > 0)
115 {
116 windows[2] = 256;
117 windows[1] = PPU.Window1Left;
118 n_regions = 2;
119 }
120
121 if (PPU.Window1Right < 255)
122 {
123 windows[n_regions + 1] = 256;
124 windows[n_regions] = PPU.Window1Right + 1;
125 n_regions++;
126 }
127 }
128
129 if (PPU.Window2Left <= PPU.Window2Right)
130 {
131 for (i = 0; i <= n_regions; i++)
132 {
133 if (PPU.Window2Left == windows[i])
134 break;
135
136 if (PPU.Window2Left < windows[i])
137 {
138 for (j = n_regions; j >= i; j--)
139 windows[j + 1] = windows[j];
140
141 windows[i] = PPU.Window2Left;
142 n_regions++;
143 break;
144 }
145 }
146
147 for (; i <= n_regions; i++)
148 {
149 if (PPU.Window2Right + 1 == windows[i])
150 break;
151
152 if (PPU.Window2Right + 1 < windows[i])
153 {
154 for (j = n_regions; j >= i; j--)
155 windows[j + 1] = windows[j];
156
157 windows[i] = PPU.Window2Right + 1;
158 n_regions++;
159 break;
160 }
161 }
162 }
163
164 // Get a bitmap of which regions correspond to each window.
165
166 uint8 W1, W2;
167
168 if (PPU.Window1Left <= PPU.Window1Right)
169 {
170 for (i = 0; windows[i] != PPU.Window1Left; i++) ;
171 for (j = i; windows[j] != PPU.Window1Right + 1; j++) ;
172 W1 = region_map[i][j];
173 }
174 else
175 W1 = 0;
176
177 if (PPU.Window2Left <= PPU.Window2Right)
178 {
179 for (i = 0; windows[i] != PPU.Window2Left; i++) ;
180 for (j = i; windows[j] != PPU.Window2Right + 1; j++) ;
181 W2 = region_map[i][j];
182 }
183 else
184 W2 = 0;
185
186 // Color Window affects the drawing mode for each region.
187 // Modes are: 3=Draw as normal, 2=clip color (math only), 1=no math (draw only), 0=nothing.
188
189 uint8 CW_color = 0, CW_math = 0;
190 uint8 CW = CalcWindowMask(5, W1, W2);
191
192 switch (Memory.FillRAM[0x2130] & 0xc0)
193 {
194 case 0x00: CW_color = 0; break;
195 case 0x40: CW_color = ~CW; break;
196 case 0x80: CW_color = CW; break;
197 case 0xc0: CW_color = 0xff; break;
198 }
199
200 switch (Memory.FillRAM[0x2130] & 0x30)
201 {
202 case 0x00: CW_math = 0; break;
203 case 0x10: CW_math = ~CW; break;
204 case 0x20: CW_math = CW; break;
205 case 0x30: CW_math = 0xff; break;
206 }
207
208 for (i = 0; i < n_regions; i++)
209 {
210 if (!(CW_color & (1 << i)))
211 drawing_modes[i] |= 1;
212 if (!(CW_math & (1 << i)))
213 drawing_modes[i] |= 2;
214 }
215
216 // Store backdrop clip window (draw everywhere color window allows)
217
218 StoreWindowRegions(0, &IPPU.Clip[0][5], n_regions, windows, drawing_modes, FALSE, TRUE);
219 StoreWindowRegions(0, &IPPU.Clip[1][5], n_regions, windows, drawing_modes, TRUE, TRUE);
220
221 // Store per-BG and OBJ clip windows
222
223 for (j = 0; j < 5; j++)
224 {
225 uint8 W = Settings.DisableGraphicWindows ? 0 : CalcWindowMask(j, W1, W2);
226 for (int sub = 0; sub < 2; sub++)
227 {
228 if (Memory.FillRAM[sub + 0x212e] & (1 << j))
229 StoreWindowRegions(W, &IPPU.Clip[sub][j], n_regions, windows, drawing_modes, sub);
230 else
231 StoreWindowRegions(0, &IPPU.Clip[sub][j], n_regions, windows, drawing_modes, sub);
232 }
233 }
234}
235