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 "ppu.h" |
9 | #include "tile.h" |
10 | #include "controls.h" |
11 | #include "crosshairs.h" |
12 | #include "cheats.h" |
13 | #include "movie.h" |
14 | #include "screenshot.h" |
15 | #include "font.h" |
16 | #include "display.h" |
17 | |
18 | extern struct SCheatData Cheat; |
19 | extern struct SLineData LineData[240]; |
20 | extern struct SLineMatrixData LineMatrixData[240]; |
21 | |
22 | void S9xComputeClipWindows (void); |
23 | |
24 | static int font_width = 8, font_height = 9; |
25 | void (*S9xCustomDisplayString) (const char *, int, int, bool, int) = NULL; |
26 | |
27 | static void SetupOBJ (void); |
28 | static void DrawOBJS (int); |
29 | static void DisplayTime (void); |
30 | static void DisplayFrameRate (void); |
31 | static void DisplayPressedKeys (void); |
32 | static void DisplayWatchedAddresses (void); |
33 | static void DisplayStringFromBottom (const char *, int, int, bool); |
34 | static void DrawBackground (int, uint8, uint8); |
35 | static void DrawBackgroundMosaic (int, uint8, uint8); |
36 | static void DrawBackgroundOffset (int, uint8, uint8, int); |
37 | static void DrawBackgroundOffsetMosaic (int, uint8, uint8, int); |
38 | static inline void DrawBackgroundMode7 (int, void (*DrawMath) (uint32, uint32, int), void (*DrawNomath) (uint32, uint32, int), int); |
39 | static inline void DrawBackdrop (void); |
40 | static inline void RenderScreen (bool8); |
41 | static uint16 get_crosshair_color (uint8); |
42 | static void S9xDisplayStringType (const char *, int, int, bool, int); |
43 | |
44 | #define TILE_PLUS(t, x) (((t) & 0xfc00) | ((t + x) & 0x3ff)) |
45 | |
46 | |
47 | bool8 S9xGraphicsInit (void) |
48 | { |
49 | S9xInitTileRenderer(); |
50 | memset(BlackColourMap, 0, 256 * sizeof(uint16)); |
51 | |
52 | GFX.RealPPL = GFX.Pitch >> 1; |
53 | IPPU.OBJChanged = TRUE; |
54 | Settings.BG_Forced = 0; |
55 | S9xFixColourBrightness(); |
56 | S9xBuildDirectColourMaps(); |
57 | |
58 | GFX.ZERO = (uint16 *) malloc(sizeof(uint16) * 0x10000); |
59 | |
60 | GFX.ScreenSize = GFX.Pitch / 2 * SNES_HEIGHT_EXTENDED * (Settings.SupportHiRes ? 2 : 1); |
61 | GFX.SubScreen = (uint16 *) malloc(GFX.ScreenSize * sizeof(uint16)); |
62 | GFX.ZBuffer = (uint8 *) malloc(GFX.ScreenSize); |
63 | GFX.SubZBuffer = (uint8 *) malloc(GFX.ScreenSize); |
64 | |
65 | if (!GFX.ZERO || !GFX.SubScreen || !GFX.ZBuffer || !GFX.SubZBuffer) |
66 | { |
67 | S9xGraphicsDeinit(); |
68 | return (FALSE); |
69 | } |
70 | |
71 | // Lookup table for 1/2 color subtraction |
72 | memset(GFX.ZERO, 0, 0x10000 * sizeof(uint16)); |
73 | for (uint32 r = 0; r <= MAX_RED; r++) |
74 | { |
75 | uint32 r2 = r; |
76 | if (r2 & 0x10) |
77 | r2 &= ~0x10; |
78 | else |
79 | r2 = 0; |
80 | |
81 | for (uint32 g = 0; g <= MAX_GREEN; g++) |
82 | { |
83 | uint32 g2 = g; |
84 | if (g2 & GREEN_HI_BIT) |
85 | g2 &= ~GREEN_HI_BIT; |
86 | else |
87 | g2 = 0; |
88 | |
89 | for (uint32 b = 0; b <= MAX_BLUE; b++) |
90 | { |
91 | uint32 b2 = b; |
92 | if (b2 & 0x10) |
93 | b2 &= ~0x10; |
94 | else |
95 | b2 = 0; |
96 | |
97 | GFX.ZERO[BUILD_PIXEL2(r, g, b)] = BUILD_PIXEL2(r2, g2, b2); |
98 | GFX.ZERO[BUILD_PIXEL2(r, g, b) & ~ALPHA_BITS_MASK] = BUILD_PIXEL2(r2, g2, b2); |
99 | } |
100 | } |
101 | } |
102 | |
103 | return (TRUE); |
104 | } |
105 | |
106 | void S9xGraphicsDeinit (void) |
107 | { |
108 | if (GFX.ZERO) { free(GFX.ZERO); GFX.ZERO = NULL; } |
109 | if (GFX.SubScreen) { free(GFX.SubScreen); GFX.SubScreen = NULL; } |
110 | if (GFX.ZBuffer) { free(GFX.ZBuffer); GFX.ZBuffer = NULL; } |
111 | if (GFX.SubZBuffer) { free(GFX.SubZBuffer); GFX.SubZBuffer = NULL; } |
112 | } |
113 | |
114 | void S9xGraphicsScreenResize (void) |
115 | { |
116 | IPPU.MaxBrightness = PPU.Brightness; |
117 | |
118 | IPPU.Interlace = Memory.FillRAM[0x2133] & 1; |
119 | IPPU.InterlaceOBJ = Memory.FillRAM[0x2133] & 2; |
120 | IPPU.PseudoHires = Memory.FillRAM[0x2133] & 8; |
121 | |
122 | if (Settings.SupportHiRes && (PPU.BGMode == 5 || PPU.BGMode == 6 || IPPU.PseudoHires)) |
123 | { |
124 | GFX.RealPPL = GFX.Pitch >> 1; |
125 | IPPU.DoubleWidthPixels = TRUE; |
126 | IPPU.RenderedScreenWidth = SNES_WIDTH << 1; |
127 | } |
128 | else |
129 | { |
130 | #ifdef USE_OPENGL |
131 | if (Settings.OpenGLEnable) |
132 | GFX.RealPPL = SNES_WIDTH; |
133 | else |
134 | #endif |
135 | GFX.RealPPL = GFX.Pitch >> 1; |
136 | |
137 | IPPU.DoubleWidthPixels = FALSE; |
138 | IPPU.RenderedScreenWidth = SNES_WIDTH; |
139 | } |
140 | |
141 | if (Settings.SupportHiRes && IPPU.Interlace) |
142 | { |
143 | GFX.PPL = GFX.RealPPL << 1; |
144 | IPPU.DoubleHeightPixels = TRUE; |
145 | IPPU.RenderedScreenHeight = PPU.ScreenHeight << 1; |
146 | GFX.DoInterlace++; |
147 | } |
148 | else |
149 | { |
150 | GFX.PPL = GFX.RealPPL; |
151 | IPPU.DoubleHeightPixels = FALSE; |
152 | IPPU.RenderedScreenHeight = PPU.ScreenHeight; |
153 | } |
154 | } |
155 | |
156 | void S9xBuildDirectColourMaps (void) |
157 | { |
158 | IPPU.XB = mul_brightness[PPU.Brightness]; |
159 | |
160 | for (uint32 p = 0; p < 8; p++) |
161 | for (uint32 c = 0; c < 256; c++) |
162 | DirectColourMaps[p][c] = BUILD_PIXEL(IPPU.XB[((c & 7) << 2) | ((p & 1) << 1)], IPPU.XB[((c & 0x38) >> 1) | (p & 2)], IPPU.XB[((c & 0xc0) >> 3) | (p & 4)]); |
163 | } |
164 | |
165 | void S9xStartScreenRefresh (void) |
166 | { |
167 | GFX.InterlaceFrame = !GFX.InterlaceFrame; |
168 | if (GFX.DoInterlace) |
169 | GFX.DoInterlace--; |
170 | |
171 | if (IPPU.RenderThisFrame) |
172 | { |
173 | if (!GFX.DoInterlace || !GFX.InterlaceFrame) |
174 | { |
175 | if (!S9xInitUpdate()) |
176 | { |
177 | IPPU.RenderThisFrame = FALSE; |
178 | return; |
179 | } |
180 | |
181 | S9xGraphicsScreenResize(); |
182 | |
183 | IPPU.RenderedFramesCount++; |
184 | } |
185 | |
186 | PPU.MosaicStart = 0; |
187 | PPU.RecomputeClipWindows = TRUE; |
188 | IPPU.PreviousLine = IPPU.CurrentLine = 0; |
189 | |
190 | memset(GFX.ZBuffer, 0, GFX.ScreenSize); |
191 | memset(GFX.SubZBuffer, 0, GFX.ScreenSize); |
192 | } |
193 | |
194 | if (++IPPU.FrameCount % Memory.ROMFramesPerSecond == 0) |
195 | { |
196 | IPPU.DisplayedRenderedFrameCount = IPPU.RenderedFramesCount; |
197 | IPPU.RenderedFramesCount = 0; |
198 | IPPU.FrameCount = 0; |
199 | } |
200 | |
201 | if (GFX.InfoStringTimeout > 0 && --GFX.InfoStringTimeout == 0) |
202 | GFX.InfoString = NULL; |
203 | |
204 | IPPU.TotalEmulatedFrames++; |
205 | } |
206 | |
207 | void S9xEndScreenRefresh (void) |
208 | { |
209 | if (IPPU.RenderThisFrame) |
210 | { |
211 | FLUSH_REDRAW(); |
212 | |
213 | if (GFX.DoInterlace && GFX.InterlaceFrame == 0) |
214 | { |
215 | S9xControlEOF(); |
216 | S9xContinueUpdate(IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight); |
217 | } |
218 | else |
219 | { |
220 | if (IPPU.ColorsChanged) |
221 | { |
222 | uint32 saved = PPU.CGDATA[0]; |
223 | IPPU.ColorsChanged = FALSE; |
224 | S9xSetPalette(); |
225 | PPU.CGDATA[0] = saved; |
226 | } |
227 | |
228 | S9xControlEOF(); |
229 | |
230 | if (Settings.TakeScreenshot) |
231 | S9xDoScreenshot(IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight); |
232 | |
233 | if (Settings.AutoDisplayMessages) |
234 | S9xDisplayMessages(GFX.Screen, GFX.RealPPL, IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight, 1); |
235 | |
236 | S9xDeinitUpdate(IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight); |
237 | } |
238 | } |
239 | else |
240 | S9xControlEOF(); |
241 | |
242 | S9xUpdateCheatsInMemory (); |
243 | |
244 | #ifdef DEBUGGER |
245 | if (CPU.Flags & FRAME_ADVANCE_FLAG) |
246 | { |
247 | if (ICPU.FrameAdvanceCount) |
248 | { |
249 | ICPU.FrameAdvanceCount--; |
250 | IPPU.RenderThisFrame = TRUE; |
251 | IPPU.FrameSkip = 0; |
252 | } |
253 | else |
254 | { |
255 | CPU.Flags &= ~FRAME_ADVANCE_FLAG; |
256 | CPU.Flags |= DEBUG_MODE_FLAG; |
257 | } |
258 | } |
259 | #endif |
260 | |
261 | if (CPU.SRAMModified) |
262 | { |
263 | if (!CPU.AutoSaveTimer) |
264 | { |
265 | if (!(CPU.AutoSaveTimer = Settings.AutoSaveDelay * Memory.ROMFramesPerSecond)) |
266 | CPU.SRAMModified = FALSE; |
267 | } |
268 | else |
269 | { |
270 | if (!--CPU.AutoSaveTimer) |
271 | { |
272 | S9xAutoSaveSRAM(); |
273 | CPU.SRAMModified = FALSE; |
274 | } |
275 | } |
276 | } |
277 | } |
278 | |
279 | void RenderLine (uint8 C) |
280 | { |
281 | if (IPPU.RenderThisFrame) |
282 | { |
283 | LineData[C].BG[0].VOffset = PPU.BG[0].VOffset + 1; |
284 | LineData[C].BG[0].HOffset = PPU.BG[0].HOffset; |
285 | LineData[C].BG[1].VOffset = PPU.BG[1].VOffset + 1; |
286 | LineData[C].BG[1].HOffset = PPU.BG[1].HOffset; |
287 | |
288 | if (PPU.BGMode == 7) |
289 | { |
290 | struct SLineMatrixData *p = &LineMatrixData[C]; |
291 | p->MatrixA = PPU.MatrixA; |
292 | p->MatrixB = PPU.MatrixB; |
293 | p->MatrixC = PPU.MatrixC; |
294 | p->MatrixD = PPU.MatrixD; |
295 | p->CentreX = PPU.CentreX; |
296 | p->CentreY = PPU.CentreY; |
297 | p->M7HOFS = PPU.M7HOFS; |
298 | p->M7VOFS = PPU.M7VOFS; |
299 | } |
300 | else |
301 | { |
302 | LineData[C].BG[2].VOffset = PPU.BG[2].VOffset + 1; |
303 | LineData[C].BG[2].HOffset = PPU.BG[2].HOffset; |
304 | LineData[C].BG[3].VOffset = PPU.BG[3].VOffset + 1; |
305 | LineData[C].BG[3].HOffset = PPU.BG[3].HOffset; |
306 | } |
307 | |
308 | IPPU.CurrentLine = C + 1; |
309 | } |
310 | else |
311 | { |
312 | // if we're not rendering this frame, we still need to update this |
313 | // XXX: Check ForceBlank? Or anything else? |
314 | if (IPPU.OBJChanged) |
315 | SetupOBJ(); |
316 | PPU.RangeTimeOver |= GFX.OBJLines[C].RTOFlags; |
317 | } |
318 | } |
319 | |
320 | static inline void RenderScreen (bool8 sub) |
321 | { |
322 | uint8 BGActive; |
323 | int D; |
324 | |
325 | if (!sub) |
326 | { |
327 | GFX.S = GFX.Screen; |
328 | if (GFX.DoInterlace && GFX.InterlaceFrame) |
329 | GFX.S += GFX.RealPPL; |
330 | GFX.DB = GFX.ZBuffer; |
331 | GFX.Clip = IPPU.Clip[0]; |
332 | BGActive = Memory.FillRAM[0x212c] & ~Settings.BG_Forced; |
333 | D = 32; |
334 | } |
335 | else |
336 | { |
337 | GFX.S = GFX.SubScreen; |
338 | GFX.DB = GFX.SubZBuffer; |
339 | GFX.Clip = IPPU.Clip[1]; |
340 | BGActive = Memory.FillRAM[0x212d] & ~Settings.BG_Forced; |
341 | D = (Memory.FillRAM[0x2130] & 2) << 4; // 'do math' depth flag |
342 | } |
343 | |
344 | if (BGActive & 0x10) |
345 | { |
346 | BG.TileAddress = PPU.OBJNameBase; |
347 | BG.NameSelect = PPU.OBJNameSelect; |
348 | BG.EnableMath = !sub && (Memory.FillRAM[0x2131] & 0x10); |
349 | BG.StartPalette = 128; |
350 | S9xSelectTileConverter(4, FALSE, sub, FALSE); |
351 | S9xSelectTileRenderers(PPU.BGMode, sub, TRUE); |
352 | DrawOBJS(D + 4); |
353 | } |
354 | |
355 | BG.NameSelect = 0; |
356 | S9xSelectTileRenderers(PPU.BGMode, sub, FALSE); |
357 | |
358 | #define DO_BG(n, pal, depth, hires, offset, Zh, Zl, voffoff) \ |
359 | if (BGActive & (1 << n)) \ |
360 | { \ |
361 | BG.StartPalette = pal; \ |
362 | BG.EnableMath = !sub && (Memory.FillRAM[0x2131] & (1 << n)); \ |
363 | BG.TileSizeH = (!hires && PPU.BG[n].BGSize) ? 16 : 8; \ |
364 | BG.TileSizeV = (PPU.BG[n].BGSize) ? 16 : 8; \ |
365 | S9xSelectTileConverter(depth, hires, sub, PPU.BGMosaic[n]); \ |
366 | \ |
367 | if (offset) \ |
368 | { \ |
369 | BG.OffsetSizeH = (!hires && PPU.BG[2].BGSize) ? 16 : 8; \ |
370 | BG.OffsetSizeV = (PPU.BG[2].BGSize) ? 16 : 8; \ |
371 | \ |
372 | if (PPU.BGMosaic[n] && (hires || PPU.Mosaic > 1)) \ |
373 | DrawBackgroundOffsetMosaic(n, D + Zh, D + Zl, voffoff); \ |
374 | else \ |
375 | DrawBackgroundOffset(n, D + Zh, D + Zl, voffoff); \ |
376 | } \ |
377 | else \ |
378 | { \ |
379 | if (PPU.BGMosaic[n] && (hires || PPU.Mosaic > 1)) \ |
380 | DrawBackgroundMosaic(n, D + Zh, D + Zl); \ |
381 | else \ |
382 | DrawBackground(n, D + Zh, D + Zl); \ |
383 | } \ |
384 | } |
385 | |
386 | switch (PPU.BGMode) |
387 | { |
388 | case 0: |
389 | DO_BG(0, 0, 2, FALSE, FALSE, 15, 11, 0); |
390 | DO_BG(1, 32, 2, FALSE, FALSE, 14, 10, 0); |
391 | DO_BG(2, 64, 2, FALSE, FALSE, 7, 3, 0); |
392 | DO_BG(3, 96, 2, FALSE, FALSE, 6, 2, 0); |
393 | break; |
394 | |
395 | case 1: |
396 | DO_BG(0, 0, 4, FALSE, FALSE, 15, 11, 0); |
397 | DO_BG(1, 0, 4, FALSE, FALSE, 14, 10, 0); |
398 | DO_BG(2, 0, 2, FALSE, FALSE, (PPU.BG3Priority ? 17 : 7), 3, 0); |
399 | break; |
400 | |
401 | case 2: |
402 | DO_BG(0, 0, 4, FALSE, TRUE, 15, 7, 8); |
403 | DO_BG(1, 0, 4, FALSE, TRUE, 11, 3, 8); |
404 | break; |
405 | |
406 | case 3: |
407 | DO_BG(0, 0, 8, FALSE, FALSE, 15, 7, 0); |
408 | DO_BG(1, 0, 4, FALSE, FALSE, 11, 3, 0); |
409 | break; |
410 | |
411 | case 4: |
412 | DO_BG(0, 0, 8, FALSE, TRUE, 15, 7, 0); |
413 | DO_BG(1, 0, 2, FALSE, TRUE, 11, 3, 0); |
414 | break; |
415 | |
416 | case 5: |
417 | DO_BG(0, 0, 4, TRUE, FALSE, 15, 7, 0); |
418 | DO_BG(1, 0, 2, TRUE, FALSE, 11, 3, 0); |
419 | break; |
420 | |
421 | case 6: |
422 | DO_BG(0, 0, 4, TRUE, TRUE, 15, 7, 8); |
423 | break; |
424 | |
425 | case 7: |
426 | if (BGActive & 0x01) |
427 | { |
428 | BG.EnableMath = !sub && (Memory.FillRAM[0x2131] & 1); |
429 | DrawBackgroundMode7(0, GFX.DrawMode7BG1Math, GFX.DrawMode7BG1Nomath, D); |
430 | } |
431 | |
432 | if ((Memory.FillRAM[0x2133] & 0x40) && (BGActive & 0x02)) |
433 | { |
434 | BG.EnableMath = !sub && (Memory.FillRAM[0x2131] & 2); |
435 | DrawBackgroundMode7(1, GFX.DrawMode7BG2Math, GFX.DrawMode7BG2Nomath, D); |
436 | } |
437 | |
438 | break; |
439 | } |
440 | |
441 | #undef DO_BG |
442 | |
443 | BG.EnableMath = !sub && (Memory.FillRAM[0x2131] & 0x20); |
444 | |
445 | DrawBackdrop(); |
446 | } |
447 | |
448 | void S9xUpdateScreen (void) |
449 | { |
450 | if (IPPU.OBJChanged || IPPU.InterlaceOBJ) |
451 | SetupOBJ(); |
452 | |
453 | // XXX: Check ForceBlank? Or anything else? |
454 | PPU.RangeTimeOver |= GFX.OBJLines[GFX.EndY].RTOFlags; |
455 | |
456 | GFX.StartY = IPPU.PreviousLine; |
457 | if ((GFX.EndY = IPPU.CurrentLine - 1) >= PPU.ScreenHeight) |
458 | GFX.EndY = PPU.ScreenHeight - 1; |
459 | |
460 | if (!PPU.ForcedBlanking) |
461 | { |
462 | // If force blank, may as well completely skip all this. We only did |
463 | // the OBJ because (AFAWK) the RTO flags are updated even during force-blank. |
464 | |
465 | if (PPU.RecomputeClipWindows) |
466 | { |
467 | S9xComputeClipWindows(); |
468 | PPU.RecomputeClipWindows = FALSE; |
469 | } |
470 | |
471 | if (Settings.SupportHiRes) |
472 | { |
473 | if (!IPPU.DoubleWidthPixels && (PPU.BGMode == 5 || PPU.BGMode == 6 || IPPU.PseudoHires)) |
474 | { |
475 | #ifdef USE_OPENGL |
476 | if (Settings.OpenGLEnable && GFX.RealPPL == 256) |
477 | { |
478 | // Have to back out of the speed up hack where the low res. |
479 | // SNES image was rendered into a 256x239 sized buffer, |
480 | // ignoring the true, larger size of the buffer. |
481 | GFX.RealPPL = GFX.Pitch >> 1; |
482 | |
483 | for (int32 y = (int32) GFX.StartY - 1; y >= 0; y--) |
484 | { |
485 | uint16 *p = GFX.Screen + y * GFX.PPL + 255; |
486 | uint16 *q = GFX.Screen + y * GFX.RealPPL + 510; |
487 | |
488 | for (int x = 255; x >= 0; x--, p--, q -= 2) |
489 | *q = *(q + 1) = *p; |
490 | } |
491 | |
492 | GFX.PPL = GFX.RealPPL; // = GFX.Pitch >> 1 above |
493 | } |
494 | else |
495 | #endif |
496 | // Have to back out of the regular speed hack |
497 | for (uint32 y = 0; y < GFX.StartY; y++) |
498 | { |
499 | uint16 *p = GFX.Screen + y * GFX.PPL + 255; |
500 | uint16 *q = GFX.Screen + y * GFX.PPL + 510; |
501 | |
502 | for (int x = 255; x >= 0; x--, p--, q -= 2) |
503 | *q = *(q + 1) = *p; |
504 | } |
505 | |
506 | IPPU.DoubleWidthPixels = TRUE; |
507 | IPPU.RenderedScreenWidth = 512; |
508 | } |
509 | |
510 | if (!IPPU.DoubleHeightPixels && IPPU.Interlace && (PPU.BGMode == 5 || PPU.BGMode == 6)) |
511 | { |
512 | IPPU.DoubleHeightPixels = TRUE; |
513 | IPPU.RenderedScreenHeight = PPU.ScreenHeight << 1; |
514 | GFX.PPL = GFX.RealPPL << 1; |
515 | GFX.DoInterlace = 2; |
516 | |
517 | for (int32 y = (int32) GFX.StartY - 2; y >= 0; y--) |
518 | memmove(GFX.Screen + (y + 1) * GFX.PPL, GFX.Screen + y * GFX.RealPPL, GFX.PPL * sizeof(uint16)); |
519 | } |
520 | } |
521 | |
522 | if ((Memory.FillRAM[0x2130] & 0x30) != 0x30 && (Memory.FillRAM[0x2131] & 0x3f)) |
523 | GFX.FixedColour = BUILD_PIXEL(IPPU.XB[PPU.FixedColourRed], IPPU.XB[PPU.FixedColourGreen], IPPU.XB[PPU.FixedColourBlue]); |
524 | |
525 | if (PPU.BGMode == 5 || PPU.BGMode == 6 || IPPU.PseudoHires || |
526 | ((Memory.FillRAM[0x2130] & 0x30) != 0x30 && (Memory.FillRAM[0x2130] & 2) && (Memory.FillRAM[0x2131] & 0x3f) && (Memory.FillRAM[0x212d] & 0x1f))) |
527 | // If hires (Mode 5/6 or pseudo-hires) or math is to be done |
528 | // involving the subscreen, then we need to render the subscreen... |
529 | RenderScreen(TRUE); |
530 | |
531 | RenderScreen(FALSE); |
532 | } |
533 | else |
534 | { |
535 | const uint16 black = BUILD_PIXEL(0, 0, 0); |
536 | |
537 | GFX.S = GFX.Screen + GFX.StartY * GFX.PPL; |
538 | if (GFX.DoInterlace && GFX.InterlaceFrame) |
539 | GFX.S += GFX.RealPPL; |
540 | |
541 | for (uint32 l = GFX.StartY; l <= GFX.EndY; l++, GFX.S += GFX.PPL) |
542 | for (int x = 0; x < IPPU.RenderedScreenWidth; x++) |
543 | GFX.S[x] = black; |
544 | } |
545 | |
546 | IPPU.PreviousLine = IPPU.CurrentLine; |
547 | } |
548 | |
549 | static void SetupOBJ (void) |
550 | { |
551 | int SmallWidth, SmallHeight, LargeWidth, LargeHeight; |
552 | |
553 | switch (PPU.OBJSizeSelect) |
554 | { |
555 | case 0: |
556 | SmallWidth = SmallHeight = 8; |
557 | LargeWidth = LargeHeight = 16; |
558 | break; |
559 | |
560 | case 1: |
561 | SmallWidth = SmallHeight = 8; |
562 | LargeWidth = LargeHeight = 32; |
563 | break; |
564 | |
565 | case 2: |
566 | SmallWidth = SmallHeight = 8; |
567 | LargeWidth = LargeHeight = 64; |
568 | break; |
569 | |
570 | case 3: |
571 | SmallWidth = SmallHeight = 16; |
572 | LargeWidth = LargeHeight = 32; |
573 | break; |
574 | |
575 | case 4: |
576 | SmallWidth = SmallHeight = 16; |
577 | LargeWidth = LargeHeight = 64; |
578 | break; |
579 | |
580 | case 5: |
581 | default: |
582 | SmallWidth = SmallHeight = 32; |
583 | LargeWidth = LargeHeight = 64; |
584 | break; |
585 | |
586 | case 6: |
587 | SmallWidth = 16; SmallHeight = 32; |
588 | LargeWidth = 32; LargeHeight = 64; |
589 | break; |
590 | |
591 | case 7: |
592 | SmallWidth = 16; SmallHeight = 32; |
593 | LargeWidth = LargeHeight = 32; |
594 | break; |
595 | } |
596 | |
597 | int inc = IPPU.InterlaceOBJ ? 2 : 1; |
598 | |
599 | int startline = (IPPU.InterlaceOBJ && GFX.InterlaceFrame) ? 1 : 0; |
600 | |
601 | // OK, we have three cases here. Either there's no priority, priority is |
602 | // normal FirstSprite, or priority is FirstSprite+Y. The first two are |
603 | // easy, the last is somewhat more ... interesting. So we split them up. |
604 | |
605 | int Height; |
606 | uint8 S; |
607 | |
608 | if (!PPU.OAMPriorityRotation || !(PPU.OAMFlip & PPU.OAMAddr & 1)) // normal case |
609 | { |
610 | uint8 LineOBJ[SNES_HEIGHT_EXTENDED]; |
611 | memset(LineOBJ, 0, sizeof(LineOBJ)); |
612 | |
613 | for (int i = 0; i < SNES_HEIGHT_EXTENDED; i++) |
614 | { |
615 | GFX.OBJLines[i].RTOFlags = 0; |
616 | GFX.OBJLines[i].Tiles = Settings.MaxSpriteTilesPerLine; |
617 | for (int j = 0; j < 32; j++) |
618 | GFX.OBJLines[i].OBJ[j].Sprite = -1; |
619 | } |
620 | |
621 | uint8 FirstSprite = PPU.FirstSprite; |
622 | S = FirstSprite; |
623 | |
624 | do |
625 | { |
626 | if (PPU.OBJ[S].Size) |
627 | { |
628 | GFX.OBJWidths[S] = LargeWidth; |
629 | Height = LargeHeight; |
630 | } |
631 | else |
632 | { |
633 | GFX.OBJWidths[S] = SmallWidth; |
634 | Height = SmallHeight; |
635 | } |
636 | |
637 | int HPos = PPU.OBJ[S].HPos; |
638 | if (HPos == -256) |
639 | HPos = 0; |
640 | |
641 | if (HPos > -GFX.OBJWidths[S] && HPos <= 256) |
642 | { |
643 | if (HPos < 0) |
644 | GFX.OBJVisibleTiles[S] = (GFX.OBJWidths[S] + HPos + 7) >> 3; |
645 | else |
646 | if (HPos + GFX.OBJWidths[S] > 255) |
647 | GFX.OBJVisibleTiles[S] = (256 - HPos + 7) >> 3; |
648 | else |
649 | GFX.OBJVisibleTiles[S] = GFX.OBJWidths[S] >> 3; |
650 | |
651 | for (uint8 line = startline, Y = (uint8) (PPU.OBJ[S].VPos & 0xff); line < Height; Y++, line += inc) |
652 | { |
653 | if (Y >= SNES_HEIGHT_EXTENDED) |
654 | continue; |
655 | |
656 | if (LineOBJ[Y] >= 32) |
657 | { |
658 | GFX.OBJLines[Y].RTOFlags |= 0x40; |
659 | continue; |
660 | } |
661 | |
662 | GFX.OBJLines[Y].Tiles -= GFX.OBJVisibleTiles[S]; |
663 | if (GFX.OBJLines[Y].Tiles < 0) |
664 | GFX.OBJLines[Y].RTOFlags |= 0x80; |
665 | |
666 | GFX.OBJLines[Y].OBJ[LineOBJ[Y]].Sprite = S; |
667 | if (PPU.OBJ[S].VFlip) |
668 | // Yes, Width not Height. It so happens that the |
669 | // sprites with H=2*W flip as two WxW sprites. |
670 | GFX.OBJLines[Y].OBJ[LineOBJ[Y]].Line = line ^ (GFX.OBJWidths[S] - 1); |
671 | else |
672 | GFX.OBJLines[Y].OBJ[LineOBJ[Y]].Line = line; |
673 | |
674 | LineOBJ[Y]++; |
675 | } |
676 | } |
677 | |
678 | S = (S + 1) & 0x7f; |
679 | } while (S != FirstSprite); |
680 | |
681 | for (int Y = 1; Y < SNES_HEIGHT_EXTENDED; Y++) |
682 | GFX.OBJLines[Y].RTOFlags |= GFX.OBJLines[Y - 1].RTOFlags; |
683 | } |
684 | else // evil FirstSprite+Y case |
685 | { |
686 | // First, find out which sprites are on which lines |
687 | uint8 OBJOnLine[SNES_HEIGHT_EXTENDED][128]; |
688 | // memset(OBJOnLine, 0, sizeof(OBJOnLine)); |
689 | /* Hold on here, that's a lot of bytes to initialise at once! |
690 | * So we only initialise them per line, as needed. [Neb] |
691 | * Bonus: We can quickly avoid looping if a line has no OBJs. |
692 | */ |
693 | bool8 AnyOBJOnLine[SNES_HEIGHT_EXTENDED]; |
694 | memset(AnyOBJOnLine, FALSE, sizeof(AnyOBJOnLine)); // better |
695 | |
696 | for (S = 0; S < 128; S++) |
697 | { |
698 | if (PPU.OBJ[S].Size) |
699 | { |
700 | GFX.OBJWidths[S] = LargeWidth; |
701 | Height = LargeHeight; |
702 | } |
703 | else |
704 | { |
705 | GFX.OBJWidths[S] = SmallWidth; |
706 | Height = SmallHeight; |
707 | } |
708 | |
709 | int HPos = PPU.OBJ[S].HPos; |
710 | if (HPos == -256) |
711 | HPos = 256; |
712 | |
713 | if (HPos > -GFX.OBJWidths[S] && HPos <= 256) |
714 | { |
715 | if (HPos < 0) |
716 | GFX.OBJVisibleTiles[S] = (GFX.OBJWidths[S] + HPos + 7) >> 3; |
717 | else |
718 | if (HPos + GFX.OBJWidths[S] >= 257) |
719 | GFX.OBJVisibleTiles[S] = (257 - HPos + 7) >> 3; |
720 | else |
721 | GFX.OBJVisibleTiles[S] = GFX.OBJWidths[S] >> 3; |
722 | |
723 | for (uint8 line = startline, Y = (uint8) (PPU.OBJ[S].VPos & 0xff); line < Height; Y++, line += inc) |
724 | { |
725 | if (Y >= SNES_HEIGHT_EXTENDED) |
726 | continue; |
727 | |
728 | if (!AnyOBJOnLine[Y]) { |
729 | memset(OBJOnLine[Y], 0, sizeof(OBJOnLine[Y])); |
730 | AnyOBJOnLine[Y] = TRUE; |
731 | } |
732 | |
733 | if (PPU.OBJ[S].VFlip) |
734 | // Yes, Width not Height. It so happens that the |
735 | // sprites with H=2*W flip as two WxW sprites. |
736 | OBJOnLine[Y][S] = (line ^ (GFX.OBJWidths[S] - 1)) | 0x80; |
737 | else |
738 | OBJOnLine[Y][S] = line | 0x80; |
739 | } |
740 | } |
741 | } |
742 | |
743 | // Now go through and pull out those OBJ that are actually visible. |
744 | int j; |
745 | for (int Y = 0; Y < SNES_HEIGHT_EXTENDED; Y++) |
746 | { |
747 | GFX.OBJLines[Y].RTOFlags = Y ? GFX.OBJLines[Y - 1].RTOFlags : 0; |
748 | GFX.OBJLines[Y].Tiles = Settings.MaxSpriteTilesPerLine; |
749 | |
750 | uint8 FirstSprite = (PPU.FirstSprite + Y) & 0x7f; |
751 | S = FirstSprite; |
752 | j = 0; |
753 | |
754 | if (AnyOBJOnLine[Y]) |
755 | { |
756 | do |
757 | { |
758 | if (OBJOnLine[Y][S]) |
759 | { |
760 | if (j >= 32) |
761 | { |
762 | GFX.OBJLines[Y].RTOFlags |= 0x40; |
763 | break; |
764 | } |
765 | |
766 | GFX.OBJLines[Y].Tiles -= GFX.OBJVisibleTiles[S]; |
767 | if (GFX.OBJLines[Y].Tiles < 0) |
768 | GFX.OBJLines[Y].RTOFlags |= 0x80; |
769 | GFX.OBJLines[Y].OBJ[j].Sprite = S; |
770 | GFX.OBJLines[Y].OBJ[j++].Line = OBJOnLine[Y][S] & ~0x80; |
771 | } |
772 | |
773 | S = (S + 1) & 0x7f; |
774 | } while (S != FirstSprite); |
775 | } |
776 | |
777 | if (j < 32) |
778 | GFX.OBJLines[Y].OBJ[j].Sprite = -1; |
779 | } |
780 | } |
781 | |
782 | IPPU.OBJChanged = FALSE; |
783 | } |
784 | |
785 | #if defined(__GNUC__) && !defined(__clang__) |
786 | #pragma GCC push_options |
787 | #pragma GCC optimize ("no-tree-vrp") |
788 | #endif |
789 | static void DrawOBJS (int D) |
790 | { |
791 | void (*DrawTile) (uint32, uint32, uint32, uint32) = NULL; |
792 | void (*DrawClippedTile) (uint32, uint32, uint32, uint32, uint32, uint32) = NULL; |
793 | |
794 | int PixWidth = IPPU.DoubleWidthPixels ? 2 : 1; |
795 | BG.InterlaceLine = GFX.InterlaceFrame ? 8 : 0; |
796 | GFX.Z1 = 2; |
797 | |
798 | for (uint32 Y = GFX.StartY, Offset = Y * GFX.PPL; Y <= GFX.EndY; Y++, Offset += GFX.PPL) |
799 | { |
800 | int I = 0; |
801 | int tiles = GFX.OBJLines[Y].Tiles; |
802 | |
803 | for (int S = GFX.OBJLines[Y].OBJ[I].Sprite; S >= 0 && I < 32; S = GFX.OBJLines[Y].OBJ[++I].Sprite) |
804 | { |
805 | tiles += GFX.OBJVisibleTiles[S]; |
806 | if (tiles <= 0) |
807 | continue; |
808 | |
809 | int BaseTile = (((GFX.OBJLines[Y].OBJ[I].Line << 1) + (PPU.OBJ[S].Name & 0xf0)) & 0xf0) | (PPU.OBJ[S].Name & 0x100) | (PPU.OBJ[S].Palette << 10); |
810 | int TileX = PPU.OBJ[S].Name & 0x0f; |
811 | int TileLine = (GFX.OBJLines[Y].OBJ[I].Line & 7) * 8; |
812 | int TileInc = 1; |
813 | |
814 | if (PPU.OBJ[S].HFlip) |
815 | { |
816 | TileX = (TileX + (GFX.OBJWidths[S] >> 3) - 1) & 0x0f; |
817 | BaseTile |= H_FLIP; |
818 | TileInc = -1; |
819 | } |
820 | |
821 | GFX.Z2 = D + PPU.OBJ[S].Priority * 4; |
822 | |
823 | int DrawMode = 3; |
824 | int clip = 0, next_clip = -1000; |
825 | int X = PPU.OBJ[S].HPos; |
826 | if (X == -256) |
827 | X = 256; |
828 | |
829 | for (int t = tiles, O = Offset + X * PixWidth; X <= 256 && X < PPU.OBJ[S].HPos + GFX.OBJWidths[S]; TileX = (TileX + TileInc) & 0x0f, X += 8, O += 8 * PixWidth) |
830 | { |
831 | if (X < -7 || --t < 0 || X == 256) |
832 | continue; |
833 | |
834 | for (int x = X; x < X + 8;) |
835 | { |
836 | if (x >= next_clip) |
837 | { |
838 | for (; clip < GFX.Clip[4].Count && GFX.Clip[4].Left[clip] <= x; clip++) ; |
839 | if (clip == 0 || x >= GFX.Clip[4].Right[clip - 1]) |
840 | { |
841 | DrawMode = 0; |
842 | next_clip = ((clip < GFX.Clip[4].Count) ? GFX.Clip[4].Left[clip] : 1000); |
843 | } |
844 | else |
845 | { |
846 | DrawMode = GFX.Clip[4].DrawMode[clip - 1]; |
847 | next_clip = GFX.Clip[4].Right[clip - 1]; |
848 | GFX.ClipColors = !(DrawMode & 1); |
849 | |
850 | if (BG.EnableMath && (PPU.OBJ[S].Palette & 4) && (DrawMode & 2)) |
851 | { |
852 | DrawTile = GFX.DrawTileMath; |
853 | DrawClippedTile = GFX.DrawClippedTileMath; |
854 | } |
855 | else |
856 | { |
857 | DrawTile = GFX.DrawTileNomath; |
858 | DrawClippedTile = GFX.DrawClippedTileNomath; |
859 | } |
860 | } |
861 | } |
862 | |
863 | if (x == X && x + 8 < next_clip) |
864 | { |
865 | if (DrawMode) |
866 | DrawTile(BaseTile | TileX, O, TileLine, 1); |
867 | x += 8; |
868 | } |
869 | else |
870 | { |
871 | int w = (next_clip <= X + 8) ? next_clip - x : X + 8 - x; |
872 | if (DrawMode) |
873 | DrawClippedTile(BaseTile | TileX, O, x - X, w, TileLine, 1); |
874 | x += w; |
875 | } |
876 | } |
877 | } |
878 | } |
879 | } |
880 | } |
881 | #if defined(__GNUC__) && !defined(__clang__) |
882 | #pragma GCC pop_options |
883 | #endif |
884 | |
885 | static void DrawBackground (int bg, uint8 Zh, uint8 Zl) |
886 | { |
887 | BG.TileAddress = PPU.BG[bg].NameBase << 1; |
888 | |
889 | uint32 Tile; |
890 | uint16 *SC0, *SC1, *SC2, *SC3; |
891 | |
892 | SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1]; |
893 | SC1 = (PPU.BG[bg].SCSize & 1) ? SC0 + 1024 : SC0; |
894 | if (SC1 >= (uint16 *) (Memory.VRAM + 0x10000)) |
895 | SC1 -= 0x8000; |
896 | SC2 = (PPU.BG[bg].SCSize & 2) ? SC1 + 1024 : SC0; |
897 | if (SC2 >= (uint16 *) (Memory.VRAM + 0x10000)) |
898 | SC2 -= 0x8000; |
899 | SC3 = (PPU.BG[bg].SCSize & 1) ? SC2 + 1024 : SC2; |
900 | if (SC3 >= (uint16 *) (Memory.VRAM + 0x10000)) |
901 | SC3 -= 0x8000; |
902 | |
903 | uint32 Lines; |
904 | int OffsetMask = (BG.TileSizeH == 16) ? 0x3ff : 0x1ff; |
905 | int OffsetShift = (BG.TileSizeV == 16) ? 4 : 3; |
906 | int PixWidth = IPPU.DoubleWidthPixels ? 2 : 1; |
907 | bool8 HiresInterlace = IPPU.Interlace && IPPU.DoubleWidthPixels; |
908 | |
909 | void (*DrawTile) (uint32, uint32, uint32, uint32); |
910 | void (*DrawClippedTile) (uint32, uint32, uint32, uint32, uint32, uint32); |
911 | |
912 | for (int clip = 0; clip < GFX.Clip[bg].Count; clip++) |
913 | { |
914 | GFX.ClipColors = !(GFX.Clip[bg].DrawMode[clip] & 1); |
915 | |
916 | if (BG.EnableMath && (GFX.Clip[bg].DrawMode[clip] & 2)) |
917 | { |
918 | DrawTile = GFX.DrawTileMath; |
919 | DrawClippedTile = GFX.DrawClippedTileMath; |
920 | } |
921 | else |
922 | { |
923 | DrawTile = GFX.DrawTileNomath; |
924 | DrawClippedTile = GFX.DrawClippedTileNomath; |
925 | } |
926 | |
927 | for (uint32 Y = GFX.StartY; Y <= GFX.EndY; Y += Lines) |
928 | { |
929 | uint32 Y2 = HiresInterlace ? Y * 2 + GFX.InterlaceFrame : Y; |
930 | uint32 VOffset = LineData[Y].BG[bg].VOffset + (HiresInterlace ? 1 : 0); |
931 | uint32 HOffset = LineData[Y].BG[bg].HOffset; |
932 | int VirtAlign = ((Y2 + VOffset) & 7) >> (HiresInterlace ? 1 : 0); |
933 | |
934 | for (Lines = 1; Lines < GFX.LinesPerTile - VirtAlign; Lines++) |
935 | { |
936 | if ((VOffset != LineData[Y + Lines].BG[bg].VOffset) || (HOffset != LineData[Y + Lines].BG[bg].HOffset)) |
937 | break; |
938 | } |
939 | |
940 | if (Y + Lines > GFX.EndY) |
941 | Lines = GFX.EndY - Y + 1; |
942 | |
943 | VirtAlign <<= 3; |
944 | |
945 | uint32 t1, t2; |
946 | uint32 TilemapRow = (VOffset + Y2) >> OffsetShift; |
947 | BG.InterlaceLine = ((VOffset + Y2) & 1) << 3; |
948 | |
949 | if ((VOffset + Y2) & 8) |
950 | { |
951 | t1 = 16; |
952 | t2 = 0; |
953 | } |
954 | else |
955 | { |
956 | t1 = 0; |
957 | t2 = 16; |
958 | } |
959 | |
960 | uint16 *b1, *b2; |
961 | |
962 | if (TilemapRow & 0x20) |
963 | { |
964 | b1 = SC2; |
965 | b2 = SC3; |
966 | } |
967 | else |
968 | { |
969 | b1 = SC0; |
970 | b2 = SC1; |
971 | } |
972 | |
973 | b1 += (TilemapRow & 0x1f) << 5; |
974 | b2 += (TilemapRow & 0x1f) << 5; |
975 | |
976 | uint32 Left = GFX.Clip[bg].Left[clip]; |
977 | uint32 Right = GFX.Clip[bg].Right[clip]; |
978 | uint32 Offset = Left * PixWidth + Y * GFX.PPL; |
979 | uint32 HPos = (HOffset + Left) & OffsetMask; |
980 | uint32 HTile = HPos >> 3; |
981 | uint16 *t; |
982 | |
983 | if (BG.TileSizeH == 8) |
984 | { |
985 | if (HTile > 31) |
986 | t = b2 + (HTile & 0x1f); |
987 | else |
988 | t = b1 + HTile; |
989 | } |
990 | else |
991 | { |
992 | if (HTile > 63) |
993 | t = b2 + ((HTile >> 1) & 0x1f); |
994 | else |
995 | t = b1 + (HTile >> 1); |
996 | } |
997 | |
998 | uint32 Width = Right - Left; |
999 | |
1000 | if (HPos & 7) |
1001 | { |
1002 | uint32 l = HPos & 7; |
1003 | uint32 w = 8 - l; |
1004 | if (w > Width) |
1005 | w = Width; |
1006 | |
1007 | Offset -= l * PixWidth; |
1008 | Tile = READ_WORD(t); |
1009 | GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl; |
1010 | |
1011 | if (BG.TileSizeV == 16) |
1012 | Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1)); |
1013 | |
1014 | if (BG.TileSizeH == 8) |
1015 | { |
1016 | DrawClippedTile(Tile, Offset, l, w, VirtAlign, Lines); |
1017 | t++; |
1018 | if (HTile == 31) |
1019 | t = b2; |
1020 | else |
1021 | if (HTile == 63) |
1022 | t = b1; |
1023 | } |
1024 | else |
1025 | { |
1026 | if (!(Tile & H_FLIP)) |
1027 | DrawClippedTile(TILE_PLUS(Tile, (HTile & 1)), Offset, l, w, VirtAlign, Lines); |
1028 | else |
1029 | DrawClippedTile(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, l, w, VirtAlign, Lines); |
1030 | t += HTile & 1; |
1031 | if (HTile == 63) |
1032 | t = b2; |
1033 | else |
1034 | if (HTile == 127) |
1035 | t = b1; |
1036 | } |
1037 | |
1038 | HTile++; |
1039 | Offset += 8 * PixWidth; |
1040 | Width -= w; |
1041 | } |
1042 | |
1043 | while (Width >= 8) |
1044 | { |
1045 | Tile = READ_WORD(t); |
1046 | GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl; |
1047 | |
1048 | if (BG.TileSizeV == 16) |
1049 | Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1)); |
1050 | |
1051 | if (BG.TileSizeH == 8) |
1052 | { |
1053 | DrawTile(Tile, Offset, VirtAlign, Lines); |
1054 | t++; |
1055 | if (HTile == 31) |
1056 | t = b2; |
1057 | else |
1058 | if (HTile == 63) |
1059 | t = b1; |
1060 | } |
1061 | else |
1062 | { |
1063 | if (!(Tile & H_FLIP)) |
1064 | DrawTile(TILE_PLUS(Tile, (HTile & 1)), Offset, VirtAlign, Lines); |
1065 | else |
1066 | DrawTile(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, VirtAlign, Lines); |
1067 | t += HTile & 1; |
1068 | if (HTile == 63) |
1069 | t = b2; |
1070 | else |
1071 | if (HTile == 127) |
1072 | t = b1; |
1073 | } |
1074 | |
1075 | HTile++; |
1076 | Offset += 8 * PixWidth; |
1077 | Width -= 8; |
1078 | } |
1079 | |
1080 | if (Width) |
1081 | { |
1082 | Tile = READ_WORD(t); |
1083 | GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl; |
1084 | |
1085 | if (BG.TileSizeV == 16) |
1086 | Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1)); |
1087 | |
1088 | if (BG.TileSizeH == 8) |
1089 | DrawClippedTile(Tile, Offset, 0, Width, VirtAlign, Lines); |
1090 | else |
1091 | { |
1092 | if (!(Tile & H_FLIP)) |
1093 | DrawClippedTile(TILE_PLUS(Tile, (HTile & 1)), Offset, 0, Width, VirtAlign, Lines); |
1094 | else |
1095 | DrawClippedTile(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, 0, Width, VirtAlign, Lines); |
1096 | } |
1097 | } |
1098 | } |
1099 | } |
1100 | } |
1101 | |
1102 | static void DrawBackgroundMosaic (int bg, uint8 Zh, uint8 Zl) |
1103 | { |
1104 | BG.TileAddress = PPU.BG[bg].NameBase << 1; |
1105 | |
1106 | uint32 Tile; |
1107 | uint16 *SC0, *SC1, *SC2, *SC3; |
1108 | |
1109 | SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1]; |
1110 | SC1 = (PPU.BG[bg].SCSize & 1) ? SC0 + 1024 : SC0; |
1111 | if (SC1 >= (uint16 *) (Memory.VRAM + 0x10000)) |
1112 | SC1 -= 0x8000; |
1113 | SC2 = (PPU.BG[bg].SCSize & 2) ? SC1 + 1024 : SC0; |
1114 | if (SC2 >= (uint16 *) (Memory.VRAM + 0x10000)) |
1115 | SC2 -= 0x8000; |
1116 | SC3 = (PPU.BG[bg].SCSize & 1) ? SC2 + 1024 : SC2; |
1117 | if (SC3 >= (uint16 *) (Memory.VRAM + 0x10000)) |
1118 | SC3 -= 0x8000; |
1119 | |
1120 | int Lines; |
1121 | int OffsetMask = (BG.TileSizeH == 16) ? 0x3ff : 0x1ff; |
1122 | int OffsetShift = (BG.TileSizeV == 16) ? 4 : 3; |
1123 | int PixWidth = IPPU.DoubleWidthPixels ? 2 : 1; |
1124 | bool8 HiresInterlace = IPPU.Interlace && IPPU.DoubleWidthPixels; |
1125 | |
1126 | void (*DrawPix) (uint32, uint32, uint32, uint32, uint32, uint32); |
1127 | |
1128 | int MosaicStart = ((uint32) GFX.StartY - PPU.MosaicStart) % PPU.Mosaic; |
1129 | |
1130 | for (int clip = 0; clip < GFX.Clip[bg].Count; clip++) |
1131 | { |
1132 | GFX.ClipColors = !(GFX.Clip[bg].DrawMode[clip] & 1); |
1133 | |
1134 | if (BG.EnableMath && (GFX.Clip[bg].DrawMode[clip] & 2)) |
1135 | DrawPix = GFX.DrawMosaicPixelMath; |
1136 | else |
1137 | DrawPix = GFX.DrawMosaicPixelNomath; |
1138 | |
1139 | for (uint32 Y = GFX.StartY - MosaicStart; Y <= GFX.EndY; Y += PPU.Mosaic) |
1140 | { |
1141 | uint32 Y2 = HiresInterlace ? Y * 2 : Y; |
1142 | uint32 VOffset = LineData[Y + MosaicStart].BG[bg].VOffset + (HiresInterlace ? 1 : 0); |
1143 | uint32 HOffset = LineData[Y + MosaicStart].BG[bg].HOffset; |
1144 | |
1145 | Lines = PPU.Mosaic - MosaicStart; |
1146 | if (Y + MosaicStart + Lines > GFX.EndY) |
1147 | Lines = GFX.EndY - Y - MosaicStart + 1; |
1148 | |
1149 | int VirtAlign = (((Y2 + VOffset) & 7) >> (HiresInterlace ? 1 : 0)) << 3; |
1150 | |
1151 | uint32 t1, t2; |
1152 | uint32 TilemapRow = (VOffset + Y2) >> OffsetShift; |
1153 | BG.InterlaceLine = ((VOffset + Y2) & 1) << 3; |
1154 | |
1155 | if ((VOffset + Y2) & 8) |
1156 | { |
1157 | t1 = 16; |
1158 | t2 = 0; |
1159 | } |
1160 | else |
1161 | { |
1162 | t1 = 0; |
1163 | t2 = 16; |
1164 | } |
1165 | |
1166 | uint16 *b1, *b2; |
1167 | |
1168 | if (TilemapRow & 0x20) |
1169 | { |
1170 | b1 = SC2; |
1171 | b2 = SC3; |
1172 | } |
1173 | else |
1174 | { |
1175 | b1 = SC0; |
1176 | b2 = SC1; |
1177 | } |
1178 | |
1179 | b1 += (TilemapRow & 0x1f) << 5; |
1180 | b2 += (TilemapRow & 0x1f) << 5; |
1181 | |
1182 | uint32 Left = GFX.Clip[bg].Left[clip]; |
1183 | uint32 Right = GFX.Clip[bg].Right[clip]; |
1184 | uint32 Offset = Left * PixWidth + (Y + MosaicStart) * GFX.PPL; |
1185 | uint32 HPos = (HOffset + Left - (Left % PPU.Mosaic)) & OffsetMask; |
1186 | uint32 HTile = HPos >> 3; |
1187 | uint16 *t; |
1188 | |
1189 | if (BG.TileSizeH == 8) |
1190 | { |
1191 | if (HTile > 31) |
1192 | t = b2 + (HTile & 0x1f); |
1193 | else |
1194 | t = b1 + HTile; |
1195 | } |
1196 | else |
1197 | { |
1198 | if (HTile > 63) |
1199 | t = b2 + ((HTile >> 1) & 0x1f); |
1200 | else |
1201 | t = b1 + (HTile >> 1); |
1202 | } |
1203 | |
1204 | uint32 Width = Right - Left; |
1205 | |
1206 | HPos &= 7; |
1207 | |
1208 | while (Left < Right) |
1209 | { |
1210 | uint32 w = PPU.Mosaic - (Left % PPU.Mosaic); |
1211 | if (w > Width) |
1212 | w = Width; |
1213 | |
1214 | Tile = READ_WORD(t); |
1215 | GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl; |
1216 | |
1217 | if (BG.TileSizeV == 16) |
1218 | Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1)); |
1219 | |
1220 | if (BG.TileSizeH == 8) |
1221 | DrawPix(Tile, Offset, VirtAlign, HPos & 7, w, Lines); |
1222 | else |
1223 | { |
1224 | if (!(Tile & H_FLIP)) |
1225 | DrawPix(TILE_PLUS(Tile, (HTile & 1)), Offset, VirtAlign, HPos & 7, w, Lines); |
1226 | else |
1227 | DrawPix(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, VirtAlign, HPos & 7, w, Lines); |
1228 | } |
1229 | |
1230 | HPos += PPU.Mosaic; |
1231 | |
1232 | while (HPos >= 8) |
1233 | { |
1234 | HPos -= 8; |
1235 | |
1236 | if (BG.TileSizeH == 8) |
1237 | { |
1238 | t++; |
1239 | if (HTile == 31) |
1240 | t = b2; |
1241 | else |
1242 | if (HTile == 63) |
1243 | t = b1; |
1244 | } |
1245 | else |
1246 | { |
1247 | t += HTile & 1; |
1248 | if (HTile == 63) |
1249 | t = b2; |
1250 | else |
1251 | if (HTile == 127) |
1252 | t = b1; |
1253 | } |
1254 | |
1255 | HTile++; |
1256 | } |
1257 | |
1258 | Offset += w * PixWidth; |
1259 | Width -= w; |
1260 | Left += w; |
1261 | } |
1262 | |
1263 | MosaicStart = 0; |
1264 | } |
1265 | } |
1266 | } |
1267 | |
1268 | static void DrawBackgroundOffset (int bg, uint8 Zh, uint8 Zl, int VOffOff) |
1269 | { |
1270 | BG.TileAddress = PPU.BG[bg].NameBase << 1; |
1271 | |
1272 | uint32 Tile; |
1273 | uint16 *SC0, *SC1, *SC2, *SC3; |
1274 | uint16 *BPS0, *BPS1, *BPS2, *BPS3; |
1275 | |
1276 | BPS0 = (uint16 *) &Memory.VRAM[PPU.BG[2].SCBase << 1]; |
1277 | BPS1 = (PPU.BG[2].SCSize & 1) ? BPS0 + 1024 : BPS0; |
1278 | if (BPS1 >= (uint16 *) (Memory.VRAM + 0x10000)) |
1279 | BPS1 -= 0x8000; |
1280 | BPS2 = (PPU.BG[2].SCSize & 2) ? BPS1 + 1024 : BPS0; |
1281 | if (BPS2 >= (uint16 *) (Memory.VRAM + 0x10000)) |
1282 | BPS2 -= 0x8000; |
1283 | BPS3 = (PPU.BG[2].SCSize & 1) ? BPS2 + 1024 : BPS2; |
1284 | if (BPS3 >= (uint16 *) (Memory.VRAM + 0x10000)) |
1285 | BPS3 -= 0x8000; |
1286 | |
1287 | SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1]; |
1288 | SC1 = (PPU.BG[bg].SCSize & 1) ? SC0 + 1024 : SC0; |
1289 | if (SC1 >= (uint16 *) (Memory.VRAM + 0x10000)) |
1290 | SC1 -= 0x8000; |
1291 | SC2 = (PPU.BG[bg].SCSize & 2) ? SC1 + 1024 : SC0; |
1292 | if (SC2 >= (uint16 *) (Memory.VRAM + 0x10000)) |
1293 | SC2 -= 0x8000; |
1294 | SC3 = (PPU.BG[bg].SCSize & 1) ? SC2 + 1024 : SC2; |
1295 | if (SC3 >= (uint16 *) (Memory.VRAM + 0x10000)) |
1296 | SC3 -= 0x8000; |
1297 | |
1298 | int OffsetMask = (BG.TileSizeH == 16) ? 0x3ff : 0x1ff; |
1299 | int OffsetShift = (BG.TileSizeV == 16) ? 4 : 3; |
1300 | int Offset2Mask = (BG.OffsetSizeH == 16) ? 0x3ff : 0x1ff; |
1301 | int Offset2Shift = (BG.OffsetSizeV == 16) ? 4 : 3; |
1302 | int OffsetEnableMask = 0x2000 << bg; |
1303 | int PixWidth = IPPU.DoubleWidthPixels ? 2 : 1; |
1304 | bool8 HiresInterlace = IPPU.Interlace && IPPU.DoubleWidthPixels; |
1305 | |
1306 | void (*DrawClippedTile) (uint32, uint32, uint32, uint32, uint32, uint32); |
1307 | |
1308 | for (int clip = 0; clip < GFX.Clip[bg].Count; clip++) |
1309 | { |
1310 | GFX.ClipColors = !(GFX.Clip[bg].DrawMode[clip] & 1); |
1311 | |
1312 | if (BG.EnableMath && (GFX.Clip[bg].DrawMode[clip] & 2)) |
1313 | { |
1314 | DrawClippedTile = GFX.DrawClippedTileMath; |
1315 | } |
1316 | else |
1317 | { |
1318 | DrawClippedTile = GFX.DrawClippedTileNomath; |
1319 | } |
1320 | |
1321 | for (uint32 Y = GFX.StartY; Y <= GFX.EndY; Y++) |
1322 | { |
1323 | uint32 Y2 = HiresInterlace ? Y * 2 + GFX.InterlaceFrame : Y; |
1324 | uint32 VOff = LineData[Y].BG[2].VOffset - 1; |
1325 | uint32 HOff = LineData[Y].BG[2].HOffset; |
1326 | uint32 HOffsetRow = VOff >> Offset2Shift; |
1327 | uint32 VOffsetRow = (VOff + VOffOff) >> Offset2Shift; |
1328 | uint16 *s, *s1, *s2; |
1329 | |
1330 | if (HOffsetRow & 0x20) |
1331 | { |
1332 | s1 = BPS2; |
1333 | s2 = BPS3; |
1334 | } |
1335 | else |
1336 | { |
1337 | s1 = BPS0; |
1338 | s2 = BPS1; |
1339 | } |
1340 | |
1341 | s1 += (HOffsetRow & 0x1f) << 5; |
1342 | s2 += (HOffsetRow & 0x1f) << 5; |
1343 | s = ((VOffsetRow & 0x20) ? BPS2 : BPS0) + ((VOffsetRow & 0x1f) << 5); |
1344 | int32 VOffsetOffset = s - s1; |
1345 | |
1346 | uint32 Left = GFX.Clip[bg].Left[clip]; |
1347 | uint32 Right = GFX.Clip[bg].Right[clip]; |
1348 | uint32 Offset = Left * PixWidth + Y * GFX.PPL; |
1349 | uint32 HScroll = LineData[Y].BG[bg].HOffset; |
1350 | bool8 left_edge = (Left < (8 - (HScroll & 7))); |
1351 | uint32 Width = Right - Left; |
1352 | |
1353 | while (Left < Right) |
1354 | { |
1355 | uint32 VOffset, HOffset; |
1356 | |
1357 | if (left_edge) |
1358 | { |
1359 | // SNES cannot do OPT for leftmost tile column |
1360 | VOffset = LineData[Y].BG[bg].VOffset; |
1361 | HOffset = HScroll; |
1362 | left_edge = FALSE; |
1363 | } |
1364 | else |
1365 | { |
1366 | int HOffTile = ((HOff + Left - 1) & Offset2Mask) >> 3; |
1367 | |
1368 | if (BG.OffsetSizeH == 8) |
1369 | { |
1370 | if (HOffTile > 31) |
1371 | s = s2 + (HOffTile & 0x1f); |
1372 | else |
1373 | s = s1 + HOffTile; |
1374 | } |
1375 | else |
1376 | { |
1377 | if (HOffTile > 63) |
1378 | s = s2 + ((HOffTile >> 1) & 0x1f); |
1379 | else |
1380 | s = s1 + (HOffTile >> 1); |
1381 | } |
1382 | |
1383 | uint16 HCellOffset = READ_WORD(s); |
1384 | uint16 VCellOffset; |
1385 | |
1386 | if (VOffOff) |
1387 | VCellOffset = READ_WORD(s + VOffsetOffset); |
1388 | else |
1389 | { |
1390 | if (HCellOffset & 0x8000) |
1391 | { |
1392 | VCellOffset = HCellOffset; |
1393 | HCellOffset = 0; |
1394 | } |
1395 | else |
1396 | VCellOffset = 0; |
1397 | } |
1398 | |
1399 | if (VCellOffset & OffsetEnableMask) |
1400 | VOffset = VCellOffset + 1; |
1401 | else |
1402 | VOffset = LineData[Y].BG[bg].VOffset; |
1403 | |
1404 | if (HCellOffset & OffsetEnableMask) |
1405 | HOffset = (HCellOffset & ~7) | (HScroll & 7); |
1406 | else |
1407 | HOffset = HScroll; |
1408 | } |
1409 | |
1410 | if (HiresInterlace) |
1411 | VOffset++; |
1412 | |
1413 | uint32 t1, t2; |
1414 | int VirtAlign = (((Y2 + VOffset) & 7) >> (HiresInterlace ? 1 : 0)) << 3; |
1415 | int TilemapRow = (VOffset + Y2) >> OffsetShift; |
1416 | BG.InterlaceLine = ((VOffset + Y2) & 1) << 3; |
1417 | |
1418 | if ((VOffset + Y2) & 8) |
1419 | { |
1420 | t1 = 16; |
1421 | t2 = 0; |
1422 | } |
1423 | else |
1424 | { |
1425 | t1 = 0; |
1426 | t2 = 16; |
1427 | } |
1428 | |
1429 | uint16 *b1, *b2; |
1430 | |
1431 | if (TilemapRow & 0x20) |
1432 | { |
1433 | b1 = SC2; |
1434 | b2 = SC3; |
1435 | } |
1436 | else |
1437 | { |
1438 | b1 = SC0; |
1439 | b2 = SC1; |
1440 | } |
1441 | |
1442 | b1 += (TilemapRow & 0x1f) << 5; |
1443 | b2 += (TilemapRow & 0x1f) << 5; |
1444 | |
1445 | uint32 HPos = (HOffset + Left) & OffsetMask; |
1446 | uint32 HTile = HPos >> 3; |
1447 | uint16 *t; |
1448 | |
1449 | if (BG.TileSizeH == 8) |
1450 | { |
1451 | if (HTile > 31) |
1452 | t = b2 + (HTile & 0x1f); |
1453 | else |
1454 | t = b1 + HTile; |
1455 | } |
1456 | else |
1457 | { |
1458 | if (HTile > 63) |
1459 | t = b2 + ((HTile >> 1) & 0x1f); |
1460 | else |
1461 | t = b1 + (HTile >> 1); |
1462 | } |
1463 | |
1464 | uint32 l = HPos & 7; |
1465 | uint32 w = 8 - l; |
1466 | if (w > Width) |
1467 | w = Width; |
1468 | |
1469 | Offset -= l * PixWidth; |
1470 | Tile = READ_WORD(t); |
1471 | GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl; |
1472 | |
1473 | if (BG.TileSizeV == 16) |
1474 | Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1)); |
1475 | |
1476 | if (BG.TileSizeH == 8) |
1477 | { |
1478 | DrawClippedTile(Tile, Offset, l, w, VirtAlign, 1); |
1479 | } |
1480 | else |
1481 | { |
1482 | if (!(Tile & H_FLIP)) |
1483 | DrawClippedTile(TILE_PLUS(Tile, (HTile & 1)), Offset, l, w, VirtAlign, 1); |
1484 | else |
1485 | DrawClippedTile(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, l, w, VirtAlign, 1); |
1486 | } |
1487 | |
1488 | Left += w; |
1489 | Offset += 8 * PixWidth; |
1490 | Width -= w; |
1491 | } |
1492 | } |
1493 | } |
1494 | } |
1495 | |
1496 | static void DrawBackgroundOffsetMosaic (int bg, uint8 Zh, uint8 Zl, int VOffOff) |
1497 | { |
1498 | BG.TileAddress = PPU.BG[bg].NameBase << 1; |
1499 | |
1500 | uint32 Tile; |
1501 | uint16 *SC0, *SC1, *SC2, *SC3; |
1502 | uint16 *BPS0, *BPS1, *BPS2, *BPS3; |
1503 | |
1504 | BPS0 = (uint16 *) &Memory.VRAM[PPU.BG[2].SCBase << 1]; |
1505 | BPS1 = (PPU.BG[2].SCSize & 1) ? BPS0 + 1024 : BPS0; |
1506 | if (BPS1 >= (uint16 *) (Memory.VRAM + 0x10000)) |
1507 | BPS1 -= 0x8000; |
1508 | BPS2 = (PPU.BG[2].SCSize & 2) ? BPS1 + 1024 : BPS0; |
1509 | if (BPS2 >= (uint16 *) (Memory.VRAM + 0x10000)) |
1510 | BPS2 -= 0x8000; |
1511 | BPS3 = (PPU.BG[2].SCSize & 1) ? BPS2 + 1024 : BPS2; |
1512 | if (BPS3 >= (uint16 *) (Memory.VRAM + 0x10000)) |
1513 | BPS3 -= 0x8000; |
1514 | |
1515 | SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1]; |
1516 | SC1 = (PPU.BG[bg].SCSize & 1) ? SC0 + 1024 : SC0; |
1517 | if (SC1 >= (uint16 *) (Memory.VRAM + 0x10000)) |
1518 | SC1 -= 0x8000; |
1519 | SC2 = (PPU.BG[bg].SCSize & 2) ? SC1 + 1024 : SC0; |
1520 | if (SC2 >= (uint16 *) (Memory.VRAM + 0x10000)) |
1521 | SC2 -= 0x8000; |
1522 | SC3 = (PPU.BG[bg].SCSize & 1) ? SC2 + 1024 : SC2; |
1523 | if (SC3 >= (uint16 *) (Memory.VRAM + 0x10000)) |
1524 | SC3 -= 0x8000; |
1525 | |
1526 | int Lines; |
1527 | int OffsetMask = (BG.TileSizeH == 16) ? 0x3ff : 0x1ff; |
1528 | int OffsetShift = (BG.TileSizeV == 16) ? 4 : 3; |
1529 | int Offset2Shift = (BG.OffsetSizeV == 16) ? 4 : 3; |
1530 | int OffsetEnableMask = 0x2000 << bg; |
1531 | int PixWidth = IPPU.DoubleWidthPixels ? 2 : 1; |
1532 | bool8 HiresInterlace = IPPU.Interlace && IPPU.DoubleWidthPixels; |
1533 | |
1534 | void (*DrawPix) (uint32, uint32, uint32, uint32, uint32, uint32); |
1535 | |
1536 | int MosaicStart = ((uint32) GFX.StartY - PPU.MosaicStart) % PPU.Mosaic; |
1537 | |
1538 | for (int clip = 0; clip < GFX.Clip[bg].Count; clip++) |
1539 | { |
1540 | GFX.ClipColors = !(GFX.Clip[bg].DrawMode[clip] & 1); |
1541 | |
1542 | if (BG.EnableMath && (GFX.Clip[bg].DrawMode[clip] & 2)) |
1543 | DrawPix = GFX.DrawMosaicPixelMath; |
1544 | else |
1545 | DrawPix = GFX.DrawMosaicPixelNomath; |
1546 | |
1547 | for (uint32 Y = GFX.StartY - MosaicStart; Y <= GFX.EndY; Y += PPU.Mosaic) |
1548 | { |
1549 | uint32 Y2 = HiresInterlace ? Y * 2 : Y; |
1550 | uint32 VOff = LineData[Y + MosaicStart].BG[2].VOffset - 1; |
1551 | uint32 HOff = LineData[Y + MosaicStart].BG[2].HOffset; |
1552 | |
1553 | Lines = PPU.Mosaic - MosaicStart; |
1554 | if (Y + MosaicStart + Lines > GFX.EndY) |
1555 | Lines = GFX.EndY - Y - MosaicStart + 1; |
1556 | |
1557 | uint32 HOffsetRow = VOff >> Offset2Shift; |
1558 | uint32 VOffsetRow = (VOff + VOffOff) >> Offset2Shift; |
1559 | uint16 *s, *s1, *s2; |
1560 | |
1561 | if (HOffsetRow & 0x20) |
1562 | { |
1563 | s1 = BPS2; |
1564 | s2 = BPS3; |
1565 | } |
1566 | else |
1567 | { |
1568 | s1 = BPS0; |
1569 | s2 = BPS1; |
1570 | } |
1571 | |
1572 | s1 += (HOffsetRow & 0x1f) << 5; |
1573 | s2 += (HOffsetRow & 0x1f) << 5; |
1574 | s = ((VOffsetRow & 0x20) ? BPS2 : BPS0) + ((VOffsetRow & 0x1f) << 5); |
1575 | int32 VOffsetOffset = s - s1; |
1576 | |
1577 | uint32 Left = GFX.Clip[bg].Left[clip]; |
1578 | uint32 Right = GFX.Clip[bg].Right[clip]; |
1579 | uint32 Offset = Left * PixWidth + (Y + MosaicStart) * GFX.PPL; |
1580 | uint32 HScroll = LineData[Y + MosaicStart].BG[bg].HOffset; |
1581 | uint32 Width = Right - Left; |
1582 | |
1583 | while (Left < Right) |
1584 | { |
1585 | uint32 VOffset, HOffset; |
1586 | |
1587 | if (Left < (8 - (HScroll & 7))) |
1588 | { |
1589 | // SNES cannot do OPT for leftmost tile column |
1590 | VOffset = LineData[Y + MosaicStart].BG[bg].VOffset; |
1591 | HOffset = HScroll; |
1592 | } |
1593 | else |
1594 | { |
1595 | int HOffTile = (((Left + (HScroll & 7)) - 8) + (HOff & ~7)) >> 3; |
1596 | |
1597 | if (BG.OffsetSizeH == 8) |
1598 | { |
1599 | if (HOffTile > 31) |
1600 | s = s2 + (HOffTile & 0x1f); |
1601 | else |
1602 | s = s1 + HOffTile; |
1603 | } |
1604 | else |
1605 | { |
1606 | if (HOffTile > 63) |
1607 | s = s2 + ((HOffTile >> 1) & 0x1f); |
1608 | else |
1609 | s = s1 + (HOffTile >> 1); |
1610 | } |
1611 | |
1612 | uint16 HCellOffset = READ_WORD(s); |
1613 | uint16 VCellOffset; |
1614 | |
1615 | if (VOffOff) |
1616 | VCellOffset = READ_WORD(s + VOffsetOffset); |
1617 | else |
1618 | { |
1619 | if (HCellOffset & 0x8000) |
1620 | { |
1621 | VCellOffset = HCellOffset; |
1622 | HCellOffset = 0; |
1623 | } |
1624 | else |
1625 | VCellOffset = 0; |
1626 | } |
1627 | |
1628 | if (VCellOffset & OffsetEnableMask) |
1629 | VOffset = VCellOffset + 1; |
1630 | else |
1631 | VOffset = LineData[Y + MosaicStart].BG[bg].VOffset; |
1632 | |
1633 | if (HCellOffset & OffsetEnableMask) |
1634 | HOffset = (HCellOffset & ~7) | (HScroll & 7); |
1635 | else |
1636 | HOffset = HScroll; |
1637 | } |
1638 | |
1639 | if (HiresInterlace) |
1640 | VOffset++; |
1641 | |
1642 | uint32 t1, t2; |
1643 | int VirtAlign = (((Y2 + VOffset) & 7) >> (HiresInterlace ? 1 : 0)) << 3; |
1644 | int TilemapRow = (VOffset + Y2) >> OffsetShift; |
1645 | BG.InterlaceLine = ((VOffset + Y2) & 1) << 3; |
1646 | |
1647 | if ((VOffset + Y2) & 8) |
1648 | { |
1649 | t1 = 16; |
1650 | t2 = 0; |
1651 | } |
1652 | else |
1653 | { |
1654 | t1 = 0; |
1655 | t2 = 16; |
1656 | } |
1657 | |
1658 | uint16 *b1, *b2; |
1659 | |
1660 | if (TilemapRow & 0x20) |
1661 | { |
1662 | b1 = SC2; |
1663 | b2 = SC3; |
1664 | } |
1665 | else |
1666 | { |
1667 | b1 = SC0; |
1668 | b2 = SC1; |
1669 | } |
1670 | |
1671 | b1 += (TilemapRow & 0x1f) << 5; |
1672 | b2 += (TilemapRow & 0x1f) << 5; |
1673 | |
1674 | uint32 HPos = (HOffset + Left - (Left % PPU.Mosaic)) & OffsetMask; |
1675 | uint32 HTile = HPos >> 3; |
1676 | uint16 *t; |
1677 | |
1678 | if (BG.TileSizeH == 8) |
1679 | { |
1680 | if (HTile > 31) |
1681 | t = b2 + (HTile & 0x1f); |
1682 | else |
1683 | t = b1 + HTile; |
1684 | } |
1685 | else |
1686 | { |
1687 | if (HTile > 63) |
1688 | t = b2 + ((HTile >> 1) & 0x1f); |
1689 | else |
1690 | t = b1 + (HTile >> 1); |
1691 | } |
1692 | |
1693 | uint32 w = PPU.Mosaic - (Left % PPU.Mosaic); |
1694 | if (w > Width) |
1695 | w = Width; |
1696 | |
1697 | Tile = READ_WORD(t); |
1698 | GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl; |
1699 | |
1700 | if (BG.TileSizeV == 16) |
1701 | Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1)); |
1702 | |
1703 | if (BG.TileSizeH == 8) |
1704 | DrawPix(Tile, Offset, VirtAlign, HPos & 7, w, Lines); |
1705 | else |
1706 | { |
1707 | if (!(Tile & H_FLIP)) |
1708 | DrawPix(TILE_PLUS(Tile, (HTile & 1)), Offset, VirtAlign, HPos & 7, w, Lines); |
1709 | else |
1710 | if (!(Tile & V_FLIP)) |
1711 | DrawPix(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, VirtAlign, HPos & 7, w, Lines); |
1712 | } |
1713 | |
1714 | Left += w; |
1715 | Offset += w * PixWidth; |
1716 | Width -= w; |
1717 | } |
1718 | |
1719 | MosaicStart = 0; |
1720 | } |
1721 | } |
1722 | } |
1723 | |
1724 | static inline void DrawBackgroundMode7 (int bg, void (*DrawMath) (uint32, uint32, int), void (*DrawNomath) (uint32, uint32, int), int D) |
1725 | { |
1726 | for (int clip = 0; clip < GFX.Clip[bg].Count; clip++) |
1727 | { |
1728 | GFX.ClipColors = !(GFX.Clip[bg].DrawMode[clip] & 1); |
1729 | |
1730 | if (BG.EnableMath && (GFX.Clip[bg].DrawMode[clip] & 2)) |
1731 | DrawMath(GFX.Clip[bg].Left[clip], GFX.Clip[bg].Right[clip], D); |
1732 | else |
1733 | DrawNomath(GFX.Clip[bg].Left[clip], GFX.Clip[bg].Right[clip], D); |
1734 | } |
1735 | } |
1736 | |
1737 | static inline void DrawBackdrop (void) |
1738 | { |
1739 | uint32 Offset = GFX.StartY * GFX.PPL; |
1740 | |
1741 | for (int clip = 0; clip < GFX.Clip[5].Count; clip++) |
1742 | { |
1743 | GFX.ClipColors = !(GFX.Clip[5].DrawMode[clip] & 1); |
1744 | |
1745 | if (BG.EnableMath && (GFX.Clip[5].DrawMode[clip] & 2)) |
1746 | GFX.DrawBackdropMath(Offset, GFX.Clip[5].Left[clip], GFX.Clip[5].Right[clip]); |
1747 | else |
1748 | GFX.DrawBackdropNomath(Offset, GFX.Clip[5].Left[clip], GFX.Clip[5].Right[clip]); |
1749 | } |
1750 | } |
1751 | |
1752 | void S9xReRefresh (void) |
1753 | { |
1754 | // Be careful when calling this function from the thread other than the emulation one... |
1755 | // Here it's assumed no drawing occurs from the emulation thread when Settings.Paused is TRUE. |
1756 | if (Settings.Paused) |
1757 | S9xDeinitUpdate(IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight); |
1758 | } |
1759 | |
1760 | void S9xSetInfoString (const char *string) |
1761 | { |
1762 | if (Settings.InitialInfoStringTimeout > 0) |
1763 | { |
1764 | GFX.InfoString = string; |
1765 | GFX.InfoStringTimeout = Settings.InitialInfoStringTimeout; |
1766 | S9xReRefresh(); |
1767 | } |
1768 | } |
1769 | |
1770 | void S9xDisplayChar (uint16 *s, uint8 c) |
1771 | { |
1772 | const uint16 black = BUILD_PIXEL(0, 0, 0); |
1773 | |
1774 | int line = ((c - 32) >> 4) * font_height; |
1775 | int offset = ((c - 32) & 15) * font_width; |
1776 | |
1777 | for (int h = 0; h < font_height; h++, line++, s += GFX.RealPPL - font_width) |
1778 | { |
1779 | for (int w = 0; w < font_width; w++, s++) |
1780 | { |
1781 | char p = font[line][offset + w]; |
1782 | |
1783 | if (p == '#') |
1784 | *s = Settings.DisplayColor; |
1785 | else |
1786 | if (p == '.') |
1787 | *s = black; |
1788 | } |
1789 | } |
1790 | } |
1791 | |
1792 | static void DisplayStringFromBottom (const char *string, int linesFromBottom, int pixelsFromLeft, bool allowWrap) |
1793 | { |
1794 | if (S9xCustomDisplayString) |
1795 | { |
1796 | S9xCustomDisplayString (string, linesFromBottom, pixelsFromLeft, allowWrap, S9X_NO_INFO); |
1797 | return; |
1798 | } |
1799 | |
1800 | if (linesFromBottom <= 0) |
1801 | linesFromBottom = 1; |
1802 | |
1803 | uint16 *dst = GFX.Screen + (IPPU.RenderedScreenHeight - font_height * linesFromBottom) * GFX.RealPPL + pixelsFromLeft; |
1804 | |
1805 | int len = strlen(string); |
1806 | int max_chars = IPPU.RenderedScreenWidth / (font_width - 1); |
1807 | int char_count = 0; |
1808 | |
1809 | for (int i = 0 ; i < len ; i++, char_count++) |
1810 | { |
1811 | if (char_count >= max_chars || (uint8) string[i] < 32) |
1812 | { |
1813 | if (!allowWrap) |
1814 | break; |
1815 | |
1816 | dst += font_height * GFX.RealPPL - (font_width - 1) * max_chars; |
1817 | if (dst >= GFX.Screen + IPPU.RenderedScreenHeight * GFX.RealPPL) |
1818 | break; |
1819 | |
1820 | char_count -= max_chars; |
1821 | } |
1822 | |
1823 | if ((uint8) string[i] < 32) |
1824 | continue; |
1825 | |
1826 | S9xDisplayChar(dst, string[i]); |
1827 | dst += font_width - 1; |
1828 | } |
1829 | } |
1830 | |
1831 | static void S9xDisplayStringType (const char *string, int linesFromBottom, int pixelsFromLeft, bool allowWrap, int type) |
1832 | { |
1833 | if (S9xCustomDisplayString) |
1834 | { |
1835 | S9xCustomDisplayString (string, linesFromBottom, pixelsFromLeft, allowWrap, type); |
1836 | return; |
1837 | } |
1838 | |
1839 | S9xDisplayString (string, linesFromBottom, pixelsFromLeft, allowWrap); |
1840 | } |
1841 | |
1842 | static void DisplayTime (void) |
1843 | { |
1844 | char string[10]; |
1845 | |
1846 | time_t rawtime; |
1847 | struct tm *timeinfo; |
1848 | |
1849 | time (&rawtime); |
1850 | timeinfo = localtime(&rawtime); |
1851 | |
1852 | sprintf(string, "%02u:%02u" , timeinfo->tm_hour, timeinfo->tm_min); |
1853 | S9xDisplayString(string, 0, 0, false); |
1854 | } |
1855 | |
1856 | static void DisplayFrameRate (void) |
1857 | { |
1858 | char string[10]; |
1859 | static uint32 lastFrameCount = 0, calcFps = 0; |
1860 | static time_t lastTime = time(NULL); |
1861 | |
1862 | time_t currTime = time(NULL); |
1863 | if (lastTime != currTime) { |
1864 | if (lastFrameCount < IPPU.TotalEmulatedFrames) { |
1865 | calcFps = (IPPU.TotalEmulatedFrames - lastFrameCount) / (uint32)(currTime - lastTime); |
1866 | } |
1867 | lastTime = currTime; |
1868 | lastFrameCount = IPPU.TotalEmulatedFrames; |
1869 | } |
1870 | sprintf(string, "%u fps" , calcFps); |
1871 | S9xDisplayString(string, 2, IPPU.RenderedScreenWidth - (font_width - 1) * strlen(string) - 1, false); |
1872 | |
1873 | #ifdef DEBUGGER |
1874 | const int len = 8; |
1875 | sprintf(string, "%02d/%02d %02d" , (int) IPPU.DisplayedRenderedFrameCount, (int) Memory.ROMFramesPerSecond, (int) IPPU.FrameCount); |
1876 | #else |
1877 | const int len = 5; |
1878 | sprintf(string, "%02d/%02d" , (int) IPPU.DisplayedRenderedFrameCount, (int) Memory.ROMFramesPerSecond); |
1879 | #endif |
1880 | |
1881 | S9xDisplayString(string, 1, IPPU.RenderedScreenWidth - (font_width - 1) * len - 1, false); |
1882 | } |
1883 | |
1884 | static void DisplayPressedKeys (void) |
1885 | { |
1886 | static unsigned char KeyMap[] = { '0', '1', '2', 'R', 'L', 'X', 'A', 225, 224, 227, 226, 'S', 's', 'Y', 'B' }; |
1887 | static int KeyOrder[] = { 8, 10, 7, 9, 0, 6, 14, 13, 5, 1, 4, 3, 2, 11, 12 }; // < ^ > v A B Y X L R S s |
1888 | |
1889 | enum controllers controller; |
1890 | int line = Settings.DisplayMovieFrame && S9xMovieActive() ? 2 : 1; |
1891 | int8 ids[4]; |
1892 | char string[255]; |
1893 | |
1894 | for (int port = 0; port < 2; port++) |
1895 | { |
1896 | S9xGetController(port, &controller, &ids[0], &ids[1], &ids[2], &ids[3]); |
1897 | |
1898 | switch (controller) |
1899 | { |
1900 | case CTL_MOUSE: |
1901 | { |
1902 | uint8 buf[5]; |
1903 | if (!MovieGetMouse(port, buf)) |
1904 | break; |
1905 | int16 x = READ_WORD(buf); |
1906 | int16 y = READ_WORD(buf + 2); |
1907 | uint8 buttons = buf[4]; |
1908 | sprintf(string, "#%d %d: (%03d,%03d) %c%c" , port + 1, ids[0] + 1, x, y, |
1909 | (buttons & 0x40) ? 'L' : ' ', (buttons & 0x80) ? 'R' : ' '); |
1910 | S9xDisplayStringType(string, line++, 1, false, S9X_PRESSED_KEYS_INFO); |
1911 | break; |
1912 | } |
1913 | |
1914 | case CTL_SUPERSCOPE: |
1915 | { |
1916 | uint8 buf[6]; |
1917 | if (!MovieGetScope(port, buf)) |
1918 | break; |
1919 | int16 x = READ_WORD(buf); |
1920 | int16 y = READ_WORD(buf + 2); |
1921 | uint8 buttons = buf[4]; |
1922 | sprintf(string, "#%d %d: (%03d,%03d) %c%c%c%c" , port + 1, ids[0] + 1, x, y, |
1923 | (buttons & 0x80) ? 'F' : ' ', (buttons & 0x40) ? 'C' : ' ', |
1924 | (buttons & 0x20) ? 'T' : ' ', (buttons & 0x10) ? 'P' : ' '); |
1925 | S9xDisplayStringType(string, line++, 1, false, S9X_PRESSED_KEYS_INFO); |
1926 | break; |
1927 | } |
1928 | |
1929 | case CTL_JUSTIFIER: |
1930 | { |
1931 | uint8 buf[11]; |
1932 | if (!MovieGetJustifier(port, buf)) |
1933 | break; |
1934 | int16 x1 = READ_WORD(buf); |
1935 | int16 x2 = READ_WORD(buf + 2); |
1936 | int16 y1 = READ_WORD(buf + 4); |
1937 | int16 y2 = READ_WORD(buf + 6); |
1938 | uint8 buttons = buf[8]; |
1939 | bool8 offscreen1 = buf[9]; |
1940 | bool8 offscreen2 = buf[10]; |
1941 | sprintf(string, "#%d %d: (%03d,%03d) %c%c%c / (%03d,%03d) %c%c%c" , port + 1, ids[0] + 1, |
1942 | x1, y1, (buttons & 0x80) ? 'T' : ' ', (buttons & 0x20) ? 'S' : ' ', offscreen1 ? 'O' : ' ', |
1943 | x2, y2, (buttons & 0x40) ? 'T' : ' ', (buttons & 0x10) ? 'S' : ' ', offscreen2 ? 'O' : ' '); |
1944 | S9xDisplayStringType(string, line++, 1, false, S9X_PRESSED_KEYS_INFO); |
1945 | break; |
1946 | } |
1947 | |
1948 | case CTL_JOYPAD: |
1949 | { |
1950 | sprintf(string, "#%d %d: " , port + 1, ids[0] + 1); |
1951 | uint16 pad = MovieGetJoypad(ids[0]); |
1952 | for (int i = 0; i < 15; i++) |
1953 | { |
1954 | int j = KeyOrder[i]; |
1955 | int mask = (1 << (j + 1)); |
1956 | string[6 + i]= (pad & mask) ? KeyMap[j] : ' '; |
1957 | } |
1958 | |
1959 | S9xDisplayStringType(string, line++, 1, false, S9X_PRESSED_KEYS_INFO); |
1960 | break; |
1961 | } |
1962 | |
1963 | case CTL_MP5: |
1964 | { |
1965 | for (int n = 0; n < 4; n++) |
1966 | { |
1967 | if (ids[n] != -1) |
1968 | { |
1969 | sprintf(string, "#%d %d: " , port + 1, ids[n] + 1); |
1970 | uint16 pad = MovieGetJoypad(ids[n]); |
1971 | for (int i = 0; i < 15; i++) |
1972 | { |
1973 | int j = KeyOrder[i]; |
1974 | int mask = (1 << (j + 1)); |
1975 | string[6 + i]= (pad & mask) ? KeyMap[j] : ' '; |
1976 | } |
1977 | |
1978 | S9xDisplayStringType(string, line++, 1, false, S9X_PRESSED_KEYS_INFO); |
1979 | } |
1980 | } |
1981 | |
1982 | break; |
1983 | } |
1984 | |
1985 | case CTL_MACSRIFLE: |
1986 | { |
1987 | /* |
1988 | uint8 buf[6], *p = buf; |
1989 | MovieGetScope(port, buf); |
1990 | int16 x = READ_WORD(p); |
1991 | int16 y = READ_WORD(p + 2); |
1992 | uint8 buttons = buf[4]; |
1993 | sprintf(string, "#%d %d: (%03d,%03d) %c%c%c%c", port, ids[0], x, y, |
1994 | (buttons & 0x80) ? 'F' : ' ', (buttons & 0x40) ? 'C' : ' ', |
1995 | (buttons & 0x20) ? 'T' : ' ', (buttons & 0x10) ? 'P' : ' '); |
1996 | S9xDisplayString(string, line++, 1, false); |
1997 | */ |
1998 | break; |
1999 | } |
2000 | |
2001 | case CTL_NONE: |
2002 | { |
2003 | // Display Nothing |
2004 | break; |
2005 | } |
2006 | } |
2007 | } |
2008 | } |
2009 | |
2010 | static void DisplayWatchedAddresses (void) |
2011 | { |
2012 | for (unsigned int i = 0; i < sizeof(watches) / sizeof(watches[0]); i++) |
2013 | { |
2014 | if (!watches[i].on) |
2015 | break; |
2016 | |
2017 | int32 displayNumber = 0; |
2018 | char buf[32]; |
2019 | |
2020 | for (int r = 0; r < watches[i].size; r++) |
2021 | displayNumber += (Cheat.CWatchRAM[(watches[i].address - 0x7E0000) + r]) << (8 * r); |
2022 | |
2023 | if (watches[i].format == 1) |
2024 | sprintf(buf, "%s,%du = %u" , watches[i].desc, watches[i].size, (unsigned int) displayNumber); |
2025 | else |
2026 | if (watches[i].format == 3) |
2027 | sprintf(buf, "%s,%dx = %X" , watches[i].desc, watches[i].size, (unsigned int) displayNumber); |
2028 | else // signed |
2029 | { |
2030 | if (watches[i].size == 1) |
2031 | displayNumber = (int32) ((int8) displayNumber); |
2032 | else |
2033 | if (watches[i].size == 2) |
2034 | displayNumber = (int32) ((int16) displayNumber); |
2035 | else |
2036 | if (watches[i].size == 3) |
2037 | if (displayNumber >= 8388608) |
2038 | displayNumber -= 16777216; |
2039 | |
2040 | sprintf(buf, "%s,%ds = %d" , watches[i].desc, watches[i].size, (int) displayNumber); |
2041 | } |
2042 | |
2043 | S9xDisplayString(buf, 6 + i, 1, false); |
2044 | } |
2045 | } |
2046 | |
2047 | void S9xDisplayMessages (uint16 *screen, int ppl, int width, int height, int scale) |
2048 | { |
2049 | if (Settings.DisplayTime) |
2050 | DisplayTime(); |
2051 | |
2052 | if (Settings.DisplayFrameRate) |
2053 | DisplayFrameRate(); |
2054 | |
2055 | if (Settings.DisplayWatchedAddresses) |
2056 | DisplayWatchedAddresses(); |
2057 | |
2058 | if (Settings.DisplayPressedKeys) |
2059 | DisplayPressedKeys(); |
2060 | |
2061 | if (Settings.DisplayMovieFrame && S9xMovieActive()) |
2062 | S9xDisplayString(GFX.FrameDisplayString, 1, 1, false); |
2063 | |
2064 | if (GFX.InfoString && *GFX.InfoString) |
2065 | S9xDisplayString(GFX.InfoString, 5, 1, true); |
2066 | } |
2067 | |
2068 | static uint16 get_crosshair_color (uint8 color) |
2069 | { |
2070 | switch (color & 15) |
2071 | { |
2072 | case 0: return (BUILD_PIXEL( 0, 0, 0)); // transparent, shouldn't be used |
2073 | case 1: return (BUILD_PIXEL( 0, 0, 0)); // Black |
2074 | case 2: return (BUILD_PIXEL( 8, 8, 8)); // 25Grey |
2075 | case 3: return (BUILD_PIXEL(16, 16, 16)); // 50Grey |
2076 | case 4: return (BUILD_PIXEL(23, 23, 23)); // 75Grey |
2077 | case 5: return (BUILD_PIXEL(31, 31, 31)); // White |
2078 | case 6: return (BUILD_PIXEL(31, 0, 0)); // Red |
2079 | case 7: return (BUILD_PIXEL(31, 16, 0)); // Orange |
2080 | case 8: return (BUILD_PIXEL(31, 31, 0)); // Yellow |
2081 | case 9: return (BUILD_PIXEL( 0, 31, 0)); // Green |
2082 | case 10: return (BUILD_PIXEL( 0, 31, 31)); // Cyan |
2083 | case 11: return (BUILD_PIXEL( 0, 23, 31)); // Sky |
2084 | case 12: return (BUILD_PIXEL( 0, 0, 31)); // Blue |
2085 | case 13: return (BUILD_PIXEL(23, 0, 31)); // Violet |
2086 | case 14: return (BUILD_PIXEL(31, 0, 31)); // Magenta |
2087 | case 15: return (BUILD_PIXEL(31, 0, 16)); // Purple |
2088 | } |
2089 | |
2090 | return (0); |
2091 | } |
2092 | |
2093 | void S9xDrawCrosshair (const char *crosshair, uint8 fgcolor, uint8 bgcolor, int16 x, int16 y) |
2094 | { |
2095 | if (!crosshair) |
2096 | return; |
2097 | |
2098 | int16 r, rx = 1, c, cx = 1, W = SNES_WIDTH, H = PPU.ScreenHeight; |
2099 | uint16 fg, bg; |
2100 | |
2101 | x -= 7; |
2102 | y -= 7; |
2103 | |
2104 | if (IPPU.DoubleWidthPixels) { cx = 2; x *= 2; W *= 2; } |
2105 | if (IPPU.DoubleHeightPixels) { rx = 2; y *= 2; H *= 2; } |
2106 | |
2107 | fg = get_crosshair_color(fgcolor); |
2108 | bg = get_crosshair_color(bgcolor); |
2109 | |
2110 | uint16 *s = GFX.Screen + y * (int32)GFX.RealPPL + x; |
2111 | |
2112 | for (r = 0; r < 15 * rx; r++, s += GFX.RealPPL - 15 * cx) |
2113 | { |
2114 | if (y + r < 0) |
2115 | { |
2116 | s += 15 * cx; |
2117 | continue; |
2118 | } |
2119 | |
2120 | if (y + r >= H) |
2121 | break; |
2122 | |
2123 | for (c = 0; c < 15 * cx; c++, s++) |
2124 | { |
2125 | if (x + c < 0 || s < GFX.Screen) |
2126 | continue; |
2127 | |
2128 | if (x + c >= W) |
2129 | { |
2130 | s += 15 * cx - c; |
2131 | break; |
2132 | } |
2133 | |
2134 | uint8 p = crosshair[(r / rx) * 15 + (c / cx)]; |
2135 | |
2136 | if (p == '#' && fgcolor) |
2137 | *s = (fgcolor & 0x10) ? COLOR_ADD::fn1_2(fg, *s) : fg; |
2138 | else |
2139 | if (p == '.' && bgcolor) |
2140 | *s = (bgcolor & 0x10) ? COLOR_ADD::fn1_2(*s, bg) : bg; |
2141 | } |
2142 | } |
2143 | } |
2144 | |
2145 | |