~random-stuff/random-stuff/snes9x-OLD

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