/*
 * Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
 *
 * (c) Copyright 1996, 1997, 1998 Gary Henderson (gary@daniver.demon.co.uk) and
 *                                Jerremy Koot (jkoot@snes9x.com)
 *
 * Super FX C emulator code 
 * (c) Copyright 1997, 1998 Ivar (Ivar@snes9x.com) and
 *                          Gary Henderson.
 * Super FX assembler emulator code (c) Copyright 1998 zsKnight and _Demo_.
 *
 * DSP1 emulator code (c) Copyright 1998 Ivar, _Demo_ and Gary Henderson.
 * DOS port code contains the works of other authors. See headers in
 * individual files.
 *
 * Snes9x homepage: www.snes9x.com
 *
 * Permission to use, copy, modify and distribute Snes9x in both binary and
 * source form, for non-commercial purposes, is hereby granted without fee,
 * providing that this license information and copyright notice appear with
 * all copies and any derived work.
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event shall the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Snes9x is freeware for PERSONAL USE only. Commercial users should
 * seek permission of the copyright holders first. Commercial use includes
 * charging money for Snes9x or software derived from Snes9x.
 *
 * The copyright holders request that bug fixes and improvements to the code
 * should be forwarded to them so everyone can benefit from the modifications
 * in future versions.
 *
 * Super NES and Super Nintendo Entertainment System are trademarks of
 * Nintendo Co., Limited and its subsidiary companies.
 */

#include "snes9x.h"

#include "memmap.h"
#include "ppu.h"
#include "cpuexec.h"
#include "display.h"
#include "gfx.h"
#include "apu.h"

void ComputeClipWindows ();
static void S9xDisplayFrameRate ();
static void S9xDisplayString (const char *string);

extern int StartY;
extern int EndY;
extern struct ClipData *pCurrentClip;
extern uint8 BitShifts[8][4];
extern uint8 TileShifts[8][4];
extern uint8 PaletteShifts[8][4];
extern uint8 PaletteMasks[8][4];
extern uint8 Depths[8][4];
extern uint8 BGSizes [2];

extern NormalTileRenderer DrawTilePtr;
extern ClippedTileRenderer DrawClippedTilePtr;
extern NormalTileRenderer DrawHiResTilePtr;
extern ClippedTileRenderer DrawHiResClippedTilePtr;
extern LargePixelRenderer DrawLargePixelPtr;
extern int PixSize;

extern int OBJList [4][129];
extern int Sizes [129];
extern int VPositions [129];

extern struct SBG BG;

extern struct SLineData LineData[240];
extern struct SLineMatrixData LineMatrixData [240];

extern uint32 Mode7Mask;

#define ON_MAIN(N) \
(Memory.FillRAM [0x212c] & (1 << (N)) && \
 !(PPU.BG_Forced & (1 << (N))))

#define SUB_OR_ADD(N) \
(Memory.FillRAM [0x2131] & (1 << (N)))

#define ON_SUB(N) \
((Memory.FillRAM [0x2130] & 0x30) != 0x30 && \
 (Memory.FillRAM [0x2130] & 2) && \
 (Memory.FillRAM [0x212d] & (1 << N)) && \
 !(PPU.BG_Forced & (1 << (N))))

#define ANYTHING_ON_SUB \
((Memory.FillRAM [0x2130] & 0x30) != 0x30 && \
 (Memory.FillRAM [0x2130] & 2) && \
 (Memory.FillRAM [0x212d] & 0x1f))

#define SUB_ENABLED \
(Memory.FillRAM [0x2130] & 2)

#define FIXED_ENABLED \
((Memory.FillRAM [0x2130] & 2) == 0 && \
 (PPU.FixedColourRed || PPU.FixedColourGreen || PPU.FixedColourBlue))

#define ADD_OR_SUB_ON_ANYTHING \
(Memory.FillRAM [0x2131] & 0x3f)

void DrawTile (uint32 Tile, uint32 Offset, uint32 StartLine,
	       uint32 LineCount, uint8 Z = 0);
void DrawClippedTile (uint32 Tile, uint32 Offset,
		      uint32 StartPixel, uint32 Width,
		      uint32 StartLine, uint32 LineCount, uint8 Z = 0);
void DrawTilex2 (uint32 Tile, uint32 Offset, uint32 StartLine,
		 uint32 LineCount, uint8 Z = 0);
void DrawClippedTilex2 (uint32 Tile, uint32 Offset,
			uint32 StartPixel, uint32 Width,
			uint32 StartLine, uint32 LineCount, uint8 Z = 0);
void DrawTilex2x2 (uint32 Tile, uint32 Offset, uint32 StartLine,
	       uint32 LineCount, uint8 Z = 0);
void DrawClippedTilex2x2 (uint32 Tile, uint32 Offset,
			  uint32 StartPixel, uint32 Width,
			  uint32 StartLine, uint32 LineCount, uint8 Z = 0);
void DrawLargePixel (uint32 Tile, uint32 Offset,
		     uint32 StartPixel, uint32 Pixels,
		     uint32 StartLine, uint32 LineCount, uint8 Z = 0);

void DrawTile16 (uint32 Tile, uint32 Offset, uint32 StartLine,
	         uint32 LineCount, uint8 Z = 0);
void DrawClippedTile16 (uint32 Tile, uint32 Offset,
		        uint32 StartPixel, uint32 Width,
		        uint32 StartLine, uint32 LineCount, uint8 Z = 0);
void DrawTile16x2 (uint32 Tile, uint32 Offset, uint32 StartLine,
		   uint32 LineCount, uint8 Z = 0);
void DrawClippedTile16x2 (uint32 Tile, uint32 Offset,
			  uint32 StartPixel, uint32 Width,
			  uint32 StartLine, uint32 LineCount, uint8 Z = 0);
void DrawTile16x2x2 (uint32 Tile, uint32 Offset, uint32 StartLine,
		     uint32 LineCount, uint8 Z = 0);
void DrawClippedTile16x2x2 (uint32 Tile, uint32 Offset,
			    uint32 StartPixel, uint32 Width,
			    uint32 StartLine, uint32 LineCount, uint8 Z = 0);
void DrawLargePixel16 (uint32 Tile, uint32 Offset,
		       uint32 StartPixel, uint32 Pixels,
		       uint32 StartLine, uint32 LineCount, uint8 Z = 0);

void DrawTile16Add (uint32 Tile, uint32 Offset, uint32 StartLine,
		    uint32 LineCount, uint8 Z = 0);

void DrawClippedTile16Add (uint32 Tile, uint32 Offset,
			   uint32 StartPixel, uint32 Width,
			   uint32 StartLine, uint32 LineCount, uint8 Z = 0);

void DrawTile16Add1_2 (uint32 Tile, uint32 Offset, uint32 StartLine,
		       uint32 LineCount, uint8 Z = 0);

void DrawClippedTile16Add1_2 (uint32 Tile, uint32 Offset,
			      uint32 StartPixel, uint32 Width,
			      uint32 StartLine, uint32 LineCount, uint8 Z = 0);

void DrawTile16Sub (uint32 Tile, uint32 Offset, uint32 StartLine,
		    uint32 LineCount, uint8 Z = 0);

void DrawClippedTile16Sub (uint32 Tile, uint32 Offset,
			   uint32 StartPixel, uint32 Width,
			   uint32 StartLine, uint32 LineCount, uint8 Z = 0);

void DrawTile16Sub1_2 (uint32 Tile, uint32 Offset, uint32 StartLine,
		       uint32 LineCount, uint8 Z = 0);

void DrawClippedTile16Sub1_2 (uint32 Tile, uint32 Offset,
			      uint32 StartPixel, uint32 Width,
			      uint32 StartLine, uint32 LineCount, uint8 Z = 0);

void DrawLargePixel16Add (uint32 Tile, uint32 Offset,
			  uint32 StartPixel, uint32 Pixels,
			  uint32 StartLine, uint32 LineCount, uint8 Z = 0);

void DrawLargePixel16Add1_2 (uint32 Tile, uint32 Offset,
			     uint32 StartPixel, uint32 Pixels,
			     uint32 StartLine, uint32 LineCount, uint8 Z = 0);

void DrawLargePixel16Sub (uint32 Tile, uint32 Offset,
			  uint32 StartPixel, uint32 Pixels,
			  uint32 StartLine, uint32 LineCount, uint8 Z = 0);

void DrawLargePixel16Sub1_2 (uint32 Tile, uint32 Offset,
			     uint32 StartPixel, uint32 Pixels,
			     uint32 StartLine, uint32 LineCount, uint8 Z = 0);

bool8 S9xGraphicsInit ()
{
    register uint32 PixelOdd = 1;
    register uint32 PixelEven = 2;

#ifdef GFX_MULTI_FORMAT
    if (GFX.BuildPixel == NULL)
	S9xSetRenderPixelFormat (RGB565);
#endif

    for (uint8 bitshift = 0; bitshift < 4; bitshift++)
    {
	for (register char i = 0; i < 16; i++)
	{
	    register uint32 h = 0;
	    register uint32 l = 0;

#if defined(LSB_FIRST)
	    if (i & 8)
		h |= PixelOdd;
	    if (i & 4)
		h |= PixelOdd << 8;
	    if (i & 2)
		h |= PixelOdd << 16;
	    if (i & 1)
		h |= PixelOdd << 24;
	    if (i & 8)
		l |= PixelOdd;
	    if (i & 4)
		l |= PixelOdd << 8;
	    if (i & 2)
		l |= PixelOdd << 16;
	    if (i & 1)
		l |= PixelOdd << 24;
#else
	    if (i & 8)
		h |= (PixelOdd << 24);
	    if (i & 4)
		h |= (PixelOdd << 16);
	    if (i & 2)
		h |= (PixelOdd << 8);
	    if (i & 1)
		h |= PixelOdd;
	    if (i & 8)
		l |= (PixelOdd << 24);
	    if (i & 4)
		l |= (PixelOdd << 16);
	    if (i & 2)
		l |= (PixelOdd << 8);
	    if (i & 1)
		l |= PixelOdd;
#endif

	    odd_high[bitshift][i] = h;
	    odd_low[bitshift][i] = l;
	    h = l = 0;

#if defined(LSB_FIRST)
	    if (i & 8)
		h |= PixelEven;
	    if (i & 4)
		h |= PixelEven << 8;
	    if (i & 2)
		h |= PixelEven << 16;
	    if (i & 1)
		h |= PixelEven << 24;
	    if (i & 8)
		l |= PixelEven;
	    if (i & 4)
		l |= PixelEven << 8;
	    if (i & 2)
		l |= PixelEven << 16;
	    if (i & 1)
		l |= PixelEven << 24;
#else
	    if (i & 8)
		h |= (PixelEven << 24);
	    if (i & 4)
		h |= (PixelEven << 16);
	    if (i & 2)
		h |= (PixelEven << 8);
	    if (i & 1)
		h |= PixelEven;
	    if (i & 8)
		l |= (PixelEven << 24);
	    if (i & 4)
		l |= (PixelEven << 16);
	    if (i & 2)
		l |= (PixelEven << 8);
	    if (i & 1)
		l |= PixelEven;
#endif

	    even_high[bitshift][i] = h;
	    even_low[bitshift][i] = l;
	}
	PixelEven <<= 2;
	PixelOdd <<= 2;
    }

    GFX.RealPitch = GFX.Pitch;
    GFX.Pitchx2 = GFX.Pitch * 2;
    GFX.Delta = (GFX.SubScreen - GFX.Screen) >> 1;
    GFX.InfoStringTimeout = 0;
    GFX.InfoString = NULL;

    PPU.BG_Forced = 0;
    IPPU.OBJChanged = TRUE;
    if (Settings.Transparency)
	Settings.SixteenBit = TRUE;

    PixSize = 1;
    if (Settings.SixteenBit)
    {
	DrawTilePtr = DrawTile16;
	DrawClippedTilePtr = DrawClippedTile16;
	DrawLargePixelPtr = DrawLargePixel16;
	DrawHiResTilePtr= DrawTile16;
	DrawHiResClippedTilePtr = DrawClippedTile16;
	GFX.PPL = GFX.Pitch >> 1;
	GFX.PPLx2 = GFX.Pitch;
    }
    else
    {
	DrawTilePtr = DrawTile;
	DrawClippedTilePtr = DrawClippedTile;
	DrawLargePixelPtr = DrawLargePixel;
	DrawHiResTilePtr = DrawTile;
	DrawHiResClippedTilePtr = DrawClippedTile;
	GFX.PPL = GFX.Pitch;
	GFX.PPLx2 = GFX.Pitch * 2;
    }
    S9xFixColourBrightness ();

    if (Settings.Transparency)
    {
	if (!(GFX.X2 = new uint16 [0x10000]))
	    return (FALSE);

	if (!(GFX.ZERO_OR_X2 = new uint16 [0x10000]) ||
	    !(GFX.ZERO = new uint16 [0x10000]))
	{
	    delete GFX.ZERO_OR_X2;
	    delete GFX.X2;
	    GFX.X2 = NULL;
	    return (FALSE);
	}
	int r, g, b;

	// Build a lookup table that multiplies a packed RGB value by 2 with
	// saturation.
	for (r = 0; r <= MAX_RED; r++)
	{
	    int r2 = r << 1;
	    if (r2 > MAX_RED)
		r2 = MAX_RED;
	    for (g = 0; g <= MAX_GREEN; g++)
	    {
		int g2 = g << 1;
		if (g2 > MAX_GREEN)
		    g2 = MAX_GREEN;
		for (b = 0; b <= MAX_BLUE; b++)
		{
		    int b2 = b << 1;
		    if (b2 > MAX_BLUE)
			b2 = MAX_BLUE;
		    GFX.X2 [BUILD_PIXEL2 (r, g, b)] = BUILD_PIXEL2 (r2, g2, b2);
		}
	    }
	}
	ZeroMemory (GFX.ZERO, 0x10000 * sizeof (uint16));
	ZeroMemory (GFX.ZERO_OR_X2, 0x10000 * sizeof (uint16));
	// Build a lookup table that if the top bit of the color value is zero
	// then the value is zero, otherwise multiply the value by 2. Used by
	// the color subtraction code.

	for (r = 0; r <= MAX_RED; r++)
	{
	    int r2 = r;
	    if ((r2 & 0x10) == 0)
		r2 = 0;
	    else
	    {
		r2 = (r2 << 1) & MAX_RED;
	    }
	    for (g = 0; g <= MAX_GREEN; g++)
	    {
		int g2 = g;
		if ((g2 & GREEN_HI_BIT) == 0)
		    g2 = 0;
		else
		{
		    g2 = (g2 << 1) & MAX_GREEN;
		}
		for (b = 0; b <= MAX_BLUE; b++)
		{
		    int b2 = b;
		    if ((b2 & 0x10) == 0)
			b2 = 0;
		    else
		    {
			b2 = (b2 << 1) & MAX_BLUE;
		    }
		    GFX.ZERO_OR_X2 [BUILD_PIXEL2 (r, g, b)] = BUILD_PIXEL2 (r2, g2, b2);
		}
	    }
	}
	// Build a lookup table that if the top bit of the color value is zero
	// then the value is zero, otherwise its just the value.
	for (r = 0; r <= MAX_RED; r++)
	{
	    int r2 = r;
	    if ((r2 & 0x10) == 0)
		r2 = 0;
	    else
		r2 &= ~0x10;

	    for (g = 0; g <= MAX_GREEN; g++)
	    {
		int g2 = g;
		if ((g2 & GREEN_HI_BIT) == 0)
		    g2 = 0;
		else
		    g2 &= ~GREEN_HI_BIT;
		for (b = 0; b <= MAX_BLUE; b++)
		{
		    int b2 = b;
		    if ((b2 & 0x10) == 0)
			b2 = 0;
		    else
			b2 &= ~0x10;
		    GFX.ZERO [BUILD_PIXEL2 (r, g, b)] = BUILD_PIXEL2 (r2, g2, b2);
		}
	    }
	}
    }
    else
    {
	GFX.X2 = NULL;
	GFX.ZERO_OR_X2 = NULL;
	GFX.ZERO = NULL;
    }

    return (TRUE);
}
/**********************************************************************************************/

