888 lines
19 KiB
C++
888 lines
19 KiB
C++
/******************************************************************************/
|
|
/* Mednafen Sega Saturn Emulation Module */
|
|
/******************************************************************************/
|
|
/* vdp1.cpp - VDP1 Emulation
|
|
** Copyright (C) 2015-2017 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: Check to see what registers are reset on reset.
|
|
|
|
// TODO: Fix preclipping when raw system clipping values have bit12==1(sign bit?).
|
|
|
|
// TODO: SS_SetPhysMemMap(0x05C80000, 0x05CFFFFF, FB[FBDrawWhich], sizeof(FB[0]));
|
|
// (...but goes weird in 8bpp rotated mode...)
|
|
|
|
// TODO: Test 1x1 line, polyline, sprite, and polygon.
|
|
|
|
// TODO: Framebuffer swap/auto drawing start happens a bit too early, should happen near
|
|
// end of hblank instead of the beginning.
|
|
|
|
#include "ss.h"
|
|
#include "scu.h"
|
|
#include "vdp1.h"
|
|
#include "vdp2.h"
|
|
#include "vdp1_common.h"
|
|
|
|
enum { VDP1_UpdateTimingGran = 263 };
|
|
enum { VDP1_IdleTimingGran = 1019 };
|
|
|
|
namespace MDFN_IEN_SS
|
|
{
|
|
namespace VDP1
|
|
{
|
|
|
|
uint8 spr_w_shift_tab[8];
|
|
line_data LineSetup;
|
|
uint8 gouraud_lut[0x40];
|
|
|
|
uint16 VRAM[0x40000];
|
|
uint16 FB[2][0x20000];
|
|
bool FBDrawWhich;
|
|
|
|
static bool FBManualPending;
|
|
|
|
static bool FBVBErasePending;
|
|
static bool FBVBEraseActive;
|
|
static sscpu_timestamp_t FBVBEraseLastTS;
|
|
|
|
int32 SysClipX, SysClipY;
|
|
int32 UserClipX0, UserClipY0, UserClipX1, UserClipY1;
|
|
int32 LocalX, LocalY;
|
|
|
|
static uint32 CurCommandAddr;
|
|
static int32 RetCommandAddr;
|
|
static bool DrawingActive;
|
|
|
|
static uint16 LOPR;
|
|
|
|
static uint16 EWDR; // Erase/Write Data
|
|
static uint16 EWLR; // Erase/Write Upper Left Coordinate
|
|
static uint16 EWRR; // Erase/Write Lower Right Coordinate
|
|
|
|
static struct
|
|
{
|
|
bool rot8;
|
|
uint32 fb_x_mask;
|
|
|
|
uint32 y_start;
|
|
uint32 x_start;
|
|
|
|
uint32 y_end;
|
|
uint32 x_bound;
|
|
|
|
uint16 fill_data;
|
|
} EraseParams;
|
|
|
|
static uint32 EraseYCounter;
|
|
|
|
uint8 TVMR;
|
|
uint8 FBCR;
|
|
uint8 PTMR;
|
|
static uint8 EDSR;
|
|
|
|
static bool vb_status, hb_status;
|
|
static sscpu_timestamp_t lastts;
|
|
static int32 CycleCounter;
|
|
|
|
static bool vbcdpending;
|
|
|
|
void Init(void)
|
|
{
|
|
vbcdpending = false;
|
|
|
|
for(int i = 0; i < 0x40; i++)
|
|
{
|
|
gouraud_lut[i] = std::min<int>(31, std::max<int>(0, i - 16));
|
|
}
|
|
|
|
for(int i = 0; i < 8; i++)
|
|
{
|
|
spr_w_shift_tab[i] = (7 - i) / 3;
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
SS_SetPhysMemMap(0x05C00000, 0x05C7FFFF, VRAM, sizeof(VRAM), true);
|
|
//SS_SetPhysMemMap(0x05C80000, 0x05CFFFFF, FB[FBDrawWhich], sizeof(FB[0]), true);
|
|
|
|
vb_status = false;
|
|
hb_status = false;
|
|
lastts = 0;
|
|
FBVBEraseLastTS = 0;
|
|
}
|
|
|
|
void Reset(bool powering_up)
|
|
{
|
|
if(powering_up)
|
|
{
|
|
for(unsigned i = 0; i < 0x40000; i++)
|
|
{
|
|
uint16 val;
|
|
|
|
if((i & 0xF) == 0)
|
|
val = 0x8000;
|
|
else if(i & 0x1)
|
|
val = 0x5555;
|
|
else
|
|
val = 0xAAAA;
|
|
|
|
VRAM[i] = val;
|
|
}
|
|
|
|
for(unsigned fb = 0; fb < 2; fb++)
|
|
for(unsigned i = 0; i < 0x20000; i++)
|
|
FB[fb][i] = 0xFFFF;
|
|
|
|
memset(&LineSetup, 0, sizeof(LineSetup));
|
|
|
|
//
|
|
//
|
|
//
|
|
EWDR = 0;
|
|
EWLR = 0;
|
|
EWRR = 0;
|
|
|
|
TVMR = 0;
|
|
FBCR = 0;
|
|
}
|
|
|
|
UserClipX0 = 0;
|
|
UserClipY0 = 0;
|
|
UserClipX1 = 0;
|
|
UserClipY1 = 0;
|
|
|
|
SysClipX = 0;
|
|
SysClipY = 0;
|
|
|
|
LocalX = 0;
|
|
LocalY = 0;
|
|
|
|
FBDrawWhich = 0;
|
|
//SS_SetPhysMemMap(0x05C80000, 0x05CFFFFF, FB[FBDrawWhich], sizeof(FB[0]), true);
|
|
|
|
FBManualPending = false;
|
|
FBVBErasePending = false;
|
|
FBVBEraseActive = false;
|
|
|
|
LOPR = 0;
|
|
CurCommandAddr = 0;
|
|
RetCommandAddr = -1;
|
|
DrawingActive = false;
|
|
|
|
PTMR = 0;
|
|
EDSR = 0;
|
|
|
|
memset(&EraseParams, 0, sizeof(EraseParams));
|
|
EraseYCounter = ~0U;
|
|
|
|
CycleCounter = 0;
|
|
}
|
|
|
|
static int32 CMD_SetUserClip(const uint16* cmd_data)
|
|
{
|
|
UserClipX0 = cmd_data[0x6] & 0x3FF;
|
|
UserClipY0 = cmd_data[0x7] & 0x1FF;
|
|
|
|
UserClipX1 = cmd_data[0xA] & 0x3FF;
|
|
UserClipY1 = cmd_data[0xB] & 0x1FF;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32 CMD_SetSystemClip(const uint16* cmd_data)
|
|
{
|
|
SysClipX = cmd_data[0xA] & 0x3FF;
|
|
SysClipY = cmd_data[0xB] & 0x1FF;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32 CMD_SetLocalCoord(const uint16* cmd_data)
|
|
{
|
|
LocalX = sign_x_to_s32(11, cmd_data[0x6] & 0x7FF);
|
|
LocalY = sign_x_to_s32(11, cmd_data[0x7] & 0x7FF);
|
|
|
|
return 0;
|
|
}
|
|
|
|
template<unsigned ECDSPDMode>
|
|
static uint32 MDFN_FASTCALL TexFetch(uint32 x)
|
|
{
|
|
const uint32 base = LineSetup.tex_base;
|
|
const bool ECD = ECDSPDMode & 0x10;
|
|
const bool SPD = ECDSPDMode & 0x08;
|
|
const unsigned ColorMode = ECDSPDMode & 0x07;
|
|
|
|
uint32 rtd;
|
|
uint32 ret_or = 0;
|
|
|
|
switch(ColorMode)
|
|
{
|
|
case 0: // 16 colors, color bank
|
|
rtd = (VRAM[(base + (x >> 2)) & 0x3FFFF] >> (((x & 0x3) ^ 0x3) << 2)) & 0xF;
|
|
|
|
if(!ECD && rtd == 0xF)
|
|
{
|
|
LineSetup.ec_count--;
|
|
return -1;
|
|
}
|
|
ret_or = LineSetup.cb_or;
|
|
|
|
if(!SPD) ret_or |= (int32)(rtd - 1) >> 31;
|
|
|
|
return rtd | ret_or;
|
|
|
|
case 1: // 16 colors, LUT
|
|
rtd = (VRAM[(base + (x >> 2)) & 0x3FFFF] >> (((x & 0x3) ^ 0x3) << 2)) & 0xF;
|
|
|
|
if(!ECD && rtd == 0xF)
|
|
{
|
|
LineSetup.ec_count--;
|
|
return -1;
|
|
}
|
|
|
|
if(!SPD) ret_or |= (int32)(rtd - 1) >> 31;
|
|
|
|
return LineSetup.CLUT[rtd] | ret_or;
|
|
|
|
case 2: // 64 colors, color bank
|
|
rtd = (VRAM[(base + (x >> 1)) & 0x3FFFF] >> (((x & 0x1) ^ 0x1) << 3)) & 0xFF;
|
|
|
|
if(!ECD && rtd == 0xFF)
|
|
{
|
|
LineSetup.ec_count--;
|
|
return -1;
|
|
}
|
|
|
|
ret_or = LineSetup.cb_or;
|
|
|
|
if(!SPD) ret_or |= (int32)(rtd - 1) >> 31;
|
|
|
|
return (rtd & 0x3F) | ret_or;
|
|
|
|
case 3: // 128 colors, color bank
|
|
rtd = (VRAM[(base + (x >> 1)) & 0x3FFFF] >> (((x & 0x1) ^ 0x1) << 3)) & 0xFF;
|
|
|
|
if(!ECD && rtd == 0xFF)
|
|
{
|
|
LineSetup.ec_count--;
|
|
return -1;
|
|
}
|
|
|
|
ret_or = LineSetup.cb_or;
|
|
|
|
if(!SPD) ret_or |= (int32)(rtd - 1) >> 31;
|
|
|
|
return (rtd & 0x7F) | ret_or;
|
|
|
|
case 4: // 256 colors, color bank
|
|
rtd = (VRAM[(base + (x >> 1)) & 0x3FFFF] >> (((x & 0x1) ^ 0x1) << 3)) & 0xFF;
|
|
|
|
if(!ECD && rtd == 0xFF)
|
|
{
|
|
LineSetup.ec_count--;
|
|
return -1;
|
|
}
|
|
|
|
ret_or = LineSetup.cb_or;
|
|
|
|
if(!SPD) ret_or |= (int32)(rtd - 1) >> 31;
|
|
|
|
return rtd | ret_or;
|
|
|
|
case 5: // 32K colors, RGB
|
|
case 6:
|
|
case 7:
|
|
if(ColorMode >= 6)
|
|
rtd = VRAM[0];
|
|
else
|
|
rtd = VRAM[(base + x) & 0x3FFFF];
|
|
|
|
if(!ECD && (rtd & 0xC000) == 0x4000)
|
|
{
|
|
LineSetup.ec_count--;
|
|
return -1;
|
|
}
|
|
|
|
if(!SPD) ret_or |= (int32)(rtd - 0x4000) >> 31;
|
|
|
|
return rtd | ret_or;
|
|
}
|
|
}
|
|
|
|
|
|
extern uint32 (MDFN_FASTCALL *const TexFetchTab[0x20])(uint32 x) =
|
|
{
|
|
#define TF(a) (TexFetch<a>)
|
|
|
|
TF(0x00), TF(0x01), TF(0x02), TF(0x03),
|
|
TF(0x04), TF(0x05), TF(0x06), TF(0x07),
|
|
|
|
TF(0x08), TF(0x09), TF(0x0A), TF(0x0B),
|
|
TF(0x0C), TF(0x0D), TF(0x0E), TF(0x0F),
|
|
|
|
TF(0x10), TF(0x11), TF(0x12), TF(0x13),
|
|
TF(0x14), TF(0x15), TF(0x16), TF(0x17),
|
|
|
|
TF(0x18), TF(0x19), TF(0x1A), TF(0x1B),
|
|
TF(0x1C), TF(0x1D), TF(0x1E), TF(0x1F),
|
|
|
|
#undef TF
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
Notes:
|
|
When vblank starts: Abort command processing, and if VBE=1, erase framebuffer just displayed according to set values.
|
|
|
|
When vblank ends: Abort framebuffer erase, swap framebuffer, and if (PTMR&2) start command processing.
|
|
|
|
See if EDSR and LOPR are modified or not when PTMR=0 and an auto framebuffer swap occurs.
|
|
|
|
FB erase params are latched at framebuffer swap time probably.
|
|
|
|
VBE=1 is persistent.
|
|
*/
|
|
|
|
sscpu_timestamp_t Update(sscpu_timestamp_t timestamp)
|
|
{
|
|
if(MDFN_UNLIKELY(timestamp < lastts))
|
|
{
|
|
// Don't else { } normal execution, since this bug condition miiight occur in the call from SetHBVB(),
|
|
// and we need drawing to start ASAP before silly games overwrite the beginning of the command table.
|
|
//
|
|
SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP1, "[VDP1] [BUG] timestamp(%d) < lastts(%d)", timestamp, lastts);
|
|
timestamp = lastts;
|
|
}
|
|
//
|
|
//
|
|
//
|
|
int32 cycles = timestamp - lastts;
|
|
lastts = timestamp;
|
|
|
|
CycleCounter += cycles;
|
|
if(CycleCounter > VDP1_UpdateTimingGran)
|
|
CycleCounter = VDP1_UpdateTimingGran;
|
|
|
|
if(CycleCounter > 0 && SCU_CheckVDP1HaltKludge())
|
|
{
|
|
//puts("Kludge");
|
|
CycleCounter = 0;
|
|
}
|
|
else if(DrawingActive)
|
|
{
|
|
while(CycleCounter > 0)
|
|
{
|
|
uint16 cmd_data[0x10];
|
|
|
|
// Fetch command data
|
|
memcpy(cmd_data, &VRAM[CurCommandAddr], sizeof(cmd_data));
|
|
CycleCounter -= 16;
|
|
|
|
//SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP1, "[VDP1] Command @ 0x%06x: 0x%04x\n", CurCommandAddr, cmd_data[0]);
|
|
|
|
if(MDFN_LIKELY(!(cmd_data[0] & 0xC000)))
|
|
{
|
|
const unsigned cc = cmd_data[0] & 0xF;
|
|
|
|
if(MDFN_UNLIKELY(cc >= 0xC))
|
|
{
|
|
DrawingActive = false;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
static int32 (*const command_table[0xC])(const uint16* cmd_data) =
|
|
{
|
|
/* 0x0 */ /* 0x1 */ /* 0x2 */ /* 0x3 */
|
|
CMD_NormalSprite, CMD_ScaledSprite, CMD_DistortedSprite, CMD_DistortedSprite,
|
|
|
|
/* 0x4 */ /* 0x5 */ /* 0x6 */ /* 0x7 */
|
|
CMD_Polygon, CMD_Polyline, CMD_Line, CMD_Polyline,
|
|
|
|
/* 0x8*/ /* 0x9 */ /* 0xA */ /* 0xB */
|
|
CMD_SetUserClip, CMD_SetSystemClip, CMD_SetLocalCoord, CMD_SetUserClip
|
|
};
|
|
|
|
CycleCounter -= command_table[cc](cmd_data);
|
|
}
|
|
}
|
|
else if(MDFN_UNLIKELY(cmd_data[0] & 0x8000))
|
|
{
|
|
SS_DBGTI(SS_DBG_VDP1, "[VDP1] Drawing finished at 0x%05x", CurCommandAddr);
|
|
DrawingActive = false;
|
|
|
|
EDSR |= 0x2; // TODO: Does EDSR reflect IRQ out status?
|
|
|
|
SCU_SetInt(SCU_INT_VDP1, true);
|
|
SCU_SetInt(SCU_INT_VDP1, false);
|
|
break;
|
|
}
|
|
|
|
CurCommandAddr = (CurCommandAddr + 0x10) & 0x3FFFF;
|
|
switch((cmd_data[0] >> 12) & 0x3)
|
|
{
|
|
case 0:
|
|
break;
|
|
|
|
case 1:
|
|
CurCommandAddr = (cmd_data[1] << 2) &~ 0xF;
|
|
break;
|
|
|
|
case 2:
|
|
if(RetCommandAddr < 0)
|
|
RetCommandAddr = CurCommandAddr;
|
|
|
|
CurCommandAddr = (cmd_data[1] << 2) &~ 0xF;
|
|
break;
|
|
|
|
case 3:
|
|
if(RetCommandAddr >= 0)
|
|
{
|
|
CurCommandAddr = RetCommandAddr;
|
|
RetCommandAddr = -1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return timestamp + (DrawingActive ? std::max<int32>(VDP1_UpdateTimingGran, 0 - CycleCounter) : VDP1_IdleTimingGran);
|
|
}
|
|
|
|
// Draw-clear minimum x amount is 2(16-bit units) for normal and 8bpp, and 8 for rotate...actually, seems like
|
|
// rotate being enabled forces vblank erase mode somehow.
|
|
|
|
static void StartDrawing(void)
|
|
{
|
|
if(DrawingActive)
|
|
{
|
|
SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP1, "[VDP1] Drawing interrupted by new drawing start request.");
|
|
}
|
|
|
|
SS_DBGTI(SS_DBG_VDP1, "[VDP1] Started drawing to framebuffer %d.", FBDrawWhich);
|
|
|
|
// On draw start, clear CEF.
|
|
EDSR &= ~0x2;
|
|
|
|
CurCommandAddr = 0;
|
|
RetCommandAddr = -1;
|
|
DrawingActive = true;
|
|
CycleCounter = VDP1_UpdateTimingGran;
|
|
}
|
|
|
|
void SetHBVB(const sscpu_timestamp_t event_timestamp, const bool new_hb_status, const bool new_vb_status)
|
|
{
|
|
const bool old_hb_status = hb_status;
|
|
const bool old_vb_status = vb_status;
|
|
|
|
hb_status = new_hb_status;
|
|
vb_status = new_vb_status;
|
|
|
|
if(MDFN_UNLIKELY(vbcdpending & hb_status & (old_hb_status ^ hb_status)))
|
|
{
|
|
vbcdpending = false;
|
|
|
|
if(vb_status) // Going into v-blank
|
|
{
|
|
//
|
|
// v-blank erase
|
|
//
|
|
if((TVMR & TVMR_VBE) || FBVBErasePending)
|
|
{
|
|
SS_DBGTI(SS_DBG_VDP1, "[VDP1] VB erase start of framebuffer %d.", !FBDrawWhich);
|
|
|
|
FBVBErasePending = false;
|
|
FBVBEraseActive = true;
|
|
FBVBEraseLastTS = event_timestamp;
|
|
}
|
|
}
|
|
else // Leaving v-blank
|
|
{
|
|
// Run vblank erase at end of vblank all at once(not strictly accurate, but should only have visible side effects wrt the debugger and reset).
|
|
if(FBVBEraseActive)
|
|
{
|
|
int32 count = event_timestamp - FBVBEraseLastTS;
|
|
//printf("%d %d, %d\n", event_timestamp, FBVBEraseLastTS, count);
|
|
//
|
|
//
|
|
//
|
|
uint32 y = EraseParams.y_start;
|
|
|
|
do
|
|
{
|
|
uint16* fbyptr;
|
|
uint32 x = EraseParams.x_start;
|
|
|
|
fbyptr = &FB[!FBDrawWhich][(y & 0xFF) << 9];
|
|
if(EraseParams.rot8)
|
|
fbyptr += (y & 0x100);
|
|
|
|
count -= 8;
|
|
do
|
|
{
|
|
for(unsigned sub = 0; sub < 8; sub++)
|
|
{
|
|
//printf("%d %d:%d %04x\n", FBDrawWhich, x, y, fill_data);
|
|
//printf("%lld\n", &fbyptr[x & fb_x_mask] - FB[!FBDrawWhich]);
|
|
fbyptr[x & EraseParams.fb_x_mask] = EraseParams.fill_data;
|
|
x++;
|
|
}
|
|
count -= 8;
|
|
if(MDFN_UNLIKELY(count <= 0))
|
|
{
|
|
SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP1, "[VDP1] VB erase of framebuffer %d ran out of time.", !FBDrawWhich);
|
|
goto AbortVBErase;
|
|
}
|
|
} while(x < EraseParams.x_bound);
|
|
} while(++y <= EraseParams.y_end);
|
|
|
|
AbortVBErase:;
|
|
//
|
|
FBVBEraseActive = false;
|
|
}
|
|
//
|
|
//
|
|
//
|
|
//
|
|
if(!(FBCR & FBCR_FCM) || (FBManualPending && (FBCR & FBCR_FCT))) // Swap framebuffers
|
|
{
|
|
if(DrawingActive)
|
|
{
|
|
SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP1, "[VDP1] Drawing aborted by framebuffer swap.");
|
|
DrawingActive = false;
|
|
}
|
|
|
|
FBDrawWhich = !FBDrawWhich;
|
|
|
|
SS_DBGTI(SS_DBG_VDP1, "[VDP1] Displayed framebuffer changed to %d.", !FBDrawWhich);
|
|
|
|
// On fb swap, copy CEF to BEF, clear CEF, and copy COPR to LOPR.
|
|
EDSR = EDSR >> 1;
|
|
LOPR = CurCommandAddr >> 2;
|
|
|
|
//
|
|
EraseParams.rot8 = (TVMR & (TVMR_8BPP | TVMR_ROTATE)) == (TVMR_8BPP | TVMR_ROTATE);
|
|
EraseParams.fb_x_mask = EraseParams.rot8 ? 0xFF : 0x1FF;
|
|
|
|
EraseParams.y_start = EWLR & 0x1FF;
|
|
EraseParams.x_start = ((EWLR >> 9) & 0x3F) << 3;
|
|
|
|
EraseParams.y_end = EWRR & 0x1FF;
|
|
EraseParams.x_bound = ((EWRR >> 9) & 0x7F) << 3;
|
|
|
|
EraseParams.fill_data = EWDR;
|
|
//
|
|
|
|
if(PTMR & 0x2) // Start drawing(but only if we swapped the frame)
|
|
{
|
|
StartDrawing();
|
|
SS_SetEventNT(&events[SS_EVENT_VDP1], Update(event_timestamp));
|
|
}
|
|
}
|
|
|
|
if(!(FBCR & FBCR_FCM) || (FBManualPending && !(FBCR & FBCR_FCT)))
|
|
{
|
|
if(TVMR & TVMR_ROTATE)
|
|
{
|
|
EraseYCounter = ~0U;
|
|
FBVBErasePending = true;
|
|
}
|
|
else
|
|
{
|
|
EraseYCounter = EraseParams.y_start;
|
|
}
|
|
}
|
|
|
|
FBManualPending = false;
|
|
}
|
|
}
|
|
vbcdpending |= old_vb_status ^ vb_status;
|
|
}
|
|
|
|
bool GetLine(const int line, uint16* buf, unsigned w, uint32 rot_x, uint32 rot_y, uint32 rot_xinc, uint32 rot_yinc)
|
|
{
|
|
bool ret = false;
|
|
//
|
|
//
|
|
//
|
|
if(TVMR & TVMR_ROTATE)
|
|
{
|
|
const uint16* fbptr = FB[!FBDrawWhich];
|
|
|
|
if(TVMR & TVMR_8BPP)
|
|
{
|
|
for(unsigned i = 0; MDFN_LIKELY(i < w); i++)
|
|
{
|
|
const uint32 fb_x = rot_x >> 9;
|
|
const uint32 fb_y = rot_y >> 9;
|
|
|
|
if((fb_x | fb_y) &~ 0x1FF)
|
|
buf[i] = 0; // Not 0xFF00
|
|
else
|
|
{
|
|
const uint16* fbyptr = fbptr + ((fb_y & 0xFF) << 9);
|
|
uint8 tmp = ne16_rbo_be<uint8>(fbyptr, (fb_x & 0x1FF) | ((fb_y & 0x100) << 1));
|
|
|
|
buf[i] = 0xFF00 | tmp;
|
|
}
|
|
|
|
rot_x += rot_xinc;
|
|
rot_y += rot_yinc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(unsigned i = 0; MDFN_LIKELY(i < w); i++)
|
|
{
|
|
const uint32 fb_x = rot_x >> 9;
|
|
const uint32 fb_y = rot_y >> 9;
|
|
|
|
if((fb_x &~ 0x1FF) | (fb_y &~ 0xFF))
|
|
buf[i] = 0;
|
|
else
|
|
buf[i] = fbptr[(fb_y << 9) + fb_x];
|
|
|
|
rot_x += rot_xinc;
|
|
rot_y += rot_yinc;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const uint16* fbyptr = &FB[!FBDrawWhich][(line & 0xFF) << 9];
|
|
|
|
if(TVMR & TVMR_8BPP)
|
|
ret = true;
|
|
|
|
for(unsigned i = 0; MDFN_LIKELY(i < w); i++)
|
|
buf[i] = fbyptr[i];
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
if(EraseYCounter <= EraseParams.y_end)
|
|
{
|
|
uint16* fbyptr;
|
|
uint32 x = EraseParams.x_start;
|
|
|
|
fbyptr = &FB[!FBDrawWhich][(EraseYCounter & 0xFF) << 9];
|
|
if(EraseParams.rot8)
|
|
fbyptr += (EraseYCounter & 0x100);
|
|
|
|
do
|
|
{
|
|
for(unsigned sub = 0; sub < 2; sub++)
|
|
{
|
|
//printf("%d %d:%d %04x\n", FBDrawWhich, x, y, fill_data);
|
|
//printf("%lld\n", &fbyptr[x & fb_x_mask] - FB[!FBDrawWhich]);
|
|
fbyptr[x & EraseParams.fb_x_mask] = EraseParams.fill_data;
|
|
x++;
|
|
}
|
|
} while(x < EraseParams.x_bound);
|
|
|
|
EraseYCounter++;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void AdjustTS(const int32 delta)
|
|
{
|
|
lastts += delta;
|
|
if(FBVBEraseActive)
|
|
FBVBEraseLastTS += delta;
|
|
}
|
|
|
|
static INLINE void WriteReg(const unsigned which, const uint16 value)
|
|
{
|
|
SS_SetEventNT(&events[SS_EVENT_VDP2], VDP2::Update(SH7095_mem_timestamp));
|
|
sscpu_timestamp_t nt = Update(SH7095_mem_timestamp);
|
|
|
|
SS_DBGTI(SS_DBG_VDP1_REGW, "[VDP1] Register write: 0x%02x: 0x%04x", which << 1, value);
|
|
|
|
switch(which)
|
|
{
|
|
default:
|
|
SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP1, "[VDP1] Unknown write of value 0x%04x to register 0x%02x", value, which << 1);
|
|
break;
|
|
|
|
case 0x0: // TVMR
|
|
TVMR = value & 0xF;
|
|
break;
|
|
|
|
case 0x1: // FBCR
|
|
FBCR = value & 0x1F;
|
|
FBManualPending |= value & 0x2;
|
|
break;
|
|
|
|
case 0x2: // PTMR
|
|
PTMR = (value & 0x3);
|
|
if(value & 0x1)
|
|
{
|
|
StartDrawing();
|
|
nt = SH7095_mem_timestamp + 1;
|
|
}
|
|
break;
|
|
|
|
case 0x3: // EWDR
|
|
EWDR = value;
|
|
break;
|
|
|
|
case 0x4: // EWLR
|
|
EWLR = value & 0x7FFF;
|
|
break;
|
|
|
|
case 0x5: // EWRR
|
|
EWRR = value;
|
|
break;
|
|
|
|
case 0x6: // ENDR
|
|
if(DrawingActive)
|
|
{
|
|
DrawingActive = false;
|
|
if(CycleCounter < 0)
|
|
CycleCounter = 0;
|
|
nt = SH7095_mem_timestamp + VDP1_IdleTimingGran;
|
|
SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP1, "[VDP1] Program forced termination of VDP1 drawing.");
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
SS_SetEventNT(&events[SS_EVENT_VDP1], nt);
|
|
}
|
|
|
|
static INLINE uint16 ReadReg(const unsigned which)
|
|
{
|
|
switch(which)
|
|
{
|
|
default:
|
|
SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP1, "[VDP1] Unknown read from register 0x%02x", which);
|
|
return 0;
|
|
|
|
case 0x8: // EDSR
|
|
return EDSR;
|
|
|
|
case 0x9: // LOPR
|
|
return LOPR;
|
|
|
|
case 0xA: // COPR
|
|
return CurCommandAddr >> 2;
|
|
|
|
case 0xB: // MODR
|
|
return (0x1 << 12) | ((PTMR & 0x2) << 7) | ((FBCR & 0x1E) << 3) | (TVMR << 0);
|
|
}
|
|
}
|
|
|
|
void Write8_DB(uint32 A, uint16 DB)
|
|
{
|
|
A &= 0x1FFFFF;
|
|
|
|
if(A < 0x80000)
|
|
{
|
|
ne16_wbo_be<uint8>(VRAM, A, DB >> (((A & 1) ^ 1) << 3) );
|
|
return;
|
|
}
|
|
|
|
if(A < 0x100000)
|
|
{
|
|
uint32 FBA = A;
|
|
|
|
if((TVMR & (TVMR_8BPP | TVMR_ROTATE)) == (TVMR_8BPP | TVMR_ROTATE))
|
|
FBA = (FBA & 0x1FF) | ((FBA << 1) & 0x3FC00) | ((FBA >> 8) & 0x200);
|
|
|
|
ne16_wbo_be<uint8>(FB[FBDrawWhich], FBA & 0x3FFFF, DB >> (((A & 1) ^ 1) << 3) );
|
|
return;
|
|
}
|
|
|
|
SS_DBGTI(SS_DBG_WARNING | SS_DBG_VDP1, "[VDP1] 8-bit write to 0x%08x(DB=0x%04x)", A, DB);
|
|
WriteReg((A - 0x100000) >> 1, DB);
|
|
}
|
|
|
|
void Write16_DB(uint32 A, uint16 DB)
|
|
{
|
|
A &= 0x1FFFFE;
|
|
|
|
if(A < 0x80000)
|
|
{
|
|
VRAM[A >> 1] = DB;
|
|
return;
|
|
}
|
|
|
|
if(A < 0x100000)
|
|
{
|
|
uint32 FBA = A;
|
|
|
|
if((TVMR & (TVMR_8BPP | TVMR_ROTATE)) == (TVMR_8BPP | TVMR_ROTATE))
|
|
FBA = (FBA & 0x1FF) | ((FBA << 1) & 0x3FC00) | ((FBA >> 8) & 0x200);
|
|
|
|
FB[FBDrawWhich][(FBA >> 1) & 0x1FFFF] = DB;
|
|
return;
|
|
}
|
|
|
|
WriteReg((A - 0x100000) >> 1, DB);
|
|
}
|
|
|
|
uint16 Read16_DB(uint32 A)
|
|
{
|
|
A &= 0x1FFFFE;
|
|
|
|
if(A < 0x080000)
|
|
return VRAM[A >> 1];
|
|
|
|
if(A < 0x100000)
|
|
{
|
|
uint32 FBA = A;
|
|
|
|
if((TVMR & (TVMR_8BPP | TVMR_ROTATE)) == (TVMR_8BPP | TVMR_ROTATE))
|
|
FBA = (FBA & 0x1FF) | ((FBA << 1) & 0x3FC00) | ((FBA >> 8) & 0x200);
|
|
|
|
return FB[FBDrawWhich][(FBA >> 1) & 0x1FFFF];
|
|
}
|
|
|
|
return ReadReg((A - 0x100000) >> 1);
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
/*void MakeDump(const std::string& path)
|
|
{
|
|
FileStream fp(path, FileStream::MODE_WRITE);
|
|
|
|
for(unsigned i = 0; i < 0x40000; i++)
|
|
fp.print_format("0x%04x, ", VRAM[i]);
|
|
|
|
fp.close();
|
|
}*/
|
|
|
|
}
|
|
|
|
}
|