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