void S9xGraphicsDeinit (void)
{
    // Free any memory allocated in S9xGraphicsInit
    delete GFX.X2;
    delete GFX.ZERO_OR_X2;
    delete GFX.ZERO;
    GFX.X2 = NULL;
    GFX.ZERO_OR_X2 = NULL;
    GFX.ZERO = NULL;
}

/**********************************************************************************************/
void S9xStartScreenRefresh ()
{
    if (GFX.InfoStringTimeout > 0 && --GFX.InfoStringTimeout == 0)
	GFX.InfoString = NULL;

    if (IPPU.RenderThisFrame)
    {
	if (!S9xInitUpdate ())
	{
	    IPPU.RenderThisFrame = FALSE;
	    return;
	}
	IPPU.RenderedFramesCount++;
	IPPU.PreviousLine = IPPU.CurrentLine = 0;
	IPPU.MaxBrightness = PPU.Brightness;
	IPPU.LatchedBlanking = PPU.ForcedBlanking;
	if (Settings.SupportHiRes && (PPU.BGMode == 5 || PPU.BGMode == 6))
	{
	    IPPU.RenderedScreenWidth = 512;
	    IPPU.DoubleWidthPixels = TRUE;
	    if ((IPPU.LatchedInterlace = (Memory.FillRAM[0x2133] & 1)))
	    {
		IPPU.RenderedScreenHeight = PPU.ScreenHeight << 1;
		GFX.Pitch = GFX.Pitchx2;
		GFX.PPL = GFX.PPLx2;
	    }
	    else
	    {
		IPPU.RenderedScreenHeight = PPU.ScreenHeight;
		GFX.Pitch = GFX.RealPitch;
		GFX.PPL = GFX.PPLx2 >> 1;
	    }
	}
	else
	{
	    IPPU.RenderedScreenWidth = 256;
	    IPPU.RenderedScreenHeight = PPU.ScreenHeight;
	    IPPU.LatchedInterlace = FALSE;
	    IPPU.DoubleWidthPixels = FALSE;
	    GFX.Pitch = GFX.RealPitch;
	    GFX.PPL = GFX.PPLx2 >> 1;
	}
	PPU.RecomputeClipWindows = TRUE;
	GFX.Delta = (GFX.SubScreen - GFX.Screen) >> 1;
    }
    if (++IPPU.FrameCount % Memory.ROMFramesPerSecond == 0)
    {
	IPPU.DisplayedRenderedFrameCount = IPPU.RenderedFramesCount;
	IPPU.RenderedFramesCount = 0;
	IPPU.FrameCount = 0;
    }
}

void RenderLine (uint8 C)
{
    if (IPPU.RenderThisFrame)
    {
	LineData[C].BG[0].VOffset = PPU.BG[0].VOffset + 1;
	LineData[C].BG[0].HOffset = PPU.BG[0].HOffset;

	if (PPU.BGMode == 7)
	{
	    struct SLineMatrixData *p = &LineMatrixData [C];
	    p->MatrixA = PPU.MatrixA;
	    p->MatrixB = PPU.MatrixB;
	    p->MatrixC = PPU.MatrixC;
	    p->MatrixD = PPU.MatrixD;
	    p->CentreX = PPU.CentreX;
	    p->CentreY = PPU.CentreY;
	}
	else
	{
	    LineData[C].BG[1].VOffset = PPU.BG[1].VOffset + 1;
	    LineData[C].BG[1].HOffset = PPU.BG[1].HOffset;
	    LineData[C].BG[2].VOffset = PPU.BG[2].VOffset + 1;
	    LineData[C].BG[2].HOffset = PPU.BG[2].HOffset;
	    LineData[C].BG[3].VOffset = PPU.BG[3].VOffset + 1;
	    LineData[C].BG[3].HOffset = PPU.BG[3].HOffset;
	}
	IPPU.CurrentLine = C + 1;
    }
}

void S9xEndScreenRefresh ()
{
    IPPU.HDMAStarted = FALSE;
    if (IPPU.RenderThisFrame)
    {
	FLUSH_REDRAW ();
	if (IPPU.ColorsChanged)
	{
	    uint32 saved = PPU.CGDATA[0];
	    if (!Settings.SixteenBit)
	    {
		// Hack for Super Mario World - to get its sky blue
		// (It uses Fixed colour addition on the backdrop colour)
		if (!(Memory.FillRAM [0x2131] & 0x80) &&
		    (Memory.FillRAM[0x2131] & 0x20) &&
		    (PPU.FixedColourRed || PPU.FixedColourGreen ||
		     PPU.FixedColourBlue))
		{
		    PPU.CGDATA[0] = (PPU.FixedColourRed & 0x1f) |
				    ((PPU.FixedColourGreen & 0x1f) << 5) |
				    ((PPU.FixedColourBlue & 0x1f) << 10);
		}
	    }
	    IPPU.ColorsChanged = FALSE;
	    S9xSetPalette ();
	    PPU.CGDATA[0] = saved;
	}
	GFX.Pitch = GFX.RealPitch;
	GFX.PPL = GFX.PPLx2 >> 1;
	if (Settings.DisplayFrameRate)
	    S9xDisplayFrameRate ();
	if (GFX.InfoString)
	    S9xDisplayString (GFX.InfoString);

	S9xDeinitUpdate (IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight,
			 Settings.SixteenBit);
    }
#ifdef DEBUGGER
    if (CPU.Flags & FRAME_ADVANCE_FLAG)
    {
	CPU.Flags &= ~FRAME_ADVANCE_FLAG;
	CPU.Flags |= DEBUG_MODE_FLAG;
    }
#endif
}

void S9xSetInfoString (const char *string)
{
    GFX.InfoString = string;
    GFX.InfoStringTimeout = 80;
}

inline void SelectTileRenderer (bool8 normal)
{
    if (normal)
    {
	DrawTilePtr = DrawTile16;
	DrawClippedTilePtr = DrawClippedTile16;
	DrawLargePixelPtr = DrawLargePixel16;
    }
    else
    {
	if (Memory.FillRAM [0x2131] & 0x80)
	{
	    if (Memory.FillRAM [0x2131] & 0x40)
	    {
		DrawTilePtr = DrawTile16Sub1_2;
		DrawClippedTilePtr = DrawClippedTile16Sub1_2;
		DrawLargePixelPtr = DrawLargePixel16Sub1_2;
	    }
	    else
	    {
		DrawTilePtr = DrawTile16Sub;
		DrawClippedTilePtr = DrawClippedTile16Sub;
		DrawLargePixelPtr = DrawLargePixel16Sub;
	    }
	}
	else
	{
	    if (Memory.FillRAM [0x2131] & 0x40)
	    {
		DrawTilePtr = DrawTile16Add1_2;
		DrawClippedTilePtr = DrawClippedTile16Add1_2;
		DrawLargePixelPtr = DrawLargePixel16Add1_2;
	    }
	    else
	    {
		DrawTilePtr = DrawTile16Add;
		DrawClippedTilePtr = DrawClippedTile16Add;
		DrawLargePixelPtr = DrawLargePixel16Add;
	    }
	}
    }
}

void S9xSetupOBJ ()
{
    int SmallSize;
    int LargeSize;
    switch (PPU.OBJSizeSelect)
    {
    case 0:
	SmallSize = 8;
	LargeSize = 16;
	break;
    case 1:
	SmallSize = 8;
	LargeSize = 32;
	break;
    case 2:
	SmallSize = 8;
	LargeSize = 64;
	break;
    case 3:
	SmallSize = 16;
	LargeSize = 32;
	break;
    case 4:
	SmallSize = 16;
	LargeSize = 64;
	break;
    case 5:
    default:
	SmallSize = 32;
	LargeSize = 64;
	break;
    }
    for (uint8 priority = 0; priority < 4; priority++)
    {
	int C = 0;
	
	int FirstSprite = (PPU.FirstSprite - 1) & 0x7f;
	int S = FirstSprite;
	do
	{
	    if (PPU.OBJ [S].Priority == priority)
	    {
		int Size;
		if (PPU.OBJ [S].Size)
		    Size = LargeSize;
		else
		    Size = SmallSize;
		long VPos = PPU.OBJ [S].VPos;
		if (VPos >= PPU.ScreenHeight)
		    VPos -= 256;
		if (PPU.OBJ [S].HPos < 256 && PPU.OBJ [S].HPos > -Size &&
		    VPos < PPU.ScreenHeight && VPos > -Size)
		{
		    OBJList [priority][C++] = S;
		    Sizes[S] = Size;
		    VPositions[S] = VPos;
		}
	    }
	    S = (S - 1) & 0x7f;
	} while (S != FirstSprite);
	// Terminate the list
	OBJList [priority][C] = -1;
    }
    IPPU.OBJChanged = FALSE;
}

