1797 lines
40 KiB
C++
1797 lines
40 KiB
C++
/* Mednafen - Multi-system Emulator
|
|
*
|
|
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/* VDC emulation */
|
|
|
|
#include "../defs.h"
|
|
//#include <mednafen/video.h>
|
|
|
|
#include <math.h>
|
|
#include "vdc.h"
|
|
|
|
#define VDC_DEBUG(x, ...) { }
|
|
//#define VDC_DEBUG(x, ...) printf(x ": HPhase=%d, HPhaseCounter=%d, RCRCount=%d\n", ## __VA_ARGS__, HPhase, HPhaseCounter, RCRCount);
|
|
|
|
#define VDC_UNDEFINED(format, ...) { }
|
|
//#define VDC_UNDEFINED(format, ...) printf(format " RCRCount=%d" "\n", ## __VA_ARGS__, RCRCount)
|
|
|
|
#define VDC_WARNING(format, ...) { }
|
|
//#define VDC_WARNING(format, ...) { printf(format "\n", ## __VA_ARGS__); }
|
|
|
|
#define ULE_BG 1
|
|
#define ULE_SPR 2
|
|
|
|
static const unsigned int bat_width_tab[4] = { 32, 64, 128, 128 };
|
|
static const unsigned int bat_width_shift_tab[4] = { 5, 6, 7, 7 };
|
|
static const unsigned int bat_height_tab[2] = { 32, 64 };
|
|
|
|
void VDC::FixTileCache(uint16 A)
|
|
{
|
|
uint32 charname = (A >> 4);
|
|
uint32 y = (A & 0x7);
|
|
uint8 *tc = bg_tile_cache[charname][y];
|
|
|
|
uint32 bitplane01 = VRAM[y + charname * 16];
|
|
uint32 bitplane23 = VRAM[y+ 8 + charname * 16];
|
|
|
|
for(int x = 0; x < 8; x++)
|
|
{
|
|
uint32 raw_pixel = ((bitplane01 >> x) & 1);
|
|
raw_pixel |= ((bitplane01 >> (x + 8)) & 1) << 1;
|
|
raw_pixel |= ((bitplane23 >> x) & 1) << 2;
|
|
raw_pixel |= ((bitplane23 >> (x + 8)) & 1) << 3;
|
|
tc[7 - x] = raw_pixel;
|
|
}
|
|
}
|
|
|
|
// Some virtual vdc macros to make code simpler to read
|
|
#define M_vdc_HSW (HSR & 0x1F) // Horizontal Synchro Width
|
|
#define M_vdc_HDS ((HSR >> 8) & 0x7F) // Horizontal Display Start
|
|
#define M_vdc_HDW (HDR & 0x7F) // Horizontal Display Width
|
|
#define M_vdc_HDE ((HDR >> 8) & 0x7F) // Horizontal Display End
|
|
|
|
#define M_vdc_VSW (VSR & 0x1F) // Vertical synchro width
|
|
#define M_vdc_VDS ((VSR >> 8) & 0xFF) // Vertical Display Start
|
|
#define M_vdc_VDW (VDR & 0x1FF) // Vertical Display Width(Height? :b)
|
|
#define M_vdc_VCR (VCR & 0xFF)
|
|
|
|
#define M_vdc_EX ((CR >> 4) & 0x3)
|
|
#define M_vdc_TE ((CR >> 8) & 0x3)
|
|
|
|
#define VDCS_CR 0x01 // Sprite #0 collision interrupt occurred
|
|
#define VDCS_OR 0x02 // sprite overflow "" ""
|
|
#define VDCS_RR 0x04 // RCR "" ""
|
|
#define VDCS_DS 0x08 // VRAM to SAT DMA completion interrupt occurred
|
|
#define VDCS_DV 0x10 // VRAM to VRAM DMA completion interrupt occurred
|
|
#define VDCS_VD 0x20 // Vertical blank interrupt occurred
|
|
#define VDCS_BSY 0x40 // VDC is waiting for a CPU access slot during the active display area??
|
|
|
|
uint32 VDC::GetRegister(const unsigned int id, char *special, const uint32 special_len)
|
|
{
|
|
uint32 value = 0xDEADBEEF;
|
|
|
|
switch(id)
|
|
{
|
|
case GSREG_SELECT:
|
|
value = select;
|
|
break;
|
|
|
|
case GSREG_STATUS:
|
|
value = status;
|
|
break;
|
|
|
|
case GSREG_MAWR:
|
|
value = MAWR;
|
|
break;
|
|
|
|
case GSREG_MARR:
|
|
value = MARR;
|
|
break;
|
|
|
|
case GSREG_CR:
|
|
value = CR;
|
|
|
|
if(special)
|
|
{
|
|
trio_snprintf(special, special_len, "Sprite Hit IRQ: %s, Sprite Overflow IRQ: %s, RCR IRQ: %s, VBlank IRQ: %s, Sprites: %s, Background: %s", (value & 1) ? "On" : "Off", (value & 2) ? "On" : "Off",
|
|
(value & 4) ? "On" : "Off", (value & 8) ? "On" : "Off", (value & 0x40) ? "On" : "Off", (value & 0x80) ? "On" : "Off");
|
|
}
|
|
break;
|
|
|
|
case GSREG_RCR:
|
|
value = RCR;
|
|
break;
|
|
|
|
case GSREG_BXR:
|
|
value = BXR;
|
|
break;
|
|
|
|
case GSREG_BYR:
|
|
value = BYR;
|
|
break;
|
|
|
|
case GSREG_MWR:
|
|
value = MWR;
|
|
|
|
if(special)
|
|
{
|
|
trio_snprintf(special, special_len, "CG Mode: %d, BAT Width: %d(tiles), BAT Height: %d(tiles)", (int)(bool)(value & 0x80),
|
|
bat_width_tab[(value >> 4) & 0x3],
|
|
bat_height_tab[(value >> 6) & 0x1]);
|
|
}
|
|
break;
|
|
|
|
case GSREG_HSR:
|
|
value = HSR;
|
|
if(special)
|
|
{
|
|
trio_snprintf(special, special_len, "HSW: %02x, HDS: %02x", value & 0x1F, (value >> 8) & 0x7F);
|
|
}
|
|
break;
|
|
|
|
case GSREG_HDR:
|
|
value = HDR;
|
|
if(special)
|
|
{
|
|
trio_snprintf(special, special_len, "HDW: %02x, HDE: %02x", value & 0x7F, (value >> 8) & 0x7F);
|
|
}
|
|
break;
|
|
|
|
|
|
case GSREG_VSR:
|
|
value = VSR;
|
|
if(special)
|
|
{
|
|
trio_snprintf(special, special_len, "VSW: %02x, VDS: %02x", value & 0x1F, (value >> 8) & 0xFF);
|
|
}
|
|
break;
|
|
|
|
case GSREG_VDR:
|
|
value = VDR;
|
|
break;
|
|
|
|
case GSREG_VCR:
|
|
value = VCR;
|
|
break;
|
|
|
|
case GSREG_DCR:
|
|
value = DCR;
|
|
if(special)
|
|
{
|
|
trio_snprintf(special, special_len, "SATB DMA IRQ: %s, VRAM DMA IRQ: %s, DMA Source Address: %s, DMA Dest Address: %s, Auto SATB DMA: %s",
|
|
(DCR & 0x1) ? "On" : "Off", (DCR & 0x2) ? "On" : "Off", (DCR & 0x4) ? "Decrement" : "Increment", (DCR & 0x8) ? "Decrement" : "Increment",
|
|
(DCR & 0x10) ? "On" : "Off");
|
|
}
|
|
break;
|
|
|
|
case GSREG_SOUR:
|
|
value = SOUR;
|
|
break;
|
|
|
|
case GSREG_DESR:
|
|
value = DESR;
|
|
break;
|
|
|
|
case GSREG_LENR:
|
|
value = LENR;
|
|
break;
|
|
|
|
case GSREG_DVSSR:
|
|
value = DVSSR;
|
|
break;
|
|
}
|
|
|
|
return(value);
|
|
}
|
|
|
|
void VDC::SetRegister(const unsigned int id, const uint32 value)
|
|
{
|
|
switch(id)
|
|
{
|
|
default: break;
|
|
|
|
case GSREG_STATUS:
|
|
status = value & 0x3F;
|
|
break;
|
|
|
|
case GSREG_SELECT:
|
|
select = value & 0x1F;
|
|
break;
|
|
|
|
case GSREG_MAWR:
|
|
MAWR = value & 0xFFFF;
|
|
break;
|
|
|
|
case GSREG_MARR:
|
|
MARR = value & 0xFFFF;
|
|
break;
|
|
|
|
case GSREG_CR:
|
|
CR = value & 0xFFFF;
|
|
break;
|
|
|
|
case GSREG_RCR:
|
|
RCR = value & 0x3FF;
|
|
break;
|
|
|
|
case GSREG_BXR:
|
|
BXR = value & 0x3FF;
|
|
break;
|
|
|
|
case GSREG_BYR:
|
|
BYR = value & 0x1FF;
|
|
break;
|
|
|
|
case GSREG_MWR:
|
|
MWR = value & 0xFFFF;
|
|
break;
|
|
|
|
case GSREG_HSR:
|
|
HSR = value & 0xFFFF;
|
|
break;
|
|
|
|
case GSREG_HDR:
|
|
HDR = value & 0xFFFF;
|
|
break;
|
|
|
|
case GSREG_VSR:
|
|
VSR = value & 0xFFFF;
|
|
break;
|
|
|
|
case GSREG_VDR:
|
|
VDR = value & 0xFFFF;
|
|
break;
|
|
|
|
case GSREG_VCR:
|
|
VCR = value & 0xFFFF;
|
|
break;
|
|
|
|
case GSREG_DCR:
|
|
DCR = value & 0xFFFF;
|
|
break;
|
|
|
|
case GSREG_SOUR:
|
|
SOUR = value & 0xFFFF;
|
|
break;
|
|
|
|
case GSREG_DESR:
|
|
DESR = value & 0xFFFF;
|
|
break;
|
|
|
|
case GSREG_LENR:
|
|
LENR = value & 0xFFFF;
|
|
break;
|
|
|
|
case GSREG_DVSSR:
|
|
DVSSR = value & 0xFFFF;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void VDC::SetLayerEnableMask(uint64 mask)
|
|
{
|
|
userle = mask;
|
|
}
|
|
|
|
void VDC::RunSATDMA(int32 cycles, bool force_completion)
|
|
{
|
|
assert(sat_dma_counter > 0);
|
|
|
|
if(force_completion)
|
|
cycles = sat_dma_counter;
|
|
|
|
sat_dma_counter -= cycles;
|
|
if(sat_dma_counter <= 0)
|
|
{
|
|
if(DCR & 0x01)
|
|
{
|
|
VDC_DEBUG("Sprite DMA IRQ");
|
|
status |= VDCS_DS;
|
|
IRQHook(TRUE);
|
|
}
|
|
CheckAndCommitPending();
|
|
burst_mode = true;
|
|
}
|
|
}
|
|
|
|
void VDC::RunDMA(int32 cycles, bool force_completion)
|
|
{
|
|
int num_transfers = 0;
|
|
|
|
if(force_completion)
|
|
{
|
|
VDMA_CycleCounter = 0;
|
|
|
|
num_transfers = 65536 * 2;
|
|
}
|
|
else
|
|
{
|
|
VDMA_CycleCounter += cycles;
|
|
num_transfers = VDMA_CycleCounter >> 1;
|
|
VDMA_CycleCounter -= num_transfers << 1;
|
|
}
|
|
|
|
while(num_transfers--)
|
|
{
|
|
if(!DMAReadWrite)
|
|
{
|
|
if(SOUR >= VRAM_Size)
|
|
VDC_UNDEFINED("Unmapped VRAM DMA read");
|
|
|
|
DMAReadBuffer = VRAM[SOUR];
|
|
//printf("DMA Read: %04x, %04x\n", SOUR, DMAReadBuffer);
|
|
}
|
|
else
|
|
{
|
|
if(DESR < VRAM_Size)
|
|
{
|
|
VRAM[DESR] = DMAReadBuffer;
|
|
FixTileCache(DESR);
|
|
}
|
|
|
|
SOUR += (((DCR & 0x4) >> 1) ^ 2) - 1;
|
|
DESR += (((DCR & 0x8) >> 2) ^ 2) - 1;
|
|
LENR--;
|
|
|
|
if(LENR == 0xFFFF) // DMA is done.
|
|
{
|
|
DMARunning = 0; // Clear this BEFORE CheckAndCommitPending()
|
|
|
|
CheckAndCommitPending();
|
|
|
|
if(DCR & 0x02)
|
|
{
|
|
status |= VDCS_DV;
|
|
IRQHook(TRUE);
|
|
VDC_DEBUG("DMA IRQ");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
DMAReadWrite ^= 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
<RyphZomb> ChrlyMac: Was it you who determined exactly how many VDC clocks the SAT DMA took?
|
|
<RyphZomb> I know someone did, but I can't remember the results...
|
|
<ChrlyMac> 1024
|
|
<ChrlyMac> It happens at the VDW->VCR transition
|
|
*/
|
|
|
|
void VDC::IncRCR(void)
|
|
{
|
|
if(NeedBGYInc)
|
|
{
|
|
NeedBGYInc = false;
|
|
if(0 == RCRCount)
|
|
BG_YMoo = BYR;
|
|
else
|
|
BG_YMoo++;
|
|
}
|
|
|
|
NeedBGYInc = true;
|
|
RCRCount++;
|
|
|
|
VPhaseCounter--;
|
|
|
|
if(VPhaseCounter <= 0)
|
|
{
|
|
VPhase = (VPhase + 1) % VPHASE_COUNT;
|
|
switch(VPhase)
|
|
{
|
|
case VPHASE_VDS: VPhaseCounter = VDS_cache + 2;
|
|
break;
|
|
|
|
case VPHASE_VDW: VPhaseCounter = VDW_cache + 1;
|
|
//BG_YMoo = BYR - 1;
|
|
RCRCount = 0;
|
|
burst_mode = !(CR & 0xC0);
|
|
NeedVBIRQTest = true;
|
|
NeedSATDMATest = true;
|
|
|
|
if(!burst_mode)
|
|
{
|
|
if(sat_dma_counter > 0)
|
|
{
|
|
printf("SAT DMA cancelled???\n");
|
|
sat_dma_counter = 0;
|
|
CheckAndCommitPending();
|
|
}
|
|
if(DMARunning)
|
|
{
|
|
printf("DMA Running Cancelled\n");
|
|
DMARunning = false;
|
|
CheckAndCommitPending();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VPHASE_VCR: VPhaseCounter = VCR_cache;
|
|
break;
|
|
|
|
case VPHASE_VSW: VPhaseCounter = VSW_cache + 1;
|
|
MWR_cache = MWR;
|
|
VDS_cache = M_vdc_VDS;
|
|
VSW_cache = M_vdc_VSW;
|
|
VDW_cache = M_vdc_VDW;
|
|
VCR_cache = M_vdc_VCR;
|
|
//VDC_WARNING("VSW Started");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(VPhase == VPHASE_VDW && !burst_mode)
|
|
{
|
|
FetchSpriteData();
|
|
}
|
|
|
|
if((int)RCRCount == ((int)RCR - 0x40) && (CR & 0x04))
|
|
{
|
|
VDC_DEBUG("RCR IRQ");
|
|
status |= VDCS_RR;
|
|
IRQHook(TRUE);
|
|
}
|
|
}
|
|
|
|
void VDC::DoVBIRQTest(void)
|
|
{
|
|
if(CR & 0x08)
|
|
{
|
|
VDC_DEBUG("VBlank IRQ");
|
|
status |= VDCS_VD;
|
|
IRQHook(TRUE);
|
|
}
|
|
}
|
|
|
|
static const int Cycles_Between_RCRIRQ_And_HDWEnd = 4;
|
|
|
|
int VDC::TimeFromHDSStartToBYRLatch(void)
|
|
{
|
|
int ret = 1;
|
|
|
|
if(HDS_cache > 2)
|
|
ret += ((HDS_cache + 1) * 8) - 24 + 2;
|
|
|
|
|
|
//printf("%d, %d\n", HDS_cache, ret);
|
|
|
|
return(ret);
|
|
}
|
|
|
|
int VDC::TimeFromBYRLatchToBXRLatch(void)
|
|
{
|
|
int ret = 2;
|
|
|
|
if(HDS_cache > 2)
|
|
ret = 1;
|
|
|
|
return(ret);
|
|
}
|
|
|
|
void VDC::HDS_Start(void)
|
|
{
|
|
if(NeedRCRInc)
|
|
{
|
|
IncRCR();
|
|
NeedRCRInc = false;
|
|
}
|
|
|
|
if(sprite_cg_fetch_counter > 0)
|
|
{
|
|
VDC_WARNING("Sprite truncation on %d. Wanted sprites: %d, cycles needed but not left: %d\n", RCRCount, active_sprites, sprite_cg_fetch_counter);
|
|
sprite_cg_fetch_counter = 0;
|
|
CheckAndCommitPending();
|
|
}
|
|
|
|
HSW_cache = M_vdc_HSW;
|
|
HDS_cache = M_vdc_HDS;
|
|
HDW_cache = M_vdc_HDW;
|
|
HDE_cache = M_vdc_HDE;
|
|
|
|
VDC_DEBUG("HDS Start! HSW: %d, HDW: %d, HDW: %d, HDE: %d\n", HSW_cache, HDS_cache, HDW_cache, HDE_cache);
|
|
|
|
CR_cache = CR;
|
|
|
|
HPhase = HPHASE_HDS;
|
|
HPhaseCounter = TimeFromHDSStartToBYRLatch();
|
|
}
|
|
|
|
int32 VDC::HSync(bool hb)
|
|
{
|
|
if(M_vdc_EX)
|
|
{
|
|
in_exhsync = 0;
|
|
return(CalcNextEvent());
|
|
}
|
|
in_exhsync = hb;
|
|
|
|
if(hb) // Going into hsync
|
|
{
|
|
mystery_counter = 48;
|
|
mystery_phase = false;
|
|
}
|
|
else // Leaving hsync
|
|
{
|
|
HPhase = HPHASE_HSW;
|
|
HPhaseCounter = 8;
|
|
|
|
//HDS_Start();
|
|
//HPhaseCounter += 8;
|
|
|
|
pixel_copy_count = 0;
|
|
}
|
|
|
|
|
|
return(CalcNextEvent());
|
|
}
|
|
|
|
int32 VDC::VSync(bool vb)
|
|
{
|
|
if(M_vdc_EX >= 0x2)
|
|
{
|
|
in_exvsync = 0;
|
|
return(CalcNextEvent());
|
|
}
|
|
in_exvsync = vb;
|
|
|
|
//printf("VBlank: %d\n", vb);
|
|
if(vb) // Going into vsync
|
|
{
|
|
NeedRCRInc = false;
|
|
NeedBGYInc = false;
|
|
/* if(NeedRCRInc)
|
|
{
|
|
IncRCR();
|
|
NeedRCRInc = false;
|
|
}
|
|
*/
|
|
MWR_cache = MWR;
|
|
|
|
VDS_cache = M_vdc_VDS;
|
|
VSW_cache = M_vdc_VSW;
|
|
VDW_cache = M_vdc_VDW;
|
|
VCR_cache = M_vdc_VCR;
|
|
|
|
VPhase = VPHASE_VSW;
|
|
VPhaseCounter = VSW_cache + 1;
|
|
}
|
|
else // Leaving vsync
|
|
{
|
|
|
|
}
|
|
return(CalcNextEvent());
|
|
}
|
|
|
|
//int32 VDC::Run(int32 clocks, bool hs, bool vs, uint16 *pixels, bool skip)
|
|
int32 VDC::Run(int32 clocks, uint16 *pixels, bool skip)
|
|
{
|
|
//uint16 *spixels = pixels;
|
|
|
|
//puts("Run begin");
|
|
//fflush(stdout);
|
|
|
|
while(clocks > 0)
|
|
{
|
|
int32 chunk_clocks = clocks;
|
|
|
|
if(chunk_clocks > HPhaseCounter)
|
|
{
|
|
chunk_clocks = HPhaseCounter;
|
|
}
|
|
|
|
if(sat_dma_counter > 0 && chunk_clocks > sat_dma_counter)
|
|
chunk_clocks = sat_dma_counter;
|
|
|
|
if(sprite_cg_fetch_counter > 0 && chunk_clocks > sprite_cg_fetch_counter)
|
|
chunk_clocks = sprite_cg_fetch_counter;
|
|
|
|
if(mystery_counter > 0 && chunk_clocks > mystery_counter)
|
|
chunk_clocks = mystery_counter;
|
|
|
|
if(mystery_counter > 0)
|
|
{
|
|
mystery_counter -= chunk_clocks;
|
|
if(mystery_counter <= 0)
|
|
{
|
|
mystery_phase = !mystery_phase;
|
|
if(mystery_phase)
|
|
mystery_counter = 16;
|
|
else
|
|
CheckAndCommitPending();
|
|
}
|
|
}
|
|
|
|
if(sprite_cg_fetch_counter > 0)
|
|
{
|
|
sprite_cg_fetch_counter -= chunk_clocks;
|
|
if(sprite_cg_fetch_counter <= 0)
|
|
CheckAndCommitPending();
|
|
}
|
|
|
|
if(VPhase != VPHASE_VDW)
|
|
{
|
|
if(NeedSATDMATest)
|
|
{
|
|
NeedSATDMATest = false;
|
|
if(SATBPending || (DCR & 0x10))
|
|
{
|
|
SATBPending = 0;
|
|
|
|
sat_dma_counter = 1024;
|
|
|
|
if(DVSSR > (VRAM_Size - 0x100))
|
|
VDC_UNDEFINED("Unmapped VRAM DVSSR DMA read");
|
|
|
|
if(DVSSR < VRAM_Size)
|
|
{
|
|
uint32 len = 256;
|
|
if(DVSSR > (VRAM_Size - 0x100))
|
|
len = VRAM_Size - DVSSR;
|
|
memcpy(SAT, &VRAM[DVSSR], len * sizeof(uint16));
|
|
}
|
|
}
|
|
else
|
|
burst_mode = true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
if(DMAPending && burst_mode)
|
|
{
|
|
VDC_DEBUG("DMA Started");
|
|
DMAPending = false;
|
|
DMARunning = true;
|
|
VDMA_CycleCounter = 0;
|
|
DMAReadWrite = 0;
|
|
}
|
|
|
|
if(sat_dma_counter > 0)
|
|
RunSATDMA(chunk_clocks);
|
|
else if(DMARunning)
|
|
RunDMA(chunk_clocks);
|
|
|
|
if(pixel_copy_count > 0)
|
|
{
|
|
if(!skip)
|
|
{
|
|
for(int i = 0; i < chunk_clocks; i++)
|
|
pixels[i] = linebuf[pixel_desu + i];
|
|
//memcpy(pixels, linebuf + pixel_desu, chunk_clocks * sizeof(uint16));
|
|
|
|
if(M_vdc_TE == 0x1)
|
|
for(int i = 0; i < chunk_clocks; i++)
|
|
pixels[i] |= VDC_DISP_OUT_MASK;
|
|
}
|
|
|
|
pixel_desu += chunk_clocks;
|
|
pixel_copy_count -= chunk_clocks;
|
|
}
|
|
else
|
|
{
|
|
uint16 pix = 0x100;
|
|
|
|
if(M_vdc_TE == 0x1)
|
|
{
|
|
if(HPhase != HPHASE_HDS && HPhase != HPHASE_HDS_PART2 && HPhase != HPHASE_HDS_PART3)
|
|
pix |= VDC_DISP_OUT_MASK;
|
|
}
|
|
|
|
if(HPhase == HPHASE_HSW)
|
|
{
|
|
if(M_vdc_EX >= 0x1)
|
|
pix |= VDC_HSYNC_OUT_MASK;
|
|
|
|
if(M_vdc_TE >= 0x2)
|
|
pix |= VDC_DISP_OUT_MASK;
|
|
}
|
|
if(VPhase == VPHASE_VSW && M_vdc_EX >= 0x2)
|
|
pix |= VDC_VSYNC_OUT_MASK;
|
|
|
|
if(!(userle & 1))
|
|
pix |= VDC_BGDISABLE_OUT_MASK;
|
|
|
|
if(!skip)
|
|
{
|
|
for(int i = 0; i < chunk_clocks; i++)
|
|
pixels[i] = pix;
|
|
}
|
|
}
|
|
|
|
HPhaseCounter -= chunk_clocks;
|
|
|
|
assert(HPhaseCounter >= 0);
|
|
|
|
while(HPhaseCounter <= 0)
|
|
{
|
|
HPhase = (HPhase + 1) % HPHASE_COUNT;
|
|
|
|
switch(HPhase)
|
|
{
|
|
case HPHASE_HDS: HDS_Start();
|
|
break;
|
|
|
|
|
|
case HPHASE_HDS_PART2:
|
|
HPhaseCounter = TimeFromBYRLatchToBXRLatch();
|
|
|
|
if(NeedBGYInc && !in_exhsync)
|
|
{
|
|
NeedBGYInc = false;
|
|
|
|
if(0 == RCRCount)
|
|
BG_YMoo = BYR;
|
|
else
|
|
BG_YMoo++;
|
|
}
|
|
BG_YOffset = BG_YMoo;
|
|
break;
|
|
|
|
case HPHASE_HDS_PART3:
|
|
HPhaseCounter = (HDS_cache + 1) * 8 - TimeFromHDSStartToBYRLatch() - TimeFromBYRLatchToBXRLatch();
|
|
|
|
assert(HPhaseCounter > 0);
|
|
|
|
BG_XOffset = BXR;
|
|
break;
|
|
|
|
case HPHASE_HDW:
|
|
NeedRCRInc = true;
|
|
if(VPhase != VPHASE_VDW && NeedVBIRQTest)
|
|
{
|
|
DoVBIRQTest();
|
|
NeedVBIRQTest = false;
|
|
}
|
|
CheckAndCommitPending();
|
|
|
|
HPhaseCounter = (HDW_cache + 1) * 8 - Cycles_Between_RCRIRQ_And_HDWEnd;
|
|
if(VPhase == VPHASE_VDW)
|
|
{
|
|
if(!burst_mode)
|
|
{
|
|
pixel_desu = 0;
|
|
pixel_copy_count = (HDW_cache + 1) * 8;
|
|
|
|
// BG off, sprite on: fill = 0x100. bg off, sprite off: fill = 0x000
|
|
if(!(CR_cache & 0x80))
|
|
{
|
|
uint16 fill_val;
|
|
|
|
if(!(CR_cache & 0xC0)) // Sprites and BG off
|
|
fill_val = 0x000;
|
|
else // Only BG off
|
|
fill_val = 0x100 | ((userle & ULE_BG) ? 0 : VDC_BGDISABLE_OUT_MASK);
|
|
|
|
if(!(userle & ULE_BG))
|
|
fill_val |= VDC_BGDISABLE_OUT_MASK;
|
|
|
|
for(int i = 0; i < pixel_copy_count; i++)
|
|
linebuf[i] = fill_val;
|
|
}
|
|
|
|
if(!skip)
|
|
if(CR_cache & 0x80)
|
|
{
|
|
DrawBG(linebuf, userle & ULE_BG);
|
|
}
|
|
//printf("%d %02x %02x\n", RCRCount, CR, CR_cache);
|
|
if(CR_cache & 0x40)
|
|
DrawSprites(linebuf, (userle & ULE_SPR) && !skip);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case HPHASE_HDW_FINAL:
|
|
if(NeedRCRInc)
|
|
{
|
|
IncRCR();
|
|
NeedRCRInc = false;
|
|
}
|
|
HPhaseCounter = Cycles_Between_RCRIRQ_And_HDWEnd;
|
|
break;
|
|
|
|
case HPHASE_HDE: //if(!burst_mode) //if(VPhase == VPHASE_VDW) //if(!burst_mode)
|
|
// lastats = 16; // + 16;
|
|
//else
|
|
// lastats = 16;
|
|
HPhaseCounter = (HDE_cache + 1) * 8;
|
|
break;
|
|
|
|
case HPHASE_HSW: HPhaseCounter = (HSW_cache + 1) * 8; break;
|
|
}
|
|
}
|
|
pixels += chunk_clocks;
|
|
clocks -= chunk_clocks;
|
|
}
|
|
|
|
//puts("Run end");
|
|
//fflush(stdout);
|
|
|
|
return(CalcNextEvent());
|
|
}
|
|
|
|
|
|
void VDC::CalcWidthStartEnd(uint32 &display_width, uint32 &start, uint32 &end)
|
|
{
|
|
display_width = (M_vdc_HDW + 1) * 8;
|
|
|
|
start = 0;
|
|
end = start + display_width;
|
|
}
|
|
|
|
void VDC::DrawBG(uint16 *target, int enabled)
|
|
{
|
|
uint32 width;
|
|
uint32 start;
|
|
uint32 end;
|
|
int bat_width = bat_width_tab[(MWR_cache >> 4) & 3];
|
|
int bat_width_mask = bat_width - 1;
|
|
int bat_width_shift = bat_width_shift_tab[(MWR_cache >> 4) & 3];
|
|
int bat_height_mask = bat_height_tab[(MWR_cache >> 6) & 1] - 1;
|
|
|
|
CalcWidthStartEnd(width, start, end);
|
|
|
|
if(!enabled)
|
|
{
|
|
for(uint32 x = start; x < end; x++)
|
|
target[x] = 0x000 | VDC_BGDISABLE_OUT_MASK;
|
|
return;
|
|
}
|
|
|
|
{
|
|
int bat_y = ((BG_YOffset >> 3) & bat_height_mask) << bat_width_shift;
|
|
uint32 first_end = start + 8 - (BG_XOffset & 7);
|
|
uint32 dohmask = 0xFFFFFFFF;
|
|
|
|
if((MWR_cache & 0x3) == 0x3)
|
|
{
|
|
if(MWR_cache & 0x80)
|
|
dohmask = 0xCCCCCCCC;
|
|
else
|
|
dohmask = 0x33333333;
|
|
}
|
|
|
|
// Draw the first pixels of the first tile, depending on the lower 3 bits of the xscroll/xoffset register, to
|
|
// we can render the rest of the line in 8x1 chunks, which is faster.
|
|
for(uint32 x = start; x < first_end; x++)
|
|
{
|
|
int bat_x = (BG_XOffset >> 3) & bat_width_mask;
|
|
uint16 bat = VRAM[bat_x | bat_y];
|
|
const uint8 pal_or = ((bat >> 8) & 0xF0);
|
|
int palette_index = ((bat >> 12) & 0x0F) << 4;
|
|
uint32 raw_pixel;
|
|
|
|
raw_pixel = bg_tile_cache[bat & 0xFFF][BG_YOffset & 7][BG_XOffset & 0x7] & dohmask;
|
|
target[x] = palette_index | raw_pixel | pal_or;
|
|
|
|
if((bat & 0xFFF) > VRAM_BGTileNoMask)
|
|
VDC_UNDEFINED("Unmapped BG tile read");
|
|
|
|
BG_XOffset++;
|
|
}
|
|
|
|
int bat_boom = (BG_XOffset >> 3) & bat_width_mask;
|
|
int line_sub = BG_YOffset & 7;
|
|
|
|
if((MWR_cache & 0x3) == 0x3)
|
|
{
|
|
for(uint32 x = first_end; x < end; x+=8)
|
|
{
|
|
const uint16 bat = VRAM[bat_boom | bat_y];
|
|
const uint8 pal_or = ((bat >> 8) & 0xF0);
|
|
uint8 *pix_lut = bg_tile_cache[bat & 0xFFF][line_sub];
|
|
|
|
if((bat & 0xFFF) > VRAM_BGTileNoMask)
|
|
VDC_UNDEFINED("Unmapped BG tile read");
|
|
|
|
|
|
(target + 0)[x] = (pix_lut[0] & dohmask) | pal_or;
|
|
(target + 1)[x] = (pix_lut[1] & dohmask) | pal_or;
|
|
(target + 2)[x] = (pix_lut[2] & dohmask) | pal_or;
|
|
(target + 3)[x] = (pix_lut[3] & dohmask) | pal_or;
|
|
(target + 4)[x] = (pix_lut[4] & dohmask) | pal_or;
|
|
(target + 5)[x] = (pix_lut[5] & dohmask) | pal_or;
|
|
(target + 6)[x] = (pix_lut[6] & dohmask) | pal_or;
|
|
(target + 7)[x] = (pix_lut[7] & dohmask) | pal_or;
|
|
|
|
bat_boom = (bat_boom + 1) & bat_width_mask;
|
|
BG_XOffset++;
|
|
|
|
}
|
|
}
|
|
else
|
|
for(uint32 x = first_end; x < end; x+=8) // This will draw past the right side of the buffer, but since our pitch is 1024, and max width is ~512, we're safe. Also,
|
|
// any overflow that is on the visible screen are will be hidden by the overscan color code below this code.
|
|
{
|
|
const uint16 bat = VRAM[bat_boom | bat_y];
|
|
const uint8 pal_or = ((bat >> 8) & 0xF0);
|
|
uint8 *pix_lut = bg_tile_cache[bat & 0xFFF][line_sub];
|
|
|
|
if((bat & 0xFFF) > VRAM_BGTileNoMask)
|
|
VDC_UNDEFINED("Unmapped BG tile read");
|
|
|
|
(target + 0)[x] = pix_lut[0] | pal_or;
|
|
(target + 1)[x] = pix_lut[1] | pal_or;
|
|
(target + 2)[x] = pix_lut[2] | pal_or;
|
|
(target + 3)[x] = pix_lut[3] | pal_or;
|
|
(target + 4)[x] = pix_lut[4] | pal_or;
|
|
(target + 5)[x] = pix_lut[5] | pal_or;
|
|
(target + 6)[x] = pix_lut[6] | pal_or;
|
|
(target + 7)[x] = pix_lut[7] | pal_or;
|
|
|
|
bat_boom = (bat_boom + 1) & bat_width_mask;
|
|
BG_XOffset++;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define SPRF_PRIORITY 0x00080
|
|
#define SPRF_HFLIP 0x00800
|
|
#define SPRF_VFLIP 0x08000
|
|
#define SPRF_SPRITE0 0x10000
|
|
|
|
static const unsigned int sprite_height_tab[4] = { 16, 32, 64, 64 };
|
|
static const unsigned int sprite_height_no_mask[4] = { ~0U, ~2U, ~6U, ~6U };
|
|
static const unsigned int sprite_width_tab[2] = { 16, 32 };
|
|
|
|
void VDC::FetchSpriteData(void)
|
|
{
|
|
active_sprites = 0;
|
|
|
|
// First, grab the up to 16 sprites.
|
|
for(int i = 0; i < 64; i++)
|
|
{
|
|
int16 y = (SAT[i * 4 + 0] & 0x3FF) - 0x40;
|
|
uint16 x = (SAT[i * 4 + 1] & 0x3FF);
|
|
uint16 no = (SAT[i * 4 + 2] >> 1) & 0x3FF; // Todo, cg mode bit
|
|
uint16 flags = (SAT[i * 4 + 3]);
|
|
|
|
uint32 palette_index = (flags & 0xF) << 4;
|
|
uint32 height = sprite_height_tab[(flags >> 12) & 3];
|
|
uint32 width = sprite_width_tab[(flags >> 8) & 1];
|
|
|
|
if((int32)RCRCount >= y && (int32)RCRCount < (int32)(y + height))
|
|
{
|
|
bool second_half = 0;
|
|
uint32 y_offset = RCRCount - y;
|
|
if(y_offset > height) continue;
|
|
|
|
|
|
breepbreep:
|
|
|
|
if(active_sprites == 16)
|
|
{
|
|
if(CR & 0x2)
|
|
{
|
|
status |= VDCS_OR;
|
|
IRQHook(TRUE);
|
|
VDC_DEBUG("Overflow IRQ");
|
|
}
|
|
if(!unlimited_sprites)
|
|
break;
|
|
}
|
|
|
|
|
|
{
|
|
if(flags & SPRF_VFLIP)
|
|
y_offset = height - 1 - y_offset;
|
|
|
|
no &= sprite_height_no_mask[(flags >> 12) & 3];
|
|
no |= (y_offset & 0x30) >> 3;
|
|
if(width == 32) no &= ~1;
|
|
if(second_half)
|
|
no |= 1;
|
|
|
|
SpriteList[active_sprites].flags = flags;
|
|
|
|
if(flags & SPRF_HFLIP && width == 32)
|
|
no ^= 1;
|
|
//printf("Found: %d %d\n", RCRCount, x);
|
|
SpriteList[active_sprites].x = x;
|
|
SpriteList[active_sprites].palette_index = palette_index;
|
|
|
|
if((no * 64) >= VRAM_Size)
|
|
VDC_UNDEFINED("Unmapped VRAM sprite tile read");
|
|
|
|
if((MWR_cache & 0xC) == 4)
|
|
{
|
|
if(SAT[i * 4 + 2] & 1)
|
|
{
|
|
SpriteList[active_sprites].pattern_data[0] = VRAM[no * 64 + (y_offset & 15) + 32];
|
|
SpriteList[active_sprites].pattern_data[1] = VRAM[no * 64 + (y_offset & 15) + 48];
|
|
SpriteList[active_sprites].pattern_data[2] = 0;
|
|
SpriteList[active_sprites].pattern_data[3] = 0;
|
|
}
|
|
else
|
|
{
|
|
SpriteList[active_sprites].pattern_data[0] = VRAM[no * 64 + (y_offset & 15) ];
|
|
SpriteList[active_sprites].pattern_data[1] = VRAM[no * 64 + (y_offset & 15) + 16];
|
|
SpriteList[active_sprites].pattern_data[2] = 0;
|
|
SpriteList[active_sprites].pattern_data[3] = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SpriteList[active_sprites].pattern_data[0] = VRAM[no * 64 + (y_offset & 15) ];
|
|
SpriteList[active_sprites].pattern_data[1] = VRAM[no * 64 + (y_offset & 15) + 16];
|
|
SpriteList[active_sprites].pattern_data[2] = VRAM[no * 64 + (y_offset & 15) + 32];
|
|
SpriteList[active_sprites].pattern_data[3] = VRAM[no * 64 + (y_offset & 15) + 48];
|
|
}
|
|
|
|
SpriteList[active_sprites].flags |= i ? 0 : SPRF_SPRITE0;
|
|
|
|
active_sprites++;
|
|
|
|
if(width == 32 && !second_half)
|
|
{
|
|
second_half = 1;
|
|
x += 16;
|
|
y_offset = RCRCount - y; // Fix the y offset so that sprites that are hflipped + vflipped display properly
|
|
goto breepbreep;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sprite_cg_fetch_counter = ((active_sprites < 16) ? active_sprites : 16) * 4;
|
|
}
|
|
|
|
void VDC::DrawSprites(uint16 *target, int enabled)
|
|
{
|
|
alignas(16) uint16 sprite_line_buf[1024];
|
|
|
|
uint32 display_width, start, end;
|
|
|
|
CalcWidthStartEnd(display_width, start, end);
|
|
|
|
for(unsigned int i = start; i < end; i++)
|
|
sprite_line_buf[i] = 0;
|
|
|
|
for(int i = (active_sprites - 1) ; i >= 0; i--)
|
|
{
|
|
int32 pos = SpriteList[i].x - 0x20 + start;
|
|
uint32 prio_or = 0;
|
|
|
|
if(SpriteList[i].flags & SPRF_PRIORITY)
|
|
prio_or = 0x200;
|
|
|
|
if((SpriteList[i].flags & SPRF_SPRITE0) && (CR & 0x01))
|
|
{
|
|
for(uint32 x = 0; x < 16; x++)
|
|
{
|
|
uint32 raw_pixel;
|
|
uint32 pi = SpriteList[i].palette_index;
|
|
uint32 rev_x = 15 - x;
|
|
|
|
if(SpriteList[i].flags & SPRF_HFLIP)
|
|
rev_x = x;
|
|
|
|
raw_pixel = (SpriteList[i].pattern_data[0] >> rev_x) & 1;
|
|
raw_pixel |= ((SpriteList[i].pattern_data[1] >> rev_x) & 1) << 1;
|
|
raw_pixel |= ((SpriteList[i].pattern_data[2] >> rev_x) & 1) << 2;
|
|
raw_pixel |= ((SpriteList[i].pattern_data[3] >> rev_x) & 1) << 3;
|
|
|
|
if(raw_pixel)
|
|
{
|
|
pi |= 0x100;
|
|
uint32 tx = pos + x;
|
|
|
|
if(tx >= end) // Covers negative and overflowing the right side.
|
|
continue;
|
|
|
|
if(sprite_line_buf[tx] & 0xF)
|
|
{
|
|
status |= VDCS_CR;
|
|
VDC_DEBUG("Sprite hit IRQ");
|
|
IRQHook(TRUE);
|
|
}
|
|
sprite_line_buf[tx] = pi | raw_pixel | prio_or;
|
|
}
|
|
}
|
|
} // End sprite hit loop
|
|
else
|
|
{
|
|
for(uint32 x = 0; x < 16; x++)
|
|
{
|
|
uint32 raw_pixel;
|
|
uint32 pi = SpriteList[i].palette_index;
|
|
uint32 rev_x = 15 - x;
|
|
|
|
if(SpriteList[i].flags & SPRF_HFLIP)
|
|
rev_x = x;
|
|
|
|
raw_pixel = (SpriteList[i].pattern_data[0] >> rev_x) & 1;
|
|
raw_pixel |= ((SpriteList[i].pattern_data[1] >> rev_x) & 1) << 1;
|
|
raw_pixel |= ((SpriteList[i].pattern_data[2] >> rev_x) & 1) << 2;
|
|
raw_pixel |= ((SpriteList[i].pattern_data[3] >> rev_x) & 1) << 3;
|
|
|
|
if(raw_pixel)
|
|
{
|
|
pi |= 0x100;
|
|
uint32 tx = pos + x;
|
|
|
|
if(tx >= end) // Covers negative and overflowing the right side.
|
|
continue;
|
|
sprite_line_buf[tx] = pi | raw_pixel | prio_or;
|
|
}
|
|
}
|
|
} // End non-sprite-hit loop
|
|
}
|
|
|
|
if(enabled)
|
|
{
|
|
for(unsigned int x = start; x < end; x++)
|
|
{
|
|
if(sprite_line_buf[x] & 0x0F)
|
|
{
|
|
if(!(target[x] & 0x0F) || (sprite_line_buf[x] & 0x200))
|
|
target[x] = sprite_line_buf[x] & 0x1FF;
|
|
}
|
|
}
|
|
}
|
|
active_sprites = 0;
|
|
}
|
|
|
|
/*
|
|
Caution: If we ever add something to Write() or Read() that will affect the timing of the next event, make sure
|
|
to set the passed-by-reference next_event BEFORE calling this function, or otherwise re-engineer this convoluted setup.
|
|
*/
|
|
void VDC::DoWaitStates(void)
|
|
{
|
|
//bool did_wait = VDC_IS_BSY;
|
|
|
|
while(VDC_IS_BSY)
|
|
{
|
|
//int32 to_wait = CalcNextEvent();
|
|
//if(!WSHook || !WSHook(to_wait))
|
|
if(!WSHook || !WSHook(-1)) // Event-counter-based wait-stating
|
|
{
|
|
if(DMARunning)
|
|
{
|
|
VDC_WARNING("VRAM DMA completion forced.");
|
|
RunDMA(0, TRUE);
|
|
}
|
|
|
|
if(sat_dma_counter > 0)
|
|
{
|
|
VDC_WARNING("SAT DMA completion forced.");
|
|
RunSATDMA(0, TRUE);
|
|
}
|
|
|
|
if(mystery_phase)
|
|
{
|
|
bool backup_mystery_phase = mystery_phase;
|
|
mystery_phase = false;
|
|
CheckAndCommitPending();
|
|
mystery_phase = backup_mystery_phase;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//if(did_wait)
|
|
// printf("End of wait stating: %d %d\n", VDMA_CycleCounter, sat_dma_counter);
|
|
|
|
assert(!pending_read);
|
|
assert(!pending_write);
|
|
}
|
|
|
|
uint8 VDC::Read(uint32 A, int32 &next_event, bool peek)
|
|
{
|
|
uint8 ret = 0;
|
|
int msb = A & 1;
|
|
|
|
A &= 0x3;
|
|
|
|
switch(A)
|
|
{
|
|
case 0x0: ret = status | (VDC_IS_BSY ? 0x40 : 0x00);
|
|
|
|
if(!peek)
|
|
{
|
|
status &= ~0x3F;
|
|
IRQHook(FALSE);
|
|
}
|
|
break;
|
|
|
|
case 0x2:
|
|
case 0x3:
|
|
if(!peek)
|
|
{
|
|
// Should we only wait on MSB reads...
|
|
DoWaitStates();
|
|
}
|
|
|
|
ret = VDC_REGGETP(read_buffer, msb);
|
|
|
|
if(select == 0x2) // VRR - VRAM Read Register
|
|
{
|
|
if(msb)
|
|
{
|
|
if(!peek)
|
|
{
|
|
pending_read = TRUE;
|
|
pending_read_addr = MARR;
|
|
MARR += vram_inc_tab[(CR >> 11) & 0x3];
|
|
|
|
CheckAndCommitPending();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return(ret);
|
|
}
|
|
|
|
uint16 VDC::Read16(bool A, bool peek)
|
|
{
|
|
uint16 ret = 0;
|
|
|
|
if(!A)
|
|
{
|
|
ret = status | (VDC_IS_BSY ? 0x40 : 0x00);
|
|
|
|
if(!peek)
|
|
{
|
|
status &= ~0x3F;
|
|
IRQHook(FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(!peek)
|
|
DoWaitStates();
|
|
|
|
ret = read_buffer;
|
|
|
|
if(select == 0x2) // VRR - VRAM Read Register
|
|
{
|
|
if(!peek)
|
|
{
|
|
pending_read = TRUE;
|
|
pending_read_addr = MARR;
|
|
MARR += vram_inc_tab[(CR >> 11) & 0x3];
|
|
|
|
CheckAndCommitPending();
|
|
}
|
|
}
|
|
}
|
|
|
|
return(ret);
|
|
}
|
|
|
|
|
|
void VDC::CheckAndCommitPending(void)
|
|
{
|
|
if(sat_dma_counter <= 0 && !DMARunning /* && sprite_cg_fetch_counter <= 0*/ && !mystery_phase)
|
|
{
|
|
if(pending_write)
|
|
{
|
|
if(pending_write_addr < VRAM_Size)
|
|
{
|
|
VRAM[pending_write_addr] = pending_write_latch;
|
|
FixTileCache(pending_write_addr);
|
|
}
|
|
//else
|
|
// VDC_UNDEFINED("Unmapped VRAM write");
|
|
|
|
pending_write = FALSE;
|
|
}
|
|
|
|
if(pending_read)
|
|
{
|
|
if(pending_read_addr >= VRAM_Size)
|
|
VDC_UNDEFINED("Unmapped VRAM VRR read");
|
|
|
|
read_buffer = VRAM[pending_read_addr];
|
|
pending_read = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void VDC::Write(uint32 A, uint8 V, int32 &next_event)
|
|
{
|
|
int msb = A & 1;
|
|
|
|
A &= 0x3;
|
|
|
|
//if((A == 0x2 || A == 0x3) && (select >= 0xF && select <= 0x12))
|
|
//if((A == 2 || A == 3) && select != 2)
|
|
// printf("VDC Write(RCRCount=%d): A=%02x, Select=%02x, V=%02x\n", RCRCount, A, select, V);
|
|
|
|
switch(A)
|
|
{
|
|
case 0x0: select = V & 0x1F;
|
|
break;
|
|
|
|
case 0x2:
|
|
case 0x3:
|
|
//if((select & 0x1F) >= 0x9 && (select & 0x1F) <= 0x1F)
|
|
// VDC_DEBUG("%02x %d, %02x", select & 0x1F, msb, V);
|
|
|
|
switch(select & 0x1F)
|
|
{
|
|
case 0x00: VDC_REGSETP(MAWR, V, msb);
|
|
break;
|
|
|
|
case 0x01: VDC_REGSETP(MARR, V, msb);
|
|
if(msb)
|
|
{
|
|
DoWaitStates();
|
|
|
|
pending_read = TRUE;
|
|
pending_read_addr = MARR;
|
|
MARR += vram_inc_tab[(CR >> 11) & 0x3];
|
|
|
|
CheckAndCommitPending();
|
|
}
|
|
break;
|
|
|
|
case 0x02: if(!msb)
|
|
{
|
|
write_latch = V;
|
|
}
|
|
else
|
|
{
|
|
// We must call CommitPendingWrite at the end of SAT/VRAM DMA for this to work!
|
|
DoWaitStates();
|
|
|
|
pending_write = TRUE;
|
|
pending_write_addr = MAWR;
|
|
pending_write_latch = write_latch | (V << 8);
|
|
MAWR += vram_inc_tab[(CR >> 11) & 0x3];
|
|
|
|
CheckAndCommitPending();
|
|
}
|
|
break;
|
|
|
|
case 0x05: VDC_REGSETP(CR, V, msb);
|
|
//printf("CR: %04x, %d\n", CR, msb);
|
|
break;
|
|
|
|
case 0x06: VDC_REGSETP(RCR, V, msb);
|
|
RCR &= 0x3FF;
|
|
break;
|
|
|
|
case 0x07: VDC_REGSETP(BXR, V, msb);
|
|
BXR &= 0x3FF;
|
|
//VDC_DEBUG("BXR Set");
|
|
break;
|
|
|
|
case 0x08: VDC_REGSETP(BYR, V, msb);
|
|
BYR &= 0x1FF;
|
|
BG_YMoo = BYR; // Set it on LSB and MSB writes(only changing on MSB breaks Youkai Douchuuki)
|
|
//VDC_DEBUG("BYR Set");
|
|
break;
|
|
|
|
case 0x09: VDC_REGSETP(MWR, V, msb); break;
|
|
case 0x0a: VDC_REGSETP(HSR, V, msb); break;
|
|
case 0x0b: VDC_REGSETP(HDR, V, msb); break;
|
|
case 0x0c: VDC_REGSETP(VSR, V, msb); break;
|
|
case 0x0d: VDC_REGSETP(VDR, V, msb); break;
|
|
case 0x0e: VDC_REGSETP(VCR, V, msb); break;
|
|
case 0x0f: VDC_REGSETP(DCR, V, msb);
|
|
if(DMARunning)
|
|
{
|
|
VDC_UNDEFINED("Set DCR during DMA: %04x\n", DCR);
|
|
}
|
|
|
|
if(DMAPending)
|
|
{
|
|
VDC_UNDEFINED("Set DCR while DMAPending: %04x\n", DCR);
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x10: VDC_REGSETP(SOUR, V, msb);
|
|
if(DMARunning)
|
|
{
|
|
VDC_UNDEFINED("Set SOUR during DMA: %04x\n", SOUR);
|
|
}
|
|
|
|
if(DMAPending)
|
|
{
|
|
VDC_UNDEFINED("Set SOUR while DMAPending: %04x\n", SOUR);
|
|
}
|
|
break;
|
|
|
|
case 0x11: VDC_REGSETP(DESR, V, msb);
|
|
if(DMARunning)
|
|
{
|
|
VDC_UNDEFINED("Set DESR during DMA: %04x\n", DESR);
|
|
}
|
|
if(DMAPending)
|
|
{
|
|
VDC_UNDEFINED("Set DESR while DMAPending: %04x\n", DESR);
|
|
}
|
|
break;
|
|
|
|
case 0x12: VDC_REGSETP(LENR, V, msb);
|
|
if(DMARunning)
|
|
{
|
|
VDC_UNDEFINED("Set LENR during DMA: %04x\n", LENR);
|
|
}
|
|
|
|
if(DMAPending)
|
|
{
|
|
VDC_UNDEFINED("Set LENR while DMAPending: %04x\n", LENR);
|
|
}
|
|
|
|
if(msb)
|
|
{
|
|
VDC_DEBUG("DMA: %04x %04x %04x, %02x", SOUR, DESR, LENR, DCR);
|
|
DMAPending = 1;
|
|
}
|
|
break;
|
|
|
|
case 0x13: VDC_REGSETP(DVSSR, V, msb);
|
|
SATBPending = 1;
|
|
break;
|
|
|
|
default: VDC_WARNING("Unknown VDC register write: %04x %02x", select, V);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void VDC::Write16(bool A, uint16 V)
|
|
{
|
|
if(!A)
|
|
select = V & 0x1F;
|
|
else
|
|
{
|
|
switch(select & 0x1F)
|
|
{
|
|
case 0x00: MAWR = V;
|
|
break;
|
|
|
|
|
|
case 0x01: MARR = V;
|
|
|
|
DoWaitStates();
|
|
|
|
pending_read = TRUE;
|
|
pending_read_addr = MARR;
|
|
|
|
MARR += vram_inc_tab[(CR >> 11) & 0x3];
|
|
|
|
CheckAndCommitPending();
|
|
break;
|
|
|
|
|
|
case 0x02: // We must call CommitPendingWrite at the end of SAT/VRAM DMA for this to work!
|
|
DoWaitStates();
|
|
|
|
pending_write = TRUE;
|
|
pending_write_addr = MAWR;
|
|
pending_write_latch = V;
|
|
MAWR += vram_inc_tab[(CR >> 11) & 0x3];
|
|
|
|
CheckAndCommitPending();
|
|
break;
|
|
|
|
case 0x05: CR = V;
|
|
break;
|
|
|
|
case 0x06: RCR = V & 0x3FF;
|
|
break;
|
|
|
|
case 0x07: BXR = V & 0x3FF;
|
|
//VDC_DEBUG("BXR Set");
|
|
break;
|
|
|
|
case 0x08: BYR = V & 0x1FF;
|
|
BG_YMoo = BYR;
|
|
//VDC_DEBUG("BYR Set");
|
|
break;
|
|
|
|
case 0x09: MWR = V; break;
|
|
case 0x0a: HSR = V; break;
|
|
case 0x0b: HDR = V; break;
|
|
case 0x0c: VSR = V; break;
|
|
case 0x0d: VDR = V; break;
|
|
case 0x0e: VCR = V; break;
|
|
|
|
case 0x0f: DCR = V;
|
|
if(DMARunning)
|
|
{
|
|
VDC_UNDEFINED("Set DCR during DMA: %04x\n", DCR);
|
|
}
|
|
|
|
if(DMAPending)
|
|
{
|
|
VDC_UNDEFINED("Set DCR while DMAPending: %04x\n", DCR);
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x10: SOUR = V;
|
|
if(DMARunning)
|
|
{
|
|
VDC_UNDEFINED("Set SOUR during DMA: %04x\n", SOUR);
|
|
}
|
|
|
|
if(DMAPending)
|
|
{
|
|
VDC_UNDEFINED("Set SOUR while DMAPending: %04x\n", SOUR);
|
|
}
|
|
break;
|
|
|
|
case 0x11: DESR = V;
|
|
if(DMARunning)
|
|
{
|
|
VDC_UNDEFINED("Set DESR during DMA: %04x\n", DESR);
|
|
}
|
|
if(DMAPending)
|
|
{
|
|
VDC_UNDEFINED("Set DESR while DMAPending: %04x\n", DESR);
|
|
}
|
|
break;
|
|
|
|
case 0x12: LENR = V;
|
|
if(DMARunning)
|
|
{
|
|
VDC_UNDEFINED("Set LENR during DMA: %04x\n", LENR);
|
|
}
|
|
|
|
if(DMAPending)
|
|
{
|
|
VDC_UNDEFINED("Set LENR while DMAPending: %04x\n", LENR);
|
|
}
|
|
|
|
VDC_DEBUG("DMA: %04x %04x %04x, %02x", SOUR, DESR, LENR, DCR);
|
|
|
|
DMAPending = 1;
|
|
break;
|
|
|
|
case 0x13: DVSSR = V;
|
|
SATBPending = 1;
|
|
break;
|
|
|
|
default: VDC_WARNING("Oops 2: %04x %02x", select, V);
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int32 VDC::Reset(void)
|
|
{
|
|
memset(VRAM, 0, sizeof(VRAM));
|
|
memset(SAT, 0, sizeof(SAT));
|
|
memset(SpriteList, 0, sizeof(SpriteList));
|
|
|
|
for(uint32 A = 0; A < 65536; A += 16)
|
|
FixTileCache(A);
|
|
|
|
pending_read = false;
|
|
pending_read_addr = 0xFFFF;
|
|
read_buffer = 0xFFFF;
|
|
write_latch = 0;
|
|
|
|
pending_write = false;
|
|
pending_write_addr = 0xFFFF;
|
|
pending_write_latch = 0xFFFF;
|
|
|
|
status = 0;
|
|
|
|
HSR = 0;
|
|
HDR = 0;
|
|
VSR = 0;
|
|
VDR = 0;
|
|
VCR = 0;
|
|
|
|
HSW_cache = M_vdc_HSW;
|
|
HDS_cache = M_vdc_HDS;
|
|
HDW_cache = M_vdc_HDW;
|
|
HDE_cache = M_vdc_HDE;
|
|
|
|
VDS_cache = M_vdc_VDS;
|
|
VSW_cache = M_vdc_VSW;
|
|
VDW_cache = M_vdc_VDW;
|
|
VCR_cache = M_vdc_VCR;
|
|
|
|
|
|
|
|
MAWR = 0;
|
|
MARR = 0;
|
|
|
|
CR = CR_cache = 0;
|
|
RCR = 0;
|
|
BXR = 0;
|
|
BYR = 0;
|
|
MWR = 0;
|
|
MWR_cache = 0;
|
|
|
|
DCR = 0;
|
|
SOUR = 0;
|
|
DESR = 0;
|
|
LENR = 0;
|
|
DVSSR = 0;
|
|
|
|
VDMA_CycleCounter = 0;
|
|
|
|
RCRCount = 0;
|
|
|
|
DMAReadBuffer = 0;
|
|
DMAReadWrite = 0;
|
|
DMARunning = 0;
|
|
DMAPending = 0;
|
|
SATBPending = 0;
|
|
burst_mode = 0;
|
|
|
|
BG_XOffset = 0;
|
|
BG_YOffset = 0;
|
|
BG_YMoo = 0;
|
|
|
|
sat_dma_counter = 0;
|
|
select = 0;
|
|
|
|
pixel_copy_count = 0;
|
|
|
|
|
|
NeedRCRInc = false;
|
|
NeedVBIRQTest = false;
|
|
NeedSATDMATest = false;
|
|
NeedBGYInc = false;
|
|
|
|
HPhase = 0;
|
|
VPhase = 0;
|
|
HPhaseCounter = 1;
|
|
VPhaseCounter = 1;
|
|
|
|
sprite_cg_fetch_counter = 0;
|
|
|
|
mystery_counter = 0;
|
|
mystery_phase = false;
|
|
|
|
pixel_desu = 0;
|
|
pixel_copy_count = 0;
|
|
active_sprites = 0;
|
|
|
|
return(CalcNextEvent());
|
|
}
|
|
|
|
VDC::VDC()
|
|
{
|
|
SetUnlimitedSprites(false);
|
|
SetVRAMSize(65536);
|
|
userle = ~0;
|
|
|
|
WSHook = NULL;
|
|
IRQHook = NULL;
|
|
|
|
in_exhsync = false;
|
|
in_exvsync = false;
|
|
}
|
|
|
|
void VDC::SetUnlimitedSprites(const bool nospritelimit)
|
|
{
|
|
unlimited_sprites = nospritelimit;
|
|
}
|
|
|
|
void VDC::SetVRAMSize(const uint32 par_VRAM_Size)
|
|
{
|
|
//const uint32 old_VRAM_Size;
|
|
|
|
assert(par_VRAM_Size == round_up_pow2(par_VRAM_Size));
|
|
assert(par_VRAM_Size >= 16 && par_VRAM_Size <= 65536);
|
|
|
|
VRAM_Size = par_VRAM_Size;
|
|
VRAM_SizeMask = VRAM_Size - 1;
|
|
VRAM_BGTileNoMask = VRAM_SizeMask / 16;
|
|
|
|
// for(uint32 A = std::min<uint32>(old_VRAM_Size, par_VRAM_Size); A < 65536; A += 16)
|
|
// FixTileCache(A);
|
|
}
|
|
|
|
VDC::~VDC()
|
|
{
|
|
|
|
}
|
|
|
|
#ifdef WANT_DEBUGGER
|
|
bool VDC::DoGfxDecode(uint32 *target, const uint32 *color_table, const uint32 TransparentColor, bool DecodeSprites,
|
|
int32 w, int32 h, int32 scroll)
|
|
{
|
|
const uint32 *palette_ptr = color_table;
|
|
|
|
if(DecodeSprites)
|
|
{
|
|
for(int y = 0; y < h; y++)
|
|
{
|
|
for(int x = 0; x < w; x += 16)
|
|
{
|
|
int which_tile = (x / 16) + (scroll + (y / 16)) * (w / 16);
|
|
|
|
if(which_tile >= VRAM_Size / 64)
|
|
{
|
|
for(int sx = 0; sx < 16; sx++)
|
|
{
|
|
target[x + sx] = TransparentColor;
|
|
target[x + w * 1 + sx] = 0;
|
|
target[x + w * 2 + sx] = 0;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
uint16 cg[4];
|
|
cg[0] = VRAM[which_tile * 64 + (y & 15)];
|
|
cg[1] = VRAM[which_tile * 64 + (y & 15) + 16];
|
|
cg[2] = VRAM[which_tile * 64 + (y & 15) + 32];
|
|
cg[3] = VRAM[which_tile * 64 + (y & 15) + 48];
|
|
for(int sx = 0; sx < 16; sx++)
|
|
{
|
|
int rev_sx = 15 - sx;
|
|
target[x + sx] = palette_ptr[(((cg[0] >> rev_sx) & 1) << 0) |
|
|
(((cg[1] >> rev_sx) & 1) << 1) | (((cg[2] >> rev_sx) & 1) << 2) | (((cg[3] >> rev_sx) & 1) << 3)];
|
|
target[x + w * 1 + sx] = which_tile;
|
|
target[x + w * 2 + sx] = which_tile * 64;
|
|
}
|
|
}
|
|
target += w * 3;
|
|
}
|
|
}
|
|
else for(int y = 0; y < h; y++)
|
|
{
|
|
for(int x = 0; x < w; x+=8)
|
|
{
|
|
int which_tile = (x / 8) + (scroll + (y / 8)) * (w / 8);
|
|
|
|
if(which_tile >= (VRAM_Size / 16))
|
|
{
|
|
for(int sx = 0; sx < 8; sx++)
|
|
{
|
|
target[x + sx] = TransparentColor;
|
|
target[x + w * 1 + sx] = 0;
|
|
target[x + w * 2 + sx] = 0;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
target[x + 0] = palette_ptr[ bg_tile_cache[which_tile][y & 0x7][0]];
|
|
target[x + 1] = palette_ptr[ bg_tile_cache[which_tile][y & 0x7][1]];
|
|
target[x + 2] = palette_ptr[ bg_tile_cache[which_tile][y & 0x7][2]];
|
|
target[x + 3] = palette_ptr[ bg_tile_cache[which_tile][y & 0x7][3]];
|
|
target[x + 4] = palette_ptr[ bg_tile_cache[which_tile][y & 0x7][4]];
|
|
target[x + 5] = palette_ptr[ bg_tile_cache[which_tile][y & 0x7][5]];
|
|
target[x + 6] = palette_ptr[ bg_tile_cache[which_tile][y & 0x7][6]];
|
|
target[x + 7] = palette_ptr[ bg_tile_cache[which_tile][y & 0x7][7]];
|
|
|
|
target[x + w*1 + 0]=target[x + w*1 + 1]=target[x + w*1 + 2]=target[x + w*1 + 3] =
|
|
target[x + w*1 + 4]=target[x + w*1 + 5]=target[x + w*1 + 6]=target[x + w*1 + 7] = which_tile;
|
|
|
|
target[x + w*2 + 0]=target[x + w*2 + 1]=target[x + w*2 + 2]=target[x + w*2 + 3] =
|
|
target[x + w*2 + 4]=target[x + w*2 + 5]=target[x + w*2 + 6]=target[x + w*2 + 7] = which_tile * 16;
|
|
}
|
|
target += w * 3;
|
|
}
|
|
|
|
return(1);
|
|
}
|
|
#endif
|
|
|