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 }; |
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 | |