void DrawOBJ (uint8 Priority, bool8 OnMain = FALSE, uint8 Z = 0)
{
    uint32 O;
    uint32 BaseTile, Tile;

    CHECK_SOUND();

    BG.BitShift = 4;
    BG.TileShift = 5;
    BG.TileAddress = PPU.OBJNameBase;
    BG.StartPalette = 128;
    BG.PaletteShift = 4;
    BG.PaletteMask = 7;
    BG.Buffer = IPPU.TileCache [TILE_4BIT];
    BG.Buffered = IPPU.TileCached [TILE_4BIT];
    BG.NameSelect = PPU.OBJNameSelect;
    
    PixSize = 1;

    if (Settings.SupportHiRes)
    {
	if (PPU.BGMode == 5 || PPU.BGMode == 6)
	{
	    PixSize = 2;
	    if (IPPU.LatchedInterlace)
	    {
		if (Settings.SixteenBit)
		{
		    DrawTilePtr = DrawTile16x2x2;
		    DrawClippedTilePtr = DrawClippedTile16x2x2;
		}
		else
		{
		    DrawTilePtr = DrawTilex2x2;
		    DrawClippedTilePtr = DrawClippedTilex2x2;
		}
	    }
	    else
	    {
		if (Settings.SixteenBit)
		{
		    DrawTilePtr = DrawTile16x2;
		    DrawClippedTilePtr = DrawClippedTile16x2;
		}
		else
		{
		    DrawTilePtr = DrawTilex2;
		    DrawClippedTilePtr = DrawClippedTilex2;
		}
	    }
	}
	else
	{
	    if (Settings.SixteenBit)
	    {
		DrawTilePtr = DrawTile16;
		DrawClippedTilePtr = DrawClippedTile16;
	    }
	    else
	    {
		DrawTilePtr = DrawTile;
		DrawClippedTilePtr = DrawClippedTile;
	    }
	}
    }

    int I = 0;
    for (int S = OBJList [Priority][I++]; S >= 0; S = OBJList [Priority][I++])
    {
	int VPos = VPositions [S];
	int Size = Sizes[S];
	int TileInc = 1;
	int Offset;

	if (VPos + Size <= StartY || VPos > EndY)
	    continue;
#if 0
	if (Screen != GFX.SubScreen && ON_SUB(4))
	{
	    if (PPU.OBJ [S].Palette >= 4)
		continue;
	}
	else
	{
	    if (PPU.OBJ [S].Palette >= 4)
		continue;
	}
#endif
	if (OnMain && SUB_OR_ADD(4))
	{
	    SelectTileRenderer (PPU.OBJ [S].Palette < 4);
	}
	BaseTile = PPU.OBJ[S].Name | (PPU.OBJ[S].Palette << 10);

	if (PPU.OBJ[S].HFlip)
	{
	    BaseTile += ((Size >> 3) - 1) | H_FLIP;
	    TileInc = -1;
	}
	if (PPU.OBJ[S].VFlip)
	    BaseTile |= V_FLIP;

	int clipcount = pCurrentClip->Count [4];
	if (!clipcount)
	    clipcount = 1;

	for (int clip = 0; clip < clipcount; clip++)
	{
	    int Left; 
	    int Right;
	    if (!pCurrentClip->Count [4])
	    {
		Left = 0;
		Right = 256;
	    }
	    else
	    {
		Left = pCurrentClip->Left [clip][4];
		Right = pCurrentClip->Right [clip][4];
	    }

	    if (Right <= Left || PPU.OBJ[S].HPos + Size <= Left ||
		PPU.OBJ[S].HPos >= Right)
		continue;

	    for (int Y = 0; Y < Size; Y += 8)
	    {
		if (VPos + Y + 7 >= StartY && VPos + Y <= EndY)
		{
		    int StartLine;
		    int TileLine;
		    int LineCount;
		    int Last;
		    
		    if ((StartLine = VPos + Y) < StartY)
		    {
			StartLine = StartY - StartLine;
			LineCount = 8 - StartLine;
		    }
		    else
		    {
			StartLine = 0;
			LineCount = 8;
		    }
		    if ((Last = VPos + Y + 7 - EndY) > 0)
			if ((LineCount -= Last) <= 0)
			    break;
			
		    TileLine = StartLine << 3;
		    O = (VPos + Y + StartLine) * GFX.PPL;
		    if (!PPU.OBJ[S].VFlip)
			Tile = BaseTile + (Y << 1);
		    else
			Tile = BaseTile + ((Size - Y - 8) << 1);

		    int Middle = Size >> 3;
		    if (PPU.OBJ[S].HPos < Left)
		    {
			Tile += ((Left - PPU.OBJ[S].HPos) >> 3) * TileInc;
			Middle -= (Left - PPU.OBJ[S].HPos) >> 3;
			O += Left * PixSize;
			if (Offset = (Left - PPU.OBJ[S].HPos) & 7)
			{
			    O -= Offset * PixSize;
			    int W = 8 - Offset;
			    int Width = Right - Left;
			    if (W > Width)
				W = Width;
			    (*DrawClippedTilePtr) (Tile, O, Offset, W,
						   TileLine, LineCount, Z);
			    
			    if (W >= Width)
				continue;
			    Tile += TileInc;
			    Middle--;
			    O += 8 * PixSize;
			}
		    }
		    else
			O += PPU.OBJ[S].HPos * PixSize;

		    if (PPU.OBJ[S].HPos + Size >= Right)
		    {
			Middle -= ((PPU.OBJ[S].HPos + Size + 7) -
				   Right) >> 3;
			Offset = (Right - (PPU.OBJ[S].HPos + Size)) & 7;
		    }
		    else
			Offset = 0;

		    for (int X = 0; X < Middle; X++, O += 8 * PixSize,
			 Tile += TileInc)
		    {
			(*DrawTilePtr) (Tile, O, TileLine, LineCount, Z);
		    }
		    if (Offset)
		    {
			(*DrawClippedTilePtr) (Tile, O, 0, Offset,
					       TileLine, LineCount, Z);
		    }
		}
	    }
	}
    }
}

bool8 DrawBackgroundMosaic (uint32 BGMode, uint32 bg, bool8 Priority)
{
    CHECK_SOUND();

    uint32 Tile;
    uint16 *SC0;
    uint16 *SC1;
    uint16 *SC2;
    uint16 *SC3;
    uint32 PriorityBit = Priority ? 0x2000 : 0;
    
    BG.TileSize = BGSizes [PPU.BG[bg].BGSize];
    BG.BitShift = BitShifts[BGMode][bg];
    BG.TileShift = TileShifts[BGMode][bg];
    BG.TileAddress = PPU.BG[bg].NameBase << 1;
    BG.NameSelect = 0;
    BG.Buffer = IPPU.TileCache [Depths [BGMode][bg]];
    BG.Buffered = IPPU.TileCached [Depths [BGMode][bg]];
    BG.PaletteShift = PaletteShifts[BGMode][bg];
    BG.PaletteMask = PaletteMasks[BGMode][bg];

    if (BGMode == 0)
	BG.StartPalette = bg << 5;
    else
	BG.StartPalette = 0;

    int OffsetAlign = BG.TileSize - 1;

    SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1];

    if (PPU.BG[bg].SCSize & 1)
	SC1 = SC0 + 1024;
    else
	SC1 = SC0;

    if (PPU.BG[bg].SCSize & 2)
	SC2 = SC1 + 1024;
    else
	SC2 = SC0;

    if (PPU.BG[bg].SCSize & 1)
	SC3 = SC2 + 1024;
    else
	SC3 = SC2;

    int Lines;
    int OffsetMask;
    int OffsetShift;

    if (BG.TileSize == 16)
    {
	OffsetMask = 0x3ff;
	OffsetShift = 4;
    }
    else
    {
	OffsetMask = 0x1ff;
	OffsetShift = 3;
    }

    for (int Y = StartY; Y <= EndY; Y += Lines)
    {
	uint32 VOffset = LineData [Y].BG[bg].VOffset;
	uint32 HOffset = LineData [Y].BG[bg].HOffset;
	uint32 MosaicOffset = Y % PPU.Mosaic;

	for (Lines = 1; Lines < PPU.Mosaic - MosaicOffset; Lines++)
	    if ((VOffset != LineData [Y + Lines].BG[bg].VOffset) ||
		(HOffset != LineData [Y + Lines].BG[bg].HOffset))
		break;
	
	int MosaicLine = VOffset + Y - MosaicOffset;

	if (Y + Lines > EndY)
	    Lines = EndY + 1 - Y;
	int VirtAlign = (MosaicLine & 7) << 3;
	
	uint16 *b1;
	uint16 *b2;

	int ScreenLine = MosaicLine >> OffsetShift;
	int Rem16 = MosaicLine & 15;

	if ((ScreenLine & 0x3f) >= 32)
	    b1 = SC2, b2 = SC3;
	else
	    b1 = SC0, b2 = SC1;

	b1 += (ScreenLine & 0x1f) << 5;
	b2 += (ScreenLine & 0x1f) << 5;
	uint16 *t;
	int Left;
	int Right;

	int ClipCount = pCurrentClip->Count [bg];
	uint32 HPos;
	int PixWidth;

	if (!ClipCount)
	{
	    ClipCount = 1;
	    Left = 0;
	    Right = 256;
	    HPos = HOffset;
	    PixWidth = PPU.Mosaic;
	}

	for (int clip = 0; clip < ClipCount; clip++)
	{
	    if (pCurrentClip->Count [bg])
	    {
		Left = pCurrentClip->Left [clip][bg];
		Right = pCurrentClip->Right [clip][bg];
		uint32 r = Left % PPU.Mosaic;
		HPos = HOffset + Left;
		PixWidth = PPU.Mosaic - r;
	    }
	    uint32 s = Y * GFX.PPL + Left * PixSize;
	    for (int x = Left; x < Right; x += PixWidth, 
		 s += PixWidth * PixSize,
		 HPos += PixWidth, PixWidth = PPU.Mosaic)
	    {
		uint32 Quot = (HPos & OffsetMask) >> 3;

		if (x + PixWidth >= Right)
		    PixWidth = Right - x;

		if (BG.TileSize == 8)
		{
		    if (Quot > 31)
			t = b2 + (Quot & 0x1f);
		    else
			t = b1 + Quot;
		}
		else
		{
		    if (Quot > 63)
			t = b2 + ((Quot >> 1) & 0x1f);
		    else
			t = b1 + (Quot >> 1);
		}

		Tile = READ_2BYTES (t);
		if ((Tile & 0x2000) == PriorityBit)
		{
		    // Draw tile...
		    if (BG.TileSize != 8)
		    {
			if (Tile & H_FLIP)
			{
			    // Horizontal flip, but what about vertical flip ?
			    if (Tile & V_FLIP)
			    {
				// Both horzontal & vertical flip
				if (Rem16 < 8)
				{
				    (*DrawLargePixelPtr) (Tile + 17 - (Quot & 1), s,
							  HPos & 7, PixWidth,
							  VirtAlign, Lines);
				}
				else
				{
				    (*DrawLargePixelPtr) (Tile + 1 - (Quot & 1), s,
							  HPos & 7, PixWidth,
							  VirtAlign, Lines);
				}
			    }
			    else
			    {
				// Horizontal flip only
				if (Rem16 > 7)
				{
				    (*DrawLargePixelPtr) (Tile + 17 - (Quot & 1), s,
							  HPos & 7, PixWidth,
							  VirtAlign, Lines);
				}
				else
				{
				    (*DrawLargePixelPtr) (Tile + 1 - (Quot & 1), s,
							  HPos & 7, PixWidth,
							  VirtAlign, Lines);
				}
			    }
			}
			else
			{
			    // No horizontal flip, but is there a vertical flip ?
			    if (Tile & V_FLIP)
			    {
				// Vertical flip only
				if (Rem16 < 8)
				{
				    (*DrawLargePixelPtr) (Tile + 16 + (Quot & 1), s,
							  HPos & 7, PixWidth,
							  VirtAlign, Lines);
				}
				else
				{
				    (*DrawLargePixelPtr) (Tile + (Quot & 1), s,
							  HPos & 7, PixWidth,
							  VirtAlign, Lines);
				}
			    }
			    else
			    {
				// Normal unflipped
				if (Rem16 > 7)
				{
				    (*DrawLargePixelPtr) (Tile + 16 + (Quot & 1), s,
							  HPos & 7, PixWidth,
							  VirtAlign, Lines);
				}
				else
				{
				    (*DrawLargePixelPtr) (Tile + (Quot & 1), s,
							  HPos & 7, PixWidth,
							  VirtAlign, Lines);
				}
			    }
			}
		    }
		    else
			(*DrawLargePixelPtr) (Tile, s, HPos & 7, PixWidth,
					      VirtAlign, Lines);
		}
	    }
	}
    }
    return (TRUE);
}

bool8 DrawBackgroundOffset (uint32 BGMode, uint32 bg, bool8 Priority)
{
    CHECK_SOUND();

    BG.TileSize = BGSizes [PPU.BG[bg].BGSize];
    BG.BitShift = BitShifts[BGMode][bg];
    BG.TileShift = TileShifts[BGMode][bg];
    BG.TileAddress = PPU.BG[bg].NameBase << 1;
    BG.NameSelect = 0;
    BG.Buffer = IPPU.TileCache [Depths [BGMode][bg]];
    BG.Buffered = IPPU.TileCached [Depths [BGMode][bg]];
    BG.PaletteShift = PaletteShifts[BGMode][bg];
    BG.PaletteMask = PaletteMasks[BGMode][bg];

    uint32 Tile;
    uint16 *SC0;
    uint16 *SC1;
    uint16 *SC2;
    uint16 *SC3;
    uint16 *BPS0;
    uint16 *BPS1;
    uint16 *BPS2;
    uint16 *BPS3;
    uint32 PriorityBit = Priority ? 0x2000 : 0;
    bool8 P = FALSE;
    uint32 Width;
    int OffsetOffset = BGMode == 4 ? -1 : 31;
    
    if (BGMode == 0)
	BG.StartPalette = bg << 5;
    else
	BG.StartPalette = 0;

    int OffsetAlign = BG.TileSize - 1;

#define NO_SCREEN_SIZE_ON_OFFSET_MODE

    BPS0 = (uint16 *) &Memory.VRAM[PPU.BG[2].SCBase << 1];

    if (PPU.BG[2].SCSize & 1)
	BPS1 = BPS0 + 1024;
    else
	BPS1 = BPS0;
    if (PPU.BG[2].SCSize & 2)
	BPS2 = BPS1 + 1024;
    else
	BPS2 = BPS0;
    if (PPU.BG[2].SCSize & 1)
	BPS3 = BPS2 + 1024;
    else
	BPS3 = BPS2;
    
    SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1];

    if (PPU.BG[bg].SCSize & 1)
	SC1 = SC0 + 1024;
    else
	SC1 = SC0;
