BizHawk/waterbox/pcfx/pcfx.cpp

955 lines
24 KiB
C++

/******************************************************************************/
/* Mednafen NEC PC-FX Emulation Module */
/******************************************************************************/
/* pcfx.cpp:
** Copyright (C) 2006-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.
*/
#include "pcfx.h"
#include "soundbox.h"
#include "input.h"
#include "king.h"
#include "timer.h"
#include "interrupt.h"
#include "rainbow.h"
#include "huc6273.h"
#include "fxscsi.h"
#include "cdrom/cdromif.h"
#include "cdrom/scsicd.h"
//#include <mednafen/mempatcher.h>
#include <errno.h>
#include <string.h>
#include <math.h>
#include "../emulibc/emulibc.h"
#include "../emulibc/waterboxcore.h"
namespace MDFN_IEN_PCFX
{
/* 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;
V810 PCFX_V810;
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 Lagged;
static void (*InputCallback)();
// 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, const v810_timestamp_t new_base_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 - new_base_timestamp);
next_timer_ts -= (timestamp - new_base_timestamp);
next_adpcm_ts -= (timestamp - new_base_timestamp);
next_king_ts -= (timestamp - new_base_timestamp);
//printf("RTS: %d %d %d %d\n", next_pad_ts, next_timer_ts, next_adpcm_ts, next_king_ts);
}
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());
//printf("FEU: %d %d %d %d\n", next_pad_ts, next_timer_ts, next_adpcm_ts, next_king_ts);
}
#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 const CDGameEntry GameList[] =
{
#include "gamedb.inc"
};
static void Emulate(EmulateSpecStruct *espec)
{
FXINPUT_Frame();
KING_StartFrame(fx_vdc_chips, espec); //espec->surface, &espec->DisplayRect, espec->LineWidths, espec->skip);
v810_timestamp_t v810_timestamp;
v810_timestamp = PCFX_V810.Run(pcfx_event_handler);
PCFX_FixNonEvents();
// Call before resetting v810_timestamp
ForceEventUpdates(v810_timestamp);
//
// Call KING_EndFrame() before SoundBox_Flush(), otherwise CD-DA audio distortion will occur due to sound data being updated
// after it was needed instead of before.
//
KING_EndFrame(v810_timestamp);
//
// new_base_ts is guaranteed to be <= v810_timestamp
//
v810_timestamp_t new_base_ts;
espec->SoundBufSize = SoundBox_Flush(v810_timestamp, &new_base_ts, espec->SoundBuf, espec->SoundBufMaxSize, false);
KING_ResetTS(new_base_ts);
FXTIMER_ResetTS(new_base_ts);
FXINPUT_ResetTS(new_base_ts);
SoundBox_ResetTS(new_base_ts);
// Call this AFTER all the EndFrame/Flush/ResetTS stuff
RebaseTS(v810_timestamp, new_base_ts);
espec->MasterCycles = v810_timestamp - new_base_ts;
PCFX_V810.ResetTS(new_base_ts);
}
static void PCFX_Reset(void)
{
const uint32 timestamp = PCFX_V810.v810_timestamp;
//printf("Reset: %d\n", timestamp);
// Make sure all devices are synched to current timestamp before calling their Reset()/Power()(though devices should already do this sort of thing on their
// own, but it's not implemented for all of them yet, and even if it was all implemented this is also INSURANCE).
ForceEventUpdates(timestamp);
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 MDFN_NOWARN_UNUSED;
dummy_ne = fx_vdc_chips[i]->Reset();
}
KING_Reset(timestamp); // SCSICD_Power() is called from KING_Reset()
SoundBox_Reset(timestamp);
RAINBOW_Reset();
if (WantHuC6273)
HuC6273_Reset();
PCFXIRQ_Reset();
FXTIMER_Reset();
PCFX_V810.Reset();
// Force device updates so we can get new next event timestamp values.
ForceEventUpdates(timestamp);
}
static void PCFX_Power(void)
{
PCFX_Reset();
}
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 MDFN_COLD void LoadCommon(std::vector<CDIF *> *CDInterfaces, const uint8_t *bios)
{
V810_Emu_Mode cpu_mode;
cpu_mode = (V810_Emu_Mode)Setting_CpuEmulation;
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};
RAM = PCFX_V810.SetFastMap(RAM_Map_Addresses, 0x00200000, 1, _("RAM"), true);
BIOSROM = PCFX_V810.SetFastMap(BIOSROM_Map_Addresses, 0x00100000, 1, _("BIOS ROM"), false);
memcpy(BIOSROM, bios, 1024 * 1024);
/*{
std::string fxscsi_path = MDFN_GetSettingS("pcfx.fxscsi"); // For developers only, so don't make it convenient.
if (fxscsi_path != "0" && fxscsi_path != "" && fxscsi_path != "none")
{
FileStream FXSCSIFile(fxscsi_path, FileStream::MODE_READ);
uint32 FXSCSI_Map_Addresses[1] = {0x80780000};
FXSCSIROM = PCFX_V810.SetFastMap(FXSCSI_Map_Addresses, 0x0080000, 1, _("FX-SCSI ROM"), false);
FXSCSIFile.read(FXSCSIROM, 1024 * 512);
}
}*/
for (int i = 0; i < 2; i++)
{
fx_vdc_chips[i] = new VDC();
fx_vdc_chips[i]->SetUnlimitedSprites(Setting_NoSpriteLimit);
fx_vdc_chips[i]->SetVRAMSize(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, Setting_NoSpriteLimit);
//fx_vdc_chips[1] = FXVDC_Init(PCFXIRQ_SOURCE_VDCB, Setting_NoSpriteLimit);
}
SoundBox_Init(Setting_AdpcmBuggy, Setting_AdpcmNoClicks);
RAINBOW_Init(Setting_ChromaInterpolate);
FXINPUT_Init();
FXTIMER_Init();
if (WantHuC6273)
HuC6273_Init();
KING_Init();
SCSICD_SetDisc(true, NULL, 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, (int)CDInterfaces->size(), track, toc.last_track - toc.first_track + 1);
sectors = toc.tracks[(track == toc.last_track) ? 100 : 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);
//BackupSignalDirty = false;
//BackupSaveDelay = 0;
// 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));
// 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);
}
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;
printf("%s\n", found_entry->name);
printf("%s\n", found_entry->name_original);
break;
}
} // end: for(unsigned if_disc = 0; if_disc < CDInterfaces->size(); if_disc++)
}
// 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 MDFN_COLD void LoadCD(std::vector<CDIF *> *CDInterfaces, const uint8_t *bios)
{
EmuFlags = 0;
cdifs = CDInterfaces;
DoMD5CDVoodoo(CDInterfaces);
LoadCommon(CDInterfaces, bios);
MDFN_printf(_("Emulated CD-ROM drive speed: %ux\n"), (unsigned int)Setting_CdSpeed);
PCFX_Power();
}
}
using namespace MDFN_IEN_PCFX;
#define EXPORT extern "C" ECL_EXPORT
struct FrontendTOC
{
int32 FirstTrack;
int32 LastTrack;
int32 DiskType;
struct
{
int32 Adr;
int32 Control;
int32 Lba;
int32 Valid;
} Tracks[101];
};
static void (*ReadTOCCallback)(int disk, FrontendTOC *dest);
static void (*ReadSector2448Callback)(int disk, int lba, uint8 *dest);
EXPORT void SetCDCallbacks(void (*toccallback)(int disk, FrontendTOC *dest), void (*sectorcallback)(int disk, int lba, uint8 *dest))
{
ReadTOCCallback = toccallback;
ReadSector2448Callback = sectorcallback;
}
class MyCDIF : public CDIF
{
private:
int disk;
public:
MyCDIF(int disk) : disk(disk)
{
FrontendTOC t;
ReadTOCCallback(disk, &t);
disc_toc.first_track = t.FirstTrack;
disc_toc.last_track = t.LastTrack;
disc_toc.disc_type = t.DiskType;
for (int i = 0; i < 101; i++)
{
disc_toc.tracks[i].adr = t.Tracks[i].Adr;
disc_toc.tracks[i].control = t.Tracks[i].Control;
disc_toc.tracks[i].lba = t.Tracks[i].Lba;
disc_toc.tracks[i].valid = t.Tracks[i].Valid;
}
}
virtual void HintReadSector(int32 lba) {}
virtual bool ReadRawSector(uint8 *buf, int32 lba)
{
ReadSector2448Callback(disk, lba, buf);
return true;
}
};
static std::vector<CDIF *> CDInterfaces;
static uint32_t InputData[8];
struct MyFrameInfo : public FrameInfo
{
uint32_t Buttons[3]; // port 1, port 2, console
};
static EmulateSpecStruct Ess;
static int32_t LineWidths[480];
ECL_INVISIBLE static uint32_t FrameBuffer[1024 * 480];
EXPORT bool Init(int numDisks, const uint8_t *bios)
{
for (int i = 0; i < numDisks; i++)
CDInterfaces.push_back(new MyCDIF(i));
if (!TestMagicCD(&CDInterfaces))
return false;
LoadCD(&CDInterfaces, bios);
KING_SetPixelFormat();
SoundBox_SetSoundRate(44100);
SCSICD_SetDisc(false, CDInterfaces[0]);
// multitap is experimental emulation for a never release peripheral, so let's ignore it for now
FXINPUT_SetMultitap(false, false);
for (int i = 0; i < 2; i++)
FXINPUT_SetInput(i, Setting_PortDevice[i], &InputData[i]); // FXIT_GAMEPAD
PCFX_Power();
Ess.pixels = FrameBuffer;
Ess.pitch32 = 1024;
Ess.LineWidths = LineWidths;
Ess.SoundBufMaxSize = 2048;
return true;
}
static int ActiveDisk;
static uint32_t PrevConsoleButtons;
static void Blit(MyFrameInfo &f)
{
// two widths to deal with: 256 and "341" (which can be 256, 341, or 1024 wide depending on settings)
// two heights: 240 and 480, but watch out for scanlinestart / scanline end
// in pixel pro mode, 341 width is forced to 1024. we upsize 256 to 1024 as well, and double 240 tall
const uint32_t *src = FrameBuffer;
uint32_t *dst = f.VideoBuffer;
const int srcp = 1024;
src += Ess.y * srcp;
if (Setting_PixelPro)
{
f.Width = 1024;
f.Height = Ess.h;
const int dstp = 1024;
if (Ess.h > 240) // interlace
{
if (Ess.w == 256)
{
for (int j = 0; j < Ess.h; j++, src += srcp, dst += dstp)
{
for (int i = 0; i < 256; i++)
{
auto c = src[i];
dst[i * 4 + 0] = c;
dst[i * 4 + 1] = c;
dst[i * 4 + 2] = c;
dst[i * 4 + 3] = c;
}
}
}
else
{
for (int j = 0; j < Ess.h; j++, src += srcp, dst += dstp)
{
memcpy(dst, src, LineWidths[j + Ess.y] * sizeof(uint32_t));
}
}
}
else // progressive: line double
{
f.Height *= 2;
if (Ess.w == 256)
{
for (int j = 0; j < Ess.h; j++, src += srcp, dst += dstp * 2)
{
for (int i = 0; i < 256; i++)
{
auto c = src[i];
dst[i * 4 + 0] = c;
dst[i * 4 + 1] = c;
dst[i * 4 + 2] = c;
dst[i * 4 + 3] = c;
}
memcpy(dst + dstp, dst, 4096);
}
}
else
{
for (int j = 0; j < Ess.h; j++, src += srcp, dst += dstp * 2)
{
memcpy(dst, src, 4096);
memcpy(dst + dstp, src, 4096);
}
}
}
}
else
{
f.Width = Ess.w;
f.Height = Ess.h;
const int dstp = Ess.w;
for (int j = 0; j < Ess.h; j++, src += srcp, dst += dstp)
{
memcpy(dst, src, LineWidths[j + Ess.y] * sizeof(uint32_t));
}
}
}
EXPORT void FrameAdvance(MyFrameInfo &f)
{
for (int i = 0; i < 2; i++)
InputData[i] = f.Buttons[i];
Lagged = true;
uint32_t ConsoleButtons = f.Buttons[2];
int NewActiveDisk = ActiveDisk;
#define ROSE(n) ((ConsoleButtons & 1 << (n)) > (PrevConsoleButtons & 1 << (n)))
if (ROSE(0))
PCFX_Power();
if (ROSE(1))
PCFX_Reset();
if (ROSE(2))
NewActiveDisk--;
if (ROSE(3))
NewActiveDisk++;
#undef ROSE
NewActiveDisk = std::max(NewActiveDisk, -1);
NewActiveDisk = std::min<int>(NewActiveDisk, CDInterfaces.size() - 1);
if (NewActiveDisk != ActiveDisk)
SCSICD_SetDisc(NewActiveDisk == -1, NewActiveDisk == -1 ? nullptr : CDInterfaces[NewActiveDisk]);
ActiveDisk = NewActiveDisk;
PrevConsoleButtons = ConsoleButtons;
Ess.SoundBuf = f.SoundBuffer;
Emulate(&Ess);
f.Cycles = Ess.MasterCycles;
f.Samples = Ess.SoundBufSize;
f.Lagged = Lagged;
Blit(f);
}
EXPORT void GetMemoryAreas(MemoryArea *m)
{
m[0].Data = BackupRAM;
m[0].Name = "Backup RAM";
m[0].Size = sizeof(BackupRAM);
m[0].Flags = MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_SAVERAMMABLE;
m[1].Data = ExBackupRAM;
m[1].Name = "Extra Backup RAM";
m[1].Size = sizeof(ExBackupRAM);
m[1].Flags = MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_SAVERAMMABLE;
m[2].Data = BIOSROM;
m[2].Name = "BIOS ROM";
m[2].Size = 1024 * 1024;
m[2].Flags = MEMORYAREA_FLAGS_WORDSIZE4;
m[3].Data = RAM;
m[3].Name = "Main RAM";
m[3].Size = 2 * 1024 * 1024;
m[3].Flags = MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_PRIMARY;
// m[4].Data = FXSCSIROM;
// m[4].Name = "Scsi Rom";
// m[4].Size = 512 * 1024;
// m[4].Flags = MEMORYAREA_FLAGS_WORDSIZE4;
for (int i = 0; i < 2; i++)
{
m[i + 5].Data = fx_vdc_chips[i]->GetVramPointer();
m[i + 5].Name = i == 0 ? "VDC A VRAM" : "VDC B VRAM";
m[i + 5].Size = fx_vdc_chips[i]->GetVramByteSize();
m[i + 5].Flags = MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_WORDSIZE2;
}
}
EXPORT void EnableLayers(int mask)
{
// BG0 = 0
// BG1
// BG2
// BG3
// VDC-A BG
// VDC-A SPR
// VDC-B BG
// VDC-B SPR
// RAINBOW
KING_SetLayerEnableMask(mask);
}
EXPORT void SetInputCallback(void (*callback)())
{
InputCallback = callback;
}
// settings
ECL_SEALED int Setting_HighDotclockWidth = 341;
ECL_SEALED int Setting_CdSpeed = 2;
ECL_SEALED int Setting_SlStart = 4;
ECL_SEALED int Setting_SlEnd = 235;
ECL_SEALED double Setting_ResampRateError = 0.0000009;
ECL_SEALED int Setting_ResampQuality = 3;
ECL_SEALED int Setting_CpuEmulation = 2; // 0 = fast, 1 = accurate, 2 = auto
ECL_SEALED bool Setting_NoSpriteLimit;
ECL_SEALED bool Setting_AdpcmBuggy = false;
ECL_SEALED bool Setting_AdpcmNoClicks = true;
ECL_SEALED bool Setting_ChromaInterpolate = false;
ECL_SEALED int Setting_PortDevice[2];
ECL_SEALED bool Setting_PixelPro;
struct FrontendSettings
{
int32_t AdpcmEmulateBuggyCodec;
int32_t AdpcmSuppressChannelResetClicks;
int32_t HiResEmulation;
int32_t DisableSpriteLimit;
int32_t ChromaInterpolation;
int32_t ScanlineStart;
int32_t ScanlineEnd;
int32_t CdSpeed;
int32_t CpuEmulation;
int32_t Port1;
int32_t Port2;
int32_t PixelPro;
};
EXPORT void PutSettingsBeforeInit(const FrontendSettings &s)
{
Setting_AdpcmBuggy = s.AdpcmEmulateBuggyCodec;
Setting_AdpcmNoClicks = s.AdpcmSuppressChannelResetClicks;
Setting_HighDotclockWidth = s.PixelPro ? 1024 : s.HiResEmulation;
Setting_NoSpriteLimit = s.DisableSpriteLimit;
Setting_ChromaInterpolate = s.ChromaInterpolation;
Setting_SlStart = s.ScanlineStart;
Setting_SlEnd = s.ScanlineEnd;
Setting_CdSpeed = s.CdSpeed;
Setting_CpuEmulation = s.CpuEmulation;
Setting_PortDevice[0] = s.Port1;
Setting_PortDevice[1] = s.Port2;
Setting_PixelPro = s.PixelPro;
}
/*MDFNGI EmulatedPCFX =
{
FXINPUT_SetInput,
SetMedia,
DoSimpleCommand,
NULL,
PCFXSettings,
MDFN_MASTERCLOCK_FIXED(PCFX_MASTER_CLOCK),
0,
TRUE, // Multires possible?
288, // Nominal width
240, // Nominal height
1024, // Framebuffer width
512, // Framebuffer height
};*/
int main()
{
return 0;
}