BizHawk/waterbox/ss/vdp2_render.cpp

3354 lines
104 KiB
C++

/******************************************************************************/
/* Mednafen Sega Saturn Emulation Module */
/******************************************************************************/
/* vdp2_render.cpp - VDP2 Rendering
** Copyright (C) 2016 Mednafen Team
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License
** as published by the Free Software Foundation; either version 2
** of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software Foundation, Inc.,
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// TODO: 31KHz monitor mode.
// When implementing GetRegister(), remember RPRCTL and window x start and end registers can
// change outside of direct register writes
// Ignore T4-T7 in hires and 31KHz monitor mode.
#include "ss.h"
// #include <mednafen/Time.h>
#include "vdp2_common.h"
#include "vdp2_render.h"
#include <array>
#include <atomic>
#include <algorithm>
// "Array" is a bit of a misnomer, but it helps avoid confusion with memset() semantics hopefully.
static INLINE void MDFN_FastArraySet(uint64* const dst, const uint64 value, const size_t count)
{
#if defined(ARCH_X86) && defined(__x86_64__)
{
uint32 dummy_output0, dummy_output1;
asm volatile(
"cld\n\t"
"rep stosq\n\t"
: "=D" (dummy_output0), "=c" (dummy_output1)
: "a" (value), "D" (dst), "c" (count)
: "cc", "memory");
}
#else
for(uint64 *ai = dst; MDFN_LIKELY(ai != (dst + count)); ai++)
MDFN_ennsb<uint64, true>(ai, value);
#endif
}
static INLINE void MDFN_FastArraySet(uint32* const dst, const uint32 value, const size_t count)
{
#if defined(ARCH_X86) && !defined(__x86_64__)
{
uint32 dummy_output0, dummy_output1;
asm volatile(
"cld\n\t"
"rep stosl\n\t"
: "=D" (dummy_output0), "=c" (dummy_output1)
: "a" (value), "D" (dst), "c"(count)
: "cc", "memory");
return;
}
#else
if(0 == ((uintptr_t)dst & (sizeof(uint64) - 1)) && !(count & 1))
MDFN_FastArraySet((uint64*)dst, value | ((uint64)value << 32), count >> 1);
else
{
for(uint32 *ai = dst; MDFN_LIKELY(ai != (dst + count)); ai++)
MDFN_ennsb<uint32, true>(ai, value);
}
#endif
}
static INLINE void MDFN_FastArraySet(uint16* const dst, const uint16 value, const size_t count)
{
if(0 == ((uintptr_t)dst & (sizeof(uint32) - 1)) && !(count & 1))
MDFN_FastArraySet((uint32*)dst, value | (value << 16), count >> 1);
else
{
for(uint16 *ai = dst; MDFN_LIKELY(ai != (dst + count)); ai++)
MDFN_ennsb<uint16, true>(ai, value);
}
}
static INLINE void MDFN_FastArraySet(uint8* const dst, const uint16 value, const size_t count)
{
if(0 == ((uintptr_t)dst & (sizeof(uint16) - 1)) && !(count & 1))
MDFN_FastArraySet((uint16*)dst, value | (value << 8), count >> 1);
else
{
for(uint8 *ai = dst; MDFN_LIKELY(ai != (dst + count)); ai++)
*ai = value;
}
}
namespace MDFN_IEN_SS
{
//uint8 vdp2rend_prepad_bss
static EmulateSpecStruct* espec = NULL;
static bool PAL;
bool CorrectAspect;
bool ShowHOverscan;
bool DoHBlend;
int LineVisFirst, LineVisLast;
static uint32 OutLineCounter;
static bool Clock28M;
static unsigned VisibleLines;
static VDP2Rend_LIB LIB[256];
static uint16 VRAM[262144];
static uint16 CRAM[2048];
static uint8 HRes, VRes;
static bool BorderMode;
static uint8 InterlaceMode;
enum { IM_NONE, IM_ILLEGAL, IM_SINGLE, IM_DOUBLE };
static bool CRKTE;
static uint8 CRAM_Mode;
enum
{
CRAM_MODE_RGB555_1024 = 0,
CRAM_MODE_RGB555_2048 = 1,
CRAM_MODE_RGB888_1024 = 2,
CRAM_MODE_ILLEGAL = 3
};
static uint8 VRAM_Mode;
static uint8 RDBS_Mode;
static uint8 VCPRegs[4][8];
static const uint16 DummyTileNT[8 * 8 * 4 / sizeof(uint16)] = { 0 };
static uint32 UserLayerEnableMask;
//
//
//
static uint16 BGON;
static uint16 MZCTL;
static uint8 MosaicVCount;
static uint8 SFSEL;
static uint16 SFCODE;
static uint16 CHCTLA;
static uint16 CHCTLB;
static uint16 BMPNA;
static uint8 BMPNB;
static uint16 PNCN[4];
static uint16 PNCNR;
static uint16 PLSZ;
static uint16 MPOFN;
static uint16 MPOFR;
static uint8 MapRegs[4][4];
static uint8 RotMapRegs[2][16];
//
static uint16 XScrollI[4], YScrollI[4];
static uint8 XScrollF[2], YScrollF[2];
static uint16 ZMCTL;
static uint16 SCRCTL;
static uint32 LineScrollAddr[2];
static uint32 VCScrollAddr;
static uint32 VCLast[2];
static uint16 XCoordInc[2], YCoordInc[2];
static uint32 YCoordAccum[2];
static uint32 MosEff_YCoordAccum[2];
static uint32 CurXScrollIF[2];
static uint32 CurYScrollIF[2];
static uint16 CurXCoordInc[2];
static uint32 CurLSA[2];
static uint16 NBG23_YCounter[2];
static uint16 MosEff_NBG23_YCounter[2];
//
static uint8 RPMD;
static uint8 KTCTL[2];
static uint16 OVPNR[2];
//
static uint32 BKTA;
static uint32 CurBackTabAddr;
static uint16 CurBackColor;
static uint32 LCTA;
static uint32 CurLCTabAddr;
static uint16 CurLCColor;
static uint8 LineColorEn;
static uint16 SFPRMD;
static uint16 CCCTL;
static uint16 SFCCMD;
//
static uint8 NBGPrioNum[4];
static uint8 RBG0PrioNum;
static uint8 NBGCCRatio[4];
static uint8 RBG0CCRatio;
static uint8 LineColorCCRatio;
static uint8 BackCCRatio;
//
static struct
{
uint16 XStart, XEnd;
uint16 YStart, YEnd;
uint32 LineWinAddr;
bool LineWinEn;
//
bool YMet;
uint16 CurXStart, CurXEnd;
uint32 CurLineWinAddr;
} Window[2];
static uint8 WinControl[8];
enum
{
WINLAYER_NBG0 = 0,
WINLAYER_NBG1 = 1,
WINLAYER_NBG2 = 2,
WINLAYER_NBG3 = 3,
WINLAYER_RBG0 = 4,
WINLAYER_SPRITE = 5,
WINLAYER_ROTPARAM = 6,
WINLAYER_CC = 7,
};
static std::array<unsigned, 5> WinPieces;
//
static uint8 SpriteCCCond;
static uint8 SpriteCCNum;
static uint8 SPCTL_Low;
static uint16 SDCTL;
static uint8 SpritePrioNum[8];
static uint8 SpriteCCRatio[8];
static uint8 SpriteCCLUT[8]; // Temp optimization data
static uint8 SpriteCC3Mask; // Temp optimization data
//
static uint8 CRAMAddrOffs_NBG[4];
static uint8 CRAMAddrOffs_RBG0;
static uint8 CRAMAddrOffs_Sprite;
//
static uint8 ColorOffsEn;
static uint8 ColorOffsSel;
//enum
//{
// COLOFFS_ENSEL_NBG0 = 0,
// COLOFFS_ENSEL_NBG1 = 0,
//};
static int32 ColorOffs[2][3]; // [A,B] [R << 0, G << 8, B << 16]
template<bool IsRot>
struct TileFetcher
{
unsigned CRAOffs;
bool BMSCC;
bool BMSPR;
unsigned BMPalNo;
unsigned BMSize;
unsigned PlaneSize;
unsigned PlaneOver;
uint16 PlaneOverChar;
bool PNDSize;
bool CharSize;
bool AuxMode;
unsigned Supp;
//
//
//
unsigned BMOffset;
unsigned BMWShift;
unsigned BMWMask;
unsigned BMHMask;
uint32 adj_map_regs[IsRot ? 16 : 4];
uint32 doxm, doym;
bool nt_ok[4];
bool cg_ok[4];
// n=0...3, NBG0...3
// n=4, RBG0
// n=5, RBG1
INLINE void Start(const unsigned n, const bool bmen, const unsigned map_offset, const uint8* map_regs)
{
BMOffset = map_offset << 16;
BMWShift = ((BMSize & 2) ? 10 : 9);
BMWMask = (1U << BMWShift) - 8;
BMHMask = (BMSize & 1) ? 0x1FF : 0xFF;
const unsigned psshift = (13 - PNDSize - (CharSize << 1));
for(unsigned i = 0; i < (IsRot ? 16 : 4); i++)
{
adj_map_regs[i] = ((map_offset << 6) + (map_regs[i] &~ PlaneSize)) << psshift;
}
if(IsRot)
{
if(bmen)
{
doxm = ~(BMWMask + 7);
doym = ~BMHMask;
}
else
{
doxm = ~((1U << ((9 + (bool)(PlaneSize & 0x1)) + (IsRot ? 2 : 1))) - 1);
doym = ~((1U << ((9 + (bool)(PlaneSize & 0x2)) + (IsRot ? 2 : 1))) - 1);
}
if(PlaneOver == 0)
doxm = doym = 0;
else if(PlaneOver == 3)
doxm = doym = ~511;
}
// Kludgeyness:
for(unsigned bank = 0; bank < 4; bank++)
{
const unsigned esb = bank & (2 | ((VRAM_Mode >> (bank >> 1)) & 1));
const uint8 rdbs = (RDBS_Mode >> (esb << 1)) & 0x3;
if(IsRot)
{
if(!(BGON & 0x20) || n == 4)
{
nt_ok[bank] = (rdbs == RDBS_NAME) && (bank < 2 || !(BGON & 0x20));
cg_ok[bank] = (rdbs == RDBS_CHAR) && (bank < 2 || !(BGON & 0x20));
}
else
{
nt_ok[bank] = (bank == 3);
cg_ok[bank] = (bank == 2);
}
}
else
{
nt_ok[bank] = false;
cg_ok[bank] = false;
if(!(BGON & 0x30) || rdbs == RDBS_UNUSED)
{
for(unsigned ac = 0; ac < ((HRes & 0x6) ? 4 : 8); ac++)
{
if(VCPRegs[esb][ac] == (VCP_NBG0_CG + n))
cg_ok[bank] = true;
if(VCPRegs[esb][ac] == (VCP_NBG0_NT + n))
nt_ok[bank] = true;
}
}
}
}
#if 1
pcco = 0;
spr = false;
scc = false;
tile_vrb = nullptr;
cellx_xor = 0;
#endif
}
//
//
//
uint32 pcco;
bool spr;
bool scc;
const uint16* tile_vrb;
uint32 cellx_xor;
template<unsigned TA_bpp>
INLINE bool Fetch(const bool bmen, const uint32 ix, const uint32 iy)
{
size_t cg_addr;
uint32 palno;
bool is_outside = false;
if(IsRot)
is_outside = (ix & doxm) | (iy & doym);
if(bmen)
{
palno = BMPalNo;
spr = BMSPR;
scc = BMSCC;
cellx_xor = (ix &~ 0x7);
cg_addr = (BMOffset + ((((ix & BMWMask) + ((iy & BMHMask) << BMWShift)) * TA_bpp) >> 4)) & 0x3FFFF;
}
else
{
bool vflip, hflip;
uint16 charno;
uint32 mapidx, planeidx, planeoffs, pageoffs;
const uint16* pnd;
uint32 celly;
size_t nt_addr;
if(IsRot)
mapidx = ((ix >> (9 + (bool)(PlaneSize & 0x1))) & 0x3) | ((iy >> (9 + (bool)(PlaneSize & 0x2) - 2)) & 0xC);
else
mapidx = ((ix >> (9 + (bool)(PlaneSize & 0x1))) & 0x1) | ((iy >> (9 + (bool)(PlaneSize & 0x2) - 1)) & 0x2);
planeidx = ((ix >> 9) & PlaneSize & 0x1) | ((iy >> (9 - 1)) & PlaneSize & 0x2);
planeoffs = planeidx << (13 - PNDSize - (CharSize << 1));
pageoffs = ((((ix >> 3) & 0x3F) >> CharSize) + ((((iy >> 3) & 0x3F) >> CharSize) << (6 - CharSize))) << (1 - PNDSize);
nt_addr = (adj_map_regs[mapidx] + planeoffs + pageoffs) & 0x3FFFF;
pnd = &VRAM[nt_addr];
if(!nt_ok[nt_addr >> 16])
pnd = DummyTileNT;
if(IsRot && is_outside && PlaneOver == 1)
{
pnd = &PlaneOverChar;
goto OverCharCase;
}
if(!PNDSize)
{
uint16 tmp = pnd[0];
palno = tmp & 0x7F;
vflip = (bool)(tmp & 0x8000);
hflip = (bool)(tmp & 0x4000);
spr = (bool)(tmp & 0x2000);
scc = (bool)(tmp & 0x1000);
charno = pnd[1] & 0x7FFF;
}
else
{
OverCharCase:;
uint16 tmp = pnd[0];
if(TA_bpp >= 8)
palno = ((tmp >> 12) & 0x7) << 4;
else
palno = ((tmp >> 12) & 0xF) | (((Supp >> 5) & 0x7) << 4);
spr = (bool)(Supp & 0x200);
scc = (bool)(Supp & 0x100);
if(!AuxMode)
{
vflip = (bool)(tmp & 0x800);
hflip = (bool)(tmp & 0x400);
if(CharSize)
charno = ((tmp & 0x3FF) << 2) + ((Supp & 0x1C) << 10) + (Supp & 0x3);
else
charno = (tmp & 0x3FF) + ((Supp & 0x1F) << 10);
}
else
{
hflip = vflip = false;
if(CharSize)
charno = ((tmp & 0xFFF) << 2) + ((Supp & 0x10) << 10) + (Supp & 0x3);
else
charno = (tmp & 0xFFF) + ((Supp & 0x1C) << 10);
}
}
if(CharSize)
{
uint32 cidx = (((ix >> 3) ^ hflip) & 0x1) + (((iy >> 2) ^ (vflip << 1)) & 0x2);
charno = (charno + cidx * (TA_bpp >> 2)) & 0x7FFF;
}
cellx_xor = (ix &~ 0x7) | (hflip ? 0x7 : 0x0);
celly = (iy & 0x7) ^ (vflip ? 0x7 : 0);
cg_addr = ((charno << 4) + ((celly * TA_bpp) >> 1)) & 0x3FFFF;
}
tile_vrb = &VRAM[cg_addr];
if(!cg_ok[cg_addr >> 16])
{
//printf("Goop: %08zx, %d %02x, %016llx %016llx %016llx %016llx\n", cg_addr, TA_bpp, VRAM_Mode, MDFN_de64lsb(VCPRegs[0]), MDFN_de64lsb(VCPRegs[1]), MDFN_de64lsb(VCPRegs[2]), MDFN_de64lsb(VCPRegs[3]));
tile_vrb = DummyTileNT;
}
//
//
//
pcco = ((palno << 4) &~ ((1U << (TA_bpp & 0x1F)) - 1)) + CRAOffs;
return IsRot && is_outside && (PlaneOver & 2);
}
};
struct RotVars
{
int32 Xsp, Ysp;// .10
int32 Xp, Yp; // .10
int32 dX, dY; // .10
int32 kx, ky; // .16
bool use_coeff;
uint32 base_coeff;
TileFetcher<true> tf;
};
static struct
{
uint64 spr[704];
uint64 rbg0[704];
union
{
uint64 nbg[4][8 + 704 + 8];
struct
{
uint8 dummy[sizeof(nbg) / 2];
uint16 vcscr[2][88 + 1 + 1]; // + 1 for fine x scroll != 0, + 1 for pointer shenanigans in FetchVCScroll
};
struct
{
uint8 rotdummy[sizeof(nbg) / 4];
uint8 rotabsel[352]; // Also used as a scratch buffer in T_DrawRBG() to handle mosaic-related junk.
RotVars rotv[2];
uint32 rotcoeff[352];
};
};
alignas(16) uint8 lc[704];
} LB;
// ColorOffsEn, etc. ?...hmm, discrepancy with ColorCalcEn and LineColorEn...
enum
{
LAYER_NBG0 = 0,
//LAYER_RBG1 = 0,
LAYER_NBG1 = 1,
//LAYER_EXBG = 1,
LAYER_NBG2 = 2,
LAYER_NBG3 = 3,
LAYER_RBG0 = 4,
LAYER_BACK = 5, // Line color?
LAYER_SPRITE = 6,
};
//
//
//
static uint32 ColorCache[2048];
static void CacheCRE(const unsigned cri)
{
if(CRAM_Mode & CRAM_MODE_RGB888_1024)
{
(ColorCache + 0x000)[cri >> 1] = (ColorCache + 0x400)[cri >> 1] = (((CRAM + 0x000)[(cri >> 1) & 0x3FF] & 0x80FF) << 16) | ((CRAM + 0x400)[(cri >> 1) & 0x3FF] << 0);
}
else
{
const uint16 t = CRAM[cri & ((CRAM_Mode == CRAM_MODE_RGB555_1024) ? 0x3FF : 0x7FF)];
const uint32 col = ((t << 3) & 0xF8) | ((t << 6) & 0xF800) | ((t << 9) & 0xF80000) | ((t << 16) & 0x80000000);
if(CRAM_Mode == CRAM_MODE_RGB555_1024)
(ColorCache + 0x000)[cri & 0x3FF] = (ColorCache + 0x400)[cri & 0x3FF] = col;
else
ColorCache[cri] = col;
}
}
static void RecalcColorCache(void)
{
if(CRAM_Mode & CRAM_MODE_RGB888_1024)
{
for(unsigned i = 0; i < 2048; i += 2)
CacheCRE(i);
}
else
{
const unsigned count = (CRAM_Mode == CRAM_MODE_RGB555_2048) ? 2048 : 1024;
for(unsigned i = 0; i < count; i++)
CacheCRE(i);
}
}
//
// Register writes seem to always be 16-bit
//
static INLINE void RegsWrite(uint32 A, uint16 V)
{
A &= 0x1FE;
switch(A)
{
default:
break;
case 0x00:
//DisplayOn = (V >> 15) & 0x1;
BorderMode = (V >> 8) & 0x1;
InterlaceMode = (V >> 6) & 0x3;
VRes = (V >> 4) & 0x3;
HRes = (V >> 0) & 0x7;
break;
case 0x02:
//ExLatchEnable = (V >> 9) & 0x1;
//ExSyncEnable = (V >> 8) & 0x1;
//DispAreaSelect = (V >> 1) & 0x1;
//ExBGEnable = (V >> 0) & 0x1;
break;
case 0x0E:
{
const unsigned old_CRAM_Mode = CRAM_Mode;
CRKTE = (V >> 15) & 0x1;
CRAM_Mode = (V >> 12) & 0x3;;
VRAM_Mode = (V >> 8) & 0x3;
RDBS_Mode = V & 0xFF;
if(old_CRAM_Mode != CRAM_Mode)
RecalcColorCache();
}
break;
//
case 0x10:
case 0x12:
case 0x14:
case 0x16:
case 0x18:
case 0x1A:
case 0x1C:
case 0x1E:
{
uint8* const b = &VCPRegs[(A >> 2) & 3][(A & 0x2) << 1];
b[0] = (V >> 12) & 0xF;
b[1] = (V >> 8) & 0xF;
b[2] = (V >> 4) & 0xF;
b[3] = (V >> 0) & 0xF;
}
break;
//
case 0x20:
BGON = V & 0x1F3F;
break;
case 0x22:
MZCTL = V & 0xFF1F;
break;
case 0x24:
SFSEL = V & 0x1F;
break;
case 0x26:
SFCODE = V;
break;
case 0x28:
CHCTLA = V & 0x3F7F;
break;
case 0x2A:
CHCTLB = V & 0x7733;
break;
case 0x2C:
BMPNA = V & 0x3737;
break;
case 0x2E:
BMPNB = V & 0x37;
break;
//
case 0x30:
case 0x32:
case 0x34:
case 0x36:
PNCN[(A & 0x6) >> 1] = V & 0xC3FF;
break;
case 0x38:
PNCNR = V & 0xC3FF;
break;
//
case 0x3A:
PLSZ = V; // Plane size
break;
case 0x3C:
MPOFN = V & 0x7777; // Map offset NBG
break;
case 0x3E:
MPOFR = V & 0x0077; // Map offset RBG
break;
//
case 0x40:
case 0x42:
case 0x44:
case 0x46:
case 0x48:
case 0x4A:
case 0x4C:
case 0x4E:
MapRegs[(A & 0xC) >> 2][(A & 0x2) + 0] = (V >> 0) & 0x3F;
MapRegs[(A & 0xC) >> 2][(A & 0x2) + 1] = (V >> 8) & 0x3F;
break;
case 0x50: case 0x52: case 0x54: case 0x56: case 0x58: case 0x5A: case 0x5C: case 0x5E:
case 0x60: case 0x62: case 0x64: case 0x66: case 0x68: case 0x6A: case 0x6C: case 0x6E:
RotMapRegs[(bool)(A & 0x20)][(A & 0xE) + 0] = (V >> 0) & 0x3F;
RotMapRegs[(bool)(A & 0x20)][(A & 0xE) + 1] = (V >> 8) & 0x3F;
break;
//
case 0x70:
case 0x80:
XScrollI[A >> 7] = V & 0x7FF;
break;
case 0x72:
case 0x82:
XScrollF[A >> 7] = (V >> 8) & 0xFF;
break;
case 0x74:
case 0x84:
YScrollI[A >> 7] = V & 0x7FF;
break;
case 0x76:
case 0x86:
YScrollF[A >> 7] = (V >> 8) & 0xFF;
break;
case 0x78:
case 0x88:
XCoordInc[A >> 7] = (XCoordInc[A >> 7] & 0xFF) | ((V & 0x7) << 8);
break;
case 0x7A:
case 0x8A:
XCoordInc[A >> 7] = (XCoordInc[A >> 7] & 0x700) | ((V >> 8) & 0xFF);
break;
case 0x7C:
case 0x8C:
YCoordInc[A >> 7] = (YCoordInc[A >> 7] & 0xFF) | ((V & 0x7) << 8);
break;
case 0x7E:
case 0x8E:
YCoordInc[A >> 7] = (YCoordInc[A >> 7] & 0x700) | ((V >> 8) & 0xFF);
break;
case 0x90:
case 0x94:
XScrollI[2 + (bool)(A & 0x4)] = V & 0x7FF;
break;
case 0x92:
case 0x96:
{
const unsigned which = (bool)(A & 0x4);
NBG23_YCounter[which] = YScrollI[2 + which] = V & 0x7FF;
}
break;
case 0x98:
ZMCTL = V & 0x0303;
break;
case 0x9A:
SCRCTL = V & 0x3F3F;
break;
case 0x9C:
VCScrollAddr = (VCScrollAddr & 0xFFFF) | ((V & 0x7) << 16);
break;
case 0x9E:
VCScrollAddr = (VCScrollAddr & 0x70000) | (V & 0xFFFE);
break;
case 0xA0:
LineScrollAddr[0] = (LineScrollAddr[0] & 0xFFFF) | ((V & 0x7) << 16);
break;
case 0xA2:
LineScrollAddr[0] = (LineScrollAddr[0] & 0x70000) | (V & 0xFFFE);
break;
case 0xA4:
LineScrollAddr[1] = (LineScrollAddr[1] & 0xFFFF) | ((V & 0x7) << 16);
break;
case 0xA6:
LineScrollAddr[1] = (LineScrollAddr[1] & 0x70000) | (V & 0xFFFE);
break;
//
case 0xA8:
LCTA = (LCTA & 0xFFFF) | ((V & 0x8007) << 16);
break;
case 0xAA:
LCTA = (LCTA & ~0xFFFF) | V;
break;
case 0xAC:
BKTA = (BKTA & 0xFFFF) | ((V & 0x8007) << 16);
break;
case 0xAE:
BKTA = (BKTA & ~0xFFFF) | V;
break;
//
case 0xB0:
RPMD = V & 0x3;
break;
case 0xB4:
KTCTL[0] = (V >> 0) & 0x1F;
KTCTL[1] = (V >> 8) & 0x1F;
break;
case 0xB8:
OVPNR[0] = V;
break;
case 0xBA:
OVPNR[1] = V;
break;
//
case 0xC0: Window[0].XStart = V & 0x3FF; break;
case 0xC2: Window[0].YStart = V & 0x1FF; break;
case 0xC4: Window[0].XEnd = V & 0x3FF; break;
case 0xC6: Window[0].YEnd = V & 0x1FF; break;
case 0xC8: Window[1].XStart = V & 0x3FF; break;
case 0xCA: Window[1].YStart = V & 0x1FF; break;
case 0xCC: Window[1].XEnd = V & 0x3FF; break;
case 0xCE: Window[1].YEnd = V & 0x1FF; break;
case 0xD0:
case 0xD2:
case 0xD4:
WinControl[(A & 0x6) + 0] = (V >> 0) & 0xBF;
WinControl[(A & 0x6) + 1] = (V >> 8) & 0xBF;
break;
case 0xD6:
WinControl[(A & 0x6) + 0] = (V >> 0) & 0x8F; // Rot
WinControl[(A & 0x6) + 1] = (V >> 8) & 0xBF; // CC
break;
case 0xD8:
case 0xDC:
{
const unsigned w = (A & 0x4) >> 2;
Window[w].LineWinEn = (bool)(V & 0x8000);
Window[w].LineWinAddr = (Window[w].LineWinAddr & 0xFFFF) | ((V & 0x7) << 16);
}
break;
case 0xDA:
case 0xDE:
{
const unsigned w = (A & 0x4) >> 2;
Window[w].LineWinAddr = (Window[w].LineWinAddr & 0x70000) | (V & 0xFFFE);
}
break;
//
case 0xE0:
SpriteCCCond = (V >> 12) & 0x3;
SpriteCCNum = (V >> 8) & 0x7;
SPCTL_Low = V & 0x3F;
break;
case 0xE2:
SDCTL = V & 0x13F;
break;
case 0xE4:
CRAMAddrOffs_NBG[0] = (V >> 0) & 0x7;
CRAMAddrOffs_NBG[1] = (V >> 4) & 0x7;
CRAMAddrOffs_NBG[2] = (V >> 8) & 0x7;
CRAMAddrOffs_NBG[3] = (V >> 12) & 0x7;
break;
case 0xE6:
CRAMAddrOffs_RBG0 = (V >> 0) & 0x7;
CRAMAddrOffs_Sprite = (V >> 4) & 0x7;
break;
case 0xE8:
LineColorEn = V & 0x3F;
break;
case 0xEA:
SFPRMD = V & 0x3FF;
break;
case 0xEC:
CCCTL = V & 0xF77F;
break;
case 0xEE:
SFCCMD = V & 0x3FF;
break;
case 0xF0:
case 0xF2:
case 0xF4:
case 0xF6:
SpritePrioNum[(A & 0x6) + 0] = ((V >> 0) & 0x7);
SpritePrioNum[(A & 0x6) + 1] = ((V >> 8) & 0x7);
break;
case 0xF8:
NBGPrioNum[0] = (V >> 0) & 0x7;
NBGPrioNum[1] = (V >> 8) & 0x7;
break;
case 0xFA:
NBGPrioNum[2] = (V >> 0) & 0x7;
NBGPrioNum[3] = (V >> 8) & 0x7;
break;
case 0xFC:
RBG0PrioNum = (V >> 0) & 0x7;
break;
case 0x100:
case 0x102:
case 0x104:
case 0x106:
SpriteCCRatio[(A & 0x6) + 0] = (V >> 0) & 0x1F;
SpriteCCRatio[(A & 0x6) + 1] = (V >> 8) & 0x1F;
break;
case 0x108:
case 0x10A:
NBGCCRatio[(A & 0x2) + 0] = (V >> 0) & 0x1F;
NBGCCRatio[(A & 0x2) + 1] = (V >> 8) & 0x1F;
break;
case 0x10C:
RBG0CCRatio = V & 0x1F;
break;
case 0x10E:
LineColorCCRatio = (V >> 0) & 0x1F;
BackCCRatio = (V >> 8) & 0x1F;
break;
case 0x110:
ColorOffsEn = V & 0x7F;
break;
case 0x112:
ColorOffsSel = V & 0x7F;
break;
case 0x114: // A Red
case 0x116: // A Green
case 0x118: // A Blue
case 0x11A: // B Red
case 0x11C: // B Green
case 0x11E: // B Blue
{
const unsigned ab = (A >= 0x11A);
const unsigned wcc = ((A - 0x114) >> 1) % 3;
ColorOffs[ab][wcc] = (uint32)sign_x_to_s32(9, V) << (wcc << 3);
}
break;
}
}
template<typename T>
static INLINE void MemW(uint32 A, const uint16 DB)
{
A &= 0x1FFFFF;
//
// VRAM
//
if(A < 0x100000)
{
const size_t vri = (A & 0x7FFFF) >> 1;
const unsigned mask = (sizeof(T) == 2) ? 0xFFFF : (0xFF00 >> ((A & 1) << 3));
VRAM[vri] = (VRAM[vri] &~ mask) | (DB & mask);
return;
}
//
// CRAM
//
if(A < 0x180000)
{
const unsigned cri = (A & 0xFFF) >> 1;
switch(CRAM_Mode)
{
case CRAM_MODE_RGB555_1024:
(CRAM + 0x000)[cri & 0x3FF] = DB;
(CRAM + 0x400)[cri & 0x3FF] = DB;
CacheCRE(cri);
break;
case CRAM_MODE_RGB555_2048:
CRAM[cri] = DB;
CacheCRE(cri);
break;
case CRAM_MODE_RGB888_1024:
case CRAM_MODE_ILLEGAL:
default:
CRAM[((cri >> 1) & 0x3FF) | ((cri & 1) << 10)] = DB;
CacheCRE(cri);
break;
}
return;
}
//
// Registers
//
if(A < 0x1C0000)
{
RegsWrite(A, DB);
return;
}
}
static void Reset(bool powering_up)
{
if(powering_up)
{
memset(VRAM, 0, sizeof(VRAM));
memset(CRAM, 0, sizeof(CRAM));
}
//
//
CRKTE = false;
CRAM_Mode = 0;
VRAM_Mode = 0;
RDBS_Mode = 0;
HRes = 0;
VRes = 0;
BorderMode = false;
InterlaceMode = 0;
//
memset(VCPRegs, 0, sizeof(VCPRegs));
//
BGON = 0;
MZCTL = 0;
MosaicVCount = 0;
SFSEL = 0;
SFCODE = 0;
CHCTLA = 0;
CHCTLB = 0;
BMPNA = 0;
BMPNB = 0;
for(unsigned n = 0; n < 4; n++)
PNCN[n] = 0;
PNCNR = 0;
PLSZ = 0;
MPOFN = 0;
MPOFR = 0;
for(unsigned n = 0; n < 4; n++)
{
for(unsigned i = 0; i < 4; i++)
MapRegs[n][i] = 0;
}
for(unsigned rn = 0; rn < 2; rn++)
{
for(unsigned i = 0; i < 16; i++)
RotMapRegs[rn][i] = 0;
}
//
for(unsigned n = 0; n < 4; n++)
{
XScrollI[n] = 0;
YScrollI[n] = 0;
if(n < 2)
{
XScrollF[n] = 0;
YScrollF[n] = 0;
XCoordInc[n] = 0;
YCoordInc[n] = 0;
YCoordAccum[n] = 0;
MosEff_YCoordAccum[n] = 0;
}
else
{
NBG23_YCounter[n & 1] = 0;
MosEff_NBG23_YCounter[n & 1] = 0;
}
}
ZMCTL = 0;
SCRCTL = 0;
LineScrollAddr[0] = 0;
LineScrollAddr[1] = 0;
VCScrollAddr = 0;
VCLast[0] = VCLast[1] = 0;
for(unsigned n = 0; n < 2; n++)
{
CurXScrollIF[n] = 0;
CurYScrollIF[n] = 0;
CurLSA[n] = 0;
CurXCoordInc[n] = 0;
}
for(unsigned n = 0; n < 4; n++)
NBGPrioNum[n] = 0;
RBG0PrioNum = 0;
for(unsigned n = 0; n < 4; n++)
NBGCCRatio[n] = 0;
RBG0CCRatio = 0;
LineColorCCRatio = 0;
BackCCRatio = 0;
for(unsigned w = 0; w < 2; w++)
{
Window[w].XStart = 0;
Window[w].XEnd = 0;
Window[w].YStart = 0;
Window[w].YEnd = 0;
Window[w].LineWinAddr = 0;
Window[w].LineWinEn = false;
Window[w].YMet = false;
Window[w].CurXStart = 0;
Window[w].CurXEnd = 0;
Window[w].CurLineWinAddr = 0;
}
for(unsigned i = 0; i < 8; i++)
WinControl[i] = 0;
//
RPMD = 0;
for(unsigned i = 0; i < 2; i++)
{
KTCTL[i] = 0;
OVPNR[i] = 0;
}
//
BKTA = 0;
CurBackTabAddr = 0;
CurBackColor = 0;
LCTA = 0;
CurLCTabAddr = 0;
CurLCColor = 0;
LineColorEn = 0;
SFPRMD = 0;
SFCCMD = 0;
CCCTL = 0;
//
SpriteCCCond = 0;
SpriteCCNum = 0;
SPCTL_Low = 0;
SDCTL = 0;
//
for(auto& spn : SpritePrioNum)
spn = 0;
//
for(auto& scr : SpriteCCRatio)
scr = 0;
//
for(auto& ao : CRAMAddrOffs_NBG)
ao = 0;
CRAMAddrOffs_RBG0 = 0;
CRAMAddrOffs_Sprite = 0;
//
ColorOffsEn = 0;
ColorOffsSel = 0;
for(auto& co : ColorOffs)
for(auto& coe : co)
coe = 0;
}
// Prio(3 bits), color calc(1 bit), layer num(3 bits), 1 bit for palette/rgb format, 1 bit for line color enable, 1 bit for color offs enable, 1 bit for color offs select
// 1 bit for line color screen enable?, 1 bit allow sprite shadow, 1 bit do sprite shadow
// Prio, color calc, layer num
enum
{
PIX_ISRGB_SHIFT = 0, // original format, 0 = paletted, 1 = RGB
PIX_LCE_SHIFT = 1, // Line color enable
PIX_COE_SHIFT = 2, // Color offs enable
PIX_COSEL_SHIFT = 3, // Color offset select(which color offset registers to use)
PIX_CCE_SHIFT = 4, // Color calc enable
//
// Sprite shadow nonsense
// Keep these in this order at these bit positions
PIX_SHADEN_SHIFT = 5, //
PIX_DOSHAD_SHIFT = 6,
PIX_SELFSHAD_SHIFT = 7,
PIX_SHADHALVTEST8_VAL = 0x60,
//
//
//
//
// 8 ... 15
PIX_PRIO_TEST_SHIFT = 8,
PIX_PRIO_SHIFT = PIX_PRIO_TEST_SHIFT + 3,
//
PIX_GRAD_SHIFT = 16,
PIX_LAYER_CCE_SHIFT = 17, // For extended color calculation
// 24...31
PIX_CCRATIO_SHIFT = 24,
// 32 ... 55
PIX_RGB_SHIFT = 32,
//
PIX_SWBIT_SHIFT = 56,
// Reminder that highest bit can be == 1 when RGB data is pulled from ColorCache
//SPECIAL_CCALC_SHIFT = 63
};
static INLINE void GetCWV(const uint8 ctrl, const bool* const xmet, bool* cwv)
{
const bool logic = (ctrl >> 7) & 1; // 0 = OR, 1 = AND
const bool w_enable[2] = { (bool)(ctrl & 0x02), (bool)(ctrl & 0x08) };
const bool w_area[2] = { (bool)(ctrl & 0x01), (bool)(ctrl & 0x04) };
const bool sw_enable = ctrl & 0x20;
const bool sw_area = ctrl & 0x10;
for(unsigned swinput = 0; swinput < 2; swinput++)
{
bool wval[2];
bool swval;
wval[0] = (w_enable[0] ? ((xmet[0] & Window[0].YMet) ^ w_area[0]) : logic);
wval[1] = (w_enable[1] ? ((xmet[1] & Window[1].YMet) ^ w_area[1]) : logic);
swval = sw_enable ? (swinput ^ sw_area) : logic;
if(logic)
cwv[swinput] = wval[0] & wval[1] & swval;
else
cwv[swinput] = wval[0] | wval[1] | swval;
}
}
static void GetWinRotAB(void)
{
unsigned x = 0;
for(unsigned piece = 0; piece < WinPieces.size(); piece++)
{
bool xmet[2];
xmet[0] = ((x >= Window[0].CurXStart) & (x <= Window[0].CurXEnd));
xmet[1] = ((x >= Window[1].CurXStart) & (x <= Window[1].CurXEnd));
//
//
//
bool cwv[2];
GetCWV(WinControl[WINLAYER_ROTPARAM], xmet, cwv);
if(HRes & 0x2)
{
for(; MDFN_LIKELY(x < WinPieces[piece]); x += 2)
LB.rotabsel[x >> 1] = cwv[(LB.spr[x] >> PIX_SWBIT_SHIFT) & 1];
}
else
{
for(; MDFN_LIKELY(x < WinPieces[piece]); x++)
LB.rotabsel[x] = cwv[(LB.spr[x] >> PIX_SWBIT_SHIFT) & 1];
}
}
}
static void ApplyWin(const unsigned wlayer, uint64* buf)
{
unsigned x = 0;
//printf("%d %d %d %d %d --- %d %d\n", WinPieces[0], WinPieces[1], WinPieces[2], WinPieces[3], WinPieces[4], Window[0].CurXStart, Window[0].CurXEnd);
for(unsigned piece = 0; piece < WinPieces.size(); piece++)
{
bool xmet[2];
xmet[0] = ((x >= Window[0].CurXStart) & (x <= Window[0].CurXEnd));
xmet[1] = ((x >= Window[1].CurXStart) & (x <= Window[1].CurXEnd));
//
//
//
bool cwv[2];
bool cc_cwv[2];
GetCWV(WinControl[wlayer], xmet, cwv);
GetCWV(WinControl[WINLAYER_CC], xmet, cc_cwv);
if(!((cwv[0] ^ cwv[1]) | (cc_cwv[0] ^ cc_cwv[1]))) // Fast path(no sprite window, or sprite window wouldn't have an effect in this piece).
{
if(cwv[0])
{
for(; MDFN_LIKELY(x < WinPieces[piece]); x++)
buf[x] &= ~(uint64)0xFFFFFFFF;
}
else if(cc_cwv[0])
{
for(; MDFN_LIKELY(x < WinPieces[piece]); x++)
buf[x] &= ~(uint64)(1U << PIX_CCE_SHIFT);
}
x = WinPieces[piece];
}
else
{
uint64 masks[2];
for(unsigned i = 0; i < 2; i++)
{
uint64 m = ~(uint64)0;
if(cwv[i])
m = ~(uint64)0xFFFFFFFF;
if(cc_cwv[i])
m &= ~(uint64)(1U << PIX_CCE_SHIFT);
masks[i] = m;
}
for(; MDFN_LIKELY(x < WinPieces[piece]); x++)
{
buf[x] &= masks[(LB.spr[x] >> PIX_SWBIT_SHIFT) & 1];
}
}
}
}
#pragma GCC push_options
#pragma GCC optimize("no-unroll-loops,no-peel-loops,no-crossjumping")
static NO_INLINE void ApplyHMosaic(const unsigned layer, uint64* buf, const unsigned w)
{
if(!(MZCTL & (1U << layer)))
return;
const unsigned moz_horiz_param = ((MZCTL >> 8) & 0xF);
const unsigned moz_wmax = w - moz_horiz_param;
unsigned x = 0;
switch(moz_horiz_param)
{
case 0x0: x = moz_wmax; break;
case 0x1: for(; x < moz_wmax; x += 0x2) { auto b = buf[x]; buf[x + 1] = b; } break;
case 0x2: for(; x < moz_wmax; x += 0x3) { auto b = buf[x]; buf[x + 1] = b; buf[x + 2] = b; } break;
case 0x3: for(; x < moz_wmax; x += 0x4) { auto b = buf[x]; buf[x + 1] = b; buf[x + 2] = b; buf[x + 3] = b; } break;
case 0x4: for(; x < moz_wmax; x += 0x5) { auto b = buf[x]; buf[x + 1] = b; buf[x + 2] = b; buf[x + 3] = b; buf[x + 4] = b; } break;
case 0x5: for(; x < moz_wmax; x += 0x6) { auto b = buf[x]; buf[x + 1] = b; buf[x + 2] = b; buf[x + 3] = b; buf[x + 4] = b; buf[x + 5] = b; } break;
case 0x6: for(; x < moz_wmax; x += 0x7) { auto b = buf[x]; buf[x + 1] = b; buf[x + 2] = b; buf[x + 3] = b; buf[x + 4] = b; buf[x + 5] = b; buf[x + 6] = b; } break;
case 0x7: for(; x < moz_wmax; x += 0x8) { auto b = buf[x]; buf[x + 1] = b; buf[x + 2] = b; buf[x + 3] = b; buf[x + 4] = b; buf[x + 5] = b; buf[x + 6] = b; buf[x + 7] = b; } break;
case 0x8: for(; x < moz_wmax; x += 0x9) { auto b = buf[x]; buf[x + 1] = b; buf[x + 2] = b; buf[x + 3] = b; buf[x + 4] = b; buf[x + 5] = b; buf[x + 6] = b; buf[x + 7] = b; buf[x + 8] = b; } break;
case 0x9: for(; x < moz_wmax; x += 0xA) { auto b = buf[x]; buf[x + 1] = b; buf[x + 2] = b; buf[x + 3] = b; buf[x + 4] = b; buf[x + 5] = b; buf[x + 6] = b; buf[x + 7] = b; buf[x + 8] = b; buf[x + 9] = b; } break;
case 0xA: for(; x < moz_wmax; x += 0xB) { auto b = buf[x]; buf[x + 1] = b; buf[x + 2] = b; buf[x + 3] = b; buf[x + 4] = b; buf[x + 5] = b; buf[x + 6] = b; buf[x + 7] = b; buf[x + 8] = b; buf[x + 9] = b; buf[x + 10] = b; } break;
case 0xB: for(; x < moz_wmax; x += 0xC) { auto b = buf[x]; buf[x + 1] = b; buf[x + 2] = b; buf[x + 3] = b; buf[x + 4] = b; buf[x + 5] = b; buf[x + 6] = b; buf[x + 7] = b; buf[x + 8] = b; buf[x + 9] = b; buf[x + 10] = b; buf[x + 11] = b; } break;
case 0xC: for(; x < moz_wmax; x += 0xD) { auto b = buf[x]; buf[x + 1] = b; buf[x + 2] = b; buf[x + 3] = b; buf[x + 4] = b; buf[x + 5] = b; buf[x + 6] = b; buf[x + 7] = b; buf[x + 8] = b; buf[x + 9] = b; buf[x + 10] = b; buf[x + 11] = b; buf[x + 12] = b; } break;
case 0xD: for(; x < moz_wmax; x += 0xE) { auto b = buf[x]; buf[x + 1] = b; buf[x + 2] = b; buf[x + 3] = b; buf[x + 4] = b; buf[x + 5] = b; buf[x + 6] = b; buf[x + 7] = b; buf[x + 8] = b; buf[x + 9] = b; buf[x + 10] = b; buf[x + 11] = b; buf[x + 12] = b; buf[x + 13] = b; } break;
case 0xE: for(; x < moz_wmax; x += 0xF) { auto b = buf[x]; buf[x + 1] = b; buf[x + 2] = b; buf[x + 3] = b; buf[x + 4] = b; buf[x + 5] = b; buf[x + 6] = b; buf[x + 7] = b; buf[x + 8] = b; buf[x + 9] = b; buf[x + 10] = b; buf[x + 11] = b; buf[x + 12] = b; buf[x + 13] = b; buf[x + 14] = b; } break;
case 0xF: for(; x < moz_wmax; x += 0x10) { auto b = buf[x]; buf[x + 1] = b; buf[x + 2] = b; buf[x + 3] = b; buf[x + 4] = b; buf[x + 5] = b; buf[x + 6] = b; buf[x + 7] = b; buf[x + 8] = b; buf[x + 9] = b; buf[x + 10] = b; buf[x + 11] = b; buf[x + 12] = b; buf[x + 13] = b; buf[x + 14] = b; buf[x + 15] = b;} break;
}
assert(x <= w);
for(auto b = buf[x]; x < w; x++)
buf[x] = b;
}
#pragma GCC pop_options
//
// NBG0(HRES=0x1):
// Cycle 0: OK
// Cycle 1: OK
//
// Cycle 2:
// [Entry 0] [Entry 0] [Entry 1] [Entry 2]
// Cycle 3 ... 7:
// [Entry 44] [Entry 44] [Entry 0] [Entry 1]
//
static void FetchVCScroll(const unsigned w)
{
const bool vcon[2] = { (bool)(SCRCTL & BGON & !(MZCTL & 0x1)), (bool)((SCRCTL >> 8) & (BGON >> 1) & !(MZCTL & 0x2) & 0x1) };
const unsigned max_cyc = (HRes & 0x6) ? 4 : 8;
const unsigned tc = (w >> 3) + 1;
uint32 tmp[2] = { VCLast[0], VCLast[1] };
uint32 vcaddr = VCScrollAddr & 0x3FFFE;
uint32 base[2];
unsigned inc[8];
uint8 VRMVCPCache[4][8];
for(unsigned bank = 0; bank < 4; bank++)
{
//unsigned brm[4];
//brm[bank] = bank & (2 | ((VRAM_Mode >> (bank >> 1)) & 1));
const unsigned esb = bank & (2 | ((VRAM_Mode >> (bank >> 1)) & 1));
memcpy(VRMVCPCache[bank], VCPRegs[esb], 8);
}
for(unsigned n = 0; n < 2; n++)
base[n] = CurYScrollIF[n] + YCoordAccum[n];
for(unsigned cyc = 0; cyc < max_cyc; cyc++)
{
unsigned do_inc = 0;
for(unsigned bank = 0; bank < 4; bank++)
{
const unsigned act = VRMVCPCache[bank][cyc];
do_inc |= vcon[0] & (act == VCP_NBG0_VCS);
do_inc |= vcon[1] & (act == VCP_NBG1_VCS);
}
inc[cyc] = do_inc << 1;
}
for(unsigned tile = 0; MDFN_LIKELY(tile < tc); tile++)
{
for(unsigned cyc = 0; cyc < max_cyc; cyc++)
{
const unsigned act = VRMVCPCache[vcaddr >> 16][cyc];
// NBG0
if(vcon[0])
{
if(cyc == 3)
LB.vcscr[0][tile] = ((base[0] + tmp[0]) >> 8);
if(cyc == 3)
tmp[0] = VCLast[0];
if(act == VCP_NBG0_VCS)
{
VCLast[0] = (VRAM[vcaddr + 0] << 8) | (VRAM[vcaddr + 1] >> 8);
if(cyc <= (1 + !tile))
tmp[0] = VCLast[0];
}
}
// NBG1
if(vcon[1])
{
if(cyc == 4)
LB.vcscr[1][tile] = ((base[1] + tmp[1]) >> 8);
if(cyc == 4)
tmp[1] = VCLast[1];
if(act == VCP_NBG1_VCS)
{
VCLast[1] = (VRAM[vcaddr + 0] << 8) | (VRAM[vcaddr + 1] >> 8);
if(cyc <= 2) //(2 + !tile)) // TODO: Check
tmp[1] = VCLast[1];
}
}
vcaddr = (vcaddr + inc[cyc]) & 0x3FFFE;
}
}
}
template<unsigned TA_PrioMode, unsigned TA_CCMode>
static INLINE void MakeSFCodeLUT(const unsigned layer, int16* const sfcode_lut)
{
const uint8 code = SFCODE >> (((SFSEL >> layer) & 1) << 3);
for(unsigned i = 0; i < 8; i++)
{
uint16 tmp = 0xFFFF;
if(!((code >> i) & 1))
{
if(TA_PrioMode & 2)
tmp &= ~(1U << PIX_PRIO_SHIFT);
if(TA_CCMode == 2)
tmp &= ~(1U << PIX_CCE_SHIFT);
}
sfcode_lut[i] = tmp;
}
}
static INLINE uint32 rgb15_to_rgb24(uint16 src)
{
return ((((src << 3) & 0xF8) | ((src << 6) & 0xF800) | ((src << 9) & 0xF80000) | ((src << 16) & 0x80000000)));;
}
template<bool TA_bmen, unsigned TA_bpp, bool TA_isrgb, bool TA_igntp, unsigned TA_PrioMode, unsigned TA_CCMode, typename T>
static INLINE uint64 MakeNBGRBGPix(T& tf, const uint32 pix_base_or, const int16* sfcode_lut, const uint32 ix, const uint32 iy)
{
uint32 cellx = (ix ^ tf.cellx_xor);
const uint16* vrb = &tf.tile_vrb[((cellx * TA_bpp) >> 4)];
//
//
//
uint32 pbor = pix_base_or;
uint32 rgb24;
bool opaque;
if(TA_CCMode == 1 || (TA_CCMode == 2 && !TA_isrgb))
pbor |= (tf.scc << PIX_CCE_SHIFT);
if(TA_PrioMode == 1 || (TA_PrioMode == 2 && !TA_isrgb))
pbor |= (tf.spr << PIX_PRIO_SHIFT);
if(TA_isrgb)
{
if(TA_bpp == 32)
{
uint32 tmp = (vrb[0] << 16) | vrb[1];
rgb24 = tmp & 0xFFFFFF;
opaque = (bool)(tmp & 0x80000000);
}
else
{
uint32 tmp = vrb[0];
rgb24 = rgb15_to_rgb24(tmp & 0x7FFF);
opaque = (bool)(tmp & 0x8000);
}
if(TA_CCMode == 3)
pbor |= (1 << PIX_CCE_SHIFT);
}
else
{
uint32 dcc;
uint32 tmp = vrb[0]; //charno ^ (charno << 8); //vrb[0];
if(TA_bpp == 16)
dcc = tmp & 0x7FF;
else if(TA_bpp == 8)
dcc = (tmp >> (((cellx & 1) ^ 1) << 3)) & 0xFF;
else
dcc = (tmp >> (((cellx & 3) ^ 3) << 2)) & 0x0F;
opaque = (bool)dcc;
rgb24 = ColorCache[(tf.pcco + dcc) & 2047];
if(TA_CCMode == 3)
pbor |= ((int32)rgb24 >> 31) & (1 << PIX_CCE_SHIFT);
//
if(TA_PrioMode == 2 || TA_CCMode == 2)
pbor &= *(const int16*)((const uint8*)sfcode_lut + (dcc & 0xE));
}
if(!TA_igntp && !opaque)
pbor = 0;
return pbor | ((uint64)rgb24 << PIX_RGB_SHIFT);
}
template<bool TA_bmen, unsigned TA_bpp, bool TA_isrgb, bool TA_igntp, unsigned TA_PrioMode, unsigned TA_CCMode>
static void T_DrawNBG(const unsigned n, uint64* bgbuf, const unsigned w, const uint32 pix_base_or)
{
assert(n < 2);
//
//
const bool VCSEn = ((SCRCTL >> (n << 3)) & 0x1) && !(MZCTL & (1U << n));
//
TileFetcher<false> tf;
uint32 xcinc;
uint32 xc;
uint32 iy;
int16 sfcode_lut[8];
tf.CRAOffs = CRAMAddrOffs_NBG[n] << 8;
//
tf.BMSCC = ((BMPNA >> (4 + (n << 3))) & 1);
tf.BMSPR = ((BMPNA >> (5 + (n << 3))) & 1);
tf.BMPalNo = ((BMPNA >> (0 + (n << 3))) & 0x7) << 4;
tf.BMSize = ((CHCTLA >> (2 + (n << 3))) & 0x3);
//
tf.PlaneSize = (PLSZ >> (n << 1)) & 0x3;
tf.PNDSize = (PNCN[n] >> 15) & 1; // 0 = 2 words, 1 = 1 word
tf.CharSize = ((CHCTLA >> (0 + (n << 3))) & 1);
tf.AuxMode = (PNCN[n] >> 14) & 1;
tf.Supp = (PNCN[n] & 0x3FF); // Supplement bits when PNDSize == 1
//
tf.Start(n, TA_bmen, (MPOFN >> (n << 2)) & 0x7, MapRegs[n]);
MakeSFCodeLUT<TA_PrioMode, TA_CCMode>(n, sfcode_lut);
xc = CurXScrollIF[n];
iy = (CurYScrollIF[n] + MosEff_YCoordAccum[n]) >> 8;
xcinc = CurXCoordInc[n];
//if(line == 64)
// printf("Mega %d: planesize=0x%1x charsize=%d pndsize=%d(auxmode=%d,supp=0x%04x) bpp=%d/%d ccmode=0x%04x SFSEL=0x%04x SFCODE=0x%04x SFCCMD=0x%04x\n", n, PlaneSize, CharSize, PNDSize, AuxMode, Supp, TA_bpp, TA_isrgb, TA_CCMode, SFSEL, SFCODE, SFCCMD);
//printf("Mega %d %02x %d --- %04x %04x -- %04x --- lsa=0x%06x vcsa=0x%06x --- imode=0x%01x\n", TA_bpp, BMSize, w, xcinc, ycinc, SCRCTL, LineScrollAddr[n], VCScrollAddr, InterlaceMode);
// Map: 2x2 planes
// Plane: 1x1, 2x1, or 2x2 pages
// Page: 64x64 cells
// Character: 1x1, 2x2 cells
// Cell: 8x8 dots
uint32 prev_ix = ~0U;
if(((ZMCTL >> (n << 3)) & 0x3) && VCSEn)
{
for(unsigned i = 0; MDFN_LIKELY(i < w); i++)
{
const uint32 ix = xc >> 8;
iy = LB.vcscr[n][i >> 3];
tf.Fetch<TA_bpp>(TA_bmen, ix, iy);
//
//
//
bgbuf[i] = MakeNBGRBGPix<TA_bmen, TA_bpp, TA_isrgb, TA_igntp, TA_PrioMode, TA_CCMode>(tf, pix_base_or, sfcode_lut, ix, iy);
xc += xcinc;
}
}
else
{
for(unsigned i = 0; MDFN_LIKELY(i < w); i++)
{
const uint32 ix = xc >> 8;
if((ix >> 3) != prev_ix)
{
prev_ix = ix >> 3;
//
if(VCSEn)
iy = LB.vcscr[n][(i + 7) >> 3];
tf.Fetch<TA_bpp>(TA_bmen, ix, iy);
}
//
//
//
bgbuf[i] = MakeNBGRBGPix<TA_bmen, TA_bpp, TA_isrgb, TA_igntp, TA_PrioMode, TA_CCMode>(tf, pix_base_or, sfcode_lut, ix, iy);
xc += xcinc;
}
}
}
static void (*DrawNBG[2 /*bitmap enable*/][5/*col mode*/][2/*igntp*/][3/*priomode*/][4/*ccmode*/])(const unsigned n, uint64* bgbuf, const unsigned w, const uint32 pix_base_or) =
{
{
{ { { T_DrawNBG<0, 4, 0, 0, 0, 0>, T_DrawNBG<0, 4, 0, 0, 0, 1>, T_DrawNBG<0, 4, 0, 0, 0, 2>, T_DrawNBG<0, 4, 0, 0, 0, 3>, }, { T_DrawNBG<0, 4, 0, 0, 1, 0>, T_DrawNBG<0, 4, 0, 0, 1, 1>, T_DrawNBG<0, 4, 0, 0, 1, 2>, T_DrawNBG<0, 4, 0, 0, 1, 3>, }, { T_DrawNBG<0, 4, 0, 0, 2, 0>, T_DrawNBG<0, 4, 0, 0, 2, 1>, T_DrawNBG<0, 4, 0, 0, 2, 2>, T_DrawNBG<0, 4, 0, 0, 2, 3>, }, }, { { T_DrawNBG<0, 4, 0, 1, 0, 0>, T_DrawNBG<0, 4, 0, 1, 0, 1>, T_DrawNBG<0, 4, 0, 1, 0, 2>, T_DrawNBG<0, 4, 0, 1, 0, 3>, }, { T_DrawNBG<0, 4, 0, 1, 1, 0>, T_DrawNBG<0, 4, 0, 1, 1, 1>, T_DrawNBG<0, 4, 0, 1, 1, 2>, T_DrawNBG<0, 4, 0, 1, 1, 3>, }, { T_DrawNBG<0, 4, 0, 1, 2, 0>, T_DrawNBG<0, 4, 0, 1, 2, 1>, T_DrawNBG<0, 4, 0, 1, 2, 2>, T_DrawNBG<0, 4, 0, 1, 2, 3>, }, }, },
{ { { T_DrawNBG<0, 8, 0, 0, 0, 0>, T_DrawNBG<0, 8, 0, 0, 0, 1>, T_DrawNBG<0, 8, 0, 0, 0, 2>, T_DrawNBG<0, 8, 0, 0, 0, 3>, }, { T_DrawNBG<0, 8, 0, 0, 1, 0>, T_DrawNBG<0, 8, 0, 0, 1, 1>, T_DrawNBG<0, 8, 0, 0, 1, 2>, T_DrawNBG<0, 8, 0, 0, 1, 3>, }, { T_DrawNBG<0, 8, 0, 0, 2, 0>, T_DrawNBG<0, 8, 0, 0, 2, 1>, T_DrawNBG<0, 8, 0, 0, 2, 2>, T_DrawNBG<0, 8, 0, 0, 2, 3>, }, }, { { T_DrawNBG<0, 8, 0, 1, 0, 0>, T_DrawNBG<0, 8, 0, 1, 0, 1>, T_DrawNBG<0, 8, 0, 1, 0, 2>, T_DrawNBG<0, 8, 0, 1, 0, 3>, }, { T_DrawNBG<0, 8, 0, 1, 1, 0>, T_DrawNBG<0, 8, 0, 1, 1, 1>, T_DrawNBG<0, 8, 0, 1, 1, 2>, T_DrawNBG<0, 8, 0, 1, 1, 3>, }, { T_DrawNBG<0, 8, 0, 1, 2, 0>, T_DrawNBG<0, 8, 0, 1, 2, 1>, T_DrawNBG<0, 8, 0, 1, 2, 2>, T_DrawNBG<0, 8, 0, 1, 2, 3>, }, }, },
{ { { T_DrawNBG<0, 16, 0, 0, 0, 0>, T_DrawNBG<0, 16, 0, 0, 0, 1>, T_DrawNBG<0, 16, 0, 0, 0, 2>, T_DrawNBG<0, 16, 0, 0, 0, 3>, }, { T_DrawNBG<0, 16, 0, 0, 1, 0>, T_DrawNBG<0, 16, 0, 0, 1, 1>, T_DrawNBG<0, 16, 0, 0, 1, 2>, T_DrawNBG<0, 16, 0, 0, 1, 3>, }, { T_DrawNBG<0, 16, 0, 0, 2, 0>, T_DrawNBG<0, 16, 0, 0, 2, 1>, T_DrawNBG<0, 16, 0, 0, 2, 2>, T_DrawNBG<0, 16, 0, 0, 2, 3>, }, }, { { T_DrawNBG<0, 16, 0, 1, 0, 0>, T_DrawNBG<0, 16, 0, 1, 0, 1>, T_DrawNBG<0, 16, 0, 1, 0, 2>, T_DrawNBG<0, 16, 0, 1, 0, 3>, }, { T_DrawNBG<0, 16, 0, 1, 1, 0>, T_DrawNBG<0, 16, 0, 1, 1, 1>, T_DrawNBG<0, 16, 0, 1, 1, 2>, T_DrawNBG<0, 16, 0, 1, 1, 3>, }, { T_DrawNBG<0, 16, 0, 1, 2, 0>, T_DrawNBG<0, 16, 0, 1, 2, 1>, T_DrawNBG<0, 16, 0, 1, 2, 2>, T_DrawNBG<0, 16, 0, 1, 2, 3>, }, }, },
{ { { T_DrawNBG<0, 16, 1, 0, 0, 0>, T_DrawNBG<0, 16, 1, 0, 0, 1>, T_DrawNBG<0, 16, 1, 0, 0, 2>, T_DrawNBG<0, 16, 1, 0, 0, 3>, }, { T_DrawNBG<0, 16, 1, 0, 1, 0>, T_DrawNBG<0, 16, 1, 0, 1, 1>, T_DrawNBG<0, 16, 1, 0, 1, 2>, T_DrawNBG<0, 16, 1, 0, 1, 3>, }, { T_DrawNBG<0, 16, 1, 0, 2, 0>, T_DrawNBG<0, 16, 1, 0, 2, 1>, T_DrawNBG<0, 16, 1, 0, 2, 2>, T_DrawNBG<0, 16, 1, 0, 2, 3>, }, }, { { T_DrawNBG<0, 16, 1, 1, 0, 0>, T_DrawNBG<0, 16, 1, 1, 0, 1>, T_DrawNBG<0, 16, 1, 1, 0, 2>, T_DrawNBG<0, 16, 1, 1, 0, 3>, }, { T_DrawNBG<0, 16, 1, 1, 1, 0>, T_DrawNBG<0, 16, 1, 1, 1, 1>, T_DrawNBG<0, 16, 1, 1, 1, 2>, T_DrawNBG<0, 16, 1, 1, 1, 3>, }, { T_DrawNBG<0, 16, 1, 1, 2, 0>, T_DrawNBG<0, 16, 1, 1, 2, 1>, T_DrawNBG<0, 16, 1, 1, 2, 2>, T_DrawNBG<0, 16, 1, 1, 2, 3>, }, }, },
{ { { T_DrawNBG<0, 32, 1, 0, 0, 0>, T_DrawNBG<0, 32, 1, 0, 0, 1>, T_DrawNBG<0, 32, 1, 0, 0, 2>, T_DrawNBG<0, 32, 1, 0, 0, 3>, }, { T_DrawNBG<0, 32, 1, 0, 1, 0>, T_DrawNBG<0, 32, 1, 0, 1, 1>, T_DrawNBG<0, 32, 1, 0, 1, 2>, T_DrawNBG<0, 32, 1, 0, 1, 3>, }, { T_DrawNBG<0, 32, 1, 0, 2, 0>, T_DrawNBG<0, 32, 1, 0, 2, 1>, T_DrawNBG<0, 32, 1, 0, 2, 2>, T_DrawNBG<0, 32, 1, 0, 2, 3>, }, }, { { T_DrawNBG<0, 32, 1, 1, 0, 0>, T_DrawNBG<0, 32, 1, 1, 0, 1>, T_DrawNBG<0, 32, 1, 1, 0, 2>, T_DrawNBG<0, 32, 1, 1, 0, 3>, }, { T_DrawNBG<0, 32, 1, 1, 1, 0>, T_DrawNBG<0, 32, 1, 1, 1, 1>, T_DrawNBG<0, 32, 1, 1, 1, 2>, T_DrawNBG<0, 32, 1, 1, 1, 3>, }, { T_DrawNBG<0, 32, 1, 1, 2, 0>, T_DrawNBG<0, 32, 1, 1, 2, 1>, T_DrawNBG<0, 32, 1, 1, 2, 2>, T_DrawNBG<0, 32, 1, 1, 2, 3>, }, }, },
},
{
{ { { T_DrawNBG<1, 4, 0, 0, 0, 0>, T_DrawNBG<1, 4, 0, 0, 0, 1>, T_DrawNBG<1, 4, 0, 0, 0, 2>, T_DrawNBG<1, 4, 0, 0, 0, 3>, }, { T_DrawNBG<1, 4, 0, 0, 1, 0>, T_DrawNBG<1, 4, 0, 0, 1, 1>, T_DrawNBG<1, 4, 0, 0, 1, 2>, T_DrawNBG<1, 4, 0, 0, 1, 3>, }, { T_DrawNBG<1, 4, 0, 0, 2, 0>, T_DrawNBG<1, 4, 0, 0, 2, 1>, T_DrawNBG<1, 4, 0, 0, 2, 2>, T_DrawNBG<1, 4, 0, 0, 2, 3>, }, }, { { T_DrawNBG<1, 4, 0, 1, 0, 0>, T_DrawNBG<1, 4, 0, 1, 0, 1>, T_DrawNBG<1, 4, 0, 1, 0, 2>, T_DrawNBG<1, 4, 0, 1, 0, 3>, }, { T_DrawNBG<1, 4, 0, 1, 1, 0>, T_DrawNBG<1, 4, 0, 1, 1, 1>, T_DrawNBG<1, 4, 0, 1, 1, 2>, T_DrawNBG<1, 4, 0, 1, 1, 3>, }, { T_DrawNBG<1, 4, 0, 1, 2, 0>, T_DrawNBG<1, 4, 0, 1, 2, 1>, T_DrawNBG<1, 4, 0, 1, 2, 2>, T_DrawNBG<1, 4, 0, 1, 2, 3>, }, }, },
{ { { T_DrawNBG<1, 8, 0, 0, 0, 0>, T_DrawNBG<1, 8, 0, 0, 0, 1>, T_DrawNBG<1, 8, 0, 0, 0, 2>, T_DrawNBG<1, 8, 0, 0, 0, 3>, }, { T_DrawNBG<1, 8, 0, 0, 1, 0>, T_DrawNBG<1, 8, 0, 0, 1, 1>, T_DrawNBG<1, 8, 0, 0, 1, 2>, T_DrawNBG<1, 8, 0, 0, 1, 3>, }, { T_DrawNBG<1, 8, 0, 0, 2, 0>, T_DrawNBG<1, 8, 0, 0, 2, 1>, T_DrawNBG<1, 8, 0, 0, 2, 2>, T_DrawNBG<1, 8, 0, 0, 2, 3>, }, }, { { T_DrawNBG<1, 8, 0, 1, 0, 0>, T_DrawNBG<1, 8, 0, 1, 0, 1>, T_DrawNBG<1, 8, 0, 1, 0, 2>, T_DrawNBG<1, 8, 0, 1, 0, 3>, }, { T_DrawNBG<1, 8, 0, 1, 1, 0>, T_DrawNBG<1, 8, 0, 1, 1, 1>, T_DrawNBG<1, 8, 0, 1, 1, 2>, T_DrawNBG<1, 8, 0, 1, 1, 3>, }, { T_DrawNBG<1, 8, 0, 1, 2, 0>, T_DrawNBG<1, 8, 0, 1, 2, 1>, T_DrawNBG<1, 8, 0, 1, 2, 2>, T_DrawNBG<1, 8, 0, 1, 2, 3>, }, }, },
{ { { T_DrawNBG<1, 16, 0, 0, 0, 0>, T_DrawNBG<1, 16, 0, 0, 0, 1>, T_DrawNBG<1, 16, 0, 0, 0, 2>, T_DrawNBG<1, 16, 0, 0, 0, 3>, }, { T_DrawNBG<1, 16, 0, 0, 1, 0>, T_DrawNBG<1, 16, 0, 0, 1, 1>, T_DrawNBG<1, 16, 0, 0, 1, 2>, T_DrawNBG<1, 16, 0, 0, 1, 3>, }, { T_DrawNBG<1, 16, 0, 0, 2, 0>, T_DrawNBG<1, 16, 0, 0, 2, 1>, T_DrawNBG<1, 16, 0, 0, 2, 2>, T_DrawNBG<1, 16, 0, 0, 2, 3>, }, }, { { T_DrawNBG<1, 16, 0, 1, 0, 0>, T_DrawNBG<1, 16, 0, 1, 0, 1>, T_DrawNBG<1, 16, 0, 1, 0, 2>, T_DrawNBG<1, 16, 0, 1, 0, 3>, }, { T_DrawNBG<1, 16, 0, 1, 1, 0>, T_DrawNBG<1, 16, 0, 1, 1, 1>, T_DrawNBG<1, 16, 0, 1, 1, 2>, T_DrawNBG<1, 16, 0, 1, 1, 3>, }, { T_DrawNBG<1, 16, 0, 1, 2, 0>, T_DrawNBG<1, 16, 0, 1, 2, 1>, T_DrawNBG<1, 16, 0, 1, 2, 2>, T_DrawNBG<1, 16, 0, 1, 2, 3>, }, }, },
{ { { T_DrawNBG<1, 16, 1, 0, 0, 0>, T_DrawNBG<1, 16, 1, 0, 0, 1>, T_DrawNBG<1, 16, 1, 0, 0, 2>, T_DrawNBG<1, 16, 1, 0, 0, 3>, }, { T_DrawNBG<1, 16, 1, 0, 1, 0>, T_DrawNBG<1, 16, 1, 0, 1, 1>, T_DrawNBG<1, 16, 1, 0, 1, 2>, T_DrawNBG<1, 16, 1, 0, 1, 3>, }, { T_DrawNBG<1, 16, 1, 0, 2, 0>, T_DrawNBG<1, 16, 1, 0, 2, 1>, T_DrawNBG<1, 16, 1, 0, 2, 2>, T_DrawNBG<1, 16, 1, 0, 2, 3>, }, }, { { T_DrawNBG<1, 16, 1, 1, 0, 0>, T_DrawNBG<1, 16, 1, 1, 0, 1>, T_DrawNBG<1, 16, 1, 1, 0, 2>, T_DrawNBG<1, 16, 1, 1, 0, 3>, }, { T_DrawNBG<1, 16, 1, 1, 1, 0>, T_DrawNBG<1, 16, 1, 1, 1, 1>, T_DrawNBG<1, 16, 1, 1, 1, 2>, T_DrawNBG<1, 16, 1, 1, 1, 3>, }, { T_DrawNBG<1, 16, 1, 1, 2, 0>, T_DrawNBG<1, 16, 1, 1, 2, 1>, T_DrawNBG<1, 16, 1, 1, 2, 2>, T_DrawNBG<1, 16, 1, 1, 2, 3>, }, }, },
{ { { T_DrawNBG<1, 32, 1, 0, 0, 0>, T_DrawNBG<1, 32, 1, 0, 0, 1>, T_DrawNBG<1, 32, 1, 0, 0, 2>, T_DrawNBG<1, 32, 1, 0, 0, 3>, }, { T_DrawNBG<1, 32, 1, 0, 1, 0>, T_DrawNBG<1, 32, 1, 0, 1, 1>, T_DrawNBG<1, 32, 1, 0, 1, 2>, T_DrawNBG<1, 32, 1, 0, 1, 3>, }, { T_DrawNBG<1, 32, 1, 0, 2, 0>, T_DrawNBG<1, 32, 1, 0, 2, 1>, T_DrawNBG<1, 32, 1, 0, 2, 2>, T_DrawNBG<1, 32, 1, 0, 2, 3>, }, }, { { T_DrawNBG<1, 32, 1, 1, 0, 0>, T_DrawNBG<1, 32, 1, 1, 0, 1>, T_DrawNBG<1, 32, 1, 1, 0, 2>, T_DrawNBG<1, 32, 1, 1, 0, 3>, }, { T_DrawNBG<1, 32, 1, 1, 1, 0>, T_DrawNBG<1, 32, 1, 1, 1, 1>, T_DrawNBG<1, 32, 1, 1, 1, 2>, T_DrawNBG<1, 32, 1, 1, 1, 3>, }, { T_DrawNBG<1, 32, 1, 1, 2, 0>, T_DrawNBG<1, 32, 1, 1, 2, 1>, T_DrawNBG<1, 32, 1, 1, 2, 2>, T_DrawNBG<1, 32, 1, 1, 2, 3>, }, }, },
}
};
template<bool TA_igntp, unsigned TA_PrioMode, unsigned TA_CCMode>
static INLINE uint64 MakeNBG23Pix(uint32 dcc, uint32 pbor, const int16* sfcode_lut, uint32 colcacheoffs)
{
uint32 rgb24;
rgb24 = ColorCache[(colcacheoffs + dcc) & 2047];
if(TA_CCMode == 3)
pbor |= ((int32)rgb24 >> 31) & (1 << PIX_CCE_SHIFT);
if(TA_PrioMode == 2 || TA_CCMode == 2)
pbor &= *(const int16*)((const uint8*)sfcode_lut + (dcc & 0xE));
if(!TA_igntp && !dcc)
pbor = 0;
return pbor + ((uint64)rgb24 << PIX_RGB_SHIFT);
}
//
// CCMode will be forced to 0 in the effective instantiation if corresponding NBG CCE bit in CCCTL is 0.
//
template<unsigned TA_bpp, bool TA_igntp, unsigned TA_PrioMode, unsigned TA_CCMode>
static void T_DrawNBG23(const unsigned n, uint64* bgbuf, const unsigned w, const uint32 pix_base_or)
{
assert(n >= 2);
TileFetcher<false> tf;
int16 sfcode_lut[8];
unsigned tc = 1 + (w >> 3);
const unsigned xscr = XScrollI[n];
const unsigned yscr = MosEff_NBG23_YCounter[n & 1];
unsigned tx;
tf.CRAOffs = CRAMAddrOffs_NBG[n] << 8;
//
tf.PlaneSize = (PLSZ >> (n << 1)) & 0x3;
tf.PNDSize = (PNCN[n] >> 15) & 1; // 0 = 2 words, 1 = 1 word
tf.CharSize = ((CHCTLB >> (0 + ((n & 1) << 2))) & 1);
tf.AuxMode = (PNCN[n] >> 14) & 1;
tf.Supp = (PNCN[n] & 0x3FF); // Supplement bits when PNDSize == 1
//
tf.Start(n, false, (MPOFN >> (n << 2)) & 0x7, MapRegs[n]);
MakeSFCodeLUT<TA_PrioMode, TA_CCMode>(n, sfcode_lut);
bgbuf -= xscr & 0x7;
tx = xscr >> 3;
//if(TA_bpp == 4 && n == 3)
// printf("Goop: %d %d %02x, %016llx %016llx %016llx %016llx\n", n, TA_bpp, VRAM_Mode, MDFN_de64lsb(VCPRegs[0]), MDFN_de64lsb(VCPRegs[1]), MDFN_de64lsb(VCPRegs[2]), MDFN_de64lsb(VCPRegs[3]));
// Kludge for Akumajou Dracula X
if(MDFN_UNLIKELY(TA_bpp == 4 && n == 3 && VRAM_Mode == 0x2 && MDFN_de64lsb(VCPRegs[0]) == 0x0f0f070406060505ULL && MDFN_de64lsb(VCPRegs[2]) == 0x0f0f03000f0f0201ULL && MDFN_de64lsb(VCPRegs[3]) == 0x0f0f0f0f0f0f0f0fULL))
{
for(unsigned i = 0; i < 8; i++)
*bgbuf++ = 0;
tc--;
}
while(MDFN_LIKELY(tc--))
{
uint32 pbor = pix_base_or;
tf.Fetch<TA_bpp>(false, tx << 3, yscr);
if(TA_CCMode == 1 || TA_CCMode == 2)
pbor |= (tf.scc << PIX_CCE_SHIFT);
if(TA_PrioMode == 1 || TA_PrioMode == 2)
pbor |= (tf.spr << PIX_PRIO_SHIFT);
//
//
auto* const mbp = MakeNBG23Pix<TA_igntp, TA_PrioMode, TA_CCMode>;
if(TA_bpp == 8)
{
if(tf.cellx_xor & 0x7)
{
bgbuf[7] = mbp((uint8)(tf.tile_vrb[0] >> 8), /**/pbor, sfcode_lut, tf.pcco);
bgbuf[6] = mbp((uint8)(tf.tile_vrb[0] >> 0), /**/pbor, sfcode_lut, tf.pcco);
bgbuf[5] = mbp((uint8)(tf.tile_vrb[1] >> 8), /**/pbor, sfcode_lut, tf.pcco);
bgbuf[4] = mbp((uint8)(tf.tile_vrb[1] >> 0), /**/pbor, sfcode_lut, tf.pcco);
bgbuf[3] = mbp((uint8)(tf.tile_vrb[2] >> 8), /**/pbor, sfcode_lut, tf.pcco);
bgbuf[2] = mbp((uint8)(tf.tile_vrb[2] >> 0), /**/pbor, sfcode_lut, tf.pcco);
bgbuf[1] = mbp((uint8)(tf.tile_vrb[3] >> 8), /**/pbor, sfcode_lut, tf.pcco);
bgbuf[0] = mbp((uint8)(tf.tile_vrb[3] >> 0), /**/pbor, sfcode_lut, tf.pcco);
}
else
{
bgbuf[0] = mbp((uint8)(tf.tile_vrb[0] >> 8), /**/pbor, sfcode_lut, tf.pcco);
bgbuf[1] = mbp((uint8)(tf.tile_vrb[0] >> 0), /**/pbor, sfcode_lut, tf.pcco);
bgbuf[2] = mbp((uint8)(tf.tile_vrb[1] >> 8), /**/pbor, sfcode_lut, tf.pcco);
bgbuf[3] = mbp((uint8)(tf.tile_vrb[1] >> 0), /**/pbor, sfcode_lut, tf.pcco);
bgbuf[4] = mbp((uint8)(tf.tile_vrb[2] >> 8), /**/pbor, sfcode_lut, tf.pcco);
bgbuf[5] = mbp((uint8)(tf.tile_vrb[2] >> 0), /**/pbor, sfcode_lut, tf.pcco);
bgbuf[6] = mbp((uint8)(tf.tile_vrb[3] >> 8), /**/pbor, sfcode_lut, tf.pcco);
bgbuf[7] = mbp((uint8)(tf.tile_vrb[3] >> 0), /**/pbor, sfcode_lut, tf.pcco);
}
}
else
{
if(tf.cellx_xor & 0x7)
{
bgbuf[7] = mbp((tf.tile_vrb[0] >> 12), /**/pbor, sfcode_lut, tf.pcco);
bgbuf[6] = mbp((tf.tile_vrb[0] >> 8) & 0xF, /**/pbor, sfcode_lut, tf.pcco);
bgbuf[5] = mbp((tf.tile_vrb[0] >> 4) & 0xF, /**/pbor, sfcode_lut, tf.pcco);
bgbuf[4] = mbp((tf.tile_vrb[0] >> 0) & 0xF, /**/pbor, sfcode_lut, tf.pcco);
bgbuf[3] = mbp((tf.tile_vrb[1] >> 12), /**/pbor, sfcode_lut, tf.pcco);
bgbuf[2] = mbp((tf.tile_vrb[1] >> 8) & 0xF, /**/pbor, sfcode_lut, tf.pcco);
bgbuf[1] = mbp((tf.tile_vrb[1] >> 4) & 0xF, /**/pbor, sfcode_lut, tf.pcco);
bgbuf[0] = mbp((tf.tile_vrb[1] >> 0) & 0xF, /**/pbor, sfcode_lut, tf.pcco);
}
else
{
bgbuf[0] = mbp((tf.tile_vrb[0] >> 12), /**/pbor, sfcode_lut, tf.pcco);
bgbuf[1] = mbp((tf.tile_vrb[0] >> 8) & 0xF, /**/pbor, sfcode_lut, tf.pcco);
bgbuf[2] = mbp((tf.tile_vrb[0] >> 4) & 0xF, /**/pbor, sfcode_lut, tf.pcco);
bgbuf[3] = mbp((tf.tile_vrb[0] >> 0) & 0xF, /**/pbor, sfcode_lut, tf.pcco);
bgbuf[4] = mbp((tf.tile_vrb[1] >> 12), /**/pbor, sfcode_lut, tf.pcco);
bgbuf[5] = mbp((tf.tile_vrb[1] >> 8) & 0xF, /**/pbor, sfcode_lut, tf.pcco);
bgbuf[6] = mbp((tf.tile_vrb[1] >> 4) & 0xF, /**/pbor, sfcode_lut, tf.pcco);
bgbuf[7] = mbp((tf.tile_vrb[1] >> 0) & 0xF, /**/pbor, sfcode_lut, tf.pcco);
}
}
//
//
//
tx++;
bgbuf += 8;
}
}
static void (*DrawNBG23[2/*col mode*/][2/*igntp*/][3/*priomode*/][4/*ccmode*/])(const unsigned n, uint64* bgbuf, const unsigned w, const uint32 pix_base_or) =
{
{
{ { T_DrawNBG23<4, 0, 0, 0>, T_DrawNBG23<4, 0, 0, 1>, T_DrawNBG23<4, 0, 0, 2>, T_DrawNBG23<4, 0, 0, 3>, }, { T_DrawNBG23<4, 0, 1, 0>, T_DrawNBG23<4, 0, 1, 1>, T_DrawNBG23<4, 0, 1, 2>, T_DrawNBG23<4, 0, 1, 3>, }, { T_DrawNBG23<4, 0, 2, 0>, T_DrawNBG23<4, 0, 2, 1>, T_DrawNBG23<4, 0, 2, 2>, T_DrawNBG23<4, 0, 2, 3>, }, },
{ { T_DrawNBG23<4, 1, 0, 0>, T_DrawNBG23<4, 1, 0, 1>, T_DrawNBG23<4, 1, 0, 2>, T_DrawNBG23<4, 1, 0, 3>, }, { T_DrawNBG23<4, 1, 1, 0>, T_DrawNBG23<4, 1, 1, 1>, T_DrawNBG23<4, 1, 1, 2>, T_DrawNBG23<4, 1, 1, 3>, }, { T_DrawNBG23<4, 1, 2, 0>, T_DrawNBG23<4, 1, 2, 1>, T_DrawNBG23<4, 1, 2, 2>, T_DrawNBG23<4, 1, 2, 3>, }, },
},
{
{ { T_DrawNBG23<8, 0, 0, 0>, T_DrawNBG23<8, 0, 0, 1>, T_DrawNBG23<8, 0, 0, 2>, T_DrawNBG23<8, 0, 0, 3>, }, { T_DrawNBG23<8, 0, 1, 0>, T_DrawNBG23<8, 0, 1, 1>, T_DrawNBG23<8, 0, 1, 2>, T_DrawNBG23<8, 0, 1, 3>, }, { T_DrawNBG23<8, 0, 2, 0>, T_DrawNBG23<8, 0, 2, 1>, T_DrawNBG23<8, 0, 2, 2>, T_DrawNBG23<8, 0, 2, 3>, }, },
{ { T_DrawNBG23<8, 1, 0, 0>, T_DrawNBG23<8, 1, 0, 1>, T_DrawNBG23<8, 1, 0, 2>, T_DrawNBG23<8, 1, 0, 3>, }, { T_DrawNBG23<8, 1, 1, 0>, T_DrawNBG23<8, 1, 1, 1>, T_DrawNBG23<8, 1, 1, 2>, T_DrawNBG23<8, 1, 1, 3>, }, { T_DrawNBG23<8, 1, 2, 0>, T_DrawNBG23<8, 1, 2, 1>, T_DrawNBG23<8, 1, 2, 2>, T_DrawNBG23<8, 1, 2, 3>, }, },
},
};
static INLINE uint32 GetCoeffAddr(const unsigned i, uint32 offset)
{
const uint32 src_mask = (CRKTE ? 0x3FF : 0x3FFFF);
offset >>= 10;
offset <<= !(KTCTL[i] & 0x2);
offset &= src_mask;
return offset;
}
static INLINE uint32 ReadCoeff(const unsigned i, const uint32 addr)
{
const uint16* src = (CRKTE ? &CRAM[0x400] : VRAM);
uint32 coeff;
if(KTCTL[i] & 0x2)
{
const uint16 tmp = src[addr];
coeff = (sign_x_to_s32(21, tmp << 6) & 0x00FFFFFF) | ((tmp & 0x8000) << 16);
//printf("%d Coeff %08x, %04x -- %08x\n", i, offset, tmp, coeff);
}
else
{
const uint16* ea = &src[addr];
coeff = (ea[0] << 16) | ea[1];
}
return coeff;
}
// Coefficient table reading can (temporarily) override kx, ky, and/or Xp
//
// When RBG1 is enabled, line color screen uses rotation parameter A coefficient table
//
// RBG1 always uses MSB of coefficient data as transparent bit.
//
// RBG1 requires RPMD == 0, or else bad things happen?
template<typename T>
static void SetupRotVars(const T* rs, const unsigned rbg_w)
{
const uint8 EffRPMD = ((BGON & 0x20) ? 0 : RPMD);
if(EffRPMD < 2)
{
for(unsigned x = 0; MDFN_LIKELY(x < rbg_w); x++)
LB.rotabsel[x] = RPMD;
}
else if(EffRPMD == 3)
GetWinRotAB();
//
//
//
for(unsigned i = 0; i < 2; i++)
{
auto& r = LB.rotv[i];
r.Xsp = rs[i].Xsp;
r.Ysp = rs[i].Ysp;
r.Xp = rs[i].Xp;
r.Yp = rs[i].Yp;
r.dX = rs[i].dX;
r.dY = rs[i].dY;
r.kx = rs[i].kx;
r.ky = rs[i].ky;
LB.rotv[i].tf.BMSCC = ((BMPNB >> 4) & 1);
LB.rotv[i].tf.BMSPR = ((BMPNB >> 5) & 1);
LB.rotv[i].tf.BMPalNo = ((BMPNB >> 0) & 0x7) << 4;
LB.rotv[i].tf.BMSize = ((CHCTLB >> 10) & 0x1);
//
//
//
//
//
//
if((BGON & 0x20) && i)
{
LB.rotv[1].tf.CRAOffs = CRAMAddrOffs_NBG[0] << 8;
LB.rotv[1].tf.PNDSize = (PNCN[0] >> 15) & 1;
LB.rotv[1].tf.CharSize = ((CHCTLA >> 0) & 1);
LB.rotv[1].tf.AuxMode = (PNCN[0] >> 14) & 1;
LB.rotv[1].tf.Supp = (PNCN[0] & 0x3FF);
}
else
{
LB.rotv[i].tf.CRAOffs = CRAMAddrOffs_RBG0 << 8;
LB.rotv[i].tf.PNDSize = (PNCNR >> 15) & 1;
LB.rotv[i].tf.CharSize = ((CHCTLB >> 8) & 1);
LB.rotv[i].tf.AuxMode = (PNCNR >> 14) & 1;
LB.rotv[i].tf.Supp = (PNCNR & 0x3FF);
}
LB.rotv[i].tf.PlaneSize = (PLSZ >> ( 8 + (i << 2))) & 0x3;
LB.rotv[i].tf.PlaneOver = (PLSZ >> (10 + (i << 2))) & 0x3;
LB.rotv[i].tf.PlaneOverChar = OVPNR[i];
LB.rotv[i].tf.Start(4 + i, !i && ((CHCTLB >> 9) & 1), (MPOFR >> (i << 2)) & 0x7, RotMapRegs[i]);
}
//
//
//
{
bool bank_tab[4];
for(unsigned i = 0; i < 4; i++)
bank_tab[i] = ((RDBS_Mode >> (i << 1)) & 0x3) == RDBS_COEFF;
//
if(!(VRAM_Mode & 0x1))
bank_tab[1] = bank_tab[0];
if(!(VRAM_Mode & 0x2))
bank_tab[3] = bank_tab[2];
if(BGON & 0x20)
bank_tab[2] = bank_tab[3] = false;
//
if(CRKTE)
bank_tab[0] = bank_tab[1] = bank_tab[2] = bank_tab[3] = true;
//
//
LB.rotv[0].use_coeff = (bool)(KTCTL[0] & 0x1);
LB.rotv[1].use_coeff = (bool)(KTCTL[1] & 0x1);
uint32 coeff[2];
for(unsigned i = 0; i < 2; i++)
LB.rotv[i].base_coeff = coeff[i] = ReadCoeff(i, GetCoeffAddr(i, rs[i].KAstAccum));
//if(grumpus == 120)
// printf("BankTab: %d %d %d %d, UC: %d %d, Coeff: @0x%05x=0x%08x @0x%05x=0x%08x, DKAx: %f %f\n", bank_tab[0], bank_tab[1], bank_tab[2], bank_tab[3], LB.rotv[0].use_coeff, LB.rotv[1].use_coeff, GetCoeffAddr(0, rs[0].KAstAccum), coeff[0], GetCoeffAddr(1, rs[1].KAstAccum), coeff[1], rs[0].DKAx / 1024.0, rs[1].DKAx / 1024.0);
for(unsigned x = 0; MDFN_LIKELY(x < rbg_w); x++)
{
const unsigned i = ((EffRPMD == 2) ? 0 : LB.rotabsel[x]);
const uint32 addr = GetCoeffAddr(i, rs[i].KAstAccum + (x * rs[i].DKAx));
if(bank_tab[addr >> 16])
coeff[i] = ReadCoeff(i, addr);
if(KTCTL[i] & 0x10)
LB.lc[x] = (coeff[i] >> 24) & 0x7F;
if(EffRPMD == 2)
{
uint32 tmp = coeff[0];
LB.rotabsel[x] = tmp >> 31;
if((int32)tmp < 0)
tmp = coeff[1];
LB.rotcoeff[x] = tmp;
}
else
LB.rotcoeff[x] = coeff[i];
}
}
}
// const bool TA_bmen = ((rn == 1) ? false : ((CHCTLB >> 9) & 1));
template<bool TA_bmen, unsigned TA_bpp, bool TA_isrgb, bool TA_igntp, unsigned TA_PrioMode, unsigned TA_CCMode>
static void T_DrawRBG(const bool rn, uint64* bgbuf, const unsigned w, const uint32 pix_base_or)
{
// Full color format selection for both RBG0 and RBG1
// Bitmap only allowed for RBG0
// RBG0 can use rot param A and B, RBG1 is fixed to rot param B
// RBG1 shares setting bitfields with NBG0
// 16 planes instead of 4 like with NBG*
// Mosaic only has effect in the horizontal direction?
//
int16 sfcode_lut[8];
MakeSFCodeLUT<TA_PrioMode, TA_CCMode>((rn ? 0 : 4), sfcode_lut);
for(unsigned i = 0; MDFN_LIKELY(i < w); i++)
{
const unsigned ab = LB.rotabsel[i];
auto& r = LB.rotv[ab];
auto& tf = r.tf;
uint32 Xp = r.Xp;
int32 kx = r.kx;
int32 ky = r.ky;
bool rot_tp = false;
if(r.use_coeff)
{
const uint32 coeff = (rn ? r.base_coeff : LB.rotcoeff[i]);
rot_tp = ((int32)coeff < 0);
const uint32 sext = sign_x_to_s32(24, coeff);
switch((KTCTL[ab] >> 2) & 0x3)
{
case 0: kx = ky = sext; break;
case 1: kx = sext; break;
case 2: ky = sext; break;
case 3: Xp = sext << 2; break;
}
}
const uint32 ix = ( Xp + (uint32)(((int64)kx * (int32)(r.Xsp + (r.dX * i))) >> 16)) >> 10;
const uint32 iy = (r.Yp + (uint32)(((int64)ky * (int32)(r.Ysp + (r.dY * i))) >> 16)) >> 10;
rot_tp |= tf.Fetch<TA_bpp>(TA_bmen, ix, iy);
LB.rotabsel[i] = rot_tp;
//
//
//
bgbuf[i] = MakeNBGRBGPix<TA_bmen, TA_bpp, TA_isrgb, TA_igntp, TA_PrioMode, TA_CCMode>(tf, pix_base_or, sfcode_lut, ix, iy);
}
}
//template<unsigned TA_bpp, bool TA_isrgb, bool TA_igntp, unsigned TA_PrioMode, unsigned TA_CCMode>
static void (*DrawRBG[2 /*bitmap enable*/][5/*col mode*/][2/*igntp*/][3/*priomode*/][4/*ccmode*/])(const bool rn, uint64* bgbuf, const unsigned w, const uint32 pix_base_or) =
{
{
{ { { T_DrawRBG<0, 4, 0, 0, 0, 0>, T_DrawRBG<0, 4, 0, 0, 0, 1>, T_DrawRBG<0, 4, 0, 0, 0, 2>, T_DrawRBG<0, 4, 0, 0, 0, 3>, }, { T_DrawRBG<0, 4, 0, 0, 1, 0>, T_DrawRBG<0, 4, 0, 0, 1, 1>, T_DrawRBG<0, 4, 0, 0, 1, 2>, T_DrawRBG<0, 4, 0, 0, 1, 3>, }, { T_DrawRBG<0, 4, 0, 0, 2, 0>, T_DrawRBG<0, 4, 0, 0, 2, 1>, T_DrawRBG<0, 4, 0, 0, 2, 2>, T_DrawRBG<0, 4, 0, 0, 2, 3>, }, }, { { T_DrawRBG<0, 4, 0, 1, 0, 0>, T_DrawRBG<0, 4, 0, 1, 0, 1>, T_DrawRBG<0, 4, 0, 1, 0, 2>, T_DrawRBG<0, 4, 0, 1, 0, 3>, }, { T_DrawRBG<0, 4, 0, 1, 1, 0>, T_DrawRBG<0, 4, 0, 1, 1, 1>, T_DrawRBG<0, 4, 0, 1, 1, 2>, T_DrawRBG<0, 4, 0, 1, 1, 3>, }, { T_DrawRBG<0, 4, 0, 1, 2, 0>, T_DrawRBG<0, 4, 0, 1, 2, 1>, T_DrawRBG<0, 4, 0, 1, 2, 2>, T_DrawRBG<0, 4, 0, 1, 2, 3>, }, }, },
{ { { T_DrawRBG<0, 8, 0, 0, 0, 0>, T_DrawRBG<0, 8, 0, 0, 0, 1>, T_DrawRBG<0, 8, 0, 0, 0, 2>, T_DrawRBG<0, 8, 0, 0, 0, 3>, }, { T_DrawRBG<0, 8, 0, 0, 1, 0>, T_DrawRBG<0, 8, 0, 0, 1, 1>, T_DrawRBG<0, 8, 0, 0, 1, 2>, T_DrawRBG<0, 8, 0, 0, 1, 3>, }, { T_DrawRBG<0, 8, 0, 0, 2, 0>, T_DrawRBG<0, 8, 0, 0, 2, 1>, T_DrawRBG<0, 8, 0, 0, 2, 2>, T_DrawRBG<0, 8, 0, 0, 2, 3>, }, }, { { T_DrawRBG<0, 8, 0, 1, 0, 0>, T_DrawRBG<0, 8, 0, 1, 0, 1>, T_DrawRBG<0, 8, 0, 1, 0, 2>, T_DrawRBG<0, 8, 0, 1, 0, 3>, }, { T_DrawRBG<0, 8, 0, 1, 1, 0>, T_DrawRBG<0, 8, 0, 1, 1, 1>, T_DrawRBG<0, 8, 0, 1, 1, 2>, T_DrawRBG<0, 8, 0, 1, 1, 3>, }, { T_DrawRBG<0, 8, 0, 1, 2, 0>, T_DrawRBG<0, 8, 0, 1, 2, 1>, T_DrawRBG<0, 8, 0, 1, 2, 2>, T_DrawRBG<0, 8, 0, 1, 2, 3>, }, }, },
{ { { T_DrawRBG<0, 16, 0, 0, 0, 0>, T_DrawRBG<0, 16, 0, 0, 0, 1>, T_DrawRBG<0, 16, 0, 0, 0, 2>, T_DrawRBG<0, 16, 0, 0, 0, 3>, }, { T_DrawRBG<0, 16, 0, 0, 1, 0>, T_DrawRBG<0, 16, 0, 0, 1, 1>, T_DrawRBG<0, 16, 0, 0, 1, 2>, T_DrawRBG<0, 16, 0, 0, 1, 3>, }, { T_DrawRBG<0, 16, 0, 0, 2, 0>, T_DrawRBG<0, 16, 0, 0, 2, 1>, T_DrawRBG<0, 16, 0, 0, 2, 2>, T_DrawRBG<0, 16, 0, 0, 2, 3>, }, }, { { T_DrawRBG<0, 16, 0, 1, 0, 0>, T_DrawRBG<0, 16, 0, 1, 0, 1>, T_DrawRBG<0, 16, 0, 1, 0, 2>, T_DrawRBG<0, 16, 0, 1, 0, 3>, }, { T_DrawRBG<0, 16, 0, 1, 1, 0>, T_DrawRBG<0, 16, 0, 1, 1, 1>, T_DrawRBG<0, 16, 0, 1, 1, 2>, T_DrawRBG<0, 16, 0, 1, 1, 3>, }, { T_DrawRBG<0, 16, 0, 1, 2, 0>, T_DrawRBG<0, 16, 0, 1, 2, 1>, T_DrawRBG<0, 16, 0, 1, 2, 2>, T_DrawRBG<0, 16, 0, 1, 2, 3>, }, }, },
{ { { T_DrawRBG<0, 16, 1, 0, 0, 0>, T_DrawRBG<0, 16, 1, 0, 0, 1>, T_DrawRBG<0, 16, 1, 0, 0, 2>, T_DrawRBG<0, 16, 1, 0, 0, 3>, }, { T_DrawRBG<0, 16, 1, 0, 1, 0>, T_DrawRBG<0, 16, 1, 0, 1, 1>, T_DrawRBG<0, 16, 1, 0, 1, 2>, T_DrawRBG<0, 16, 1, 0, 1, 3>, }, { T_DrawRBG<0, 16, 1, 0, 2, 0>, T_DrawRBG<0, 16, 1, 0, 2, 1>, T_DrawRBG<0, 16, 1, 0, 2, 2>, T_DrawRBG<0, 16, 1, 0, 2, 3>, }, }, { { T_DrawRBG<0, 16, 1, 1, 0, 0>, T_DrawRBG<0, 16, 1, 1, 0, 1>, T_DrawRBG<0, 16, 1, 1, 0, 2>, T_DrawRBG<0, 16, 1, 1, 0, 3>, }, { T_DrawRBG<0, 16, 1, 1, 1, 0>, T_DrawRBG<0, 16, 1, 1, 1, 1>, T_DrawRBG<0, 16, 1, 1, 1, 2>, T_DrawRBG<0, 16, 1, 1, 1, 3>, }, { T_DrawRBG<0, 16, 1, 1, 2, 0>, T_DrawRBG<0, 16, 1, 1, 2, 1>, T_DrawRBG<0, 16, 1, 1, 2, 2>, T_DrawRBG<0, 16, 1, 1, 2, 3>, }, }, },
{ { { T_DrawRBG<0, 32, 1, 0, 0, 0>, T_DrawRBG<0, 32, 1, 0, 0, 1>, T_DrawRBG<0, 32, 1, 0, 0, 2>, T_DrawRBG<0, 32, 1, 0, 0, 3>, }, { T_DrawRBG<0, 32, 1, 0, 1, 0>, T_DrawRBG<0, 32, 1, 0, 1, 1>, T_DrawRBG<0, 32, 1, 0, 1, 2>, T_DrawRBG<0, 32, 1, 0, 1, 3>, }, { T_DrawRBG<0, 32, 1, 0, 2, 0>, T_DrawRBG<0, 32, 1, 0, 2, 1>, T_DrawRBG<0, 32, 1, 0, 2, 2>, T_DrawRBG<0, 32, 1, 0, 2, 3>, }, }, { { T_DrawRBG<0, 32, 1, 1, 0, 0>, T_DrawRBG<0, 32, 1, 1, 0, 1>, T_DrawRBG<0, 32, 1, 1, 0, 2>, T_DrawRBG<0, 32, 1, 1, 0, 3>, }, { T_DrawRBG<0, 32, 1, 1, 1, 0>, T_DrawRBG<0, 32, 1, 1, 1, 1>, T_DrawRBG<0, 32, 1, 1, 1, 2>, T_DrawRBG<0, 32, 1, 1, 1, 3>, }, { T_DrawRBG<0, 32, 1, 1, 2, 0>, T_DrawRBG<0, 32, 1, 1, 2, 1>, T_DrawRBG<0, 32, 1, 1, 2, 2>, T_DrawRBG<0, 32, 1, 1, 2, 3>, }, }, },
},
{
{ { { T_DrawRBG<1, 4, 0, 0, 0, 0>, T_DrawRBG<1, 4, 0, 0, 0, 1>, T_DrawRBG<1, 4, 0, 0, 0, 2>, T_DrawRBG<1, 4, 0, 0, 0, 3>, }, { T_DrawRBG<1, 4, 0, 0, 1, 0>, T_DrawRBG<1, 4, 0, 0, 1, 1>, T_DrawRBG<1, 4, 0, 0, 1, 2>, T_DrawRBG<1, 4, 0, 0, 1, 3>, }, { T_DrawRBG<1, 4, 0, 0, 2, 0>, T_DrawRBG<1, 4, 0, 0, 2, 1>, T_DrawRBG<1, 4, 0, 0, 2, 2>, T_DrawRBG<1, 4, 0, 0, 2, 3>, }, }, { { T_DrawRBG<1, 4, 0, 1, 0, 0>, T_DrawRBG<1, 4, 0, 1, 0, 1>, T_DrawRBG<1, 4, 0, 1, 0, 2>, T_DrawRBG<1, 4, 0, 1, 0, 3>, }, { T_DrawRBG<1, 4, 0, 1, 1, 0>, T_DrawRBG<1, 4, 0, 1, 1, 1>, T_DrawRBG<1, 4, 0, 1, 1, 2>, T_DrawRBG<1, 4, 0, 1, 1, 3>, }, { T_DrawRBG<1, 4, 0, 1, 2, 0>, T_DrawRBG<1, 4, 0, 1, 2, 1>, T_DrawRBG<1, 4, 0, 1, 2, 2>, T_DrawRBG<1, 4, 0, 1, 2, 3>, }, }, },
{ { { T_DrawRBG<1, 8, 0, 0, 0, 0>, T_DrawRBG<1, 8, 0, 0, 0, 1>, T_DrawRBG<1, 8, 0, 0, 0, 2>, T_DrawRBG<1, 8, 0, 0, 0, 3>, }, { T_DrawRBG<1, 8, 0, 0, 1, 0>, T_DrawRBG<1, 8, 0, 0, 1, 1>, T_DrawRBG<1, 8, 0, 0, 1, 2>, T_DrawRBG<1, 8, 0, 0, 1, 3>, }, { T_DrawRBG<1, 8, 0, 0, 2, 0>, T_DrawRBG<1, 8, 0, 0, 2, 1>, T_DrawRBG<1, 8, 0, 0, 2, 2>, T_DrawRBG<1, 8, 0, 0, 2, 3>, }, }, { { T_DrawRBG<1, 8, 0, 1, 0, 0>, T_DrawRBG<1, 8, 0, 1, 0, 1>, T_DrawRBG<1, 8, 0, 1, 0, 2>, T_DrawRBG<1, 8, 0, 1, 0, 3>, }, { T_DrawRBG<1, 8, 0, 1, 1, 0>, T_DrawRBG<1, 8, 0, 1, 1, 1>, T_DrawRBG<1, 8, 0, 1, 1, 2>, T_DrawRBG<1, 8, 0, 1, 1, 3>, }, { T_DrawRBG<1, 8, 0, 1, 2, 0>, T_DrawRBG<1, 8, 0, 1, 2, 1>, T_DrawRBG<1, 8, 0, 1, 2, 2>, T_DrawRBG<1, 8, 0, 1, 2, 3>, }, }, },
{ { { T_DrawRBG<1, 16, 0, 0, 0, 0>, T_DrawRBG<1, 16, 0, 0, 0, 1>, T_DrawRBG<1, 16, 0, 0, 0, 2>, T_DrawRBG<1, 16, 0, 0, 0, 3>, }, { T_DrawRBG<1, 16, 0, 0, 1, 0>, T_DrawRBG<1, 16, 0, 0, 1, 1>, T_DrawRBG<1, 16, 0, 0, 1, 2>, T_DrawRBG<1, 16, 0, 0, 1, 3>, }, { T_DrawRBG<1, 16, 0, 0, 2, 0>, T_DrawRBG<1, 16, 0, 0, 2, 1>, T_DrawRBG<1, 16, 0, 0, 2, 2>, T_DrawRBG<1, 16, 0, 0, 2, 3>, }, }, { { T_DrawRBG<1, 16, 0, 1, 0, 0>, T_DrawRBG<1, 16, 0, 1, 0, 1>, T_DrawRBG<1, 16, 0, 1, 0, 2>, T_DrawRBG<1, 16, 0, 1, 0, 3>, }, { T_DrawRBG<1, 16, 0, 1, 1, 0>, T_DrawRBG<1, 16, 0, 1, 1, 1>, T_DrawRBG<1, 16, 0, 1, 1, 2>, T_DrawRBG<1, 16, 0, 1, 1, 3>, }, { T_DrawRBG<1, 16, 0, 1, 2, 0>, T_DrawRBG<1, 16, 0, 1, 2, 1>, T_DrawRBG<1, 16, 0, 1, 2, 2>, T_DrawRBG<1, 16, 0, 1, 2, 3>, }, }, },
{ { { T_DrawRBG<1, 16, 1, 0, 0, 0>, T_DrawRBG<1, 16, 1, 0, 0, 1>, T_DrawRBG<1, 16, 1, 0, 0, 2>, T_DrawRBG<1, 16, 1, 0, 0, 3>, }, { T_DrawRBG<1, 16, 1, 0, 1, 0>, T_DrawRBG<1, 16, 1, 0, 1, 1>, T_DrawRBG<1, 16, 1, 0, 1, 2>, T_DrawRBG<1, 16, 1, 0, 1, 3>, }, { T_DrawRBG<1, 16, 1, 0, 2, 0>, T_DrawRBG<1, 16, 1, 0, 2, 1>, T_DrawRBG<1, 16, 1, 0, 2, 2>, T_DrawRBG<1, 16, 1, 0, 2, 3>, }, }, { { T_DrawRBG<1, 16, 1, 1, 0, 0>, T_DrawRBG<1, 16, 1, 1, 0, 1>, T_DrawRBG<1, 16, 1, 1, 0, 2>, T_DrawRBG<1, 16, 1, 1, 0, 3>, }, { T_DrawRBG<1, 16, 1, 1, 1, 0>, T_DrawRBG<1, 16, 1, 1, 1, 1>, T_DrawRBG<1, 16, 1, 1, 1, 2>, T_DrawRBG<1, 16, 1, 1, 1, 3>, }, { T_DrawRBG<1, 16, 1, 1, 2, 0>, T_DrawRBG<1, 16, 1, 1, 2, 1>, T_DrawRBG<1, 16, 1, 1, 2, 2>, T_DrawRBG<1, 16, 1, 1, 2, 3>, }, }, },
{ { { T_DrawRBG<1, 32, 1, 0, 0, 0>, T_DrawRBG<1, 32, 1, 0, 0, 1>, T_DrawRBG<1, 32, 1, 0, 0, 2>, T_DrawRBG<1, 32, 1, 0, 0, 3>, }, { T_DrawRBG<1, 32, 1, 0, 1, 0>, T_DrawRBG<1, 32, 1, 0, 1, 1>, T_DrawRBG<1, 32, 1, 0, 1, 2>, T_DrawRBG<1, 32, 1, 0, 1, 3>, }, { T_DrawRBG<1, 32, 1, 0, 2, 0>, T_DrawRBG<1, 32, 1, 0, 2, 1>, T_DrawRBG<1, 32, 1, 0, 2, 2>, T_DrawRBG<1, 32, 1, 0, 2, 3>, }, }, { { T_DrawRBG<1, 32, 1, 1, 0, 0>, T_DrawRBG<1, 32, 1, 1, 0, 1>, T_DrawRBG<1, 32, 1, 1, 0, 2>, T_DrawRBG<1, 32, 1, 1, 0, 3>, }, { T_DrawRBG<1, 32, 1, 1, 1, 0>, T_DrawRBG<1, 32, 1, 1, 1, 1>, T_DrawRBG<1, 32, 1, 1, 1, 2>, T_DrawRBG<1, 32, 1, 1, 1, 3>, }, { T_DrawRBG<1, 32, 1, 1, 2, 0>, T_DrawRBG<1, 32, 1, 1, 2, 1>, T_DrawRBG<1, 32, 1, 1, 2, 2>, T_DrawRBG<1, 32, 1, 1, 2, 3>, }, }, },
}
};
template<typename T>
static INLINE void Doubleize(T* ptr, const int orig_len)
{
for(int i = orig_len - 1; MDFN_LIKELY(i >= 0); i--)
{
auto tmp = *(ptr + i);
*(ptr + (i << 1) + 0) = tmp;
*(ptr + (i << 1) + 1) = tmp;
}
}
static void RBGPP(const unsigned layer, uint64* buf, const unsigned rbg_w)
{
ApplyHMosaic(layer, buf, rbg_w);
for(unsigned i = 0; i < rbg_w; i++)
{
uint64 tmp = buf[i];
if(LB.rotabsel[i])
tmp &= ~(uint64)0xFFFFFFFF;
buf[i] = tmp;
}
if(HRes & 0x2)
Doubleize(buf, rbg_w);
ApplyWin(layer, buf);
}
// Call before DrawSpriteData()
static INLINE void MakeSpriteCCLUT(void)
{
const bool cce = ((CCCTL >> 6) & 1);
for(unsigned pr = 0; pr < 8; pr++)
{
bool mask = false;
switch(SpriteCCCond)
{
case 0: mask = (SpritePrioNum[pr] <= SpriteCCNum); break;
case 1: mask = (SpritePrioNum[pr] == SpriteCCNum); break;
case 2: mask = (SpritePrioNum[pr] >= SpriteCCNum); break;
}
SpriteCCLUT[pr] = (cce & mask) << PIX_CCE_SHIFT;
}
SpriteCC3Mask = 0;
if(SpriteCCCond == 3 && cce)
SpriteCC3Mask = 1U << PIX_CCE_SHIFT;
}
template<bool TA_HiRes, bool TA_TPShadSel, unsigned TA_SPCTL_Low>
static void T_DrawSpriteData(const uint16* vdp1sb, const bool vdp1_hires8, unsigned w)
{
const unsigned SpriteType = (TA_SPCTL_Low & 0xF);
const bool SpriteWinEn = (TA_SPCTL_Low & 0x10);
const bool SpriteColorMode = (TA_SPCTL_Low & 0x20);
//
const size_t cao = CRAMAddrOffs_Sprite << 8;
uint32 spix_base_or = 0;
spix_base_or |= ((ColorOffsEn >> 6) & 1) << PIX_COE_SHIFT;
spix_base_or |= ((ColorOffsSel >> 6) & 1) << PIX_COSEL_SHIFT;
spix_base_or |= ((LineColorEn >> 5/*5 here, not 6*/) & 1) << PIX_LCE_SHIFT;
spix_base_or |= (((CCCTL >> 12) & 0x7) == 0x0) << PIX_GRAD_SHIFT;
spix_base_or |= ((CCCTL >> 6) & 1) << PIX_LAYER_CCE_SHIFT;
for(unsigned i = 0; MDFN_LIKELY(i < w); i++)
{
unsigned src;
unsigned pr = 0, cc = 0;
bool tp = false;
uint64 spix;
src = vdp1sb[i >> TA_HiRes];
if(vdp1_hires8)
{
if(TA_HiRes)
src = 0xFF00 | (src >> (((i & 1) ^ 1) << 3));
else
src = 0xFF00 | (src >> 8);
}
if(SpriteColorMode && (src & 0x8000))
{
spix = (uint64)rgb15_to_rgb24(src) << PIX_RGB_SHIFT;
spix |= 1U << PIX_ISRGB_SHIFT;
spix |= SpriteCC3Mask;
if(SpriteType & 0x8)
tp = !(src & 0xFF);
else if(SpriteWinEn)
{
if(SpriteType >= 0x2 && SpriteType <= 0x7)
tp = !(src & 0x7FFF);
}
}
else
{
bool nshad = false;
bool sd = false;
unsigned dc;
if(SpriteType & 0x8)
src &= 0xFF;
tp = !src;
switch(SpriteType)
{
case 0x0:
pr = (src >> 14) & 0x3;
cc = (src >> 11) & 0x7;
dc = src & 0x7FF;
nshad = (dc == 0x7FE);
break;
case 0x1:
pr = (src >> 13) & 0x7;
cc = (src >> 11) & 0x3;
dc = src & 0x7FF;
nshad = (dc == 0x7FE);
break;
case 0x2:
sd = (src >> 15) & 0x1;
pr = (src >> 14) & 0x1;
cc = (src >> 11) & 0x7;
dc = src & 0x7FF;
nshad = (dc == 0x7FE);
break;
case 0x3:
sd = (src >> 15) & 0x1;
pr = (src >> 13) & 0x3;
cc = (src >> 11) & 0x3;
dc = src & 0x7FF;
nshad = (dc == 0x7FE);
break;
case 0x4:
sd = (src >> 15) & 0x1;
pr = (src >> 13) & 0x3;
cc = (src >> 10) & 0x7;
dc = src & 0x3FF;
nshad = (dc == 0x3FE);
break;
case 0x5:
sd = (src >> 15) & 0x1;
pr = (src >> 12) & 0x7;
cc = (src >> 11) & 0x1;
dc = src & 0x7FF;
nshad = (dc == 0x7FE);
break;
case 0x6:
sd = (src >> 15) & 0x1;
pr = (src >> 12) & 0x7;
cc = (src >> 10) & 0x3;
dc = src & 0x3FF;
nshad = (dc == 0x3FE);
break;
case 0x7:
sd = (src >> 15) & 0x1;
pr = (src >> 12) & 0x7;
cc = (src >> 9) & 0x7;
dc = src & 0x1FF;
nshad = (dc == 0x1FE);
break;
//
//
//
case 0x8:
pr = (src >> 7) & 0x1;
dc = src & 0x7F;
nshad = (dc == 0x7E);
break;
case 0x9:
pr = (src >> 7) & 0x1;
cc = (src >> 6) & 0x1;
dc = src & 0x3F;
nshad = (dc == 0x3E);
break;
case 0xA:
pr = (src >> 6) & 0x3;
dc = src & 0x3F;
nshad = (dc == 0x3E);
break;
case 0xB:
cc = (src >> 6) & 0x3;
dc = src & 0x3F;
nshad = (dc == 0x3E);
break;
//
case 0xC:
pr = (src >> 7) & 0x1;
dc = src & 0xFF;
nshad = (dc == 0xFE);
break;
case 0xD:
pr = (src >> 7) & 0x1;
cc = (src >> 6) & 0x1;
dc = src & 0xFF;
nshad = (dc == 0xFE);
break;
case 0xE:
pr = (src >> 6) & 0x3;
dc = src & 0xFF;
nshad = (dc == 0xFE);
break;
case 0xF:
cc = (src >> 6) & 0x3;
dc = src & 0xFF;
nshad = (dc == 0xFE);
break;
}
//
//
//
uint32 rgb24 = ColorCache[(cao + dc) & 0x7FF];
spix = (uint64)rgb24 << PIX_RGB_SHIFT;
spix |= ((int32)rgb24 >> 31) & SpriteCC3Mask;
if(SpriteWinEn) // Sprite window enable
spix |= ((uint64)sd << PIX_SWBIT_SHIFT);
if(nshad)
spix |= 1 << PIX_DOSHAD_SHIFT;
else
{
if(SpriteWinEn)
{
if(SpriteType >= 0x2 && SpriteType <= 0x7)
tp = !(src & 0x7FFF);
}
else if(sd)
{
if(src & 0x7FFF)
spix |= 1 << PIX_SELFSHAD_SHIFT;
else if(TA_TPShadSel)
spix |= 1 << PIX_DOSHAD_SHIFT;
else
tp = true;
}
}
}
spix |= spix_base_or;
spix |= (tp ? 0 : SpritePrioNum[pr]) << PIX_PRIO_SHIFT;
spix |= SpriteCCRatio[cc] << PIX_CCRATIO_SHIFT;
spix |= SpriteCCLUT[pr];
LB.spr[i] = spix;
}
}
static void (*DrawSpriteData[2][2][0x40])(const uint16* vdp1sb, const bool vdp1_hires8, unsigned w) =
{
{
{ T_DrawSpriteData<0, 0, 0x00>, T_DrawSpriteData<0, 0, 0x01>, T_DrawSpriteData<0, 0, 0x02>, T_DrawSpriteData<0, 0, 0x03>, T_DrawSpriteData<0, 0, 0x04>, T_DrawSpriteData<0, 0, 0x05>, T_DrawSpriteData<0, 0, 0x06>, T_DrawSpriteData<0, 0, 0x07>, T_DrawSpriteData<0, 0, 0x08>, T_DrawSpriteData<0, 0, 0x09>, T_DrawSpriteData<0, 0, 0x0a>, T_DrawSpriteData<0, 0, 0x0b>, T_DrawSpriteData<0, 0, 0x0c>, T_DrawSpriteData<0, 0, 0x0d>, T_DrawSpriteData<0, 0, 0x0e>, T_DrawSpriteData<0, 0, 0x0f>, T_DrawSpriteData<0, 0, 0x10>, T_DrawSpriteData<0, 0, 0x11>, T_DrawSpriteData<0, 0, 0x12>, T_DrawSpriteData<0, 0, 0x13>, T_DrawSpriteData<0, 0, 0x14>, T_DrawSpriteData<0, 0, 0x15>, T_DrawSpriteData<0, 0, 0x16>, T_DrawSpriteData<0, 0, 0x17>, T_DrawSpriteData<0, 0, 0x18>, T_DrawSpriteData<0, 0, 0x19>, T_DrawSpriteData<0, 0, 0x1a>, T_DrawSpriteData<0, 0, 0x1b>, T_DrawSpriteData<0, 0, 0x1c>, T_DrawSpriteData<0, 0, 0x1d>, T_DrawSpriteData<0, 0, 0x1e>, T_DrawSpriteData<0, 0, 0x1f>, T_DrawSpriteData<0, 0, 0x20>, T_DrawSpriteData<0, 0, 0x21>, T_DrawSpriteData<0, 0, 0x22>, T_DrawSpriteData<0, 0, 0x23>, T_DrawSpriteData<0, 0, 0x24>, T_DrawSpriteData<0, 0, 0x25>, T_DrawSpriteData<0, 0, 0x26>, T_DrawSpriteData<0, 0, 0x27>, T_DrawSpriteData<0, 0, 0x28>, T_DrawSpriteData<0, 0, 0x29>, T_DrawSpriteData<0, 0, 0x2a>, T_DrawSpriteData<0, 0, 0x2b>, T_DrawSpriteData<0, 0, 0x2c>, T_DrawSpriteData<0, 0, 0x2d>, T_DrawSpriteData<0, 0, 0x2e>, T_DrawSpriteData<0, 0, 0x2f>, T_DrawSpriteData<0, 0, 0x30>, T_DrawSpriteData<0, 0, 0x31>, T_DrawSpriteData<0, 0, 0x32>, T_DrawSpriteData<0, 0, 0x33>, T_DrawSpriteData<0, 0, 0x34>, T_DrawSpriteData<0, 0, 0x35>, T_DrawSpriteData<0, 0, 0x36>, T_DrawSpriteData<0, 0, 0x37>, T_DrawSpriteData<0, 0, 0x38>, T_DrawSpriteData<0, 0, 0x39>, T_DrawSpriteData<0, 0, 0x3a>, T_DrawSpriteData<0, 0, 0x3b>, T_DrawSpriteData<0, 0, 0x3c>, T_DrawSpriteData<0, 0, 0x3d>, T_DrawSpriteData<0, 0, 0x3e>, T_DrawSpriteData<0, 0, 0x3f> },
{ T_DrawSpriteData<0, 1, 0x00>, T_DrawSpriteData<0, 1, 0x01>, T_DrawSpriteData<0, 1, 0x02>, T_DrawSpriteData<0, 1, 0x03>, T_DrawSpriteData<0, 1, 0x04>, T_DrawSpriteData<0, 1, 0x05>, T_DrawSpriteData<0, 1, 0x06>, T_DrawSpriteData<0, 1, 0x07>, T_DrawSpriteData<0, 1, 0x08>, T_DrawSpriteData<0, 1, 0x09>, T_DrawSpriteData<0, 1, 0x0a>, T_DrawSpriteData<0, 1, 0x0b>, T_DrawSpriteData<0, 1, 0x0c>, T_DrawSpriteData<0, 1, 0x0d>, T_DrawSpriteData<0, 1, 0x0e>, T_DrawSpriteData<0, 1, 0x0f>, T_DrawSpriteData<0, 1, 0x10>, T_DrawSpriteData<0, 1, 0x11>, T_DrawSpriteData<0, 1, 0x12>, T_DrawSpriteData<0, 1, 0x13>, T_DrawSpriteData<0, 1, 0x14>, T_DrawSpriteData<0, 1, 0x15>, T_DrawSpriteData<0, 1, 0x16>, T_DrawSpriteData<0, 1, 0x17>, T_DrawSpriteData<0, 1, 0x18>, T_DrawSpriteData<0, 1, 0x19>, T_DrawSpriteData<0, 1, 0x1a>, T_DrawSpriteData<0, 1, 0x1b>, T_DrawSpriteData<0, 1, 0x1c>, T_DrawSpriteData<0, 1, 0x1d>, T_DrawSpriteData<0, 1, 0x1e>, T_DrawSpriteData<0, 1, 0x1f>, T_DrawSpriteData<0, 1, 0x20>, T_DrawSpriteData<0, 1, 0x21>, T_DrawSpriteData<0, 1, 0x22>, T_DrawSpriteData<0, 1, 0x23>, T_DrawSpriteData<0, 1, 0x24>, T_DrawSpriteData<0, 1, 0x25>, T_DrawSpriteData<0, 1, 0x26>, T_DrawSpriteData<0, 1, 0x27>, T_DrawSpriteData<0, 1, 0x28>, T_DrawSpriteData<0, 1, 0x29>, T_DrawSpriteData<0, 1, 0x2a>, T_DrawSpriteData<0, 1, 0x2b>, T_DrawSpriteData<0, 1, 0x2c>, T_DrawSpriteData<0, 1, 0x2d>, T_DrawSpriteData<0, 1, 0x2e>, T_DrawSpriteData<0, 1, 0x2f>, T_DrawSpriteData<0, 1, 0x30>, T_DrawSpriteData<0, 1, 0x31>, T_DrawSpriteData<0, 1, 0x32>, T_DrawSpriteData<0, 1, 0x33>, T_DrawSpriteData<0, 1, 0x34>, T_DrawSpriteData<0, 1, 0x35>, T_DrawSpriteData<0, 1, 0x36>, T_DrawSpriteData<0, 1, 0x37>, T_DrawSpriteData<0, 1, 0x38>, T_DrawSpriteData<0, 1, 0x39>, T_DrawSpriteData<0, 1, 0x3a>, T_DrawSpriteData<0, 1, 0x3b>, T_DrawSpriteData<0, 1, 0x3c>, T_DrawSpriteData<0, 1, 0x3d>, T_DrawSpriteData<0, 1, 0x3e>, T_DrawSpriteData<0, 1, 0x3f> },
},
{
{ T_DrawSpriteData<1, 0, 0x00>, T_DrawSpriteData<1, 0, 0x01>, T_DrawSpriteData<1, 0, 0x02>, T_DrawSpriteData<1, 0, 0x03>, T_DrawSpriteData<1, 0, 0x04>, T_DrawSpriteData<1, 0, 0x05>, T_DrawSpriteData<1, 0, 0x06>, T_DrawSpriteData<1, 0, 0x07>, T_DrawSpriteData<1, 0, 0x08>, T_DrawSpriteData<1, 0, 0x09>, T_DrawSpriteData<1, 0, 0x0a>, T_DrawSpriteData<1, 0, 0x0b>, T_DrawSpriteData<1, 0, 0x0c>, T_DrawSpriteData<1, 0, 0x0d>, T_DrawSpriteData<1, 0, 0x0e>, T_DrawSpriteData<1, 0, 0x0f>, T_DrawSpriteData<1, 0, 0x10>, T_DrawSpriteData<1, 0, 0x11>, T_DrawSpriteData<1, 0, 0x12>, T_DrawSpriteData<1, 0, 0x13>, T_DrawSpriteData<1, 0, 0x14>, T_DrawSpriteData<1, 0, 0x15>, T_DrawSpriteData<1, 0, 0x16>, T_DrawSpriteData<1, 0, 0x17>, T_DrawSpriteData<1, 0, 0x18>, T_DrawSpriteData<1, 0, 0x19>, T_DrawSpriteData<1, 0, 0x1a>, T_DrawSpriteData<1, 0, 0x1b>, T_DrawSpriteData<1, 0, 0x1c>, T_DrawSpriteData<1, 0, 0x1d>, T_DrawSpriteData<1, 0, 0x1e>, T_DrawSpriteData<1, 0, 0x1f>, T_DrawSpriteData<1, 0, 0x20>, T_DrawSpriteData<1, 0, 0x21>, T_DrawSpriteData<1, 0, 0x22>, T_DrawSpriteData<1, 0, 0x23>, T_DrawSpriteData<1, 0, 0x24>, T_DrawSpriteData<1, 0, 0x25>, T_DrawSpriteData<1, 0, 0x26>, T_DrawSpriteData<1, 0, 0x27>, T_DrawSpriteData<1, 0, 0x28>, T_DrawSpriteData<1, 0, 0x29>, T_DrawSpriteData<1, 0, 0x2a>, T_DrawSpriteData<1, 0, 0x2b>, T_DrawSpriteData<1, 0, 0x2c>, T_DrawSpriteData<1, 0, 0x2d>, T_DrawSpriteData<1, 0, 0x2e>, T_DrawSpriteData<1, 0, 0x2f>, T_DrawSpriteData<1, 0, 0x30>, T_DrawSpriteData<1, 0, 0x31>, T_DrawSpriteData<1, 0, 0x32>, T_DrawSpriteData<1, 0, 0x33>, T_DrawSpriteData<1, 0, 0x34>, T_DrawSpriteData<1, 0, 0x35>, T_DrawSpriteData<1, 0, 0x36>, T_DrawSpriteData<1, 0, 0x37>, T_DrawSpriteData<1, 0, 0x38>, T_DrawSpriteData<1, 0, 0x39>, T_DrawSpriteData<1, 0, 0x3a>, T_DrawSpriteData<1, 0, 0x3b>, T_DrawSpriteData<1, 0, 0x3c>, T_DrawSpriteData<1, 0, 0x3d>, T_DrawSpriteData<1, 0, 0x3e>, T_DrawSpriteData<1, 0, 0x3f> },
{ T_DrawSpriteData<1, 1, 0x00>, T_DrawSpriteData<1, 1, 0x01>, T_DrawSpriteData<1, 1, 0x02>, T_DrawSpriteData<1, 1, 0x03>, T_DrawSpriteData<1, 1, 0x04>, T_DrawSpriteData<1, 1, 0x05>, T_DrawSpriteData<1, 1, 0x06>, T_DrawSpriteData<1, 1, 0x07>, T_DrawSpriteData<1, 1, 0x08>, T_DrawSpriteData<1, 1, 0x09>, T_DrawSpriteData<1, 1, 0x0a>, T_DrawSpriteData<1, 1, 0x0b>, T_DrawSpriteData<1, 1, 0x0c>, T_DrawSpriteData<1, 1, 0x0d>, T_DrawSpriteData<1, 1, 0x0e>, T_DrawSpriteData<1, 1, 0x0f>, T_DrawSpriteData<1, 1, 0x10>, T_DrawSpriteData<1, 1, 0x11>, T_DrawSpriteData<1, 1, 0x12>, T_DrawSpriteData<1, 1, 0x13>, T_DrawSpriteData<1, 1, 0x14>, T_DrawSpriteData<1, 1, 0x15>, T_DrawSpriteData<1, 1, 0x16>, T_DrawSpriteData<1, 1, 0x17>, T_DrawSpriteData<1, 1, 0x18>, T_DrawSpriteData<1, 1, 0x19>, T_DrawSpriteData<1, 1, 0x1a>, T_DrawSpriteData<1, 1, 0x1b>, T_DrawSpriteData<1, 1, 0x1c>, T_DrawSpriteData<1, 1, 0x1d>, T_DrawSpriteData<1, 1, 0x1e>, T_DrawSpriteData<1, 1, 0x1f>, T_DrawSpriteData<1, 1, 0x20>, T_DrawSpriteData<1, 1, 0x21>, T_DrawSpriteData<1, 1, 0x22>, T_DrawSpriteData<1, 1, 0x23>, T_DrawSpriteData<1, 1, 0x24>, T_DrawSpriteData<1, 1, 0x25>, T_DrawSpriteData<1, 1, 0x26>, T_DrawSpriteData<1, 1, 0x27>, T_DrawSpriteData<1, 1, 0x28>, T_DrawSpriteData<1, 1, 0x29>, T_DrawSpriteData<1, 1, 0x2a>, T_DrawSpriteData<1, 1, 0x2b>, T_DrawSpriteData<1, 1, 0x2c>, T_DrawSpriteData<1, 1, 0x2d>, T_DrawSpriteData<1, 1, 0x2e>, T_DrawSpriteData<1, 1, 0x2f>, T_DrawSpriteData<1, 1, 0x30>, T_DrawSpriteData<1, 1, 0x31>, T_DrawSpriteData<1, 1, 0x32>, T_DrawSpriteData<1, 1, 0x33>, T_DrawSpriteData<1, 1, 0x34>, T_DrawSpriteData<1, 1, 0x35>, T_DrawSpriteData<1, 1, 0x36>, T_DrawSpriteData<1, 1, 0x37>, T_DrawSpriteData<1, 1, 0x38>, T_DrawSpriteData<1, 1, 0x39>, T_DrawSpriteData<1, 1, 0x3a>, T_DrawSpriteData<1, 1, 0x3b>, T_DrawSpriteData<1, 1, 0x3c>, T_DrawSpriteData<1, 1, 0x3d>, T_DrawSpriteData<1, 1, 0x3e>, T_DrawSpriteData<1, 1, 0x3f> },
}
};
// Don't change these constants without also updating the template variable
// setup for the call into MixIt(and the contents of MixIt itself...).
enum
{
MIXIT_SPECIAL_NONE = 0x0,
MIXIT_SPECIAL_GRAD = 0x1,
MIXIT_SPECIAL_EXCC_CRAM0 = 0x2,
MIXIT_SPECIAL_EXCC_CRAM12 = 0x3,
MIXIT_SPECIAL_EXCC_LINE_CRAM0 = 0x4,
MIXIT_SPECIAL_EXCC_LINE_CRAM12 = 0x5
};
template<bool TA_rbg1en, unsigned TA_Special, bool TA_CCRTMD, bool TA_CCMD>
static void T_MixIt(uint32* target, const unsigned vdp2_line, const unsigned w, const uint32 back_rgb24, const uint64* blursrc)
{
//printf("MixIt: %d, %d, %d, %d\n", TA_rbg1en, TA_Special, TA_CCRTMD, TA_CCMD);
const uint32* lclut = &ColorCache[CurLCColor &~ 0x7F];
uint32 blurprev[2];
if(TA_Special == MIXIT_SPECIAL_GRAD)
blurprev[0] = blurprev[1] = *blursrc >> PIX_RGB_SHIFT;
uint32 line_pix_l;
{
line_pix_l = 0U << PIX_ISRGB_SHIFT;
line_pix_l |= LineColorCCRatio << PIX_CCRATIO_SHIFT;
line_pix_l |= ((CCCTL >> 5) & 1) << PIX_CCE_SHIFT;
line_pix_l |= ((CCCTL >> 5) & 1) << PIX_LAYER_CCE_SHIFT;
}
//
//
uint64 back_pix;
{
back_pix = (uint64)back_rgb24 << PIX_RGB_SHIFT;
back_pix |= 1U << PIX_ISRGB_SHIFT;
back_pix |= ((ColorOffsEn >> 5) & 1) << PIX_COE_SHIFT;
back_pix |= ((ColorOffsSel >> 5) & 1) << PIX_COSEL_SHIFT;
back_pix |= ((SDCTL >> 5) & 1) << PIX_SHADEN_SHIFT;
back_pix |= BackCCRatio << PIX_CCRATIO_SHIFT;
}
for(uint32 i = 0; MDFN_LIKELY(i < w); i++)
{
uint64 pix = back_pix;
uint32 blurcake;
//
// Listed from lowest priority to greatest priority when prio levels are equal(back pixel has prio level of 0,
// and should display on "top" of any other layers).
//
uint64 tmp_pix[8] =
{
(TA_rbg1en ? 0 : (LB.nbg[3] + 8)[i]),
(TA_rbg1en ? 0 : (LB.nbg[2] + 8)[i]),
(TA_rbg1en ? 0 : (LB.nbg[1] + 8)[i]),
(LB.nbg[0] + 8)[i],
LB.rbg0[i],
LB.spr[i],
0/*null pixel*/,
back_pix
};
uint64 pt;
unsigned st;
pt = 0x01ULL << (uint8)(tmp_pix[0] >> PIX_PRIO_TEST_SHIFT);
pt |= 0x02ULL << (uint8)(tmp_pix[1] >> PIX_PRIO_TEST_SHIFT);
pt |= 0x04ULL << (uint8)(tmp_pix[2] >> PIX_PRIO_TEST_SHIFT);
pt |= 0x08ULL << (uint8)(tmp_pix[3] >> PIX_PRIO_TEST_SHIFT);
pt |= 0x10ULL << (uint8)(tmp_pix[4] >> PIX_PRIO_TEST_SHIFT);
pt |= 0x20ULL << (uint8)(tmp_pix[5] >> PIX_PRIO_TEST_SHIFT);
pt |= 0xC0ULL; // Back pixel(0x80) and null pixel(0x40)
st = 63 ^ MDFN_lzcount64_0UD(pt);
pt ^= 1ULL << st;
pt |= 0x40; // Restore the null!
pix = tmp_pix[st & 0x7];
if(pix & (1U << PIX_DOSHAD_SHIFT))
{
st = 63 ^ MDFN_lzcount64_0UD(pt);
pt ^= 1ULL << st;
pt |= 0x40; // Restore the null!
pix = tmp_pix[st & 0x7];
pix |= (1U << PIX_DOSHAD_SHIFT);
}
if(TA_Special == MIXIT_SPECIAL_GRAD)
{
const uint32 blurpie = blursrc[i] >> PIX_RGB_SHIFT;
blurcake = ((blurprev[0] + blurprev[1]) - ((blurprev[0] ^ blurprev[1]) & 0x01010101)) >> 1;
blurcake = ((blurcake + blurpie) - ((blurcake ^ blurpie) & 0x01010101)) >> 1;
blurprev[0] = blurprev[1];
blurprev[1] = blurpie;
}
//
// Color calculation
//
if(pix & (1U << PIX_CCE_SHIFT))
{
uint64 pix2, pix3;
st = 63 ^ MDFN_lzcount64_0UD(pt);
pt ^= 1ULL << st;
pt |= 0x40; // Restore the null!
pix2 = tmp_pix[st & 0x7];
st = 63 ^ MDFN_lzcount64_0UD(pt);
pt ^= 1ULL << st;
pt |= 0x40; // Restore the null!
pix3 = tmp_pix[st & 0x7];
if(TA_Special == MIXIT_SPECIAL_GRAD)
{
if((pix | pix2) & (1U << PIX_GRAD_SHIFT))
pix2 = (uint32)pix2 | ((uint64)blurcake << PIX_RGB_SHIFT); // Be sure to preserve the color calc ratio, at least.
}
else if(pix & (1U << PIX_LCE_SHIFT))
{
//
// Line color
//
const uint64 pix4 = pix3;
const uint32 line_pix_rgb = lclut[LB.lc[i]];
pix3 = pix2;
pix2 = line_pix_l | ((uint64)line_pix_rgb << PIX_RGB_SHIFT);
if(TA_Special == MIXIT_SPECIAL_EXCC_LINE_CRAM0)
{
uint32 sec_rgb = line_pix_rgb;
uint32 third_rgb = (pix3 >> PIX_RGB_SHIFT);
if(pix3 & (1U << PIX_LAYER_CCE_SHIFT))
third_rgb = (third_rgb >> 1) & 0x7F7F7F;
sec_rgb = ((sec_rgb + third_rgb) - ((sec_rgb ^ third_rgb) & 0x01010101)) >> 1;
pix2 = (uint32)pix2 | ((uint64)sec_rgb << PIX_RGB_SHIFT);
}
else if(TA_Special == MIXIT_SPECIAL_EXCC_LINE_CRAM12)
{
uint32 sec_rgb = line_pix_rgb;
uint32 third_rgb = (pix3 >> PIX_RGB_SHIFT);
if(pix3 & (1U << PIX_ISRGB_SHIFT))
{
if((pix3 & (1U << PIX_LAYER_CCE_SHIFT)) && (pix4 & (1U << PIX_ISRGB_SHIFT)))
{
const uint32 fourth_rgb = (pix4 >> PIX_RGB_SHIFT);
third_rgb = ((third_rgb + fourth_rgb) - ((third_rgb ^ fourth_rgb) & 0x01010101)) >> 1;
}
sec_rgb = ((sec_rgb + third_rgb) - ((sec_rgb ^ third_rgb) & 0x01010101)) >> 1;
pix2 = (uint32)pix2 | ((uint64)sec_rgb << PIX_RGB_SHIFT);
}
}
}
else
{
if(TA_Special == MIXIT_SPECIAL_EXCC_CRAM0 || TA_Special == MIXIT_SPECIAL_EXCC_CRAM12 || TA_Special == MIXIT_SPECIAL_EXCC_LINE_CRAM0 || TA_Special == MIXIT_SPECIAL_EXCC_LINE_CRAM12)
{
if(pix2 & (1U << PIX_LAYER_CCE_SHIFT))
{
if(TA_Special == MIXIT_SPECIAL_EXCC_CRAM0 || TA_Special == MIXIT_SPECIAL_EXCC_LINE_CRAM0 || (pix3 & (1U << PIX_ISRGB_SHIFT)))
{
uint32 sec_rgb = pix2 >> PIX_RGB_SHIFT;
const uint32 third_rgb = (pix3 >> PIX_RGB_SHIFT);
sec_rgb = ((sec_rgb + third_rgb) - ((sec_rgb ^ third_rgb) & 0x01010101)) >> 1;
pix2 = (uint32)pix2 | ((uint64)sec_rgb << PIX_RGB_SHIFT);
}
}
}
}
uint32 fore_rgb = pix >> PIX_RGB_SHIFT;
uint32 sec_rgb = pix2 >> PIX_RGB_SHIFT;
uint32 new_rgb;
if(TA_CCMD) // Ignore ratio, add as-is.
{
new_rgb = std::min<unsigned>(0x0000FF, (fore_rgb & 0x0000FF) + (sec_rgb & 0x0000FF));
new_rgb |= std::min<unsigned>(0x00FF00, (fore_rgb & 0x00FF00) + (sec_rgb & 0x00FF00));
new_rgb |= std::min<unsigned>(0xFF0000, (fore_rgb & 0xFF0000) + (sec_rgb & 0xFF0000));
}
else
{
unsigned fore_ratio = ((uint32)(TA_CCRTMD ? pix2 : pix) >> PIX_CCRATIO_SHIFT) ^ 0x1F;
unsigned sec_ratio = 0x20 - fore_ratio;
new_rgb = ((((fore_rgb & 0x0000FF) * fore_ratio) + ((sec_rgb & 0x0000FF) * sec_ratio)) >> 5);
new_rgb |= ((((fore_rgb & 0x00FF00) * fore_ratio) + ((sec_rgb & 0x00FF00) * sec_ratio)) >> 5) & 0x00FF00;
new_rgb |= ((((fore_rgb & 0xFF0000) * fore_ratio) + ((sec_rgb & 0xFF0000) * sec_ratio)) >> 5) & 0xFF0000;
}
pix = ((uint64)new_rgb << 32) | (uint32)pix;
}
//
// Color offset
//
if(pix & (1U << PIX_COE_SHIFT))
{
const unsigned sel = (pix >> PIX_COSEL_SHIFT) & 1;
const uint32 rgb_tmp = pix >> PIX_RGB_SHIFT;
int32 rt, gt, bt;
rt = ColorOffs[sel][0] + (rgb_tmp & 0x000000FF);
if(rt < 0) rt = 0;
if(rt & 0x00000100) rt = 0x000000FF;
gt = ColorOffs[sel][1] + (rgb_tmp & 0x0000FF00);
if(gt < 0) gt = 0;
if(gt & 0x00010000) gt = 0x0000FF00;
bt = ColorOffs[sel][2] + (rgb_tmp & 0x00FF0000);
if(bt < 0) bt = 0;
if(bt & 0x01000000) bt = 0x00FF0000;
pix = (uint32)pix | ((uint64)(uint32)(rt | gt | bt) << PIX_RGB_SHIFT);
}
//
// Sprite shadow
//
if((uint8)pix >= PIX_SHADHALVTEST8_VAL)
pix = (uint32)pix | ((pix >> 1) & 0x7F7F7F00000000ULL);
target[i] = pix >> PIX_RGB_SHIFT;
}
}
//template<bool TA_rbg1en, unsigned TA_Special, bool TA_CCRTMD, bool TA_CCMD>
static void (*MixIt[2][6][2][2])(uint32* target, const unsigned vdp2_line, const unsigned w, const uint32 back_rgb24, const uint64* blursrc) =
{
{ { { T_MixIt<0, 0, 0, 0>, T_MixIt<0, 0, 0, 1>, }, { T_MixIt<0, 0, 1, 0>, T_MixIt<0, 0, 1, 1>, }, }, { { T_MixIt<0, 1, 0, 0>, T_MixIt<0, 1, 0, 1>, }, { T_MixIt<0, 1, 1, 0>, T_MixIt<0, 1, 1, 1>, }, }, { { T_MixIt<0, 2, 0, 0>, T_MixIt<0, 2, 0, 1>, }, { T_MixIt<0, 2, 1, 0>, T_MixIt<0, 2, 1, 1>, }, }, { { T_MixIt<0, 3, 0, 0>, T_MixIt<0, 3, 0, 1>, }, { T_MixIt<0, 3, 1, 0>, T_MixIt<0, 3, 1, 1>, }, }, { { T_MixIt<0, 4, 0, 0>, T_MixIt<0, 4, 0, 1>, }, { T_MixIt<0, 4, 1, 0>, T_MixIt<0, 4, 1, 1>, }, }, { { T_MixIt<0, 5, 0, 0>, T_MixIt<0, 5, 0, 1>, }, { T_MixIt<0, 5, 1, 0>, T_MixIt<0, 5, 1, 1>, }, }, },
{ { { T_MixIt<1, 0, 0, 0>, T_MixIt<1, 0, 0, 1>, }, { T_MixIt<1, 0, 1, 0>, T_MixIt<1, 0, 1, 1>, }, }, { { T_MixIt<1, 1, 0, 0>, T_MixIt<1, 1, 0, 1>, }, { T_MixIt<1, 1, 1, 0>, T_MixIt<1, 1, 1, 1>, }, }, { { T_MixIt<1, 2, 0, 0>, T_MixIt<1, 2, 0, 1>, }, { T_MixIt<1, 2, 1, 0>, T_MixIt<1, 2, 1, 1>, }, }, { { T_MixIt<1, 3, 0, 0>, T_MixIt<1, 3, 0, 1>, }, { T_MixIt<1, 3, 1, 0>, T_MixIt<1, 3, 1, 1>, }, }, { { T_MixIt<1, 4, 0, 0>, T_MixIt<1, 4, 0, 1>, }, { T_MixIt<1, 4, 1, 0>, T_MixIt<1, 4, 1, 1>, }, }, { { T_MixIt<1, 5, 0, 0>, T_MixIt<1, 5, 0, 1>, }, { T_MixIt<1, 5, 1, 0>, T_MixIt<1, 5, 1, 1>, }, }, },
};
static int32 ApplyHBlend(uint32* const target, int32 w)
{
#define BHALF(m, n) ((((uint64)(m) + (n)) - (((m) ^ (n)) & 0x01010101)) >> 1)
assert(w >= 4);
#if 1
if(!(HRes & 0x2))
{
target[(w - 1) * 2 + 1] = target[w - 1];
target[(w - 1) * 2 + 0] = BHALF(BHALF(target[w - 2], target[w - 1]), target[w - 1]);
for(int32 x = w - 2; x > 0; x--)
{
uint32 ptxm1 = target[x - 1];
uint32 ptx = target[x];
uint32 ptxp1 = target[x + 1];
uint32 ptxm1_ptx = BHALF(ptxm1, ptx);
uint32 ptx_ptxp1 = BHALF(ptx, ptxp1);
target[x * 2 + 0] = BHALF(ptxm1_ptx, ptx);
target[x * 2 + 1] = BHALF(ptx_ptxp1, ptx);
}
target[1] = BHALF(BHALF(target[0], target[1]), target[0]);
target[0] = target[0];
return w << 1;
}
else
#else
if(!(HRes & 0x2))
{
for(int32 x = w - 1; x >= 0; x--)
target[x * 2 + 0] = target[x * 2 + 1] = target[x];
w <<= 1;
}
#endif
{
uint32 a = target[0];
for(int32 x = 0; x < w - 1; x++)
{
uint32 b = target[x];
uint32 c = target[x + 1];
uint32 ac = BHALF(a, c);
uint32 bac = BHALF(b, ac);
target[x] = bac;
a = b;
}
return w;
}
#undef BHALF
}
static void ReorderRGB(uint32* target, const unsigned w)
{
const unsigned Rshift = 16;
const unsigned Gshift = 8;
const unsigned Bshift = 0;
assert(!(w & 1));
uint32* const bound = target + w;
while(MDFN_LIKELY(target != bound))
{
const uint32 tmp0 = target[0];
const uint32 tmp1 = target[1];
target[0] = ((uint8)(tmp0 >> 0) << Rshift) |
((uint8)(tmp0 >> 8) << Gshift) |
((uint8)(tmp0 >> 16) << Bshift);
target[1] = ((uint8)(tmp1 >> 0) << Rshift) |
((uint8)(tmp1 >> 8) << Gshift) |
((uint8)(tmp1 >> 16) << Bshift);
target += 2;
}
}
static NO_INLINE void DrawLine(const uint16 out_line, const uint16 vdp2_line, const bool field)
{
uint32* target;
const int32 tvdw = ((!CorrectAspect || Clock28M) ? 352 : 330) << ((HRes & 0x2) >> 1);
const unsigned rbg_w = ((HRes & 0x1) ? 352 : 320);
const unsigned w = ((HRes & 0x1) ? 352 : 320) << ((HRes & 0x2) >> 1);
const int32 tvxo = std::max<int32>(0, (int32)(tvdw - w) >> 1);
uint32 back_rgb24;
uint32 border_ncf;
target = espec->pixels + out_line * espec->pitch32;
espec->LineWidths[out_line] = tvdw;
if(!ShowHOverscan)
{
const int32 ntdw = tvdw * 1024 / 1056;
const int32 tadj = std::max<int32>(0, espec->x - ((tvdw - ntdw) >> 1));
//if(out_line == 100)
// printf("tvdw=%d, ntdw=%d, tadj=%d --- tvdw+tadj=%d\n", tvdw, ntdw, tadj, tvdw + tadj);
assert((tvdw + tadj) <= 704);
target += tadj;
espec->LineWidths[out_line] = ntdw;
}
//
// FIXME: Timing
//
if(vdp2_line == 0)
{
CurBackTabAddr = (BKTA & 0x7FFFF) + ((BKTA & 0x80000000) && InterlaceMode == IM_DOUBLE && field);
CurLCTabAddr = (LCTA & 0x7FFFF) + ((LCTA & 0x80000000) && InterlaceMode == IM_DOUBLE && field);
for(unsigned n = 0; n < 2; n++)
{
YCoordAccum[n] = (InterlaceMode == IM_DOUBLE && field) ? YCoordInc[n] : 0;
CurLSA[n] = LineScrollAddr[n];
if(InterlaceMode == IM_DOUBLE && field)
{
const uint8 sc = (SCRCTL >> (n << 3));
CurLSA[n] += ((bool)(sc & 0x2) + (bool)(sc & 0x4) + (bool)(sc & 0x8)) << 1;
}
//
//
NBG23_YCounter[n & 1] = YScrollI[2 + n];
}
for(unsigned d = 0; d < 2; d++)
{
Window[d].CurLineWinAddr = Window[d].LineWinAddr;
if(InterlaceMode == IM_DOUBLE && field)
Window[d].CurLineWinAddr += 2;
}
MosaicVCount = 0;
}
if(vdp2_line != 0xFFFF)
{
CurBackColor = VRAM[CurBackTabAddr & 0x3FFFF] & 0x7FFF;
if(BKTA & 0x80000000)
CurBackTabAddr += 1 << (InterlaceMode == IM_DOUBLE);
//
CurLCColor = VRAM[CurLCTabAddr & 0x3FFFF] & 0x07FF;
if(LCTA & 0x80000000)
CurLCTabAddr += 1 << (InterlaceMode == IM_DOUBLE);
}
back_rgb24 = rgb15_to_rgb24(CurBackColor);
if(BorderMode)
border_ncf = back_rgb24 & 0xff00 | back_rgb24 << 16 && 0xff0000 | back_rgb24 >> 16 & 0xff;
else
border_ncf = 0;
if(vdp2_line == 0xFFFF)
{
for(int32 i = 0; i < tvdw; i++)
target[i] = border_ncf;
}
else
{
//
// Line scroll
//
unsigned ls_comp_line = vdp2_line;
for(unsigned n = 0; n < 2; n++)
{
const uint8 sc = (SCRCTL >> (n << 3));
const uint8 lss = ((sc >> 4) & 0x3);
if((ls_comp_line & ((1 << lss) - 1)) == 0)
{
if(sc & 0x2) // X
{
CurXScrollIF[n] = (VRAM[CurLSA[n] & 0x3FFFF] & 0x7FF) << 8;
CurLSA[n]++;
CurXScrollIF[n] |= VRAM[CurLSA[n] & 0x3FFFF] >> 8;
CurLSA[n]++;
CurXScrollIF[n] += (XScrollI[n] << 8) + XScrollF[n];
}
if(sc & 0x4) // Y
{
YCoordAccum[n] = 0; // Don't (InterlaceMode == IM_DOUBLE && field)
//
CurYScrollIF[n] = (VRAM[CurLSA[n] & 0x3FFFF] & 0x7FF) << 8;
CurLSA[n]++;
CurYScrollIF[n] |= VRAM[CurLSA[n] & 0x3FFFF] >> 8;
CurLSA[n]++;
CurYScrollIF[n] += (YScrollI[n] << 8) + YScrollF[n];
//printf("%d %d %08x: %08x \n", vdp2_line, n, CurLSA[n], CurYScrollIF[n]);
}
if(sc & 0x8) // X zoom
{
CurXCoordInc[n] = (VRAM[CurLSA[n] & 0x3FFFF] & 0x7) << 8;
CurLSA[n]++;
CurXCoordInc[n] |= VRAM[CurLSA[n] & 0x3FFFF] >> 8;
CurLSA[n]++;
}
if(InterlaceMode == IM_DOUBLE)
CurLSA[n] += ((bool)(sc & 0x2) + (bool)(sc & 0x4) + (bool)(sc & 0x8)) << 1;
}
if(!(sc & 0x2))
CurXScrollIF[n] = (XScrollI[n] << 8) + XScrollF[n];
if(!(sc & 0x4))
CurYScrollIF[n] = (YScrollI[n] << 8) + YScrollF[n];
if(!(sc & 0x8))
CurXCoordInc[n] = XCoordInc[n];
}
//
// Line Window
//
{
int32 w_ycomp_line;
if(InterlaceMode == IM_DOUBLE)
w_ycomp_line = (vdp2_line << 1) + field;
else
w_ycomp_line = vdp2_line;
for(unsigned d = 0; d < 2; d++)
{
int32 ys = Window[d].YStart, ye = Window[d].YEnd;
if(Window[d].LineWinEn)
{
const uint16* vrt = &VRAM[Window[d].CurLineWinAddr & 0x3FFFE];
Window[d].XStart = vrt[0] & 0x3FF;
Window[d].XEnd = vrt[1] & 0x3FF;
//printf("LWin %d, %d(%08x): %04x %04x\n", vdp2_line, d, Window[d].CurLineWinAddr & 0x3FFFE, vrt[0], vrt[1]);
}
//
//
//
int32 xs = Window[d].XStart, xe = Window[d].XEnd;
// FIXME: Kludge, until we can figure out what's going on.
if(xs >= 0x380)
xs = 0;
// FIXME: Kludge, until we can figure out what's going on.
if(xe >= 0x380)
{
xs = 2;
xe = 0;
}
if(!(HRes & 0x2))
{
xs >>= 1;
xe >>= 1;
}
Window[d].CurXStart = xs;
Window[d].CurXEnd = xe;
Window[d].CurLineWinAddr += 2 << (InterlaceMode == IM_DOUBLE);
if(InterlaceMode != IM_DOUBLE)
{
ys >>= 1;
ye >>= 1;
}
Window[d].YMet = (w_ycomp_line >= Window[d].YStart) & (w_ycomp_line <= Window[d].YEnd);
//
//
//
}
//
//
//
WinPieces[0] = Window[0].CurXStart;
WinPieces[1] = Window[0].CurXEnd + 1;
WinPieces[2] = Window[1].CurXStart;
WinPieces[3] = Window[1].CurXEnd + 1;
WinPieces[4] = w;
for(unsigned piece = 0; piece < WinPieces.size(); piece++)
WinPieces[piece] = std::min<unsigned>(w, WinPieces[piece]); // Almost forgot to do this...
std::sort(WinPieces.begin(), WinPieces.end());
}
//
//
//
if(vdp2_line == 64)
{
//printf("%d:%d, %d:%d (%d) --- %d:%d, %d:%d (%d)\n", Window[0].XStart, Window[0].YStart, Window[0].XEnd, Window[0].YEnd, Window[0].LineWinEn, Window[1].XStart, Window[1].YStart, Window[1].XEnd, Window[1].YEnd, Window[1].LineWinEn);
//printf("SPCTL_Low: %02x, SDCTL: %03x, SpriteCCCond: %01x, CCNum: %01x -- %01x %01x %01x %01x %01x %01x %01x %01x \n", SPCTL_Low, SDCTL, SpriteCCCond, SpriteCCNum, SpriteCCRatio[0], SpriteCCRatio[1], SpriteCCRatio[2], SpriteCCRatio[3], SpriteCCRatio[4], SpriteCCRatio[5], SpriteCCRatio[6], SpriteCCRatio[7]);
//printf("WinControl[WINLAYER_CC]=%02x\n", WinControl[WINLAYER_CC]);
}
//
// Process sprite data before NBG0-3 and RBG0-1, but defer applying the window until after NBG and RBG are handled(so the sprite window
// bit in the sprite linebuffer data isn't trashed prematurely).
//
if(MDFN_LIKELY(UserLayerEnableMask & (1U << 6)))
{
MakeSpriteCCLUT();
DrawSpriteData[(HRes & 0x2) >> 0x1][(SDCTL >> 8) & 0x1][SPCTL_Low](LIB[vdp2_line].vdp1_line, LIB[vdp2_line].vdp1_hires8, w);
}
else
MDFN_FastArraySet(LB.spr, 0, w);
//
//
//
//
//
//
//
if(BGON & 0x30)
{
MDFN_FastArraySet(LB.lc, CurLCColor & 0x7F, rbg_w);
SetupRotVars(LIB[vdp2_line].rv, rbg_w);
if(HRes & 0x2)
Doubleize(LB.lc, rbg_w);
// RBG0
if(MDFN_LIKELY(UserLayerEnableMask & 0x10))
{
const bool igntp = (BGON >> 12) & 1;
const bool bmen = (CHCTLB >> 9) & 1;
const unsigned colornum = std::min<unsigned>(4, (CHCTLB >> 12) & 0x7); // TODO: Test 5 ... 7
const unsigned priomode = (SFPRMD >> 8) & 0x3;
const unsigned ccmode = (CCCTL & 0x10) ? ((SFCCMD >> 8) & 0x3) : 0;
const uint32 prio = RBG0PrioNum;
uint32 pix_base_or;
pix_base_or = ((colornum >= 3) << PIX_ISRGB_SHIFT);
pix_base_or |= ((ColorOffsEn >> 4) & 1) << PIX_COE_SHIFT;
pix_base_or |= ((ColorOffsSel >> 4) & 1) << PIX_COSEL_SHIFT;
pix_base_or |= ((LineColorEn >> 4) & 1) << PIX_LCE_SHIFT;
pix_base_or |= RBG0CCRatio << PIX_CCRATIO_SHIFT;
pix_base_or |= (((CCCTL >> 12) & 0x7) == 0x1) << PIX_GRAD_SHIFT;
pix_base_or |= ((CCCTL >> 4) & 1) << PIX_LAYER_CCE_SHIFT;
pix_base_or |= ((SDCTL >> 4) & 1) << PIX_SHADEN_SHIFT;
if(ccmode == 0)
pix_base_or |= ((CCCTL >> 4) & 1) << PIX_CCE_SHIFT;
if(priomode >= 1)
pix_base_or |= ((prio &~ 1) << PIX_PRIO_SHIFT);
else
pix_base_or |= (prio << PIX_PRIO_SHIFT);
DrawRBG[bmen][colornum][igntp][priomode % 3][ccmode](0, LB.rbg0, rbg_w, pix_base_or);
RBGPP(4, LB.rbg0, rbg_w);
}
else
MDFN_FastArraySet(LB.rbg0, 0, w);
// RBG1
if(BGON & UserLayerEnableMask & 0x20)
{
const bool igntp = (BGON >> 8) & 1;
const unsigned colornum = std::min<unsigned>(4, (CHCTLA >> 4) & 0x7); // TODO: Test 5 ... 7
const unsigned priomode = (SFPRMD >> 0) & 0x3;
const unsigned ccmode = (CCCTL & 0x01) ? ((SFCCMD >> 0) & 0x3) : 0;
const uint32 prio = NBGPrioNum[0];
uint32 pix_base_or;
pix_base_or = (false << PIX_ISRGB_SHIFT);
pix_base_or |= ((ColorOffsEn >> 0) & 1) << PIX_COE_SHIFT;
pix_base_or |= ((ColorOffsSel >> 0) & 1) << PIX_COSEL_SHIFT;
pix_base_or |= ((LineColorEn >> 0) & 1) << PIX_LCE_SHIFT;
pix_base_or |= NBGCCRatio[0] << PIX_CCRATIO_SHIFT;
pix_base_or |= (((CCCTL >> 12) & 0x7) == 0x2) << PIX_GRAD_SHIFT;
pix_base_or |= ((CCCTL >> 0) & 1) << PIX_LAYER_CCE_SHIFT;
pix_base_or |= ((SDCTL >> 0) & 1) << PIX_SHADEN_SHIFT;
if(ccmode == 0)
pix_base_or |= ((CCCTL >> 0) & 1) << PIX_CCE_SHIFT;
if(priomode >= 1)
pix_base_or |= ((prio &~ 1) << PIX_PRIO_SHIFT);
else
pix_base_or |= (prio << PIX_PRIO_SHIFT);
MDFN_FastArraySet(LB.rotabsel, 1, rbg_w);
DrawRBG[false][colornum][igntp][priomode % 3][ccmode](1, LB.nbg[0] + 8, rbg_w, pix_base_or);
RBGPP(0, LB.nbg[0] + 8, rbg_w);
}
else if(BGON & 0x20)
MDFN_FastArraySet(LB.nbg[0] + 8, 0, w);
}
else
{
MDFN_FastArraySet(LB.lc, CurLCColor & 0x7F, w);
MDFN_FastArraySet(LB.rbg0, 0, w);
}
//
//
//
for(unsigned n = 0; n < 4; n++)
{
if(!MosaicVCount || !(MZCTL & (1U << n)))
{
if(n < 2)
{
MosEff_YCoordAccum[n] = YCoordAccum[n]; // Don't + (InterlaceMode == IM_DOUBLE && field)
}
else
{
MosEff_NBG23_YCounter[n & 1] = NBG23_YCounter[n & 1] + (InterlaceMode == IM_DOUBLE && field);
}
}
}
if(SCRCTL & 0x0101)
FetchVCScroll(w); // Call after handling line scroll, and before DrawNBG() stuff
if(!(BGON & 0x20))
{
for(unsigned n = 0; n < 4; n++)
{
if(((BGON >> n) & 1) && MDFN_LIKELY((UserLayerEnableMask >> n) & 1))
{
const bool igntp = (BGON >> (n + 8)) & 1;
bool bmen = false;
unsigned colornum;
unsigned priomode;
unsigned ccmode;
if(n < 2)
{
const unsigned nshift = (n & 1) << 3;
bmen = (CHCTLA >> (1 + nshift)) & 1;
colornum = (CHCTLA >> (4 + nshift)) & (n ? 0x3 : 0x7);
}
else // n >= 2
{
const unsigned nshift = (n & 1) << 2;
colornum = (CHCTLB >> (1 + nshift)) & 1;
}
if(colornum > 4) // TODO: test 5 ... 7
colornum = 4;
priomode = (SFPRMD >> (n << 1)) & 0x3;
ccmode = (SFCCMD >> (n << 1)) & 0x3;
if(!((CCCTL >> n) & 1))
ccmode = 0;
//
//
const uint32 prio = NBGPrioNum[n];
uint32 pix_base_or;
pix_base_or = ((colornum >= 3) << PIX_ISRGB_SHIFT);
pix_base_or |= ((ColorOffsEn >> n) & 1) << PIX_COE_SHIFT;
pix_base_or |= ((ColorOffsSel >> n) & 1) << PIX_COSEL_SHIFT;
pix_base_or |= ((LineColorEn >> n) & 1) << PIX_LCE_SHIFT;
pix_base_or |= NBGCCRatio[n] << PIX_CCRATIO_SHIFT;
pix_base_or |= (((CCCTL >> 12) & 0x7) == (3 + n - !n)) << PIX_GRAD_SHIFT;
pix_base_or |= ((CCCTL >> n) & 1) << PIX_LAYER_CCE_SHIFT;
pix_base_or |= ((SDCTL >> n) & 1) << PIX_SHADEN_SHIFT;
if(ccmode == 0)
pix_base_or |= ((CCCTL >> n) & 1) << PIX_CCE_SHIFT;
if(priomode >= 1)
pix_base_or |= ((prio &~ 1) << PIX_PRIO_SHIFT);
else
pix_base_or |= (prio << PIX_PRIO_SHIFT);
if(n < 2)
DrawNBG[bmen][colornum][igntp][priomode % 3][ccmode](n, LB.nbg[n] + 8, w, pix_base_or);
else
DrawNBG23[colornum][igntp][priomode % 3][ccmode](n, LB.nbg[n] + 8, w, pix_base_or);
ApplyHMosaic(n, LB.nbg[n] + 8, w);
ApplyWin(n, LB.nbg[n] + 8);
}
else
MDFN_FastArraySet(LB.nbg[n] + 8, 0, w);
}
}
//
//
//
//
//
// Apply window to sprite linebuffer after BG layers have windows applied.
ApplyWin(WINLAYER_SPRITE, LB.spr);
//
for(int32 i = 0; i < tvxo; i++)
target[i] = border_ncf;
for(int32 i = tvxo + w; i < tvdw; i++)
target[i] = border_ncf;
{
const bool rbg1en = (bool)(BGON & 0x20);
unsigned special = MIXIT_SPECIAL_NONE;
const bool CCRTMD = (bool)(CCCTL & 0x0200);
const bool CCMD = (bool)(CCCTL & 0x0100);
static const uint64* blurremap[8] = { LB.spr, LB.rbg0, LB.nbg[0] + 8, /*Dummy:*/LB.spr,
LB.nbg[1] + 8, LB.nbg[2] + 8, LB.nbg[3] + 8, /*Dummy:*/LB.spr
};
const uint64* blursrc = blurremap[(CCCTL >> 12) & 0x7];
if(!(HRes & 0x6))
{
if(CCCTL & 0x8000)
{
if(CRAM_Mode == 0)
special = MIXIT_SPECIAL_GRAD;
}
else if(CCCTL & 0x0400)
{
special = 0x2;
special += (bool)CRAM_Mode;
special += (CCCTL >> 4) & 0x2;
}
}
MixIt[rbg1en][special][CCRTMD][CCMD](target + tvxo, vdp2_line, w, back_rgb24, blursrc);
ReorderRGB(target + tvxo, w);
}
//
//
//
// FIXME: Timing
//
for(unsigned n = 0; n < 2; n++)
{
YCoordAccum[n] += YCoordInc[n] << (InterlaceMode == IM_DOUBLE);
NBG23_YCounter[n & 1] += 1 << (InterlaceMode == IM_DOUBLE);
}
if(MosaicVCount >= ((MZCTL >> 12) & 0xF))
MosaicVCount = 0;
else
MosaicVCount++;
}
//
//
//
if(DoHBlend)
{
espec->LineWidths[out_line] = ApplyHBlend(espec->pixels + out_line * espec->pitch32 + espec->x, espec->LineWidths[out_line]);
// Kind of late, but meh. ;p
assert((espec->x + espec->LineWidths[out_line]) <= 704);
}
}
enum
{
COMMAND_WRITE8 = 0,
COMMAND_WRITE16,
COMMAND_DRAW_LINE,
COMMAND_SET_LEM,
COMMAND_RESET,
COMMAND_EXIT
};
static int32 LastDrawnLine;
static INLINE void WWQ(uint16 command, uint32 arg32 = 0, uint16 arg16 = 0)
{
switch(command)
{
case COMMAND_WRITE8:
MemW<uint8>(arg32, arg16);
break;
case COMMAND_WRITE16:
MemW<uint16>(arg32, arg16);
break;
case COMMAND_DRAW_LINE:
//for(unsigned i = 0; i < 2; i++)
DrawLine((uint16)arg32, arg32 >> 16, arg16);
//
LastDrawnLine = (uint16)arg32;
break;
case COMMAND_RESET:
Reset(arg32);
break;
case COMMAND_SET_LEM:
UserLayerEnableMask = arg32;
break;
}
}
//
//
//
//
//
void VDP2REND_Init(const bool IsPAL)
{
PAL = IsPAL;
VisibleLines = PAL ? 288 : 240;
//
UserLayerEnableMask = ~0U;
}
void VDP2REND_StartFrame(EmulateSpecStruct* espec_arg, const bool clock28m, const int SurfInterlaceField)
{
OutLineCounter = 0;
Clock28M = clock28m;
espec = espec_arg;
if(SurfInterlaceField >= 0)
{
espec->LineWidths[0] = 0;
espec->InterlaceOn = true;
espec->InterlaceField = SurfInterlaceField;
}
else
espec->InterlaceOn = false;
espec->x = (ShowHOverscan ? 0 : 10);
espec->y = LineVisFirst << espec->InterlaceOn;
espec->h = (LineVisLast + 1 - LineVisFirst) << espec->InterlaceOn;
}
void VDP2REND_EndFrame(void)
{
if(OutLineCounter < VisibleLines)
{
//printf("OutLineCounter(%d) < VisibleLines(%d)\n", OutLineCounter, VisibleLines);
do
{
uint16 out_line = OutLineCounter;
uint32* target;
if(espec->InterlaceOn)
out_line = (out_line << 1) | espec->InterlaceField;
target = espec->pixels + out_line * espec->pitch32;
target[0] = target[1] = target[2] = target[3] = 0;
espec->LineWidths[out_line] = 4;
} while(++OutLineCounter < VisibleLines);
}
espec = NULL;
}
VDP2Rend_LIB* VDP2REND_GetLIB(unsigned line)
{
assert(line < (PAL ? 256 : 240)); // NO: VisibleLines);
return &LIB[line];
}
void VDP2REND_DrawLine(int vdp2_line, const bool field)
{
if(MDFN_LIKELY(OutLineCounter < VisibleLines))
{
uint16 out_line = OutLineCounter;
if(espec->InterlaceOn)
out_line = (out_line << 1) | espec->InterlaceField;
WWQ(COMMAND_DRAW_LINE, ((uint16)vdp2_line << 16) | out_line, field);
OutLineCounter++;
}
}
void VDP2REND_Reset(bool powering_up)
{
WWQ(COMMAND_RESET, powering_up);
}
void VDP2REND_SetLayerEnableMask(uint64 mask)
{
WWQ(COMMAND_SET_LEM, mask);
}
void VDP2REND_Write8_DB(uint32 A, uint16 DB)
{
//if(DrawCounter.load(std::memory_order_acquire) != 0)
WWQ(COMMAND_WRITE8, A, DB);
//else
// MemW<uint8>(A, DB);
}
void VDP2REND_Write16_DB(uint32 A, uint16 DB)
{
//if(DrawCounter.load(std::memory_order_acquire) != 0)
WWQ(COMMAND_WRITE16, A, DB);
//else
// MemW<uint16>(A, DB);
}
}