#if 1
    if (PPU.BG[bg].SCSize & 2)
	SC2 = SC1 + 1024;
    else
#endif
	SC2 = SC0;
    if (PPU.BG[bg].SCSize & 1)
	SC3 = SC2 + 1024;
    else
	SC3 = SC2;

    int Lines = 1;
    int OffsetMask;
    int OffsetShift;
    int OffsetEnableMask = 1 << (bg + 13);

    if (BG.TileSize == 16)
    {
	OffsetMask = 0x3ff;
	OffsetShift = 4;
    }
    else
    {
	OffsetMask = 0x1ff;
	OffsetShift = 3;
    }

    for (int Y = StartY; Y <= EndY; Y++)
    {
	uint32 VOff = LineData [Y].BG[2].VOffset;
	uint32 HOff = LineData [Y].BG[2].HOffset;
	int VirtAlign;
	int ScreenLine = VOff >> 3;
	int t1;
	int t2;
	uint16 *s0;
	uint16 *s1;
	uint16 *s2;

	if ((ScreenLine & 0x3f) >= 32)
	    s1 = BPS2, s2 = BPS3;
	else
	    s1 = BPS0, s2 = BPS1;

	s1 += (ScreenLine & 0x1f) << 5;
	s2 += (ScreenLine & 0x1f) << 5;

	int clipcount = pCurrentClip->Count [bg];
	if (!clipcount)
	    clipcount = 1;
	for (int clip = 0; clip < clipcount; clip++)
	{
	    int Left;
	    int Right;

	    if (!pCurrentClip->Count [bg])
	    {
		Left = 0;
		Right = 256;
	    }
	    else
	    {
		Left = pCurrentClip->Left [clip][bg];
		Right = pCurrentClip->Right [clip][bg];

		if (Right <= Left)
		    continue;
	    }

	    uint32 VOffset;
	    uint32 HOffset = LineData [Y].BG[bg].HOffset;

	    uint32 s = Left * PixSize + Y * GFX.PPL;

	    int HPos = (HOff + Left) & 0x1ff;
	    uint32 Quot2 = HPos >> 3;
	    s0 = s1 + OffsetOffset + (Quot2 & 0x1f);
	    uint32 CellOffset = READ_2BYTES (s0);
	    s0++;

	    if (Left >= 8 && (CellOffset & OffsetEnableMask))
		VOffset = (CellOffset + 1) & OffsetMask;
	    else
		VOffset = LineData [Y].BG[bg].VOffset;

	    VirtAlign = ((Y + VOffset) & 7) << 3;
	    ScreenLine = (VOffset + Y) >> OffsetShift;

	    if (((VOffset + Y) & 15) > 7)
	    {
		t1 = 16;
		t2 = 0;
	    }
	    else
	    {
	        t1 = 0;
	        t2 = 16;
	    }
	    uint16 *b1;
	    uint16 *b2;

	    if ((ScreenLine & 0x3f) >= 32)
		b1 = SC2, b2 = SC3;
	    else
		b1 = SC0, b2 = SC1;

	    b1 += (ScreenLine & 0x1f) << 5;
	    b2 += (ScreenLine & 0x1f) << 5;

	    HPos = (HOffset + Left) & OffsetMask;

	    uint32 Quot = HPos >> 3;
	    int Count = 0;
	    
	    uint16 *t;
	    if (BG.TileSize == 8)
	    {
		if (Quot > 31)
		    t = b2 + (Quot & 0x1f);
		else
		    t = b1 + Quot;
	    }
	    else
	    {
		if (Quot > 63)
		    t = b2 + ((Quot >> 1) & 0x1f);
		else
		    t = b1 + (Quot >> 1);
	    }

	    Width = Right - Left;
	    // Left hand edge clipped tile
	    if (HPos & 7)
	    {
		int Offset = (HPos & 7);
		Count = 8 - Offset;
		if (Count > Width)
		    Count = Width;
		s -= Offset * PixSize;
		Tile = READ_2BYTES(t);
		if (BG.TileSize == 8)
		{
		    if ((Tile & 0x2000) == PriorityBit)
			(*DrawClippedTilePtr) (Tile, s, Offset, Count, VirtAlign, Lines);
		    else
			P = TRUE;
		}
		else
		{
		    if ((Tile & 0x2000) == PriorityBit)
		    {
			if (!(Tile & (V_FLIP | H_FLIP)))
			{
			    // Normal, unflipped
			    (*DrawClippedTilePtr) (Tile + t1 + (Quot & 1),
					     s, Offset, Count, VirtAlign, Lines);
			}
			else
			if (Tile & H_FLIP)
			{
			    if (Tile & V_FLIP)
			    {
				// H & V flip
				(*DrawClippedTilePtr) (Tile + t2 + 1 - (Quot & 1),
						 s, Offset, Count, VirtAlign, Lines);
			    }
			    else
			    {
				// H flip only
				(*DrawClippedTilePtr) (Tile + t1 + 1 - (Quot & 1),
						 s, Offset, Count, VirtAlign, Lines);
			    }
			}
			else
			{
			    // V flip only
			    (*DrawClippedTilePtr) (Tile + t2 + (Quot & 1),
					     s, Offset, Count, VirtAlign, Lines);
			}
		    }
		    else
			P = TRUE;
		}

		CellOffset = READ_2BYTES (s0);
		s0++;
		if (CellOffset & OffsetEnableMask)
		    VOffset = (CellOffset + 1) & OffsetMask;
		else
		    VOffset = LineData [Y].BG[bg].VOffset;

		VirtAlign = ((Y + VOffset) & 7) << 3;
		ScreenLine = (VOffset + Y) >> OffsetShift;

		if (((VOffset + Y) & 15) > 7)
	        {
		    t1 = 16;
		    t2 = 0;
		}
		else
		{
		    t1 = 0;
		    t2 = 16;
	        }

		if ((ScreenLine & 0x3f) >= 32)
		    b1 = SC2, b2 = SC3;
		else
		    b1 = SC0, b2 = SC1;

		b1 += (ScreenLine & 0x1f) << 5;
		b2 += (ScreenLine & 0x1f) << 5;

		Quot++;
		if (BG.TileSize == 8)
		{
		    Quot &= 63;
		    if (Quot > 31)
			t = b2 + (Quot & 0x1f);
		    else
			t = b1 + Quot;
		}
		else
		{
		    Quot &= 127;
		    if (Quot > 63)
			t = b2 + ((Quot >> 1) & 0x1f);
		    else
			t = b1 + (Quot >> 1);
		}
		s += 8 * PixSize;
	    }

	    // Middle, unclipped tiles
	    Count = Width - Count;
	    int Middle = Count >> 3;
	    Count &= 7;
	    for (int C = Middle; C > 0; s += 8 * PixSize, C--)
	    {
		Tile = READ_2BYTES (t);
		if ((Tile & 0x2000) == PriorityBit)
		{
		    // Draw tile...
		    if (BG.TileSize != 8)
		    {
			if (Tile & H_FLIP)
			{
			    // Horizontal flip, but what about vertical flip ?
			    if (Tile & V_FLIP)
			    {
				// Both horzontal & vertical flip
				(*DrawTilePtr) (Tile + t2 + 1 - (Quot & 1), s, VirtAlign, Lines);
			    }
			    else
			    {
				// Horizontal flip only
				(*DrawTilePtr) (Tile + t1 + 1 - (Quot & 1), s, VirtAlign, Lines);
			    }
			}
			else
			{
			    // No horizontal flip, but is there a vertical flip ?
			    if (Tile & V_FLIP)
			    {
				// Vertical flip only
				(*DrawTilePtr) (Tile + t2 + (Quot & 1), s, VirtAlign, Lines);
			    }
			    else
			    {
				// Normal unflipped
				(*DrawTilePtr) (Tile + t1 + (Quot & 1), s, VirtAlign, Lines);
			    }
			}
		    }
		    else
			(*DrawTilePtr) (Tile, s, VirtAlign, Lines);
		}
		else
		    P = TRUE;

		CellOffset = READ_2BYTES (s0);
		s0++;
		if (CellOffset & OffsetEnableMask)
		    VOffset = (CellOffset + 1) & OffsetMask;
		else
		    VOffset = LineData [Y].BG[bg].VOffset;
		VirtAlign = ((Y + VOffset) & 7) << 3;
		ScreenLine = (VOffset + Y) >> OffsetShift;

		if (((VOffset + Y) & 15) > 7)
	        {
		    t1 = 16;
		    t2 = 0;
		}
		else
		{
		    t1 = 0;
		    t2 = 16;
	        }

		if ((ScreenLine & 0x3f) >= 32)
		    b1 = SC2, b2 = SC3;
		else
		    b1 = SC0, b2 = SC1;

		b1 += (ScreenLine & 0x1f) << 5;
		b2 += (ScreenLine & 0x1f) << 5;

		Quot++;
		if (BG.TileSize == 8)
		{
		    Quot &= 63;
		    if (Quot > 31)
			t = b2 + (Quot & 0x1f);
		    else
			t = b1 + Quot;
		}
		else
		{
		    Quot &= 127;
		    if (Quot > 63)
			t = b2 + ((Quot >> 1) & 0x1f);
		    else
			t = b1 + (Quot >> 1);
		}
	    }

	    // Right-hand edge clipped tiles
	    if (Count)
	    {
		Tile = READ_2BYTES (t);
		if (BG.TileSize == 8)
		{
		    if ((Tile & 0x2000) == PriorityBit)
			(*DrawClippedTilePtr) (Tile, s, 0, Count, VirtAlign, Lines);
		    else
			P = TRUE;
		}
		else
		{
		    if ((Tile & 0x2000) == PriorityBit)
		    {
			if (!(Tile & (V_FLIP | H_FLIP)))
			{
			    // Normal, unflipped
			    (*DrawClippedTilePtr) (Tile + t1 + (Quot & 1),
						   s, 0, Count, VirtAlign, Lines);
			}
			else
			if (Tile & H_FLIP)
			{
			    if (Tile & V_FLIP)
			    {
				// H & V flip
				(*DrawClippedTilePtr) (Tile + t2 + 1 - (Quot & 1),
						       s, 0, Count, VirtAlign, Lines);
			    }
			    else
			    {
				// H flip only
				(*DrawClippedTilePtr) (Tile + t1 + 1 - (Quot & 1),
						       s, 0, Count, VirtAlign, Lines);
			    }
			}
			else
			{
			    // V flip only
			    (*DrawClippedTilePtr) (Tile + t2 + (Quot & 1),
						   s, 0, Count, VirtAlign, Lines);
			}
		    }
		    else
			P = TRUE;
		}
	    }
	}
    }
    return (P);
}

