/*****************************************************************************\ Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. This file is licensed under the Snes9x License. For further information, consult the LICENSE file in the root directory. \*****************************************************************************/ #include "tileimpl.h" using namespace TileImpl; namespace { uint32 pixbit[8][16]; uint8 hrbit_odd[256]; uint8 hrbit_even[256]; // Here are the tile converters, selected by S9xSelectTileConverter(). // Really, except for the definition of DOBIT and the number of times it is called, they're all the same. #define DOBIT(n, i) \ if ((pix = *(tp + (n)))) \ { \ p1 |= pixbit[(i)][pix >> 4]; \ p2 |= pixbit[(i)][pix & 0xf]; \ } uint8 ConvertTile2 (uint8 *pCache, uint32 TileAddr, uint32) { uint8 *tp = &Memory.VRAM[TileAddr]; uint32 *p = (uint32 *) pCache; uint32 non_zero = 0; uint8 line; for (line = 8; line != 0; line--, tp += 2) { uint32 p1 = 0; uint32 p2 = 0; uint8 pix; DOBIT( 0, 0); DOBIT( 1, 1); *p++ = p1; *p++ = p2; non_zero |= p1 | p2; } return (non_zero ? TRUE : BLANK_TILE); } uint8 ConvertTile4 (uint8 *pCache, uint32 TileAddr, uint32) { uint8 *tp = &Memory.VRAM[TileAddr]; uint32 *p = (uint32 *) pCache; uint32 non_zero = 0; uint8 line; for (line = 8; line != 0; line--, tp += 2) { uint32 p1 = 0; uint32 p2 = 0; uint8 pix; DOBIT( 0, 0); DOBIT( 1, 1); DOBIT(16, 2); DOBIT(17, 3); *p++ = p1; *p++ = p2; non_zero |= p1 | p2; } return (non_zero ? TRUE : BLANK_TILE); } uint8 ConvertTile8 (uint8 *pCache, uint32 TileAddr, uint32) { uint8 *tp = &Memory.VRAM[TileAddr]; uint32 *p = (uint32 *) pCache; uint32 non_zero = 0; uint8 line; for (line = 8; line != 0; line--, tp += 2) { uint32 p1 = 0; uint32 p2 = 0; uint8 pix; DOBIT( 0, 0); DOBIT( 1, 1); DOBIT(16, 2); DOBIT(17, 3); DOBIT(32, 4); DOBIT(33, 5); DOBIT(48, 6); DOBIT(49, 7); *p++ = p1; *p++ = p2; non_zero |= p1 | p2; } return (non_zero ? TRUE : BLANK_TILE); } #undef DOBIT #define DOBIT(n, i) \ if ((pix = hrbit_odd[*(tp1 + (n))])) \ p1 |= pixbit[(i)][pix]; \ if ((pix = hrbit_odd[*(tp2 + (n))])) \ p2 |= pixbit[(i)][pix]; uint8 ConvertTile2h_odd (uint8 *pCache, uint32 TileAddr, uint32 Tile) { uint8 *tp1 = &Memory.VRAM[TileAddr], *tp2; uint32 *p = (uint32 *) pCache; uint32 non_zero = 0; uint8 line; if (Tile == 0x3ff) tp2 = tp1 - (0x3ff << 4); else tp2 = tp1 + (1 << 4); for (line = 8; line != 0; line--, tp1 += 2, tp2 += 2) { uint32 p1 = 0; uint32 p2 = 0; uint8 pix; DOBIT( 0, 0); DOBIT( 1, 1); *p++ = p1; *p++ = p2; non_zero |= p1 | p2; } return (non_zero ? TRUE : BLANK_TILE); } uint8 ConvertTile4h_odd (uint8 *pCache, uint32 TileAddr, uint32 Tile) { uint8 *tp1 = &Memory.VRAM[TileAddr], *tp2; uint32 *p = (uint32 *) pCache; uint32 non_zero = 0; uint8 line; if (Tile == 0x3ff) tp2 = tp1 - (0x3ff << 5); else tp2 = tp1 + (1 << 5); for (line = 8; line != 0; line--, tp1 += 2, tp2 += 2) { uint32 p1 = 0; uint32 p2 = 0; uint8 pix; DOBIT( 0, 0); DOBIT( 1, 1); DOBIT(16, 2); DOBIT(17, 3); *p++ = p1; *p++ = p2; non_zero |= p1 | p2; } return (non_zero ? TRUE : BLANK_TILE); } #undef DOBIT #define DOBIT(n, i) \ if ((pix = hrbit_even[*(tp1 + (n))])) \ p1 |= pixbit[(i)][pix]; \ if ((pix = hrbit_even[*(tp2 + (n))])) \ p2 |= pixbit[(i)][pix]; uint8 ConvertTile2h_even (uint8 *pCache, uint32 TileAddr, uint32 Tile) { uint8 *tp1 = &Memory.VRAM[TileAddr], *tp2; uint32 *p = (uint32 *) pCache; uint32 non_zero = 0; uint8 line; if (Tile == 0x3ff) tp2 = tp1 - (0x3ff << 4); else tp2 = tp1 + (1 << 4); for (line = 8; line != 0; line--, tp1 += 2, tp2 += 2) { uint32 p1 = 0; uint32 p2 = 0; uint8 pix; DOBIT( 0, 0); DOBIT( 1, 1); *p++ = p1; *p++ = p2; non_zero |= p1 | p2; } return (non_zero ? TRUE : BLANK_TILE); } uint8 ConvertTile4h_even (uint8 *pCache, uint32 TileAddr, uint32 Tile) { uint8 *tp1 = &Memory.VRAM[TileAddr], *tp2; uint32 *p = (uint32 *) pCache; uint32 non_zero = 0; uint8 line; if (Tile == 0x3ff) tp2 = tp1 - (0x3ff << 5); else tp2 = tp1 + (1 << 5); for (line = 8; line != 0; line--, tp1 += 2, tp2 += 2) { uint32 p1 = 0; uint32 p2 = 0; uint8 pix; DOBIT( 0, 0); DOBIT( 1, 1); DOBIT(16, 2); DOBIT(17, 3); *p++ = p1; *p++ = p2; non_zero |= p1 | p2; } return (non_zero ? TRUE : BLANK_TILE); } #undef DOBIT } // anonymous namespace void S9xInitTileRenderer (void) { int i; for (i = 0; i < 16; i++) { uint32 b = 0; #ifdef LSB_FIRST if (i & 8) b |= 1; if (i & 4) b |= 1 << 8; if (i & 2) b |= 1 << 16; if (i & 1) b |= 1 << 24; #else if (i & 8) b |= 1 << 24; if (i & 4) b |= 1 << 16; if (i & 2) b |= 1 << 8; if (i & 1) b |= 1; #endif for (uint8 bitshift = 0; bitshift < 8; bitshift++) pixbit[bitshift][i] = b << bitshift; } for (i = 0; i < 256; i++) { uint8 m = 0; uint8 s = 0; if (i & 0x80) s |= 8; if (i & 0x40) m |= 8; if (i & 0x20) s |= 4; if (i & 0x10) m |= 4; if (i & 0x08) s |= 2; if (i & 0x04) m |= 2; if (i & 0x02) s |= 1; if (i & 0x01) m |= 1; hrbit_odd[i] = m; hrbit_even[i] = s; } } // Functions to select which converter and renderer to use. extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; extern template struct TileImpl::Renderers; void S9xSelectTileRenderers (int BGMode, bool8 sub, bool8 obj) { void (**DT) (uint32, uint32, uint32, uint32); void (**DCT) (uint32, uint32, uint32, uint32, uint32, uint32); void (**DMP) (uint32, uint32, uint32, uint32, uint32, uint32); void (**DB) (uint32, uint32, uint32); void (**DM7BG1) (uint32, uint32, int); void (**DM7BG2) (uint32, uint32, int); bool8 M7M1, M7M2; M7M1 = PPU.BGMosaic[0] && PPU.Mosaic > 1; M7M2 = PPU.BGMosaic[1] && PPU.Mosaic > 1; bool8 interlace = obj ? FALSE : IPPU.Interlace; bool8 hires = !sub && (BGMode == 5 || BGMode == 6 || IPPU.PseudoHires); if (!IPPU.DoubleWidthPixels) // normal width { DT = Renderers::Functions; DCT = Renderers::Functions; DMP = Renderers::Functions; DB = Renderers::Functions; DM7BG1 = M7M1 ? Renderers::Functions : Renderers::Functions; DM7BG2 = M7M2 ? Renderers::Functions : Renderers::Functions; GFX.LinesPerTile = 8; } else if(hires) // hires double width { if (interlace) { DT = Renderers::Functions; DCT = Renderers::Functions; DMP = Renderers::Functions; DB = Renderers::Functions; DM7BG1 = M7M1 ? Renderers::Functions : Renderers::Functions; DM7BG2 = M7M2 ? Renderers::Functions : Renderers::Functions; GFX.LinesPerTile = 4; } else { DT = Renderers::Functions; DCT = Renderers::Functions; DMP = Renderers::Functions; DB = Renderers::Functions; DM7BG1 = M7M1 ? Renderers::Functions : Renderers::Functions; DM7BG2 = M7M2 ? Renderers::Functions : Renderers::Functions; GFX.LinesPerTile = 8; } } else // normal double width { if (interlace) { DT = Renderers::Functions; DCT = Renderers::Functions; DMP = Renderers::Functions; DB = Renderers::Functions; DM7BG1 = M7M1 ? Renderers::Functions : Renderers::Functions; DM7BG2 = M7M2 ? Renderers::Functions : Renderers::Functions; GFX.LinesPerTile = 4; } else { DT = Renderers::Functions; DCT = Renderers::Functions; DMP = Renderers::Functions; DB = Renderers::Functions; DM7BG1 = M7M1 ? Renderers::Functions : Renderers::Functions; DM7BG2 = M7M2 ? Renderers::Functions : Renderers::Functions; GFX.LinesPerTile = 8; } } GFX.DrawTileNomath = DT[0]; GFX.DrawClippedTileNomath = DCT[0]; GFX.DrawMosaicPixelNomath = DMP[0]; GFX.DrawBackdropNomath = DB[0]; GFX.DrawMode7BG1Nomath = DM7BG1[0]; GFX.DrawMode7BG2Nomath = DM7BG2[0]; int i; if (!Settings.Transparency) i = 0; else { i = (Memory.FillRAM[0x2131] & 0x80) ? 4 : 1; if (Memory.FillRAM[0x2131] & 0x40) { i++; if (Memory.FillRAM[0x2130] & 2) i++; } if (IPPU.MaxBrightness != 0xf) { if (i == 1) i = 7; else if (i == 3) i = 8; } } GFX.DrawTileMath = DT[i]; GFX.DrawClippedTileMath = DCT[i]; GFX.DrawMosaicPixelMath = DMP[i]; GFX.DrawBackdropMath = DB[i]; GFX.DrawMode7BG1Math = DM7BG1[i]; GFX.DrawMode7BG2Math = DM7BG2[i]; } void S9xSelectTileConverter (int depth, bool8 hires, bool8 sub, bool8 mosaic) { switch (depth) { case 8: BG.ConvertTile = BG.ConvertTileFlip = ConvertTile8; BG.Buffer = BG.BufferFlip = IPPU.TileCache[TILE_8BIT]; BG.Buffered = BG.BufferedFlip = IPPU.TileCached[TILE_8BIT]; BG.TileShift = 6; BG.PaletteShift = 0; BG.PaletteMask = 0; BG.DirectColourMode = Memory.FillRAM[0x2130] & 1; break; case 4: if (hires) { if (sub || mosaic) { BG.ConvertTile = ConvertTile4h_even; BG.Buffer = IPPU.TileCache[TILE_4BIT_EVEN]; BG.Buffered = IPPU.TileCached[TILE_4BIT_EVEN]; BG.ConvertTileFlip = ConvertTile4h_odd; BG.BufferFlip = IPPU.TileCache[TILE_4BIT_ODD]; BG.BufferedFlip = IPPU.TileCached[TILE_4BIT_ODD]; } else { BG.ConvertTile = ConvertTile4h_odd; BG.Buffer = IPPU.TileCache[TILE_4BIT_ODD]; BG.Buffered = IPPU.TileCached[TILE_4BIT_ODD]; BG.ConvertTileFlip = ConvertTile4h_even; BG.BufferFlip = IPPU.TileCache[TILE_4BIT_EVEN]; BG.BufferedFlip = IPPU.TileCached[TILE_4BIT_EVEN]; } } else { BG.ConvertTile = BG.ConvertTileFlip = ConvertTile4; BG.Buffer = BG.BufferFlip = IPPU.TileCache[TILE_4BIT]; BG.Buffered = BG.BufferedFlip = IPPU.TileCached[TILE_4BIT]; } BG.TileShift = 5; BG.PaletteShift = 10 - 4; BG.PaletteMask = 7 << 4; BG.DirectColourMode = FALSE; break; case 2: if (hires) { if (sub || mosaic) { BG.ConvertTile = ConvertTile2h_even; BG.Buffer = IPPU.TileCache[TILE_2BIT_EVEN]; BG.Buffered = IPPU.TileCached[TILE_2BIT_EVEN]; BG.ConvertTileFlip = ConvertTile2h_odd; BG.BufferFlip = IPPU.TileCache[TILE_2BIT_ODD]; BG.BufferedFlip = IPPU.TileCached[TILE_2BIT_ODD]; } else { BG.ConvertTile = ConvertTile2h_odd; BG.Buffer = IPPU.TileCache[TILE_2BIT_ODD]; BG.Buffered = IPPU.TileCached[TILE_2BIT_ODD]; BG.ConvertTileFlip = ConvertTile2h_even; BG.BufferFlip = IPPU.TileCache[TILE_2BIT_EVEN]; BG.BufferedFlip = IPPU.TileCached[TILE_2BIT_EVEN]; } } else { BG.ConvertTile = BG.ConvertTileFlip = ConvertTile2; BG.Buffer = BG.BufferFlip = IPPU.TileCache[TILE_2BIT]; BG.Buffered = BG.BufferedFlip = IPPU.TileCached[TILE_2BIT]; } BG.TileShift = 4; BG.PaletteShift = 10 - 2; BG.PaletteMask = 7 << 2; BG.DirectColourMode = FALSE; break; } }