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 #ifndef _TILEIMPL_H_
8 #define _TILEIMPL_H_
9 
10 #include "snes9x.h"
11 #include "ppu.h"
12 #include "tile.h"
13 
14 extern struct SLineMatrixData	LineMatrixData[240];
15 
16 
17 namespace TileImpl {
18 
19 	struct BPProgressive
20 	{
21 		enum { Pitch = 1 };
GetBPProgressive22 		static alwaysinline uint32 Get(uint32 StartLine) { return StartLine; }
23 	};
24 
25 	// Interlace: Only draw every other line, so we'll redefine bpstart_t and Pitch to do so.
26 	// Otherwise, it's the same as Normal2x1/Hires2x1.
27 	struct BPInterlace
28 	{
29 		enum { Pitch = 2 };
GetBPInterlace30 		static alwaysinline uint32 Get(uint32 StartLine) { return StartLine * 2 + BG.InterlaceLine; }
31 	};
32 
33 
34 	// The 1x1 pixel plotter, for speedhacking modes.
35 	template<class MATH, class BPSTART>
36 	struct Normal1x1Base
37 	{
38 		enum { Pitch = BPSTART::Pitch };
39 		typedef BPSTART bpstart_t;
40 
41 		static void Draw(int N, int M, uint32 Offset, uint32 OffsetInLine, uint8 Pix, uint8 Z1, uint8 Z2);
42 	};
43 
44 	template<class MATH>
45 	struct Normal1x1 : public Normal1x1Base<MATH, BPProgressive> {};
46 
47 
48 	// The 2x1 pixel plotter, for normal rendering when we've used hires/interlace already this frame.
49 	template<class MATH, class BPSTART>
50 	struct Normal2x1Base
51 	{
52 		enum { Pitch = BPSTART::Pitch };
53 		typedef BPSTART bpstart_t;
54 
55 		static void Draw(int N, int M, uint32 Offset, uint32 OffsetInLine, uint8 Pix, uint8 Z1, uint8 Z2);
56 	};
57 
58 	template<class MATH>
59 	struct Normal2x1 : public Normal2x1Base<MATH, BPProgressive> {};
60 	template<class MATH>
61 	struct Interlace : public Normal2x1Base<MATH, BPInterlace> {};
62 
63 
64 	// Hires pixel plotter, this combines the main and subscreen pixels as appropriate to render hires or pseudo-hires images.
65 	// Use it only on the main screen, subscreen should use Normal2x1 instead.
66 	// Hires math:
67 	//     Main pixel is mathed as normal: Main(x, y) * Sub(x, y).
68 	//     Sub pixel is mathed somewhat weird: Basically, for Sub(x + 1, y) we apply the same operation we applied to Main(x, y)
69 	//     (e.g. no math, add fixed, add1/2 subscreen) using Main(x, y) as the "corresponding subscreen pixel".
70 	//     Also, color window clipping clips Sub(x + 1, y) if Main(x, y) is clipped, not Main(x + 1, y).
71 	//     We don't know how Sub(0, y) is handled.
72 	template<class MATH, class BPSTART>
73 	struct HiresBase
74 	{
75 		enum { Pitch = BPSTART::Pitch };
76 		typedef BPSTART bpstart_t;
77 
78 		static void Draw(int N, int M, uint32 Offset, uint32 OffsetInLine, uint8 Pix, uint8 Z1, uint8 Z2);
79 	};
80 
81 	template<class MATH>
82 	struct Hires : public HiresBase<MATH, BPProgressive> {};
83 	template<class MATH>
84 	struct HiresInterlace : public HiresBase<MATH, BPInterlace> {};
85 
86 
87 	class CachedTile
88 	{
89 	public:
CachedTile(uint32 tile)90 		CachedTile(uint32 tile) : Tile(tile) {}
91 
GetCachedTile()92 		alwaysinline void GetCachedTile()
93 		{
94 			TileAddr = BG.TileAddress + ((Tile & 0x3ff) << BG.TileShift);
95 			if (Tile & 0x100)
96 				TileAddr += BG.NameSelect;
97 			TileAddr &= 0xffff;
98 			TileNumber = TileAddr >> BG.TileShift;
99 			if (Tile & H_FLIP)
100 			{
101 				pCache = &BG.BufferFlip[TileNumber << 6];
102 				if (!BG.BufferedFlip[TileNumber])
103 					BG.BufferedFlip[TileNumber] = BG.ConvertTileFlip(pCache, TileAddr, Tile & 0x3ff);
104 			}
105 			else
106 			{
107 				pCache = &BG.Buffer[TileNumber << 6];
108 				if (!BG.Buffered[TileNumber])
109 					BG.Buffered[TileNumber] = BG.ConvertTile(pCache, TileAddr, Tile & 0x3ff);
110 			}
111 		}
112 
IsBlankTile()113 		alwaysinline bool IsBlankTile() const
114 		{
115 			return ((Tile & H_FLIP) ? BG.BufferedFlip[TileNumber] : BG.Buffered[TileNumber]) == BLANK_TILE;
116 		}
117 
SelectPalette()118 		alwaysinline void SelectPalette() const
119 		{
120 			if (BG.DirectColourMode)
121 			{
122 				GFX.RealScreenColors = DirectColourMaps[(Tile >> 10) & 7];
123 			}
124 			else
125 				GFX.RealScreenColors = &IPPU.ScreenColors[((Tile >> BG.PaletteShift) & BG.PaletteMask) + BG.StartPalette];
126 			GFX.ScreenColors = GFX.ClipColors ? BlackColourMap : GFX.RealScreenColors;
127 		}
128 
Ptr()129 		alwaysinline uint8* Ptr() const
130 		{
131 			return pCache;
132 		}
133 
134 	private:
135 		uint8  *pCache;
136 		uint32 Tile;
137 		uint32 TileNumber;
138 		uint32 TileAddr;
139 	};
140 
141 
142 	struct NOMATH
143 	{
CalcNOMATH144 		static alwaysinline uint16 Calc(uint16 Main, uint16 Sub, uint8 SD)
145 		{
146 			return Main;
147 		}
148 	};
149 	typedef NOMATH Blend_None;
150 
151 	template<class Op>
152 	struct REGMATH
153 	{
CalcREGMATH154 		static alwaysinline uint16 Calc(uint16 Main, uint16 Sub, uint8 SD)
155 		{
156 			return Op::fn(Main, (SD & 0x20) ? Sub : GFX.FixedColour);
157 		}
158 	};
159 	typedef REGMATH<COLOR_ADD> Blend_Add;
160 	typedef REGMATH<COLOR_SUB> Blend_Sub;
161 	typedef REGMATH<COLOR_ADD_BRIGHTNESS> Blend_AddBrightness;
162 
163 	template<class Op>
164 	struct MATHF1_2
165 	{
CalcMATHF1_2166 		static alwaysinline uint16 Calc(uint16 Main, uint16 Sub, uint8 SD)
167 		{
168 			return GFX.ClipColors ? Op::fn(Main, GFX.FixedColour) : Op::fn1_2(Main, GFX.FixedColour);
169 		}
170 	};
171 	typedef MATHF1_2<COLOR_ADD> Blend_AddF1_2;
172 	typedef MATHF1_2<COLOR_SUB> Blend_SubF1_2;
173 
174 	template<class Op>
175 	struct MATHS1_2
176 	{
CalcMATHS1_2177 		static alwaysinline uint16 Calc(uint16 Main, uint16 Sub, uint8 SD)
178 		{
179 			return GFX.ClipColors ? REGMATH<Op>::Calc(Main, Sub, SD) : (SD & 0x20) ? Op::fn1_2(Main, Sub) : Op::fn(Main, GFX.FixedColour);
180 		}
181 	};
182 	typedef MATHS1_2<COLOR_ADD> Blend_AddS1_2;
183 	typedef MATHS1_2<COLOR_SUB> Blend_SubS1_2;
184 	typedef MATHS1_2<COLOR_ADD_BRIGHTNESS> Blend_AddS1_2Brightness;
185 
186 	template<
187 		template<class PIXEL_> class TILE,
188 		template<class MATH> class PIXEL
189 	>
190 	struct Renderers
191 	{
192 		enum { Pitch = PIXEL<Blend_None>::Pitch };
193 		typedef typename TILE< PIXEL<Blend_None> >::call_t call_t;
194 
195 		static call_t Functions[9];
196 	};
197 
198 	#ifdef _TILEIMPL_CPP_
199 	template<
200 		template<class PIXEL_> class TILE,
201 		template<class MATH> class PIXEL
202 	>
203 	typename Renderers<TILE, PIXEL>::call_t Renderers<TILE, PIXEL>::Functions[9] =
204 	{
205 		TILE< PIXEL<Blend_None> >::Draw,
206 		TILE< PIXEL<Blend_Add> >::Draw,
207 		TILE< PIXEL<Blend_AddF1_2> >::Draw,
208 		TILE< PIXEL<Blend_AddS1_2> >::Draw,
209 		TILE< PIXEL<Blend_Sub> >::Draw,
210 		TILE< PIXEL<Blend_SubF1_2> >::Draw,
211 		TILE< PIXEL<Blend_SubS1_2> >::Draw,
212 		TILE< PIXEL<Blend_AddBrightness> >::Draw,
213 		TILE< PIXEL<Blend_AddS1_2Brightness> >::Draw,
214 	};
215 	#endif
216 
217 	// Basic routine to render an unclipped tile.
218 	// Input parameters:
219 	//     bpstart_t = either StartLine or (StartLine * 2 + BG.InterlaceLine),
220 	//     so interlace modes can render every other line from the tile.
221 	//     Pitch = 1 or 2, again so interlace can count lines properly.
222 	//     DRAW_PIXEL(N, M) is a routine to actually draw the pixel. N is the pixel in the row to draw,
223 	//     and M is a test which if false means the pixel should be skipped.
224 	//     Z1 is the "draw if Z1 > cur_depth".
225 	//     Z2 is the "cur_depth = new_depth". OBJ need the two separate.
226 	//     Pix is the pixel to draw.
227 
228 	#define OFFSET_IN_LINE \
229 		uint32 OffsetInLine = Offset % GFX.RealPPL;
230 	#define DRAW_PIXEL(N, M) PIXEL::Draw(N, M, Offset, OffsetInLine, Pix, Z1, Z2)
231 	#define Z1	GFX.Z1
232 	#define Z2	GFX.Z2
233 
234 	template<class PIXEL>
235 	struct DrawTile16
236 	{
237 		typedef void (*call_t)(uint32, uint32, uint32, uint32);
238 
239 		enum { Pitch = PIXEL::Pitch };
240 		typedef typename PIXEL::bpstart_t bpstart_t;
241 
DrawDrawTile16242 		static void Draw(uint32 Tile, uint32 Offset, uint32 StartLine, uint32 LineCount)
243 		{
244 			CachedTile cache(Tile);
245 			int32	l;
246 			uint8	*bp, Pix;
247 
248 			cache.GetCachedTile();
249 			if (cache.IsBlankTile())
250 				return;
251 			cache.SelectPalette();
252 
253 			if (!(Tile & (V_FLIP | H_FLIP)))
254 			{
255 				bp = cache.Ptr() + bpstart_t::Get(StartLine);
256 				OFFSET_IN_LINE;
257 				for (l = LineCount; l > 0; l--, bp += 8 * Pitch, Offset += GFX.PPL)
258 				{
259 					for (int x = 0; x < 8; x++) {
260 						Pix = bp[x]; DRAW_PIXEL(x, Pix);
261 					}
262 				}
263 			}
264 			else
265 			if (!(Tile & V_FLIP))
266 			{
267 				bp = cache.Ptr() + bpstart_t::Get(StartLine);
268 				OFFSET_IN_LINE;
269 				for (l = LineCount; l > 0; l--, bp += 8 * Pitch, Offset += GFX.PPL)
270 				{
271 					for (int x = 0; x < 8; x++) {
272 						Pix = bp[7 - x]; DRAW_PIXEL(x, Pix);
273 					}
274 				}
275 			}
276 			else
277 			if (!(Tile & H_FLIP))
278 			{
279 				bp = cache.Ptr() + 56 - bpstart_t::Get(StartLine);
280 				OFFSET_IN_LINE;
281 				for (l = LineCount; l > 0; l--, bp -= 8 * Pitch, Offset += GFX.PPL)
282 				{
283 					for (int x = 0; x < 8; x++) {
284 						Pix = bp[x]; DRAW_PIXEL(x, Pix);
285 					}
286 				}
287 			}
288 			else
289 			{
290 				bp = cache.Ptr() + 56 - bpstart_t::Get(StartLine);
291 				OFFSET_IN_LINE;
292 				for (l = LineCount; l > 0; l--, bp -= 8 * Pitch, Offset += GFX.PPL)
293 				{
294 					for (int x = 0; x < 8; x++) {
295 						Pix = bp[7 - x]; DRAW_PIXEL(x, Pix);
296 					}
297 				}
298 			}
299 		}
300 	};
301 
302 	#undef Z1
303 	#undef Z2
304 
305 	// Basic routine to render a clipped tile. Inputs same as above.
306 
307 	#define Z1	GFX.Z1
308 	#define Z2	GFX.Z2
309 
310 	template<class PIXEL>
311 	struct DrawClippedTile16
312 	{
313 		typedef void (*call_t)(uint32, uint32, uint32, uint32, uint32, uint32);
314 
315 		enum { Pitch = PIXEL::Pitch };
316 		typedef typename PIXEL::bpstart_t bpstart_t;
317 
DrawDrawClippedTile16318 		static void Draw(uint32 Tile, uint32 Offset, uint32 StartPixel, uint32 Width, uint32 StartLine, uint32 LineCount)
319 		{
320 			CachedTile cache(Tile);
321 			int32	l;
322 			uint8	*bp, Pix, w;
323 
324 			cache.GetCachedTile();
325 			if (cache.IsBlankTile())
326 				return;
327 			cache.SelectPalette();
328 
329 			if (!(Tile & (V_FLIP | H_FLIP)))
330 			{
331 				bp = cache.Ptr() + bpstart_t::Get(StartLine);
332 				OFFSET_IN_LINE;
333 				for (l = LineCount; l > 0; l--, bp += 8 * Pitch, Offset += GFX.PPL)
334 				{
335 					w = Width;
336 					switch (StartPixel)
337 					{
338 						case 0: Pix = bp[0]; DRAW_PIXEL(0, Pix); if (!--w) break; /* Fall through */
339 						case 1: Pix = bp[1]; DRAW_PIXEL(1, Pix); if (!--w) break; /* Fall through */
340 						case 2: Pix = bp[2]; DRAW_PIXEL(2, Pix); if (!--w) break; /* Fall through */
341 						case 3: Pix = bp[3]; DRAW_PIXEL(3, Pix); if (!--w) break; /* Fall through */
342 						case 4: Pix = bp[4]; DRAW_PIXEL(4, Pix); if (!--w) break; /* Fall through */
343 						case 5: Pix = bp[5]; DRAW_PIXEL(5, Pix); if (!--w) break; /* Fall through */
344 						case 6: Pix = bp[6]; DRAW_PIXEL(6, Pix); if (!--w) break; /* Fall through */
345 						case 7: Pix = bp[7]; DRAW_PIXEL(7, Pix); break;
346 					}
347 				}
348 			}
349 			else
350 			if (!(Tile & V_FLIP))
351 			{
352 				bp = cache.Ptr() + bpstart_t::Get(StartLine);
353 				OFFSET_IN_LINE;
354 				for (l = LineCount; l > 0; l--, bp += 8 * Pitch, Offset += GFX.PPL)
355 				{
356 					w = Width;
357 					switch (StartPixel)
358 					{
359 						case 0: Pix = bp[7]; DRAW_PIXEL(0, Pix); if (!--w) break; /* Fall through */
360 						case 1: Pix = bp[6]; DRAW_PIXEL(1, Pix); if (!--w) break; /* Fall through */
361 						case 2: Pix = bp[5]; DRAW_PIXEL(2, Pix); if (!--w) break; /* Fall through */
362 						case 3: Pix = bp[4]; DRAW_PIXEL(3, Pix); if (!--w) break; /* Fall through */
363 						case 4: Pix = bp[3]; DRAW_PIXEL(4, Pix); if (!--w) break; /* Fall through */
364 						case 5: Pix = bp[2]; DRAW_PIXEL(5, Pix); if (!--w) break; /* Fall through */
365 						case 6: Pix = bp[1]; DRAW_PIXEL(6, Pix); if (!--w) break; /* Fall through */
366 						case 7: Pix = bp[0]; DRAW_PIXEL(7, Pix); break;
367 					}
368 				}
369 			}
370 			else
371 			if (!(Tile & H_FLIP))
372 			{
373 				bp = cache.Ptr() + 56 - bpstart_t::Get(StartLine);
374 				OFFSET_IN_LINE;
375 				for (l = LineCount; l > 0; l--, bp -= 8 * Pitch, Offset += GFX.PPL)
376 				{
377 					w = Width;
378 					switch (StartPixel)
379 					{
380 						case 0: Pix = bp[0]; DRAW_PIXEL(0, Pix); if (!--w) break; /* Fall through */
381 						case 1: Pix = bp[1]; DRAW_PIXEL(1, Pix); if (!--w) break; /* Fall through */
382 						case 2: Pix = bp[2]; DRAW_PIXEL(2, Pix); if (!--w) break; /* Fall through */
383 						case 3: Pix = bp[3]; DRAW_PIXEL(3, Pix); if (!--w) break; /* Fall through */
384 						case 4: Pix = bp[4]; DRAW_PIXEL(4, Pix); if (!--w) break; /* Fall through */
385 						case 5: Pix = bp[5]; DRAW_PIXEL(5, Pix); if (!--w) break; /* Fall through */
386 						case 6: Pix = bp[6]; DRAW_PIXEL(6, Pix); if (!--w) break; /* Fall through */
387 						case 7: Pix = bp[7]; DRAW_PIXEL(7, Pix); break;
388 					}
389 				}
390 			}
391 			else
392 			{
393 				bp = cache.Ptr() + 56 - bpstart_t::Get(StartLine);
394 				OFFSET_IN_LINE;
395 				for (l = LineCount; l > 0; l--, bp -= 8 * Pitch, Offset += GFX.PPL)
396 				{
397 					w = Width;
398 					switch (StartPixel)
399 					{
400 						case 0: Pix = bp[7]; DRAW_PIXEL(0, Pix); if (!--w) break; /* Fall through */
401 						case 1: Pix = bp[6]; DRAW_PIXEL(1, Pix); if (!--w) break; /* Fall through */
402 						case 2: Pix = bp[5]; DRAW_PIXEL(2, Pix); if (!--w) break; /* Fall through */
403 						case 3: Pix = bp[4]; DRAW_PIXEL(3, Pix); if (!--w) break; /* Fall through */
404 						case 4: Pix = bp[3]; DRAW_PIXEL(4, Pix); if (!--w) break; /* Fall through */
405 						case 5: Pix = bp[2]; DRAW_PIXEL(5, Pix); if (!--w) break; /* Fall through */
406 						case 6: Pix = bp[1]; DRAW_PIXEL(6, Pix); if (!--w) break; /* Fall through */
407 						case 7: Pix = bp[0]; DRAW_PIXEL(7, Pix); break;
408 					}
409 				}
410 			}
411 		}
412 	};
413 
414 	#undef Z1
415 	#undef Z2
416 
417 	// Basic routine to render a single mosaic pixel.
418 	// DRAW_PIXEL, bpstart_t, Z1, Z2 and Pix are the same as above, but Pitch is not used.
419 
420 	#define Z1	GFX.Z1
421 	#define Z2	GFX.Z2
422 
423 	template<class PIXEL>
424 	struct DrawMosaicPixel16
425 	{
426 		typedef void (*call_t)(uint32, uint32, uint32, uint32, uint32, uint32);
427 
428 		typedef typename PIXEL::bpstart_t bpstart_t;
429 
DrawDrawMosaicPixel16430 		static void Draw(uint32 Tile, uint32 Offset, uint32 StartLine, uint32 StartPixel, uint32 Width, uint32 LineCount)
431 		{
432 			CachedTile cache(Tile);
433 			int32	l, w;
434 			uint8	Pix;
435 
436 			cache.GetCachedTile();
437 			if (cache.IsBlankTile())
438 				return;
439 			cache.SelectPalette();
440 
441 			if (Tile & H_FLIP)
442 				StartPixel = 7 - StartPixel;
443 
444 			if (Tile & V_FLIP)
445 				Pix = cache.Ptr()[56 - bpstart_t::Get(StartLine) + StartPixel];
446 			else
447 				Pix = cache.Ptr()[bpstart_t::Get(StartLine) + StartPixel];
448 
449 			if (Pix)
450 			{
451 				OFFSET_IN_LINE;
452 				for (l = LineCount; l > 0; l--, Offset += GFX.PPL)
453 				{
454 					for (w = Width - 1; w >= 0; w--)
455 						DRAW_PIXEL(w, 1);
456 				}
457 			}
458 		}
459 	};
460 
461 	#undef Z1
462 	#undef Z2
463 
464 	// Basic routine to render the backdrop.
465 	// DRAW_PIXEL is the same as above, but since we're just replicating a single pixel there's no need for Pitch or bpstart_t
466 	// (or interlace at all, really).
467 	// The backdrop is always depth = 1, so Z1 = Z2 = 1. And backdrop is always color 0.
468 
469 	#define Z1				1
470 	#define Z2				1
471 	#define Pix				0
472 
473 	template<class PIXEL>
474 	struct DrawBackdrop16
475 	{
476 		typedef void (*call_t)(uint32 Offset, uint32 Left, uint32 Right);
477 
DrawDrawBackdrop16478 		static void Draw(uint32 Offset, uint32 Left, uint32 Right)
479 		{
480 			uint32	l, x;
481 
482 			GFX.RealScreenColors = IPPU.ScreenColors;
483 			GFX.ScreenColors = GFX.ClipColors ? BlackColourMap : GFX.RealScreenColors;
484 
485 			OFFSET_IN_LINE;
486 			for (l = GFX.StartY; l <= GFX.EndY; l++, Offset += GFX.PPL)
487 			{
488 				for (x = Left; x < Right; x++)
489 					DRAW_PIXEL(x, 1);
490 			}
491 		}
492 	};
493 
494 	#undef Pix
495 	#undef Z1
496 	#undef Z2
497 	#undef DRAW_PIXEL
498 
499 	// Basic routine to render a chunk of a Mode 7 BG.
500 	// Mode 7 has no interlace, so bpstart_t and Pitch are unused.
501 	// We get some new parameters, so we can use the same DRAW_TILE to do BG1 or BG2:
502 	//     DCMODE tests if Direct Color should apply.
503 	//     BG is the BG, so we use the right clip window.
504 	//     MASK is 0xff or 0x7f, the 'color' portion of the pixel.
505 	// We define Z1/Z2 to either be constant 5 or to vary depending on the 'priority' portion of the pixel.
506 
507 	#define CLIP_10_BIT_SIGNED(a)	(((a) & 0x2000) ? ((a) | ~0x3ff) : ((a) & 0x3ff))
508 
509 	#define DRAW_PIXEL(N, M) PIXEL::Draw(N, M, Offset, OffsetInLine, Pix, OP::Z1(D, b), OP::Z2(D, b))
510 
511 	struct DrawMode7BG1_OP
512 	{
513 		enum {
514 			MASK = 0xff,
515 			BG   = 0
516 		};
Z1DrawMode7BG1_OP517 		static uint8 Z1(int D, uint8 b) { return D + 7; }
Z2DrawMode7BG1_OP518 		static uint8 Z2(int D, uint8 b) { return D + 7; }
DCMODEDrawMode7BG1_OP519 		static uint8 DCMODE() { return Memory.FillRAM[0x2130] & 1; }
520 	};
521 	struct DrawMode7BG2_OP
522 	{
523 		enum {
524 			MASK = 0x7f,
525 			BG   = 1
526 		};
Z1DrawMode7BG2_OP527 		static uint8 Z1(int D, uint8 b) { return D + ((b & 0x80) ? 11 : 3); }
Z2DrawMode7BG2_OP528 		static uint8 Z2(int D, uint8 b) { return D + ((b & 0x80) ? 11 : 3); }
DCMODEDrawMode7BG2_OP529 		static uint8 DCMODE() { return 0; }
530 	};
531 
532 	template<class PIXEL, class OP>
533 	struct DrawTileNormal
534 	{
535 		typedef void (*call_t)(uint32 Left, uint32 Right, int D);
536 
DrawDrawTileNormal537 		static void Draw(uint32 Left, uint32 Right, int D)
538 		{
539 			uint8	*VRAM1 = Memory.VRAM + 1;
540 
541 			if (OP::DCMODE())
542 			{
543 				GFX.RealScreenColors = DirectColourMaps[0];
544 			}
545 			else
546 				GFX.RealScreenColors = IPPU.ScreenColors;
547 
548 			GFX.ScreenColors = GFX.ClipColors ? BlackColourMap : GFX.RealScreenColors;
549 
550 			int	aa, cc;
551 			int	startx;
552 
553 			uint32	Offset = GFX.StartY * GFX.PPL;
554 			struct SLineMatrixData	*l = &LineMatrixData[GFX.StartY];
555 
556 			OFFSET_IN_LINE;
557 			for (uint32 Line = GFX.StartY; Line <= GFX.EndY; Line++, Offset += GFX.PPL, l++)
558 			{
559 				int	yy, starty;
560 
561 				int32	HOffset = ((int32) l->M7HOFS  << 19) >> 19;
562 				int32	VOffset = ((int32) l->M7VOFS  << 19) >> 19;
563 
564 				int32	CentreX = ((int32) l->CentreX << 19) >> 19;
565 				int32	CentreY = ((int32) l->CentreY << 19) >> 19;
566 
567 				if (PPU.Mode7VFlip)
568 					starty = 255 - (int) (Line + 1);
569 				else
570 					starty = Line + 1;
571 
572 				yy = CLIP_10_BIT_SIGNED(VOffset - CentreY);
573 
574 				int	BB = ((l->MatrixB * starty) & ~63) + ((l->MatrixB * yy) & ~63) + (CentreX << 8);
575 				int	DD = ((l->MatrixD * starty) & ~63) + ((l->MatrixD * yy) & ~63) + (CentreY << 8);
576 
577 				if (PPU.Mode7HFlip)
578 				{
579 					startx = Right - 1;
580 					aa = -l->MatrixA;
581 					cc = -l->MatrixC;
582 				}
583 				else
584 				{
585 					startx = Left;
586 					aa = l->MatrixA;
587 					cc = l->MatrixC;
588 				}
589 
590 				int	xx = CLIP_10_BIT_SIGNED(HOffset - CentreX);
591 				int	AA = l->MatrixA * startx + ((l->MatrixA * xx) & ~63);
592 				int	CC = l->MatrixC * startx + ((l->MatrixC * xx) & ~63);
593 
594 				uint8	Pix;
595 
596 				if (!PPU.Mode7Repeat)
597 				{
598 					for (uint32 x = Left; x < Right; x++, AA += aa, CC += cc)
599 					{
600 						int	X = ((AA + BB) >> 8) & 0x3ff;
601 						int	Y = ((CC + DD) >> 8) & 0x3ff;
602 
603 						uint8	*TileData = VRAM1 + (Memory.VRAM[((Y & ~7) << 5) + ((X >> 2) & ~1)] << 7);
604 						uint8	b = *(TileData + ((Y & 7) << 4) + ((X & 7) << 1));
605 
606 						Pix = b & OP::MASK; DRAW_PIXEL(x, Pix);
607 					}
608 				}
609 				else
610 				{
611 					for (uint32 x = Left; x < Right; x++, AA += aa, CC += cc)
612 					{
613 						int	X = ((AA + BB) >> 8);
614 						int	Y = ((CC + DD) >> 8);
615 
616 						uint8	b;
617 
618 						if (((X | Y) & ~0x3ff) == 0)
619 						{
620 							uint8	*TileData = VRAM1 + (Memory.VRAM[((Y & ~7) << 5) + ((X >> 2) & ~1)] << 7);
621 							b = *(TileData + ((Y & 7) << 4) + ((X & 7) << 1));
622 						}
623 						else
624 						if (PPU.Mode7Repeat == 3)
625 							b = *(VRAM1    + ((Y & 7) << 4) + ((X & 7) << 1));
626 						else
627 							continue;
628 
629 						Pix = b & OP::MASK; DRAW_PIXEL(x, Pix);
630 					}
631 				}
632 			}
633 		}
634 	};
635 
636 	template<class PIXEL>
637 	struct DrawMode7BG1 : public DrawTileNormal<PIXEL, DrawMode7BG1_OP> {};
638 	template<class PIXEL>
639 	struct DrawMode7BG2 : public DrawTileNormal<PIXEL, DrawMode7BG2_OP> {};
640 
641 	template<class PIXEL, class OP>
642 	struct DrawTileMosaic
643 	{
644 		typedef void (*call_t)(uint32 Left, uint32 Right, int D);
645 
DrawDrawTileMosaic646 		static void Draw(uint32 Left, uint32 Right, int D)
647 		{
648 			uint8	*VRAM1 = Memory.VRAM + 1;
649 
650 			if (OP::DCMODE())
651 			{
652 				GFX.RealScreenColors = DirectColourMaps[0];
653 			}
654 			else
655 				GFX.RealScreenColors = IPPU.ScreenColors;
656 
657 			GFX.ScreenColors = GFX.ClipColors ? BlackColourMap : GFX.RealScreenColors;
658 
659 			int	aa, cc;
660 			int	startx, StartY = GFX.StartY;
661 
662 			int		HMosaic = 1, VMosaic = 1, MosaicStart = 0;
663 			int32	MLeft = Left, MRight = Right;
664 
665 			if (PPU.BGMosaic[0])
666 			{
667 				VMosaic = PPU.Mosaic;
668 				MosaicStart = ((uint32) GFX.StartY - PPU.MosaicStart) % VMosaic;
669 				StartY -= MosaicStart;
670 			}
671 
672 			if (PPU.BGMosaic[OP::BG])
673 			{
674 				HMosaic = PPU.Mosaic;
675 				MLeft  -= MLeft  % HMosaic;
676 				MRight += HMosaic - 1;
677 				MRight -= MRight % HMosaic;
678 			}
679 
680 			uint32	Offset = StartY * GFX.PPL;
681 			struct SLineMatrixData	*l = &LineMatrixData[StartY];
682 
683 			OFFSET_IN_LINE;
684 			for (uint32 Line = StartY; Line <= GFX.EndY; Line += VMosaic, Offset += VMosaic * GFX.PPL, l += VMosaic)
685 			{
686 				if (Line + VMosaic > GFX.EndY)
687 					VMosaic = GFX.EndY - Line + 1;
688 
689 				int	yy, starty;
690 
691 				int32	HOffset = ((int32) l->M7HOFS  << 19) >> 19;
692 				int32	VOffset = ((int32) l->M7VOFS  << 19) >> 19;
693 
694 				int32	CentreX = ((int32) l->CentreX << 19) >> 19;
695 				int32	CentreY = ((int32) l->CentreY << 19) >> 19;
696 
697 				if (PPU.Mode7VFlip)
698 					starty = 255 - (int) (Line + 1);
699 				else
700 					starty = Line + 1;
701 
702 				yy = CLIP_10_BIT_SIGNED(VOffset - CentreY);
703 
704 				int	BB = ((l->MatrixB * starty) & ~63) + ((l->MatrixB * yy) & ~63) + (CentreX << 8);
705 				int	DD = ((l->MatrixD * starty) & ~63) + ((l->MatrixD * yy) & ~63) + (CentreY << 8);
706 
707 				if (PPU.Mode7HFlip)
708 				{
709 					startx = MRight - 1;
710 					aa = -l->MatrixA;
711 					cc = -l->MatrixC;
712 				}
713 				else
714 				{
715 					startx = MLeft;
716 					aa = l->MatrixA;
717 					cc = l->MatrixC;
718 				}
719 
720 				int	xx = CLIP_10_BIT_SIGNED(HOffset - CentreX);
721 				int	AA = l->MatrixA * startx + ((l->MatrixA * xx) & ~63);
722 				int	CC = l->MatrixC * startx + ((l->MatrixC * xx) & ~63);
723 
724 				uint8	Pix;
725 				uint8	ctr = 1;
726 
727 				if (!PPU.Mode7Repeat)
728 				{
729 					for (int32 x = MLeft; x < MRight; x++, AA += aa, CC += cc)
730 					{
731 						if (--ctr)
732 							continue;
733 						ctr = HMosaic;
734 
735 						int	X = ((AA + BB) >> 8) & 0x3ff;
736 						int	Y = ((CC + DD) >> 8) & 0x3ff;
737 
738 						uint8	*TileData = VRAM1 + (Memory.VRAM[((Y & ~7) << 5) + ((X >> 2) & ~1)] << 7);
739 						uint8	b = *(TileData + ((Y & 7) << 4) + ((X & 7) << 1));
740 
741 						if ((Pix = (b & OP::MASK)))
742 						{
743 							for (int32 h = MosaicStart; h < VMosaic; h++)
744 							{
745 								for (int32 w = x + HMosaic - 1; w >= x; w--)
746 									DRAW_PIXEL(w + h * GFX.PPL, (w >= (int32) Left && w < (int32) Right));
747 							}
748 						}
749 					}
750 				}
751 				else
752 				{
753 					for (int32 x = MLeft; x < MRight; x++, AA += aa, CC += cc)
754 					{
755 						if (--ctr)
756 							continue;
757 						ctr = HMosaic;
758 
759 						int	X = ((AA + BB) >> 8);
760 						int	Y = ((CC + DD) >> 8);
761 
762 						uint8	b;
763 
764 						if (((X | Y) & ~0x3ff) == 0)
765 						{
766 							uint8	*TileData = VRAM1 + (Memory.VRAM[((Y & ~7) << 5) + ((X >> 2) & ~1)] << 7);
767 							b = *(TileData + ((Y & 7) << 4) + ((X & 7) << 1));
768 						}
769 						else
770 						if (PPU.Mode7Repeat == 3)
771 							b = *(VRAM1    + ((Y & 7) << 4) + ((X & 7) << 1));
772 						else
773 							continue;
774 
775 						if ((Pix = (b & OP::MASK)))
776 						{
777 							for (int32 h = MosaicStart; h < VMosaic; h++)
778 							{
779 								for (int32 w = x + HMosaic - 1; w >= x; w--)
780 									DRAW_PIXEL(w + h * GFX.PPL, (w >= (int32) Left && w < (int32) Right));
781 							}
782 						}
783 					}
784 				}
785 
786 				MosaicStart = 0;
787 			}
788 		}
789 	};
790 
791 	template<class PIXEL>
792 	struct DrawMode7MosaicBG1 : public DrawTileMosaic<PIXEL, DrawMode7BG1_OP> {};
793 	template<class PIXEL>
794 	struct DrawMode7MosaicBG2 : public DrawTileMosaic<PIXEL, DrawMode7BG2_OP> {};
795 
796 
797 	#undef DRAW_PIXEL
798 
799 } // namespace TileImpl
800 
801 #endif
802