bool8 DrawBackgroundMode5 (uint32 BGMode, uint32 bg, bool8 Priority)
{
    CHECK_SOUND();

    BG.TileSize = BGSizes [PPU.BG[bg].BGSize];
    BG.BitShift = BitShifts[BGMode][bg];
    BG.TileShift = TileShifts[BGMode][bg];
    BG.TileAddress = PPU.BG[bg].NameBase << 1;
    BG.NameSelect = 0;
    BG.Buffer = IPPU.TileCache [Depths [BGMode][bg]];
    BG.Buffered = IPPU.TileCached [Depths [BGMode][bg]];
    BG.PaletteShift = PaletteShifts[BGMode][bg];
    BG.PaletteMask = PaletteMasks[BGMode][bg];

    GFX.Pitch = GFX.RealPitch;
    GFX.PPL = GFX.PPLx2 >> 1;
    PixSize = 1;

    uint32 Tile;
    uint16 *SC0;
    uint16 *SC1;
    uint16 *SC2;
    uint16 *SC3;
    uint32 PriorityBit = Priority ? 0x2000 : 0;
    bool8 P = FALSE;
    uint32 Width;
    
    if (BGMode == 0)
	BG.StartPalette = bg << 5;
    else
	BG.StartPalette = 0;

    int VOffsetAlign = BG.TileSize - 1;
    int HOffsetAlign = 15;

    SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1];

    if ((PPU.BG[bg].SCSize & 1))
	SC1 = SC0 + 1024;
    else
	SC1 = SC0;

    if ((PPU.BG[bg].SCSize & 2))
	SC2 = SC1 + 1024;
    else
	SC2 = SC0;

    if ((PPU.BG[bg].SCSize & 1))
	SC3 = SC2 + 1024;
    else
	SC3 = SC2;
    
    int Lines;
    int VOffsetMask;
    int VOffsetShift;

    if (BG.TileSize == 16)
    {
	VOffsetMask = 0x3ff;
	VOffsetShift = 4;
    }
    else
    {
	VOffsetMask = 0x1ff;
	VOffsetShift = 3;
    }
    int endy = IPPU.LatchedInterlace ? EndY << 1 : EndY;

    for (int Y = IPPU.LatchedInterlace ? StartY << 1 : StartY; Y <= endy; Y += Lines)
    {
	int y = IPPU.LatchedInterlace ? (Y >> 1) : Y;
	uint32 VOffset = LineData [y].BG[bg].VOffset;
	uint32 HOffset = LineData [y].BG[bg].HOffset;
	int VirtAlign = (Y + VOffset) & 7;
	
	for (Lines = 1; Lines < 8 - VirtAlign; Lines++)
	    if ((VOffset != LineData [y + Lines].BG[bg].VOffset) ||
		(HOffset != LineData [y + Lines].BG[bg].HOffset))
		break;

	HOffset <<= 1;
	if (Y + Lines > endy)
	    Lines = endy + 1 - Y;
	VirtAlign <<= 3;
	
	int ScreenLine = (VOffset + Y) >> VOffsetShift;
	int t1;
	int t2;
	if (((VOffset + Y) & 15) > 7)
	{
	    t1 = 16;
	    t2 = 0;
	}
	else
	{
	    t1 = 0;
	    t2 = 16;
	}
	uint16 *b1;
	uint16 *b2;

	if ((ScreenLine & 0x3f) >= 32)
	    b1 = SC2, b2 = SC3;
	else
	    b1 = SC0, b2 = SC1;

	b1 += (ScreenLine & 0x1f) << 5;
	b2 += (ScreenLine & 0x1f) << 5;

	int clipcount = pCurrentClip->Count [bg];
	if (!clipcount)
	    clipcount = 1;
	for (int clip = 0; clip < clipcount; clip++)
	{
	    int Left;
	    int Right;

	    if (!pCurrentClip->Count [bg])
	    {
		Left = 0;
		Right = 512;
	    }
	    else
	    {
		Left = pCurrentClip->Left [clip][bg] * 2;
		Right = pCurrentClip->Right [clip][bg] * 2;

		if (Right <= Left)
		    continue;
	    }

	    uint32 s = Left * PixSize + Y * GFX.PPL;
	    int HPos = (HOffset + Left * PixSize) & 0x3ff;

	    uint32 Quot = HPos >> 3;
	    int Count = 0;
	    
	    uint16 *t;
	    if (Quot > 63)
		t = b2 + ((Quot >> 1) & 0x1f);
	    else
		t = b1 + (Quot >> 1);

	    Width = Right - Left;
	    // Left hand edge clipped tile
	    if (HPos & 7)
	    {
		int Offset = (HPos & 7);
		Count = 8 - Offset;
		if (Count > Width)
		    Count = Width;
		s -= Offset;
		Tile = READ_2BYTES (t);
		if ((Tile & 0x2000) == PriorityBit)
		{
		    if (BG.TileSize == 8)
		    {
			if (!(Tile & H_FLIP))
			{
			    // Normal, unflipped
			    (*DrawHiResClippedTilePtr) (Tile + (Quot & 1),
							s, Offset, Count, VirtAlign, Lines);
			}
			else
			{
			    // H flip
			    (*DrawHiResClippedTilePtr) (Tile + 1 - (Quot & 1),
							s, Offset, Count, VirtAlign, Lines);
			}
		    }
		    else
		    {
			if (!(Tile & (V_FLIP | H_FLIP)))
			{
			    // Normal, unflipped
			    (*DrawHiResClippedTilePtr) (Tile + t1 + (Quot & 1),
							s, Offset, Count, VirtAlign, Lines);
			}
			else
			if (Tile & H_FLIP)
			{
			    if (Tile & V_FLIP)
			    {
				// H & V flip
				(*DrawHiResClippedTilePtr) (Tile + t2 + 1 - (Quot & 1),
							    s, Offset, Count, VirtAlign, Lines);
			    }
			    else
			    {
				// H flip only
				(*DrawHiResClippedTilePtr) (Tile + t1 + 1 - (Quot & 1),
							    s, Offset, Count, VirtAlign, Lines);
			    }
			}
			else
			{
			    // V flip only
			    (*DrawHiResClippedTilePtr) (Tile + t2 + (Quot & 1),
							s, Offset, Count, VirtAlign, Lines);
			}
		    }
		}
		else
		    P = TRUE;

		t += Quot & 1;
		if (Quot == 63)
		    t = b2;
		else if (Quot == 127)
		    t = b1;
		Quot++;
		s += 8;
	    }

	    // Middle, unclipped tiles
	    Count = Width - Count;
	    int Middle = Count >> 3;
	    Count &= 7;
	    for (int C = Middle; C > 0; s += 8, Quot++, C--)
	    {
		Tile = READ_2BYTES(t);
		if ((Tile & 0x2000) == PriorityBit)
		{
		    if (BG.TileSize == 8)
		    {
			if (!(Tile & H_FLIP))
			{
			    // Normal, unflipped
			    (*DrawHiResTilePtr) (Tile + (Quot & 1),
						 s, VirtAlign, Lines);
			}
			else
			{
			    // H flip
			    (*DrawHiResTilePtr) (Tile + 1 - (Quot & 1),
						s, VirtAlign, Lines);
			}
		    }
		    else
		    {
			if (!(Tile & (V_FLIP | H_FLIP)))
			{
			    // Normal, unflipped
			    (*DrawHiResTilePtr) (Tile + t1 + (Quot & 1),
						 s, VirtAlign, Lines);
			}
			else
			if (Tile & H_FLIP)
			{
			    if (Tile & V_FLIP)
			    {
				// H & V flip
				(*DrawHiResTilePtr) (Tile + t2 + 1 - (Quot & 1),
						     s, VirtAlign, Lines);
			    }
			    else
			    {
				// H flip only
				(*DrawHiResTilePtr) (Tile + t1 + 1 - (Quot & 1),
						     s, VirtAlign, Lines);
			    }
			}
			else
			{
			    // V flip only
			    (*DrawHiResTilePtr) (Tile + t2 + (Quot & 1),
						 s, VirtAlign, Lines);
			}
		    }
		}
		else
		    P = TRUE;
		t += Quot & 1;
		if (Quot == 63)
		    t = b2;
		else
		if (Quot == 127)
		    t = b1;
	    }

	    // Right-hand edge clipped tiles
	    if (Count)
	    {
		Tile = READ_2BYTES(t);
		if ((Tile & 0x2000) == PriorityBit)
		{
		    if (BG.TileSize == 8)
		    {
			if (!(Tile & H_FLIP))
			{
			    // Normal, unflipped
			    (*DrawHiResClippedTilePtr) (Tile + (Quot & 1),
							s, 0, Count, VirtAlign, Lines);
			}
			else
			{
			    // H flip
			    (*DrawHiResClippedTilePtr) (Tile + 1 - (Quot & 1),
							s, 0, Count, VirtAlign, Lines);
			}
		    }
		    else
		    {
			if (!(Tile & (V_FLIP | H_FLIP)))
			{
			    // Normal, unflipped
			    (*DrawHiResClippedTilePtr) (Tile + t1 + (Quot & 1),
							s, 0, Count, VirtAlign, Lines);
			}
			else
			if (Tile & H_FLIP)
			{
			    if (Tile & V_FLIP)
			    {
				// H & V flip
				(*DrawHiResClippedTilePtr) (Tile + t2 + 1 - (Quot & 1),
							    s, 0, Count, VirtAlign, Lines);
			    }
			    else
			    {
				// H flip only
				(*DrawHiResClippedTilePtr) (Tile + t1 + 1 - (Quot & 1),
							    s, 0, Count, VirtAlign, Lines);
			    }
			}
			else
			{
			    // V flip only
			    (*DrawHiResClippedTilePtr) (Tile + t2 + (Quot & 1),
							s, 0, Count, VirtAlign, Lines);
			}
		    }
		}
		else
		    P = TRUE;
	    }
	}
    }
    GFX.Pitch = IPPU.LatchedInterlace ? GFX.Pitchx2 : GFX.RealPitch;
    GFX.PPL = IPPU.LatchedInterlace ? GFX.PPLx2 : (GFX.PPLx2 >> 1);

    return (P);
}

bool8 DrawBackground (uint32 BGMode, uint32 bg, bool8 Priority)
{
    PixSize = 1;

    if (PPU.BGMosaic [bg] && PPU.Mosaic > 1)
	return (DrawBackgroundMosaic (BGMode, bg, Priority));

    switch (BGMode)
    {
    case 2:
    case 4: // Used by Puzzle Bobble
	return (DrawBackgroundOffset (BGMode, bg, Priority));

    case 5:
    case 6: // XXX: is also offset per tile.
	if (Settings.SupportHiRes)
	    return (DrawBackgroundMode5 (BGMode, bg, Priority));
	break;
    }
    CHECK_SOUND();

    BG.TileSize = BGSizes [PPU.BG[bg].BGSize];
    BG.BitShift = BitShifts[BGMode][bg];
    BG.TileShift = TileShifts[BGMode][bg];
    BG.TileAddress = PPU.BG[bg].NameBase << 1;
    BG.NameSelect = 0;
    BG.Buffer = IPPU.TileCache [Depths [BGMode][bg]];
    BG.Buffered = IPPU.TileCached [Depths [BGMode][bg]];
    BG.PaletteShift = PaletteShifts[BGMode][bg];
    BG.PaletteMask = PaletteMasks[BGMode][bg];

    uint32 Tile;
    uint16 *SC0;
    uint16 *SC1;
    uint16 *SC2;
    uint16 *SC3;
    uint32 PriorityBit = Priority ? 0x2000 : 0;
    bool8 P = FALSE;
    uint32 Width;
    
    if (BGMode == 0)
	BG.StartPalette = bg << 5;
    else
	BG.StartPalette = 0;

    int OffsetAlign = BG.TileSize - 1;

    SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1];

    if (PPU.BG[bg].SCSize & 1)
	SC1 = SC0 + 1024;
    else
	SC1 = SC0;

    if (PPU.BG[bg].SCSize & 2)
	SC2 = SC1 + 1024;
    else
	SC2 = SC0;

    if (PPU.BG[bg].SCSize & 1)
	SC3 = SC2 + 1024;
    else
	SC3 = SC2;
    
    int Lines;
    int OffsetMask;
    int OffsetShift;

    if (BG.TileSize == 16)
    {
	OffsetMask = 0x3ff;
	OffsetShift = 4;
    }
    else
    {
	OffsetMask = 0x1ff;
	OffsetShift = 3;
    }

    for (int Y = StartY; Y <= EndY; Y += Lines)
    {
	uint32 VOffset = LineData [Y].BG[bg].VOffset;
	uint32 HOffset = LineData [Y].BG[bg].HOffset;
	int VirtAlign = (Y + VOffset) & 7;
	
	for (Lines = 1; Lines < 8 - VirtAlign; Lines++)
	    if ((VOffset != LineData [Y + Lines].BG[bg].VOffset) ||
		(HOffset != LineData [Y + Lines].BG[bg].HOffset))
		break;

	if (Y + Lines > EndY)
	    Lines = EndY + 1 - Y;
	VirtAlign <<= 3;
	
	int ScreenLine = (VOffset + Y) >> OffsetShift;
	int t1;
	int t2;
	if (((VOffset + Y) & 15) > 7)
	{
	    t1 = 16;
	    t2 = 0;
	}
	else
	{
	    t1 = 0;
	    t2 = 16;
	}
	uint16 *b1;
	uint16 *b2;

	if ((ScreenLine & 0x3f) >= 32)
	    b1 = SC2, b2 = SC3;
	else
	    b1 = SC0, b2 = SC1;

	b1 += (ScreenLine & 0x1f) << 5;
	b2 += (ScreenLine & 0x1f) << 5;

	int clipcount = pCurrentClip->Count [bg];
	if (!clipcount)
	    clipcount = 1;
	for (int clip = 0; clip < clipcount; clip++)
	{
	    int Left;
	    int Right;

	    if (!pCurrentClip->Count [bg])
	    {
		Left = 0;
		Right = 256;
	    }
	    else
	    {
		Left = pCurrentClip->Left [clip][bg];
		Right = pCurrentClip->Right [clip][bg];

		if (Right <= Left)
		    continue;
	    }

	    uint32 s = Left * PixSize + Y * GFX.PPL;
	    int HPos = (HOffset + Left) & OffsetMask;

	    uint32 Quot = HPos >> 3;
	    int Count = 0;
	    
	    uint16 *t;
	    if (BG.TileSize == 8)
	    {
		if (Quot > 31)
		    t = b2 + (Quot & 0x1f);
		else
		    t = b1 + Quot;
	    }
	    else
	    {
		if (Quot > 63)
		    t = b2 + ((Quot >> 1) & 0x1f);
		else
		    t = b1 + (Quot >> 1);
	    }

	    Width = Right - Left;
	    // Left hand edge clipped tile
	    if (HPos & 7)
	    {
		int Offset = (HPos & 7);
		Count = 8 - Offset;
		if (Count > Width)
		    Count = Width;
		s -= Offset * PixSize;
		Tile = READ_2BYTES(t);

		if ((Tile & 0x2000) == PriorityBit)
		{
		    if (BG.TileSize == 8)
		    {
			(*DrawClippedTilePtr) (Tile, s, Offset, Count, VirtAlign, Lines);
		    }
		    else
		    {
			if (!(Tile & (V_FLIP | H_FLIP)))
			{
			    // Normal, unflipped
			    (*DrawClippedTilePtr) (Tile + t1 + (Quot & 1),
						   s, Offset, Count, VirtAlign, Lines);
			}
			else
			if (Tile & H_FLIP)
			{
			    if (Tile & V_FLIP)
			    {
				// H & V flip
				(*DrawClippedTilePtr) (Tile + t2 + 1 - (Quot & 1),
						       s, Offset, Count, VirtAlign, Lines);
			    }
			    else
			    {
				// H flip only
				(*DrawClippedTilePtr) (Tile + t1 + 1 - (Quot & 1),
						       s, Offset, Count, VirtAlign, Lines);
			    }
			}
			else
			{
			    // V flip only
			    (*DrawClippedTilePtr) (Tile + t2 + (Quot & 1),
						   s, Offset, Count, VirtAlign, Lines);
			}
		    }
		}
		else
		    P = TRUE;

		if (BG.TileSize == 8)
		{
		    t++;
		    if (Quot == 31)
			t = b2;
		    else if (Quot == 63)
			t = b1;
		}
		else
		{
		    t += Quot & 1;
		    if (Quot == 63)
			t = b2;
		    else if (Quot == 127)
			t = b1;
		}
		Quot++;
		s += 8 * PixSize;
	    }

	    // Middle, unclipped tiles
	    Count = Width - Count;
	    int Middle = Count >> 3;
	    Count &= 7;
	    for (int C = Middle; C > 0; s += 8 * PixSize, Quot++, C--)
	    {
		Tile = READ_2BYTES(t);

		if ((Tile & 0x2000) == PriorityBit)
		{
		    // Draw tile...
		    if (BG.TileSize != 8)
		    {
			if (Tile & H_FLIP)
			{
			    // Horizontal flip, but what about vertical flip ?
			    if (Tile & V_FLIP)
			    {
				// Both horzontal & vertical flip
				(*DrawTilePtr) (Tile + t2 + 1 - (Quot & 1), s, VirtAlign, Lines);
			    }
			    else
			    {
				// Horizontal flip only
				(*DrawTilePtr) (Tile + t1 + 1 - (Quot & 1), s, VirtAlign, Lines);
			    }
			}
			else
			{
			    // No horizontal flip, but is there a vertical flip ?
			    if (Tile & V_FLIP)
			    {
				// Vertical flip only
				(*DrawTilePtr) (Tile + t2 + (Quot & 1), s, VirtAlign, Lines);
			    }
			    else
			    {
				// Normal unflipped
				(*DrawTilePtr) (Tile + t1 + (Quot & 1), s, VirtAlign, Lines);
			    }
			}
		    }
		    else
		    {
			(*DrawTilePtr) (Tile, s, VirtAlign, Lines);
		    }
		}
		else
		    P = TRUE;

		if (BG.TileSize == 8)
		{
		    t++;
		    if (Quot == 31)
			t = b2;
		    else
		    if (Quot == 63)
			t = b1;
		}
		else
		{
		    t += Quot & 1;
		    if (Quot == 63)
			t = b2;
		    else
		    if (Quot == 127)
			t = b1;
		}
	    }
	    // Right-hand edge clipped tiles
	    if (Count)
	    {
		Tile = READ_2BYTES(t);
		if ((Tile & 0x2000) == PriorityBit)
		{
		    if (BG.TileSize == 8)
			(*DrawClippedTilePtr) (Tile, s, 0, Count, VirtAlign, Lines);
		    else
		    {
			if (!(Tile & (V_FLIP | H_FLIP)))
			{
			    // Normal, unflipped
			    (*DrawClippedTilePtr) (Tile + t1 + (Quot & 1),
						   s, 0, Count, VirtAlign, Lines);
			}
			else
			if (Tile & H_FLIP)
			{
			    if (Tile & V_FLIP)
			    {
				// H & V flip
				(*DrawClippedTilePtr) (Tile + t2 + 1 - (Quot & 1),
						       s, 0, Count, VirtAlign, Lines);
			    }
			    else
			    {
				// H flip only
				(*DrawClippedTilePtr) (Tile + t1 + 1 - (Quot & 1),
						       s, 0, Count, VirtAlign, Lines);
			    }
			}
			else
			{
			    // V flip only
			    (*DrawClippedTilePtr) (Tile + t2 + (Quot & 1),
						   s, 0, Count, VirtAlign, Lines);
			}
		    }
		}
		else
		    P = TRUE;
	    }
	}
    }
    return (P);
}

