2 * Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
4 * (c) Copyright 1996 - 2001 Gary Henderson (gary.henderson@ntlworld.com) and
5 * Jerremy Koot (jkoot@snes9x.com)
7 * Super FX C emulator code
8 * (c) Copyright 1997 - 1999 Ivar (ivar@snes9x.com) and
10 * Super FX assembler emulator code (c) Copyright 1998 zsKnight and _Demo_.
12 * DSP1 emulator code (c) Copyright 1998 Ivar, _Demo_ and Gary Henderson.
13 * C4 asm and some C emulation code (c) Copyright 2000 zsKnight and _Demo_.
14 * C4 C code (c) Copyright 2001 Gary Henderson (gary.henderson@ntlworld.com).
16 * DOS port code contains the works of other authors. See headers in
19 * Snes9x homepage: http://www.snes9x.com
21 * Permission to use, copy, modify and distribute Snes9x in both binary and
22 * source form, for non-commercial purposes, is hereby granted without fee,
23 * providing that this license information and copyright notice appear with
24 * all copies and any derived work.
26 * This software is provided 'as-is', without any express or implied
27 * warranty. In no event shall the authors be held liable for any damages
28 * arising from the use of this software.
30 * Snes9x is freeware for PERSONAL USE only. Commercial users should
31 * seek permission of the copyright holders first. Commercial use includes
32 * charging money for Snes9x or software derived from Snes9x.
34 * The copyright holders request that bug fixes and improvements to the code
35 * should be forwarded to them so everyone can benefit from the modifications
38 * Super NES and Super Nintendo Entertainment System are trademarks of
39 * Nintendo Co., Limited and its subsidiary companies.
55 #define MIN(A,B) ((A) < (B) ? (A) : (B))
56 #define MAX(A,B) ((A) > (B) ? (A) : (B))
57 #define BAND_EMPTY(B) (B.Left >= B.Right)
58 #define BANDS_INTERSECT(A,B) ((A.Left >= B.Left && A.Left < B.Right) || \
59 (A.Right > B.Left && A.Right <= B.Right))
60 #define OR_BANDS(R,A,B) {\
61 R.Left = MIN(A.Left, B.Left); \
62 R.Right = MAX(A.Right, B.Right);}
64 #define AND_BANDS(R,A,B) {\
65 R.Left = MAX(A.Left, B.Left); \
66 R.Right = MIN(A.Right, B.Right);}
69 static int IntCompare (const void *d1, const void *d2)
71 static int _cdecl IntCompare (const void *d1, const void *d2)
74 if (*(uint32 *) d1 > *(uint32 *) d2)
77 if (*(uint32 *) d1 < *(uint32 *) d2)
83 static int BandCompare (const void *d1, const void *d2)
85 static int _cdecl BandCompare (const void *d1, const void *d2)
88 if (((struct Band *) d1)->Left > ((struct Band *) d2)->Left)
91 if (((struct Band *) d1)->Left < ((struct Band *) d2)->Left)
96 void ComputeClipWindows ()
98 struct ClipData *pClip = &IPPU.Clip [0];
100 // Loop around the main screen then the sub-screen.
101 for (int c = 0; c < 2; c++, pClip++)
103 // Loop around the colour window then a clip window for each of the
104 // background layers.
105 for (int w = 5; w >= 0; w--)
109 if (w == 5) // The colour window...
111 if (c == 0) // ... on the main screen
113 if ((Memory.FillRAM [0x2130] & 0xc0) == 0xc0)
115 // The whole of the main screen is switched off,
116 // completely clip everything.
117 for (int i = 0; i < 6; i++)
119 IPPU.Clip [c].Count [i] = 1;
120 IPPU.Clip [c].Left [0][i] = 1;
121 IPPU.Clip [c].Right [0][i] = 0;
126 if ((Memory.FillRAM [0x2130] & 0xc0) == 0x00)
131 // .. colour window on the sub-screen.
132 if ((Memory.FillRAM [0x2130] & 0x30) == 0x30)
134 // The sub-screen is switched off, completely
136 for (int i = 0; i < 6; i++)
138 IPPU.Clip [1].Count [i] = 1;
139 IPPU.Clip [1].Left [0][i] = 1;
140 IPPU.Clip [1].Right [0][i] = 0;
145 if ((Memory.FillRAM [0x2130] & 0x30) == 0x00)
149 if (!Settings.DisableGraphicWindows)
151 if (w == 5 || pClip->Count [5] ||
152 (Memory.FillRAM [0x212c + c] &
153 Memory.FillRAM [0x212e + c] & (1 << w)))
157 uint32 Window1Enabled = 0;
158 uint32 Window2Enabled = 0;
159 bool8_32 invert = (w == 5 &&
160 ((c == 1 && (Memory.FillRAM [0x2130] & 0x30) == 0x10) ||
161 (c == 0 && (Memory.FillRAM [0x2130] & 0xc0) == 0x40)));
164 (Memory.FillRAM [0x212c + c] & Memory.FillRAM [0x212e + c] & (1 << w)))
166 if (PPU.ClipWindow1Enable [w])
168 if (!PPU.ClipWindow1Inside [w])
170 Win1[Window1Enabled].Left = PPU.Window1Left;
171 Win1[Window1Enabled++].Right = PPU.Window1Right + 1;
175 if (PPU.Window1Left <= PPU.Window1Right)
177 if (PPU.Window1Left > 0)
179 Win1[Window1Enabled].Left = 0;
180 Win1[Window1Enabled++].Right = PPU.Window1Left;
182 if (PPU.Window1Right < 255)
184 Win1[Window1Enabled].Left = PPU.Window1Right + 1;
185 Win1[Window1Enabled++].Right = 256;
187 if (Window1Enabled == 0)
189 Win1[Window1Enabled].Left = 1;
190 Win1[Window1Enabled++].Right = 0;
195 // 'outside' a window with no range -
196 // appears to be the whole screen.
197 Win1[Window1Enabled].Left = 0;
198 Win1[Window1Enabled++].Right = 256;
202 if (PPU.ClipWindow2Enable [w])
204 if (!PPU.ClipWindow2Inside [w])
206 Win2[Window2Enabled].Left = PPU.Window2Left;
207 Win2[Window2Enabled++].Right = PPU.Window2Right + 1;
211 if (PPU.Window2Left <= PPU.Window2Right)
213 if (PPU.Window2Left > 0)
215 Win2[Window2Enabled].Left = 0;
216 Win2[Window2Enabled++].Right = PPU.Window2Left;
218 if (PPU.Window2Right < 255)
220 Win2[Window2Enabled].Left = PPU.Window2Right + 1;
221 Win2[Window2Enabled++].Right = 256;
223 if (Window2Enabled == 0)
225 Win2[Window2Enabled].Left = 1;
226 Win2[Window2Enabled++].Right = 0;
231 Win2[Window2Enabled].Left = 0;
232 Win2[Window2Enabled++].Right = 256;
237 if (Window1Enabled && Window2Enabled)
241 // Each window will be in one of three states:
242 // 1. <no range> (Left > Right. One band)
243 // 2. | ---------------- | (Left >= 0, Right <= 255, Left <= Right. One band)
244 // 3. |------------ ----------| (Left1 == 0, Right1 < Left2; Left2 > Right1, Right2 == 255. Two bands)
246 struct Band Bands [6];
248 switch (PPU.ClipWindowOverlapLogic [w] ^ 1)
251 if (Window1Enabled == 1)
253 if (BAND_EMPTY(Win1[0]))
256 memmove (Bands, Win2,
257 sizeof(Win2[0]) * Window2Enabled);
261 if (Window2Enabled == 1)
263 if (BAND_EMPTY (Win2[0]))
264 Bands[B++] = Win1[0];
267 if (BANDS_INTERSECT (Win1[0], Win2[0]))
269 OR_BANDS(Bands[0],Win1[0], Win2[0])
274 Bands[B++] = Win1[0];
275 Bands[B++] = Win2[0];
281 if (BANDS_INTERSECT(Win1[0], Win2[0]))
283 OR_BANDS(Bands[0], Win1[0], Win2[0])
284 if (BANDS_INTERSECT(Win1[0], Win2[1]))
285 OR_BANDS(Bands[1], Win1[0], Win2[1])
289 if (BANDS_INTERSECT(Bands[0], Bands[1]))
290 OR_BANDS(Bands[0], Bands[0], Bands[1])
295 if (BANDS_INTERSECT(Win1[0], Win2[1]))
297 Bands[B++] = Win2[0];
298 OR_BANDS(Bands[B], Win1[0], Win2[1]);
312 if (Window2Enabled == 1)
314 if (BAND_EMPTY(Win2[0]))
316 // Window 2 defines an empty range - just
317 // use window 1 as the clipping (which
318 // could also be empty).
320 memmove (Bands, Win1,
321 sizeof(Win1[0]) * Window1Enabled);
325 // Window 1 has two bands and Window 2 has one.
326 // Neither is an empty region.
327 if (BANDS_INTERSECT(Win2[0], Win1[0]))
329 OR_BANDS(Bands[0], Win2[0], Win1[0])
330 if (BANDS_INTERSECT(Win2[0], Win1[1]))
331 OR_BANDS(Bands[1], Win2[0], Win1[1])
335 if (BANDS_INTERSECT(Bands[0], Bands[1]))
336 OR_BANDS(Bands[0], Bands[0], Bands[1])
341 if (BANDS_INTERSECT(Win2[0], Win1[1]))
343 Bands[B++] = Win1[0];
344 OR_BANDS(Bands[B], Win2[0], Win1[1]);
358 // Both windows have two bands
359 OR_BANDS(Bands[0], Win1[0], Win2[0]);
360 OR_BANDS(Bands[1], Win1[1], Win2[1]);
362 if (BANDS_INTERSECT(Bands[0], Bands[1]))
363 OR_BANDS(Bands[0], Bands[0], Bands[1])
370 if (Window1Enabled == 1)
372 // Window 1 has one band
373 if (BAND_EMPTY(Win1[0]))
374 Bands [B++] = Win1[0];
376 if (Window2Enabled == 1)
378 if (BAND_EMPTY (Win2[0]))
379 Bands [B++] = Win2[0];
382 AND_BANDS(Bands[0], Win1[0], Win2[0]);
388 AND_BANDS(Bands[0], Win1[0], Win2[0]);
389 AND_BANDS(Bands[1], Win1[0], Win2[1]);
394 if (Window2Enabled == 1)
396 if (BAND_EMPTY(Win2[0]))
397 Bands[B++] = Win2[0];
400 // Window 1 has two bands.
401 AND_BANDS(Bands[0], Win1[0], Win2[0]);
402 AND_BANDS(Bands[1], Win1[1], Win2[0]);
408 // Both windows have two bands.
409 AND_BANDS(Bands[0], Win1[0], Win2[0]);
410 AND_BANDS(Bands[1], Win1[1], Win2[1]);
412 if (BANDS_INTERSECT(Win1[0], Win2[1]))
414 AND_BANDS(Bands[2], Win1[0], Win2[1]);
418 if (BANDS_INTERSECT(Win1[1], Win2[0]))
420 AND_BANDS(Bands[2], Win1[1], Win2[0]);
430 if (Window1Enabled == 1 && BAND_EMPTY(Win1[0]))
433 memmove (Bands, Win2,
434 sizeof(Win2[0]) * Window2Enabled);
437 if (Window2Enabled == 1 && BAND_EMPTY(Win2[0]))
440 memmove (Bands, Win1,
441 sizeof(Win1[0]) * Window1Enabled);
450 // Build an array of points (window edges)
452 for (i = 0; i < Window1Enabled; i++)
454 points [p++] = Win1[i].Left;
455 points [p++] = Win1[i].Right;
457 for (i = 0; i < Window2Enabled; i++)
459 points [p++] = Win2[i].Left;
460 points [p++] = Win2[i].Right;
464 qsort ((void *) points, p, sizeof (points [0]),
466 for (i = 0; i < p; i += 2)
468 if (points [i] == points [i + 1])
470 Bands [B].Left = points [i];
472 points [i + 1] == points [i + 2])
476 Bands [B++].Right = points [i + 1];
485 int empty_band_count = 0;
487 // First remove all empty bands from the list.
488 for (b = 0; b < B; b++)
490 if (!BAND_EMPTY(Bands[b]))
505 // Easy case to deal with, so special case it.
507 if (Bands[0].Left > 0)
509 pClip->Left[j][w] = 0;
510 pClip->Right[j++][w] = Bands[0].Left + 1;
512 if (Bands[0].Right < 256)
514 pClip->Left[j][w] = Bands[0].Right;
515 pClip->Right[j++][w] = 256;
519 pClip->Left[j][w] = 1;
520 pClip->Right[j++][w] = 0;
525 // Now sort the bands into order
527 qsort ((void *) Bands, B,
528 sizeof (Bands [0]), BandCompare);
530 // Now invert the area the bands cover
532 for (b = 0; b < B; b++)
534 if (b == 0 && Bands[b].Left > 0)
536 pClip->Left[j][w] = 0;
537 pClip->Right[j++][w] = Bands[b].Left + 1;
540 if (b == B - 1 && Bands[b].Right < 256)
542 pClip->Left[j][w] = Bands[b].Right;
543 pClip->Right[j++][w] = 256;
547 pClip->Left[j][w] = Bands[b].Right;
548 pClip->Right[j++][w] = Bands[b + 1].Left + 1;
555 // Inverting a window that consisted of only
556 // empty bands is the whole width of the screen.
557 // Needed for Mario Kart's rear-view mirror display.
558 if (empty_band_count)
560 pClip->Left[j][w] = 0;
561 pClip->Right[j][w] = 256;
569 for (int j = 0; j < B; j++)
571 pClip->Left[j][w] = Bands[j].Left;
572 pClip->Right[j][w] = Bands[j].Right;
574 pClip->Count [w] = B;
579 // Only one window enabled so no need to perform
580 // complex overlap logic...
588 if (Window1Enabled == 1)
590 if (Win1[0].Left <= Win1[0].Right)
592 if (Win1[0].Left > 0)
594 pClip->Left[j][w] = 0;
595 pClip->Right[j++][w] = Win1[0].Left;
597 if (Win1[0].Right < 256)
599 pClip->Left[j][w] = Win1[0].Right;
600 pClip->Right[j++][w] = 256;
604 pClip->Left[j][w] = 1;
605 pClip->Right[j++][w] = 0;
610 pClip->Left[j][w] = 0;
611 pClip->Right[j++][w] = 256;
616 pClip->Left [j][w] = Win1[0].Right;
617 pClip->Right[j++][w] = Win1[1].Left;
619 pClip->Count [w] = j;
623 for (uint32 j = 0; j < Window1Enabled; j++)
625 pClip->Left [j][w] = Win1[j].Left;
626 pClip->Right [j][w] = Win1[j].Right;
628 pClip->Count [w] = Window1Enabled;
637 if (Window2Enabled == 1)
639 if (Win2[0].Left <= Win2[0].Right)
641 if (Win2[0].Left > 0)
643 pClip->Left[j][w] = 0;
644 pClip->Right[j++][w] = Win2[0].Left;
646 if (Win2[0].Right < 256)
648 pClip->Left[j][w] = Win2[0].Right;
649 pClip->Right[j++][w] = 256;
653 pClip->Left[j][w] = 1;
654 pClip->Right[j++][w] = 0;
659 pClip->Left[j][w] = 0;
660 pClip->Right[j++][w] = 256;
665 pClip->Left [j][w] = Win2[0].Right;
666 pClip->Right[j++][w] = Win2[1].Left + 1;
668 pClip->Count [w] = j;
672 for (uint32 j = 0; j < Window2Enabled; j++)
674 pClip->Left [j][w] = Win2[j].Left;
675 pClip->Right [j][w] = Win2[j].Right;
677 pClip->Count [w] = Window2Enabled;
684 if (pClip->Count [5])
686 // Colour window enabled. Set the
687 // clip windows for all remaining backgrounds to be
688 // the same as the colour window.
689 if (pClip->Count [w] == 0)
691 pClip->Count [w] = pClip->Count [5];
692 for (uint32 i = 0; i < pClip->Count [w]; i++)
694 pClip->Left [i][w] = pClip->Left [i][5];
695 pClip->Right [i][w] = pClip->Right [i][5];
700 // Intersect the colour window with the bg's
702 for (uint32 i = 0; i < pClip->Count [w]; i++)
705 for (j = 0; j < pClip->Count [5]; j++)
707 if((pClip->Left[i][w] >= pClip->Left[j][5] && pClip->Left[i][w] < pClip->Right[j][5]) || (pClip->Left[j][5] >= pClip->Left[i][w] && pClip->Left[j][5] < pClip->Right[i][w])){
708 // Found an intersection!
709 pClip->Left[i][w]=MAX(pClip->Left[i][w], pClip->Left[j][5]);
710 pClip->Right[i][w]=MIN(pClip->Right[i][w], pClip->Right[j][5]);
714 // no intersection, nullify it
716 pClip->Right[i][w]=0;
718 j=0; // dummy statement
723 } // if (w == 5 | ...
724 } // if (!Settings.DisableGraphicWindows)