mirror of
https://github.com/libretro/beetle-pce-fast-libretro.git
synced 2024-11-24 00:10:14 +00:00
1428 lines
41 KiB
C++
1428 lines
41 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
|
|
*/
|
|
|
|
#include "pcfx.h"
|
|
#include "soundbox.h"
|
|
#include "input.h"
|
|
#include "king.h"
|
|
#include "timer.h"
|
|
#include "interrupt.h"
|
|
#include "debug.h"
|
|
#include "rainbow.h"
|
|
#include "huc6273.h"
|
|
#include "fxscsi.h"
|
|
#include "../cdrom/scsicd.h"
|
|
#include "../mempatcher.h"
|
|
#include "../cdrom/cdromif.h"
|
|
#include "../md5.h"
|
|
#include "../clamp.h"
|
|
|
|
#include <trio/trio.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
/* FIXME: soundbox, vce, vdc, rainbow, and king store wait states should be 4, not 2, but V810 has write buffers which can mask wait state penalties.
|
|
This is a hack to somewhat address the issue, but to really fix it, we need to handle write buffer emulation in the V810 emulation core itself.
|
|
*/
|
|
|
|
static std::vector<CDIF*> *cdifs = NULL;
|
|
static bool CD_TrayOpen;
|
|
static int CD_SelectedDisc; // -1 for no disc
|
|
|
|
V810 PCFX_V810;
|
|
|
|
int64 pcfx_timestamp_base;
|
|
|
|
static uint8 *BIOSROM = NULL; // 1MB
|
|
static uint8 *RAM = NULL; // 2MB
|
|
static uint8 *FXSCSIROM = NULL; // 512KiB
|
|
|
|
static uint32 RAM_LPA; // Last page access
|
|
|
|
static const int RAM_PageSize = 2048;
|
|
static const int RAM_PageNOTMask = ~(RAM_PageSize - 1);
|
|
|
|
static uint16 Last_VDC_AR[2];
|
|
|
|
static bool WantHuC6273 = FALSE;
|
|
|
|
//static
|
|
VDC *fx_vdc_chips[2];
|
|
|
|
static uint16 BackupControl;
|
|
static uint8 BackupRAM[0x8000], ExBackupRAM[0x8000];
|
|
static uint8 ExBusReset; // I/O Register at 0x0700
|
|
|
|
static bool BRAMDisabled; // Cached at game load, don't remove this caching behavior or save game loss may result(if we ever get a GUI).
|
|
|
|
// Checks to see if this main-RAM-area access
|
|
// is in the same DRAM page as the last access.
|
|
#define RAMLPCHECK \
|
|
{ \
|
|
if((A & RAM_PageNOTMask) != RAM_LPA) \
|
|
{ \
|
|
timestamp += 3; \
|
|
RAM_LPA = A & RAM_PageNOTMask; \
|
|
} \
|
|
}
|
|
|
|
static v810_timestamp_t next_pad_ts, next_timer_ts, next_adpcm_ts, next_king_ts;
|
|
|
|
void PCFX_FixNonEvents(void)
|
|
{
|
|
if(next_pad_ts & 0x40000000)
|
|
next_pad_ts = PCFX_EVENT_NONONO;
|
|
|
|
if(next_timer_ts & 0x40000000)
|
|
next_timer_ts = PCFX_EVENT_NONONO;
|
|
|
|
if(next_adpcm_ts & 0x40000000)
|
|
next_adpcm_ts = PCFX_EVENT_NONONO;
|
|
|
|
if(next_king_ts & 0x40000000)
|
|
next_king_ts = PCFX_EVENT_NONONO;
|
|
}
|
|
|
|
void PCFX_Event_Reset(void)
|
|
{
|
|
next_pad_ts = PCFX_EVENT_NONONO;
|
|
next_timer_ts = PCFX_EVENT_NONONO;
|
|
next_adpcm_ts = PCFX_EVENT_NONONO;
|
|
next_king_ts = PCFX_EVENT_NONONO;
|
|
}
|
|
|
|
static INLINE uint32 CalcNextTS(void)
|
|
{
|
|
v810_timestamp_t next_timestamp = next_king_ts;
|
|
|
|
if(next_timestamp > next_pad_ts)
|
|
next_timestamp = next_pad_ts;
|
|
|
|
if(next_timestamp > next_timer_ts)
|
|
next_timestamp = next_timer_ts;
|
|
|
|
if(next_timestamp > next_adpcm_ts)
|
|
next_timestamp = next_adpcm_ts;
|
|
|
|
return(next_timestamp);
|
|
}
|
|
|
|
static void RebaseTS(const v810_timestamp_t timestamp)
|
|
{
|
|
assert(next_pad_ts > timestamp);
|
|
assert(next_timer_ts > timestamp);
|
|
assert(next_adpcm_ts > timestamp);
|
|
assert(next_king_ts > timestamp);
|
|
|
|
next_pad_ts -= timestamp;
|
|
next_timer_ts -= timestamp;
|
|
next_adpcm_ts -= timestamp;
|
|
next_king_ts -= timestamp;
|
|
}
|
|
|
|
|
|
void PCFX_SetEvent(const int type, const v810_timestamp_t next_timestamp)
|
|
{
|
|
//assert(next_timestamp > PCFX_V810.v810_timestamp);
|
|
|
|
if(type == PCFX_EVENT_PAD)
|
|
next_pad_ts = next_timestamp;
|
|
else if(type == PCFX_EVENT_TIMER)
|
|
next_timer_ts = next_timestamp;
|
|
else if(type == PCFX_EVENT_ADPCM)
|
|
next_adpcm_ts = next_timestamp;
|
|
else if(type == PCFX_EVENT_KING)
|
|
next_king_ts = next_timestamp;
|
|
|
|
if(next_timestamp < PCFX_V810.GetEventNT())
|
|
PCFX_V810.SetEventNT(next_timestamp);
|
|
}
|
|
|
|
int32 MDFN_FASTCALL pcfx_event_handler(const v810_timestamp_t timestamp)
|
|
{
|
|
if(timestamp >= next_king_ts)
|
|
next_king_ts = KING_Update(timestamp);
|
|
|
|
if(timestamp >= next_pad_ts)
|
|
next_pad_ts = FXINPUT_Update(timestamp);
|
|
|
|
if(timestamp >= next_timer_ts)
|
|
next_timer_ts = FXTIMER_Update(timestamp);
|
|
|
|
if(timestamp >= next_adpcm_ts)
|
|
next_adpcm_ts = SoundBox_ADPCMUpdate(timestamp);
|
|
|
|
#if 1
|
|
assert(next_king_ts > timestamp);
|
|
assert(next_pad_ts > timestamp);
|
|
assert(next_timer_ts > timestamp);
|
|
assert(next_adpcm_ts > timestamp);
|
|
#endif
|
|
return(CalcNextTS());
|
|
}
|
|
|
|
static void ForceEventUpdates(const uint32 timestamp)
|
|
{
|
|
next_king_ts = KING_Update(timestamp);
|
|
next_pad_ts = FXINPUT_Update(timestamp);
|
|
next_timer_ts = FXTIMER_Update(timestamp);
|
|
next_adpcm_ts = SoundBox_ADPCMUpdate(timestamp);
|
|
|
|
//printf("Meow: %d\n", CalcNextTS());
|
|
PCFX_V810.SetEventNT(CalcNextTS());
|
|
}
|
|
|
|
#include "io-handler.inc"
|
|
#include "mem-handler.inc"
|
|
|
|
typedef struct
|
|
{
|
|
int8 tracknum;
|
|
int8 format;
|
|
uint32 lba;
|
|
} CDGameEntryTrack;
|
|
|
|
typedef struct
|
|
{
|
|
const char *name;
|
|
const char *name_original; // Original non-Romanized text.
|
|
const uint32 flags; // Emulation flags.
|
|
const unsigned int discs; // Number of discs for this game.
|
|
CDGameEntryTrack tracks[2][100]; // 99 tracks and 1 leadout track
|
|
} CDGameEntry;
|
|
|
|
#define CDGE_FORMAT_AUDIO 0
|
|
#define CDGE_FORMAT_DATA 1
|
|
|
|
#define CDGE_FLAG_ACCURATE_V810 0x01
|
|
#define CDGE_FLAG_FXGA 0x02
|
|
|
|
static uint32 EmuFlags;
|
|
|
|
static CDGameEntry GameList[] =
|
|
{
|
|
#include "gamedb.inc"
|
|
};
|
|
|
|
|
|
static void Emulate(EmulateSpecStruct *espec)
|
|
{
|
|
v810_timestamp_t v810_timestamp;
|
|
|
|
FXINPUT_Frame();
|
|
|
|
MDFNMP_ApplyPeriodicCheats();
|
|
|
|
if(espec->VideoFormatChanged)
|
|
KING_SetPixelFormat(espec->surface->format); //.Rshift, espec->surface->format.Gshift, espec->surface->format.Bshift);
|
|
|
|
if(espec->SoundFormatChanged)
|
|
SoundBox_SetSoundRate(espec->SoundRate);
|
|
|
|
|
|
KING_StartFrame(fx_vdc_chips, espec); //espec->surface, &espec->DisplayRect, espec->LineWidths, espec->skip);
|
|
|
|
v810_timestamp = PCFX_V810.Run(pcfx_event_handler);
|
|
|
|
|
|
PCFX_FixNonEvents();
|
|
|
|
// Call before resetting v810_timestamp to 0
|
|
ForceEventUpdates(v810_timestamp);
|
|
|
|
espec->SoundBufSize = SoundBox_Flush(v810_timestamp, espec->SoundBuf, espec->SoundBufMaxSize);
|
|
|
|
KING_EndFrame(v810_timestamp);
|
|
FXTIMER_ResetTS();
|
|
FXINPUT_ResetTS();
|
|
SoundBox_ResetTS();
|
|
|
|
// Call this AFTER all the EndFrame/Flush/ResetTS stuff
|
|
RebaseTS(v810_timestamp);
|
|
|
|
espec->MasterCycles = v810_timestamp;
|
|
|
|
pcfx_timestamp_base += v810_timestamp;
|
|
|
|
PCFX_V810.ResetTS();
|
|
}
|
|
|
|
static void PCFX_Reset(void)
|
|
{
|
|
PCFX_Event_Reset();
|
|
|
|
RAM_LPA = 0;
|
|
|
|
ExBusReset = 0;
|
|
BackupControl = 0;
|
|
|
|
Last_VDC_AR[0] = 0;
|
|
Last_VDC_AR[1] = 0;
|
|
|
|
memset(RAM, 0x00, 2048 * 1024);
|
|
|
|
for(int i = 0; i < 2; i++)
|
|
{
|
|
int32 dummy_ne;
|
|
|
|
dummy_ne = fx_vdc_chips[i]->Reset();
|
|
}
|
|
|
|
KING_Reset(); // SCSICD_Power() is called from KING_Reset()
|
|
SoundBox_Reset();
|
|
RAINBOW_Reset();
|
|
|
|
if(WantHuC6273)
|
|
HuC6273_Reset();
|
|
|
|
PCFXIRQ_Reset();
|
|
FXTIMER_Reset();
|
|
PCFX_V810.Reset();
|
|
pcfx_timestamp_base = 0;
|
|
|
|
ForceEventUpdates(0);
|
|
}
|
|
|
|
static void PCFX_Power(void)
|
|
{
|
|
PCFX_Reset();
|
|
}
|
|
|
|
#ifdef WANT_DEBUGGER
|
|
|
|
static uint8 GAS_SectorCache[2048];
|
|
static int32 GAS_SectorCacheWhich = -1; // disc num is |'d in after << 24
|
|
|
|
static void PCFXDBG_GetAddressSpaceBytes(const char *name, uint32 Address, uint32 Length, uint8 *Buffer)
|
|
{
|
|
int32 ws = 0;
|
|
|
|
if(!strcmp(name, "cpu"))
|
|
{
|
|
while(Length--)
|
|
{
|
|
Address &= 0xFFFFFFFF;
|
|
*Buffer = mem_rbyte(ws, Address);
|
|
Address++;
|
|
Buffer++;
|
|
}
|
|
}
|
|
else if(!strcmp(name, "ram"))
|
|
{
|
|
while(Length--)
|
|
{
|
|
Address &= 2048 * 1024 - 1;
|
|
*Buffer = RAM[Address];
|
|
Address++;
|
|
Buffer++;
|
|
}
|
|
}
|
|
else if(!strcmp(name, "backup"))
|
|
{
|
|
while(Length--)
|
|
{
|
|
Address &= 0x7FFF;
|
|
*Buffer = BackupRAM[Address];
|
|
Address++;
|
|
Buffer++;
|
|
}
|
|
}
|
|
else if(!strcmp(name, "exbackup"))
|
|
{
|
|
while(Length--)
|
|
{
|
|
Address &= 0x7FFF;
|
|
*Buffer = ExBackupRAM[Address];
|
|
Address++;
|
|
Buffer++;
|
|
}
|
|
}
|
|
else if(!strcmp(name, "bios"))
|
|
{
|
|
while(Length--)
|
|
{
|
|
Address &= 1024 * 1024 - 1;
|
|
*Buffer = BIOSROM[Address];
|
|
Address++;
|
|
Buffer++;
|
|
}
|
|
}
|
|
else if(!strncmp(name, "track", strlen("track")))
|
|
{
|
|
int disc = 0, track = 0, sector_base = 0;
|
|
|
|
trio_sscanf(name, "track%d-%d-%d", &disc, &track, §or_base);
|
|
|
|
while(Length--)
|
|
{
|
|
int32 sector = (Address / 2048) + sector_base;
|
|
int32 sector_offset = Address % 2048;
|
|
|
|
if((sector | (disc << 24)) != GAS_SectorCacheWhich)
|
|
{
|
|
if(!(*cdifs)[disc]->ReadSector(GAS_SectorCache, sector, 1))
|
|
memset(GAS_SectorCache, 0, 2048);
|
|
|
|
GAS_SectorCacheWhich = sector | (disc << 24);
|
|
}
|
|
|
|
*Buffer = GAS_SectorCache[sector_offset];
|
|
|
|
Address++;
|
|
Buffer++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void PCFXDBG_PutAddressSpaceBytes(const char *name, uint32 Address, uint32 Length, uint32 Granularity, bool hl, const uint8 *Buffer)
|
|
{
|
|
if(!strcmp(name, "cpu"))
|
|
{
|
|
while(Length--)
|
|
{
|
|
Address &= 0xFFFFFFFF;
|
|
if(Address >= 0xFFF00000 && Address <= 0xFFFFFFFF)
|
|
{
|
|
BIOSROM[Address & 0xFFFFF] = *Buffer;
|
|
}
|
|
else if(Address <= 0x1FFFFF)
|
|
{
|
|
RAM[Address & 0x1FFFFF] = *Buffer;
|
|
}
|
|
else if(Address >= 0xE0000000 && Address <= 0xE7FFFFFF)
|
|
{
|
|
if(!(Address & 1))
|
|
BackupRAM[(Address & 0xFFFF) >> 1] = *Buffer;
|
|
}
|
|
else if(Address >= 0xE8000000 && Address <= 0xE9FFFFFF)
|
|
{
|
|
if(!(Address & 1))
|
|
ExBackupRAM[(Address & 0xFFFF) >> 1] = *Buffer;
|
|
}
|
|
Address++;
|
|
Buffer++;
|
|
}
|
|
}
|
|
else if(!strcmp(name, "ram"))
|
|
{
|
|
while(Length--)
|
|
{
|
|
Address &= 2048 * 1024 - 1;
|
|
RAM[Address] = *Buffer;
|
|
Address++;
|
|
Buffer++;
|
|
}
|
|
}
|
|
else if(!strcmp(name, "backup"))
|
|
{
|
|
while(Length--)
|
|
{
|
|
Address &= 0x7FFF;
|
|
BackupRAM[Address] = *Buffer;
|
|
Address++;
|
|
Buffer++;
|
|
}
|
|
}
|
|
else if(!strcmp(name, "exbackup"))
|
|
{
|
|
while(Length--)
|
|
{
|
|
Address &= 0x7FFF;
|
|
ExBackupRAM[Address] = *Buffer;
|
|
Address++;
|
|
Buffer++;
|
|
}
|
|
}
|
|
|
|
else if(!strcmp(name, "bios"))
|
|
{
|
|
while(Length--)
|
|
{
|
|
Address &= 1024 * 1024 - 1;
|
|
BIOSROM[Address] = *Buffer;
|
|
Address++;
|
|
Buffer++;
|
|
}
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
static void VDCA_IRQHook(bool asserted)
|
|
{
|
|
PCFXIRQ_Assert(PCFXIRQ_SOURCE_VDCA, asserted);
|
|
}
|
|
|
|
static void VDCB_IRQHook(bool asserted)
|
|
{
|
|
PCFXIRQ_Assert(PCFXIRQ_SOURCE_VDCB, asserted);
|
|
}
|
|
|
|
static void SetRegGroups(void);
|
|
|
|
static bool LoadCommon(std::vector<CDIF *> *CDInterfaces)
|
|
{
|
|
std::string biospath = MDFN_MakeFName(MDFNMKF_FIRMWARE, 0, MDFN_GetSettingS("pcfx.bios").c_str());
|
|
std::string fxscsi_path = MDFN_GetSettingS("pcfx.fxscsi"); // For developers only, so don't make it convenient.
|
|
MDFNFILE BIOSFile;
|
|
V810_Emu_Mode cpu_mode;
|
|
|
|
#ifdef WANT_DEBUGGER
|
|
SetRegGroups();
|
|
#endif
|
|
|
|
if(!BIOSFile.Open(biospath, NULL, "BIOS"))
|
|
return(0);
|
|
|
|
cpu_mode = (V810_Emu_Mode)MDFN_GetSettingI("pcfx.cpu_emulation");
|
|
if(cpu_mode == _V810_EMU_MODE_COUNT)
|
|
{
|
|
cpu_mode = (EmuFlags & CDGE_FLAG_ACCURATE_V810) ? V810_EMU_MODE_ACCURATE : V810_EMU_MODE_FAST;
|
|
}
|
|
|
|
if(EmuFlags & CDGE_FLAG_FXGA)
|
|
{
|
|
//WantHuC6273 = TRUE;
|
|
}
|
|
|
|
MDFN_printf(_("V810 Emulation Mode: %s\n"), (cpu_mode == V810_EMU_MODE_ACCURATE) ? _("Accurate") : _("Fast"));
|
|
PCFX_V810.Init(cpu_mode, false);
|
|
|
|
uint32 RAM_Map_Addresses[1] = { 0x00000000 };
|
|
uint32 BIOSROM_Map_Addresses[1] = { 0xFFF00000 };
|
|
|
|
// todo: cleanup on error
|
|
if(!(RAM = PCFX_V810.SetFastMap(RAM_Map_Addresses, 0x00200000, 1, _("RAM"))))
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
if(!(BIOSROM = PCFX_V810.SetFastMap(BIOSROM_Map_Addresses, 0x00100000, 1, _("BIOS ROM"))))
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
if(BIOSFile.f_size != 1024 * 1024)
|
|
{
|
|
MDFN_PrintError(_("BIOS ROM file is incorrect size.\n"));
|
|
return(0);
|
|
}
|
|
|
|
memcpy(BIOSROM, BIOSFile.f_data, 1024 * 1024);
|
|
|
|
BIOSFile.Close();
|
|
|
|
if(fxscsi_path != "0" && fxscsi_path != "" && fxscsi_path != "none")
|
|
{
|
|
MDFNFILE FXSCSIFile;
|
|
|
|
if(!FXSCSIFile.Open(fxscsi_path, NULL, "FX-SCSI ROM"))
|
|
return(0);
|
|
|
|
if(FXSCSIFile.f_size != 1024 * 512)
|
|
{
|
|
MDFN_PrintError(_("BIOS ROM file is incorrect size.\n"));
|
|
return(0);
|
|
}
|
|
|
|
uint32 FXSCSI_Map_Addresses[1] = { 0x80780000 };
|
|
|
|
if(!(FXSCSIROM = PCFX_V810.SetFastMap(FXSCSI_Map_Addresses, 0x0080000, 1, _("FX-SCSI ROM"))))
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
memcpy(FXSCSIROM, FXSCSIFile.f_data, 1024 * 512);
|
|
|
|
FXSCSIFile.Close();
|
|
}
|
|
|
|
#ifdef WANT_DEBUGGER
|
|
ASpace_Add(PCFXDBG_GetAddressSpaceBytes, PCFXDBG_PutAddressSpaceBytes, "cpu", "CPU Physical", 32);
|
|
ASpace_Add(PCFXDBG_GetAddressSpaceBytes, PCFXDBG_PutAddressSpaceBytes, "ram", "RAM", 21);
|
|
ASpace_Add(PCFXDBG_GetAddressSpaceBytes, PCFXDBG_PutAddressSpaceBytes, "backup", "Internal Backup Memory", 15);
|
|
ASpace_Add(PCFXDBG_GetAddressSpaceBytes, PCFXDBG_PutAddressSpaceBytes, "exbackup", "External Backup Memory", 15);
|
|
ASpace_Add(PCFXDBG_GetAddressSpaceBytes, PCFXDBG_PutAddressSpaceBytes, "bios", "BIOS ROM", 20);
|
|
#endif
|
|
|
|
for(int i = 0; i < 2; i++)
|
|
{
|
|
fx_vdc_chips[i] = new VDC(MDFN_GetSettingB("pcfx.nospritelimit"), 65536);
|
|
fx_vdc_chips[i]->SetWSHook(NULL);
|
|
fx_vdc_chips[i]->SetIRQHook(i ? VDCB_IRQHook : VDCA_IRQHook);
|
|
|
|
//fx_vdc_chips[0] = FXVDC_Init(PCFXIRQ_SOURCE_VDCA, MDFN_GetSettingB("pcfx.nospritelimit"));
|
|
//fx_vdc_chips[1] = FXVDC_Init(PCFXIRQ_SOURCE_VDCB, MDFN_GetSettingB("pcfx.nospritelimit"));
|
|
}
|
|
|
|
SoundBox_Init(MDFN_GetSettingB("pcfx.adpcm.emulate_buggy_codec"), MDFN_GetSettingB("pcfx.adpcm.suppress_channel_reset_clicks"));
|
|
RAINBOW_Init(MDFN_GetSettingB("pcfx.rainbow.chromaip"));
|
|
FXINPUT_Init();
|
|
|
|
if(WantHuC6273)
|
|
HuC6273_Init();
|
|
|
|
if(!KING_Init())
|
|
{
|
|
MDFN_free(BIOSROM);
|
|
MDFN_free(RAM);
|
|
BIOSROM = NULL;
|
|
RAM = NULL;
|
|
return(0);
|
|
}
|
|
|
|
CD_TrayOpen = false;
|
|
CD_SelectedDisc = 0;
|
|
|
|
SCSICD_SetDisc(true, NULL, true);
|
|
SCSICD_SetDisc(false, (*CDInterfaces)[0], true);
|
|
|
|
|
|
#ifdef WANT_DEBUGGER
|
|
for(unsigned disc = 0; disc < CDInterfaces->size(); disc++)
|
|
{
|
|
CDUtility::TOC toc;
|
|
|
|
(*CDInterfaces)[disc]->ReadTOC(&toc);
|
|
|
|
for(int32 track = toc.first_track; track <= toc.last_track; track++)
|
|
{
|
|
if(toc.tracks[track].control & 0x4)
|
|
{
|
|
char tmpn[256], tmpln[256];
|
|
uint32 sectors;
|
|
|
|
trio_snprintf(tmpn, 256, "track%d-%d-%d", disc, track, toc.tracks[track].lba);
|
|
trio_snprintf(tmpln, 256, "CD - Disc %d/%d - Track %d/%d", disc + 1, CDInterfaces->size(), track, toc.last_track - toc.first_track + 1);
|
|
|
|
sectors = toc.tracks[track + 1].lba - toc.tracks[track].lba;
|
|
ASpace_Add(PCFXDBG_GetAddressSpaceBytes, PCFXDBG_PutAddressSpaceBytes, tmpn, tmpln, 0, sectors * 2048);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
MDFNGameInfo->fps = (uint32)((double)7159090.90909090 / 455 / 263 * 65536 * 256);
|
|
|
|
MDFNGameInfo->nominal_height = MDFN_GetSettingUI("pcfx.slend") - MDFN_GetSettingUI("pcfx.slstart") + 1;
|
|
|
|
// Emulation raw framebuffer image should always be of 256 width when the pcfx.high_dotclock_width setting is set to "256",
|
|
// but it could be either 256 or 341 when the setting is set to "341", so stay with 1024 in that case so we won't have
|
|
// a messed up aspect ratio in our recorded QuickTime movies.
|
|
MDFNGameInfo->lcm_width = (MDFN_GetSettingUI("pcfx.high_dotclock_width") == 256) ? 256 : 1024;
|
|
MDFNGameInfo->lcm_height = MDFNGameInfo->nominal_height;
|
|
|
|
MDFNMP_Init(1024 * 1024, ((uint64)1 << 32) / (1024 * 1024));
|
|
MDFNMP_AddRAM(2048 * 1024, 0x00000000, RAM);
|
|
|
|
|
|
BRAMDisabled = MDFN_GetSettingB("pcfx.disable_bram");
|
|
|
|
if(BRAMDisabled)
|
|
MDFN_printf(_("Warning: BRAM is disabled per pcfx.disable_bram setting. This is simulating a malfunction.\n"));
|
|
|
|
if(!BRAMDisabled)
|
|
{
|
|
// Initialize backup RAM
|
|
memset(BackupRAM, 0, sizeof(BackupRAM));
|
|
memset(ExBackupRAM, 0, sizeof(ExBackupRAM));
|
|
|
|
static const uint8 BRInit00[] = { 0x24, 0x8A, 0xDF, 0x50, 0x43, 0x46, 0x58, 0x53, 0x72, 0x61, 0x6D, 0x80,
|
|
0x00, 0x01, 0x01, 0x00, 0x01, 0x40, 0x00, 0x00, 0x01, 0xF9, 0x03, 0x00,
|
|
0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
static const uint8 BRInit80[] = { 0xF9, 0xFF, 0xFF };
|
|
|
|
memcpy(BackupRAM + 0x00, BRInit00, sizeof(BRInit00));
|
|
memcpy(BackupRAM + 0x80, BRInit80, sizeof(BRInit80));
|
|
|
|
|
|
static const uint8 ExBRInit00[] = { 0x24, 0x8A, 0xDF, 0x50, 0x43, 0x46, 0x58, 0x43, 0x61, 0x72, 0x64, 0x80,
|
|
0x00, 0x01, 0x01, 0x00, 0x01, 0x40, 0x00, 0x00, 0x01, 0xF9, 0x03, 0x00,
|
|
0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
static const uint8 ExBRInit80[] = { 0xF9, 0xFF, 0xFF };
|
|
|
|
memcpy(ExBackupRAM + 0x00, ExBRInit00, sizeof(ExBRInit00));
|
|
memcpy(ExBackupRAM + 0x80, ExBRInit80, sizeof(ExBRInit80));
|
|
|
|
FILE *savefp;
|
|
if((savefp = fopen(MDFN_MakeFName(MDFNMKF_SAV, 0, "sav").c_str(), "rb")))
|
|
{
|
|
fread(BackupRAM, 0x8000, 1, savefp);
|
|
fread(ExBackupRAM, 0x8000, 1, savefp);
|
|
fclose(savefp);
|
|
}
|
|
}
|
|
|
|
// Default to 16-bit bus.
|
|
for(int i = 0; i < 256; i++)
|
|
{
|
|
PCFX_V810.SetMemReadBus32(i, FALSE);
|
|
PCFX_V810.SetMemWriteBus32(i, FALSE);
|
|
}
|
|
|
|
// 16MiB RAM area.
|
|
PCFX_V810.SetMemReadBus32(0, TRUE);
|
|
PCFX_V810.SetMemWriteBus32(0, TRUE);
|
|
|
|
// Bitstring read range
|
|
for(int i = 0xA0; i <= 0xAF; i++)
|
|
{
|
|
PCFX_V810.SetMemReadBus32(i, FALSE); // Reads to the read range are 16-bit, and
|
|
PCFX_V810.SetMemWriteBus32(i, TRUE); // writes are 32-bit.
|
|
}
|
|
|
|
// Bitstring write range
|
|
for(int i = 0xB0; i <= 0xBF; i++)
|
|
{
|
|
PCFX_V810.SetMemReadBus32(i, TRUE); // Reads to the write range are 32-bit,
|
|
PCFX_V810.SetMemWriteBus32(i, FALSE); // but writes are 16-bit!
|
|
}
|
|
|
|
// BIOS area
|
|
for(int i = 0xF0; i <= 0xFF; i++)
|
|
{
|
|
PCFX_V810.SetMemReadBus32(i, FALSE);
|
|
PCFX_V810.SetMemWriteBus32(i, FALSE);
|
|
}
|
|
|
|
PCFX_V810.SetMemReadHandlers(mem_rbyte, mem_rhword, mem_rword);
|
|
PCFX_V810.SetMemWriteHandlers(mem_wbyte, mem_whword, mem_wword);
|
|
|
|
PCFX_V810.SetIOReadHandlers(port_rbyte, port_rhword, NULL);
|
|
PCFX_V810.SetIOWriteHandlers(port_wbyte, port_whword, NULL);
|
|
|
|
|
|
|
|
return(1);
|
|
}
|
|
|
|
static void DoMD5CDVoodoo(std::vector<CDIF *> *CDInterfaces)
|
|
{
|
|
const CDGameEntry *found_entry = NULL;
|
|
CDUtility::TOC toc;
|
|
|
|
#if 0
|
|
puts("{");
|
|
puts(" ,");
|
|
puts(" ,");
|
|
puts(" 0,");
|
|
puts(" 1,");
|
|
puts(" {");
|
|
puts(" {");
|
|
|
|
for(int i = CDIF_GetFirstTrack(); i <= CDIF_GetLastTrack(); i++)
|
|
{
|
|
CDIF_Track_Format tf;
|
|
|
|
CDIF_GetTrackFormat(i, tf);
|
|
|
|
printf(" { %d, %s, %d },\n", i, (tf == CDIF_FORMAT_AUDIO) ? "CDIF_FORMAT_AUDIO" : "CDIF_FORMAT_MODE1", CDIF_GetTrackStartPositionLBA(i));
|
|
}
|
|
printf(" { -1, (CDIF_Track_Format)-1, %d },\n", CDIF_GetSectorCountLBA());
|
|
puts(" }");
|
|
puts(" }");
|
|
puts("},");
|
|
//exit(1);
|
|
#endif
|
|
|
|
for(unsigned if_disc = 0; if_disc < CDInterfaces->size(); if_disc++)
|
|
{
|
|
(*CDInterfaces)[if_disc]->ReadTOC(&toc);
|
|
|
|
if(toc.first_track == 1)
|
|
{
|
|
for(unsigned int g = 0; g < sizeof(GameList) / sizeof(CDGameEntry); g++)
|
|
{
|
|
const CDGameEntry *entry = &GameList[g];
|
|
|
|
assert(entry->discs == 1 || entry->discs == 2);
|
|
|
|
for(unsigned int disc = 0; disc < entry->discs; disc++)
|
|
{
|
|
const CDGameEntryTrack *et = entry->tracks[disc];
|
|
bool GameFound = TRUE;
|
|
|
|
while(et->tracknum != -1 && GameFound)
|
|
{
|
|
assert(et->tracknum > 0 && et->tracknum < 100);
|
|
|
|
if(toc.tracks[et->tracknum].lba != et->lba)
|
|
GameFound = FALSE;
|
|
|
|
if( ((et->format == CDGE_FORMAT_DATA) ? 0x4 : 0x0) != (toc.tracks[et->tracknum].control & 0x4))
|
|
GameFound = FALSE;
|
|
|
|
et++;
|
|
}
|
|
|
|
if(et->tracknum == -1)
|
|
{
|
|
if((et - 1)->tracknum != toc.last_track)
|
|
GameFound = FALSE;
|
|
|
|
if(et->lba != toc.tracks[100].lba)
|
|
GameFound = FALSE;
|
|
}
|
|
|
|
if(GameFound)
|
|
{
|
|
found_entry = entry;
|
|
goto FoundIt;
|
|
}
|
|
} // End disc count loop
|
|
}
|
|
}
|
|
|
|
FoundIt: ;
|
|
|
|
if(found_entry)
|
|
{
|
|
EmuFlags = found_entry->flags;
|
|
|
|
if(found_entry->discs > 1)
|
|
{
|
|
const char *hash_prefix = "Mednafen PC-FX Multi-Game Set";
|
|
md5_context md5_gameset;
|
|
|
|
md5_gameset.starts();
|
|
|
|
md5_gameset.update_string(hash_prefix);
|
|
|
|
for(unsigned int disc = 0; disc < found_entry->discs; disc++)
|
|
{
|
|
const CDGameEntryTrack *et = found_entry->tracks[disc];
|
|
|
|
while(et->tracknum)
|
|
{
|
|
md5_gameset.update_u32_as_lsb(et->tracknum);
|
|
md5_gameset.update_u32_as_lsb((uint32)et->format);
|
|
md5_gameset.update_u32_as_lsb(et->lba);
|
|
|
|
if(et->tracknum == -1)
|
|
break;
|
|
et++;
|
|
}
|
|
}
|
|
md5_gameset.finish(MDFNGameInfo->GameSetMD5);
|
|
MDFNGameInfo->GameSetMD5Valid = TRUE;
|
|
}
|
|
//printf("%s\n", found_entry->name);
|
|
MDFNGameInfo->name = (UTF8*)strdup(found_entry->name);
|
|
break;
|
|
}
|
|
} // end: for(unsigned if_disc = 0; if_disc < CDInterfaces->size(); if_disc++)
|
|
|
|
MDFN_printf(_("CD Layout MD5: 0x%s\n"), md5_context::asciistr(MDFNGameInfo->MD5, 0).c_str());
|
|
|
|
if(MDFNGameInfo->GameSetMD5Valid)
|
|
MDFN_printf(_("GameSet MD5: 0x%s\n"), md5_context::asciistr(MDFNGameInfo->GameSetMD5, 0).c_str());
|
|
}
|
|
|
|
// PC-FX BIOS will look at all data tracks(not just the first one), in contrast to the PCE CD BIOS, which only looks
|
|
// at the first data track.
|
|
static bool TestMagicCD(std::vector<CDIF *> *CDInterfaces)
|
|
{
|
|
CDIF *cdiface = (*CDInterfaces)[0];
|
|
CDUtility::TOC toc;
|
|
uint8 sector_buffer[2048];
|
|
|
|
memset(sector_buffer, 0, sizeof(sector_buffer));
|
|
|
|
cdiface->ReadTOC(&toc);
|
|
|
|
for(int32 track = toc.first_track; track <= toc.last_track; track++)
|
|
{
|
|
if(toc.tracks[track].control & 0x4)
|
|
{
|
|
cdiface->ReadSector(sector_buffer, toc.tracks[track].lba, 1);
|
|
if(!strncmp("PC-FX:Hu_CD-ROM", (char*)sector_buffer, strlen("PC-FX:Hu_CD-ROM")))
|
|
{
|
|
return(TRUE);
|
|
}
|
|
|
|
if(!strncmp((char *)sector_buffer + 64, "PPPPHHHHOOOOTTTTOOOO____CCCCDDDD", 32))
|
|
return(true);
|
|
}
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
static int LoadCD(std::vector<CDIF *> *CDInterfaces)
|
|
{
|
|
EmuFlags = 0;
|
|
|
|
cdifs = CDInterfaces;
|
|
|
|
DoMD5CDVoodoo(CDInterfaces);
|
|
|
|
if(!LoadCommon(CDInterfaces))
|
|
return(0);
|
|
|
|
MDFN_printf(_("Emulated CD-ROM drive speed: %ux\n"), (unsigned int)MDFN_GetSettingUI("pcfx.cdspeed"));
|
|
|
|
MDFNGameInfo->GameType = GMT_CDROM;
|
|
|
|
PCFX_Power();
|
|
|
|
return(1);
|
|
}
|
|
|
|
static void PCFX_CDInsertEject(void)
|
|
{
|
|
CD_TrayOpen = !CD_TrayOpen;
|
|
|
|
for(unsigned disc = 0; disc < cdifs->size(); disc++)
|
|
{
|
|
if(!(*cdifs)[disc]->Eject(CD_TrayOpen))
|
|
{
|
|
MDFN_DispMessage(_("Eject error."));
|
|
CD_TrayOpen = !CD_TrayOpen;
|
|
}
|
|
}
|
|
|
|
if(CD_TrayOpen)
|
|
MDFN_DispMessage(_("Virtual CD Drive Tray Open"));
|
|
else
|
|
MDFN_DispMessage(_("Virtual CD Drive Tray Closed"));
|
|
|
|
SCSICD_SetDisc(CD_TrayOpen, (CD_SelectedDisc >= 0 && !CD_TrayOpen) ? (*cdifs)[CD_SelectedDisc] : NULL);
|
|
}
|
|
|
|
static void PCFX_CDEject(void)
|
|
{
|
|
if(!CD_TrayOpen)
|
|
PCFX_CDInsertEject();
|
|
}
|
|
|
|
static void PCFX_CDSelect(void)
|
|
{
|
|
if(cdifs && CD_TrayOpen)
|
|
{
|
|
CD_SelectedDisc = (CD_SelectedDisc + 1) % (cdifs->size() + 1);
|
|
|
|
if((unsigned)CD_SelectedDisc == cdifs->size())
|
|
CD_SelectedDisc = -1;
|
|
|
|
if(CD_SelectedDisc == -1)
|
|
MDFN_DispMessage(_("Disc absence selected."));
|
|
else
|
|
MDFN_DispMessage(_("Disc %d of %d selected."), CD_SelectedDisc + 1, (int)cdifs->size());
|
|
}
|
|
}
|
|
|
|
static void CloseGame(void)
|
|
{
|
|
if(!BRAMDisabled)
|
|
{
|
|
std::vector<PtrLengthPair> EvilRams;
|
|
|
|
EvilRams.push_back(PtrLengthPair(BackupRAM, 0x8000));
|
|
EvilRams.push_back(PtrLengthPair(ExBackupRAM, 0x8000));
|
|
|
|
MDFN_DumpToFile(MDFN_MakeFName(MDFNMKF_SAV, 0, "sav").c_str(), 6, EvilRams);
|
|
}
|
|
|
|
for(int i = 0; i < 2; i++)
|
|
if(fx_vdc_chips[i])
|
|
{
|
|
delete fx_vdc_chips[i];
|
|
fx_vdc_chips[i] = NULL;
|
|
}
|
|
|
|
RAINBOW_Close();
|
|
KING_Close();
|
|
PCFX_V810.Kill();
|
|
|
|
// The allocated memory RAM and BIOSROM is free'd in V810_Kill()
|
|
RAM = NULL;
|
|
BIOSROM = NULL;
|
|
}
|
|
|
|
static void DoSimpleCommand(int cmd)
|
|
{
|
|
switch(cmd)
|
|
{
|
|
case MDFN_MSC_INSERT_DISK:
|
|
PCFX_CDInsertEject();
|
|
break;
|
|
|
|
case MDFN_MSC_SELECT_DISK:
|
|
PCFX_CDSelect();
|
|
break;
|
|
|
|
case MDFN_MSC_EJECT_DISK:
|
|
PCFX_CDEject();
|
|
break;
|
|
|
|
case MDFN_MSC_RESET: PCFX_Reset(); break;
|
|
case MDFN_MSC_POWER: PCFX_Power(); break;
|
|
}
|
|
}
|
|
|
|
static int StateAction(StateMem *sm, int load, int data_only)
|
|
{
|
|
SFORMAT StateRegs[] =
|
|
{
|
|
SFARRAY(RAM, 0x200000),
|
|
SFARRAY16(Last_VDC_AR, 2),
|
|
SFVAR(BackupControl),
|
|
SFVAR(ExBusReset),
|
|
SFARRAY(BackupRAM, BRAMDisabled ? 0 : 0x8000),
|
|
SFARRAY(ExBackupRAM, BRAMDisabled ? 0 : 0x8000),
|
|
|
|
SFVAR(pcfx_timestamp_base),
|
|
|
|
SFVAR(next_pad_ts),
|
|
SFVAR(next_timer_ts),
|
|
SFVAR(next_adpcm_ts),
|
|
SFVAR(next_king_ts),
|
|
|
|
SFVAR(CD_TrayOpen),
|
|
SFVAR(CD_SelectedDisc),
|
|
|
|
SFEND
|
|
};
|
|
|
|
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "MAIN");
|
|
|
|
for(int i = 0; i < 2; i++)
|
|
ret &= fx_vdc_chips[i]->StateAction(sm, load, data_only, i ? "VDC1" : "VDC0");
|
|
|
|
ret &= FXINPUT_StateAction(sm, load, data_only);
|
|
ret &= PCFXIRQ_StateAction(sm, load, data_only);
|
|
ret &= KING_StateAction(sm, load, data_only);
|
|
ret &= PCFX_V810.StateAction(sm, load, data_only);
|
|
ret &= FXTIMER_StateAction(sm, load, data_only);
|
|
ret &= SoundBox_StateAction(sm, load, data_only);
|
|
ret &= SCSICD_StateAction(sm, load, data_only, "CDRM");
|
|
ret &= RAINBOW_StateAction(sm, load, data_only);
|
|
|
|
if(load)
|
|
{
|
|
//clamp(&PCFX_V810.v810_timestamp, 0, 30 * 1000 * 1000);
|
|
//PCFX_SetEvent(PCFX_EVENT_SCSI, SCSICD_Run(v810_timestamp)); // hmm, fixme?
|
|
|
|
if(cdifs)
|
|
{
|
|
// Sanity check.
|
|
if(CD_SelectedDisc >= (int)cdifs->size())
|
|
CD_SelectedDisc = (int)cdifs->size() - 1;
|
|
|
|
SCSICD_SetDisc(CD_TrayOpen, (CD_SelectedDisc >= 0 && !CD_TrayOpen) ? (*cdifs)[CD_SelectedDisc] : NULL, true);
|
|
}
|
|
}
|
|
|
|
return(ret);
|
|
}
|
|
|
|
static const MDFNSetting_EnumList V810Mode_List[] =
|
|
{
|
|
{ "fast", (int)V810_EMU_MODE_FAST, gettext_noop("Fast Mode"), gettext_noop("Fast mode trades timing accuracy, cache emulation, and executing from hardware registers and RAM not intended for code use for performance.")},
|
|
{ "accurate", (int)V810_EMU_MODE_ACCURATE, gettext_noop("Accurate Mode"), gettext_noop("Increased timing accuracy, though not perfect, along with cache emulation, at the cost of decreased performance. Additionally, even the pipeline isn't correctly and fully emulated in this mode.") },
|
|
{ "auto", (int)_V810_EMU_MODE_COUNT, gettext_noop("Auto Mode"), gettext_noop("Selects \"fast\" or \"accurate\" automatically based on an internal database. If the CD image is not recognized, defaults to \"fast\".") },
|
|
{ NULL, 0 },
|
|
};
|
|
|
|
|
|
static const MDFNSetting_EnumList HDCWidthList[] =
|
|
{
|
|
{ "256", 256, "256 pixels", gettext_noop("This value will cause heavy pixel distortion.") },
|
|
{ "341", 341, "341 pixels", gettext_noop("This value will cause moderate pixel distortion.") },
|
|
{ "1024", 1024, "1024 pixels", gettext_noop("This value will cause no pixel distortion as long as interpolation is enabled on the video output device and the resolution is sufficiently high, but it will use a lot of CPU time.") },
|
|
{ NULL, 0 },
|
|
};
|
|
|
|
static MDFNSetting PCFXSettings[] =
|
|
{
|
|
/*
|
|
{ "pcfx.input.port1", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Input device for input port 1."), NULL, MDFNST_STRING, "gamepad", NULL, NULL },
|
|
{ "pcfx.input.port2", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Input device for input port 2."), NULL, MDFNST_STRING, "gamepad", NULL, NULL },
|
|
{ "pcfx.input.port3", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Input device for input port 3."), NULL, MDFNST_STRING, "gamepad", NULL, NULL },
|
|
{ "pcfx.input.port4", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Input device for input port 4."), NULL, MDFNST_STRING, "gamepad", NULL, NULL },
|
|
{ "pcfx.input.port5", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Input device for input port 5."), NULL, MDFNST_STRING, "gamepad", NULL, NULL },
|
|
{ "pcfx.input.port6", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Input device for input port 6."), NULL, MDFNST_STRING, "gamepad", NULL, NULL },
|
|
{ "pcfx.input.port7", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Input device for input port 7."), NULL, MDFNST_STRING, "gamepad", NULL, NULL },
|
|
{ "pcfx.input.port8", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Input device for input port 8."), NULL, MDFNST_STRING, "gamepad", NULL, NULL },
|
|
*/
|
|
|
|
{ "pcfx.input.port1.multitap", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Enable multitap on PC-FX port 1."), gettext_noop("EXPERIMENTAL emulation of the unreleased multitap. Enables ports 3 4 5."), MDFNST_BOOL, "0", NULL, NULL },
|
|
{ "pcfx.input.port2.multitap", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Enable multitap on PC-FX port 2."), gettext_noop("EXPERIMENTAL emulation of the unreleased multitap. Enables ports 6 7 8."), MDFNST_BOOL, "0", NULL, NULL },
|
|
|
|
|
|
{ "pcfx.mouse_sensitivity", MDFNSF_NOFLAGS, gettext_noop("Mouse sensitivity."), NULL, MDFNST_FLOAT, "1.25", NULL, NULL },
|
|
{ "pcfx.disable_softreset", MDFNSF_NOFLAGS, gettext_noop("When RUN+SEL are pressed simultaneously, disable both buttons temporarily."), NULL, MDFNST_BOOL, "0", NULL, NULL, NULL, FXINPUT_SettingChanged },
|
|
|
|
{ "pcfx.cpu_emulation", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("CPU emulation mode."), NULL, MDFNST_ENUM, "auto", NULL, NULL, NULL, NULL, V810Mode_List },
|
|
{ "pcfx.bios", MDFNSF_EMU_STATE, gettext_noop("Path to the ROM BIOS"), NULL, MDFNST_STRING, "pcfx.rom" },
|
|
{ "pcfx.fxscsi", MDFNSF_EMU_STATE, gettext_noop("Path to the FX-SCSI ROM"), gettext_noop("Intended for developers only."), MDFNST_STRING, "0" },
|
|
{ "pcfx.disable_bram", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Disable internal and external BRAM."), gettext_noop("It is intended for viewing games' error screens that may be different from simple BRAM full and uninitialized BRAM error screens, though it can cause the game to crash outright."), MDFNST_BOOL, "0" },
|
|
{ "pcfx.cdspeed", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Emulated CD-ROM speed."), gettext_noop("Setting the value higher than 2, the default, will decrease loading times in most games by some degree."), MDFNST_UINT, "2", "2", "10" },
|
|
|
|
{ "pcfx.nospritelimit", MDFNSF_NOFLAGS, gettext_noop("Remove 16-sprites-per-scanline hardware limit."), NULL, MDFNST_BOOL, "0" },
|
|
{ "pcfx.high_dotclock_width", MDFNSF_NOFLAGS, gettext_noop("Emulated width for 7.16MHz dot-clock mode."), gettext_noop("Lower values are faster, but will cause some degree of pixel distortion."), MDFNST_ENUM, "1024", NULL, NULL, NULL, NULL, HDCWidthList },
|
|
|
|
{ "pcfx.slstart", MDFNSF_NOFLAGS, gettext_noop("First rendered scanline."), NULL, MDFNST_UINT, "4", "0", "239" },
|
|
{ "pcfx.slend", MDFNSF_NOFLAGS, gettext_noop("Last rendered scanline."), NULL, MDFNST_UINT, "235", "0", "239" },
|
|
|
|
{ "pcfx.rainbow.chromaip", MDFNSF_NOFLAGS, gettext_noop("Enable bilinear interpolation on the chroma channel of RAINBOW YUV output."), NULL, MDFNST_BOOL, "1" },
|
|
|
|
{ "pcfx.adpcm.suppress_channel_reset_clicks", MDFNSF_NOFLAGS, gettext_noop("Hack to suppress clicks caused by forced channel resets."), NULL, MDFNST_BOOL, "1" },
|
|
|
|
// Hack that emulates the codec a buggy ADPCM encoder used for some games' ADPCM. Not enabled by default because it makes some games(with
|
|
//correctly-encoded ADPCM?) sound worse
|
|
{ "pcfx.adpcm.emulate_buggy_codec", MDFNSF_NOFLAGS, gettext_noop("Hack that emulates the codec a buggy ADPCM encoder used for some games' ADPCM."), NULL, MDFNST_BOOL, "0" },
|
|
|
|
{ NULL }
|
|
};
|
|
|
|
#ifdef WANT_DEBUGGER
|
|
static RegType PCFXRegs0[] =
|
|
{
|
|
{ 0, "PC", "Program Counter", 4 },
|
|
{ 0, "PR1", "Program Register 1", 4 },
|
|
{ 0, "HSP", "Program Register 2(Handler Stack Pointer)", 4 },
|
|
{ 0, "SP", "Program Register 3(Stack Pointer)", 4 },
|
|
{ 0, "GP", "Program Register 4(Global Pointer)", 4 },
|
|
{ 0, "TP", "Program Register 5(Text Pointer)", 4 },
|
|
{ 0, "PR6", "Program Register 6", 4 },
|
|
{ 0, "PR7", "Program Register 7", 4 },
|
|
{ 0, "PR8", "Program Register 8", 4 },
|
|
{ 0, "PR9", "Program Register 9", 4 },
|
|
{ 0, "PR10", "Program Register 10", 4 },
|
|
{ 0, "PR11", "Program Register 11", 4 },
|
|
{ 0, "PR12", "Program Register 12", 4 },
|
|
{ 0, "PR13", "Program Register 13", 4 },
|
|
{ 0, "PR14", "Program Register 14", 4 },
|
|
{ 0, "PR15", "Program Register 15", 4 },
|
|
{ 0, "PR16", "Program Register 16", 4 },
|
|
{ 0, "PR17", "Program Register 17", 4 },
|
|
{ 0, "PR18", "Program Register 18", 4 },
|
|
{ 0, "PR19", "Program Register 19", 4 },
|
|
{ 0, "PR20", "Program Register 20", 4 },
|
|
{ 0, "PR21", "Program Register 21", 4 },
|
|
{ 0, "PR22", "Program Register 22", 4 },
|
|
{ 0, "PR23", "Program Register 23", 4 },
|
|
{ 0, "PR24", "Program Register 24", 4 },
|
|
{ 0, "PR25", "Program Register 25", 4 },
|
|
{ 0, "PR26", "Program Register 26(String Dest Bit Offset)", 4 },
|
|
{ 0, "PR27", "Program Register 27(String Source Bit Offset)", 4 },
|
|
{ 0, "PR28", "Program Register 28(String Length)", 4 },
|
|
{ 0, "PR29", "Program Register 29(String Dest)", 4 },
|
|
{ 0, "PR30", "Program Register 30(String Source)", 4 },
|
|
{ 0, "LP", "Program Register 31(Link Pointer)", 4 },
|
|
{ 0, "SR0", "Exception/Interrupt PC", 4 },
|
|
{ 0, "SR1", "Exception/Interrupt PSW", 4 },
|
|
{ 0, "SR2", "Fatal Error PC", 4 },
|
|
{ 0, "SR3", "Fatal Error PSW", 4 },
|
|
{ 0, "SR4", "Exception Cause Register", 4 },
|
|
{ 0, "SR5", "Program Status Word", 4 },
|
|
{ 0, "SR6", "Processor ID Register", 4 },
|
|
{ 0, "SR7", "Task Control Word", 4 },
|
|
{ 0, "SR24", "Cache Control Word", 4 },
|
|
{ 0, "SR25", "Address Trap Register", 4 },
|
|
{ 0, "IPEND", "Interrupts Pending", 2 },
|
|
{ 0, "IMASK", "Interrupt Mask", 2 },
|
|
{ 0, "IPRIO0", "Interrupt Priority Register 0", 2 },
|
|
{ 0, "IPRIO1", "Interrupt Priority Register 1", 2 },
|
|
{ 0, "TCTRL", "Timer Control", 2 },
|
|
{ 0, "TPRD", "Timer Period", 2 },
|
|
{ 0, "TCNTR", "Timer Counter", 3 },
|
|
|
|
{ 0, "KPCTRL0", "Keyport 0 Control", 1 },
|
|
{ 0, "KPCTRL1", "Keyport 1 Control", 1 },
|
|
|
|
{ 0, "TStamp", "Timestamp", 3 },
|
|
{ 0, "", "", 0 },
|
|
};
|
|
|
|
static RegType PCFXRegs1[] =
|
|
{
|
|
{ 0, "AR", "Active Register", 1 },
|
|
{ 0, "MPROGADDR", "Micro-program Address", 2 },
|
|
{ 0, "MPROGCTRL", "Micro-program Control", 2 },
|
|
|
|
{ 0, "PAGESET", "KRAM Page Settings", 2 },
|
|
{ 0, "RTCTRL", "Rainbow Transfer Control", 2 },
|
|
{ 0, "RKRAMA", "Rainbow Transfer K-RAM Address", 3},
|
|
{ 0, "RSTART", "Rainbow Transfer Start Position", 2},
|
|
{ 0, "RCOUNT", "Rainbow Transfer Block Count", 2 },
|
|
{ 0, "RIRQLINE", "Raster IRQ Line", 2 },
|
|
{ 0, "KRAMWA", "K-RAM Write Address", 4 },
|
|
{ 0, "KRAMRA", "K-RAM Read Address", 4 },
|
|
{ 0, "DMATA", "DMA Transfer Address", 3 },
|
|
{ 0, "DMATS", "DMA Transfer Size", 4 },
|
|
{ 0, "DMASTT", "DMA Status", 2 },
|
|
{ 0, "ADPCMCTRL", "ADPCM Control", 2 },
|
|
{ 0, "ADPCMBM0", "ADPCM Buffer Mode Ch0", 2 },
|
|
{ 0, "ADPCMBM1", "ADPCM Buffer Mode Ch1", 2 },
|
|
{ 0, "ADPCMPA0", "ADPCM PlayAddress Ch0", 0x100 | 18 },
|
|
{ 0, "ADPCMPA1", "ADPCM PlayAddress Ch1", 0x100 | 18 },
|
|
{ 0, "ADPCMSA0", "ADPCM Start Address Ch0", 2 },
|
|
{ 0, "ADPCMSA1", "ADPCM Start Address Ch1", 2 },
|
|
{ 0, "ADPCMIA0", "ADPCM Intermediate Address Ch0", 2 },
|
|
{ 0, "ADPCMIA1", "ADPCM Intermediate Address Ch1", 2 },
|
|
{ 0, "ADPCMEA0", "ADPCM End Address Ch0", 0x100 | 18 },
|
|
{ 0, "ADPCMEA1", "ADPCM End Address Ch1", 0x100 | 18 },
|
|
{ 0, "ADPCMStat", "ADPCM Status Register", 1 },
|
|
{ 0, "Reg01", "KING Register 0x01", 1 },
|
|
{ 0, "Reg02", "KING Register 0x02", 1 },
|
|
{ 0, "Reg03", "KING Register 0x03", 1 },
|
|
{ 0, "SUBCC", "Sub-channel Control", 1 },
|
|
{ 0, "------", "", 0xFFFF },
|
|
{ 0, "DB", "SCSI Data Bus", 0x100 | 8 },
|
|
{ 0, "BSY", "SCSI BSY", 0x100 | 1 },
|
|
{ 0, "REQ", "SCSI REQ", 0x100 | 1 },
|
|
{ 0, "ACK", "SCSI ACK", 0x100 | 1 },
|
|
{ 0, "MSG", "SCSI MSG", 0x100 | 1 },
|
|
{ 0, "IO", "SCSI IO", 0x100 | 1 },
|
|
{ 0, "CD", "SCSI CD", 0x100 | 1 },
|
|
{ 0, "SEL", "SCSI SEL", 0x100 | 1 },
|
|
|
|
{ 0, "", "", 0 },
|
|
};
|
|
|
|
static RegType PCFXRegs4[] =
|
|
{
|
|
{ 0, "--VCE--", "", 0xFFFF },
|
|
{ 0, "Line", "VCE Frame Counter", 0x100 | 9 },
|
|
{ 0, "PRIO0", "VCE Priority 0", 0x100 | 12 },
|
|
{ 0, "PRIO1", "VCE Priority 1", 2 },
|
|
{ 0, "PICMODE", "VCE Picture Mode", 2},
|
|
{ 0, "PALRWOF", "VCE Palette R/W Offset", 2 },
|
|
{ 0, "PALRWLA", "VCE Palette R/W Latch", 2 },
|
|
{ 0, "PALOFS0", "VCE Palette Offset 0", 2 } ,
|
|
{ 0, "PALOFS1", "VCE Palette Offset 1", 2 },
|
|
{ 0, "PALOFS2", "VCE Palette Offset 2", 2 },
|
|
{ 0, "PALOFS3", "VCE Palette Offset 3", 1 },
|
|
{ 0, "CCR", "VCE Fixed Color Register", 2 },
|
|
{ 0, "BLE", "VCE Cellophane Setting Register", 2 },
|
|
{ 0, "SPBL", "VCE Sprite Cellophane Setting Register", 2 },
|
|
{ 0, "COEFF0", "VCE Cellophane Coefficient 0(1A)", 0x100 | 12 },
|
|
{ 0, "COEFF1", "VCE Cellophane Coefficient 1(1B)", 0x100 | 12 },
|
|
{ 0, "COEFF2", "VCE Cellophane Coefficient 2(2A)", 0x100 | 12 },
|
|
{ 0, "COEFF3", "VCE Cellophane Coefficient 3(2B)", 0x100 | 12 },
|
|
{ 0, "COEFF4", "VCE Cellophane Coefficient 4(3A)", 0x100 | 12 },
|
|
{ 0, "COEFF5", "VCE Cellophane Coefficient 5(3B)", 0x100 | 12 },
|
|
{ 0, "CKeyY", "VCE Chroma Key Y", 2 },
|
|
{ 0, "CKeyU", "VCE Chroma Key U", 2 },
|
|
{ 0, "CKeyV", "VCE Chroma Key V", 2 },
|
|
{ 0, "", "", 0 },
|
|
};
|
|
|
|
static uint32 GetRegister_VDC(const unsigned int id, char *special, const uint32 special_len)
|
|
{
|
|
return(fx_vdc_chips[(id >> 15) & 1]->GetRegister(id &~0x8000, special, special_len));
|
|
}
|
|
|
|
static void SetRegister_VDC(const unsigned int id, uint32 value)
|
|
{
|
|
fx_vdc_chips[(id >> 15) & 1]->SetRegister(id &~0x8000, value);
|
|
}
|
|
|
|
|
|
static RegType VDCRegs[] =
|
|
{
|
|
{ 0, "--VDC-A--", "", 0xFFFF },
|
|
|
|
{ VDC::GSREG_SELECT, "Select", "Register Select, VDC-A", 1 },
|
|
{ VDC::GSREG_STATUS, "Status", "Status, VDC-A", 1 },
|
|
|
|
{ VDC::GSREG_MAWR, "MAWR", "Memory Write Address, VDC-A", 2 },
|
|
{ VDC::GSREG_MARR, "MARR", "Memory Read Address, VDC-A", 2 },
|
|
{ VDC::GSREG_CR, "CR", "Control, VDC-A", 2 },
|
|
{ VDC::GSREG_RCR, "RCR", "Raster Counter, VDC-A", 2 },
|
|
{ VDC::GSREG_BXR, "BXR", "X Scroll, VDC-A", 2 },
|
|
{ VDC::GSREG_BYR, "BYR", "Y Scroll, VDC-A", 2 },
|
|
{ VDC::GSREG_MWR, "MWR", "Memory Width, VDC-A", 2 },
|
|
|
|
{ VDC::GSREG_HSR, "HSR", "HSR, VDC-A", 2 },
|
|
{ VDC::GSREG_HDR, "HDR", "HDR, VDC-A", 2 },
|
|
{ VDC::GSREG_VSR, "VSR", "VSR, VDC-A", 2 },
|
|
{ VDC::GSREG_VDR, "VDR", "VDR, VDC-A", 2 },
|
|
|
|
{ VDC::GSREG_VCR, "VCR", "VCR, VDC-A", 2 },
|
|
{ VDC::GSREG_DCR, "DCR", "DMA Control, VDC-A", 2 },
|
|
{ VDC::GSREG_SOUR, "SOUR", "VRAM DMA Source Address, VDC-A", 2 },
|
|
{ VDC::GSREG_DESR, "DESR", "VRAM DMA Dest Address, VDC-A", 2 },
|
|
{ VDC::GSREG_LENR, "LENR", "VRAM DMA Length, VDC-A", 2 },
|
|
{ VDC::GSREG_DVSSR, "DVSSR", "DVSSR Update Address, VDC-A", 2 },
|
|
{ 0, "------", "", 0xFFFF },
|
|
|
|
{ 0, "--VDC-B--", "", 0xFFFF },
|
|
|
|
{ 0x8000 | VDC::GSREG_SELECT, "Select", "Register Select, VDC-B", 1 },
|
|
{ 0x8000 | VDC::GSREG_STATUS, "Status", "Status, VDC-B", 1 },
|
|
|
|
{ 0x8000 | VDC::GSREG_MAWR, "MAWR", "Memory Write Address, VDC-B", 2 },
|
|
{ 0x8000 | VDC::GSREG_MARR, "MARR", "Memory Read Address, VDC-B", 2 },
|
|
{ 0x8000 | VDC::GSREG_CR, "CR", "Control, VDC-B", 2 },
|
|
{ 0x8000 | VDC::GSREG_RCR, "RCR", "Raster Counter, VDC-B", 2 },
|
|
{ 0x8000 | VDC::GSREG_BXR, "BXR", "X Scroll, VDC-B", 2 },
|
|
{ 0x8000 | VDC::GSREG_BYR, "BYR", "Y Scroll, VDC-B", 2 },
|
|
{ 0x8000 | VDC::GSREG_MWR, "MWR", "Memory Width, VDC-B", 2 },
|
|
|
|
{ 0x8000 | VDC::GSREG_HSR, "HSR", "HSR, VDC-B", 2 },
|
|
{ 0x8000 | VDC::GSREG_HDR, "HDR", "HDR, VDC-B", 2 },
|
|
{ 0x8000 | VDC::GSREG_VSR, "VSR", "VSR, VDC-B", 2 },
|
|
{ 0x8000 | VDC::GSREG_VDR, "VDR", "VDR, VDC-B", 2 },
|
|
|
|
{ 0x8000 | VDC::GSREG_VCR, "VCR", "VCR, VDC-B", 2 },
|
|
{ 0x8000 | VDC::GSREG_DCR, "DCR", "DMA Control, VDC-B", 2 },
|
|
{ 0x8000 | VDC::GSREG_SOUR, "SOUR", "VRAM DMA Source Address, VDC-B", 2 },
|
|
{ 0x8000 | VDC::GSREG_DESR, "DESR", "VRAM DMA Dest Address, VDC-B", 2 },
|
|
{ 0x8000 | VDC::GSREG_LENR, "LENR", "VRAM DMA Length, VDC-B", 2 },
|
|
{ 0x8000 | VDC::GSREG_DVSSR, "DVSSR", "DVSSR Update Address, VDC-B", 2 },
|
|
|
|
{ 0, "", "", 0 },
|
|
};
|
|
|
|
static RegGroupType PCFXRegs0Group =
|
|
{
|
|
NULL,
|
|
PCFXRegs0,
|
|
NULL,
|
|
NULL,
|
|
PCFXDBG_GetRegister,
|
|
PCFXDBG_SetRegister,
|
|
};
|
|
|
|
static RegGroupType PCFXRegs1Group =
|
|
{
|
|
NULL,
|
|
PCFXRegs1,
|
|
NULL,
|
|
NULL,
|
|
KING_GetRegister,
|
|
KING_SetRegister
|
|
};
|
|
|
|
|
|
static RegGroupType PCFXRegs4Group =
|
|
{
|
|
NULL,
|
|
PCFXRegs4,
|
|
NULL,
|
|
NULL,
|
|
FXVDCVCE_GetRegister,
|
|
FXVDCVCE_SetRegister
|
|
};
|
|
|
|
static RegGroupType VDCRegsGroup =
|
|
{
|
|
NULL,
|
|
VDCRegs,
|
|
GetRegister_VDC,
|
|
SetRegister_VDC
|
|
};
|
|
|
|
|
|
static void SetRegGroups(void)
|
|
{
|
|
MDFNDBG_AddRegGroup(&PCFXRegs0Group);
|
|
MDFNDBG_AddRegGroup(&PCFXRegs4Group);
|
|
MDFNDBG_AddRegGroup(&VDCRegsGroup);
|
|
MDFNDBG_AddRegGroup(&PCFXRegs1Group);
|
|
}
|
|
|
|
static void ForceIRQ(int level)
|
|
{
|
|
//v810_int(level);
|
|
}
|
|
|
|
static DebuggerInfoStruct DBGInfo =
|
|
{
|
|
"shift_jis",
|
|
4,
|
|
2, // Instruction alignment(bytes)
|
|
32,
|
|
32,
|
|
0x00000000,
|
|
~0,
|
|
|
|
PCFXDBG_MemPeek,
|
|
PCFXDBG_Disassemble,
|
|
NULL,
|
|
ForceIRQ,
|
|
NULL, //NESDBG_GetVector,
|
|
PCFXDBG_FlushBreakPoints,
|
|
PCFXDBG_AddBreakPoint,
|
|
PCFXDBG_SetCPUCallback,
|
|
PCFXDBG_SetBPCallback,
|
|
PCFXDBG_GetBranchTrace,
|
|
KING_SetGraphicsDecode,
|
|
PCFXDBG_SetLogFunc,
|
|
};
|
|
#endif
|
|
|
|
static const FileExtensionSpecStruct KnownExtensions[] =
|
|
{
|
|
//{ ".ex", gettext_noop("PC-FX HuEXE") },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
MDFNGI EmulatedPCFX =
|
|
{
|
|
"pcfx",
|
|
"PC-FX",
|
|
KnownExtensions,
|
|
MODPRIO_INTERNAL_HIGH,
|
|
#ifdef WANT_DEBUGGER
|
|
&DBGInfo,
|
|
#else
|
|
NULL,
|
|
#endif
|
|
&PCFXInputInfo,
|
|
NULL,
|
|
NULL,
|
|
LoadCD,
|
|
TestMagicCD,
|
|
CloseGame,
|
|
KING_SetLayerEnableMask,
|
|
"BG0\0BG1\0BG2\0BG3\0VDC-A BG\0VDC-A SPR\0VDC-B BG\0VDC-B SPR\0RAINBOW\0",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
false,
|
|
StateAction,
|
|
Emulate,
|
|
FXINPUT_SetInput,
|
|
DoSimpleCommand,
|
|
PCFXSettings,
|
|
MDFN_MASTERCLOCK_FIXED(PCFX_MASTER_CLOCK),
|
|
0,
|
|
TRUE, // Multires possible?
|
|
|
|
0, // lcm_width
|
|
0, // lcm_height
|
|
NULL, // Dummy
|
|
|
|
288, // Nominal width
|
|
240, // Nominal height
|
|
|
|
1024, // Framebuffer width
|
|
512, // Framebuffer height
|
|
|
|
2, // Number of output sound channels
|
|
};
|
|
|