#define RENDER_BACKGROUND_MODE7(TYPE,FUNC) \
    CHECK_SOUND(); \
\
    uint8 *VRAM1 = Memory.VRAM + 1; \
\
    int aa, cc; \
    int dir; \
    int startx, endx; \
    int Left, Right; \
    int ClipCount = pCurrentClip->Count [bg]; \
\
    if (!ClipCount) \
    { \
	ClipCount = 1; \
	Left = 0; \
	Right = 256; \
    } \
\
    Screen += StartY * GFX.Pitch; \
    struct SLineMatrixData *l = &LineMatrixData [StartY]; \
\
    for (int Line = StartY; Line <= EndY; Line++, Screen += GFX.Pitch, l++) \
    { \
	int yy; \
\
	int HOffset = ((int) LineData [Line].BG[0].HOffset << 19) >> 19; \
	int VOffset = ((int) LineData [Line].BG[0].VOffset << 19) >> 19; \
\
	int CentreX = ((int) l->CentreX << 19) >> 19; \
	int CentreY = ((int) l->CentreY << 19) >> 19; \
\
	if (PPU.Mode7VFlip) \
	    yy = (((int) 261 - Line) + VOffset) - CentreY; \
	else \
	    yy = (Line + VOffset) - CentreY; \
\
	int BB = l->MatrixB * yy + (CentreX << 8); \
	int DD = l->MatrixD * yy + (CentreY << 8); \
\
	for (int clip = 0; clip < ClipCount; clip++) \
	{ \
	    if (pCurrentClip->Count [bg]) \
	    { \
		Left = pCurrentClip->Left [clip][bg]; \
		Right = pCurrentClip->Right [clip][bg]; \
		if (Right <= Left) \
		    continue; \
	    } \
	    TYPE *p = (TYPE *) Screen + Left; \
\
	    if (PPU.Mode7HFlip) \
	    { \
		startx = Right - 1; \
		endx = Left - 1; \
		dir = -1; \
		aa = -l->MatrixA; \
		cc = -l->MatrixC; \
	    } \
	    else \
	    { \
		startx = Left; \
		endx = Right; \
		dir = 1; \
		aa = l->MatrixA; \
		cc = l->MatrixC; \
	    } \
	    int xx = (startx + HOffset) - CentreX; \
	    int AA = l->MatrixA * xx; \
	    int CC = l->MatrixC * xx; \
\
	    if (!PPU.Mode7Repeat) \
	    { \
		for (int x = startx; x != endx; x += dir, AA += aa, CC += cc, p++) \
		{ \
		    int X = ((AA + BB) >> 8) & 0x3ff; \
		    int Y = ((CC + DD) >> 8) & 0x3ff; \
		    uint8 *TileData = VRAM1 + (Memory.VRAM[((Y & ~7) << 5) + ((X >> 2) & ~1)] << 7); \
		    uint32 b; \
		    if (b = *(TileData + ((Y & 7) << 4) + ((X & 7) << 1))) \
			*p = (FUNC); \
		} \
	    } \
	    else \
	    { \
		for (int x = startx; x != endx; x += dir, AA += aa, CC += cc, p++) \
		{ \
		    int X = ((AA + BB) >> 8); \
		    int Y = ((CC + DD) >> 8); \
\
		    if (((X | Y) & ~0x3ff) == 0) \
		    { \
			uint8 *TileData = VRAM1 + (Memory.VRAM[((Y & ~7) << 5) + ((X >> 2) & ~1)] << 7); \
			uint32 b; \
			if (b = *(TileData + ((Y & 7) << 4) + ((X & 7) << 1))) \
			    *p = (FUNC); \
		    } \
		    else \
		    { \
			if (PPU.Mode7Repeat == 3) \
			{ \
			    X = (x + HOffset) & 7; \
			    Y = (yy + CentreY) & 7; \
			    uint32 b; \
    \
			    if (b = *(VRAM1 + ((Y & 7) << 4) + ((X & 7) << 1))) \
				*p = (FUNC); \
			} \
		    } \
		} \
	    } \
	} \
    }

void DrawBGMode7Background (uint8 *Screen, int bg)
{
    RENDER_BACKGROUND_MODE7 (uint8, (uint8) (b & Mode7Mask))
}

void DrawBGMode7Background16 (uint8 *Screen, int bg)
{
    RENDER_BACKGROUND_MODE7 (uint16, IPPU.ScreenColors [b & Mode7Mask]);
}

void DrawBGMode7Background16Add (uint8 *Screen, int bg)
{
    RENDER_BACKGROUND_MODE7 (uint16, COLOR_ADD (IPPU.ScreenColors [b & Mode7Mask],
						p [GFX.Delta]));
}

void DrawBGMode7Background16Add1_2 (uint8 *Screen, int bg)
{
    RENDER_BACKGROUND_MODE7 (uint16, COLOR_ADD1_2 (IPPU.ScreenColors [b & Mode7Mask],
						   p [GFX.Delta]));
}

void DrawBGMode7Background16Sub (uint8 *Screen, int bg)
{
    RENDER_BACKGROUND_MODE7 (uint16, COLOR_SUB (IPPU.ScreenColors [b & Mode7Mask],
						p [GFX.Delta]));
}

void DrawBGMode7Background16Sub1_2 (uint8 *Screen, int bg)
{
    RENDER_BACKGROUND_MODE7 (uint16, COLOR_SUB1_2 (IPPU.ScreenColors [b & Mode7Mask],
						   p [GFX.Delta]));
}

void RenderScreen (uint8 *Screen, bool8 sub, bool8 force_no_add)
{
    bool8 BG0;
    bool8 BG1;
    bool8 BG2;
    bool8 BG3;
    bool8 OB;

    GFX.S = Screen;

    if (!sub)
    {
	pCurrentClip = &IPPU.Clip [0];
	BG0 = ON_MAIN (0);
	BG1 = ON_MAIN (1);
	BG2 = ON_MAIN (2);
	BG3 = ON_MAIN (3);
	OB  = ON_MAIN (4);
    }
    else
    {
	pCurrentClip = &IPPU.Clip [1];
	BG0 = ON_SUB (0);
	BG1 = ON_SUB (1);
	BG2 = ON_SUB (2);
	BG3 = ON_SUB (3);
	OB  = ON_SUB (4);
    }

    sub |= force_no_add;

    if (PPU.BGMode <= 1)
    {
	if (PPU.BGMode == 0)
	{
	    SelectTileRenderer (sub || !SUB_OR_ADD(3));
	    BG3 = BG3 && DrawBackground (PPU.BGMode, 3, FALSE);
	}

	SelectTileRenderer (sub || !SUB_OR_ADD(2));
	BG2 = BG2 && DrawBackground (PPU.BGMode, 2, FALSE);
	if (OB)
	{
	    SelectTileRenderer (sub || !SUB_OR_ADD(4));
	    DrawOBJ (0, !sub);
	}

	if (PPU.BGMode == 0)
	{
	    SelectTileRenderer (sub || !SUB_OR_ADD(3));
	    BG3 = BG3 && DrawBackground (PPU.BGMode, 3, TRUE);
	}

	if ((Memory.FillRAM [0x2105] & 8) == 0 && BG2)
	{
	    SelectTileRenderer (sub || !SUB_OR_ADD(2));
	    DrawBackground (PPU.BGMode, 2, TRUE);
	}

	if (OB)
	{
	    SelectTileRenderer (sub || !SUB_OR_ADD(4));
	    DrawOBJ (1, !sub);
	}

	SelectTileRenderer (sub || !SUB_OR_ADD(1));
	BG1 = BG1 && DrawBackground (PPU.BGMode, 1, FALSE);

	SelectTileRenderer (sub || !SUB_OR_ADD(0));
	BG0 = BG0 && DrawBackground (PPU.BGMode, 0, FALSE);

	if (OB)
	{
	    SelectTileRenderer (sub || !SUB_OR_ADD(4));
	    DrawOBJ (2, !sub);
	}

	if (BG1)
	{
	    SelectTileRenderer (sub || !SUB_OR_ADD(1));
	    DrawBackground (PPU.BGMode, 1, TRUE);
	}

	if (BG0)
	{
	    SelectTileRenderer (sub || !SUB_OR_ADD(0));
	    DrawBackground (PPU.BGMode, 0, TRUE);
	}

	if (OB)
	{
	    SelectTileRenderer (sub || !SUB_OR_ADD(4));
	    DrawOBJ (3, !sub);
	}

	if ((Memory.FillRAM [0x2105] & 8) != 0 && BG2)
	{
	    SelectTileRenderer (sub || !SUB_OR_ADD(2));
	    DrawBackground (PPU.BGMode, 2, TRUE);
	}
    }
    else if (PPU.BGMode != 7)
    {			// Use 1/2 Screenmode priority
	if (PPU.BGMode != 6)
	{
	    SelectTileRenderer (sub || !SUB_OR_ADD(1));
	    BG1 = BG1 && DrawBackground (PPU.BGMode, 1, FALSE);
	}

	if (OB)
	{
	    SelectTileRenderer (sub || !SUB_OR_ADD(4));
	    DrawOBJ (0, !sub);
	}

	SelectTileRenderer (sub || !SUB_OR_ADD(0));
	BG0 = BG0 && DrawBackground (PPU.BGMode, 0, FALSE);

	if (OB)
	{
	    SelectTileRenderer (sub || !SUB_OR_ADD(4));
	    DrawOBJ (1, !sub);
	}

	if (PPU.BGMode != 6 && BG1)
	{
	    SelectTileRenderer (sub || !SUB_OR_ADD(1));
	    DrawBackground (PPU.BGMode, 1, TRUE);
	}

	if (OB)
	{
	    SelectTileRenderer (sub || !SUB_OR_ADD(4));
	    DrawOBJ (2, !sub);
	}

	if (BG0)
	{
	    SelectTileRenderer (sub || !SUB_OR_ADD(0));
	    DrawBackground (PPU.BGMode, 0, TRUE);
	}

	if (OB)
	{
	    SelectTileRenderer (sub || !SUB_OR_ADD(4));
	    DrawOBJ (3, !sub);
	}
    }
    else
    {
	if (OB)
	{
	    SelectTileRenderer (sub || !SUB_OR_ADD(4));
	    DrawOBJ (0, !sub);
	}
	if (BG0 || ((Memory.FillRAM [0x2133] & 0x40) && BG1))
	{
	    int bg;

	    if (Memory.FillRAM [0x2133] & 0x40)
	    {
		Mode7Mask = 0x7f;
		bg = 1;
	    }
	    else
	    {
		Mode7Mask = 0xff;
		bg = 0;
	    }

	    if (sub || !SUB_OR_ADD(0))
		DrawBGMode7Background16 (Screen, bg);
	    else
	    {
		if (Memory.FillRAM [0x2131] & 0x80)
		{
		    if (Memory.FillRAM [0x2131] & 0x40)
			DrawBGMode7Background16Sub1_2 (Screen, bg);
		    else
			DrawBGMode7Background16Sub (Screen, bg);
		}
		else
		{
		    if (Memory.FillRAM [0x2131] & 0x40)
			DrawBGMode7Background16Add1_2 (Screen, bg);
		    else
			DrawBGMode7Background16Add (Screen, bg);
		}
	    }
	}

	if (OB)
	{
	    if (Memory.FillRAM [0x2133] & 0x40)
		DrawOBJ (0, !sub);
	    DrawOBJ (1, !sub);
	    DrawOBJ (2, !sub);
	    DrawOBJ (3, !sub);
	}
    }
}

