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
14extern struct SLineMatrixData LineMatrixData[240];
15
16
17namespace TileImpl {
18
19 struct BPProgressive
20 {
21 enum { Pitch = 1 };
22 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 };
30 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:
90 CachedTile(uint32 tile) : Tile(tile) {}
91
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
113 alwaysinline bool IsBlankTile() const
114 {
115 return ((Tile & H_FLIP) ? BG.BufferedFlip[TileNumber] : BG.Buffered[TileNumber]) == BLANK_TILE;
116 }
117
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
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 {
144 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 {
154 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 {
166 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 {
177 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
242 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
318 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
430 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
478 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 };
517 static uint8 Z1(int D, uint8 b) { return D + 7; }
518 static uint8 Z2(int D, uint8 b) { return D + 7; }
519 static uint8 DCMODE() { return Memory.FillRAM[0x2130] & 1; }
520 };
521 struct DrawMode7BG2_OP
522 {
523 enum {
524 MASK = 0x7f,
525 BG = 1
526 };
527 static uint8 Z1(int D, uint8 b) { return D + ((b & 0x80) ? 11 : 3); }
528 static uint8 Z2(int D, uint8 b) { return D + ((b & 0x80) ? 11 : 3); }
529 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
537 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
646 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