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 | |
10 | static 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 | |
19 | static inline uint8 CalcWindowMask (int, uint8, uint8); |
20 | static inline void StoreWindowRegions (uint8, struct ClipData *, int, int16 *, uint8 *, bool8, bool8 s = FALSE); |
21 | |
22 | |
23 | static 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 | |
72 | static 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 | |
102 | void 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 | |