#include "font.h"

void DisplayChar (uint8 *Screen, uint8 c)
{
    int line = (((c & 0x7f) - 32) >> 4) * font_height;
    int offset = (((c & 0x7f) - 32) & 15) * font_width;
    if (Settings.SixteenBit)
    {
	int h, w;
	uint16 *s = (uint16 *) Screen;
	for (h = 0; h < font_height; h++, line++,
	     s += GFX.PPL - font_width)
	{
	    for (w = 0; w < font_width; w++, s++)
	    {
		uint8 p = font [line][offset + w];

		if (p == '#')
		    *s = 0xffff;
		else
		if (p == '.')
		    *s = 0x0000;
	    }
	}
    }
    else
    {
	int h, w;
	uint8 *s = Screen;
	for (h = 0; h < font_height; h++, line++,
	     s += GFX.PPL - font_width)
	{
	    for (w = 0; w < font_width; w++, s++)
	    {
		uint8 p = font [line][offset + w];

		if (p == '#')
		    *s = 255;
		else
		if (p == '.')
		    *s = 0;
	    }
	}
    }
}

static void S9xDisplayFrameRate ()
{
    uint8 *Screen = GFX.Screen + 2 +
		    (PPU.ScreenHeight - font_height - 1) * GFX.RealPitch;
    char string [10];
    int len = 5;

    sprintf (string, "%02d/%02d", IPPU.DisplayedRenderedFrameCount,
	     Memory.ROMFramesPerSecond);

    int i;
    for (i = 0; i < len; i++)
    {
	DisplayChar (Screen, string [i]);
	Screen += Settings.SixteenBit ? (font_width - 1) * sizeof (uint16) : 
		  (font_width - 1);
    }
}

static void S9xDisplayString (const char *string)
{
    uint8 *Screen = GFX.Screen + 2 +
		    (PPU.ScreenHeight - font_height * 2 - 1) * GFX.RealPitch;
    int len = strlen (string);

    int i;
    for (i = 0; i < len; i++)
    {
	DisplayChar (Screen, string [i]);
	Screen += Settings.SixteenBit ? (font_width - 1) * sizeof (uint16) : 
		  (font_width - 1);
    }
}

