gfx.cpp
   1  /*****************************************************************************\
   2       Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
   3                  This file is licensed under the Snes9x License.
   4     For further information, consult the LICENSE file in the root directory.
   5  \*****************************************************************************/
   6  
   7  #include "snes9x.h"
   8  #include "ppu.h"
   9  #include "tile.h"
  10  #include "controls.h"
  11  //#include "font.h"
  12  
  13  #define TILE_PLUS(t, x)	(((t) & 0xfc00) | ((t + x) & 0x3ff))
  14  
  15  static struct SLineData	LineData[240];
  16  
  17  struct SGFX	GFX;
  18  struct SBG	BG;
  19  
  20  
  21  static void DrawBackground (int bg, uint8 Zh, uint8 Zl)
  22  {
  23  	BG.TileAddress = PPU.BG[bg].NameBase << 1;
  24  
  25  	uint32	Tile;
  26  	uint16	*SC0, *SC1, *SC2, *SC3;
  27  
  28  	SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1];
  29  	SC1 = (PPU.BG[bg].SCSize & 1) ? SC0 + 1024 : SC0;
  30  	if (SC1 >= (uint16 *) (Memory.VRAM + 0x10000))
  31  		SC1 -= 0x8000;
  32  	SC2 = (PPU.BG[bg].SCSize & 2) ? SC1 + 1024 : SC0;
  33  	if (SC2 >= (uint16 *) (Memory.VRAM + 0x10000))
  34  		SC2 -= 0x8000;
  35  	SC3 = (PPU.BG[bg].SCSize & 1) ? SC2 + 1024 : SC2;
  36  	if (SC3 >= (uint16 *) (Memory.VRAM + 0x10000))
  37  		SC3 -= 0x8000;
  38  
  39  	uint32	Lines;
  40  	int		OffsetMask  = (BG.TileSizeH == 16) ? 0x3ff : 0x1ff;
  41  	int		OffsetShift = (BG.TileSizeV == 16) ? 4 : 3;
  42  
  43  	void (*DrawTile) (uint32, uint32, uint32, uint32);
  44  	void (*DrawClippedTile) (uint32, uint32, uint32, uint32, uint32, uint32);
  45  
  46  	for (int clip = 0; clip < GFX.Clip[bg].Count; clip++)
  47  	{
  48  		GFX.ClipColors = !(GFX.Clip[bg].DrawMode[clip] & 1);
  49  
  50  		if (BG.EnableMath && (GFX.Clip[bg].DrawMode[clip] & 2))
  51  		{
  52  			DrawTile = GFX.DrawTileMath;
  53  			DrawClippedTile = GFX.DrawClippedTileMath;
  54  		}
  55  		else
  56  		{
  57  			DrawTile = GFX.DrawTileNomath;
  58  			DrawClippedTile = GFX.DrawClippedTileNomath;
  59  		}
  60  
  61  		for (uint32 Y = GFX.StartY; Y <= GFX.EndY; Y += Lines)
  62  		{
  63  			uint32	VOffset = LineData[Y].BG[bg].VOffset;
  64  			uint32	HOffset = LineData[Y].BG[bg].HOffset;
  65  			int		VirtAlign = ((Y + VOffset) & 7);
  66  
  67  			for (Lines = 1; Lines < 8 - VirtAlign; Lines++)
  68  			{
  69  				if ((VOffset != LineData[Y + Lines].BG[bg].VOffset) || (HOffset != LineData[Y + Lines].BG[bg].HOffset))
  70  					break;
  71  			}
  72  
  73  			if (Y + Lines > GFX.EndY)
  74  				Lines = GFX.EndY - Y + 1;
  75  
  76  			VirtAlign <<= 3;
  77  
  78  			uint32	t1, t2;
  79  			uint32	TilemapRow = (VOffset + Y) >> OffsetShift;
  80  
  81  			if ((VOffset + Y) & 8)
  82  			{
  83  				t1 = 16;
  84  				t2 = 0;
  85  			}
  86  			else
  87  			{
  88  				t1 = 0;
  89  				t2 = 16;
  90  			}
  91  
  92  			uint16	*b1, *b2;
  93  
  94  			if (TilemapRow & 0x20)
  95  			{
  96  				b1 = SC2;
  97  				b2 = SC3;
  98  			}
  99  			else
 100  			{
 101  				b1 = SC0;
 102  				b2 = SC1;
 103  			}
 104  
 105  			b1 += (TilemapRow & 0x1f) << 5;
 106  			b2 += (TilemapRow & 0x1f) << 5;
 107  
 108  			uint32	Left   = GFX.Clip[bg].Left[clip];
 109  			uint32	Right  = GFX.Clip[bg].Right[clip];
 110  			uint32	Offset = Left + Y * GFX.PPL;
 111  			uint32	HPos   = (HOffset + Left) & OffsetMask;
 112  			uint32	HTile  = HPos >> 3;
 113  			uint16	*t;
 114  
 115  			if (BG.TileSizeH == 8)
 116  			{
 117  				if (HTile > 31)
 118  					t = b2 + (HTile & 0x1f);
 119  				else
 120  					t = b1 + HTile;
 121  			}
 122  			else
 123  			{
 124  				if (HTile > 63)
 125  					t = b2 + ((HTile >> 1) & 0x1f);
 126  				else
 127  					t = b1 + (HTile >> 1);
 128  			}
 129  
 130  			uint32	Width = Right - Left;
 131  
 132  			if (HPos & 7)
 133  			{
 134  				uint32	l = HPos & 7;
 135  				uint32	w = 8 - l;
 136  				if (w > Width)
 137  					w = Width;
 138  
 139  				Offset -= l;
 140  				Tile = READ_WORD(t);
 141  				GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl;
 142  
 143  				if (BG.TileSizeV == 16)
 144  					Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1));
 145  
 146  				if (BG.TileSizeH == 8)
 147  				{
 148  					DrawClippedTile(Tile, Offset, l, w, VirtAlign, Lines);
 149  					t++;
 150  					if (HTile == 31)
 151  						t = b2;
 152  					else
 153  					if (HTile == 63)
 154  						t = b1;
 155  				}
 156  				else
 157  				{
 158  					if (!(Tile & H_FLIP))
 159  						DrawClippedTile(TILE_PLUS(Tile, (HTile & 1)), Offset, l, w, VirtAlign, Lines);
 160  					else
 161  						DrawClippedTile(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, l, w, VirtAlign, Lines);
 162  					t += HTile & 1;
 163  					if (HTile == 63)
 164  						t = b2;
 165  					else
 166  					if (HTile == 127)
 167  						t = b1;
 168  				}
 169  
 170  				HTile++;
 171  				Offset += 8;
 172  				Width -= w;
 173  			}
 174  
 175  			while (Width >= 8)
 176  			{
 177  				Tile = READ_WORD(t);
 178  				GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl;
 179  
 180  				if (BG.TileSizeV == 16)
 181  					Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1));
 182  
 183  				if (BG.TileSizeH == 8)
 184  				{
 185  					DrawTile(Tile, Offset, VirtAlign, Lines);
 186  					t++;
 187  					if (HTile == 31)
 188  						t = b2;
 189  					else
 190  					if (HTile == 63)
 191  						t = b1;
 192  				}
 193  				else
 194  				{
 195  					if (!(Tile & H_FLIP))
 196  						DrawTile(TILE_PLUS(Tile, (HTile & 1)), Offset, VirtAlign, Lines);
 197  					else
 198  						DrawTile(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, VirtAlign, Lines);
 199  					t += HTile & 1;
 200  					if (HTile == 63)
 201  						t = b2;
 202  					else
 203  					if (HTile == 127)
 204  						t = b1;
 205  				}
 206  
 207  				HTile++;
 208  				Offset += 8;
 209  				Width -= 8;
 210  			}
 211  
 212  			if (Width)
 213  			{
 214  				Tile = READ_WORD(t);
 215  				GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl;
 216  
 217  				if (BG.TileSizeV == 16)
 218  					Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1));
 219  
 220  				if (BG.TileSizeH == 8)
 221  					DrawClippedTile(Tile, Offset, 0, Width, VirtAlign, Lines);
 222  				else
 223  				{
 224  					if (!(Tile & H_FLIP))
 225  						DrawClippedTile(TILE_PLUS(Tile, (HTile & 1)), Offset, 0, Width, VirtAlign, Lines);
 226  					else
 227  						DrawClippedTile(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, 0, Width, VirtAlign, Lines);
 228  				}
 229  			}
 230  		}
 231  	}
 232  }
 233  
 234  static void DrawBackgroundMosaic (int bg, uint8 Zh, uint8 Zl)
 235  {
 236  	BG.TileAddress = PPU.BG[bg].NameBase << 1;
 237  
 238  	uint32	Tile;
 239  	uint16	*SC0, *SC1, *SC2, *SC3;
 240  
 241  	SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1];
 242  	SC1 = (PPU.BG[bg].SCSize & 1) ? SC0 + 1024 : SC0;
 243  	if (SC1 >= (uint16 *) (Memory.VRAM + 0x10000))
 244  		SC1 -= 0x8000;
 245  	SC2 = (PPU.BG[bg].SCSize & 2) ? SC1 + 1024 : SC0;
 246  	if (SC2 >= (uint16 *) (Memory.VRAM + 0x10000))
 247  		SC2 -= 0x8000;
 248  	SC3 = (PPU.BG[bg].SCSize & 1) ? SC2 + 1024 : SC2;
 249  	if (SC3 >= (uint16 *) (Memory.VRAM + 0x10000))
 250  		SC3 -= 0x8000;
 251  
 252  	int	Lines;
 253  	int	OffsetMask  = (BG.TileSizeH == 16) ? 0x3ff : 0x1ff;
 254  	int	OffsetShift = (BG.TileSizeV == 16) ? 4 : 3;
 255  
 256  	void (*DrawPix) (uint32, uint32, uint32, uint32, uint32, uint32);
 257  
 258  	int	MosaicStart = ((uint32) GFX.StartY - PPU.MosaicStart) % PPU.Mosaic;
 259  
 260  	for (int clip = 0; clip < GFX.Clip[bg].Count; clip++)
 261  	{
 262  		GFX.ClipColors = !(GFX.Clip[bg].DrawMode[clip] & 1);
 263  
 264  		if (BG.EnableMath && (GFX.Clip[bg].DrawMode[clip] & 2))
 265  			DrawPix = GFX.DrawMosaicPixelMath;
 266  		else
 267  			DrawPix = GFX.DrawMosaicPixelNomath;
 268  
 269  		for (uint32 Y = GFX.StartY - MosaicStart; Y <= GFX.EndY; Y += PPU.Mosaic)
 270  		{
 271  			uint32	VOffset = LineData[Y + MosaicStart].BG[bg].VOffset + (0);
 272  			uint32	HOffset = LineData[Y + MosaicStart].BG[bg].HOffset;
 273  
 274  			Lines = PPU.Mosaic - MosaicStart;
 275  			if (Y + MosaicStart + Lines > GFX.EndY)
 276  				Lines = GFX.EndY - Y - MosaicStart + 1;
 277  
 278  			int	VirtAlign = ((Y + VOffset) & 7) << 3;
 279  
 280  			uint32	t1, t2;
 281  			uint32	TilemapRow = (VOffset + Y) >> OffsetShift;
 282  
 283  			if ((VOffset + Y) & 8)
 284  			{
 285  				t1 = 16;
 286  				t2 = 0;
 287  			}
 288  			else
 289  			{
 290  				t1 = 0;
 291  				t2 = 16;
 292  			}
 293  
 294  			uint16	*b1, *b2;
 295  
 296  			if (TilemapRow & 0x20)
 297  			{
 298  				b1 = SC2;
 299  				b2 = SC3;
 300  			}
 301  			else
 302  			{
 303  				b1 = SC0;
 304  				b2 = SC1;
 305  			}
 306  
 307  			b1 += (TilemapRow & 0x1f) << 5;
 308  			b2 += (TilemapRow & 0x1f) << 5;
 309  
 310  			uint32	Left   = GFX.Clip[bg].Left[clip];
 311  			uint32	Right  = GFX.Clip[bg].Right[clip];
 312  			uint32	Offset = Left + (Y + MosaicStart) * GFX.PPL;
 313  			uint32	HPos   = (HOffset + Left - (Left % PPU.Mosaic)) & OffsetMask;
 314  			uint32	HTile  = HPos >> 3;
 315  			uint16	*t;
 316  
 317  			if (BG.TileSizeH == 8)
 318  			{
 319  				if (HTile > 31)
 320  					t = b2 + (HTile & 0x1f);
 321  				else
 322  					t = b1 + HTile;
 323  			}
 324  			else
 325  			{
 326  				if (HTile > 63)
 327  					t = b2 + ((HTile >> 1) & 0x1f);
 328  				else
 329  					t = b1 + (HTile >> 1);
 330  			}
 331  
 332  			uint32	Width = Right - Left;
 333  
 334  			HPos &= 7;
 335  
 336  			while (Left < Right)
 337  			{
 338  				uint32	w = PPU.Mosaic - (Left % PPU.Mosaic);
 339  				if (w > Width)
 340  					w = Width;
 341  
 342  				Tile = READ_WORD(t);
 343  				GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl;
 344  
 345  				if (BG.TileSizeV == 16)
 346  					Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1));
 347  
 348  				if (BG.TileSizeH == 8)
 349  					DrawPix(Tile, Offset, VirtAlign, HPos & 7, w, Lines);
 350  				else
 351  				{
 352  					if (!(Tile & H_FLIP))
 353  						DrawPix(TILE_PLUS(Tile, (HTile & 1)), Offset, VirtAlign, HPos & 7, w, Lines);
 354  					else
 355  						DrawPix(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, VirtAlign, HPos & 7, w, Lines);
 356  				}
 357  
 358  				HPos += PPU.Mosaic;
 359  
 360  				while (HPos >= 8)
 361  				{
 362  					HPos -= 8;
 363  
 364  					if (BG.TileSizeH == 8)
 365  					{
 366  						t++;
 367  						if (HTile == 31)
 368  							t = b2;
 369  						else
 370  						if (HTile == 63)
 371  							t = b1;
 372  					}
 373  					else
 374  					{
 375  						t += HTile & 1;
 376  						if (HTile == 63)
 377  							t = b2;
 378  						else
 379  						if (HTile == 127)
 380  							t = b1;
 381  					}
 382  
 383  					HTile++;
 384  				}
 385  
 386  				Offset += w;
 387  				Width -= w;
 388  				Left += w;
 389  			}
 390  
 391  			MosaicStart = 0;
 392  		}
 393  	}
 394  }
 395  
 396  static void DrawBackgroundOffset (int bg, uint8 Zh, uint8 Zl, int VOffOff)
 397  {
 398  	BG.TileAddress = PPU.BG[bg].NameBase << 1;
 399  
 400  	uint32	Tile;
 401  	uint16	*SC0, *SC1, *SC2, *SC3;
 402  	uint16	*BPS0, *BPS1, *BPS2, *BPS3;
 403  
 404  	BPS0 = (uint16 *) &Memory.VRAM[PPU.BG[2].SCBase << 1];
 405  	BPS1 = (PPU.BG[2].SCSize & 1) ? BPS0 + 1024 : BPS0;
 406  	if (BPS1 >= (uint16 *) (Memory.VRAM + 0x10000))
 407  		BPS1 -= 0x8000;
 408  	BPS2 = (PPU.BG[2].SCSize & 2) ? BPS1 + 1024 : BPS0;
 409  	if (BPS2 >= (uint16 *) (Memory.VRAM + 0x10000))
 410  		BPS2 -= 0x8000;
 411  	BPS3 = (PPU.BG[2].SCSize & 1) ? BPS2 + 1024 : BPS2;
 412  	if (BPS3 >= (uint16 *) (Memory.VRAM + 0x10000))
 413  		BPS3 -= 0x8000;
 414  
 415  	SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1];
 416  	SC1 = (PPU.BG[bg].SCSize & 1) ? SC0 + 1024 : SC0;
 417  	if (SC1 >= (uint16 *) (Memory.VRAM + 0x10000))
 418  		SC1 -= 0x8000;
 419  	SC2 = (PPU.BG[bg].SCSize & 2) ? SC1 + 1024 : SC0;
 420  	if (SC2 >= (uint16 *) (Memory.VRAM + 0x10000))
 421  		SC2 -= 0x8000;
 422  	SC3 = (PPU.BG[bg].SCSize & 1) ? SC2 + 1024 : SC2;
 423  	if (SC3 >= (uint16 *) (Memory.VRAM + 0x10000))
 424  		SC3 -= 0x8000;
 425  
 426  	int	OffsetMask   = (BG.TileSizeH   == 16) ? 0x3ff : 0x1ff;
 427  	int	OffsetShift  = (BG.TileSizeV   == 16) ? 4 : 3;
 428  	int	Offset2Mask  = (BG.OffsetSizeH == 16) ? 0x3ff : 0x1ff;
 429  	int	Offset2Shift = (BG.OffsetSizeV == 16) ? 4 : 3;
 430  	int	OffsetEnableMask = 0x2000 << bg;
 431  
 432  	void (*DrawClippedTile) (uint32, uint32, uint32, uint32, uint32, uint32);
 433  
 434  	for (int clip = 0; clip < GFX.Clip[bg].Count; clip++)
 435  	{
 436  		GFX.ClipColors = !(GFX.Clip[bg].DrawMode[clip] & 1);
 437  
 438  		if (BG.EnableMath && (GFX.Clip[bg].DrawMode[clip] & 2))
 439  		{
 440  			DrawClippedTile = GFX.DrawClippedTileMath;
 441  		}
 442  		else
 443  		{
 444  			DrawClippedTile = GFX.DrawClippedTileNomath;
 445  		}
 446  
 447  		for (uint32 Y = GFX.StartY; Y <= GFX.EndY; Y++)
 448  		{
 449  			uint32	VOff = LineData[Y].BG[2].VOffset - 1;
 450  			uint32	HOff = LineData[Y].BG[2].HOffset;
 451  			uint32	HOffsetRow = VOff >> Offset2Shift;
 452  			uint32	VOffsetRow = (VOff + VOffOff) >> Offset2Shift;
 453  			uint16	*s, *s1, *s2;
 454  
 455  			if (HOffsetRow & 0x20)
 456  			{
 457  				s1 = BPS2;
 458  				s2 = BPS3;
 459  			}
 460  			else
 461  			{
 462  				s1 = BPS0;
 463  				s2 = BPS1;
 464  			}
 465  
 466  			s1 += (HOffsetRow & 0x1f) << 5;
 467  			s2 += (HOffsetRow & 0x1f) << 5;
 468  			s = ((VOffsetRow & 0x20) ? BPS2 : BPS0) + ((VOffsetRow & 0x1f) << 5);
 469  			int32	VOffsetOffset = s - s1;
 470  
 471  			uint32	Left  = GFX.Clip[bg].Left[clip];
 472  			uint32	Right = GFX.Clip[bg].Right[clip];
 473  			uint32	Offset = Left + Y * GFX.PPL;
 474  			uint32	HScroll = LineData[Y].BG[bg].HOffset;
 475  			uint32	left_edge = (Left < (8 - (HScroll & 7)));
 476  			uint32	Width = Right - Left;
 477  
 478  			while (Left < Right)
 479  			{
 480  				uint32	VOffset, HOffset;
 481  
 482  				if (left_edge)
 483  				{
 484  					// SNES cannot do OPT for leftmost tile column
 485  					VOffset = LineData[Y].BG[bg].VOffset;
 486  					HOffset = HScroll;
 487  					left_edge = FALSE;
 488  				}
 489  				else
 490  				{
 491  					int HOffTile = ((HOff + Left - 1) & Offset2Mask) >> 3;
 492  
 493  					if (BG.OffsetSizeH == 8)
 494  					{
 495  						if (HOffTile > 31)
 496  							s = s2 + (HOffTile & 0x1f);
 497  						else
 498  							s = s1 + HOffTile;
 499  					}
 500  					else
 501  					{
 502  						if (HOffTile > 63)
 503  							s = s2 + ((HOffTile >> 1) & 0x1f);
 504  						else
 505  							s = s1 + (HOffTile >> 1);
 506  					}
 507  
 508  					uint16	HCellOffset = READ_WORD(s);
 509  					uint16	VCellOffset;
 510  
 511  					if (VOffOff)
 512  						VCellOffset = READ_WORD(s + VOffsetOffset);
 513  					else
 514  					{
 515  						if (HCellOffset & 0x8000)
 516  						{
 517  							VCellOffset = HCellOffset;
 518  							HCellOffset = 0;
 519  						}
 520  						else
 521  							VCellOffset = 0;
 522  					}
 523  
 524  					if (VCellOffset & OffsetEnableMask)
 525  						VOffset = VCellOffset + 1;
 526  					else
 527  						VOffset = LineData[Y].BG[bg].VOffset;
 528  
 529  					if (HCellOffset & OffsetEnableMask)
 530  						HOffset = (HCellOffset & ~7) | (HScroll & 7);
 531  					else
 532  						HOffset = HScroll;
 533  				}
 534  
 535  				uint32	t1, t2;
 536  				int		VirtAlign = ((Y + VOffset) & 7) << 3;
 537  				int		TilemapRow = (VOffset + Y) >> OffsetShift;
 538  
 539  				if ((VOffset + Y) & 8)
 540  				{
 541  					t1 = 16;
 542  					t2 = 0;
 543  				}
 544  				else
 545  				{
 546  					t1 = 0;
 547  					t2 = 16;
 548  				}
 549  
 550  				uint16	*b1, *b2;
 551  
 552  				if (TilemapRow & 0x20)
 553  				{
 554  					b1 = SC2;
 555  					b2 = SC3;
 556  				}
 557  				else
 558  				{
 559  					b1 = SC0;
 560  					b2 = SC1;
 561  				}
 562  
 563  				b1 += (TilemapRow & 0x1f) << 5;
 564  				b2 += (TilemapRow & 0x1f) << 5;
 565  
 566  				uint32	HPos = (HOffset + Left) & OffsetMask;
 567  				uint32	HTile = HPos >> 3;
 568  				uint16	*t;
 569  
 570  				if (BG.TileSizeH == 8)
 571  				{
 572  					if (HTile > 31)
 573  						t = b2 + (HTile & 0x1f);
 574  					else
 575  						t = b1 + HTile;
 576  				}
 577  				else
 578  				{
 579  					if (HTile > 63)
 580  						t = b2 + ((HTile >> 1) & 0x1f);
 581  					else
 582  						t = b1 + (HTile >> 1);
 583  				}
 584  
 585  				uint32	l = HPos & 7;
 586  				uint32	w = 8 - l;
 587  				if (w > Width)
 588  					w = Width;
 589  
 590  				Offset -= l;
 591  				Tile = READ_WORD(t);
 592  				GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl;
 593  
 594  				if (BG.TileSizeV == 16)
 595  					Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1));
 596  
 597  				if (BG.TileSizeH == 8)
 598  				{
 599  					DrawClippedTile(Tile, Offset, l, w, VirtAlign, 1);
 600  				}
 601  				else
 602  				{
 603  					if (!(Tile & H_FLIP))
 604  						DrawClippedTile(TILE_PLUS(Tile, (HTile & 1)), Offset, l, w, VirtAlign, 1);
 605  					else
 606  						DrawClippedTile(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, l, w, VirtAlign, 1);
 607  				}
 608  
 609  				Left += w;
 610  				Offset += 8;
 611  				Width -= w;
 612  			}
 613  		}
 614  	}
 615  }
 616  
 617  static void DrawBackgroundOffsetMosaic (int bg, uint8 Zh, uint8 Zl, int VOffOff)
 618  {
 619  	BG.TileAddress = PPU.BG[bg].NameBase << 1;
 620  
 621  	uint32	Tile;
 622  	uint16	*SC0, *SC1, *SC2, *SC3;
 623  	uint16	*BPS0, *BPS1, *BPS2, *BPS3;
 624  
 625  	BPS0 = (uint16 *) &Memory.VRAM[PPU.BG[2].SCBase << 1];
 626  	BPS1 = (PPU.BG[2].SCSize & 1) ? BPS0 + 1024 : BPS0;
 627  	if (BPS1 >= (uint16 *) (Memory.VRAM + 0x10000))
 628  		BPS1 -= 0x8000;
 629  	BPS2 = (PPU.BG[2].SCSize & 2) ? BPS1 + 1024 : BPS0;
 630  	if (BPS2 >= (uint16 *) (Memory.VRAM + 0x10000))
 631  		BPS2 -= 0x8000;
 632  	BPS3 = (PPU.BG[2].SCSize & 1) ? BPS2 + 1024 : BPS2;
 633  	if (BPS3 >= (uint16 *) (Memory.VRAM + 0x10000))
 634  		BPS3 -= 0x8000;
 635  
 636  	SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1];
 637  	SC1 = (PPU.BG[bg].SCSize & 1) ? SC0 + 1024 : SC0;
 638  	if (SC1 >= (uint16 *) (Memory.VRAM + 0x10000))
 639  		SC1 -= 0x8000;
 640  	SC2 = (PPU.BG[bg].SCSize & 2) ? SC1 + 1024 : SC0;
 641  	if (SC2 >= (uint16 *) (Memory.VRAM + 0x10000))
 642  		SC2 -= 0x8000;
 643  	SC3 = (PPU.BG[bg].SCSize & 1) ? SC2 + 1024 : SC2;
 644  	if (SC3 >= (uint16 *) (Memory.VRAM + 0x10000))
 645  		SC3 -= 0x8000;
 646  
 647  	int	Lines;
 648  	int	OffsetMask   = (BG.TileSizeH   == 16) ? 0x3ff : 0x1ff;
 649  	int	OffsetShift  = (BG.TileSizeV   == 16) ? 4 : 3;
 650  	int	Offset2Shift = (BG.OffsetSizeV == 16) ? 4 : 3;
 651  	int	OffsetEnableMask = 0x2000 << bg;
 652  
 653  	void (*DrawPix) (uint32, uint32, uint32, uint32, uint32, uint32);
 654  
 655  	int	MosaicStart = ((uint32) GFX.StartY - PPU.MosaicStart) % PPU.Mosaic;
 656  
 657  	for (int clip = 0; clip < GFX.Clip[bg].Count; clip++)
 658  	{
 659  		GFX.ClipColors = !(GFX.Clip[bg].DrawMode[clip] & 1);
 660  
 661  		if (BG.EnableMath && (GFX.Clip[bg].DrawMode[clip] & 2))
 662  			DrawPix = GFX.DrawMosaicPixelMath;
 663  		else
 664  			DrawPix = GFX.DrawMosaicPixelNomath;
 665  
 666  		for (uint32 Y = GFX.StartY - MosaicStart; Y <= GFX.EndY; Y += PPU.Mosaic)
 667  		{
 668  			uint32	VOff = LineData[Y + MosaicStart].BG[2].VOffset - 1;
 669  			uint32	HOff = LineData[Y + MosaicStart].BG[2].HOffset;
 670  
 671  			Lines = PPU.Mosaic - MosaicStart;
 672  			if (Y + MosaicStart + Lines > GFX.EndY)
 673  				Lines = GFX.EndY - Y - MosaicStart + 1;
 674  
 675  			uint32	HOffsetRow = VOff >> Offset2Shift;
 676  			uint32	VOffsetRow = (VOff + VOffOff) >> Offset2Shift;
 677  			uint16	*s, *s1, *s2;
 678  
 679  			if (HOffsetRow & 0x20)
 680  			{
 681  				s1 = BPS2;
 682  				s2 = BPS3;
 683  			}
 684  			else
 685  			{
 686  				s1 = BPS0;
 687  				s2 = BPS1;
 688  			}
 689  
 690  			s1 += (HOffsetRow & 0x1f) << 5;
 691  			s2 += (HOffsetRow & 0x1f) << 5;
 692  			s = ((VOffsetRow & 0x20) ? BPS2 : BPS0) + ((VOffsetRow & 0x1f) << 5);
 693  			int32	VOffsetOffset = s - s1;
 694  
 695  			uint32	Left =  GFX.Clip[bg].Left[clip];
 696  			uint32	Right = GFX.Clip[bg].Right[clip];
 697  			uint32	Offset = Left + (Y + MosaicStart) * GFX.PPL;
 698  			uint32	HScroll = LineData[Y + MosaicStart].BG[bg].HOffset;
 699  			uint32	Width = Right - Left;
 700  
 701  			while (Left < Right)
 702  			{
 703  				uint32	VOffset, HOffset;
 704  
 705  				if (Left < (8 - (HScroll & 7)))
 706  				{
 707  					// SNES cannot do OPT for leftmost tile column
 708  					VOffset = LineData[Y + MosaicStart].BG[bg].VOffset;
 709  					HOffset = HScroll;
 710  				}
 711  				else
 712  				{
 713  					int HOffTile = (((Left + (HScroll & 7)) - 8) + (HOff & ~7)) >> 3;
 714  
 715  					if (BG.OffsetSizeH == 8)
 716  					{
 717  						if (HOffTile > 31)
 718  							s = s2 + (HOffTile & 0x1f);
 719  						else
 720  							s = s1 + HOffTile;
 721  					}
 722  					else
 723  					{
 724  						if (HOffTile > 63)
 725  							s = s2 + ((HOffTile >> 1) & 0x1f);
 726  						else
 727  							s = s1 + (HOffTile >> 1);
 728  					}
 729  
 730  					uint16	HCellOffset = READ_WORD(s);
 731  					uint16	VCellOffset;
 732  
 733  					if (VOffOff)
 734  						VCellOffset = READ_WORD(s + VOffsetOffset);
 735  					else
 736  					{
 737  						if (HCellOffset & 0x8000)
 738  						{
 739  							VCellOffset = HCellOffset;
 740  							HCellOffset = 0;
 741  						}
 742  						else
 743  							VCellOffset = 0;
 744  					}
 745  
 746  					if (VCellOffset & OffsetEnableMask)
 747  						VOffset = VCellOffset + 1;
 748  					else
 749  						VOffset = LineData[Y + MosaicStart].BG[bg].VOffset;
 750  
 751  					if (HCellOffset & OffsetEnableMask)
 752  						HOffset = (HCellOffset & ~7) | (HScroll & 7);
 753  					else
 754  						HOffset = HScroll;
 755  				}
 756  
 757  				uint32	t1, t2;
 758  				int		VirtAlign = (((Y + VOffset) & 7) >> (0)) << 3;
 759  				int		TilemapRow = (VOffset + Y) >> OffsetShift;
 760  
 761  				if ((VOffset + Y) & 8)
 762  				{
 763  					t1 = 16;
 764  					t2 = 0;
 765  				}
 766  				else
 767  				{
 768  					t1 = 0;
 769  					t2 = 16;
 770  				}
 771  
 772  				uint16	*b1, *b2;
 773  
 774  				if (TilemapRow & 0x20)
 775  				{
 776  					b1 = SC2;
 777  					b2 = SC3;
 778  				}
 779  				else
 780  				{
 781  					b1 = SC0;
 782  					b2 = SC1;
 783  				}
 784  
 785  				b1 += (TilemapRow & 0x1f) << 5;
 786  				b2 += (TilemapRow & 0x1f) << 5;
 787  
 788  				uint32	HPos = (HOffset + Left - (Left % PPU.Mosaic)) & OffsetMask;
 789  				uint32	HTile = HPos >> 3;
 790  				uint16	*t;
 791  
 792  				if (BG.TileSizeH == 8)
 793  				{
 794  					if (HTile > 31)
 795  						t = b2 + (HTile & 0x1f);
 796  					else
 797  						t = b1 + HTile;
 798  				}
 799  				else
 800  				{
 801  					if (HTile > 63)
 802  						t = b2 + ((HTile >> 1) & 0x1f);
 803  					else
 804  						t = b1 + (HTile >> 1);
 805  				}
 806  
 807  				uint32	w = PPU.Mosaic - (Left % PPU.Mosaic);
 808  				if (w > Width)
 809  					w = Width;
 810  
 811  				Tile = READ_WORD(t);
 812  				GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl;
 813  
 814  				if (BG.TileSizeV == 16)
 815  					Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1));
 816  
 817  				if (BG.TileSizeH == 8)
 818  					DrawPix(Tile, Offset, VirtAlign, HPos & 7, w, Lines);
 819  				else
 820  				{
 821  					if (!(Tile & H_FLIP))
 822  						DrawPix(TILE_PLUS(Tile, (HTile & 1)), Offset, VirtAlign, HPos & 7, w, Lines);
 823  					else
 824  					if (!(Tile & V_FLIP))
 825  						DrawPix(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, VirtAlign, HPos & 7, w, Lines);
 826  				}
 827  
 828  				Left += w;
 829  				Offset += w;
 830  				Width -= w;
 831  			}
 832  
 833  			MosaicStart = 0;
 834  		}
 835  	}
 836  }
 837  
 838  static inline void DrawBackgroundMode7 (int bg, void (*DrawMath) (uint32, uint32, int), void (*DrawNomath) (uint32, uint32, int), int D)
 839  {
 840  	for (int clip = 0; clip < GFX.Clip[bg].Count; clip++)
 841  	{
 842  		GFX.ClipColors = !(GFX.Clip[bg].DrawMode[clip] & 1);
 843  
 844  		if (BG.EnableMath && (GFX.Clip[bg].DrawMode[clip] & 2))
 845  			DrawMath(GFX.Clip[bg].Left[clip], GFX.Clip[bg].Right[clip], D);
 846  		else
 847  			DrawNomath(GFX.Clip[bg].Left[clip], GFX.Clip[bg].Right[clip], D);
 848  	}
 849  }
 850  
 851  static inline void DrawBackdrop (void)
 852  {
 853  	uint32	Offset = GFX.StartY * GFX.PPL;
 854  
 855  	for (int clip = 0; clip < GFX.Clip[5].Count; clip++)
 856  	{
 857  		GFX.ClipColors = !(GFX.Clip[5].DrawMode[clip] & 1);
 858  
 859  		if (BG.EnableMath && (GFX.Clip[5].DrawMode[clip] & 2))
 860  			GFX.DrawBackdropMath(Offset, GFX.Clip[5].Left[clip], GFX.Clip[5].Right[clip]);
 861  		else
 862  			GFX.DrawBackdropNomath(Offset, GFX.Clip[5].Left[clip], GFX.Clip[5].Right[clip]);
 863  	}
 864  }
 865  
 866  static void SetupOBJ (void)
 867  {
 868  	int	SmallWidth, SmallHeight, LargeWidth, LargeHeight;
 869  
 870  	switch (PPU.OBJSizeSelect)
 871  	{
 872  		case 0:
 873  			SmallWidth = SmallHeight = 8;
 874  			LargeWidth = LargeHeight = 16;
 875  			break;
 876  
 877  		case 1:
 878  			SmallWidth = SmallHeight = 8;
 879  			LargeWidth = LargeHeight = 32;
 880  			break;
 881  
 882  		case 2:
 883  			SmallWidth = SmallHeight = 8;
 884  			LargeWidth = LargeHeight = 64;
 885  			break;
 886  
 887  		case 3:
 888  			SmallWidth = SmallHeight = 16;
 889  			LargeWidth = LargeHeight = 32;
 890  			break;
 891  
 892  		case 4:
 893  			SmallWidth = SmallHeight = 16;
 894  			LargeWidth = LargeHeight = 64;
 895  			break;
 896  
 897  		case 5:
 898  		default:
 899  			SmallWidth = SmallHeight = 32;
 900  			LargeWidth = LargeHeight = 64;
 901  			break;
 902  
 903  		case 6:
 904  			SmallWidth = 16; SmallHeight = 32;
 905  			LargeWidth = 32; LargeHeight = 64;
 906  			break;
 907  
 908  		case 7:
 909  			SmallWidth = 16; SmallHeight = 32;
 910  			LargeWidth = LargeHeight = 32;
 911  			break;
 912  	}
 913  
 914  	// OK, we have three cases here. Either there's no priority, priority is
 915  	// normal FirstSprite, or priority is FirstSprite+Y. The first two are
 916  	// easy, the last is somewhat more ... interesting. So we split them up.
 917  
 918  	uint8 LineOBJ[SNES_HEIGHT_EXTENDED];
 919  	memset(LineOBJ, 0, sizeof(LineOBJ));
 920  
 921  	int Height;
 922  
 923  	if (!PPU.OAMPriorityRotation || !(PPU.OAMFlip & PPU.OAMAddr & 1)) // normal case
 924  	{
 925  		for (int i = 0; i < SNES_HEIGHT_EXTENDED; i++)
 926  		{
 927  			GFX.OBJLines[i].RTOFlags = 0;
 928  			GFX.OBJLines[i].Tiles = SNES_SPRITE_TILE_PER_LINE;
 929  			for (int j = 0; j < 32; j++)
 930  				GFX.OBJLines[i].OBJ[j].Sprite = -1;
 931  		}
 932  
 933  		int	FirstSprite = PPU.FirstSprite;
 934  		int S = FirstSprite;
 935  
 936  		do
 937  		{
 938  			if (PPU.OBJ[S].Size)
 939  			{
 940  				GFX.OBJWidths[S] = LargeWidth;
 941  				Height = LargeHeight;
 942  			}
 943  			else
 944  			{
 945  				GFX.OBJWidths[S] = SmallWidth;
 946  				Height = SmallHeight;
 947  			}
 948  
 949  			int	HPos = PPU.OBJ[S].HPos;
 950  			if (HPos == -256)
 951  				HPos = 0;
 952  
 953  			if (HPos > -GFX.OBJWidths[S] && HPos <= 256)
 954  			{
 955  				if (HPos < 0)
 956  					GFX.OBJVisibleTiles[S] = (GFX.OBJWidths[S] + HPos + 7) >> 3;
 957  				else
 958  				if (HPos + GFX.OBJWidths[S] > 255)
 959  					GFX.OBJVisibleTiles[S] = (256 - HPos + 7) >> 3;
 960  				else
 961  					GFX.OBJVisibleTiles[S] = GFX.OBJWidths[S] >> 3;
 962  
 963  				for (int line = 0, Y = (uint8) (PPU.OBJ[S].VPos & 0xff); line < Height; Y++, line++)
 964  				{
 965  					if (Y >= SNES_HEIGHT_EXTENDED)
 966  						continue;
 967  
 968  					if (LineOBJ[Y] >= 32)
 969  					{
 970  						GFX.OBJLines[Y].RTOFlags |= 0x40;
 971  						continue;
 972  					}
 973  
 974  					GFX.OBJLines[Y].Tiles -= GFX.OBJVisibleTiles[S];
 975  					if (GFX.OBJLines[Y].Tiles < 0)
 976  						GFX.OBJLines[Y].RTOFlags |= 0x80;
 977  
 978  					GFX.OBJLines[Y].OBJ[LineOBJ[Y]].Sprite = S;
 979  					if (PPU.OBJ[S].VFlip)
 980  						// Yes, Width not Height. It so happens that the
 981  						// sprites with H=2*W flip as two WxW sprites.
 982  						GFX.OBJLines[Y].OBJ[LineOBJ[Y]].Line = line ^ (GFX.OBJWidths[S] - 1);
 983  					else
 984  						GFX.OBJLines[Y].OBJ[LineOBJ[Y]].Line = line;
 985  
 986  					LineOBJ[Y]++;
 987  				}
 988  			}
 989  
 990  			S = (S + 1) & 0x7f;
 991  		} while (S != FirstSprite);
 992  
 993  		for (int Y = 1; Y < SNES_HEIGHT_EXTENDED; Y++)
 994  			GFX.OBJLines[Y].RTOFlags |= GFX.OBJLines[Y - 1].RTOFlags;
 995  	}
 996  	else // evil FirstSprite+Y case
 997  	{
 998  		// printf("evil FirstSprite+Y case\n");
 999  
1000  		// We can't afford 30K of ram, we abuse the tile cache instead
1001  		uint8 *OBJOnLine = (uint8 *)IPPU.TileCacheData;
1002  		memset(IPPU.TileCache, 0, (SNES_HEIGHT_EXTENDED * 128) / 64);
1003  
1004  		for (int S = 0; S < 128; S++)
1005  		{
1006  			if (PPU.OBJ[S].Size)
1007  			{
1008  				GFX.OBJWidths[S] = LargeWidth;
1009  				Height = LargeHeight;
1010  			}
1011  			else
1012  			{
1013  				GFX.OBJWidths[S] = SmallWidth;
1014  				Height = SmallHeight;
1015  			}
1016  
1017  			int	HPos = PPU.OBJ[S].HPos;
1018  			if (HPos == -256)
1019  				HPos = 256;
1020  
1021  			if (HPos > -GFX.OBJWidths[S] && HPos <= 256)
1022  			{
1023  				if (HPos < 0)
1024  					GFX.OBJVisibleTiles[S] = (GFX.OBJWidths[S] + HPos + 7) >> 3;
1025  				else
1026  				if (HPos + GFX.OBJWidths[S] >= 257)
1027  					GFX.OBJVisibleTiles[S] = (257 - HPos + 7) >> 3;
1028  				else
1029  					GFX.OBJVisibleTiles[S] = GFX.OBJWidths[S] >> 3;
1030  
1031  				for (int line = 0, Y = (uint8) (PPU.OBJ[S].VPos & 0xff); line < Height; Y++, line++)
1032  				{
1033  					if (Y >= SNES_HEIGHT_EXTENDED)
1034  						continue;
1035  
1036  					if (!LineOBJ[Y]) {
1037  						memset(OBJOnLine + (Y * 128), 0, 128);
1038  						LineOBJ[Y] = TRUE;
1039  					}
1040  
1041  					if (PPU.OBJ[S].VFlip)
1042  						// Yes, Width not Height. It so happens that the
1043  						// sprites with H=2*W flip as two WxW sprites.
1044  						OBJOnLine[(Y * 128) + S] = (line ^ (GFX.OBJWidths[S] - 1)) | 0x80;
1045  					else
1046  						OBJOnLine[(Y * 128) + S] = line | 0x80;
1047  				}
1048  			}
1049  		}
1050  
1051  		// Now go through and pull out those OBJ that are actually visible.
1052  		int	j;
1053  		for (int Y = 0; Y < SNES_HEIGHT_EXTENDED; Y++)
1054  		{
1055  			GFX.OBJLines[Y].RTOFlags = Y ? GFX.OBJLines[Y - 1].RTOFlags : 0;
1056  			GFX.OBJLines[Y].Tiles = SNES_SPRITE_TILE_PER_LINE;
1057  
1058  			int	FirstSprite = (PPU.FirstSprite + Y) & 0x7f;
1059  			int S = FirstSprite;
1060  			j = 0;
1061  
1062  			if (LineOBJ[Y])
1063  			{
1064  				do
1065  				{
1066  					if (OBJOnLine[(Y * 128) + S])
1067  					{
1068  						if (j >= 32)
1069  						{
1070  							GFX.OBJLines[Y].RTOFlags |= 0x40;
1071  							break;
1072  						}
1073  
1074  						GFX.OBJLines[Y].Tiles -= GFX.OBJVisibleTiles[S];
1075  						if (GFX.OBJLines[Y].Tiles < 0)
1076  							GFX.OBJLines[Y].RTOFlags |= 0x80;
1077  						GFX.OBJLines[Y].OBJ[j].Sprite = S;
1078  						GFX.OBJLines[Y].OBJ[j++].Line = OBJOnLine[(Y * 128) + S] & ~0x80;
1079  					}
1080  
1081  					S = (S + 1) & 0x7f;
1082  				} while (S != FirstSprite);
1083  			}
1084  
1085  			if (j < 32)
1086  				GFX.OBJLines[Y].OBJ[j].Sprite = -1;
1087  		}
1088  	}
1089  
1090  	IPPU.OBJChanged = FALSE;
1091  }
1092  
1093  #if defined(__GNUC__) && !defined(__clang__)
1094  #pragma GCC push_options
1095  #pragma GCC optimize ("no-tree-vrp")
1096  #endif
1097  static void DrawOBJS (int D)
1098  {
1099  	void (*DrawTile) (uint32, uint32, uint32, uint32) = NULL;
1100  	void (*DrawClippedTile) (uint32, uint32, uint32, uint32, uint32, uint32) = NULL;
1101  
1102  	GFX.Z1 = 2;
1103  
1104  	for (uint32 Y = GFX.StartY, Offset = Y * GFX.PPL; Y <= GFX.EndY; Y++, Offset += GFX.PPL)
1105  	{
1106  		int	I = 0;
1107  		int	tiles = GFX.OBJLines[Y].Tiles;
1108  
1109  		for (int S = GFX.OBJLines[Y].OBJ[I].Sprite; S >= 0 && I < 32; S = GFX.OBJLines[Y].OBJ[++I].Sprite)
1110  		{
1111  			tiles += GFX.OBJVisibleTiles[S];
1112  			if (tiles <= 0)
1113  				continue;
1114  
1115  			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);
1116  			int	TileX = PPU.OBJ[S].Name & 0x0f;
1117  			int	TileLine = (GFX.OBJLines[Y].OBJ[I].Line & 7) * 8;
1118  			int	TileInc = 1;
1119  
1120  			if (PPU.OBJ[S].HFlip)
1121  			{
1122  				TileX = (TileX + (GFX.OBJWidths[S] >> 3) - 1) & 0x0f;
1123  				BaseTile |= H_FLIP;
1124  				TileInc = -1;
1125  			}
1126  
1127  			GFX.Z2 = D + PPU.OBJ[S].Priority * 4;
1128  
1129  			int	DrawMode = 3;
1130  			int	clip = 0, next_clip = -1000;
1131  			int	X = PPU.OBJ[S].HPos;
1132  			if (X == -256)
1133  				X = 256;
1134  
1135  			for (int t = tiles, O = Offset + X; X <= 256 && X < PPU.OBJ[S].HPos + GFX.OBJWidths[S]; TileX = (TileX + TileInc) & 0x0f, X += 8, O += 8)
1136  			{
1137  				if (X < -7 || --t < 0 || X == 256)
1138  					continue;
1139  
1140  				for (int x = X; x < X + 8;)
1141  				{
1142  					if (x >= next_clip)
1143  					{
1144  						for (; clip < GFX.Clip[4].Count && GFX.Clip[4].Left[clip] <= x; clip++) ;
1145  						if (clip == 0 || x >= GFX.Clip[4].Right[clip - 1])
1146  						{
1147  							DrawMode = 0;
1148  							next_clip = ((clip < GFX.Clip[4].Count) ? GFX.Clip[4].Left[clip] : 1000);
1149  						}
1150  						else
1151  						{
1152  							DrawMode = GFX.Clip[4].DrawMode[clip - 1];
1153  							next_clip = GFX.Clip[4].Right[clip - 1];
1154  							GFX.ClipColors = !(DrawMode & 1);
1155  
1156  							if (BG.EnableMath && (PPU.OBJ[S].Palette & 4) && (DrawMode & 2))
1157  							{
1158  								DrawTile = GFX.DrawTileMath;
1159  								DrawClippedTile = GFX.DrawClippedTileMath;
1160  							}
1161  							else
1162  							{
1163  								DrawTile = GFX.DrawTileNomath;
1164  								DrawClippedTile = GFX.DrawClippedTileNomath;
1165  							}
1166  						}
1167  					}
1168  
1169  					if (x == X && x + 8 < next_clip)
1170  					{
1171  						if (DrawMode)
1172  							DrawTile(BaseTile | TileX, O, TileLine, 1);
1173  						x += 8;
1174  					}
1175  					else
1176  					{
1177  						int	w = (next_clip <= X + 8) ? next_clip - x : X + 8 - x;
1178  						if (DrawMode)
1179  							DrawClippedTile(BaseTile | TileX, O, x - X, w, TileLine, 1);
1180  						x += w;
1181  					}
1182  				}
1183  			}
1184  		}
1185  	}
1186  }
1187  #if defined(__GNUC__) && !defined(__clang__)
1188  #pragma GCC pop_options
1189  #endif
1190  
1191  static inline void RenderScreen (bool8 sub)
1192  {
1193  	int BGActive, D;
1194  
1195  	if (!sub)
1196  	{	
1197  		GFX.S = GFX.Screen;
1198  		GFX.DB = GFX.ZBuffer;
1199  		GFX.Clip = IPPU.Clip[0];
1200  		BGActive = Memory.PPU_IO[0x12c] & ~Settings.BG_Forced;
1201  		D = 32;
1202  	}
1203  	else
1204  	{
1205  		GFX.S = GFX.SubScreen;
1206  		GFX.DB = GFX.SubZBuffer;
1207  		GFX.Clip = IPPU.Clip[1];
1208  		BGActive = Memory.PPU_IO[0x12d] & ~Settings.BG_Forced;
1209  		D = (Memory.PPU_IO[0x130] & 2) << 4; // 'do math' depth flag
1210  	}
1211  
1212  	if (BGActive & 0x10)
1213  	{
1214  		BG.TileAddress = PPU.OBJNameBase;
1215  		BG.NameSelect = PPU.OBJNameSelect;
1216  		BG.EnableMath = !sub && (Memory.PPU_IO[0x131] & 0x10);
1217  		BG.StartPalette = 128;
1218  		S9xSelectTileConverter(4, FALSE, sub, FALSE);
1219  		S9xSelectTileRenderers(PPU.BGMode, sub, TRUE);
1220  		DrawOBJS(D + 4);
1221  	}
1222  
1223  	BG.NameSelect = 0;
1224  	S9xSelectTileRenderers(PPU.BGMode, sub, FALSE);
1225  
1226  	#define DO_BG(n, pal, depth, hires, offset, Zh, Zl, voffoff) \
1227  		if (BGActive & (1 << n)) \
1228  		{ \
1229  			BG.StartPalette = pal; \
1230  			BG.EnableMath = !sub && (Memory.PPU_IO[0x131] & (1 << n)); \
1231  			BG.TileSizeH = (!hires && PPU.BG[n].BGSize) ? 16 : 8; \
1232  			BG.TileSizeV = (PPU.BG[n].BGSize) ? 16 : 8; \
1233  			S9xSelectTileConverter(depth, hires, sub, PPU.BGMosaic[n]); \
1234  			\
1235  			if (offset) \
1236  			{ \
1237  				BG.OffsetSizeH = (!hires && PPU.BG[2].BGSize) ? 16 : 8; \
1238  				BG.OffsetSizeV = (PPU.BG[2].BGSize) ? 16 : 8; \
1239  				\
1240  				if (PPU.BGMosaic[n] && (hires || PPU.Mosaic > 1)) \
1241  					DrawBackgroundOffsetMosaic(n, D + Zh, D + Zl, voffoff); \
1242  				else \
1243  					DrawBackgroundOffset(n, D + Zh, D + Zl, voffoff); \
1244  			} \
1245  			else \
1246  			{ \
1247  				if (PPU.BGMosaic[n] && (hires || PPU.Mosaic > 1)) \
1248  					DrawBackgroundMosaic(n, D + Zh, D + Zl); \
1249  				else \
1250  					DrawBackground(n, D + Zh, D + Zl); \
1251  			} \
1252  		}
1253  
1254  	switch (PPU.BGMode)
1255  	{
1256  		case 0:
1257  			DO_BG(0,  0, 2, FALSE, FALSE, 15, 11, 0);
1258  			DO_BG(1, 32, 2, FALSE, FALSE, 14, 10, 0);
1259  			DO_BG(2, 64, 2, FALSE, FALSE,  7,  3, 0);
1260  			DO_BG(3, 96, 2, FALSE, FALSE,  6,  2, 0);
1261  			break;
1262  
1263  		case 1:
1264  			DO_BG(0,  0, 4, FALSE, FALSE, 15, 11, 0);
1265  			DO_BG(1,  0, 4, FALSE, FALSE, 14, 10, 0);
1266  			DO_BG(2,  0, 2, FALSE, FALSE, (PPU.BG3Priority ? 17 : 7), 3, 0);
1267  			break;
1268  
1269  		case 2:
1270  			DO_BG(0,  0, 4, FALSE, TRUE,  15,  7, 8);
1271  			DO_BG(1,  0, 4, FALSE, TRUE,  11,  3, 8);
1272  			break;
1273  
1274  		case 3:
1275  			DO_BG(0,  0, 8, FALSE, FALSE, 15,  7, 0);
1276  			DO_BG(1,  0, 4, FALSE, FALSE, 11,  3, 0);
1277  			break;
1278  
1279  		case 4:
1280  			DO_BG(0,  0, 8, FALSE, TRUE,  15,  7, 0);
1281  			DO_BG(1,  0, 2, FALSE, TRUE,  11,  3, 0);
1282  			break;
1283  
1284  		case 5:
1285  			DO_BG(0,  0, 4, TRUE,  FALSE, 15,  7, 0);
1286  			DO_BG(1,  0, 2, TRUE,  FALSE, 11,  3, 0);
1287  			break;
1288  
1289  		case 6:
1290  			DO_BG(0,  0, 4, TRUE,  TRUE,  15,  7, 8);
1291  			break;
1292  
1293  		case 7:
1294  			if (BGActive & 0x01)
1295  			{
1296  				BG.EnableMath = !sub && (Memory.PPU_IO[0x131] & 1);
1297  				DrawBackgroundMode7(0, GFX.DrawMode7BG1Math, GFX.DrawMode7BG1Nomath, D);
1298  			}
1299  
1300  			if ((Memory.PPU_IO[0x133] & 0x40) && (BGActive & 0x02))
1301  			{
1302  				BG.EnableMath = !sub && (Memory.PPU_IO[0x131] & 2);
1303  				DrawBackgroundMode7(1, GFX.DrawMode7BG2Math, GFX.DrawMode7BG2Nomath, D);
1304  			}
1305  			break;
1306  	}
1307  
1308  	#undef DO_BG
1309  
1310  	BG.EnableMath = !sub && (Memory.PPU_IO[0x131] & 0x20);
1311  
1312  	DrawBackdrop();
1313  }
1314  
1315  static inline uint8 CalcWindowMask (int i, uint8 W1, uint8 W2)
1316  {
1317  	if (!PPU.ClipWindow1Enable[i])
1318  	{
1319  		if (!PPU.ClipWindow2Enable[i])
1320  			return (0);
1321  		if (!PPU.ClipWindow2Inside[i])
1322  			return (~W2);
1323  		return (W2);
1324  	}
1325  	else
1326  	{
1327  		if (!PPU.ClipWindow2Enable[i])
1328  		{
1329  			if (!PPU.ClipWindow1Inside[i])
1330  				return (~W1);
1331  			return (W1);
1332  		}
1333  		else
1334  		{
1335  			if (!PPU.ClipWindow1Inside[i])
1336  				W1 = ~W1;
1337  			if (!PPU.ClipWindow2Inside[i])
1338  				W2 = ~W2;
1339  
1340  			switch (PPU.ClipWindowOverlapLogic[i])
1341  			{
1342  				case 0: // OR
1343  					return (W1 | W2);
1344  
1345  				case 1: // AND
1346  					return (W1 & W2);
1347  
1348  				case 2: // XOR
1349  					return (W1 ^ W2);
1350  
1351  				case 3: // XNOR
1352  					return (~(W1 ^ W2));
1353  			}
1354  		}
1355  	}
1356  
1357  	// Never get here
1358  	return (0);
1359  }
1360  
1361  static inline void StoreWindowRegions (uint8 Mask, struct ClipData *Clip, int n_regions, int16 *windows, uint8 *drawing_modes, bool8 sub, bool8 StoreMode0)
1362  {
1363  	int	ct = 0;
1364  
1365  	for (int j = 0; j < n_regions; j++)
1366  	{
1367  		int	DrawMode = drawing_modes[j];
1368  		if (sub)
1369  			DrawMode |= 1;
1370  		if (Mask & (1 << j))
1371  			DrawMode = 0;
1372  
1373  		if (!StoreMode0 && !DrawMode)
1374  			continue;
1375  
1376  		if (ct > 0 && Clip->Right[ct - 1] == windows[j] && Clip->DrawMode[ct - 1] == DrawMode)
1377  			Clip->Right[ct - 1] = windows[j + 1]; // This region borders with and has the same drawing mode as the previous region: merge them.
1378  		else
1379  		{
1380  			// Add a new region to the BG
1381  			Clip->Left[ct]     = windows[j];
1382  			Clip->Right[ct]    = windows[j + 1];
1383  			Clip->DrawMode[ct] = DrawMode;
1384  			ct++;
1385  		}
1386  	}
1387  
1388  	Clip->Count = ct;
1389  }
1390  
1391  static inline void ComputeClipWindows (void)
1392  {
1393  	int16	windows[6] = { 0, 256, 256, 256, 256, 256 };
1394  	uint8	drawing_modes[5] = { 0, 0, 0, 0, 0 };
1395  	int		n_regions = 1;
1396  	int		i, j;
1397  
1398  	// Calculate window regions. We have at most 5 regions, because we have 6 control points
1399  	// (screen edges, window 1 left & right, and window 2 left & right).
1400  
1401  	if (PPU.Window1Left <= PPU.Window1Right)
1402  	{
1403  		if (PPU.Window1Left > 0)
1404  		{
1405  			windows[2] = 256;
1406  			windows[1] = PPU.Window1Left;
1407  			n_regions = 2;
1408  		}
1409  
1410  		if (PPU.Window1Right < 255)
1411  		{
1412  			windows[n_regions + 1] = 256;
1413  			windows[n_regions] = PPU.Window1Right + 1;
1414  			n_regions++;
1415  		}
1416  	}
1417  
1418  	if (PPU.Window2Left <= PPU.Window2Right)
1419  	{
1420  		for (i = 0; i <= n_regions; i++)
1421  		{
1422  			if (PPU.Window2Left == windows[i])
1423  				break;
1424  
1425  			if (PPU.Window2Left <  windows[i])
1426  			{
1427  				for (j = n_regions; j >= i; j--)
1428  					windows[j + 1] = windows[j];
1429  
1430  				windows[i] = PPU.Window2Left;
1431  				n_regions++;
1432  				break;
1433  			}
1434  		}
1435  
1436  		for (; i <= n_regions; i++)
1437  		{
1438  			if (PPU.Window2Right + 1 == windows[i])
1439  				break;
1440  
1441  			if (PPU.Window2Right + 1 <  windows[i])
1442  			{
1443  				for (j = n_regions; j >= i; j--)
1444  					windows[j + 1] = windows[j];
1445  
1446  				windows[i] = PPU.Window2Right + 1;
1447  				n_regions++;
1448  				break;
1449  			}
1450  		}
1451  	}
1452  
1453  	// Get a bitmap of which regions correspond to each window.
1454  
1455  	const uint8 region_map[6][6] =
1456  	{
1457  		{ 0, 0x01, 0x03, 0x07, 0x0f, 0x1f },
1458  		{ 0,    0, 0x02, 0x06, 0x0e, 0x1e },
1459  		{ 0,    0,    0, 0x04, 0x0c, 0x1c },
1460  		{ 0,    0,    0,    0, 0x08, 0x18 },
1461  		{ 0,    0,    0,    0,    0, 0x10 }
1462  	};
1463  
1464  	uint8 W1 = 0;
1465  	uint8 W2 = 0;
1466  
1467  	if (PPU.Window1Left <= PPU.Window1Right)
1468  	{
1469  		for (i = 0; windows[i] != PPU.Window1Left; i++) ;
1470  		for (j = i; windows[j] != PPU.Window1Right + 1; j++) ;
1471  		W1 = region_map[i][j];
1472  	}
1473  
1474  	if (PPU.Window2Left <= PPU.Window2Right)
1475  	{
1476  		for (i = 0; windows[i] != PPU.Window2Left; i++) ;
1477  		for (j = i; windows[j] != PPU.Window2Right + 1; j++) ;
1478  		W2 = region_map[i][j];
1479  	}
1480  
1481  	// Color Window affects the drawing mode for each region.
1482  	// Modes are: 3=Draw as normal, 2=clip color (math only), 1=no math (draw only), 0=nothing.
1483  
1484  	uint8	CW_color = 0, CW_math = 0;
1485  	uint8	CW = CalcWindowMask(5, W1, W2);
1486  
1487  	switch (Memory.PPU_IO[0x130] & 0xc0)
1488  	{
1489  		case 0x00:	CW_color = 0;		break;
1490  		case 0x40:	CW_color = ~CW;		break;
1491  		case 0x80:	CW_color = CW;		break;
1492  		case 0xc0:	CW_color = 0xff;	break;
1493  	}
1494  
1495  	switch (Memory.PPU_IO[0x130] & 0x30)
1496  	{
1497  		case 0x00:	CW_math  = 0;		break;
1498  		case 0x10:	CW_math  = ~CW;		break;
1499  		case 0x20:	CW_math  = CW;		break;
1500  		case 0x30:	CW_math  = 0xff;	break;
1501  	}
1502  
1503  	for (int i = 0; i < n_regions; i++)
1504  	{
1505  		if (!(CW_color & (1 << i)))
1506  			drawing_modes[i] |= 1;
1507  		if (!(CW_math  & (1 << i)))
1508  			drawing_modes[i] |= 2;
1509  	}
1510  
1511  	// Store backdrop clip window (draw everywhere color window allows)
1512  
1513  	StoreWindowRegions(0, &IPPU.Clip[0][5], n_regions, windows, drawing_modes, FALSE, TRUE);
1514  	StoreWindowRegions(0, &IPPU.Clip[1][5], n_regions, windows, drawing_modes, TRUE,  TRUE);
1515  
1516  	// Store per-BG and OBJ clip windows
1517  
1518  	for (int j = 0; j < 5; j++)
1519  	{
1520  		if (Memory.PPU_IO[0x12e] & (1 << j))
1521  			StoreWindowRegions(CalcWindowMask(j, W1, W2), &IPPU.Clip[0][j], n_regions, windows, drawing_modes, 0, FALSE);
1522  		else
1523  			StoreWindowRegions(0, &IPPU.Clip[0][j], n_regions, windows, drawing_modes, 0, FALSE);
1524  
1525  		if (Memory.PPU_IO[0x12f] & (1 << j))
1526  			StoreWindowRegions(CalcWindowMask(j, W1, W2), &IPPU.Clip[1][j], n_regions, windows, drawing_modes, 1, FALSE);
1527  		else
1528  			StoreWindowRegions(0, &IPPU.Clip[1][j], n_regions, windows, drawing_modes, 1, FALSE);
1529  	}
1530  }
1531  
1532  bool8 S9xGraphicsInit (void)
1533  {
1534  	GFX.PPL = SNES_WIDTH;
1535  	GFX.ScreenSize = GFX.PPL * SNES_HEIGHT_EXTENDED;
1536  	IPPU.OBJChanged = TRUE;
1537  	Settings.BG_Forced = 0;
1538  
1539  	S9xInitTileRenderer();
1540  
1541    GFX.Screen = (SNESPixel *) emu_Malloc(GFX.ScreenSize * sizeof(SNESPixel));
1542  	GFX.SubScreen  = (SNESPixel *) emu_SMalloc(GFX.ScreenSize * sizeof(SNESPixel));
1543    memset((unsigned char*)GFX.Screen, 0, GFX.ScreenSize * sizeof(SNESPixel));
1544    memset((unsigned char*)GFX.SubScreen, 0, GFX.ScreenSize * sizeof(SNESPixel));
1545    GFX.ZBuffer    = (uint8 *)  emu_Malloc(GFX.ScreenSize);
1546  	GFX.SubZBuffer = (uint8 *)  emu_Malloc(GFX.ScreenSize);
1547  	//GFX.ZERO       = (SNESPixel *) GFX.SubScreen; // This will cause garbage but for now it's okay
1548  	IPPU.TileCacheData = (uint8 *) emu_SMalloc(4096 * 64);
1549  
1550  	if (!GFX.SubScreen || !GFX.ZBuffer || !GFX.SubZBuffer || !IPPU.TileCacheData)
1551  	{
1552  		S9xGraphicsDeinit();
1553  		return (FALSE);
1554  	}
1555  
1556  	#if 0 // TO DO: pre-compute GFX.ZERO
1557  
1558  	// Lookup table for 1/2 color subtraction
1559  	//memset(GFX.ZERO, 0, 0x10000 * sizeof(uint16));
1560  	for (uint8 r = 0; r <= MAX_RED; r++)
1561  	{
1562  		uint8 r2 = (r & 0x10) ? (r & ~0x10) : (0);
1563  
1564  		for (uint8 g = 0; g <= MAX_GREEN; g++)
1565  		{
1566  			uint8 g2 = (g & GREEN_HI_BIT) ? (g & ~GREEN_HI_BIT) : (0);
1567  
1568  			for (uint8 b = 0; b <= MAX_BLUE; b++)
1569  			{
1570  				uint8 b2 = (b & 0x10) ? (b & ~0x10) : (0);
1571  
1572  				GFX.ZERO[BUILD_PIXEL2(r, g, b)] = BUILD_PIXEL2(r2, g2, b2);
1573  				GFX.ZERO[BUILD_PIXEL2(r, g, b) & ~ALPHA_BITS_MASK] = BUILD_PIXEL2(r2, g2, b2);
1574  			}
1575  		}
1576  	}
1577  	#endif
1578  
1579  	return (TRUE);
1580  }
1581  
1582  void S9xGraphicsDeinit (void)
1583  {
1584  	if (GFX.SubScreen)  { emu_SFree(GFX.SubScreen);  GFX.SubScreen  = NULL; }
1585  	if (GFX.ZBuffer)    { emu_Free(GFX.ZBuffer);    GFX.ZBuffer    = NULL; }
1586  	if (GFX.SubZBuffer) { emu_SFree(GFX.SubZBuffer); GFX.SubZBuffer = NULL; }
1587  	if (IPPU.TileCacheData) { emu_SFree(IPPU.TileCacheData); IPPU.TileCacheData = NULL; }
1588  }
1589  
1590  void S9xGraphicsScreenResize (void)
1591  {
1592  	IPPU.RenderedScreenHeight = PPU.ScreenHeight;
1593  	IPPU.RenderedScreenWidth = SNES_WIDTH;
1594  	IPPU.MaxBrightness = PPU.Brightness;
1595  
1596  	IPPU.Interlace    = Memory.PPU_IO[0x133] & 1;
1597  	IPPU.InterlaceOBJ = Memory.PPU_IO[0x133] & 2;
1598  	IPPU.PseudoHires  = Memory.PPU_IO[0x133] & 8;
1599  
1600  	static uint8 prev2133 = 0;
1601  	if (prev2133 != Memory.PPU_IO[0x133])
1602  	{
1603  		sprintf(String, "Int=%d IntOBJ=%d Hires=%d\n", IPPU.Interlace, IPPU.InterlaceOBJ, IPPU.PseudoHires);
1604  		S9xMessage(0, 0, String);
1605  		prev2133 = Memory.PPU_IO[0x133];
1606  	}
1607  }
1608  
1609  void S9xStartScreenRefresh (void)
1610  {
1611  	if (IPPU.RenderThisFrame)
1612  	{
1613  		S9xGraphicsScreenResize();
1614  
1615  		IPPU.RenderedFramesCount++;
1616  
1617  		PPU.MosaicStart = 0;
1618  		PPU.RecomputeClipWindows = TRUE;
1619  		IPPU.PreviousLine = IPPU.CurrentLine = 0;
1620  
1621  		memset(GFX.ZBuffer, 0, GFX.ScreenSize);
1622  		memset(GFX.SubZBuffer, 0, GFX.ScreenSize);
1623  	}
1624  
1625  	if (++IPPU.FrameCount % Settings.FrameRate == 0)
1626  	{
1627  		IPPU.DisplayedRenderedFrameCount = IPPU.RenderedFramesCount;
1628  		IPPU.RenderedFramesCount = 0;
1629  		IPPU.FrameCount = 0;
1630  	}
1631  
1632  	if (GFX.InfoStringTimeout > 0 && --GFX.InfoStringTimeout == 0)
1633  		GFX.InfoString = NULL;
1634  
1635  	IPPU.TotalEmulatedFrames++;
1636  }
1637  
1638  void S9xEndScreenRefresh (void)
1639  {
1640  	if (IPPU.RenderThisFrame)
1641  	{
1642  		FLUSH_REDRAW();
1643  		//S9xDisplayMessages(GFX.Screen, GFX.PPL, IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight, 1);
1644  		S9xBlitUpdate(IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight);
1645  	}
1646  
1647  #ifdef DEBUGGER
1648  	if (CPU.Flags & FRAME_ADVANCE_FLAG)
1649  	{
1650  		if (ICPU.FrameAdvanceCount)
1651  		{
1652  			ICPU.FrameAdvanceCount--;
1653  			IPPU.RenderThisFrame = TRUE;
1654  			IPPU.FrameSkip = 0;
1655  		}
1656  		else
1657  		{
1658  			CPU.Flags &= ~FRAME_ADVANCE_FLAG;
1659  			CPU.Flags |= DEBUG_MODE_FLAG;
1660  		}
1661  	}
1662  #endif
1663  }
1664  
1665  void S9xRenderLine (int C)
1666  {
1667  	if (IPPU.RenderThisFrame)
1668  	{
1669  		LineData[C].BG[0].VOffset = PPU.BG[0].VOffset + 1;
1670  		LineData[C].BG[0].HOffset = PPU.BG[0].HOffset;
1671  		LineData[C].BG[1].VOffset = PPU.BG[1].VOffset + 1;
1672  		LineData[C].BG[1].HOffset = PPU.BG[1].HOffset;
1673  
1674  		if (PPU.BGMode == 7)
1675  		{
1676  			S9UpdateLineMatrix(C);
1677  		}
1678  		else
1679  		{
1680  			LineData[C].BG[2].VOffset = PPU.BG[2].VOffset + 1;
1681  			LineData[C].BG[2].HOffset = PPU.BG[2].HOffset;
1682  			LineData[C].BG[3].VOffset = PPU.BG[3].VOffset + 1;
1683  			LineData[C].BG[3].HOffset = PPU.BG[3].HOffset;
1684  		}
1685  
1686  		IPPU.CurrentLine = C + 1;
1687  	}
1688  	else
1689  	{
1690  		// if we're not rendering this frame, we still need to update this
1691  		// XXX: Check ForceBlank? Or anything else?
1692  		if (IPPU.OBJChanged)
1693  			SetupOBJ();
1694  		PPU.RangeTimeOver |= GFX.OBJLines[C].RTOFlags;
1695  	}
1696  }
1697  
1698  
1699  void S9xUpdateScreen (void)
1700  {
1701  	if (IPPU.OBJChanged)
1702  		SetupOBJ();
1703  
1704  	// XXX: Check ForceBlank? Or anything else?
1705  	PPU.RangeTimeOver |= GFX.OBJLines[GFX.EndY].RTOFlags;
1706  
1707  	GFX.StartY = IPPU.PreviousLine;
1708  	if ((GFX.EndY = IPPU.CurrentLine - 1) >= PPU.ScreenHeight)
1709  		GFX.EndY = PPU.ScreenHeight - 1;
1710  
1711  	if (!PPU.ForcedBlanking)
1712  	{
1713  		// If force blank, may as well completely skip all this. We only did
1714  		// the OBJ because (AFAWK) the RTO flags are updated even during force-blank.
1715  
1716  		if (PPU.RecomputeClipWindows)
1717  		{
1718  			ComputeClipWindows();
1719  			PPU.RecomputeClipWindows = FALSE;
1720  		}
1721  
1722  		if ((Memory.PPU_IO[0x130] & 0x30) != 0x30 && (Memory.PPU_IO[0x131] & 0x3f))
1723  			GFX.FixedColour = BUILD_PIXEL(IPPU.XB[PPU.FixedColourRed], IPPU.XB[PPU.FixedColourGreen], IPPU.XB[PPU.FixedColourBlue]);
1724  
1725  		if (PPU.BGMode == 5 || PPU.BGMode == 6 || IPPU.PseudoHires ||
1726  			((Memory.PPU_IO[0x130] & 0x30) != 0x30 && (Memory.PPU_IO[0x130] & 2) && (Memory.PPU_IO[0x131] & 0x3f) && (Memory.PPU_IO[0x12d] & 0x1f)))
1727  			// If hires (Mode 5/6 or pseudo-hires) or math is to be done
1728  			// involving the subscreen, then we need to render the subscreen...
1729  			RenderScreen(TRUE);
1730  
1731  		RenderScreen(FALSE);
1732  	}
1733  //	else
1734  //	{
1735  //		GFX.S = (SNESPixel*)GFX.Screen + GFX.StartY * GFX.PPL;
1736  //		// Set to black
1737  //		for (int l = GFX.StartY; l <= GFX.EndY; l++, GFX.S += GFX.PPL) {
1738  //			memset(GFX.S, 0xff, IPPU.RenderedScreenWidth * sizeof(SNESPixel));			
1739  //		}	
1740  //	}
1741  
1742  	IPPU.PreviousLine = IPPU.CurrentLine;
1743  }
1744  
1745  void S9xSetInfoString (const char *string)
1746  {
1747  	if (Settings.InitialInfoStringTimeout > 0)
1748  	{
1749  		GFX.InfoString = string;
1750  		GFX.InfoStringTimeout = Settings.InitialInfoStringTimeout;
1751  		if (Settings.Paused)
1752  			S9xBlitUpdate(IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight);
1753  	}
1754  }
1755  
1756  /*
1757  static inline void DisplayChar (uint16 *s, uint8 c)
1758  {
1759  	const uint16	black = BUILD_PIXEL(0, 0, 0);
1760  
1761  	int	line   = ((c - 32) >> 4) * FONT_HEIGHT;
1762  	int	offset = ((c - 32) & 15) * FONT_WIDTH;
1763  
1764  	for (int h = 0; h < FONT_HEIGHT; h++, line++, s += GFX.PPL - FONT_WIDTH)
1765  	{
1766  		for (int w = 0; w < FONT_WIDTH; w++, s++)
1767  		{
1768  			char	p = font[line][offset + w];
1769  
1770  			if (p == '#')
1771  				*s = Settings.DisplayColor;
1772  			else
1773  			if (p == '.')
1774  				*s = black;
1775  		}
1776  	}
1777  }
1778  
1779  static inline void DisplayStringFromBottom (const char *string, int linesFromBottom, int pixelsFromLeft, bool allowWrap)
1780  {
1781  	if (linesFromBottom <= 0)
1782  		linesFromBottom = 1;
1783  
1784  	uint16	*dst = GFX.Screen + (IPPU.RenderedScreenHeight - FONT_HEIGHT * linesFromBottom) * GFX.PPL + pixelsFromLeft;
1785  
1786  	int	len = strlen(string);
1787  	int	max_chars = IPPU.RenderedScreenWidth / (FONT_WIDTH - 1);
1788  	int	char_count = 0;
1789  
1790  	for (int i = 0 ; i < len ; i++, char_count++)
1791  	{
1792  		if (char_count >= max_chars || (uint8) string[i] < 32)
1793  		{
1794  			if (!allowWrap)
1795  				break;
1796  
1797  			dst += FONT_HEIGHT * GFX.PPL - (FONT_WIDTH - 1) * max_chars;
1798  			if (dst >= GFX.Screen + IPPU.RenderedScreenHeight * GFX.PPL)
1799  				break;
1800  
1801  			char_count -= max_chars;
1802  		}
1803  
1804  		if ((uint8) string[i] < 32)
1805  			continue;
1806  
1807  		DisplayChar(dst, string[i]);
1808  		dst += FONT_WIDTH - 1;
1809  	}
1810  }
1811  */
1812  
1813  void S9xDisplayMessages (SNESPixel *screen, int ppl, int width, int height, int scale)
1814  {
1815  //	if (GFX.InfoString && *GFX.InfoString)
1816  //		DisplayStringFromBottom(GFX.InfoString, 5, 1, true);
1817  }