/******************************************************************************* Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. (c) Copyright 1996 - 2002 Gary Henderson (gary.henderson@ntlworld.com) and Jerremy Koot (jkoot@snes9x.com) (c) Copyright 2001 - 2004 John Weidman (jweidman@slip.net) (c) Copyright 2002 - 2004 Brad Jorsch (anomie@users.sourceforge.net), funkyass (funkyass@spam.shaw.ca), Joel Yliluoma (http://iki.fi/bisqwit/) Kris Bleakley (codeviolation@hotmail.com), Matthew Kendora, Nach (n-a-c-h@users.sourceforge.net), Peter Bortas (peter@bortas.org) and zones (kasumitokoduck@yahoo.com) C4 x86 assembler and some C emulation code (c) Copyright 2000 - 2003 zsKnight (zsknight@zsnes.com), _Demo_ (_demo_@zsnes.com), and Nach C4 C++ code (c) Copyright 2003 Brad Jorsch DSP-1 emulator code (c) Copyright 1998 - 2004 Ivar (ivar@snes9x.com), _Demo_, Gary Henderson, John Weidman, neviksti (neviksti@hotmail.com), Kris Bleakley, Andreas Naive DSP-2 emulator code (c) Copyright 2003 Kris Bleakley, John Weidman, neviksti, Matthew Kendora, and Lord Nightmare (lord_nightmare@users.sourceforge.net OBC1 emulator code (c) Copyright 2001 - 2004 zsKnight, pagefault (pagefault@zsnes.com) and Kris Bleakley Ported from x86 assembler to C by sanmaiwashi SPC7110 and RTC C++ emulator code (c) Copyright 2002 Matthew Kendora with research by zsKnight, John Weidman, and Dark Force S-DD1 C emulator code (c) Copyright 2003 Brad Jorsch with research by Andreas Naive and John Weidman S-RTC C emulator code (c) Copyright 2001 John Weidman ST010 C++ emulator code (c) Copyright 2003 Feather, Kris Bleakley, John Weidman and Matthew Kendora Super FX x86 assembler emulator code (c) Copyright 1998 - 2003 zsKnight, _Demo_, and pagefault Super FX C emulator code (c) Copyright 1997 - 1999 Ivar, Gary Henderson and John Weidman SH assembler code partly based on x86 assembler code (c) Copyright 2002 - 2004 Marcus Comstedt (marcus@mc.pp.se) Specific ports contains the works of other authors. See headers in individual files. Snes9x homepage: http://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" #include "cheats.h" #include "screenshot.h" #define M7 19 #define M8 19 void output_png(); void ComputeClipWindows (); static void S9xDisplayFrameRate (); static void S9xDisplayString (const char *string); 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 struct SBG BG; extern struct SLineData LineData[240]; extern struct SLineMatrixData LineMatrixData [240]; extern uint8 Mode7Depths [2]; #define CLIP_10_BIT_SIGNED(a) \ ((a) & ((1 << 10) - 1)) + (((((a) & (1 << 13)) ^ (1 << 13)) - (1 << 13)) >> 3) #define ON_MAIN(N) \ (GFX.r212c & (1 << (N)) && \ !(PPU.BG_Forced & (1 << (N)))) #define SUB_OR_ADD(N) \ (GFX.r2131 & (1 << (N))) #define ON_SUB(N) \ ((GFX.r2130 & 0x30) != 0x30 && \ (GFX.r2130 & 2) && \ (GFX.r212d & (1 << N)) && \ !(PPU.BG_Forced & (1 << (N)))) #define ANYTHING_ON_SUB \ ((GFX.r2130 & 0x30) != 0x30 && \ (GFX.r2130 & 2) && \ (GFX.r212d & 0x1f)) #define ADD_OR_SUB_ON_ANYTHING \ (GFX.r2131 & 0x3f) #define FIX_INTERLACE(SCREEN, DO_DEPTH, DEPTH) \ if (IPPU.DoubleHeightPixels && ((PPU.BGMode != 5 && PPU.BGMode != 6) || !IPPU.Interlace)) \ for (uint32 y = GFX.StartY; y <= GFX.EndY; y++) \ { \ memmove (SCREEN + (y * 2 + 1) * GFX.Pitch2, \ SCREEN + y * 2 * GFX.Pitch2, \ GFX.Pitch2); \ if(DO_DEPTH){ \ memmove (DEPTH + (y * 2 + 1) * (GFX.PPLx2>>1), \ DEPTH + y * GFX.PPL, \ GFX.PPLx2>>1); \ } \ } #define BLACK BUILD_PIXEL(0,0,0) #ifndef FOREVER_16_BIT void DrawTile (uint32 Tile, uint32 Offset, uint32 StartLine, uint32 LineCount); void DrawClippedTile (uint32 Tile, uint32 Offset, uint32 StartPixel, uint32 Width, uint32 StartLine, uint32 LineCount); void DrawTilex2 (uint32 Tile, uint32 Offset, uint32 StartLine, uint32 LineCount); void DrawClippedTilex2 (uint32 Tile, uint32 Offset, uint32 StartPixel, uint32 Width, uint32 StartLine, uint32 LineCount); void DrawTilex2x2 (uint32 Tile, uint32 Offset, uint32 StartLine, uint32 LineCount); void DrawClippedTilex2x2 (uint32 Tile, uint32 Offset, uint32 StartPixel, uint32 Width, uint32 StartLine, uint32 LineCount); void DrawLargePixel (uint32 Tile, uint32 Offset, uint32 StartPixel, uint32 Pixels, uint32 StartLine, uint32 LineCount); #endif void DrawTile16 (uint32 Tile, uint32 Offset, uint32 StartLine, uint32 LineCount); void DrawClippedTile16 (uint32 Tile, uint32 Offset, uint32 StartPixel, uint32 Width, uint32 StartLine, uint32 LineCount); void DrawTile16x2 (uint32 Tile, uint32 Offset, uint32 StartLine, uint32 LineCount); void DrawClippedTile16x2 (uint32 Tile, uint32 Offset, uint32 StartPixel, uint32 Width, uint32 StartLine, uint32 LineCount); void DrawTile16x2x2 (uint32 Tile, uint32 Offset, uint32 StartLine, uint32 LineCount); void DrawClippedTile16x2x2 (uint32 Tile, uint32 Offset, uint32 StartPixel, uint32 Width, uint32 StartLine, uint32 LineCount); void DrawLargePixel16 (uint32 Tile, uint32 Offset, uint32 StartPixel, uint32 Pixels, uint32 StartLine, uint32 LineCount); void DrawTile16Add (uint32 Tile, uint32 Offset, uint32 StartLine, uint32 LineCount); void DrawClippedTile16Add (uint32 Tile, uint32 Offset, uint32 StartPixel, uint32 Width, uint32 StartLine, uint32 LineCount); void DrawTile16Add1_2 (uint32 Tile, uint32 Offset, uint32 StartLine, uint32 LineCount); void DrawClippedTile16Add1_2 (uint32 Tile, uint32 Offset, uint32 StartPixel, uint32 Width, uint32 StartLine, uint32 LineCount); void DrawTile16FixedAdd1_2 (uint32 Tile, uint32 Offset, uint32 StartLine, uint32 LineCount); void DrawClippedTile16FixedAdd1_2 (uint32 Tile, uint32 Offset, uint32 StartPixel, uint32 Width, uint32 StartLine, uint32 LineCount); void DrawTile16Sub (uint32 Tile, uint32 Offset, uint32 StartLine, uint32 LineCount); void DrawClippedTile16Sub (uint32 Tile, uint32 Offset, uint32 StartPixel, uint32 Width, uint32 StartLine, uint32 LineCount); void DrawTile16Sub1_2 (uint32 Tile, uint32 Offset, uint32 StartLine, uint32 LineCount); void DrawClippedTile16Sub1_2 (uint32 Tile, uint32 Offset, uint32 StartPixel, uint32 Width, uint32 StartLine, uint32 LineCount); void DrawTile16FixedSub1_2 (uint32 Tile, uint32 Offset, uint32 StartLine, uint32 LineCount); void DrawClippedTile16FixedSub1_2 (uint32 Tile, uint32 Offset, uint32 StartPixel, uint32 Width, uint32 StartLine, uint32 LineCount); void DrawLargePixel16Add (uint32 Tile, uint32 Offset, uint32 StartPixel, uint32 Pixels, uint32 StartLine, uint32 LineCount); void DrawLargePixel16Add1_2 (uint32 Tile, uint32 Offset, uint32 StartPixel, uint32 Pixels, uint32 StartLine, uint32 LineCount); void DrawLargePixel16Sub (uint32 Tile, uint32 Offset, uint32 StartPixel, uint32 Pixels, uint32 StartLine, uint32 LineCount); void DrawLargePixel16Sub1_2 (uint32 Tile, uint32 Offset, uint32 StartPixel, uint32 Pixels, uint32 StartLine, uint32 LineCount); 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 int 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.Pitch2 = GFX.Pitch; GFX.ZPitch = GFX.Pitch; #ifndef FOREVER_16_BIT if (Settings.SixteenBit) #endif GFX.ZPitch >>= 1; GFX.Delta = (GFX.SubScreen - GFX.Screen) >> 1; GFX.DepthDelta = GFX.SubZBuffer - GFX.ZBuffer; //GFX.InfoStringTimeout = 0; //GFX.InfoString = NULL; PPU.BG_Forced = 0; IPPU.OBJChanged = TRUE; #ifndef FOREVER_16_BIT if (Settings.Transparency) Settings.SixteenBit = TRUE; #endif IPPU.DirectColourMapsNeedRebuild = TRUE; GFX.PixSize = 1; #ifndef FOREVER_16_BIT if (Settings.SixteenBit) { #endif DrawTilePtr = DrawTile16; DrawClippedTilePtr = DrawClippedTile16; DrawLargePixelPtr = DrawLargePixel16; DrawHiResTilePtr= DrawTile16; DrawHiResClippedTilePtr = DrawClippedTile16; GFX.PPL = GFX.Pitch >> 1; GFX.PPLx2 = GFX.Pitch; #ifndef FOREVER_16_BIT } else { DrawTilePtr = DrawTile; DrawClippedTilePtr = DrawClippedTile; DrawLargePixelPtr = DrawLargePixel; DrawHiResTilePtr = DrawTile; DrawHiResClippedTilePtr = DrawClippedTile; GFX.PPL = GFX.Pitch; GFX.PPLx2 = GFX.Pitch * 2; } #endif S9xFixColourBrightness (); #ifndef FOREVER_16_BIT if (Settings.SixteenBit) { #endif if (!(GFX.X2 = (uint16 *) malloc (sizeof (uint16) * 0x10000))) return (FALSE); if (!(GFX.ZERO_OR_X2 = (uint16 *) malloc (sizeof (uint16) * 0x10000)) || !(GFX.ZERO = (uint16 *) malloc (sizeof (uint16) * 0x10000))) { if (GFX.ZERO_OR_X2) { free ((char *) GFX.ZERO_OR_X2); GFX.ZERO_OR_X2 = NULL; } if (GFX.X2) { free ((char *) GFX.X2); GFX.X2 = NULL; } return (FALSE); } uint32 r, g, b; // Build a lookup table that multiplies a packed RGB value by 2 with // saturation. for (r = 0; r <= MAX_RED; r++) { uint32 r2 = r << 1; if (r2 > MAX_RED) r2 = MAX_RED; for (g = 0; g <= MAX_GREEN; g++) { uint32 g2 = g << 1; if (g2 > MAX_GREEN) g2 = MAX_GREEN; for (b = 0; b <= MAX_BLUE; b++) { uint32 b2 = b << 1; if (b2 > MAX_BLUE) b2 = MAX_BLUE; GFX.X2 [BUILD_PIXEL2 (r, g, b)] = BUILD_PIXEL2 (r2, g2, b2); GFX.X2 [BUILD_PIXEL2 (r, g, b) & ~ALPHA_BITS_MASK] = 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. #if defined(OLD_COLOUR_BLENDING) for (r = 0; r <= MAX_RED; r++) { uint32 r2 = r; if ((r2 & 0x10) == 0) r2 = 0; else r2 = (r2 << 1) & MAX_RED; for (g = 0; g <= MAX_GREEN; g++) { uint32 g2 = g; if ((g2 & GREEN_HI_BIT) == 0) g2 = 0; else g2 = (g2 << 1) & MAX_GREEN; for (b = 0; b <= MAX_BLUE; b++) { uint32 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); GFX.ZERO_OR_X2 [BUILD_PIXEL2 (r, g, b) & ~ALPHA_BITS_MASK] = BUILD_PIXEL2 (r2, g2, b2); } } } #else for (r = 0; r <= MAX_RED; r++) { uint32 r2 = r; if ((r2 & 0x10) == 0) r2 = 0; else r2 = (r2 << 1) & MAX_RED; if (r2 == 0) r2 = 1; for (g = 0; g <= MAX_GREEN; g++) { uint32 g2 = g; if ((g2 & GREEN_HI_BIT) == 0) g2 = 0; else g2 = (g2 << 1) & MAX_GREEN; if (g2 == 0) g2 = 1; for (b = 0; b <= MAX_BLUE; b++) { uint32 b2 = b; if ((b2 & 0x10) == 0) b2 = 0; else b2 = (b2 << 1) & MAX_BLUE; if (b2 == 0) b2 = 1; GFX.ZERO_OR_X2 [BUILD_PIXEL2 (r, g, b)] = BUILD_PIXEL2 (r2, g2, b2); GFX.ZERO_OR_X2 [BUILD_PIXEL2 (r, g, b) & ~ALPHA_BITS_MASK] = BUILD_PIXEL2 (r2, g2, b2); } } } #endif // 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++) { uint32 r2 = r; if ((r2 & 0x10) == 0) r2 = 0; else r2 &= ~0x10; for (g = 0; g <= MAX_GREEN; g++) { uint32 g2 = g; if ((g2 & GREEN_HI_BIT) == 0) g2 = 0; else g2 &= ~GREEN_HI_BIT; for (b = 0; b <= MAX_BLUE; b++) { uint32 b2 = b; if ((b2 & 0x10) == 0) b2 = 0; else b2 &= ~0x10; GFX.ZERO [BUILD_PIXEL2 (r, g, b)] = BUILD_PIXEL2 (r2, g2, b2); GFX.ZERO [BUILD_PIXEL2 (r, g, b) & ~ALPHA_BITS_MASK] = BUILD_PIXEL2 (r2, g2, b2); } } } #ifndef FOREVER_16_BIT } else { GFX.X2 = NULL; GFX.ZERO_OR_X2 = NULL; GFX.ZERO = NULL; } #endif return (TRUE); } void S9xGraphicsDeinit (void) { // Free any memory allocated in S9xGraphicsInit if (GFX.X2) { free ((char *) GFX.X2); GFX.X2 = NULL; } if (GFX.ZERO_OR_X2) { free ((char *) GFX.ZERO_OR_X2); GFX.ZERO_OR_X2 = NULL; } if (GFX.ZERO) { free ((char *) GFX.ZERO); GFX.ZERO = NULL; } } void S9xBuildDirectColourMaps () { for (uint32 p = 0; p < 8; p++) { for (uint32 c = 0; c < 256; c++) { // XXX: Brightness DirectColourMaps [p][c] = BUILD_PIXEL (((c & 7) << 2) | ((p & 1) << 1), ((c & 0x38) >> 1) | (p & 2), ((c & 0xc0) >> 3) | (p & 4)); } } IPPU.DirectColourMapsNeedRebuild = FALSE; } 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(PPU.BGMode == 5 || PPU.BGMode == 6) IPPU.Interlace = (Memory.FillRAM[0x2133] & 1); if (Settings.SupportHiRes && (PPU.BGMode == 5 || PPU.BGMode == 6 || IPPU.Interlace)) { if (PPU.BGMode == 5 || PPU.BGMode == 6 || IPPU.Interlace) { IPPU.RenderedScreenWidth = 512; IPPU.DoubleWidthPixels = TRUE; } else { IPPU.RenderedScreenWidth = 256; IPPU.DoubleWidthPixels = FALSE; } if (IPPU.Interlace) { IPPU.RenderedScreenHeight = PPU.ScreenHeight << 1; IPPU.DoubleHeightPixels = TRUE; GFX.Pitch2 = GFX.RealPitch; GFX.Pitch = GFX.RealPitch * 2; #ifndef FOREVER_16_BIT if (Settings.SixteenBit) #endif GFX.PPL = GFX.PPLx2 = GFX.RealPitch; #ifndef FOREVER_16_BIT else GFX.PPL = GFX.PPLx2 = GFX.RealPitch << 1; #endif } else { IPPU.RenderedScreenHeight = PPU.ScreenHeight; GFX.Pitch2 = GFX.Pitch = GFX.RealPitch; IPPU.DoubleHeightPixels = FALSE; #ifndef FOREVER_16_BIT if (Settings.SixteenBit) #endif GFX.PPL = GFX.Pitch >> 1; #ifndef FOREVER_16_BIT else GFX.PPL = GFX.Pitch; #endif GFX.PPLx2 = GFX.PPL << 1; } } else { IPPU.RenderedScreenWidth = 256; IPPU.RenderedScreenHeight = PPU.ScreenHeight; IPPU.DoubleWidthPixels = FALSE; IPPU.DoubleHeightPixels = FALSE; { GFX.Pitch2 = GFX.Pitch = GFX.RealPitch; GFX.PPL = GFX.PPLx2 >> 1; GFX.ZPitch = GFX.RealPitch; #ifndef FOREVER_16_BIT if (Settings.SixteenBit) #endif GFX.ZPitch >>= 1; } } PPU.RecomputeClipWindows = TRUE; GFX.DepthDelta = GFX.SubZBuffer - GFX.ZBuffer; 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; LineData[C].BG[1].VOffset = PPU.BG[1].VOffset + 1; LineData[C].BG[1].HOffset = PPU.BG[1].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 { if (Settings.StarfoxHack && PPU.BG[2].VOffset == 0 && PPU.BG[2].HOffset == 0xe000) { LineData[C].BG[2].VOffset = 0xe1; LineData[C].BG[2].HOffset = 0; } else { 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; } else { /* if we're not rendering this frame, we still need to update this */ // XXX: Check ForceBlank? Or anything else? if(IPPU.OBJChanged) S9xSetupOBJ(); PPU.RangeTimeOver |= GFX.OBJLines[C].RTOFlags; } } void S9xEndScreenRefresh () { IPPU.HDMAStarted = FALSE; if (IPPU.RenderThisFrame) { FLUSH_REDRAW (); if (IPPU.ColorsChanged) { uint32 saved = PPU.CGDATA[0]; #ifndef FOREVER_16_BIT 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 | (PPU.FixedColourGreen << 5) | (PPU.FixedColourBlue << 10); } } #endif IPPU.ColorsChanged = FALSE; PPU.CGDATA[0] = saved; } GFX.Pitch = GFX.Pitch2 = GFX.RealPitch; GFX.PPL = GFX.PPLx2 >> 1; #if 0 //take screenshot here. if(Settings.TakeScreenshot) { S9xDoScreenshot(IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight); cprintf("%s:%d\n", __FILE__, __LINE__); } if (Settings.DisplayFrameRate) { S9xDisplayFrameRate (); cprintf("%s:%d\n", __FILE__, __LINE__); } if (GFX.InfoString) { S9xDisplayString (GFX.InfoString); cprintf("%s:%d\n", __FILE__, __LINE__); } #endif S9xDeinitUpdate (IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight, #ifndef FOREVER_16_BIT Settings.SixteenBit #else TRUE #endif ); } S9xApplyCheats (); #ifdef DEBUGGER if (CPU.Flags & FRAME_ADVANCE_FLAG) { if (ICPU.FrameAdvanceCount) { ICPU.FrameAdvanceCount--; IPPU.RenderThisFrame = TRUE; IPPU.FrameSkip = 0; } else { CPU.Flags &= ~FRAME_ADVANCE_FLAG; CPU.Flags |= DEBUG_MODE_FLAG; } } #endif if (CPU.SRAMModified) { S9xAutoSaveSRAM (); CPU.SRAMModified = FALSE; /*if (!CPU.AutoSaveTimer) { if (!(CPU.AutoSaveTimer = Settings.AutoSaveDelay * Memory.ROMFramesPerSecond)) CPU.SRAMModified = FALSE; } else { if (!--CPU.AutoSaveTimer) { S9xAutoSaveSRAM (); CPU.SRAMModified = FALSE; } }*/ } } void S9xSetInfoString (const char *string) { GFX.InfoString = string; GFX.InfoStringTimeout = 120; } inline void SelectTileRenderer (bool8 normal) { if (normal) { DrawTilePtr = DrawTile16; DrawClippedTilePtr = DrawClippedTile16; DrawLargePixelPtr = DrawLargePixel16; } else { if (GFX.r2131 & 0x80) { if (GFX.r2131 & 0x40) { if (GFX.r2130 & 2) { DrawTilePtr = DrawTile16Sub1_2; DrawClippedTilePtr = DrawClippedTile16Sub1_2; } else { // Fixed colour substraction DrawTilePtr = DrawTile16FixedSub1_2; DrawClippedTilePtr = DrawClippedTile16FixedSub1_2; } DrawLargePixelPtr = DrawLargePixel16Sub1_2; } else { DrawTilePtr = DrawTile16Sub; DrawClippedTilePtr = DrawClippedTile16Sub; DrawLargePixelPtr = DrawLargePixel16Sub; } } else { if (GFX.r2131 & 0x40) { if (GFX.r2130 & 2) { DrawTilePtr = DrawTile16Add1_2; DrawClippedTilePtr = DrawClippedTile16Add1_2; } else { // Fixed colour addition DrawTilePtr = DrawTile16FixedAdd1_2; DrawClippedTilePtr = DrawClippedTile16FixedAdd1_2; } DrawLargePixelPtr = DrawLargePixel16Add1_2; } else { DrawTilePtr = DrawTile16Add; DrawClippedTilePtr = DrawClippedTile16Add; DrawLargePixelPtr = DrawLargePixel16Add; } } } } void S9xSetupOBJ () { #ifdef MK_DEBUG_RTO if(Settings.BGLayering) fprintf(stderr, "Entering SetupOBJS()\n"); #endif int SmallWidth, SmallHeight; int LargeWidth, LargeHeight; switch (PPU.OBJSizeSelect) { case 0: SmallWidth = SmallHeight = 8; LargeWidth = LargeHeight = 16; break; case 1: SmallWidth = SmallHeight = 8; LargeWidth = LargeHeight = 32; break; case 2: SmallWidth = SmallHeight = 8; LargeWidth = LargeHeight = 64; break; case 3: SmallWidth = SmallHeight = 16; LargeWidth = LargeHeight = 32; break; case 4: SmallWidth = SmallHeight = 16; LargeWidth = LargeHeight = 64; break; default: case 5: SmallWidth = SmallHeight = 32; LargeWidth = LargeHeight = 64; break; case 6: SmallWidth = 16; SmallHeight = 32; LargeWidth = 32; LargeHeight = 64; break; case 7: SmallWidth = 16; SmallHeight = 32; LargeWidth = LargeHeight = 32; break; } if(IPPU.InterlaceSprites) { SmallHeight>>=1; LargeHeight>>=1; } #ifdef MK_DEBUG_RTO if(Settings.BGLayering) fprintf(stderr, "Sizes are %dx%d and %dx%d\n", SmallWidth, SmallHeight, LargeWidth, LargeHeight); #endif /* OK, we have three cases here. Either there's no priority, priority is * normal FirstSprite, or priority is FirstSprite+Y. The first two are * easy, the last is somewhat more ... interesting. So we split them up. */ int Height; uint8 S; #ifdef MK_DEBUG_RTO if(Settings.BGLayering) fprintf(stderr, "Priority rotation=%d, OAMAddr=%d -> ", PPU.OAMPriorityRotation, PPU.OAMAddr*2 | (PPU.OAMFlip&1)); #endif if(!PPU.OAMPriorityRotation || !(PPU.OAMFlip&PPU.OAMAddr&1)){ #ifdef MK_DEBUG_RTO if(Settings.BGLayering) fprintf(stderr, "normal FirstSprite = %02x\n", PPU.FirstSprite); #endif /* normal case */ uint8 LineOBJ[SNES_HEIGHT_EXTENDED]; memset(LineOBJ, 0, sizeof(LineOBJ)); for(int i=0; i-GFX.OBJWidths[S] && HPos<=256) { if(HPos<0){ GFX.OBJVisibleTiles[S]=(GFX.OBJWidths[S]+HPos+7)>>3; } else if(HPos+GFX.OBJWidths[S]>=257){ GFX.OBJVisibleTiles[S]=(257-HPos+7)>>3; } else { GFX.OBJVisibleTiles[S]=GFX.OBJWidths[S]>>3; } for(uint8 line=0, Y=(uint8)(PPU.OBJ[S].VPos&0xff); line=SNES_HEIGHT_EXTENDED) continue; if(LineOBJ[Y]>=32){ GFX.OBJLines[Y].RTOFlags|=0x40; #ifdef MK_DEBUG_RTO if(Settings.BGLayering) fprintf(stderr, "%d: OBJ %02x ranged over\n", Y, S); #endif continue; } GFX.OBJLines[Y].Tiles-=GFX.OBJVisibleTiles[S]; if(GFX.OBJLines[Y].Tiles<0) GFX.OBJLines[Y].RTOFlags|=0x80; GFX.OBJLines[Y].OBJ[LineOBJ[Y]].Sprite=S; if(PPU.OBJ[S].VFlip){ // Yes, Width not Height. It so happens that the // sprites with H=2*W flip as two WxW sprites. GFX.OBJLines[Y].OBJ[LineOBJ[Y]].Line=line^(GFX.OBJWidths[S]-1); } else { GFX.OBJLines[Y].OBJ[LineOBJ[Y]].Line=line; } LineOBJ[Y]++; } } S=(S+1)&0x7F; } while(S!=FirstSprite); for(int Y=1; Y-GFX.OBJWidths[S] && HPos<=256) { if(HPos<0){ GFX.OBJVisibleTiles[S]=(GFX.OBJWidths[S]+HPos+7)>>3; } else if(HPos+GFX.OBJWidths[S]>=257){ GFX.OBJVisibleTiles[S]=(257-HPos+7)>>3; } else { GFX.OBJVisibleTiles[S]=GFX.OBJWidths[S]>>3; } for(uint8 line=0, Y=(uint8)(PPU.OBJ[S].VPos&0xff); line=SNES_HEIGHT_EXTENDED) continue; if(PPU.OBJ[S].VFlip){ // Yes, Width not Height. It so happens that the // sprites with H=2*W flip as two WxW sprites. OBJOnLine[Y][S]=(line^(GFX.OBJWidths[S]-1)) | 0x80; } else { OBJOnLine[Y][S]=line | 0x80; } } } } /* Now go through and pull out those OBJ that are actually visible. */ int j; for(int Y=0; Y=32){ GFX.OBJLines[Y].RTOFlags|=0x40; #ifdef MK_DEBUG_RTO if(Settings.BGLayering) fprintf(stderr, "%d: OBJ %02x ranged over\n", Y, S); #endif break; } GFX.OBJLines[Y].Tiles-=GFX.OBJVisibleTiles[S]; if(GFX.OBJLines[Y].Tiles<0) GFX.OBJLines[Y].RTOFlags|=0x80; GFX.OBJLines[Y].OBJ[j].Sprite=S; GFX.OBJLines[Y].OBJ[j++].Line=OBJOnLine[Y][S]&~0x80; } S=(S+1)&0x7F; } while(S!=FirstSprite); if(j<32) GFX.OBJLines[Y].OBJ[j].Sprite=-1; } } #ifdef MK_DEBUG_RTO if(Settings.BGLayering) { fprintf(stderr, "Sprites per line:\n"); for(int xxx=0; xxx=0; j++){ fprintf(stderr, " %02x.%d", GFX.OBJLines[xxx].OBJ[j].Sprite, GFX.OBJLines[xxx].OBJ[j].Line); } fprintf(stderr, "\n"); } fprintf(stderr, "Exiting SetupObj()\n"); } #endif IPPU.OBJChanged = FALSE; } static void DrawOBJS (bool8 OnMain = FALSE, uint8 D = 0) { #ifdef MK_DEBUG_RTO if(Settings.BGLayering) fprintf(stderr, "Entering DrawOBJS() for %d-%d\n", GFX.StartY, GFX.EndY); #endif 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; BG.DirectColourMode = FALSE; GFX.PixSize = 1; struct { uint16 Pos; bool8 Value; } Windows[7]; int clipcount = GFX.pCurrentClip->Count [4]; if (!clipcount){ Windows[0].Pos=0; Windows[0].Value=TRUE; Windows[1].Pos=256; Windows[1].Value=FALSE; Windows[2].Pos=1000; Windows[2].Value=FALSE; } else { Windows[0].Pos=1000; Windows[0].Value=FALSE; for(int clip=0, i=1; clipRight[clip][4]<=GFX.pCurrentClip->Left[clip][4]) continue; int j; for(j=0; jLeft[clip][4]; j++); if(jLeft[clip][4]){ Windows[j].Value = TRUE; } else { if(jLeft[clip][4]; Windows[j].Value = TRUE; i++; } for(j=0; jRight[clip][4]; j++); if(j>=i || Windows[j].Pos!=GFX.pCurrentClip->Right[clip][4]){ if(jRight[clip][4]; Windows[j].Value = FALSE; i++; } } } #ifdef MK_DEBUG_RTO if(Settings.BGLayering) { fprintf(stderr, "Windows:\n"); for(int xxx=0; xxx<6; xxx++){ fprintf(stderr, "%d: %d = %d\n", xxx, Windows[xxx].Pos, Windows[xxx].Value); } } #endif if (Settings.SupportHiRes) { if (PPU.BGMode == 5 || PPU.BGMode == 6) { // Bah, OnMain is never used except to determine if calling // SelectTileRenderer is necessary. So let's hack it to false here // to stop SelectTileRenderer from being called when it causes // problems. OnMain = FALSE; GFX.PixSize = 2; if (IPPU.DoubleHeightPixels) { #ifndef FOREVER_16_BIT if (Settings.SixteenBit) { #endif DrawTilePtr = DrawTile16x2x2; DrawClippedTilePtr = DrawClippedTile16x2x2; #ifndef FOREVER_16_BIT } else { DrawTilePtr = DrawTilex2x2; DrawClippedTilePtr = DrawClippedTilex2x2; } #endif } else { #ifndef FOREVER_16_BIT if (Settings.SixteenBit) { #endif DrawTilePtr = DrawTile16x2; DrawClippedTilePtr = DrawClippedTile16x2; #ifndef FOREVER_16_BIT } else { DrawTilePtr = DrawTilex2; DrawClippedTilePtr = DrawClippedTilex2; } #endif } } else { #ifndef FOREVER_16_BIT if (Settings.SixteenBit) { #endif DrawTilePtr = DrawTile16; DrawClippedTilePtr = DrawClippedTile16; #ifndef FOREVER_16_BIT } else { DrawTilePtr = DrawTile; DrawClippedTilePtr = DrawClippedTile; } #endif } } GFX.Z1 = D + 2; for(uint32 Y=GFX.StartY, Offset=Y*GFX.PPL; Y<=GFX.EndY; Y++, Offset+=GFX.PPL){ #ifdef MK_DEBUG_RTO bool8 Flag=0; #endif int I = 0; #ifdef MK_DISABLE_TIME_OVER int tiles=0; #else int tiles=GFX.OBJLines[Y].Tiles; #endif for (int S = GFX.OBJLines[Y].OBJ[I].Sprite; S >= 0 && I<32; S = GFX.OBJLines[Y].OBJ[++I].Sprite) { tiles+=GFX.OBJVisibleTiles[S]; if(tiles<=0){ #ifdef MK_DEBUG_RTO if(Settings.BGLayering) { if(!Flag){ Flag=1; fprintf(stderr, "Line %d:", Y); } fprintf(stderr, " [%02x]", S); } #endif continue; } #ifdef MK_DEBUG_RTO if(Settings.BGLayering) { if(!Flag){ Flag=1; fprintf(stderr, "Line %d:", Y); } fprintf(stderr, " %02x", S); } #endif if (OnMain && SUB_OR_ADD(4)) { SelectTileRenderer (!GFX.Pseudo && PPU.OBJ [S].Palette < 4); } 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); int TileX = PPU.OBJ[S].Name&0x0f; int TileLine = (GFX.OBJLines[Y].OBJ[I].Line&7)*8; int TileInc = 1; if (PPU.OBJ[S].HFlip) { TileX = (TileX + (GFX.OBJWidths[S] >> 3) - 1) & 0x0f; BaseTile |= H_FLIP; TileInc = -1; } GFX.Z2 = (PPU.OBJ[S].Priority + 1) * 4 + D; bool8 WinStat=TRUE; int WinIdx=0, NextPos=-1000; int X=PPU.OBJ[S].HPos; if(X==-256) X=256; for(int t=tiles, O=Offset+X*GFX.PixSize; X<=256 && X=NextPos){ for(; WinIdx<7 && Windows[WinIdx].Pos<=X; WinIdx++); if(WinIdx==0) WinStat=FALSE; else WinStat=Windows[WinIdx-1].Value; NextPos=(WinIdx<7)?Windows[WinIdx].Pos:1000; } if(X+8X+8) NextPos=X+8; } } } } #ifdef MK_DEBUG_RTO if(Settings.BGLayering) if(Flag) fprintf(stderr, "\n"); #endif } #ifdef MK_DEBUG_RTO if(Settings.BGLayering) fprintf(stderr, "Exiting DrawOBJS() for %d-%d\n", GFX.StartY, GFX.EndY); #endif } static void DrawBackgroundMosaic (uint32 BGMode, uint32 bg, uint8 Z1, uint8 Z2) { CHECK_SOUND(); uint32 Tile; uint16 *SC0; uint16 *SC1; uint16 *SC2; uint16 *SC3; uint8 depths [2] = {Z1, Z2}; if (BGMode == 0) BG.StartPalette = bg << 5; else BG.StartPalette = 0; SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1]; if (PPU.BG[bg].SCSize & 1) SC1 = SC0 + 1024; else SC1 = SC0; if(((uint8*)SC1-Memory.VRAM)>=0x10000) SC1-=0x08000; if (PPU.BG[bg].SCSize & 2) SC2 = SC1 + 1024; else SC2 = SC0; if(((uint8*)SC2-Memory.VRAM)>=0x10000) SC2-=0x08000; if (PPU.BG[bg].SCSize & 1) SC3 = SC2 + 1024; else SC3 = SC2; if(((uint8*)SC3-Memory.VRAM)>=0x10000) SC3-=0x08000; uint32 Lines; uint32 OffsetMask; uint32 OffsetShift; if (BG.TileSize == 16) { OffsetMask = 0x3ff; OffsetShift = 4; } else { OffsetMask = 0x1ff; OffsetShift = 3; } int m5 = (BGMode == 5 || BGMode == 6) ? 1 : 0; for (uint32 Y = GFX.StartY; Y <= GFX.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; uint32 MosaicLine = VOffset + Y - MosaicOffset; if (Y + Lines > GFX.EndY) Lines = GFX.EndY + 1 - Y; uint32 VirtAlign = (MosaicLine & 7) << 3; uint16 *b1; uint16 *b2; uint32 ScreenLine = MosaicLine >> OffsetShift; uint32 Rem16 = MosaicLine & 15; if (ScreenLine & 0x20) b1 = SC2, b2 = SC3; else b1 = SC0, b2 = SC1; b1 += (ScreenLine & 0x1f) << 5; b2 += (ScreenLine & 0x1f) << 5; uint16 *t; uint32 Left = 0; uint32 Right = 256 << m5; HOffset <<= m5; uint32 ClipCount = GFX.pCurrentClip->Count [bg]; uint32 HPos = HOffset; uint32 PixWidth = (PPU.Mosaic << m5); if (!ClipCount) ClipCount = 1; for (uint32 clip = 0; clip < ClipCount; clip++) { if (GFX.pCurrentClip->Count [bg]) { Left = GFX.pCurrentClip->Left [clip][bg] << m5; Right = GFX.pCurrentClip->Right [clip][bg] << m5; uint32 r = Left % (PPU.Mosaic << m5); HPos = HOffset + Left; PixWidth = (PPU.Mosaic << m5) - r; } uint32 s = Y * GFX.PPL + Left * GFX.PixSize; for (uint32 x = Left; x < Right; x += PixWidth, s += PixWidth * GFX.PixSize, HPos += PixWidth, PixWidth = (PPU.Mosaic << m5)) { uint32 Quot = (HPos & OffsetMask) >> 3; if (x + PixWidth >= Right) PixWidth = Right - x; if (BG.TileSize == 8 && !m5) { 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); GFX.Z1 = GFX.Z2 = depths [(Tile & 0x2000) >> 13]; // 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 + (Quot & 1) * m5, s, HPos & 7, PixWidth, VirtAlign, Lines); } } } } static void DrawBackgroundOffset (uint32 BGMode, uint32 bg, uint8 Z1, uint8 Z2) { CHECK_SOUND(); uint32 Tile; uint16 *SC0; uint16 *SC1; uint16 *SC2; uint16 *SC3; uint16 *BPS0; uint16 *BPS1; uint16 *BPS2; uint16 *BPS3; uint32 Width; int VOffsetOffset = BGMode == 4 ? 0 : 32; uint8 depths [2] = {Z1, Z2}; BG.StartPalette = 0; 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(((uint8*)SC1-Memory.VRAM)>=0x10000) SC1-=0x08000; if (PPU.BG[bg].SCSize & 2) SC2 = SC1 + 1024; else SC2 = SC0; if(((uint8*)SC2-Memory.VRAM)>=0x10000) SC2-=0x08000; if (PPU.BG[bg].SCSize & 1) SC3 = SC2 + 1024; else SC3 = SC2; if(((uint8*)SC3-Memory.VRAM)>=0x10000) SC3-=0x08000; static const 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 (uint32 Y = GFX.StartY; Y <= GFX.EndY; Y++) { uint32 VOff = LineData [Y].BG[2].VOffset - 1; // 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 & 0x20) s1 = BPS2, s2 = BPS3; else s1 = BPS0, s2 = BPS1; s1 += (ScreenLine & 0x1f) << 5; s2 += (ScreenLine & 0x1f) << 5; if(BGMode != 4) { if((ScreenLine & 0x1f) == 0x1f) { if(ScreenLine & 0x20) VOffsetOffset = BPS0 - BPS2 - 0x1f*32; else VOffsetOffset = BPS2 - BPS0 - 0x1f*32; } else { VOffsetOffset = 32; } } int clipcount = GFX.pCurrentClip->Count [bg]; if (!clipcount) clipcount = 1; for (int clip = 0; clip < clipcount; clip++) { uint32 Left; uint32 Right; if (!GFX.pCurrentClip->Count [bg]) { Left = 0; Right = 256; } else { Left = GFX.pCurrentClip->Left [clip][bg]; Right = GFX.pCurrentClip->Right [clip][bg]; if (Right <= Left) continue; } uint32 VOffset; uint32 HOffset; //added: uint32 LineHOffset=LineData [Y].BG[bg].HOffset; uint32 Offset; uint32 HPos; uint32 Quot; uint32 Count; uint16 *t; uint32 Quot2; uint32 VCellOffset; uint32 HCellOffset; uint16 *b1; uint16 *b2; uint32 TotalCount = 0; uint32 MaxCount = 8; uint32 s = Left * GFX.PixSize + Y * GFX.PPL; bool8 left_hand_edge = (Left == 0); Width = Right - Left; if (Left & 7) MaxCount = 8 - (Left & 7); while (Left < Right) { if (left_hand_edge) { // The SNES offset-per-tile background mode has a // hardware limitation that the offsets cannot be set // for the tile at the left-hand edge of the screen. VOffset = LineData [Y].BG[bg].VOffset; //MKendora; use temp var to reduce memory accesses //HOffset = LineData [Y].BG[bg].HOffset; HOffset = LineHOffset; //End MK left_hand_edge = FALSE; } else { // All subsequent offset tile data is shifted left by one, // hence the - 1 below. Quot2 = ((HOff + Left - 1) & OffsetMask) >> 3; if (Quot2 > 31) s0 = s2 + (Quot2 & 0x1f); else s0 = s1 + Quot2; HCellOffset = READ_2BYTES (s0); if (BGMode == 4) { VOffset = LineData [Y].BG[bg].VOffset; //MKendora another mem access hack //HOffset = LineData [Y].BG[bg].HOffset; HOffset=LineHOffset; //end MK if ((HCellOffset & OffsetEnableMask)) { if (HCellOffset & 0x8000) VOffset = HCellOffset + 1; else HOffset = HCellOffset; } } else { VCellOffset = READ_2BYTES (s0 + VOffsetOffset); if ((VCellOffset & OffsetEnableMask)) VOffset = VCellOffset + 1; else VOffset = LineData [Y].BG[bg].VOffset; //MKendora Strike Gunner fix if ((HCellOffset & OffsetEnableMask)) { //HOffset= HCellOffset; HOffset = (HCellOffset & ~7)|(LineHOffset&7); //HOffset |= LineData [Y].BG[bg].HOffset&7; } else HOffset=LineHOffset; //HOffset = LineData [Y].BG[bg].HOffset - //Settings.StrikeGunnerOffsetHack; //HOffset &= (~7); //end MK } } 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 & 0x20) b1 = SC2, b2 = SC3; else b1 = SC0, b2 = SC1; b1 += (ScreenLine & 0x1f) << 5; b2 += (ScreenLine & 0x1f) << 5; HPos = (HOffset + Left) & OffsetMask; Quot = HPos >> 3; 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); } if (MaxCount + TotalCount > Width) MaxCount = Width - TotalCount; Offset = HPos & 7; //Count =1; Count = 8 - Offset; if (Count > MaxCount) Count = MaxCount; s -= Offset * GFX.PixSize; Tile = READ_2BYTES(t); GFX.Z1 = GFX.Z2 = depths [(Tile & 0x2000) >> 13]; 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); } } Left += Count; TotalCount += Count; s += (Offset + Count) * GFX.PixSize; MaxCount = 8; } } } } static void DrawBackgroundMode5 (uint32 /* BGMODE */, uint32 bg, uint8 Z1, uint8 Z2) { CHECK_SOUND(); if(IPPU.Interlace) { GFX.Pitch = GFX.RealPitch; GFX.PPL = GFX.PPLx2 >> 1; } GFX.PixSize = 1; uint8 depths [2] = {Z1, Z2}; uint32 Tile; uint16 *SC0; uint16 *SC1; uint16 *SC2; uint16 *SC3; uint32 Width; BG.StartPalette = 0; SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1]; if ((PPU.BG[bg].SCSize & 1)) SC1 = SC0 + 1024; else SC1 = SC0; if((SC1-(unsigned short*)Memory.VRAM)>0x10000) SC1=(uint16*)&Memory.VRAM[(((uint8*)SC1)-Memory.VRAM)%0x10000]; if ((PPU.BG[bg].SCSize & 2)) SC2 = SC1 + 1024; else SC2 = SC0; if(((uint8*)SC2-Memory.VRAM)>=0x10000) SC2-=0x08000; if ((PPU.BG[bg].SCSize & 1)) SC3 = SC2 + 1024; else SC3 = SC2; if(((uint8*)SC3-Memory.VRAM)>=0x10000) SC3-=0x08000; int Lines; int VOffsetMask; int VOffsetShift; if (BG.TileSize == 16) { VOffsetMask = 0x3ff; VOffsetShift = 4; } else { VOffsetMask = 0x1ff; VOffsetShift = 3; } int endy = IPPU.Interlace ? 1 + (GFX.EndY << 1) : GFX.EndY; for (int Y = IPPU.Interlace ? GFX.StartY << 1 : GFX.StartY; Y <= endy; Y += Lines) { int y = IPPU.Interlace ? (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 & 0x20) b1 = SC2, b2 = SC3; else b1 = SC0, b2 = SC1; b1 += (ScreenLine & 0x1f) << 5; b2 += (ScreenLine & 0x1f) << 5; int clipcount = GFX.pCurrentClip->Count [bg]; if (!clipcount) clipcount = 1; for (int clip = 0; clip < clipcount; clip++) { int Left; int Right; if (!GFX.pCurrentClip->Count [bg]) { Left = 0; Right = 512; } else { Left = GFX.pCurrentClip->Left [clip][bg] * 2; Right = GFX.pCurrentClip->Right [clip][bg] * 2; if (Right <= Left) continue; } uint32 s = Left * GFX.PixSize + Y * GFX.PPL; uint32 HPos = (HOffset + Left * GFX.PixSize) & 0x3ff; uint32 Quot = HPos >> 3; uint32 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); GFX.Z1 = GFX.Z2 = depths [(Tile & 0x2000) >> 13]; 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); } } 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); GFX.Z1 = GFX.Z2 = depths [(Tile & 0x2000) >> 13]; 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); } } 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); GFX.Z1 = GFX.Z2 = depths [(Tile & 0x2000) >> 13]; 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); } } } } } GFX.Pitch = IPPU.DoubleHeightPixels ? GFX.RealPitch * 2 : GFX.RealPitch; GFX.PPL = IPPU.DoubleHeightPixels ? GFX.PPLx2 : (GFX.PPLx2 >> 1); } static void DrawBackground (uint32 BGMode, uint32 bg, uint8 Z1, uint8 Z2) { GFX.PixSize = 1; 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]; BG.DirectColourMode = (BGMode == 3 || BGMode == 4) && bg == 0 && (GFX.r2130 & 1); if (PPU.BGMosaic [bg] && PPU.Mosaic > 1) { DrawBackgroundMosaic (BGMode, bg, Z1, Z2); return; } switch (BGMode) { case 2: case 4: // Used by Puzzle Bobble DrawBackgroundOffset (BGMode, bg, Z1, Z2); return; case 5: case 6: // XXX: is also offset per tile. if (Settings.SupportHiRes) { DrawBackgroundMode5 (BGMode, bg, Z1, Z2); return; } break; } CHECK_SOUND(); uint32 Tile; uint16 *SC0; uint16 *SC1; uint16 *SC2; uint16 *SC3; uint32 Width; uint8 depths [2] = {Z1, Z2}; if (BGMode == 0) BG.StartPalette = bg << 5; else BG.StartPalette = 0; SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1]; if (PPU.BG[bg].SCSize & 1) SC1 = SC0 + 1024; else SC1 = SC0; if(SC1>=(unsigned short*)(Memory.VRAM+0x10000)) SC1=(uint16*)&Memory.VRAM[((uint8*)SC1-&Memory.VRAM[0])%0x10000]; if (PPU.BG[bg].SCSize & 2) SC2 = SC1 + 1024; else SC2 = SC0; if(((uint8*)SC2-Memory.VRAM)>=0x10000) SC2-=0x08000; if (PPU.BG[bg].SCSize & 1) SC3 = SC2 + 1024; else SC3 = SC2; if(((uint8*)SC3-Memory.VRAM)>=0x10000) SC3-=0x08000; int Lines; int OffsetMask; int OffsetShift; if (BG.TileSize == 16) { OffsetMask = 0x3ff; OffsetShift = 4; } else { OffsetMask = 0x1ff; OffsetShift = 3; } for (uint32 Y = GFX.StartY; Y <= GFX.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 > GFX.EndY) Lines = GFX.EndY + 1 - Y; VirtAlign <<= 3; uint32 ScreenLine = (VOffset + Y) >> OffsetShift; uint32 t1; uint32 t2; if (((VOffset + Y) & 15) > 7) { t1 = 16; t2 = 0; } else { t1 = 0; t2 = 16; } uint16 *b1; uint16 *b2; if (ScreenLine & 0x20) b1 = SC2, b2 = SC3; else b1 = SC0, b2 = SC1; b1 += (ScreenLine & 0x1f) << 5; b2 += (ScreenLine & 0x1f) << 5; int clipcount = GFX.pCurrentClip->Count [bg]; if (!clipcount) clipcount = 1; for (int clip = 0; clip < clipcount; clip++) { uint32 Left; uint32 Right; if (!GFX.pCurrentClip->Count [bg]) { Left = 0; Right = 256; } else { Left = GFX.pCurrentClip->Left [clip][bg]; Right = GFX.pCurrentClip->Right [clip][bg]; if (Right <= Left) continue; } uint32 s = Left * GFX.PixSize + Y * GFX.PPL; uint32 HPos = (HOffset + Left) & OffsetMask; uint32 Quot = HPos >> 3; uint32 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) { uint32 Offset = (HPos & 7); Count = 8 - Offset; if (Count > Width) Count = Width; s -= Offset * GFX.PixSize; Tile = READ_2BYTES(t); GFX.Z1 = GFX.Z2 = depths [(Tile & 0x2000) >> 13]; 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); } } 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 * GFX.PixSize; } // Middle, unclipped tiles Count = Width - Count; int Middle = Count >> 3; Count &= 7; for (int C = Middle; C > 0; s += 8 * GFX.PixSize, Quot++, C--) { Tile = READ_2BYTES(t); GFX.Z1 = GFX.Z2 = depths [(Tile & 0x2000) >> 13]; 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); } 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); GFX.Z1 = GFX.Z2 = depths [(Tile & 0x2000) >> 13]; 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); } } } } } } #define RENDER_BACKGROUND_MODE7(TYPE,FUNC) \ uint16 *ScreenColors; \ CHECK_SOUND(); \ \ uint8 *VRAM1 = Memory.VRAM + 1; \ if (GFX.r2130 & 1) \ { \ if (IPPU.DirectColourMapsNeedRebuild) \ S9xBuildDirectColourMaps (); \ ScreenColors = DirectColourMaps [0]; \ } \ else \ ScreenColors = IPPU.ScreenColors; \ \ int aa, cc; \ int dir; \ int startx, endx; \ uint32 Left = 0; \ uint32 Right = 256; \ uint32 ClipCount = GFX.pCurrentClip->Count [bg]; \ \ if (!ClipCount) \ ClipCount = 1; \ \ Screen += GFX.StartY * GFX.Pitch; \ uint8 *Depth = GFX.DB + GFX.StartY * GFX.PPL; \ struct SLineMatrixData *l = &LineMatrixData [GFX.StartY]; \ \ for (uint32 Line = GFX.StartY; Line <= GFX.EndY; Line++, Screen += GFX.Pitch, Depth += GFX.PPL, l++) \ { \ int yy; \ \ int32 HOffset = ((int32) LineData [Line].BG[0].HOffset << M7) >> M7; \ int32 VOffset = ((int32) LineData [Line].BG[0].VOffset << M7) >> M7; \ \ int32 CentreX = ((int32) l->CentreX << M7) >> M7; \ int32 CentreY = ((int32) l->CentreY << M7) >> M7; \ \ if (PPU.Mode7VFlip) \ yy = 255 - (int) Line; \ else \ yy = Line; \ \ yy += CLIP_10_BIT_SIGNED(VOffset - CentreY); \ \ int BB = l->MatrixB * yy + (CentreX << 8); \ int DD = l->MatrixD * yy + (CentreY << 8); \ \ for (uint32 clip = 0; clip < ClipCount; clip++) \ { \ if (GFX.pCurrentClip->Count [bg]) \ { \ Left = GFX.pCurrentClip->Left [clip][bg]; \ Right = GFX.pCurrentClip->Right [clip][bg]; \ if (Right <= Left) \ continue; \ } \ TYPE *p = (TYPE *) Screen + Left; \ uint8 *d = Depth + 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 + CLIP_10_BIT_SIGNED(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++, d++) \ { \ 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 = *(TileData + ((Y & 7) << 4) + ((X & 7) << 1)); \ GFX.Z1 = Mode7Depths [(b & GFX.Mode7PriorityMask) >> 7]; \ if (GFX.Z1 > *d && (b & GFX.Mode7Mask) ) \ { \ *p = (FUNC); \ *d = GFX.Z1; \ } \ } \ } \ else \ { \ for (int x = startx; x != endx; x += dir, AA += aa, CC += cc, p++, d++) \ { \ 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 = *(TileData + ((Y & 7) << 4) + ((X & 7) << 1)); \ GFX.Z1 = Mode7Depths [(b & GFX.Mode7PriorityMask) >> 7]; \ if (GFX.Z1 > *d && (b & GFX.Mode7Mask) ) \ { \ *p = (FUNC); \ *d = GFX.Z1; \ } \ } \ else \ { \ if (PPU.Mode7Repeat == 3) \ { \ X = (x + HOffset) & 7; \ Y = (yy + CentreY) & 7; \ uint32 b = *(VRAM1 + ((Y & 7) << 4) + ((X & 7) << 1)); \ GFX.Z1 = Mode7Depths [(b & GFX.Mode7PriorityMask) >> 7]; \ if (GFX.Z1 > *d && (b & GFX.Mode7Mask) ) \ { \ *p = (FUNC); \ *d = GFX.Z1; \ } \ } \ } \ } \ } \ } \ } static void DrawBGMode7Background (uint8 *Screen, int bg) { RENDER_BACKGROUND_MODE7 (uint8, (uint8) (b & GFX.Mode7Mask)) } static void DrawBGMode7Background16 (uint8 *Screen, int bg) { RENDER_BACKGROUND_MODE7 (uint16, ScreenColors [b & GFX.Mode7Mask]); } static void DrawBGMode7Background16Add (uint8 *Screen, int bg) { RENDER_BACKGROUND_MODE7 (uint16, *(d + GFX.DepthDelta) ? (*(d + GFX.DepthDelta) != 1 ? COLOR_ADD (ScreenColors [b & GFX.Mode7Mask], p [GFX.Delta]) : COLOR_ADD (ScreenColors [b & GFX.Mode7Mask], GFX.FixedColour)) : ScreenColors [b & GFX.Mode7Mask]); } static void DrawBGMode7Background16Add1_2 (uint8 *Screen, int bg) { RENDER_BACKGROUND_MODE7 (uint16, *(d + GFX.DepthDelta) ? (*(d + GFX.DepthDelta) != 1 ? COLOR_ADD1_2 (ScreenColors [b & GFX.Mode7Mask], p [GFX.Delta]) : COLOR_ADD (ScreenColors [b & GFX.Mode7Mask], GFX.FixedColour)) : ScreenColors [b & GFX.Mode7Mask]); } static void DrawBGMode7Background16Sub (uint8 *Screen, int bg) { RENDER_BACKGROUND_MODE7 (uint16, *(d + GFX.DepthDelta) ? (*(d + GFX.DepthDelta) != 1 ? COLOR_SUB (ScreenColors [b & GFX.Mode7Mask], p [GFX.Delta]) : COLOR_SUB (ScreenColors [b & GFX.Mode7Mask], GFX.FixedColour)) : ScreenColors [b & GFX.Mode7Mask]); } static void DrawBGMode7Background16Sub1_2 (uint8 *Screen, int bg) { RENDER_BACKGROUND_MODE7 (uint16, *(d + GFX.DepthDelta) ? (*(d + GFX.DepthDelta) != 1 ? COLOR_SUB1_2 (ScreenColors [b & GFX.Mode7Mask], p [GFX.Delta]) : COLOR_SUB (ScreenColors [b & GFX.Mode7Mask], GFX.FixedColour)) : ScreenColors [b & GFX.Mode7Mask]); } #define RENDER_BACKGROUND_MODE7_i(TYPE,FUNC,COLORFUNC) \ uint16 *ScreenColors; \ CHECK_SOUND(); \ \ uint8 *VRAM1 = Memory.VRAM + 1; \ if (GFX.r2130 & 1) \ { \ if (IPPU.DirectColourMapsNeedRebuild) \ S9xBuildDirectColourMaps (); \ ScreenColors = DirectColourMaps [0]; \ } \ else \ ScreenColors = IPPU.ScreenColors; \ \ int aa, cc; \ int dir; \ int startx, endx; \ uint32 Left = 0; \ uint32 Right = 256; \ uint32 ClipCount = GFX.pCurrentClip->Count [bg]; \ \ if (!ClipCount) \ ClipCount = 1; \ \ Screen += GFX.StartY * GFX.Pitch; \ uint8 *Depth = GFX.DB + GFX.StartY * GFX.PPL; \ struct SLineMatrixData *l = &LineMatrixData [GFX.StartY]; \ bool8 allowSimpleCase = FALSE; \ if (!l->MatrixB && !l->MatrixC && (l->MatrixA == 0x0100) && (l->MatrixD == 0x0100) \ && !LineMatrixData[GFX.EndY].MatrixB && !LineMatrixData[GFX.EndY].MatrixC \ && (LineMatrixData[GFX.EndY].MatrixA == 0x0100) && (LineMatrixData[GFX.EndY].MatrixD == 0x0100) \ ) \ allowSimpleCase = TRUE; \ \ for (uint32 Line = GFX.StartY; Line <= GFX.EndY; Line++, Screen += GFX.Pitch, Depth += GFX.PPL, l++) \ { \ int yy; \ \ int HOffset = ((int) LineData [Line].BG[0].HOffset << M7) >> M7; \ int VOffset = ((int) LineData [Line].BG[0].VOffset << M7) >> M7; \ \ int CentreX = ((int) l->CentreX << M7) >> M7; \ int CentreY = ((int) l->CentreY << M7) >> M7; \ \ if (PPU.Mode7VFlip) \ yy = 255 - (int) Line; \ else \ yy = Line; \ \ \ yy += CLIP_10_BIT_SIGNED(VOffset - CentreY); \ bool8 simpleCase = FALSE; \ int BB; \ int DD; \ /* Make a special case for the identity matrix, since it's a common case and */ \ /* can be done much more quickly without special effects */ \ if (allowSimpleCase && !l->MatrixB && !l->MatrixC && (l->MatrixA == 0x0100) && (l->MatrixD == 0x0100)) \ { \ BB = CentreX << 8; \ DD = (yy + CentreY) << 8; \ simpleCase = TRUE; \ } \ else \ { \ BB = l->MatrixB * yy + (CentreX << 8); \ DD = l->MatrixD * yy + (CentreY << 8); \ } \ \ for (uint32 clip = 0; clip < ClipCount; clip++) \ { \ if (GFX.pCurrentClip->Count [bg]) \ { \ Left = GFX.pCurrentClip->Left [clip][bg]; \ Right = GFX.pCurrentClip->Right [clip][bg]; \ if (Right <= Left) \ continue; \ } \ TYPE *p = (TYPE *) Screen + Left; \ uint8 *d = Depth + 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; \ \ xx = startx + CLIP_10_BIT_SIGNED(HOffset - CentreX); \ int AA, CC = 0; \ if (simpleCase) \ { \ AA = xx << 8; \ } \ else \ { \ AA = l->MatrixA * xx; \ CC = l->MatrixC * xx; \ } \ if (simpleCase) \ { \ if (!PPU.Mode7Repeat) \ { \ int x = startx; \ do \ { \ int X = ((AA + BB) >> 8) & 0x3ff; \ int Y = (DD >> 8) & 0x3ff; \ uint8 *TileData = VRAM1 + (Memory.VRAM[((Y & ~7) << 5) + ((X >> 2) & ~1)] << 7); \ uint32 b = *(TileData + ((Y & 7) << 4) + ((X & 7) << 1)); \ GFX.Z1 = Mode7Depths [(b & GFX.Mode7PriorityMask) >> 7]; \ if (GFX.Z1 > *d && (b & GFX.Mode7Mask) ) \ { \ TYPE theColor = COLORFUNC; \ *p = (FUNC) | ALPHA_BITS_MASK; \ *d = GFX.Z1; \ } \ AA += aa, p++, d++; \ x += dir; \ } while (x != endx); \ } \ else \ { \ int x = startx; \ do { \ int X = (AA + BB) >> 8; \ int Y = DD >> 8; \ \ if (((X | Y) & ~0x3ff) == 0) \ { \ uint8 *TileData = VRAM1 + (Memory.VRAM[((Y & ~7) << 5) + ((X >> 2) & ~1)] << 7); \ uint32 b = *(TileData + ((Y & 7) << 4) + ((X & 7) << 1)); \ GFX.Z1 = Mode7Depths [(b & GFX.Mode7PriorityMask) >> 7]; \ if (GFX.Z1 > *d && (b & GFX.Mode7Mask) ) \ { \ TYPE theColor = COLORFUNC; \ *p = (FUNC) | ALPHA_BITS_MASK; \ *d = GFX.Z1; \ } \ } \ else if (PPU.Mode7Repeat == 3) \ { \ X = (x + HOffset) & 7; \ Y = (yy + CentreY) & 7; \ uint8 *TileData = VRAM1 + (Memory.VRAM[((Y & ~7) << 5) + ((X >> 2) & ~1)] << 7); \ uint32 b = *(TileData + ((Y & 7) << 4) + ((X & 7) << 1)); \ GFX.Z1 = Mode7Depths [(b & GFX.Mode7PriorityMask) >> 7]; \ if (GFX.Z1 > *d && (b & GFX.Mode7Mask) ) \ { \ TYPE theColor = COLORFUNC; \ *p = (FUNC) | ALPHA_BITS_MASK; \ *d = GFX.Z1; \ } \ } \ AA += aa; p++; d++; \ x += dir; \ } while (x != endx); \ } \ } \ else if (!PPU.Mode7Repeat) \ { \ /* The bilinear interpolator: get the colors at the four points surrounding */ \ /* the location of one point in the _sampled_ image, and weight them according */ \ /* to their (city block) distance. It's very smooth, but blurry with "close up" */ \ /* points. */ \ \ /* 460 (slightly less than 2 source pixels per displayed pixel) is an educated */ \ /* guess for where bilinear filtering will become a poor method for averaging. */ \ /* (When reducing the image, the weighting used by a bilinear filter becomes */ \ /* arbitrary, and a simple mean is a better way to represent the source image.) */ \ /* You can think of this as a kind of mipmapping. */ \ if ((aa < 460 && aa > -460) && (cc < 460 && cc > -460)) \ {\ for (int x = startx; x != endx; x += dir, AA += aa, CC += cc, p++, d++) \ { \ uint32 xPos = AA + BB; \ uint32 xPix = xPos >> 8; \ uint32 yPos = CC + DD; \ uint32 yPix = yPos >> 8; \ uint32 X = xPix & 0x3ff; \ uint32 Y = yPix & 0x3ff; \ uint8 *TileData = VRAM1 + (Memory.VRAM[((Y & ~7) << 5) + ((X >> 2) & ~1)] << 7); \ uint32 b = *(TileData + ((Y & 7) << 4) + ((X & 7) << 1)); \ GFX.Z1 = Mode7Depths [(b & GFX.Mode7PriorityMask) >> 7]; \ if (GFX.Z1 > *d && (b & GFX.Mode7Mask) ) \ { \ /* X10 and Y01 are the X and Y coordinates of the next source point over. */ \ uint32 X10 = (xPix + dir) & 0x3ff; \ uint32 Y01 = (yPix + (PPU.Mode7VFlip?-1:1)) & 0x3ff; \ uint8 *TileData10 = VRAM1 + (Memory.VRAM[((Y & ~7) << 5) + ((X10 >> 2) & ~1)] << 7); \ uint8 *TileData11 = VRAM1 + (Memory.VRAM[((Y01 & ~7) << 5) + ((X10 >> 2) & ~1)] << 7); \ uint8 *TileData01 = VRAM1 + (Memory.VRAM[((Y01 & ~7) << 5) + ((X >> 2) & ~1)] << 7); \ uint32 p1 = COLORFUNC; \ p1 = (p1 & FIRST_THIRD_COLOR_MASK) | ((p1 & SECOND_COLOR_MASK) << 16); \ b = *(TileData10 + ((Y & 7) << 4) + ((X10 & 7) << 1)); \ uint32 p2 = COLORFUNC; \ p2 = (p2 & FIRST_THIRD_COLOR_MASK) | ((p2 & SECOND_COLOR_MASK) << 16); \ b = *(TileData11 + ((Y01 & 7) << 4) + ((X10 & 7) << 1)); \ uint32 p4 = COLORFUNC; \ p4 = (p4 & FIRST_THIRD_COLOR_MASK) | ((p4 & SECOND_COLOR_MASK) << 16); \ b = *(TileData01 + ((Y01 & 7) << 4) + ((X & 7) << 1)); \ uint32 p3 = COLORFUNC; \ p3 = (p3 & FIRST_THIRD_COLOR_MASK) | ((p3 & SECOND_COLOR_MASK) << 16); \ /* Xdel, Ydel: position (in 1/32nds) between the points */ \ uint32 Xdel = (xPos >> 3) & 0x1F; \ uint32 Ydel = (yPos >> 3) & 0x1F; \ uint32 XY = (Xdel*Ydel) >> 5; \ uint32 area1 = 0x20 + XY - Xdel - Ydel; \ uint32 area2 = Xdel - XY; \ uint32 area3 = Ydel - XY; \ uint32 area4 = XY; \ if(PPU.Mode7HFlip){ \ uint32 tmp=area1; area1=area2; area2=tmp; \ tmp=area3; area3=area4; area4=tmp; \ } \ if(PPU.Mode7VFlip){ \ uint32 tmp=area1; area1=area3; area3=tmp; \ tmp=area2; area2=area4; area4=tmp; \ } \ uint32 tempColor = ((area1 * p1) + \ (area2 * p2) + \ (area3 * p3) + \ (area4 * p4)) >> 5; \ TYPE theColor = (tempColor & FIRST_THIRD_COLOR_MASK) | ((tempColor >> 16) & SECOND_COLOR_MASK); \ *p = (FUNC) | ALPHA_BITS_MASK; \ *d = GFX.Z1; \ } \ } \ } \ else \ /* The oversampling method: get the colors at four corners of a square */ \ /* in the _displayed_ image, and average them. It's sharp and clean, but */ \ /* gives the usual huge pixels when the source image gets "close." */ \ { \ /* Find the dimensions of the square in the source image whose corners will be examined. */ \ uint32 aaDelX = aa >> 1; \ uint32 ccDelX = cc >> 1; \ uint32 bbDelY = l->MatrixB >> 1; \ uint32 ddDelY = l->MatrixD >> 1; \ /* Offset the location within the source image so that the four sampled points */ \ /* center around where the single point would otherwise have been drawn. */ \ BB -= (bbDelY >> 1); \ DD -= (ddDelY >> 1); \ AA -= (aaDelX >> 1); \ CC -= (ccDelX >> 1); \ uint32 BB10 = BB + aaDelX; \ uint32 BB01 = BB + bbDelY; \ uint32 BB11 = BB + aaDelX + bbDelY; \ uint32 DD10 = DD + ccDelX; \ uint32 DD01 = DD + ddDelY; \ uint32 DD11 = DD + ccDelX + ddDelY; \ for (int x = startx; x != endx; x += dir, AA += aa, CC += cc, p++, d++) \ { \ uint32 X = ((AA + BB) >> 8) & 0x3ff; \ uint32 Y = ((CC + DD) >> 8) & 0x3ff; \ uint8 *TileData = VRAM1 + (Memory.VRAM[((Y & ~7) << 5) + ((X >> 2) & ~1)] << 7); \ uint32 b = *(TileData + ((Y & 7) << 4) + ((X & 7) << 1)); \ GFX.Z1 = Mode7Depths [(b & GFX.Mode7PriorityMask) >> 7]; \ if (GFX.Z1 > *d && (b & GFX.Mode7Mask) ) \ { \ /* X, Y, X10, Y10, etc. are the coordinates of the four pixels within the */ \ /* source image that we're going to examine. */ \ uint32 X10 = ((AA + BB10) >> 8) & 0x3ff; \ uint32 Y10 = ((CC + DD10) >> 8) & 0x3ff; \ uint32 X01 = ((AA + BB01) >> 8) & 0x3ff; \ uint32 Y01 = ((CC + DD01) >> 8) & 0x3ff; \ uint32 X11 = ((AA + BB11) >> 8) & 0x3ff; \ uint32 Y11 = ((CC + DD11) >> 8) & 0x3ff; \ uint8 *TileData10 = VRAM1 + (Memory.VRAM[((Y10 & ~7) << 5) + ((X10 >> 2) & ~1)] << 7); \ uint8 *TileData01 = VRAM1 + (Memory.VRAM[((Y01 & ~7) << 5) + ((X01 >> 2) & ~1)] << 7); \ uint8 *TileData11 = VRAM1 + (Memory.VRAM[((Y11 & ~7) << 5) + ((X11 >> 2) & ~1)] << 7); \ TYPE p1 = COLORFUNC; \ b = *(TileData10 + ((Y10 & 7) << 4) + ((X10 & 7) << 1)); \ TYPE p2 = COLORFUNC; \ b = *(TileData01 + ((Y01 & 7) << 4) + ((X01 & 7) << 1)); \ TYPE p3 = COLORFUNC; \ b = *(TileData11 + ((Y11 & 7) << 4) + ((X11 & 7) << 1)); \ TYPE p4 = COLORFUNC; \ TYPE theColor = Q_INTERPOLATE(p1, p2, p3, p4); \ *p = (FUNC) | ALPHA_BITS_MASK; \ *d = GFX.Z1; \ } \ } \ } \ } \ else \ { \ for (int x = startx; x != endx; x += dir, AA += aa, CC += cc, p++, d++) \ { \ uint32 xPos = AA + BB; \ uint32 xPix = xPos >> 8; \ uint32 yPos = CC + DD; \ uint32 yPix = yPos >> 8; \ uint32 X = xPix; \ uint32 Y = yPix; \ \ \ if (((X | Y) & ~0x3ff) == 0) \ { \ uint8 *TileData = VRAM1 + (Memory.VRAM[((Y & ~7) << 5) + ((X >> 2) & ~1)] << 7); \ uint32 b = *(TileData + ((Y & 7) << 4) + ((X & 7) << 1)); \ GFX.Z1 = Mode7Depths [(b & GFX.Mode7PriorityMask) >> 7]; \ if (GFX.Z1 > *d && (b & GFX.Mode7Mask) ) \ { \ /* X10 and Y01 are the X and Y coordinates of the next source point over. */ \ uint32 X10 = (xPix + dir) & 0x3ff; \ uint32 Y01 = (yPix + dir) & 0x3ff; \ uint8 *TileData10 = VRAM1 + (Memory.VRAM[((Y & ~7) << 5) + ((X10 >> 2) & ~1)] << 7); \ uint8 *TileData11 = VRAM1 + (Memory.VRAM[((Y01 & ~7) << 5) + ((X10 >> 2) & ~1)] << 7); \ uint8 *TileData01 = VRAM1 + (Memory.VRAM[((Y01 & ~7) << 5) + ((X >> 2) & ~1)] << 7); \ uint32 p1 = COLORFUNC; \ p1 = (p1 & FIRST_THIRD_COLOR_MASK) | ((p1 & SECOND_COLOR_MASK) << 16); \ b = *(TileData10 + ((Y & 7) << 4) + ((X10 & 7) << 1)); \ uint32 p2 = COLORFUNC; \ p2 = (p2 & FIRST_THIRD_COLOR_MASK) | ((p2 & SECOND_COLOR_MASK) << 16); \ b = *(TileData11 + ((Y01 & 7) << 4) + ((X10 & 7) << 1)); \ uint32 p4 = COLORFUNC; \ p4 = (p4 & FIRST_THIRD_COLOR_MASK) | ((p4 & SECOND_COLOR_MASK) << 16); \ b = *(TileData01 + ((Y01 & 7) << 4) + ((X & 7) << 1)); \ uint32 p3 = COLORFUNC; \ p3 = (p3 & FIRST_THIRD_COLOR_MASK) | ((p3 & SECOND_COLOR_MASK) << 16); \ /* Xdel, Ydel: position (in 1/32nds) between the points */ \ uint32 Xdel = (xPos >> 3) & 0x1F; \ uint32 Ydel = (yPos >> 3) & 0x1F; \ uint32 XY = (Xdel*Ydel) >> 5; \ uint32 area1 = 0x20 + XY - Xdel - Ydel; \ uint32 area2 = Xdel - XY; \ uint32 area3 = Ydel - XY; \ uint32 area4 = XY; \ uint32 tempColor = ((area1 * p1) + \ (area2 * p2) + \ (area3 * p3) + \ (area4 * p4)) >> 5; \ TYPE theColor = (tempColor & FIRST_THIRD_COLOR_MASK) | ((tempColor >> 16) & SECOND_COLOR_MASK); \ *p = (FUNC) | ALPHA_BITS_MASK; \ *d = GFX.Z1; \ } \ } \ else \ { \ if (PPU.Mode7Repeat == 3) \ { \ X = (x + HOffset) & 7; \ Y = (yy + CentreY) & 7; \ uint32 b = *(VRAM1 + ((Y & 7) << 4) + ((X & 7) << 1)); \ GFX.Z1 = Mode7Depths [(b & GFX.Mode7PriorityMask) >> 7]; \ if (GFX.Z1 > *d && (b & GFX.Mode7Mask) ) \ { \ TYPE theColor = COLORFUNC; \ *p = (FUNC) | ALPHA_BITS_MASK; \ *d = GFX.Z1; \ } \ } \ } \ } \ } \ } \ } STATIC uint32 Q_INTERPOLATE(uint32 A, uint32 B, uint32 C, uint32 D) { register uint32 x = ((A >> 2) & HIGH_BITS_SHIFTED_TWO_MASK) + ((B >> 2) & HIGH_BITS_SHIFTED_TWO_MASK) + ((C >> 2) & HIGH_BITS_SHIFTED_TWO_MASK) + ((D >> 2) & HIGH_BITS_SHIFTED_TWO_MASK); register uint32 y = (A & TWO_LOW_BITS_MASK) + (B & TWO_LOW_BITS_MASK) + (C & TWO_LOW_BITS_MASK) + (D & TWO_LOW_BITS_MASK); y = (y>>2) & TWO_LOW_BITS_MASK; return x+y; } static void DrawBGMode7Background16_i (uint8 *Screen, int bg) { RENDER_BACKGROUND_MODE7_i (uint16, theColor, (ScreenColors[b & GFX.Mode7Mask])); } static void DrawBGMode7Background16Add_i (uint8 *Screen, int bg) { RENDER_BACKGROUND_MODE7_i (uint16, *(d + GFX.DepthDelta) ? (*(d + GFX.DepthDelta) != 1 ? (COLOR_ADD (theColor, p [GFX.Delta])) : (COLOR_ADD (theColor, GFX.FixedColour))) : theColor, (ScreenColors[b & GFX.Mode7Mask])); } static void DrawBGMode7Background16Add1_2_i (uint8 *Screen, int bg) { RENDER_BACKGROUND_MODE7_i (uint16, *(d + GFX.DepthDelta) ? (*(d + GFX.DepthDelta) != 1 ? COLOR_ADD1_2 (theColor, p [GFX.Delta]) : COLOR_ADD (theColor, GFX.FixedColour)) : theColor, (ScreenColors[b & GFX.Mode7Mask])); } static void DrawBGMode7Background16Sub_i (uint8 *Screen, int bg) { RENDER_BACKGROUND_MODE7_i (uint16, *(d + GFX.DepthDelta) ? (*(d + GFX.DepthDelta) != 1 ? COLOR_SUB (theColor, p [GFX.Delta]) : COLOR_SUB (theColor, GFX.FixedColour)) : theColor, (ScreenColors[b & GFX.Mode7Mask])); } static void DrawBGMode7Background16Sub1_2_i (uint8 *Screen, int bg) { RENDER_BACKGROUND_MODE7_i (uint16, *(d + GFX.DepthDelta) ? (*(d + GFX.DepthDelta) != 1 ? COLOR_SUB1_2 (theColor, p [GFX.Delta]) : COLOR_SUB (theColor, GFX.FixedColour)) : theColor, (ScreenColors[b & GFX.Mode7Mask])); } #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; \ FIRST_COLOR_MASK = FIRST_COLOR_MASK_##F; \ SECOND_COLOR_MASK = SECOND_COLOR_MASK_##F; \ THIRD_COLOR_MASK = THIRD_COLOR_MASK_##F; \ ALPHA_BITS_MASK = ALPHA_BITS_MASK_##F; \ FIRST_THIRD_COLOR_MASK = FIRST_COLOR_MASK | THIRD_COLOR_MASK; \ TWO_LOW_BITS_MASK = RGB_LOW_BITS_MASK | (RGB_LOW_BITS_MASK << 1); \ HIGH_BITS_SHIFTED_TWO_MASK = (( (FIRST_COLOR_MASK | SECOND_COLOR_MASK | THIRD_COLOR_MASK) & \ ~TWO_LOW_BITS_MASK ) >> 2); static void RenderScreen (uint8 *Screen, bool8 sub, bool8 force_no_add, uint8 D) { bool8 BG0; bool8 BG1; bool8 BG2; bool8 BG3; bool8 OB; GFX.S = Screen; if (!sub) { GFX.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 { GFX.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; switch (PPU.BGMode) { case 0: case 1: if (OB) { SelectTileRenderer (sub || !SUB_OR_ADD(4)); DrawOBJS (!sub, D); } if (BG0) { SelectTileRenderer (sub || !SUB_OR_ADD(0)); DrawBackground (PPU.BGMode, 0, D + 10, D + 14); } if (BG1) { SelectTileRenderer (sub || !SUB_OR_ADD(1)); DrawBackground (PPU.BGMode, 1, D + 9, D + 13); } if (BG2) { SelectTileRenderer (sub || !SUB_OR_ADD(2)); DrawBackground (PPU.BGMode, 2, D + 3, PPU.BG3Priority ? D + 17 : D + 6); } if (BG3 && PPU.BGMode == 0) { SelectTileRenderer (sub || !SUB_OR_ADD(3)); DrawBackground (PPU.BGMode, 3, D + 2, D + 5); } break; case 2: case 3: case 4: case 5: case 6: if (OB) { SelectTileRenderer (sub || !SUB_OR_ADD(4)); DrawOBJS (!sub, D); } if (BG0) { SelectTileRenderer (sub || !SUB_OR_ADD(0)); DrawBackground (PPU.BGMode, 0, D + 5, D + 13); } if (BG1 && PPU.BGMode != 6) { SelectTileRenderer (sub || !SUB_OR_ADD(1)); DrawBackground (PPU.BGMode, 1, D + 2, D + 9); } break; case 7: if (OB) { SelectTileRenderer (sub || !SUB_OR_ADD(4)); DrawOBJS (!sub, D); } if (BG0 || ((Memory.FillRAM [0x2133] & 0x40) && BG1)) { int bg; if ((Memory.FillRAM [0x2133] & 0x40)&&BG1) { GFX.Mode7Mask = 0x7f; GFX.Mode7PriorityMask = 0x80; Mode7Depths [0] = (BG0?5:1) + D; Mode7Depths [1] = 9 + D; bg = 1; } else { GFX.Mode7Mask = 0xff; GFX.Mode7PriorityMask = 0; Mode7Depths [0] = 5 + D; Mode7Depths [1] = 5 + D; bg = 0; } if (sub || !SUB_OR_ADD(0)) { if (!Settings.Mode7Interpolate) DrawBGMode7Background16 (Screen, bg); else DrawBGMode7Background16_i (Screen, bg); } else { if (GFX.r2131 & 0x80) { if (GFX.r2131 & 0x40) { if (!Settings.Mode7Interpolate) DrawBGMode7Background16Sub1_2 (Screen, bg); else DrawBGMode7Background16Sub1_2_i (Screen, bg); } else { if (!Settings.Mode7Interpolate) DrawBGMode7Background16Sub (Screen, bg); else DrawBGMode7Background16Sub_i (Screen, bg); } } else { if (GFX.r2131 & 0x40) { if (!Settings.Mode7Interpolate) DrawBGMode7Background16Add1_2 (Screen, bg); else DrawBGMode7Background16Add1_2_i (Screen, bg); } else { if (!Settings.Mode7Interpolate) DrawBGMode7Background16Add (Screen, bg); else DrawBGMode7Background16Add_i (Screen, bg); } } } } break; default: break; } } #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; #ifndef FOREVER_16_BIT if (Settings.SixteenBit) { #endif 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 == '#') { /* if(Memory.Hacked) *s= BUILD_PIXEL(31,0,0); else if(Memory.Iffy) *s= BUILD_PIXEL(31,31,0); else if(Memory.Iformat==1) *s= BUILD_PIXEL(0,31,0); else if(Memory.Iformat==2) *s= BUILD_PIXEL(0,31,31); else *s = 0xffff; */ *s=Settings.DisplayColor; } else if (p == '.') *s = BLACK; } } #ifndef FOREVER_16_BIT } 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 = BLACK; } } } #endif } static void S9xDisplayFrameRate () { uint8 *Screen = GFX.Screen + 2 + (IPPU.RenderedScreenHeight - font_height - 1) * GFX.Pitch2; char string [10]; int len = 5; sprintf (string, "%02d/%02d", IPPU.DisplayedRenderedFrameCount, (int) Memory.ROMFramesPerSecond); int i; for (i = 0; i < len; i++) { DisplayChar (Screen, string [i]); #ifndef FOREVER_16_BIT Screen += Settings.SixteenBit ? (font_width - 1) * sizeof (uint16) : (font_width - 1); #else Screen += (font_width - 1) * sizeof (uint16); #endif } } static void S9xDisplayString (const char *string) { uint8 *Screen = GFX.Screen + 2 + (IPPU.RenderedScreenHeight - font_height * 5) * GFX.Pitch2; int len = strlen (string); int max_chars = IPPU.RenderedScreenWidth / (font_width - 1); int char_count = 0; int i; for (i = 0; i < len; i++, char_count++) { if (char_count >= max_chars || string [i] < 32) { #ifndef FOREVER_16_BIT Screen -= Settings.SixteenBit ? (font_width - 1) * sizeof (uint16) * max_chars : (font_width - 1) * max_chars; #else Screen -= (font_width - 1) * max_chars * sizeof (uint16); #endif Screen += font_height * GFX.Pitch; if (Screen >= GFX.Screen + GFX.Pitch * IPPU.RenderedScreenHeight) break; char_count -= max_chars; } if (string [i] < 32) continue; DisplayChar (Screen, string [i]); #ifndef FOREVER_16_BIT Screen += Settings.SixteenBit ? (font_width - 1) * sizeof (uint16) : (font_width - 1); #else Screen += (font_width - 1) * sizeof (uint16); #endif } } void S9xUpdateScreen () { int32 x2 = 1; GFX.S = GFX.Screen; GFX.r2131 = Memory.FillRAM [0x2131]; GFX.r212c = Memory.FillRAM [0x212c]; GFX.r212d = Memory.FillRAM [0x212d]; GFX.r2130 = Memory.FillRAM [0x2130]; #ifdef JP_FIX GFX.Pseudo = (Memory.FillRAM [0x2133] & 8) != 0 && (GFX.r212c & 15) != (GFX.r212d & 15) && (GFX.r2131 == 0x3f); #else GFX.Pseudo = (Memory.FillRAM [0x2133] & 8) != 0 && (GFX.r212c & 15) != (GFX.r212d & 15) && (GFX.r2131 & 0x3f) == 0; #endif if (IPPU.OBJChanged) S9xSetupOBJ (); if (PPU.RecomputeClipWindows) { ComputeClipWindows (); PPU.RecomputeClipWindows = FALSE; } GFX.StartY = IPPU.PreviousLine; if ((GFX.EndY = IPPU.CurrentLine - 1) >= PPU.ScreenHeight) GFX.EndY = PPU.ScreenHeight - 1; // XXX: Check ForceBlank? Or anything else? PPU.RangeTimeOver |= GFX.OBJLines[GFX.EndY].RTOFlags; uint32 starty = GFX.StartY; uint32 endy = GFX.EndY; if (Settings.SupportHiRes && (PPU.BGMode == 5 || PPU.BGMode == 6 || IPPU.Interlace || IPPU.DoubleHeightPixels)) { if (PPU.BGMode == 5 || PPU.BGMode == 6|| IPPU.Interlace) { IPPU.RenderedScreenWidth = 512; x2 = 2; } if (IPPU.DoubleHeightPixels) { starty = GFX.StartY * 2; endy = GFX.EndY * 2 + 1; } if ((PPU.BGMode == 5 || PPU.BGMode == 6) && !IPPU.DoubleWidthPixels) { // The game has switched from lo-res to hi-res mode part way down // the screen. Scale any existing lo-res pixels on screen #ifndef FOREVER_16_BIT if (Settings.SixteenBit) { #endif for (register uint32 y = 0; y < starty; y++) { register uint16 *p = (uint16 *) (GFX.Screen + y * GFX.Pitch2) + 255; register uint16 *q = (uint16 *) (GFX.Screen + y * GFX.Pitch2) + 510; for (register int x = 255; x >= 0; x--, p--, q -= 2) *q = *(q + 1) = *p; } #ifndef FOREVER_16_BIT } else { for (register uint32 y = 0; y < starty; y++) { register uint8 *p = GFX.Screen + y * GFX.Pitch2 + 255; register uint8 *q = GFX.Screen + y * GFX.Pitch2 + 510; for (register int x = 255; x >= 0; x--, p--, q -= 2) *q = *(q + 1) = *p; } } #endif IPPU.DoubleWidthPixels = TRUE; } // BJ: And we have to change the height if Interlace gets set, // too. if (IPPU.Interlace && !IPPU.DoubleHeightPixels) { starty = GFX.StartY * 2; endy = GFX.EndY * 2 + 1; IPPU.RenderedScreenHeight = PPU.ScreenHeight << 1; IPPU.DoubleHeightPixels = TRUE; GFX.Pitch2 = GFX.RealPitch; GFX.Pitch = GFX.RealPitch * 2; #ifndef FOREVER_16_BIT if (Settings.SixteenBit) #endif GFX.PPL = GFX.PPLx2 = GFX.RealPitch; #ifndef FOREVER_16_BIT else GFX.PPL = GFX.PPLx2 = GFX.RealPitch << 1; #endif // The game has switched from non-interlaced to interlaced mode // part way down the screen. Scale everything. for (register int32 y = (int32) GFX.StartY - 1; y >= 0; y--) { memmove (GFX.Screen + y * 2 * GFX.Pitch2, GFX.Screen + y * GFX.Pitch2, GFX.Pitch2); memmove (GFX.Screen + (y * 2 + 1) * GFX.Pitch2, GFX.Screen + y * GFX.Pitch2, GFX.Pitch2); } } } uint32 black = BLACK | (BLACK << 16); if (Settings.Transparency #ifndef FOREVER_16_BIT && Settings.SixteenBit #endif ) { if (GFX.Pseudo) { GFX.r2131 = 0x5f; GFX.r212c &= (Memory.FillRAM [0x212d] | 0xf0); GFX.r212d |= (Memory.FillRAM [0x212c] & 0x0f); GFX.r2130 |= 2; } if (!PPU.ForcedBlanking && ADD_OR_SUB_ON_ANYTHING && (GFX.r2130 & 0x30) != 0x30 && !((GFX.r2130 & 0x30) == 0x10 && IPPU.Clip[1].Count[5] == 0)) { struct ClipData *pClip; GFX.FixedColour = BUILD_PIXEL (IPPU.XB [PPU.FixedColourRed], IPPU.XB [PPU.FixedColourGreen], IPPU.XB [PPU.FixedColourBlue]); // Clear the z-buffer, marking areas 'covered' by the fixed // colour as depth 1. pClip = &IPPU.Clip [1]; // Clear the z-buffer if (pClip->Count [5]) { // Colour window enabled. for (uint32 y = starty; y <= endy; y++) { ZeroMemory (GFX.SubZBuffer + y * GFX.ZPitch, IPPU.RenderedScreenWidth); ZeroMemory (GFX.ZBuffer + y * GFX.ZPitch, IPPU.RenderedScreenWidth); if (IPPU.Clip [0].Count [5]) { uint32 *p = (uint32 *) (GFX.SubScreen + y * GFX.Pitch2); uint32 *q = (uint32 *) ((uint16 *) p + IPPU.RenderedScreenWidth); while (p < q) *p++ = black; } for (uint32 c = 0; c < pClip->Count [5]; c++) { if (pClip->Right [c][5] > pClip->Left [c][5]) { memset (GFX.SubZBuffer + y * GFX.ZPitch + pClip->Left [c][5] * x2, 1, (pClip->Right [c][5] - pClip->Left [c][5]) * x2); if (IPPU.Clip [0].Count [5]) { // Blast, have to clear the sub-screen to the fixed-colour // because there is a colour window in effect clipping // the main screen that will allow the sub-screen // 'underneath' to show through. uint16 *p = (uint16 *) (GFX.SubScreen + y * GFX.Pitch2); uint16 *q = p + pClip->Right [c][5] * x2; p += pClip->Left [c][5] * x2; while (p < q) *p++ = (uint16) GFX.FixedColour; } } } } } else { for (uint32 y = starty; y <= endy; y++) { ZeroMemory (GFX.ZBuffer + y * GFX.ZPitch, IPPU.RenderedScreenWidth); memset (GFX.SubZBuffer + y * GFX.ZPitch, 1, IPPU.RenderedScreenWidth); if (IPPU.Clip [0].Count [5]) { // Blast, have to clear the sub-screen to the fixed-colour // because there is a colour window in effect clipping // the main screen that will allow the sub-screen // 'underneath' to show through. uint32 b = GFX.FixedColour | (GFX.FixedColour << 16); uint32 *p = (uint32 *) (GFX.SubScreen + y * GFX.Pitch2); uint32 *q = (uint32 *) ((uint16 *) p + IPPU.RenderedScreenWidth); while (p < q) *p++ = b; } } } if (ANYTHING_ON_SUB) { GFX.DB = GFX.SubZBuffer; RenderScreen (GFX.SubScreen, TRUE, TRUE, SUB_SCREEN_DEPTH); } if (IPPU.Clip [0].Count [5]) { for (uint32 y = starty; y <= endy; y++) { register uint16 *p = (uint16 *) (GFX.Screen + y * GFX.Pitch2); register uint8 *d = GFX.SubZBuffer + y * GFX.ZPitch; register uint8 *e = d + IPPU.RenderedScreenWidth; while (d < e) { if (*d > 1) *p = *(p + GFX.Delta); else *p = BLACK; d++; p++; } } } GFX.DB = GFX.ZBuffer; RenderScreen (GFX.Screen, FALSE, FALSE, MAIN_SCREEN_DEPTH); if (SUB_OR_ADD(5)) { uint32 back = IPPU.ScreenColors [0]; uint32 Left = 0; uint32 Right = 256; uint32 Count; pClip = &IPPU.Clip [0]; for (uint32 y = starty; y <= endy; y++) { if (!(Count = pClip->Count [5])) { Left = 0; Right = 256 * x2; Count = 1; } for (uint32 b = 0; b < Count; b++) { if (pClip->Count [5]) { Left = pClip->Left [b][5] * x2; Right = pClip->Right [b][5] * x2; if (Right <= Left) continue; } if (GFX.r2131 & 0x80) { if (GFX.r2131 & 0x40) { // Subtract, halving the result. register uint16 *p = (uint16 *) (GFX.Screen + y * GFX.Pitch2) + Left; register uint8 *d = GFX.ZBuffer + y * GFX.ZPitch; register uint8 *s = GFX.SubZBuffer + y * GFX.ZPitch + Left; register uint8 *e = d + Right; uint16 back_fixed = COLOR_SUB (back, GFX.FixedColour); d += Left; while (d < e) { if (*d == 0) { if (*s) { if (*s != 1) *p = COLOR_SUB1_2 (back, *(p + GFX.Delta)); else *p = back_fixed; } else *p = (uint16) back; } d++; p++; s++; } } else { // Subtract register uint16 *p = (uint16 *) (GFX.Screen + y * GFX.Pitch2) + Left; register uint8 *s = GFX.SubZBuffer + y * GFX.ZPitch + Left; register uint8 *d = GFX.ZBuffer + y * GFX.ZPitch; register uint8 *e = d + Right; uint16 back_fixed = COLOR_SUB (back, GFX.FixedColour); d += Left; while (d < e) { if (*d == 0) { if (*s) { if (*s != 1) *p = COLOR_SUB (back, *(p + GFX.Delta)); else *p = back_fixed; } else *p = (uint16) back; } d++; p++; s++; } } } else if (GFX.r2131 & 0x40) { register uint16 *p = (uint16 *) (GFX.Screen + y * GFX.Pitch2) + Left; register uint8 *d = GFX.ZBuffer + y * GFX.ZPitch; register uint8 *s = GFX.SubZBuffer + y * GFX.ZPitch + Left; register uint8 *e = d + Right; uint16 back_fixed = COLOR_ADD (back, GFX.FixedColour); d += Left; while (d < e) { if (*d == 0) { if (*s) { if (*s != 1) *p = COLOR_ADD1_2 (back, *(p + GFX.Delta)); else *p = back_fixed; } else *p = (uint16) back; } d++; p++; s++; } } else if (back != 0) { register uint16 *p = (uint16 *) (GFX.Screen + y * GFX.Pitch2) + Left; register uint8 *d = GFX.ZBuffer + y * GFX.ZPitch; register uint8 *s = GFX.SubZBuffer + y * GFX.ZPitch + Left; register uint8 *e = d + Right; uint16 back_fixed = COLOR_ADD (back, GFX.FixedColour); d += Left; while (d < e) { if (*d == 0) { if (*s) { if (*s != 1) *p = COLOR_ADD (back, *(p + GFX.Delta)); else *p = back_fixed; } else *p = (uint16) back; } d++; p++; s++; } } else { if (!pClip->Count [5]) { // The backdrop has not been cleared yet - so // copy the sub-screen to the main screen // or fill it with the back-drop colour if the // sub-screen is clear. register uint16 *p = (uint16 *) (GFX.Screen + y * GFX.Pitch2) + Left; register uint8 *d = GFX.ZBuffer + y * GFX.ZPitch; register uint8 *s = GFX.SubZBuffer + y * GFX.ZPitch + Left; register uint8 *e = d + Right; d += Left; while (d < e) { if (*d == 0) { if (*s) { if (*s != 1) *p = *(p + GFX.Delta); else *p = GFX.FixedColour; } else *p = (uint16) back; } d++; p++; s++; } } } } } } // --if (SUB_OR_ADD(5)) else { // Subscreen not being added to back uint32 back = IPPU.ScreenColors [0] | (IPPU.ScreenColors [0] << 16); pClip = &IPPU.Clip [0]; if (pClip->Count [5]) { for (uint32 y = starty; y <= endy; y++) { for (uint32 b = 0; b < pClip->Count [5]; b++) { uint32 Left = pClip->Left [b][5] * x2; uint32 Right = pClip->Right [b][5] * x2; uint16 *p = (uint16 *) (GFX.Screen + y * GFX.Pitch2) + Left; uint8 *d = GFX.ZBuffer + y * GFX.ZPitch; uint8 *e = d + Right; d += Left; while (d < e) { if (*d == 0) *p = (int16) back; d++; p++; } } } } else { for (uint32 y = starty; y <= endy; y++) { uint16 *p = (uint16 *) (GFX.Screen + y * GFX.Pitch2); uint8 *d = GFX.ZBuffer + y * GFX.ZPitch; uint8 *e = d + 256 * x2; while (d < e) { if (*d == 0) *p = (int16) back; d++; p++; } } } } } //force blanking else { // 16bit and transparency but currently no transparency effects in // operation. uint32 back = IPPU.ScreenColors [0] | (IPPU.ScreenColors [0] << 16); if (PPU.ForcedBlanking) back = black; if (IPPU.Clip [0].Count[5]) { for (uint32 y = starty; y <= endy; y++) { uint32 *p = (uint32 *) (GFX.Screen + y * GFX.Pitch2); uint32 *q = (uint32 *) ((uint16 *) p + IPPU.RenderedScreenWidth); while (p < q) *p++ = black; for (uint32 c = 0; c < IPPU.Clip [0].Count [5]; c++) { if (IPPU.Clip [0].Right [c][5] > IPPU.Clip [0].Left [c][5]) { uint16 *p = (uint16 *) (GFX.Screen + y * GFX.Pitch2); uint16 *q = p + IPPU.Clip [0].Right [c][5] * x2; p += IPPU.Clip [0].Left [c][5] * x2; while (p < q) *p++ = (uint16) back; } } } } else { for (uint32 y = starty; y <= endy; y++) { uint32 *p = (uint32 *) (GFX.Screen + y * GFX.Pitch2); uint32 *q = (uint32 *) ((uint16 *) p + IPPU.RenderedScreenWidth); while (p < q) *p++ = back; } } if (!PPU.ForcedBlanking) { for (uint32 y = starty; y <= endy; y++) { ZeroMemory (GFX.ZBuffer + y * GFX.ZPitch, IPPU.RenderedScreenWidth); } GFX.DB = GFX.ZBuffer; RenderScreen (GFX.Screen, FALSE, TRUE, SUB_SCREEN_DEPTH); } } } else { } if (Settings.SupportHiRes) { if (PPU.BGMode != 5 && PPU.BGMode != 6 && IPPU.DoubleWidthPixels) { // Mixure of background modes used on screen - scale width // of all non-mode 5 and 6 pixels. #ifndef FOREVER_16_BIT if (Settings.SixteenBit) { #endif for (register uint32 y = starty; y <= endy; y++) { register uint16 *p = (uint16 *) (GFX.Screen + y * GFX.Pitch2) + 255; register uint16 *q = (uint16 *) (GFX.Screen + y * GFX.Pitch2) + 510; for (register int x = 255; x >= 0; x--, p--, q -= 2) *q = *(q + 1) = *p; } #ifndef FOREVER_16_BIT } else { for (register uint32 y = starty; y <= endy; y++) { register uint8 *p = GFX.Screen + y * GFX.Pitch2 + 255; register uint8 *q = GFX.Screen + y * GFX.Pitch2 + 510; for (register int x = 255; x >= 0; x--, p--, q -= 2) *q = *(q + 1) = *p; } } #endif } // Double the height of the pixels just drawn FIX_INTERLACE(GFX.Screen, FALSE, GFX.ZBuffer); } IPPU.PreviousLine = IPPU.CurrentLine; }