void S9xUpdateScreen ()
{
    GFX.S = GFX.Screen;

    if (IPPU.OBJChanged)
	S9xSetupOBJ ();

    if (PPU.RecomputeClipWindows)
    {
	ComputeClipWindows ();
	PPU.RecomputeClipWindows = FALSE;
    }

    StartY = IPPU.PreviousLine;
    if ((EndY = IPPU.CurrentLine - 1) >= PPU.ScreenHeight)
	EndY = PPU.ScreenHeight - 1;

    if (Settings.SupportHiRes &&
	(PPU.BGMode == 5 || PPU.BGMode == 6))
    {
	IPPU.RenderedScreenWidth = 512;
	if (!IPPU.DoubleWidthPixels)
	{
	    // Scale any existing lo-res pixels on screen
	    if (Settings.SixteenBit)
	    {
		for (register int y = 0; y < StartY; y++)
		{
		    register uint16 *p = (uint16 *) (GFX.Screen + y * GFX.Pitch) + 255;
		    register uint16 *q = (uint16 *) (GFX.Screen + y * GFX.Pitch) + 510;
		    for (register int x = 255; x >= 0; x--, p--, q -= 2)
			*q = *(q + 1) = *p;
		}
	    }
	    else
	    {
		for (register int y = 0; y < StartY; y++)
		{
		    register uint8 *p = GFX.Screen + y * GFX.Pitch + 255;
		    register uint8 *q = GFX.Screen + y * GFX.Pitch + 510;
		    for (register int x = 255; x >= 0; x--, p--, q -= 2)
			*q = *(q + 1) = *p;
		}
	    }
	    IPPU.DoubleWidthPixels = TRUE;
	}
    }

    if (Settings.Transparency && Settings.SixteenBit)
    {
	bool8 cleared_main = FALSE;
	if (PPU.BGMode != 5 && PPU.BGMode != 6 && !PPU.ForcedBlanking &&
	    ADD_OR_SUB_ON_ANYTHING &&
	    (Memory.FillRAM [0x2130] & 0x30) != 0x30 &&
	    !((Memory.FillRAM [0x2130] & 0x30) == 0x10 && IPPU.Clip[1].Count[5] == 0) &&
	    (SUB_ENABLED || FIXED_ENABLED))
	{
	    struct ClipData *pClip;

	    uint32 back = BUILD_PIXEL (IPPU.XB [PPU.FixedColourRed & 0x1f],
				      IPPU.XB [PPU.FixedColourGreen & 0x1f],
				      IPPU.XB [PPU.FixedColourBlue & 0x1f]);
	    back = back | (back << 16);
	    // Clear sub-screen to fixed colour
	    pClip = &IPPU.Clip [1];

	    if (pClip->Count [5])
	    {
		for (int y = StartY; y <= EndY; y++)
		{
		    ZeroMemory (GFX.SubScreen + y * GFX.Pitch, 
				256 * sizeof (uint16));

		    for (int c = 0; c < pClip->Count [5]; c++)
		    {
			uint16 *p = (uint16 *) (GFX.SubScreen + y * GFX.Pitch);
			uint16 *q = (uint16 *) p + pClip->Right [c][5];
			p += pClip->Left [c][5];
			while (p < q)
			    *p++ = (uint16) back;
		    }
		}
	    }
	    else
	    {
		for (int y = StartY; y <= EndY; y++)
		{
		    uint32 *p = (uint32 *) (GFX.SubScreen + y * GFX.Pitch);
		    uint32 *q = (uint32 *) ((uint16 *) p + 256);
		    while (p < q)
			*p++ = back;
		}
	    }

	    if (ANYTHING_ON_SUB)
	    {
		// XXX: If sub-screen is only being added to back then 
		// sub-screen could be rendered directly into main-screen
		// area.
		RenderScreen (GFX.SubScreen, TRUE, TRUE);
	    }
	    if (SUB_OR_ADD(5))
	    {
		back = IPPU.ScreenColors [0];
		uint32 Left, Right;
		uint32 Count;

		pClip = &IPPU.Clip [0];
		for (int y = StartY; y <= EndY; y++)
		{
		    if (Count = pClip->Count [5])
		    {
			ZeroMemory (GFX.Screen + y * GFX.Pitch,
				    256 * sizeof (uint16));
		    }
		    else
		    {
			Left = 0;
			Right = 256;
			Count = 1;
		    }

		    for (int b = 0; b < Count; b++)
		    {
			if (pClip->Count [5])
			{
			    Left = pClip->Left [b][5];
			    Right = pClip->Right [b][5];
			    if (Right <= Left)
				continue;
			}

			if (Memory.FillRAM [0x2131] & 0x80)
			{
			    if (Memory.FillRAM [0x2131] & 0x40)
			    {
				// Subtract, halving the result.
				register uint16 *p = (uint16 *) (GFX.Screen + y * GFX.Pitch) + Left;
				register uint16 *q = p + GFX.Delta;
				for (int x = Left; x < Right; x++, q++)
				    *p++ = COLOR_SUB1_2 (back, *q);
			    }
			    else
			    {
				// Subtract
				register uint16 *p = (uint16 *) (GFX.Screen + y * GFX.Pitch) + Left;
				register uint16 *q = p + GFX.Delta;
				for (int x = Left; x < Right; x++, q++)
				    *p++ = COLOR_SUB (back, *q);
			    }
			}
			else
			if (Memory.FillRAM [0x2131] & 0x40)
			{
			    register uint16 *p = (uint16 *) (GFX.Screen + y * GFX.Pitch) + Left;
			    register uint16 *q = p + GFX.Delta;
			    for (int x = Left; x < Right; x++, q++)
				*p++ = COLOR_ADD1_2 (back, *q);
			}
			else
			{
			    if (PPU.CGDATA [0] == 0)
			    {
				// Sub-screen is being added to back-drop color and
				// the back-drop color is zero. Just
				// copy sub-screen to the main screen.
				memmove (GFX.Screen + y * GFX.Pitch +
					 Left * sizeof (uint16),
					 GFX.SubScreen + y * GFX.Pitch +
					 Left * sizeof (uint16),
					 (Right - Left) * sizeof (uint16));
			    }
			    else
			    {
				register uint16 *p = (uint16 *) (GFX.Screen + y * GFX.Pitch) + Left;
				register uint16 *q = p + GFX.Delta;
				for (int x = Left; x < Right; x++, q++)
				    *p++ = COLOR_ADD (back, *q);
			    }
			}
		    }
		}
		cleared_main = TRUE;
	    }
	    else
	    {
		for (int y = StartY; y <= EndY; y++)
		    memmove (GFX.Screen + y * GFX.Pitch, 
			     GFX.SubScreen + y * GFX.Pitch, 
			     GFX.Pitch * sizeof (uint16));
	    }

	    if (!cleared_main)
	    {
		back = IPPU.ScreenColors [0] | (IPPU.ScreenColors [0] << 16);

		pClip = &IPPU.Clip [0];

		if (pClip->Count [5])
		{
		    for (int y = StartY; y <= EndY; y++)
		    {
			if (IPPU.LatchedInterlace)
			{
#if 0
			    ZeroMemory (GFX.Screen + y * 2 * GFX.RealPitch,
					GFX.RealPitch * 2);
#endif
			}
			else
			{
#if 0
			    ZeroMemory (GFX.Screen + y * GFX.Pitch,
					256 * sizeof (uint16));
#endif
			}
			for (int b = 0; b < pClip->Count [5]; b++)
			{
			    uint32 Left = pClip->Left [b][5];
			    uint32 Right = pClip->Right [b][5];

			    if (IPPU.LatchedInterlace)
			    {
				uint16 *p = (uint16 *) (GFX.Screen + y * 2 * GFX.RealPitch) + Left;
				uint16 *q = p + (Right - Left);
				while (p < q)
				    *p++ = (int16) back;

				p = (uint16 *) (GFX.Screen + (y * 2 + 1) * GFX.RealPitch) + Left;
				q = q + (Right - Left);
				while (p < q)
				    *p++ = (int16) back;
			    }
			    else
			    {
				uint16 *p = (uint16 *) (GFX.Screen + y * GFX.Pitch) + Left;
				uint16 *q = p + (Right - Left);
				while (p < q)
				    *p++ = (int16) back;
			    }
			}
		    }
		}
		else
		{
		    if (IPPU.LatchedInterlace)
		    {
			uint32 *p = (uint32 *) (GFX.Screen + StartY * 2 * GFX.RealPitch);
			uint32 *q = (uint32 *) (GFX.Screen + (EndY + 1) * 2 * GFX.RealPitch);
			while (p < q)
			    *p++ = back;
		    }
		    else
		    {
			for (int y = StartY; y <= EndY; y++)
			{
			    uint32 *p = (uint32 *) (GFX.Screen + y * GFX.Pitch);
			    uint32 *q = (uint32 *) ((uint8 *) p + GFX.RealPitch);
			    while (p < q)
				*p++ = back;
			}
		    }
		}
	    }
	    RenderScreen (GFX.Screen, FALSE, FALSE);
	}
	else
	{
	    uint32 back = IPPU.ScreenColors [0] | 
			 (IPPU.ScreenColors [0] << 16);
	    if (PPU.ForcedBlanking)
		back = 0;

	    if (IPPU.LatchedInterlace)
	    {
		uint32 *p = (uint32 *) (GFX.Screen + StartY * 2 * GFX.RealPitch);
		uint32 *q = (uint32 *) (GFX.Screen + (EndY + 1) * 2 * GFX.RealPitch);
		while (p < q)
		    *p++ = back;
	    }
	    else
	    {
		for (int y = StartY; y <= EndY; y++)
		{
		    uint32 *p = (uint32 *) (GFX.Screen + y * GFX.Pitch);
		    uint32 *q = (uint32 *) ((uint16 *) p + IPPU.RenderedScreenWidth);
		    while (p < q)
			*p++ = back;
		}
	    }
	    if (!PPU.ForcedBlanking)
		RenderScreen (GFX.Screen, FALSE, TRUE);
	}
    }
    else
    {
	if (Settings.SixteenBit)
	{
	    uint32 back = IPPU.ScreenColors [0] | (IPPU.ScreenColors [0] << 16);
	    if (PPU.ForcedBlanking)
		back = 0;

	    if (IPPU.LatchedInterlace)
	    {
		uint32 *p = (uint32 *) (GFX.Screen + StartY * 2 * GFX.RealPitch);
		uint32 *q = (uint32 *) (GFX.Screen + (EndY + 1) * 2 * GFX.RealPitch);
		while (p < q)
		    *p++ = back;
	    }
	    else
	    {
		for (int y = StartY; y <= EndY; y++)
		{
		    uint32 *p = (uint32 *) (GFX.Screen + y * GFX.Pitch);
		    uint32 *q = (uint32 *) ((uint16 *) p + IPPU.RenderedScreenWidth);
		    while (p < q)
			*p++ = back;
		}
	    }
	    SelectTileRenderer (TRUE);
	}
	else
	{
	    if (IPPU.LatchedInterlace)
	    {
		ZeroMemory (GFX.Screen + StartY * 2 * GFX.RealPitch,
			    (EndY + 1) * 2 * GFX.RealPitch);
	    }
	    else
	    {
		for (int y = StartY; y <= EndY; y++)
		{
		    ZeroMemory (GFX.Screen + y * GFX.Pitch,
				IPPU.RenderedScreenWidth);
		}
	    }
	}
	if (!PPU.ForcedBlanking)
	{
	    pCurrentClip = &IPPU.Clip [0];

#define FIXCLIP(n)\
if (Memory.FillRAM [0x212c] & (1 << (n))) \
    pCurrentClip = &IPPU.Clip [0]; \
else \
    pCurrentClip = &IPPU.Clip [1]


#define DISPLAY(n)\
    (!(PPU.BG_Forced & n) && \
    ((!Settings.BGLayering && z == 0 && \
      ((Memory.FillRAM [0x212c] & n) || ((Memory.FillRAM [0x212d] & n) && subadd))) || \
     (Settings.BGLayering && ((z == 1 && (Memory.FillRAM [0x212c] & n)) || \
     (z == 0 && (Memory.FillRAM [0x212d] & n) && subadd && !(Memory.FillRAM [0x212c] & n))))))

	    for (int z = 0; z < 2; z++)
	    {
		uint8 subadd = Memory.FillRAM [0x2131] & 0x3f;

		bool8 BG0 = DISPLAY(1);
		bool8 BG1 = DISPLAY(2);
		bool8 BG2 = DISPLAY(4);
		bool8 BG3 = DISPLAY(8);
		bool8 OB  = DISPLAY(16);

		if (PPU.BGMode <= 1)
		{
		    if (PPU.BGMode == 0)
		    {
			FIXCLIP(3);
			BG3 = BG3 && DrawBackground (PPU.BGMode, 3, FALSE);
		    }

		    FIXCLIP(2);
		    BG2 = BG2 && DrawBackground (PPU.BGMode, 2, FALSE);
		    if (OB)
		    {
			FIXCLIP(4);
			DrawOBJ (0);
		    }

		    if (PPU.BGMode == 0)
		    {
			FIXCLIP(3);
			BG3 = BG3 && DrawBackground (PPU.BGMode, 3, TRUE);
		    }

		    if ((Memory.FillRAM [0x2105] & 8) == 0 && BG2)
		    {
			FIXCLIP(2);
			DrawBackground (PPU.BGMode, 2, TRUE);
		    }

		    if (OB)
		    {
			FIXCLIP(4);
			DrawOBJ (1);
		    }

		    FIXCLIP(1);
		    BG1 = BG1 && DrawBackground (PPU.BGMode, 1, FALSE);

		    FIXCLIP(0);
		    BG0 = BG0 && DrawBackground (PPU.BGMode, 0, FALSE);

		    if (OB)
		    {
			FIXCLIP(4);
			DrawOBJ (2);
		    }

		    if (BG1)
		    {
			FIXCLIP(1);
			DrawBackground (PPU.BGMode, 1, TRUE);
		    }

		    if (BG0)
		    {
			FIXCLIP(0);
			DrawBackground (PPU.BGMode, 0, TRUE);
		    }

		    if (OB)
		    {
			FIXCLIP(4);
			DrawOBJ (3);
		    }

		    if ((Memory.FillRAM [0x2105] & 8) != 0 && BG2)
		    {
			FIXCLIP(2);
			DrawBackground (PPU.BGMode, 2, TRUE);
		    }
		}
		else if (PPU.BGMode != 7)
		{			// Use 1/2 Screenmode priority
		    if (PPU.BGMode != 6)
		    {
			FIXCLIP(1);
			BG1 = BG1 && DrawBackground (PPU.BGMode, 1, FALSE);
		    }

		    if (OB)
		    {
			FIXCLIP(4);
			DrawOBJ (0);
		    }

		    FIXCLIP(0);
		    BG0 = BG0 && DrawBackground (PPU.BGMode, 0, FALSE);

		    if (OB)
		    {
			FIXCLIP(4);
			DrawOBJ (1);
		    }

		    if (PPU.BGMode != 6 && BG1)
		    {
			FIXCLIP(1);
			DrawBackground (PPU.BGMode, 1, TRUE);
		    }

		    if (OB)
		    {
			FIXCLIP(4);
			DrawOBJ (2);
		    }

		    if (BG0)
		    {
			FIXCLIP(0);
			DrawBackground (PPU.BGMode, 0, TRUE);
		    }

		    if (OB)
		    {
			FIXCLIP(4);
			DrawOBJ (3);
		    }
		}
		else
		{
		    if (OB)
		    {
			FIXCLIP(4);
			DrawOBJ (0);
		    }
		    if (BG0 || ((Memory.FillRAM [0x2133] & 0x40) && BG1))
		    {
			int bg;
			FIXCLIP(0);
			if (Memory.FillRAM [0x2133] & 0x40)
			{
			    Mode7Mask = 0x7f;
			    bg = 1;
			}
			else
			{
			    bg = 0;
			    Mode7Mask = 0xff;
			}

			if (!Settings.SixteenBit)
			    DrawBGMode7Background (GFX.Screen, bg);
			else
			    DrawBGMode7Background16 (GFX.Screen, bg);
		    }

		    if (OB)
		    {
			FIXCLIP(4);
			DrawOBJ (1);
			DrawOBJ (2);
			DrawOBJ (3);
		    }
		}
	    }
	}
    }
    if (Settings.SupportHiRes)
    {
	if (PPU.BGMode != 5 && PPU.BGMode != 6)
	{
	    if (IPPU.DoubleWidthPixels)
	    {
		// Mixure of background modes used on screen - scale width
		// of all non-mode 5 and 6 pixels.
		if (Settings.SixteenBit)
		{
		    for (register int y = StartY; y <= EndY; y++)
		    {
			register uint16 *p = (uint16 *) (GFX.Screen + y * GFX.Pitch) + 255;
			register uint16 *q = (uint16 *) (GFX.Screen + y * GFX.Pitch) + 510;
			for (register int x = 255; x >= 0; x--, p--, q -= 2)
			    *q = *(q + 1) = *p;
		    }
		}
		else
		{
		    for (register int y = StartY; y <= EndY; y++)
		    {
			register uint8 *p = GFX.Screen + y * GFX.Pitch + 255;
			register uint8 *q = GFX.Screen + y * GFX.Pitch + 510;
			for (register int x = 255; x >= 0; x--, p--, q -= 2)
			    *q = *(q + 1) = *p;
		    }
		}
	    }
	    if (IPPU.LatchedInterlace)
	    {
		// Interlace is enabled - scale height of all non-mode 5 and 6
		// pixels.
		for (int y = StartY; y <= EndY; y++)
		{
		    memmove (GFX.Screen + (y * 2 + 1) * GFX.RealPitch,
			     GFX.Screen + y * 2 * GFX.RealPitch,
			     GFX.RealPitch);
		}
	    }
	}
    }
    IPPU.PreviousLine = IPPU.CurrentLine;
}

#ifdef GFX_MULTI_FORMAT

#define _BUILD_PIXEL(F) \
uint32 BuildPixel##F(uint32 R, uint32 G, uint32 B) \
{ \
    return (BUILD_PIXEL_##F(R,G,B)); \
}\
uint32 BuildPixel2##F(uint32 R, uint32 G, uint32 B) \
{ \
    return (BUILD_PIXEL2_##F(R,G,B)); \
} \
void DecomposePixel##F(uint32 pixel, uint32 &R, uint32 &G, uint32 &B) \
{ \
    DECOMPOSE_PIXEL_##F(pixel,R,G,B); \
}

_BUILD_PIXEL(RGB565)
_BUILD_PIXEL(RGB555)
_BUILD_PIXEL(BGR565)
_BUILD_PIXEL(BGR555)
_BUILD_PIXEL(GBR565)
_BUILD_PIXEL(GBR555)

#define _BUILD_SETUP(F) \
GFX.BuildPixel = BuildPixel##F; \
GFX.BuildPixel2 = BuildPixel2##F; \
GFX.DecomposePixel = DecomposePixel##F; \
RED_LOW_BIT_MASK = RED_LOW_BIT_MASK_##F; \
GREEN_LOW_BIT_MASK = GREEN_LOW_BIT_MASK_##F; \
BLUE_LOW_BIT_MASK = BLUE_LOW_BIT_MASK_##F; \
RED_HI_BIT_MASK = RED_HI_BIT_MASK_##F; \
GREEN_HI_BIT_MASK = GREEN_HI_BIT_MASK_##F; \
BLUE_HI_BIT_MASK = BLUE_HI_BIT_MASK_##F; \
MAX_RED = MAX_RED_##F; \
MAX_GREEN = MAX_GREEN_##F; \
MAX_BLUE = MAX_BLUE_##F; \
GREEN_HI_BIT = ((MAX_GREEN_##F + 1) >> 1); \
SPARE_RGB_BIT_MASK = SPARE_RGB_BIT_MASK_##F; \
RGB_LOW_BITS_MASK = (RED_LOW_BIT_MASK_##F | \
 		     GREEN_LOW_BIT_MASK_##F | \
		     BLUE_LOW_BIT_MASK_##F); \
RGB_HI_BITS_MASK = (RED_HI_BIT_MASK_##F | \
		    GREEN_HI_BIT_MASK_##F | \
		    BLUE_HI_BIT_MASK_##F); \
RGB_HI_BITS_MASKx2 = ((RED_HI_BIT_MASK_##F | \
		       GREEN_HI_BIT_MASK_##F | \
		       BLUE_HI_BIT_MASK_##F) << 1); \
RGB_REMOVE_LOW_BITS_MASK = ~RGB_LOW_BITS_MASK;

bool8 S9xSetRenderPixelFormat (int format)
{
    switch (format)
    {
    case RGB565:
	_BUILD_SETUP(RGB565)
	return (TRUE);
    case RGB555:
	_BUILD_SETUP(RGB555)
	return (TRUE);
    case BGR565:
	_BUILD_SETUP(BGR565)
	return (TRUE);
    case BGR555:
	_BUILD_SETUP(BGR555)
	return (TRUE);
    case GBR565:
	_BUILD_SETUP(GBR565)
	return (TRUE);
    case GBR555:
	_BUILD_SETUP(GBR555)
	return (TRUE);

    default:
	break;
    }
    return (FALSE);
}
#endif
