MCUME/MCUME_teensy41/teensysnes/gfx.cpp

1818 wiersze
42 KiB
C++

/*****************************************************************************\
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 "snes9x.h"
#include "ppu.h"
#include "tile.h"
#include "controls.h"
//#include "font.h"
#define TILE_PLUS(t, x) (((t) & 0xfc00) | ((t + x) & 0x3ff))
static struct SLineData LineData[240];
struct SGFX GFX;
struct SBG BG;
static void DrawBackground (int bg, uint8 Zh, uint8 Zl)
{
BG.TileAddress = PPU.BG[bg].NameBase << 1;
uint32 Tile;
uint16 *SC0, *SC1, *SC2, *SC3;
SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1];
SC1 = (PPU.BG[bg].SCSize & 1) ? SC0 + 1024 : SC0;
if (SC1 >= (uint16 *) (Memory.VRAM + 0x10000))
SC1 -= 0x8000;
SC2 = (PPU.BG[bg].SCSize & 2) ? SC1 + 1024 : SC0;
if (SC2 >= (uint16 *) (Memory.VRAM + 0x10000))
SC2 -= 0x8000;
SC3 = (PPU.BG[bg].SCSize & 1) ? SC2 + 1024 : SC2;
if (SC3 >= (uint16 *) (Memory.VRAM + 0x10000))
SC3 -= 0x8000;
uint32 Lines;
int OffsetMask = (BG.TileSizeH == 16) ? 0x3ff : 0x1ff;
int OffsetShift = (BG.TileSizeV == 16) ? 4 : 3;
void (*DrawTile) (uint32, uint32, uint32, uint32);
void (*DrawClippedTile) (uint32, uint32, uint32, uint32, uint32, uint32);
for (int clip = 0; clip < GFX.Clip[bg].Count; clip++)
{
GFX.ClipColors = !(GFX.Clip[bg].DrawMode[clip] & 1);
if (BG.EnableMath && (GFX.Clip[bg].DrawMode[clip] & 2))
{
DrawTile = GFX.DrawTileMath;
DrawClippedTile = GFX.DrawClippedTileMath;
}
else
{
DrawTile = GFX.DrawTileNomath;
DrawClippedTile = GFX.DrawClippedTileNomath;
}
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 - Y + 1;
VirtAlign <<= 3;
uint32 t1, t2;
uint32 TilemapRow = (VOffset + Y) >> OffsetShift;
if ((VOffset + Y) & 8)
{
t1 = 16;
t2 = 0;
}
else
{
t1 = 0;
t2 = 16;
}
uint16 *b1, *b2;
if (TilemapRow & 0x20)
{
b1 = SC2;
b2 = SC3;
}
else
{
b1 = SC0;
b2 = SC1;
}
b1 += (TilemapRow & 0x1f) << 5;
b2 += (TilemapRow & 0x1f) << 5;
uint32 Left = GFX.Clip[bg].Left[clip];
uint32 Right = GFX.Clip[bg].Right[clip];
uint32 Offset = Left + Y * GFX.PPL;
uint32 HPos = (HOffset + Left) & OffsetMask;
uint32 HTile = HPos >> 3;
uint16 *t;
if (BG.TileSizeH == 8)
{
if (HTile > 31)
t = b2 + (HTile & 0x1f);
else
t = b1 + HTile;
}
else
{
if (HTile > 63)
t = b2 + ((HTile >> 1) & 0x1f);
else
t = b1 + (HTile >> 1);
}
uint32 Width = Right - Left;
if (HPos & 7)
{
uint32 l = HPos & 7;
uint32 w = 8 - l;
if (w > Width)
w = Width;
Offset -= l;
Tile = READ_WORD(t);
GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl;
if (BG.TileSizeV == 16)
Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1));
if (BG.TileSizeH == 8)
{
DrawClippedTile(Tile, Offset, l, w, VirtAlign, Lines);
t++;
if (HTile == 31)
t = b2;
else
if (HTile == 63)
t = b1;
}
else
{
if (!(Tile & H_FLIP))
DrawClippedTile(TILE_PLUS(Tile, (HTile & 1)), Offset, l, w, VirtAlign, Lines);
else
DrawClippedTile(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, l, w, VirtAlign, Lines);
t += HTile & 1;
if (HTile == 63)
t = b2;
else
if (HTile == 127)
t = b1;
}
HTile++;
Offset += 8;
Width -= w;
}
while (Width >= 8)
{
Tile = READ_WORD(t);
GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl;
if (BG.TileSizeV == 16)
Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1));
if (BG.TileSizeH == 8)
{
DrawTile(Tile, Offset, VirtAlign, Lines);
t++;
if (HTile == 31)
t = b2;
else
if (HTile == 63)
t = b1;
}
else
{
if (!(Tile & H_FLIP))
DrawTile(TILE_PLUS(Tile, (HTile & 1)), Offset, VirtAlign, Lines);
else
DrawTile(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, VirtAlign, Lines);
t += HTile & 1;
if (HTile == 63)
t = b2;
else
if (HTile == 127)
t = b1;
}
HTile++;
Offset += 8;
Width -= 8;
}
if (Width)
{
Tile = READ_WORD(t);
GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl;
if (BG.TileSizeV == 16)
Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1));
if (BG.TileSizeH == 8)
DrawClippedTile(Tile, Offset, 0, Width, VirtAlign, Lines);
else
{
if (!(Tile & H_FLIP))
DrawClippedTile(TILE_PLUS(Tile, (HTile & 1)), Offset, 0, Width, VirtAlign, Lines);
else
DrawClippedTile(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, 0, Width, VirtAlign, Lines);
}
}
}
}
}
static void DrawBackgroundMosaic (int bg, uint8 Zh, uint8 Zl)
{
BG.TileAddress = PPU.BG[bg].NameBase << 1;
uint32 Tile;
uint16 *SC0, *SC1, *SC2, *SC3;
SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1];
SC1 = (PPU.BG[bg].SCSize & 1) ? SC0 + 1024 : SC0;
if (SC1 >= (uint16 *) (Memory.VRAM + 0x10000))
SC1 -= 0x8000;
SC2 = (PPU.BG[bg].SCSize & 2) ? SC1 + 1024 : SC0;
if (SC2 >= (uint16 *) (Memory.VRAM + 0x10000))
SC2 -= 0x8000;
SC3 = (PPU.BG[bg].SCSize & 1) ? SC2 + 1024 : SC2;
if (SC3 >= (uint16 *) (Memory.VRAM + 0x10000))
SC3 -= 0x8000;
int Lines;
int OffsetMask = (BG.TileSizeH == 16) ? 0x3ff : 0x1ff;
int OffsetShift = (BG.TileSizeV == 16) ? 4 : 3;
void (*DrawPix) (uint32, uint32, uint32, uint32, uint32, uint32);
int MosaicStart = ((uint32) GFX.StartY - PPU.MosaicStart) % PPU.Mosaic;
for (int clip = 0; clip < GFX.Clip[bg].Count; clip++)
{
GFX.ClipColors = !(GFX.Clip[bg].DrawMode[clip] & 1);
if (BG.EnableMath && (GFX.Clip[bg].DrawMode[clip] & 2))
DrawPix = GFX.DrawMosaicPixelMath;
else
DrawPix = GFX.DrawMosaicPixelNomath;
for (uint32 Y = GFX.StartY - MosaicStart; Y <= GFX.EndY; Y += PPU.Mosaic)
{
uint32 VOffset = LineData[Y + MosaicStart].BG[bg].VOffset + (0);
uint32 HOffset = LineData[Y + MosaicStart].BG[bg].HOffset;
Lines = PPU.Mosaic - MosaicStart;
if (Y + MosaicStart + Lines > GFX.EndY)
Lines = GFX.EndY - Y - MosaicStart + 1;
int VirtAlign = ((Y + VOffset) & 7) << 3;
uint32 t1, t2;
uint32 TilemapRow = (VOffset + Y) >> OffsetShift;
if ((VOffset + Y) & 8)
{
t1 = 16;
t2 = 0;
}
else
{
t1 = 0;
t2 = 16;
}
uint16 *b1, *b2;
if (TilemapRow & 0x20)
{
b1 = SC2;
b2 = SC3;
}
else
{
b1 = SC0;
b2 = SC1;
}
b1 += (TilemapRow & 0x1f) << 5;
b2 += (TilemapRow & 0x1f) << 5;
uint32 Left = GFX.Clip[bg].Left[clip];
uint32 Right = GFX.Clip[bg].Right[clip];
uint32 Offset = Left + (Y + MosaicStart) * GFX.PPL;
uint32 HPos = (HOffset + Left - (Left % PPU.Mosaic)) & OffsetMask;
uint32 HTile = HPos >> 3;
uint16 *t;
if (BG.TileSizeH == 8)
{
if (HTile > 31)
t = b2 + (HTile & 0x1f);
else
t = b1 + HTile;
}
else
{
if (HTile > 63)
t = b2 + ((HTile >> 1) & 0x1f);
else
t = b1 + (HTile >> 1);
}
uint32 Width = Right - Left;
HPos &= 7;
while (Left < Right)
{
uint32 w = PPU.Mosaic - (Left % PPU.Mosaic);
if (w > Width)
w = Width;
Tile = READ_WORD(t);
GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl;
if (BG.TileSizeV == 16)
Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1));
if (BG.TileSizeH == 8)
DrawPix(Tile, Offset, VirtAlign, HPos & 7, w, Lines);
else
{
if (!(Tile & H_FLIP))
DrawPix(TILE_PLUS(Tile, (HTile & 1)), Offset, VirtAlign, HPos & 7, w, Lines);
else
DrawPix(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, VirtAlign, HPos & 7, w, Lines);
}
HPos += PPU.Mosaic;
while (HPos >= 8)
{
HPos -= 8;
if (BG.TileSizeH == 8)
{
t++;
if (HTile == 31)
t = b2;
else
if (HTile == 63)
t = b1;
}
else
{
t += HTile & 1;
if (HTile == 63)
t = b2;
else
if (HTile == 127)
t = b1;
}
HTile++;
}
Offset += w;
Width -= w;
Left += w;
}
MosaicStart = 0;
}
}
}
static void DrawBackgroundOffset (int bg, uint8 Zh, uint8 Zl, int VOffOff)
{
BG.TileAddress = PPU.BG[bg].NameBase << 1;
uint32 Tile;
uint16 *SC0, *SC1, *SC2, *SC3;
uint16 *BPS0, *BPS1, *BPS2, *BPS3;
BPS0 = (uint16 *) &Memory.VRAM[PPU.BG[2].SCBase << 1];
BPS1 = (PPU.BG[2].SCSize & 1) ? BPS0 + 1024 : BPS0;
if (BPS1 >= (uint16 *) (Memory.VRAM + 0x10000))
BPS1 -= 0x8000;
BPS2 = (PPU.BG[2].SCSize & 2) ? BPS1 + 1024 : BPS0;
if (BPS2 >= (uint16 *) (Memory.VRAM + 0x10000))
BPS2 -= 0x8000;
BPS3 = (PPU.BG[2].SCSize & 1) ? BPS2 + 1024 : BPS2;
if (BPS3 >= (uint16 *) (Memory.VRAM + 0x10000))
BPS3 -= 0x8000;
SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1];
SC1 = (PPU.BG[bg].SCSize & 1) ? SC0 + 1024 : SC0;
if (SC1 >= (uint16 *) (Memory.VRAM + 0x10000))
SC1 -= 0x8000;
SC2 = (PPU.BG[bg].SCSize & 2) ? SC1 + 1024 : SC0;
if (SC2 >= (uint16 *) (Memory.VRAM + 0x10000))
SC2 -= 0x8000;
SC3 = (PPU.BG[bg].SCSize & 1) ? SC2 + 1024 : SC2;
if (SC3 >= (uint16 *) (Memory.VRAM + 0x10000))
SC3 -= 0x8000;
int OffsetMask = (BG.TileSizeH == 16) ? 0x3ff : 0x1ff;
int OffsetShift = (BG.TileSizeV == 16) ? 4 : 3;
int Offset2Mask = (BG.OffsetSizeH == 16) ? 0x3ff : 0x1ff;
int Offset2Shift = (BG.OffsetSizeV == 16) ? 4 : 3;
int OffsetEnableMask = 0x2000 << bg;
void (*DrawClippedTile) (uint32, uint32, uint32, uint32, uint32, uint32);
for (int clip = 0; clip < GFX.Clip[bg].Count; clip++)
{
GFX.ClipColors = !(GFX.Clip[bg].DrawMode[clip] & 1);
if (BG.EnableMath && (GFX.Clip[bg].DrawMode[clip] & 2))
{
DrawClippedTile = GFX.DrawClippedTileMath;
}
else
{
DrawClippedTile = GFX.DrawClippedTileNomath;
}
for (uint32 Y = GFX.StartY; Y <= GFX.EndY; Y++)
{
uint32 VOff = LineData[Y].BG[2].VOffset - 1;
uint32 HOff = LineData[Y].BG[2].HOffset;
uint32 HOffsetRow = VOff >> Offset2Shift;
uint32 VOffsetRow = (VOff + VOffOff) >> Offset2Shift;
uint16 *s, *s1, *s2;
if (HOffsetRow & 0x20)
{
s1 = BPS2;
s2 = BPS3;
}
else
{
s1 = BPS0;
s2 = BPS1;
}
s1 += (HOffsetRow & 0x1f) << 5;
s2 += (HOffsetRow & 0x1f) << 5;
s = ((VOffsetRow & 0x20) ? BPS2 : BPS0) + ((VOffsetRow & 0x1f) << 5);
int32 VOffsetOffset = s - s1;
uint32 Left = GFX.Clip[bg].Left[clip];
uint32 Right = GFX.Clip[bg].Right[clip];
uint32 Offset = Left + Y * GFX.PPL;
uint32 HScroll = LineData[Y].BG[bg].HOffset;
uint32 left_edge = (Left < (8 - (HScroll & 7)));
uint32 Width = Right - Left;
while (Left < Right)
{
uint32 VOffset, HOffset;
if (left_edge)
{
// SNES cannot do OPT for leftmost tile column
VOffset = LineData[Y].BG[bg].VOffset;
HOffset = HScroll;
left_edge = FALSE;
}
else
{
int HOffTile = ((HOff + Left - 1) & Offset2Mask) >> 3;
if (BG.OffsetSizeH == 8)
{
if (HOffTile > 31)
s = s2 + (HOffTile & 0x1f);
else
s = s1 + HOffTile;
}
else
{
if (HOffTile > 63)
s = s2 + ((HOffTile >> 1) & 0x1f);
else
s = s1 + (HOffTile >> 1);
}
uint16 HCellOffset = READ_WORD(s);
uint16 VCellOffset;
if (VOffOff)
VCellOffset = READ_WORD(s + VOffsetOffset);
else
{
if (HCellOffset & 0x8000)
{
VCellOffset = HCellOffset;
HCellOffset = 0;
}
else
VCellOffset = 0;
}
if (VCellOffset & OffsetEnableMask)
VOffset = VCellOffset + 1;
else
VOffset = LineData[Y].BG[bg].VOffset;
if (HCellOffset & OffsetEnableMask)
HOffset = (HCellOffset & ~7) | (HScroll & 7);
else
HOffset = HScroll;
}
uint32 t1, t2;
int VirtAlign = ((Y + VOffset) & 7) << 3;
int TilemapRow = (VOffset + Y) >> OffsetShift;
if ((VOffset + Y) & 8)
{
t1 = 16;
t2 = 0;
}
else
{
t1 = 0;
t2 = 16;
}
uint16 *b1, *b2;
if (TilemapRow & 0x20)
{
b1 = SC2;
b2 = SC3;
}
else
{
b1 = SC0;
b2 = SC1;
}
b1 += (TilemapRow & 0x1f) << 5;
b2 += (TilemapRow & 0x1f) << 5;
uint32 HPos = (HOffset + Left) & OffsetMask;
uint32 HTile = HPos >> 3;
uint16 *t;
if (BG.TileSizeH == 8)
{
if (HTile > 31)
t = b2 + (HTile & 0x1f);
else
t = b1 + HTile;
}
else
{
if (HTile > 63)
t = b2 + ((HTile >> 1) & 0x1f);
else
t = b1 + (HTile >> 1);
}
uint32 l = HPos & 7;
uint32 w = 8 - l;
if (w > Width)
w = Width;
Offset -= l;
Tile = READ_WORD(t);
GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl;
if (BG.TileSizeV == 16)
Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1));
if (BG.TileSizeH == 8)
{
DrawClippedTile(Tile, Offset, l, w, VirtAlign, 1);
}
else
{
if (!(Tile & H_FLIP))
DrawClippedTile(TILE_PLUS(Tile, (HTile & 1)), Offset, l, w, VirtAlign, 1);
else
DrawClippedTile(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, l, w, VirtAlign, 1);
}
Left += w;
Offset += 8;
Width -= w;
}
}
}
}
static void DrawBackgroundOffsetMosaic (int bg, uint8 Zh, uint8 Zl, int VOffOff)
{
BG.TileAddress = PPU.BG[bg].NameBase << 1;
uint32 Tile;
uint16 *SC0, *SC1, *SC2, *SC3;
uint16 *BPS0, *BPS1, *BPS2, *BPS3;
BPS0 = (uint16 *) &Memory.VRAM[PPU.BG[2].SCBase << 1];
BPS1 = (PPU.BG[2].SCSize & 1) ? BPS0 + 1024 : BPS0;
if (BPS1 >= (uint16 *) (Memory.VRAM + 0x10000))
BPS1 -= 0x8000;
BPS2 = (PPU.BG[2].SCSize & 2) ? BPS1 + 1024 : BPS0;
if (BPS2 >= (uint16 *) (Memory.VRAM + 0x10000))
BPS2 -= 0x8000;
BPS3 = (PPU.BG[2].SCSize & 1) ? BPS2 + 1024 : BPS2;
if (BPS3 >= (uint16 *) (Memory.VRAM + 0x10000))
BPS3 -= 0x8000;
SC0 = (uint16 *) &Memory.VRAM[PPU.BG[bg].SCBase << 1];
SC1 = (PPU.BG[bg].SCSize & 1) ? SC0 + 1024 : SC0;
if (SC1 >= (uint16 *) (Memory.VRAM + 0x10000))
SC1 -= 0x8000;
SC2 = (PPU.BG[bg].SCSize & 2) ? SC1 + 1024 : SC0;
if (SC2 >= (uint16 *) (Memory.VRAM + 0x10000))
SC2 -= 0x8000;
SC3 = (PPU.BG[bg].SCSize & 1) ? SC2 + 1024 : SC2;
if (SC3 >= (uint16 *) (Memory.VRAM + 0x10000))
SC3 -= 0x8000;
int Lines;
int OffsetMask = (BG.TileSizeH == 16) ? 0x3ff : 0x1ff;
int OffsetShift = (BG.TileSizeV == 16) ? 4 : 3;
int Offset2Shift = (BG.OffsetSizeV == 16) ? 4 : 3;
int OffsetEnableMask = 0x2000 << bg;
void (*DrawPix) (uint32, uint32, uint32, uint32, uint32, uint32);
int MosaicStart = ((uint32) GFX.StartY - PPU.MosaicStart) % PPU.Mosaic;
for (int clip = 0; clip < GFX.Clip[bg].Count; clip++)
{
GFX.ClipColors = !(GFX.Clip[bg].DrawMode[clip] & 1);
if (BG.EnableMath && (GFX.Clip[bg].DrawMode[clip] & 2))
DrawPix = GFX.DrawMosaicPixelMath;
else
DrawPix = GFX.DrawMosaicPixelNomath;
for (uint32 Y = GFX.StartY - MosaicStart; Y <= GFX.EndY; Y += PPU.Mosaic)
{
uint32 VOff = LineData[Y + MosaicStart].BG[2].VOffset - 1;
uint32 HOff = LineData[Y + MosaicStart].BG[2].HOffset;
Lines = PPU.Mosaic - MosaicStart;
if (Y + MosaicStart + Lines > GFX.EndY)
Lines = GFX.EndY - Y - MosaicStart + 1;
uint32 HOffsetRow = VOff >> Offset2Shift;
uint32 VOffsetRow = (VOff + VOffOff) >> Offset2Shift;
uint16 *s, *s1, *s2;
if (HOffsetRow & 0x20)
{
s1 = BPS2;
s2 = BPS3;
}
else
{
s1 = BPS0;
s2 = BPS1;
}
s1 += (HOffsetRow & 0x1f) << 5;
s2 += (HOffsetRow & 0x1f) << 5;
s = ((VOffsetRow & 0x20) ? BPS2 : BPS0) + ((VOffsetRow & 0x1f) << 5);
int32 VOffsetOffset = s - s1;
uint32 Left = GFX.Clip[bg].Left[clip];
uint32 Right = GFX.Clip[bg].Right[clip];
uint32 Offset = Left + (Y + MosaicStart) * GFX.PPL;
uint32 HScroll = LineData[Y + MosaicStart].BG[bg].HOffset;
uint32 Width = Right - Left;
while (Left < Right)
{
uint32 VOffset, HOffset;
if (Left < (8 - (HScroll & 7)))
{
// SNES cannot do OPT for leftmost tile column
VOffset = LineData[Y + MosaicStart].BG[bg].VOffset;
HOffset = HScroll;
}
else
{
int HOffTile = (((Left + (HScroll & 7)) - 8) + (HOff & ~7)) >> 3;
if (BG.OffsetSizeH == 8)
{
if (HOffTile > 31)
s = s2 + (HOffTile & 0x1f);
else
s = s1 + HOffTile;
}
else
{
if (HOffTile > 63)
s = s2 + ((HOffTile >> 1) & 0x1f);
else
s = s1 + (HOffTile >> 1);
}
uint16 HCellOffset = READ_WORD(s);
uint16 VCellOffset;
if (VOffOff)
VCellOffset = READ_WORD(s + VOffsetOffset);
else
{
if (HCellOffset & 0x8000)
{
VCellOffset = HCellOffset;
HCellOffset = 0;
}
else
VCellOffset = 0;
}
if (VCellOffset & OffsetEnableMask)
VOffset = VCellOffset + 1;
else
VOffset = LineData[Y + MosaicStart].BG[bg].VOffset;
if (HCellOffset & OffsetEnableMask)
HOffset = (HCellOffset & ~7) | (HScroll & 7);
else
HOffset = HScroll;
}
uint32 t1, t2;
int VirtAlign = (((Y + VOffset) & 7) >> (0)) << 3;
int TilemapRow = (VOffset + Y) >> OffsetShift;
if ((VOffset + Y) & 8)
{
t1 = 16;
t2 = 0;
}
else
{
t1 = 0;
t2 = 16;
}
uint16 *b1, *b2;
if (TilemapRow & 0x20)
{
b1 = SC2;
b2 = SC3;
}
else
{
b1 = SC0;
b2 = SC1;
}
b1 += (TilemapRow & 0x1f) << 5;
b2 += (TilemapRow & 0x1f) << 5;
uint32 HPos = (HOffset + Left - (Left % PPU.Mosaic)) & OffsetMask;
uint32 HTile = HPos >> 3;
uint16 *t;
if (BG.TileSizeH == 8)
{
if (HTile > 31)
t = b2 + (HTile & 0x1f);
else
t = b1 + HTile;
}
else
{
if (HTile > 63)
t = b2 + ((HTile >> 1) & 0x1f);
else
t = b1 + (HTile >> 1);
}
uint32 w = PPU.Mosaic - (Left % PPU.Mosaic);
if (w > Width)
w = Width;
Tile = READ_WORD(t);
GFX.Z1 = GFX.Z2 = (Tile & 0x2000) ? Zh : Zl;
if (BG.TileSizeV == 16)
Tile = TILE_PLUS(Tile, ((Tile & V_FLIP) ? t2 : t1));
if (BG.TileSizeH == 8)
DrawPix(Tile, Offset, VirtAlign, HPos & 7, w, Lines);
else
{
if (!(Tile & H_FLIP))
DrawPix(TILE_PLUS(Tile, (HTile & 1)), Offset, VirtAlign, HPos & 7, w, Lines);
else
if (!(Tile & V_FLIP))
DrawPix(TILE_PLUS(Tile, 1 - (HTile & 1)), Offset, VirtAlign, HPos & 7, w, Lines);
}
Left += w;
Offset += w;
Width -= w;
}
MosaicStart = 0;
}
}
}
static inline void DrawBackgroundMode7 (int bg, void (*DrawMath) (uint32, uint32, int), void (*DrawNomath) (uint32, uint32, int), int D)
{
for (int clip = 0; clip < GFX.Clip[bg].Count; clip++)
{
GFX.ClipColors = !(GFX.Clip[bg].DrawMode[clip] & 1);
if (BG.EnableMath && (GFX.Clip[bg].DrawMode[clip] & 2))
DrawMath(GFX.Clip[bg].Left[clip], GFX.Clip[bg].Right[clip], D);
else
DrawNomath(GFX.Clip[bg].Left[clip], GFX.Clip[bg].Right[clip], D);
}
}
static inline void DrawBackdrop (void)
{
uint32 Offset = GFX.StartY * GFX.PPL;
for (int clip = 0; clip < GFX.Clip[5].Count; clip++)
{
GFX.ClipColors = !(GFX.Clip[5].DrawMode[clip] & 1);
if (BG.EnableMath && (GFX.Clip[5].DrawMode[clip] & 2))
GFX.DrawBackdropMath(Offset, GFX.Clip[5].Left[clip], GFX.Clip[5].Right[clip]);
else
GFX.DrawBackdropNomath(Offset, GFX.Clip[5].Left[clip], GFX.Clip[5].Right[clip]);
}
}
static void SetupOBJ (void)
{
int SmallWidth, SmallHeight, 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;
case 5:
default:
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;
}
// 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.
uint8 LineOBJ[SNES_HEIGHT_EXTENDED];
memset(LineOBJ, 0, sizeof(LineOBJ));
int Height;
if (!PPU.OAMPriorityRotation || !(PPU.OAMFlip & PPU.OAMAddr & 1)) // normal case
{
for (int i = 0; i < SNES_HEIGHT_EXTENDED; i++)
{
GFX.OBJLines[i].RTOFlags = 0;
GFX.OBJLines[i].Tiles = SNES_SPRITE_TILE_PER_LINE;
for (int j = 0; j < 32; j++)
GFX.OBJLines[i].OBJ[j].Sprite = -1;
}
int FirstSprite = PPU.FirstSprite;
int S = FirstSprite;
do
{
if (PPU.OBJ[S].Size)
{
GFX.OBJWidths[S] = LargeWidth;
Height = LargeHeight;
}
else
{
GFX.OBJWidths[S] = SmallWidth;
Height = SmallHeight;
}
int HPos = PPU.OBJ[S].HPos;
if (HPos == -256)
HPos = 0;
if (HPos > -GFX.OBJWidths[S] && HPos <= 256)
{
if (HPos < 0)
GFX.OBJVisibleTiles[S] = (GFX.OBJWidths[S] + HPos + 7) >> 3;
else
if (HPos + GFX.OBJWidths[S] > 255)
GFX.OBJVisibleTiles[S] = (256 - HPos + 7) >> 3;
else
GFX.OBJVisibleTiles[S] = GFX.OBJWidths[S] >> 3;
for (int line = 0, Y = (uint8) (PPU.OBJ[S].VPos & 0xff); line < Height; Y++, line++)
{
if (Y >= SNES_HEIGHT_EXTENDED)
continue;
if (LineOBJ[Y] >= 32)
{
GFX.OBJLines[Y].RTOFlags |= 0x40;
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 < SNES_HEIGHT_EXTENDED; Y++)
GFX.OBJLines[Y].RTOFlags |= GFX.OBJLines[Y - 1].RTOFlags;
}
else // evil FirstSprite+Y case
{
// printf("evil FirstSprite+Y case\n");
// We can't afford 30K of ram, we abuse the tile cache instead
uint8 *OBJOnLine = (uint8 *)IPPU.TileCacheData;
memset(IPPU.TileCache, 0, (SNES_HEIGHT_EXTENDED * 128) / 64);
for (int S = 0; S < 128; S++)
{
if (PPU.OBJ[S].Size)
{
GFX.OBJWidths[S] = LargeWidth;
Height = LargeHeight;
}
else
{
GFX.OBJWidths[S] = SmallWidth;
Height = SmallHeight;
}
int HPos = PPU.OBJ[S].HPos;
if (HPos == -256)
HPos = 256;
if (HPos > -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 (int line = 0, Y = (uint8) (PPU.OBJ[S].VPos & 0xff); line < Height; Y++, line++)
{
if (Y >= SNES_HEIGHT_EXTENDED)
continue;
if (!LineOBJ[Y]) {
memset(OBJOnLine + (Y * 128), 0, 128);
LineOBJ[Y] = TRUE;
}
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 * 128) + S] = (line ^ (GFX.OBJWidths[S] - 1)) | 0x80;
else
OBJOnLine[(Y * 128) + S] = line | 0x80;
}
}
}
// Now go through and pull out those OBJ that are actually visible.
int j;
for (int Y = 0; Y < SNES_HEIGHT_EXTENDED; Y++)
{
GFX.OBJLines[Y].RTOFlags = Y ? GFX.OBJLines[Y - 1].RTOFlags : 0;
GFX.OBJLines[Y].Tiles = SNES_SPRITE_TILE_PER_LINE;
int FirstSprite = (PPU.FirstSprite + Y) & 0x7f;
int S = FirstSprite;
j = 0;
if (LineOBJ[Y])
{
do
{
if (OBJOnLine[(Y * 128) + S])
{
if (j >= 32)
{
GFX.OBJLines[Y].RTOFlags |= 0x40;
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 * 128) + S] & ~0x80;
}
S = (S + 1) & 0x7f;
} while (S != FirstSprite);
}
if (j < 32)
GFX.OBJLines[Y].OBJ[j].Sprite = -1;
}
}
IPPU.OBJChanged = FALSE;
}
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC push_options
#pragma GCC optimize ("no-tree-vrp")
#endif
static void DrawOBJS (int D)
{
void (*DrawTile) (uint32, uint32, uint32, uint32) = NULL;
void (*DrawClippedTile) (uint32, uint32, uint32, uint32, uint32, uint32) = NULL;
GFX.Z1 = 2;
for (uint32 Y = GFX.StartY, Offset = Y * GFX.PPL; Y <= GFX.EndY; Y++, Offset += GFX.PPL)
{
int I = 0;
int tiles = GFX.OBJLines[Y].Tiles;
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)
continue;
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 = D + PPU.OBJ[S].Priority * 4;
int DrawMode = 3;
int clip = 0, next_clip = -1000;
int X = PPU.OBJ[S].HPos;
if (X == -256)
X = 256;
for (int t = tiles, O = Offset + X; X <= 256 && X < PPU.OBJ[S].HPos + GFX.OBJWidths[S]; TileX = (TileX + TileInc) & 0x0f, X += 8, O += 8)
{
if (X < -7 || --t < 0 || X == 256)
continue;
for (int x = X; x < X + 8;)
{
if (x >= next_clip)
{
for (; clip < GFX.Clip[4].Count && GFX.Clip[4].Left[clip] <= x; clip++) ;
if (clip == 0 || x >= GFX.Clip[4].Right[clip - 1])
{
DrawMode = 0;
next_clip = ((clip < GFX.Clip[4].Count) ? GFX.Clip[4].Left[clip] : 1000);
}
else
{
DrawMode = GFX.Clip[4].DrawMode[clip - 1];
next_clip = GFX.Clip[4].Right[clip - 1];
GFX.ClipColors = !(DrawMode & 1);
if (BG.EnableMath && (PPU.OBJ[S].Palette & 4) && (DrawMode & 2))
{
DrawTile = GFX.DrawTileMath;
DrawClippedTile = GFX.DrawClippedTileMath;
}
else
{
DrawTile = GFX.DrawTileNomath;
DrawClippedTile = GFX.DrawClippedTileNomath;
}
}
}
if (x == X && x + 8 < next_clip)
{
if (DrawMode)
DrawTile(BaseTile | TileX, O, TileLine, 1);
x += 8;
}
else
{
int w = (next_clip <= X + 8) ? next_clip - x : X + 8 - x;
if (DrawMode)
DrawClippedTile(BaseTile | TileX, O, x - X, w, TileLine, 1);
x += w;
}
}
}
}
}
}
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC pop_options
#endif
static inline void RenderScreen (bool8 sub)
{
int BGActive, D;
if (!sub)
{
GFX.S = GFX.Screen;
GFX.DB = GFX.ZBuffer;
GFX.Clip = IPPU.Clip[0];
BGActive = Memory.PPU_IO[0x12c] & ~Settings.BG_Forced;
D = 32;
}
else
{
GFX.S = GFX.SubScreen;
GFX.DB = GFX.SubZBuffer;
GFX.Clip = IPPU.Clip[1];
BGActive = Memory.PPU_IO[0x12d] & ~Settings.BG_Forced;
D = (Memory.PPU_IO[0x130] & 2) << 4; // 'do math' depth flag
}
if (BGActive & 0x10)
{
BG.TileAddress = PPU.OBJNameBase;
BG.NameSelect = PPU.OBJNameSelect;
BG.EnableMath = !sub && (Memory.PPU_IO[0x131] & 0x10);
BG.StartPalette = 128;
S9xSelectTileConverter(4, FALSE, sub, FALSE);
S9xSelectTileRenderers(PPU.BGMode, sub, TRUE);
DrawOBJS(D + 4);
}
BG.NameSelect = 0;
S9xSelectTileRenderers(PPU.BGMode, sub, FALSE);
#define DO_BG(n, pal, depth, hires, offset, Zh, Zl, voffoff) \
if (BGActive & (1 << n)) \
{ \
BG.StartPalette = pal; \
BG.EnableMath = !sub && (Memory.PPU_IO[0x131] & (1 << n)); \
BG.TileSizeH = (!hires && PPU.BG[n].BGSize) ? 16 : 8; \
BG.TileSizeV = (PPU.BG[n].BGSize) ? 16 : 8; \
S9xSelectTileConverter(depth, hires, sub, PPU.BGMosaic[n]); \
\
if (offset) \
{ \
BG.OffsetSizeH = (!hires && PPU.BG[2].BGSize) ? 16 : 8; \
BG.OffsetSizeV = (PPU.BG[2].BGSize) ? 16 : 8; \
\
if (PPU.BGMosaic[n] && (hires || PPU.Mosaic > 1)) \
DrawBackgroundOffsetMosaic(n, D + Zh, D + Zl, voffoff); \
else \
DrawBackgroundOffset(n, D + Zh, D + Zl, voffoff); \
} \
else \
{ \
if (PPU.BGMosaic[n] && (hires || PPU.Mosaic > 1)) \
DrawBackgroundMosaic(n, D + Zh, D + Zl); \
else \
DrawBackground(n, D + Zh, D + Zl); \
} \
}
switch (PPU.BGMode)
{
case 0:
DO_BG(0, 0, 2, FALSE, FALSE, 15, 11, 0);
DO_BG(1, 32, 2, FALSE, FALSE, 14, 10, 0);
DO_BG(2, 64, 2, FALSE, FALSE, 7, 3, 0);
DO_BG(3, 96, 2, FALSE, FALSE, 6, 2, 0);
break;
case 1:
DO_BG(0, 0, 4, FALSE, FALSE, 15, 11, 0);
DO_BG(1, 0, 4, FALSE, FALSE, 14, 10, 0);
DO_BG(2, 0, 2, FALSE, FALSE, (PPU.BG3Priority ? 17 : 7), 3, 0);
break;
case 2:
DO_BG(0, 0, 4, FALSE, TRUE, 15, 7, 8);
DO_BG(1, 0, 4, FALSE, TRUE, 11, 3, 8);
break;
case 3:
DO_BG(0, 0, 8, FALSE, FALSE, 15, 7, 0);
DO_BG(1, 0, 4, FALSE, FALSE, 11, 3, 0);
break;
case 4:
DO_BG(0, 0, 8, FALSE, TRUE, 15, 7, 0);
DO_BG(1, 0, 2, FALSE, TRUE, 11, 3, 0);
break;
case 5:
DO_BG(0, 0, 4, TRUE, FALSE, 15, 7, 0);
DO_BG(1, 0, 2, TRUE, FALSE, 11, 3, 0);
break;
case 6:
DO_BG(0, 0, 4, TRUE, TRUE, 15, 7, 8);
break;
case 7:
if (BGActive & 0x01)
{
BG.EnableMath = !sub && (Memory.PPU_IO[0x131] & 1);
DrawBackgroundMode7(0, GFX.DrawMode7BG1Math, GFX.DrawMode7BG1Nomath, D);
}
if ((Memory.PPU_IO[0x133] & 0x40) && (BGActive & 0x02))
{
BG.EnableMath = !sub && (Memory.PPU_IO[0x131] & 2);
DrawBackgroundMode7(1, GFX.DrawMode7BG2Math, GFX.DrawMode7BG2Nomath, D);
}
break;
}
#undef DO_BG
BG.EnableMath = !sub && (Memory.PPU_IO[0x131] & 0x20);
DrawBackdrop();
}
static inline uint8 CalcWindowMask (int i, uint8 W1, uint8 W2)
{
if (!PPU.ClipWindow1Enable[i])
{
if (!PPU.ClipWindow2Enable[i])
return (0);
if (!PPU.ClipWindow2Inside[i])
return (~W2);
return (W2);
}
else
{
if (!PPU.ClipWindow2Enable[i])
{
if (!PPU.ClipWindow1Inside[i])
return (~W1);
return (W1);
}
else
{
if (!PPU.ClipWindow1Inside[i])
W1 = ~W1;
if (!PPU.ClipWindow2Inside[i])
W2 = ~W2;
switch (PPU.ClipWindowOverlapLogic[i])
{
case 0: // OR
return (W1 | W2);
case 1: // AND
return (W1 & W2);
case 2: // XOR
return (W1 ^ W2);
case 3: // XNOR
return (~(W1 ^ W2));
}
}
}
// Never get here
return (0);
}
static inline void StoreWindowRegions (uint8 Mask, struct ClipData *Clip, int n_regions, int16 *windows, uint8 *drawing_modes, bool8 sub, bool8 StoreMode0)
{
int ct = 0;
for (int j = 0; j < n_regions; j++)
{
int DrawMode = drawing_modes[j];
if (sub)
DrawMode |= 1;
if (Mask & (1 << j))
DrawMode = 0;
if (!StoreMode0 && !DrawMode)
continue;
if (ct > 0 && Clip->Right[ct - 1] == windows[j] && Clip->DrawMode[ct - 1] == DrawMode)
Clip->Right[ct - 1] = windows[j + 1]; // This region borders with and has the same drawing mode as the previous region: merge them.
else
{
// Add a new region to the BG
Clip->Left[ct] = windows[j];
Clip->Right[ct] = windows[j + 1];
Clip->DrawMode[ct] = DrawMode;
ct++;
}
}
Clip->Count = ct;
}
static inline void ComputeClipWindows (void)
{
int16 windows[6] = { 0, 256, 256, 256, 256, 256 };
uint8 drawing_modes[5] = { 0, 0, 0, 0, 0 };
int n_regions = 1;
int i, j;
// Calculate window regions. We have at most 5 regions, because we have 6 control points
// (screen edges, window 1 left & right, and window 2 left & right).
if (PPU.Window1Left <= PPU.Window1Right)
{
if (PPU.Window1Left > 0)
{
windows[2] = 256;
windows[1] = PPU.Window1Left;
n_regions = 2;
}
if (PPU.Window1Right < 255)
{
windows[n_regions + 1] = 256;
windows[n_regions] = PPU.Window1Right + 1;
n_regions++;
}
}
if (PPU.Window2Left <= PPU.Window2Right)
{
for (i = 0; i <= n_regions; i++)
{
if (PPU.Window2Left == windows[i])
break;
if (PPU.Window2Left < windows[i])
{
for (j = n_regions; j >= i; j--)
windows[j + 1] = windows[j];
windows[i] = PPU.Window2Left;
n_regions++;
break;
}
}
for (; i <= n_regions; i++)
{
if (PPU.Window2Right + 1 == windows[i])
break;
if (PPU.Window2Right + 1 < windows[i])
{
for (j = n_regions; j >= i; j--)
windows[j + 1] = windows[j];
windows[i] = PPU.Window2Right + 1;
n_regions++;
break;
}
}
}
// Get a bitmap of which regions correspond to each window.
const uint8 region_map[6][6] =
{
{ 0, 0x01, 0x03, 0x07, 0x0f, 0x1f },
{ 0, 0, 0x02, 0x06, 0x0e, 0x1e },
{ 0, 0, 0, 0x04, 0x0c, 0x1c },
{ 0, 0, 0, 0, 0x08, 0x18 },
{ 0, 0, 0, 0, 0, 0x10 }
};
uint8 W1 = 0;
uint8 W2 = 0;
if (PPU.Window1Left <= PPU.Window1Right)
{
for (i = 0; windows[i] != PPU.Window1Left; i++) ;
for (j = i; windows[j] != PPU.Window1Right + 1; j++) ;
W1 = region_map[i][j];
}
if (PPU.Window2Left <= PPU.Window2Right)
{
for (i = 0; windows[i] != PPU.Window2Left; i++) ;
for (j = i; windows[j] != PPU.Window2Right + 1; j++) ;
W2 = region_map[i][j];
}
// Color Window affects the drawing mode for each region.
// Modes are: 3=Draw as normal, 2=clip color (math only), 1=no math (draw only), 0=nothing.
uint8 CW_color = 0, CW_math = 0;
uint8 CW = CalcWindowMask(5, W1, W2);
switch (Memory.PPU_IO[0x130] & 0xc0)
{
case 0x00: CW_color = 0; break;
case 0x40: CW_color = ~CW; break;
case 0x80: CW_color = CW; break;
case 0xc0: CW_color = 0xff; break;
}
switch (Memory.PPU_IO[0x130] & 0x30)
{
case 0x00: CW_math = 0; break;
case 0x10: CW_math = ~CW; break;
case 0x20: CW_math = CW; break;
case 0x30: CW_math = 0xff; break;
}
for (int i = 0; i < n_regions; i++)
{
if (!(CW_color & (1 << i)))
drawing_modes[i] |= 1;
if (!(CW_math & (1 << i)))
drawing_modes[i] |= 2;
}
// Store backdrop clip window (draw everywhere color window allows)
StoreWindowRegions(0, &IPPU.Clip[0][5], n_regions, windows, drawing_modes, FALSE, TRUE);
StoreWindowRegions(0, &IPPU.Clip[1][5], n_regions, windows, drawing_modes, TRUE, TRUE);
// Store per-BG and OBJ clip windows
for (int j = 0; j < 5; j++)
{
if (Memory.PPU_IO[0x12e] & (1 << j))
StoreWindowRegions(CalcWindowMask(j, W1, W2), &IPPU.Clip[0][j], n_regions, windows, drawing_modes, 0, FALSE);
else
StoreWindowRegions(0, &IPPU.Clip[0][j], n_regions, windows, drawing_modes, 0, FALSE);
if (Memory.PPU_IO[0x12f] & (1 << j))
StoreWindowRegions(CalcWindowMask(j, W1, W2), &IPPU.Clip[1][j], n_regions, windows, drawing_modes, 1, FALSE);
else
StoreWindowRegions(0, &IPPU.Clip[1][j], n_regions, windows, drawing_modes, 1, FALSE);
}
}
bool8 S9xGraphicsInit (void)
{
GFX.PPL = SNES_WIDTH;
GFX.ScreenSize = GFX.PPL * SNES_HEIGHT_EXTENDED;
IPPU.OBJChanged = TRUE;
Settings.BG_Forced = 0;
S9xInitTileRenderer();
GFX.Screen = (SNESPixel*)emu_LineBuffer(0)+(emu_LineWidth()-256)/2;
//GFX.SubScreen = GFX.Screen;
GFX.SubScreen = (SNESPixel *) emu_SMalloc(GFX.ScreenSize * sizeof(SNESPixel));
GFX.ZBuffer = (uint8 *) emu_Malloc(GFX.ScreenSize);
GFX.SubZBuffer = (uint8 *) emu_Malloc(GFX.ScreenSize);
GFX.ZERO = (SNESPixel *) GFX.SubScreen; // This will cause garbage but for now it's okay
IPPU.TileCacheData = (uint8 *) emu_SMalloc(4096 * 64);
if (!GFX.SubScreen || !GFX.ZBuffer || !GFX.SubZBuffer || !IPPU.TileCacheData)
{
S9xGraphicsDeinit();
return (FALSE);
}
#if 0 // TO DO: pre-compute GFX.ZERO
// Lookup table for 1/2 color subtraction
memset(GFX.ZERO, 0, 0x10000 * sizeof(uint16));
for (uint8 r = 0; r <= MAX_RED; r++)
{
uint8 r2 = (r & 0x10) ? (r & ~0x10) : (0);
for (uint8 g = 0; g <= MAX_GREEN; g++)
{
uint8 g2 = (g & GREEN_HI_BIT) ? (g & ~GREEN_HI_BIT) : (0);
for (uint8 b = 0; b <= MAX_BLUE; b++)
{
uint8 b2 = (b & 0x10) ? (b & ~0x10) : (0);
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);
}
}
}
#endif
return (TRUE);
}
void S9xGraphicsDeinit (void)
{
if (GFX.SubScreen) { emu_SFree(GFX.SubScreen); GFX.SubScreen = NULL; }
if (GFX.ZBuffer) { emu_Free(GFX.ZBuffer); GFX.ZBuffer = NULL; }
if (GFX.SubZBuffer) { emu_SFree(GFX.SubZBuffer); GFX.SubZBuffer = NULL; }
if (IPPU.TileCacheData) { emu_SFree(IPPU.TileCacheData); IPPU.TileCacheData = NULL; }
}
void S9xGraphicsScreenResize (void)
{
IPPU.RenderedScreenHeight = PPU.ScreenHeight;
IPPU.RenderedScreenWidth = SNES_WIDTH;
IPPU.MaxBrightness = PPU.Brightness;
IPPU.Interlace = Memory.PPU_IO[0x133] & 1;
IPPU.InterlaceOBJ = Memory.PPU_IO[0x133] & 2;
IPPU.PseudoHires = Memory.PPU_IO[0x133] & 8;
static uint8 prev2133 = 0;
if (prev2133 != Memory.PPU_IO[0x133])
{
sprintf(String, "Int=%d IntOBJ=%d Hires=%d\n", IPPU.Interlace, IPPU.InterlaceOBJ, IPPU.PseudoHires);
S9xMessage(0, 0, String);
prev2133 = Memory.PPU_IO[0x133];
}
}
void S9xStartScreenRefresh (void)
{
if (IPPU.RenderThisFrame)
{
S9xGraphicsScreenResize();
IPPU.RenderedFramesCount++;
PPU.MosaicStart = 0;
PPU.RecomputeClipWindows = TRUE;
IPPU.PreviousLine = IPPU.CurrentLine = 0;
memset(GFX.ZBuffer, 0, GFX.ScreenSize);
memset(GFX.SubZBuffer, 0, GFX.ScreenSize);
}
if (++IPPU.FrameCount % Settings.FrameRate == 0)
{
IPPU.DisplayedRenderedFrameCount = IPPU.RenderedFramesCount;
IPPU.RenderedFramesCount = 0;
IPPU.FrameCount = 0;
}
if (GFX.InfoStringTimeout > 0 && --GFX.InfoStringTimeout == 0)
GFX.InfoString = NULL;
IPPU.TotalEmulatedFrames++;
}
void S9xEndScreenRefresh (void)
{
if (IPPU.RenderThisFrame)
{
FLUSH_REDRAW();
//S9xDisplayMessages(GFX.Screen, GFX.PPL, IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight, 1);
S9xBlitUpdate(IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight);
}
#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
}
void S9xRenderLine (int 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)
{
S9UpdateLineMatrix(C);
}
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)
SetupOBJ();
PPU.RangeTimeOver |= GFX.OBJLines[C].RTOFlags;
}
}
void S9xUpdateScreen (void)
{
if (IPPU.OBJChanged)
SetupOBJ();
// XXX: Check ForceBlank? Or anything else?
PPU.RangeTimeOver |= GFX.OBJLines[GFX.EndY].RTOFlags;
GFX.StartY = IPPU.PreviousLine;
if ((GFX.EndY = IPPU.CurrentLine - 1) >= PPU.ScreenHeight)
GFX.EndY = PPU.ScreenHeight - 1;
if (!PPU.ForcedBlanking)
{
// If force blank, may as well completely skip all this. We only did
// the OBJ because (AFAWK) the RTO flags are updated even during force-blank.
if (PPU.RecomputeClipWindows)
{
ComputeClipWindows();
PPU.RecomputeClipWindows = FALSE;
}
if ((Memory.PPU_IO[0x130] & 0x30) != 0x30 && (Memory.PPU_IO[0x131] & 0x3f))
GFX.FixedColour = BUILD_PIXEL(IPPU.XB[PPU.FixedColourRed], IPPU.XB[PPU.FixedColourGreen], IPPU.XB[PPU.FixedColourBlue]);
if (PPU.BGMode == 5 || PPU.BGMode == 6 || IPPU.PseudoHires ||
((Memory.PPU_IO[0x130] & 0x30) != 0x30 && (Memory.PPU_IO[0x130] & 2) && (Memory.PPU_IO[0x131] & 0x3f) && (Memory.PPU_IO[0x12d] & 0x1f)))
// If hires (Mode 5/6 or pseudo-hires) or math is to be done
// involving the subscreen, then we need to render the subscreen...
RenderScreen(TRUE);
RenderScreen(FALSE);
}
// else
// {
// GFX.S = (SNESPixel*)GFX.Screen + GFX.StartY * GFX.PPL;
// // Set to black
// for (int l = GFX.StartY; l <= GFX.EndY; l++, GFX.S += GFX.PPL) {
// memset(GFX.S, 0xff, IPPU.RenderedScreenWidth * sizeof(SNESPixel));
// }
// }
IPPU.PreviousLine = IPPU.CurrentLine;
}
void S9xSetInfoString (const char *string)
{
if (Settings.InitialInfoStringTimeout > 0)
{
GFX.InfoString = string;
GFX.InfoStringTimeout = Settings.InitialInfoStringTimeout;
if (Settings.Paused)
S9xBlitUpdate(IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight);
}
}
/*
static inline void DisplayChar (uint16 *s, uint8 c)
{
const uint16 black = BUILD_PIXEL(0, 0, 0);
int line = ((c - 32) >> 4) * FONT_HEIGHT;
int offset = ((c - 32) & 15) * FONT_WIDTH;
for (int h = 0; h < FONT_HEIGHT; h++, line++, s += GFX.PPL - FONT_WIDTH)
{
for (int w = 0; w < FONT_WIDTH; w++, s++)
{
char p = font[line][offset + w];
if (p == '#')
*s = Settings.DisplayColor;
else
if (p == '.')
*s = black;
}
}
}
static inline void DisplayStringFromBottom (const char *string, int linesFromBottom, int pixelsFromLeft, bool allowWrap)
{
if (linesFromBottom <= 0)
linesFromBottom = 1;
uint16 *dst = GFX.Screen + (IPPU.RenderedScreenHeight - FONT_HEIGHT * linesFromBottom) * GFX.PPL + pixelsFromLeft;
int len = strlen(string);
int max_chars = IPPU.RenderedScreenWidth / (FONT_WIDTH - 1);
int char_count = 0;
for (int i = 0 ; i < len ; i++, char_count++)
{
if (char_count >= max_chars || (uint8) string[i] < 32)
{
if (!allowWrap)
break;
dst += FONT_HEIGHT * GFX.PPL - (FONT_WIDTH - 1) * max_chars;
if (dst >= GFX.Screen + IPPU.RenderedScreenHeight * GFX.PPL)
break;
char_count -= max_chars;
}
if ((uint8) string[i] < 32)
continue;
DisplayChar(dst, string[i]);
dst += FONT_WIDTH - 1;
}
}
*/
void S9xDisplayMessages (SNESPixel *screen, int ppl, int width, int height, int scale)
{
// if (GFX.InfoString && *GFX.InfoString)
// DisplayStringFromBottom(GFX.InfoString, 5, 1, true);
}