929 lines
20 KiB
C++
929 lines
20 KiB
C++
/******************************************************************************/
|
|
/* Mednafen Sega Saturn Emulation Module */
|
|
/******************************************************************************/
|
|
/* vdp2.cpp - VDP2 Emulation
|
|
** Copyright (C) 2015-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: Emulate brokenness(missing vblank) that occurs when switching from 240-height mode to 224-height mode around lines 224-239.
|
|
|
|
// TODO: Update output signals on Reset()? Might have to call VDP2::Reset() before other _Reset() then...and take special care in the
|
|
// SMPC clock change code.
|
|
|
|
#include "ss.h"
|
|
#include "vdp1.h"
|
|
#include "vdp2.h"
|
|
#include "scu.h"
|
|
#include "smpc.h"
|
|
|
|
#include "vdp2_common.h"
|
|
#include "vdp2_render.h"
|
|
|
|
namespace MDFN_IEN_SS
|
|
{
|
|
|
|
namespace VDP2
|
|
{
|
|
|
|
static bool PAL;
|
|
static sscpu_timestamp_t lastts;
|
|
//
|
|
//
|
|
//
|
|
static uint16 RawRegs[0x100]; // For debugging
|
|
|
|
static bool DisplayOn;
|
|
static bool BorderMode;
|
|
static bool ExLatchEnable;
|
|
static bool ExSyncEnable;
|
|
static bool ExBGEnable;
|
|
static bool DispAreaSelect;
|
|
|
|
static bool VRAMSize;
|
|
|
|
static uint8 HRes, VRes;
|
|
static uint8 InterlaceMode;
|
|
enum { IM_NONE, IM_ILLEGAL, IM_SINGLE, IM_DOUBLE };
|
|
|
|
static uint16 RAMCTL_Raw;
|
|
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 uint16 BGON;
|
|
static uint8 VCPRegs[4][8];
|
|
static uint32 VRAMPenalty[4];
|
|
|
|
static uint32 RPTA;
|
|
static uint8 RPRCTL[2];
|
|
static uint8 KTAOF[2];
|
|
|
|
static uint16 VRAM[262144];
|
|
|
|
static uint16 CRAM[2048];
|
|
|
|
static struct
|
|
{
|
|
// Signed values are stored sign-extended to the full 32 bits.
|
|
int32 Xst, Yst, Zst; // 1.12.10
|
|
int32 DXst, DYst; // 1. 2.10
|
|
int32 DX, DY; // 1. 2.10
|
|
int32 RotMatrix[6]; // 1. 3.10
|
|
int32 Px, Py, Pz; // 1.13. 0
|
|
int32 Cx, Cy, Cz; // 1.13. 0
|
|
int32 Mx, My; // 1.13.10
|
|
int32 kx, ky; // 1. 7.16
|
|
|
|
uint32 KAst; // 0.16.10
|
|
uint32 DKAst; // 1. 9.10
|
|
uint32 DKAx; // 1. 9.10
|
|
//
|
|
//
|
|
//
|
|
uint32 XstAccum, YstAccum; // 1.12.10 (sorta)
|
|
uint32 KAstAccum; // .10
|
|
} RotParams[2];
|
|
|
|
static void FetchRotParams(const bool field)
|
|
{
|
|
uint32 a = RPTA & 0x7FFBE;
|
|
|
|
for(unsigned i = 0; i < 2; i++)
|
|
{
|
|
auto& rp = RotParams[i];
|
|
|
|
rp.Xst = sign_x_to_s32(23, ne16_rbo_be<uint32>(VRAM, ((a + 0x00) & 0x3FFFF) << 1) >> 6);
|
|
rp.Yst = sign_x_to_s32(23, ne16_rbo_be<uint32>(VRAM, ((a + 0x02) & 0x3FFFF) << 1) >> 6);
|
|
rp.Zst = sign_x_to_s32(23, ne16_rbo_be<uint32>(VRAM, ((a + 0x04) & 0x3FFFF) << 1) >> 6);
|
|
|
|
rp.DXst = sign_x_to_s32(13, ne16_rbo_be<uint32>(VRAM, ((a + 0x06) & 0x3FFFF) << 1) >> 6);
|
|
rp.DYst = sign_x_to_s32(13, ne16_rbo_be<uint32>(VRAM, ((a + 0x08) & 0x3FFFF) << 1) >> 6);
|
|
|
|
rp.DX = sign_x_to_s32(13, ne16_rbo_be<uint32>(VRAM, ((a + 0x0A) & 0x3FFFF) << 1) >> 6);
|
|
rp.DY = sign_x_to_s32(13, ne16_rbo_be<uint32>(VRAM, ((a + 0x0C) & 0x3FFFF) << 1) >> 6);
|
|
|
|
for(unsigned m = 0; m < 6; m++)
|
|
{
|
|
rp.RotMatrix[m] = sign_x_to_s32(14, ne16_rbo_be<uint32>(VRAM, ((a + 0x0E + (m << 1)) & 0x3FFFF) << 1) >> 6);
|
|
}
|
|
|
|
rp.Px = sign_x_to_s32(14, VRAM[(a + 0x1A) & 0x3FFFF]);
|
|
rp.Py = sign_x_to_s32(14, VRAM[(a + 0x1B) & 0x3FFFF]);
|
|
rp.Pz = sign_x_to_s32(14, VRAM[(a + 0x1C) & 0x3FFFF]);
|
|
|
|
rp.Cx = sign_x_to_s32(14, VRAM[(a + 0x1E) & 0x3FFFF]);
|
|
rp.Cy = sign_x_to_s32(14, VRAM[(a + 0x1F) & 0x3FFFF]);
|
|
rp.Cz = sign_x_to_s32(14, VRAM[(a + 0x20) & 0x3FFFF]);
|
|
|
|
rp.Mx = sign_x_to_s32(24, ne16_rbo_be<uint32>(VRAM, ((a + 0x22) & 0x3FFFF) << 1) >> 6);
|
|
rp.My = sign_x_to_s32(24, ne16_rbo_be<uint32>(VRAM, ((a + 0x24) & 0x3FFFF) << 1) >> 6);
|
|
|
|
rp.kx = sign_x_to_s32(24, ne16_rbo_be<uint32>(VRAM, ((a + 0x26) & 0x3FFFF) << 1));
|
|
rp.ky = sign_x_to_s32(24, ne16_rbo_be<uint32>(VRAM, ((a + 0x28) & 0x3FFFF) << 1));
|
|
|
|
rp.KAst = ne16_rbo_be<uint32>(VRAM, ((a + 0x2A) & 0x3FFFF) << 1) >> 6;
|
|
rp.DKAst = sign_x_to_s32(20, ne16_rbo_be<uint32>(VRAM, ((a + 0x2C) & 0x3FFFF) << 1) >> 6);
|
|
rp.DKAx = sign_x_to_s32(20, ne16_rbo_be<uint32>(VRAM, ((a + 0x2E) & 0x3FFFF) << 1) >> 6);
|
|
|
|
a += 0x40;
|
|
//
|
|
// Interlace mode doesn't seem to affect operation?
|
|
//
|
|
// const bool imft = (InterlaceMode == IM_DOUBLE && field);
|
|
|
|
if(RPRCTL[i] & 0x01)
|
|
rp.XstAccum = rp.Xst; // + rp.DXst * imft;
|
|
else
|
|
rp.XstAccum += rp.DXst; // << (InterlaceMode == IM_DOUBLE);
|
|
|
|
if(RPRCTL[i] & 0x02)
|
|
rp.YstAccum = rp.Yst; // + rp.DYst * imft;
|
|
else
|
|
rp.YstAccum += rp.DYst; // << (InterlaceMode == IM_DOUBLE);
|
|
|
|
if(RPRCTL[i] & 0x04)
|
|
rp.KAstAccum = (KTAOF[i] << 26) + rp.KAst; // + rp.DKAst * imft;
|
|
else
|
|
rp.KAstAccum += rp.DKAst; // << (InterlaceMode == IM_DOUBLE);
|
|
}
|
|
}
|
|
|
|
enum
|
|
{
|
|
VPHASE_ACTIVE = 0,
|
|
|
|
VPHASE_BOTTOM_BORDER,
|
|
VPHASE_BOTTOM_BLANKING,
|
|
|
|
VPHASE_VSYNC,
|
|
|
|
VPHASE_TOP_BLANKING,
|
|
VPHASE_TOP_BORDER,
|
|
|
|
VPHASE__COUNT
|
|
};
|
|
|
|
static const int32 VTimings[2][4][VPHASE__COUNT] = // End lines
|
|
{
|
|
{ // NTSC:
|
|
{ 0x0E0, 0xE8, 0xED, 0xF0, 0x0FF, 0x107 },
|
|
{ 0x0F0, 0xF0, 0xF5, 0xF8, 0x107, 0x107 },
|
|
{ 0x0E0, 0xE8, 0xED, 0xF0, 0x0FF, 0x107 },
|
|
{ 0x0F0, 0xF0, 0xF5, 0xF8, 0x107, 0x107 },
|
|
},
|
|
{ // PAL:
|
|
// btm brdr begin, btm blnk begin, vsync begin, /***/ top blnk begin, top brdr begin, total
|
|
{ 0x0E0, 0x100, 0x103, /***/ 0x103 + 3/*?*/, 0x119, 0x139 },
|
|
{ 0x0F0, 0x108, 0x10B, /***/ 0x10B + 3/*?*/, 0x121, 0x139 },
|
|
{ 0x100, 0x110, 0x113, /***/ 0x113 + 3/*?*/, 0x129, 0x139 },
|
|
{ 0x100, 0x110, 0x113, /***/ 0x113 + 3/*?*/, 0x129, 0x139 },
|
|
},
|
|
};
|
|
|
|
static bool Out_VB; // VB output signal
|
|
|
|
static uint32 VPhase;
|
|
/*static*/ int32 VCounter;
|
|
static bool InternalVB;
|
|
static bool Odd;
|
|
//
|
|
static int SurfInterlaceField;
|
|
//
|
|
//
|
|
//
|
|
|
|
|
|
// (No 0) 8 accesses, No split: 0 added cycles
|
|
// (No 4) 4 accesses, No split: 1 added cycles
|
|
// (No 6) 2 accesses, No split: 2 added cycles
|
|
// (No 7) 1 accesses, No split: 3, Split: 3.51? added cycles
|
|
// (No 8) 0 accesses, No split: 4, Split: 5.34? added cycles
|
|
static INLINE void RecalcVRAMPenalty(void)
|
|
{
|
|
if(InternalVB)
|
|
VRAMPenalty[0] = VRAMPenalty[1] = VRAMPenalty[2] = VRAMPenalty[3] = 0;
|
|
else
|
|
{
|
|
const unsigned VRAM_Mode = (RAMCTL_Raw >> 8) & 0x3;
|
|
const unsigned RDBS_Mode = (RAMCTL_Raw & 0xFF);
|
|
const size_t sh = ((HRes & 0x6) ? 0 : 4);
|
|
uint8 vcp_type_penalty[0x10];
|
|
|
|
for(unsigned vcp_type = 0; vcp_type < 0x10; vcp_type++)
|
|
{
|
|
bool penalty;
|
|
|
|
if((vcp_type < 0x8) || vcp_type == 0xC || vcp_type == 0xD)
|
|
penalty = (bool)(BGON & (1U << (vcp_type & 0x3)));
|
|
else
|
|
penalty = false;
|
|
|
|
vcp_type_penalty[vcp_type] = penalty;
|
|
}
|
|
|
|
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;
|
|
uint32 tmp = 0;
|
|
|
|
if(BGON & 0x20)
|
|
{
|
|
if(bank >= 2 || ((BGON & 0x10) && rdbs != RDBS_UNUSED))
|
|
tmp = 8;
|
|
}
|
|
else
|
|
{
|
|
if((BGON & 0x10) && rdbs != RDBS_UNUSED)
|
|
tmp = 8;
|
|
else if(BGON & 0x0F)
|
|
{
|
|
tmp += vcp_type_penalty[VCPRegs[esb][0]];
|
|
tmp += vcp_type_penalty[VCPRegs[esb][1]];
|
|
tmp += vcp_type_penalty[VCPRegs[esb][2]];
|
|
tmp += vcp_type_penalty[VCPRegs[esb][3]];
|
|
|
|
tmp += vcp_type_penalty[VCPRegs[esb][sh + 0]];
|
|
tmp += vcp_type_penalty[VCPRegs[esb][sh + 1]];
|
|
tmp += vcp_type_penalty[VCPRegs[esb][sh + 2]];
|
|
tmp += vcp_type_penalty[VCPRegs[esb][sh + 3]];
|
|
}
|
|
}
|
|
|
|
static const uint8 tab[9] = { 0, 0, 0, 0, 1, 1, 2, 3, 4 };
|
|
VRAMPenalty[bank] = tab[tmp];
|
|
//printf("%d, %d\n", bank, tmp);
|
|
}
|
|
}
|
|
}
|
|
|
|
enum
|
|
{
|
|
HPHASE_ACTIVE = 0,
|
|
|
|
HPHASE_RIGHT_BORDER,
|
|
HPHASE_HSYNC,
|
|
|
|
// ... ? ? ?
|
|
|
|
HPHASE__COUNT
|
|
};
|
|
|
|
static const int32 HTimings[2][HPHASE__COUNT] =
|
|
{
|
|
{ 0x140, 0x15B, 0x1AB },
|
|
{ 0x160, 0x177, 0x1C7 },
|
|
};
|
|
|
|
static uint32 HPhase;
|
|
/*static*/ int32 HCounter;
|
|
|
|
static uint16 Latched_VCNT, Latched_HCNT;
|
|
static bool HVIsExLatched;
|
|
|
|
static void LatchHV(void)
|
|
{
|
|
{
|
|
unsigned vtmp;
|
|
|
|
if(VPhase >= VPHASE_VSYNC)
|
|
vtmp = VCounter + (0x200 - VTimings[PAL][VRes][VPHASE__COUNT - 1]);
|
|
else
|
|
vtmp = VCounter;
|
|
|
|
if(InterlaceMode == IM_DOUBLE)
|
|
vtmp = (vtmp << 1) | !Odd;
|
|
|
|
Latched_VCNT = vtmp;
|
|
}
|
|
|
|
if(HPhase >= HPHASE_HSYNC)
|
|
Latched_HCNT = (HCounter + (0x200 - HTimings[HRes & 1][HPHASE__COUNT - 1])) << 1;
|
|
else
|
|
Latched_HCNT = HCounter << 1;
|
|
|
|
//HVIsLatched = true; // Only on external signal-induced latching?
|
|
}
|
|
//
|
|
//
|
|
void StartFrame(EmulateSpecStruct* espec, const bool clock28m)
|
|
{
|
|
//printf("StartFrame: %d\n", SurfInterlaceField);
|
|
VDP2REND_StartFrame(espec, clock28m, SurfInterlaceField);
|
|
}
|
|
|
|
//
|
|
//
|
|
static INLINE void IncVCounter(const sscpu_timestamp_t event_timestamp)
|
|
{
|
|
VCounter = (VCounter + 1) & 0x1FF;
|
|
|
|
if(VCounter == (VTimings[PAL][VRes][VPHASE__COUNT - 1] - 1))
|
|
Out_VB = false;
|
|
|
|
// - 1, so the CPU loop will have plenty of time to exit before we reach non-hblank top border area
|
|
// (exit granularity could be large if program is executing from SCSP RAM space, for example).
|
|
if(VCounter == (VTimings[PAL][VRes][VPHASE_TOP_BLANKING] - 1))
|
|
{
|
|
#if 0
|
|
for(unsigned bank = 0; bank < 4; bank++)
|
|
{
|
|
printf("Bank %d: ", bank);
|
|
for(unsigned vc = 0; vc < 8; vc++)
|
|
printf("%01x ", VCPRegs[bank][vc]);
|
|
printf("\n");
|
|
}
|
|
#endif
|
|
|
|
SS_RequestMLExit();
|
|
VDP2REND_EndFrame();
|
|
//printf("Meow: %d\n", VCounter);
|
|
}
|
|
|
|
while(VCounter >= VTimings[PAL][VRes][VPhase] - ((VPhase == VPHASE_VSYNC - 1) && InterlaceMode))
|
|
{
|
|
VPhase++;
|
|
|
|
if(VPhase == VPHASE__COUNT)
|
|
{
|
|
VPhase = 0;
|
|
VCounter -= VTimings[PAL][VRes][VPHASE__COUNT - 1];
|
|
}
|
|
|
|
if(VPhase == VPHASE_ACTIVE)
|
|
InternalVB = !DisplayOn;
|
|
else if(VPhase == VPHASE_BOTTOM_BORDER)
|
|
{
|
|
SS_SetEventNT(&events[SS_EVENT_MIDSYNC], event_timestamp + 1);
|
|
InternalVB = true;
|
|
Out_VB = true;
|
|
}
|
|
else if(VPhase == VPHASE_VSYNC)
|
|
{
|
|
if(InterlaceMode)
|
|
{
|
|
Odd = !Odd;
|
|
VCounter += Odd;
|
|
SurfInterlaceField = !Odd;
|
|
}
|
|
else
|
|
{
|
|
SurfInterlaceField = -1;
|
|
Odd = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
RecalcVRAMPenalty();
|
|
|
|
SMPC_SetVB(event_timestamp, Out_VB);
|
|
}
|
|
|
|
static INLINE int32 AddHCounter(const sscpu_timestamp_t event_timestamp, int32 count)
|
|
{
|
|
HCounter += count;
|
|
|
|
//if(HCounter > HTimings[HRes & 1][HPhase])
|
|
// printf("VDP2 oops: %d %d\n", HCounter, HTimings[HRes & 1][HPhase]);
|
|
|
|
while(HCounter >= HTimings[HRes & 1][HPhase])
|
|
{
|
|
HPhase++;
|
|
|
|
if(HPhase == HPHASE__COUNT)
|
|
{
|
|
HPhase = 0;
|
|
HCounter -= HTimings[HRes & 1][HPHASE__COUNT - 1];
|
|
}
|
|
//
|
|
//
|
|
//
|
|
if(HPhase == HPHASE_ACTIVE)
|
|
{
|
|
if(VPhase == VPHASE_ACTIVE)
|
|
{
|
|
VDP2Rend_LIB* lib = VDP2REND_GetLIB(VCounter);
|
|
|
|
if(!InternalVB)
|
|
{
|
|
if(BGON & 0x30)
|
|
{
|
|
if(VCounter == 0)
|
|
RPRCTL[0] = RPRCTL[1] = 0x07;
|
|
FetchRotParams(false/*field*/);
|
|
RPRCTL[0] = RPRCTL[1] = 0;
|
|
}
|
|
|
|
for(unsigned i = 0; i < 2; i++)
|
|
{
|
|
auto const& rp = RotParams[i];
|
|
auto& r = lib->rv[i];
|
|
|
|
r.Xsp = ((int64)rp.RotMatrix[0] * ((int32)rp.XstAccum - (rp.Px * 1024)) +
|
|
(int64)rp.RotMatrix[1] * ((int32)rp.YstAccum - (rp.Py * 1024)) +
|
|
(int64)rp.RotMatrix[2] * (rp.Zst - (rp.Pz * 1024))) >> 10;
|
|
r.Ysp = ((int64)rp.RotMatrix[3] * ((int32)rp.XstAccum - (rp.Px * 1024)) +
|
|
(int64)rp.RotMatrix[4] * ((int32)rp.YstAccum - (rp.Py * 1024)) +
|
|
(int64)rp.RotMatrix[5] * (rp.Zst - (rp.Pz * 1024))) >> 10;
|
|
|
|
r.Xp = rp.RotMatrix[0] * (rp.Px - rp.Cx) +
|
|
rp.RotMatrix[1] * (rp.Py - rp.Cy) +
|
|
rp.RotMatrix[2] * (rp.Pz - rp.Cz) +
|
|
(rp.Cx * 1024) + rp.Mx;
|
|
|
|
r.Yp = rp.RotMatrix[3] * (rp.Px - rp.Cx) +
|
|
rp.RotMatrix[4] * (rp.Py - rp.Cy) +
|
|
rp.RotMatrix[5] * (rp.Pz - rp.Cz) +
|
|
(rp.Cy * 1024) + rp.My;
|
|
|
|
r.dX = (rp.RotMatrix[0] * rp.DX + rp.RotMatrix[1] * rp.DY) >> 10;
|
|
r.dY = (rp.RotMatrix[3] * rp.DX + rp.RotMatrix[4] * rp.DY) >> 10;
|
|
|
|
r.kx = rp.kx;
|
|
r.ky = rp.ky;
|
|
|
|
r.KAstAccum = rp.KAstAccum;
|
|
r.DKAx = rp.DKAx;
|
|
}
|
|
}
|
|
//printf("%d, 0x%08x(%f) 0x%08x(%f)\n", VCounter, RotParams[0].KAstAccum >> 10, (int32)RotParams[0].DKAst / 1024.0, RotParams[1].KAstAccum >> 10, (int32)RotParams[1].DKAst / 1024.0);
|
|
//printf("DL: %d\n", VCounter);
|
|
lib->vdp1_hires8 = VDP1::GetLine(VCounter, lib->vdp1_line, (HRes & 1) ? 352 : 320, (int32)RotParams[0].XstAccum >> 1, (int32)RotParams[0].YstAccum >> 1, (int32)RotParams[0].DX >> 1, (int32)RotParams[0].DY >> 1); // Always call, has side effects.
|
|
VDP2REND_DrawLine(InternalVB ? -1 : VCounter, !Odd);
|
|
}
|
|
else if(VPhase == VPHASE_TOP_BORDER || VPhase == VPHASE_BOTTOM_BORDER)
|
|
VDP2REND_DrawLine(-1, !Odd);
|
|
}
|
|
else if(HPhase == HPHASE_HSYNC)
|
|
{
|
|
IncVCounter(event_timestamp);
|
|
}
|
|
}
|
|
|
|
return (HTimings[HRes & 1][HPhase] - HCounter);
|
|
}
|
|
|
|
sscpu_timestamp_t Update(sscpu_timestamp_t timestamp)
|
|
{
|
|
int32 clocks = (timestamp - lastts) >> 2;
|
|
|
|
if(MDFN_UNLIKELY(timestamp < lastts))
|
|
{
|
|
SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP2, "[VDP2] [BUG] timestamp(%d) < lastts(%d)", timestamp, lastts);
|
|
clocks = 0;
|
|
}
|
|
|
|
lastts += clocks << 2;
|
|
//
|
|
//
|
|
int32 ne;
|
|
int32 tmp;
|
|
|
|
ne = AddHCounter(timestamp, clocks);
|
|
VDP1::SetHBVB(timestamp, HPhase > HPHASE_ACTIVE, Out_VB);
|
|
tmp = SCU_SetHBVB(clocks, HPhase > HPHASE_ACTIVE, Out_VB);
|
|
if(tmp < ne)
|
|
ne = tmp;
|
|
|
|
return lastts + (ne << 2);
|
|
}
|
|
|
|
//
|
|
// Register writes seem to always be 16-bit
|
|
//
|
|
static INLINE void RegsWrite(uint32 A, uint16 V)
|
|
{
|
|
A &= 0x1FE;
|
|
|
|
RawRegs[A >> 1] = V;
|
|
|
|
SS_DBGTI(SS_DBG_VDP2_REGW, "[VDP2] Register write 0x%03x: 0x%04x", A, V);
|
|
|
|
switch(A)
|
|
{
|
|
default:
|
|
// SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP2, "[VDP2] Unknown write to register at 0x%08x of value 0x%04x", A, V);
|
|
break;
|
|
|
|
case 0x00:
|
|
Update(SH7095_mem_timestamp);
|
|
//
|
|
DisplayOn = (V >> 15) & 0x1;
|
|
BorderMode = (V >> 8) & 0x1;
|
|
InterlaceMode = (V >> 6) & 0x3;
|
|
VRes = (V >> 4) & 0x3;
|
|
HRes = (V >> 0) & 0x7;
|
|
//
|
|
InternalVB |= !DisplayOn;
|
|
//
|
|
SS_SetEventNT(&events[SS_EVENT_VDP2], Update(SH7095_mem_timestamp));
|
|
break;
|
|
|
|
case 0x02:
|
|
ExLatchEnable = (V >> 9) & 0x1;
|
|
ExSyncEnable = (V >> 8) & 0x1;
|
|
|
|
DispAreaSelect = (V >> 1) & 0x1;
|
|
ExBGEnable = (V >> 0) & 0x1;
|
|
break;
|
|
|
|
case 0x06:
|
|
VRAMSize = (V >> 15) & 0x1;
|
|
|
|
if(VRAMSize)
|
|
SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP2, "[VDP2] VRAMSize=%d (unemulated)", VRAMSize);
|
|
break;
|
|
|
|
case 0x0E:
|
|
RAMCTL_Raw = V & 0xB3FF;
|
|
CRAM_Mode = (V >> 12) & 0x3;
|
|
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 0xB2:
|
|
RPRCTL[0] = (V >> 0) & 0x7;
|
|
RPRCTL[1] = (V >> 8) & 0x7;
|
|
break;
|
|
|
|
case 0xB6:
|
|
KTAOF[0] = (V >> 0) & 0x7;
|
|
KTAOF[1] = (V >> 8) & 0x7;
|
|
break;
|
|
|
|
case 0xBC:
|
|
RPTA = (RPTA & 0xFFFF) | ((V & 0x7) << 16);
|
|
break;
|
|
|
|
case 0xBE:
|
|
RPTA = (RPTA & ~0xFFFF) | (V & 0xFFFE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static INLINE uint16 RegsRead(uint32 A)
|
|
{
|
|
switch(A & 0x1FE)
|
|
{
|
|
default:
|
|
SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP2, "[VDP2] Unknown read from 0x%08x", A);
|
|
return 0;
|
|
|
|
case 0x00:
|
|
return (DisplayOn << 15) | (BorderMode << 8) | (InterlaceMode << 6) | (VRes << 4) | (HRes << 0);
|
|
|
|
case 0x02:
|
|
if(!ExLatchEnable)
|
|
{
|
|
SS_SetEventNT(&events[SS_EVENT_VDP2], Update(SH7095_mem_timestamp));
|
|
LatchHV();
|
|
}
|
|
return (ExLatchEnable << 9) | (ExSyncEnable << 8) | (DispAreaSelect << 1) | (ExBGEnable << 0);
|
|
|
|
case 0x04:
|
|
SS_SetEventNT(&events[SS_EVENT_VDP2], Update(SH7095_mem_timestamp));
|
|
{
|
|
// TODO: EXSYFG
|
|
uint16 ret = (HVIsExLatched << 9) | (InternalVB << 3) | ((HPhase > HPHASE_ACTIVE) << 2) | (Odd << 1) | (PAL << 0);
|
|
|
|
HVIsExLatched = false;
|
|
|
|
return ret;
|
|
}
|
|
|
|
case 0x06:
|
|
return VRAMSize << 15;
|
|
|
|
case 0x08:
|
|
return Latched_HCNT;
|
|
|
|
case 0x0A:
|
|
return Latched_VCNT;
|
|
|
|
case 0x0E:
|
|
return RAMCTL_Raw;
|
|
}
|
|
}
|
|
|
|
template<typename T, bool IsWrite>
|
|
static INLINE uint32 RW(uint32 A, uint16* DB)
|
|
{
|
|
static_assert(IsWrite || sizeof(T) == 2, "Wrong type for read.");
|
|
|
|
A &= 0x1FFFFF;
|
|
|
|
//
|
|
// VRAM
|
|
//
|
|
if(A < 0x100000)
|
|
{
|
|
const size_t vri = (A & 0x7FFFF) >> 1;
|
|
|
|
if(IsWrite)
|
|
{
|
|
const unsigned mask = (sizeof(T) == 2) ? 0xFFFF : (0xFF00 >> ((A & 1) << 3));
|
|
|
|
VRAM[vri] = (VRAM[vri] &~ mask) | (*DB & mask);
|
|
}
|
|
else
|
|
*DB = VRAM[vri];
|
|
|
|
return VRAMPenalty[vri >> 16];
|
|
}
|
|
|
|
//
|
|
// CRAM
|
|
//
|
|
if(A < 0x180000)
|
|
{
|
|
const unsigned cri = (A & 0xFFF) >> 1;
|
|
|
|
if(IsWrite)
|
|
{
|
|
switch(CRAM_Mode)
|
|
{
|
|
case CRAM_MODE_RGB555_1024:
|
|
(CRAM + 0x000)[cri & 0x3FF] = *DB;
|
|
(CRAM + 0x400)[cri & 0x3FF] = *DB;
|
|
break;
|
|
|
|
case CRAM_MODE_RGB555_2048:
|
|
CRAM[cri] = *DB;
|
|
break;
|
|
|
|
case CRAM_MODE_RGB888_1024:
|
|
case CRAM_MODE_ILLEGAL:
|
|
default:
|
|
CRAM[((cri >> 1) & 0x3FF) | ((cri & 1) << 10)] = *DB;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch(CRAM_Mode)
|
|
{
|
|
case CRAM_MODE_RGB555_1024:
|
|
case CRAM_MODE_RGB555_2048:
|
|
*DB = CRAM[cri];
|
|
break;
|
|
|
|
case CRAM_MODE_RGB888_1024:
|
|
case CRAM_MODE_ILLEGAL:
|
|
default:
|
|
*DB = CRAM[((cri >> 1) & 0x3FF) | ((cri & 1) << 10)];
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Registers
|
|
//
|
|
if(A < 0x1C0000)
|
|
{
|
|
if(IsWrite)
|
|
{
|
|
if(sizeof(T) == 1)
|
|
SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP2, "[VDP2] Byte-write to register at 0x%08x(DB=0x%04x)", A, *DB);
|
|
|
|
RegsWrite(A, *DB);
|
|
}
|
|
else
|
|
*DB = RegsRead(A);
|
|
|
|
return 0;
|
|
}
|
|
|
|
if(IsWrite)
|
|
{
|
|
//SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP2, "[VDP2] Unknown %zu-byte write to 0x%08x(DB=0x%04x)", sizeof(T), A, *DB);
|
|
}
|
|
else
|
|
{
|
|
//SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP2, "[VDP2] Unknown %zu-byte read from 0x%08x", sizeof(T), A);
|
|
*DB = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint16 Read16_DB(uint32 A)
|
|
{
|
|
uint16 DB;
|
|
|
|
RW<uint16, false>(A, &DB);
|
|
|
|
return DB;
|
|
}
|
|
|
|
|
|
uint32 Write8_DB(uint32 A, uint16 DB)
|
|
{
|
|
VDP2REND_Write8_DB(A, DB);
|
|
|
|
return RW<uint8, true>(A, &DB);
|
|
}
|
|
|
|
uint32 Write16_DB(uint32 A, uint16 DB)
|
|
{
|
|
VDP2REND_Write16_DB(A, DB);
|
|
|
|
return RW<uint16, true>(A, &DB);
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
void AdjustTS(const int32 delta)
|
|
{
|
|
lastts += delta;
|
|
}
|
|
|
|
|
|
void Init(const bool IsPAL)
|
|
{
|
|
SurfInterlaceField = -1;
|
|
PAL = IsPAL;
|
|
lastts = 0;
|
|
|
|
SS_SetPhysMemMap(0x05E00000, 0x05EFFFFF, VRAM, 0x80000, true);
|
|
|
|
VDP2REND_Init(IsPAL);
|
|
}
|
|
|
|
void Reset(bool powering_up)
|
|
{
|
|
DisplayOn = false;
|
|
BorderMode = false;
|
|
ExLatchEnable = false;
|
|
ExSyncEnable = false;
|
|
ExBGEnable = false;
|
|
DispAreaSelect = false;
|
|
HRes = 0;
|
|
VRes = 0;
|
|
InterlaceMode = 0;
|
|
|
|
VRAMSize = 0;
|
|
|
|
InternalVB = true;
|
|
Out_VB = false;
|
|
VPhase = VPHASE_ACTIVE;
|
|
VCounter = 0;
|
|
Odd = true;
|
|
|
|
RAMCTL_Raw = 0;
|
|
CRAM_Mode = 0;
|
|
|
|
BGON = 0;
|
|
|
|
memset(VCPRegs, 0, sizeof(VCPRegs));
|
|
|
|
for(unsigned i = 0; i < 2; i++)
|
|
{
|
|
KTAOF[i] = 0;
|
|
RPRCTL[i] = 0;
|
|
}
|
|
RPTA = 0;
|
|
memset(RotParams, 0, sizeof(RotParams));
|
|
|
|
//
|
|
// FIXME(init values), also in VDP2REND.
|
|
if(powering_up)
|
|
{
|
|
memset(VRAM, 0, sizeof(VRAM));
|
|
memset(CRAM, 0, sizeof(CRAM));
|
|
}
|
|
//
|
|
RecalcVRAMPenalty();
|
|
//
|
|
//
|
|
VDP2REND_Reset(powering_up);
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
//
|
|
uint32 GetRegister(const unsigned id, char* const special, const uint32 special_len)
|
|
{
|
|
uint32 ret = 0xDEADBEEF;
|
|
|
|
switch(id)
|
|
{
|
|
case GSREG_LINE:
|
|
ret = VCounter;
|
|
break;
|
|
|
|
case GSREG_DON:
|
|
ret = DisplayOn;
|
|
break;
|
|
|
|
case GSREG_BM:
|
|
ret = BorderMode;
|
|
break;
|
|
|
|
case GSREG_IM:
|
|
ret = InterlaceMode;
|
|
break;
|
|
|
|
case GSREG_VRES:
|
|
ret = VRes;
|
|
break;
|
|
|
|
case GSREG_HRES:
|
|
ret = HRes;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void SetRegister(const unsigned id, const uint32 value)
|
|
{
|
|
|
|
}
|
|
|
|
uint8 PeekVRAM(const uint32 addr)
|
|
{
|
|
return ne16_rbo_be<uint8>(VRAM, addr & 0x7FFFF);
|
|
}
|
|
|
|
void PokeVRAM(const uint32 addr, const uint8 val)
|
|
{
|
|
ne16_wbo_be<uint8>(VRAM, addr & 0x7FFFF, val);
|
|
//VDP2REND_Write8_DB(addr, val << (((A & 1) ^ 1) << 3));
|
|
}
|
|
|
|
void SetLayerEnableMask(uint64 mask)
|
|
{
|
|
VDP2REND_SetLayerEnableMask(mask);
|
|
}
|
|
|
|
/*void MakeDump(const std::string& path)
|
|
{
|
|
FileStream fp(path, FileStream::MODE_WRITE);
|
|
|
|
fp.print_format(" { ");
|
|
for(unsigned i = 0; i < 0x100; i++)
|
|
fp.print_format("0x%04x, ", RawRegs[i]);
|
|
fp.print_format(" }, \n");
|
|
|
|
fp.print_format(" { ");
|
|
for(unsigned i = 0; i < 2048; i++)
|
|
fp.print_format("0x%04x, ", CRAM[i]);
|
|
fp.print_format(" }, \n");
|
|
|
|
fp.print_format(" { ");
|
|
for(unsigned i = 0; i < 0x40000; i++)
|
|
fp.print_format("0x%04x, ", VRAM[i]);
|
|
fp.print_format(" }, \n");
|
|
|
|
fp.close();
|
|
}*/
|
|
|
|
}
|
|
|
|
}
|