mirror of https://github.com/AxioDL/metaforce.git
NESEmulator save fixes
This commit is contained in:
parent
c42fe06233
commit
15e5b4bbb8
|
@ -1,6 +1,6 @@
|
||||||
include_directories(${CMAKE_SOURCE_DIR}/DataSpec ${CMAKE_SOURCE_DIR}/Runtime ${CMAKE_CURRENT_SOURCE_DIR})
|
include_directories(${CMAKE_SOURCE_DIR}/DataSpec ${CMAKE_SOURCE_DIR}/Runtime ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
file(GLOB MAPPER_SRCS fixNES/mapper/*.c)
|
file(GLOB MAPPER_SRCS fixNES/mapper/*.c)
|
||||||
add_library(NESEmulator CNESEmulator.hpp CNESEmulator.cpp CNESShader.hpp CNESShader.cpp malloc.h
|
add_library(NESEmulator CNESEmulator.hpp CNESEmulator.cpp CNESShader.hpp CNESShader.cpp malloc.h
|
||||||
fixNES/apu.c fixNES/audio_fds.c fixNES/audio_mmc5.c fixNES/audio_vrc6.c fixNES/audio_vrc7.c
|
apu.c fixNES/audio_fds.c fixNES/audio_mmc5.c fixNES/audio_vrc6.c fixNES/audio_vrc7.c
|
||||||
fixNES/audio_n163.c fixNES/audio_s5b.c fixNES/cpu.c fixNES/ppu.c fixNES/mem.c fixNES/input.c
|
fixNES/audio_n163.c fixNES/audio_s5b.c fixNES/cpu.c ppu.c fixNES/mem.c fixNES/input.c
|
||||||
fixNES/mapper.c fixNES/mapperList.c fixNES/fm2play.c fixNES/vrc_irq.c ${MAPPER_SRCS})
|
fixNES/mapper.c fixNES/mapperList.c fixNES/fm2play.c fixNES/vrc_irq.c ${MAPPER_SRCS})
|
||||||
|
|
|
@ -31,7 +31,7 @@ extern "C"
|
||||||
#include "fixNES/mapper_h/nsf.h"
|
#include "fixNES/mapper_h/nsf.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2017 FIX94
|
* Portions Copyright (C) 2017 FIX94
|
||||||
*
|
*
|
||||||
* This software may be modified and distributed under the terms
|
* This software may be modified and distributed under the terms
|
||||||
* of the MIT license. See the LICENSE file for details.
|
* of the MIT license. See the LICENSE file for details.
|
||||||
|
@ -77,7 +77,7 @@ static uint32_t emuPrgRAMsize = 0;
|
||||||
uint32_t textureImage[0xF000];
|
uint32_t textureImage[0xF000];
|
||||||
bool nesPause = false;
|
bool nesPause = false;
|
||||||
bool ppuDebugPauseFrame = false;
|
bool ppuDebugPauseFrame = false;
|
||||||
bool doOverscan = true;
|
bool doOverscan = false;
|
||||||
bool nesPAL = false;
|
bool nesPAL = false;
|
||||||
bool nesEmuNSFPlayback = false;
|
bool nesEmuNSFPlayback = false;
|
||||||
|
|
||||||
|
@ -139,6 +139,9 @@ static uint16_t vrc7Clock = 1;
|
||||||
|
|
||||||
extern bool fdsSwitch;
|
extern bool fdsSwitch;
|
||||||
|
|
||||||
|
bool apuCycleURDE();
|
||||||
|
uint8_t* ppuGetVRAM();
|
||||||
|
|
||||||
int audioUpdate()
|
int audioUpdate()
|
||||||
{
|
{
|
||||||
if (!EmulatorInst)
|
if (!EmulatorInst)
|
||||||
|
@ -256,6 +259,7 @@ void CNESEmulator::InitializeEmulator()
|
||||||
|
|
||||||
CGraphics::CommitResources([this](boo::IGraphicsDataFactory::Context& ctx)
|
CGraphics::CommitResources([this](boo::IGraphicsDataFactory::Context& ctx)
|
||||||
{
|
{
|
||||||
|
// Nearest-neighbor FTW!
|
||||||
m_texture = ctx.newDynamicTexture(VISIBLE_DOTS, linesToDraw,
|
m_texture = ctx.newDynamicTexture(VISIBLE_DOTS, linesToDraw,
|
||||||
boo::TextureFormat::RGBA8,
|
boo::TextureFormat::RGBA8,
|
||||||
boo::TextureClampMode::ClampToEdgeNearest);
|
boo::TextureClampMode::ClampToEdgeNearest);
|
||||||
|
@ -393,8 +397,9 @@ size_t CNESEmulator::supplyAudio(boo::IAudioVoice& voice, size_t frames, int16_t
|
||||||
static int catchupFrames = 0;
|
static int catchupFrames = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void CNESEmulator::NesEmuMainLoop()
|
bool CNESEmulator::NesEmuMainLoop()
|
||||||
{
|
{
|
||||||
|
bool drawFrame = false;
|
||||||
int start = GetTickCount();
|
int start = GetTickCount();
|
||||||
int loopCount = 0;
|
int loopCount = 0;
|
||||||
do
|
do
|
||||||
|
@ -406,6 +411,7 @@ void CNESEmulator::NesEmuMainLoop()
|
||||||
#endif
|
#endif
|
||||||
//printf("LC RENDER: %d\n", loopCount);
|
//printf("LC RENDER: %d\n", loopCount);
|
||||||
m_texture->load(textureImage, visibleImg);
|
m_texture->load(textureImage, visibleImg);
|
||||||
|
drawFrame = true;
|
||||||
emuRenderFrame = false;
|
emuRenderFrame = false;
|
||||||
#if CATCHUP_SKIP
|
#if CATCHUP_SKIP
|
||||||
if (catchupFrames)
|
if (catchupFrames)
|
||||||
|
@ -416,11 +422,16 @@ void CNESEmulator::NesEmuMainLoop()
|
||||||
}
|
}
|
||||||
++loopCount;
|
++loopCount;
|
||||||
if(mainClock == cpuCycleTimer)
|
if(mainClock == cpuCycleTimer)
|
||||||
|
{
|
||||||
|
//URDE uses this loop to pre-fill audio buffers,
|
||||||
|
//rather than executing multiple frame loops
|
||||||
|
bool breakout = false;
|
||||||
|
do
|
||||||
{
|
{
|
||||||
//runs every 8th cpu clock
|
//runs every 8th cpu clock
|
||||||
if(apuClock == 8)
|
if(apuClock == 8)
|
||||||
{
|
{
|
||||||
if(!apuCycle())
|
if(!apuCycleURDE())
|
||||||
{
|
{
|
||||||
#if (WINDOWS_BUILD && DEBUG_MAIN_CALLS)
|
#if (WINDOWS_BUILD && DEBUG_MAIN_CALLS)
|
||||||
emuMainTimesSkipped++;
|
emuMainTimesSkipped++;
|
||||||
|
@ -429,6 +440,7 @@ void CNESEmulator::NesEmuMainLoop()
|
||||||
catchupFrames = 0;
|
catchupFrames = 0;
|
||||||
#endif
|
#endif
|
||||||
//printf("LC SKIP\n");
|
//printf("LC SKIP\n");
|
||||||
|
breakout = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
apuClock = 1;
|
apuClock = 1;
|
||||||
|
@ -437,6 +449,10 @@ void CNESEmulator::NesEmuMainLoop()
|
||||||
apuClock++;
|
apuClock++;
|
||||||
//runs every cpu cycle
|
//runs every cpu cycle
|
||||||
apuClockTimers();
|
apuClockTimers();
|
||||||
|
} while (emuSkipVsync);
|
||||||
|
if (breakout)
|
||||||
|
break;
|
||||||
|
|
||||||
//main CPU clock
|
//main CPU clock
|
||||||
if(!cpuCycle())
|
if(!cpuCycle())
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
|
@ -499,7 +515,7 @@ void CNESEmulator::NesEmuMainLoop()
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
if ((loopCount % 5000) == 0 && GetTickCount() - start >= 14)
|
if ((loopCount % 10000) == 0 && GetTickCount() - start >= 14)
|
||||||
{
|
{
|
||||||
#if CATCHUP_SKIP
|
#if CATCHUP_SKIP
|
||||||
if (catchupFrames < 50)
|
if (catchupFrames < 50)
|
||||||
|
@ -531,6 +547,8 @@ void CNESEmulator::NesEmuMainLoop()
|
||||||
}
|
}
|
||||||
emuMainFrameStart = end;
|
emuMainFrameStart = end;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
return drawFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nesEmuFdsSetup(uint8_t *src, uint8_t *dst)
|
static void nesEmuFdsSetup(uint8_t *src, uint8_t *dst)
|
||||||
|
@ -755,16 +773,142 @@ void CNESEmulator::DecompressROM(u8* dataIn, u8* dataOut, u32 dataOutLen, u8 des
|
||||||
|
|
||||||
void CNESEmulator::ProcessUserInput(const CFinalInput& input, int)
|
void CNESEmulator::ProcessUserInput(const CFinalInput& input, int)
|
||||||
{
|
{
|
||||||
|
if (input.ControllerIdx() != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (GetPasswordEntryState() != EPasswordEntryState::NotPasswordScreen)
|
||||||
|
{
|
||||||
|
// Don't swap A/B
|
||||||
|
inValReads[BUTTON_A] = input.DA();
|
||||||
|
inValReads[BUTTON_B] = input.DB();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Prime controls (B jumps, A shoots)
|
||||||
|
inValReads[BUTTON_B] = input.DA() | input.DY();
|
||||||
|
inValReads[BUTTON_A] = input.DB() | input.DX();
|
||||||
|
}
|
||||||
|
|
||||||
inValReads[BUTTON_UP] = input.DDPUp() | input.DLAUp();
|
inValReads[BUTTON_UP] = input.DDPUp() | input.DLAUp();
|
||||||
inValReads[BUTTON_DOWN] = input.DDPDown() | input.DLADown();
|
inValReads[BUTTON_DOWN] = input.DDPDown() | input.DLADown();
|
||||||
inValReads[BUTTON_LEFT] = input.DDPLeft() | input.DLALeft();
|
inValReads[BUTTON_LEFT] = input.DDPLeft() | input.DLALeft();
|
||||||
inValReads[BUTTON_RIGHT] = input.DDPRight() | input.DLARight();
|
inValReads[BUTTON_RIGHT] = input.DDPRight() | input.DLARight();
|
||||||
inValReads[BUTTON_A] = input.DA();
|
|
||||||
inValReads[BUTTON_B] = input.DB();
|
|
||||||
inValReads[BUTTON_SELECT] = input.DZ();
|
inValReads[BUTTON_SELECT] = input.DZ();
|
||||||
inValReads[BUTTON_START] = input.DStart();
|
inValReads[BUTTON_START] = input.DStart();
|
||||||
if (input.PL())
|
}
|
||||||
x20_wantsQuit = true;
|
|
||||||
|
bool CNESEmulator::CheckForGameOver(const u8* vram, u8* passwordOut)
|
||||||
|
{
|
||||||
|
// "PASS WORD"
|
||||||
|
if (memcmp(vram + 0x14B, "\x19\xa\x1c\x1c\xff\x20\x18\x1b\xd", 9))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int chOff = 0;
|
||||||
|
int encOff = 0;
|
||||||
|
u8 pwOut[18];
|
||||||
|
for (int i=0 ; i<24 ; ++i)
|
||||||
|
{
|
||||||
|
u8 chName = vram[0x1A9 + chOff];
|
||||||
|
++chOff;
|
||||||
|
if (chOff == 0x6 || chOff == 0x46)
|
||||||
|
++chOff; // mid-line space
|
||||||
|
else if (chOff == 0xd)
|
||||||
|
chOff = 64; // 2nd line
|
||||||
|
|
||||||
|
if (chName > 0x3f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (i & 0x3)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
pwOut[encOff] = chName;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
pwOut[encOff] |= chName << 6;
|
||||||
|
++encOff;
|
||||||
|
pwOut[encOff] = chName >> 2;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
pwOut[encOff] |= chName << 4;
|
||||||
|
++encOff;
|
||||||
|
pwOut[encOff] = chName >> 4;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
pwOut[encOff] |= chName << 2;
|
||||||
|
++encOff;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (passwordOut)
|
||||||
|
memmove(passwordOut, pwOut, 18);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CNESEmulator::EPasswordEntryState CNESEmulator::CheckForPasswordEntryScreen(const u8* vram)
|
||||||
|
{
|
||||||
|
// "PASS WORD PLEASE"
|
||||||
|
if (memcmp(vram + 0x88, "\x19\xa\x1c\x1c\xff\x20\x18\x1b\xd\xff\x19\x15\xe\xa\x1c\xe", 16))
|
||||||
|
return EPasswordEntryState::NotPasswordScreen;
|
||||||
|
|
||||||
|
for (int i=0 ; i<13 ; ++i)
|
||||||
|
if (vram[0x109 + i] < 0x40 || vram[0x149 + i] < 0x40)
|
||||||
|
return EPasswordEntryState::Entered;
|
||||||
|
|
||||||
|
return EPasswordEntryState::NotEntered;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CNESEmulator::SetPasswordIntoEntryScreen(u8* vram, u8* wram, const u8* password)
|
||||||
|
{
|
||||||
|
if (CheckForPasswordEntryScreen(vram) != EPasswordEntryState::NotEntered)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i=0 ; i<18 ; ++i)
|
||||||
|
if (password[i])
|
||||||
|
break;
|
||||||
|
if (i == 18)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int encOff = 0;
|
||||||
|
int chOff = 0;
|
||||||
|
u32 lastWord = 0;
|
||||||
|
for (i=0 ; i<24 ; ++i)
|
||||||
|
{
|
||||||
|
switch (i & 0x3)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
lastWord = password[encOff];
|
||||||
|
++encOff;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
lastWord = (lastWord >> 6) | (password[encOff] << 2);
|
||||||
|
++encOff;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
lastWord = (lastWord >> 4) | (password[encOff] << 4);
|
||||||
|
++encOff;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
lastWord = (lastWord >> 6);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 name = u8(lastWord & 0x3f);
|
||||||
|
wram[0x99a + i] = name;
|
||||||
|
vram[0x109 + chOff] = name;
|
||||||
|
++chOff;
|
||||||
|
if (chOff == 0x6 || chOff == 0x46)
|
||||||
|
++chOff; // mid-line space
|
||||||
|
else if (chOff == 0xd)
|
||||||
|
chOff = 64; // 2nd line
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CNESEmulator::Update()
|
void CNESEmulator::Update()
|
||||||
|
@ -779,7 +923,19 @@ void CNESEmulator::Update()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
bool gameOver = CheckForGameOver(ppuGetVRAM(), x21_passwordFromNES);
|
||||||
|
x34_passwordEntryState = CheckForPasswordEntryScreen(ppuGetVRAM());
|
||||||
|
if (x34_passwordEntryState == EPasswordEntryState::NotEntered && x38_passwordPending)
|
||||||
|
{
|
||||||
|
SetPasswordIntoEntryScreen(ppuGetVRAM(), emuPrgRAM, x39_passwordToNES);
|
||||||
|
x38_passwordPending = false;
|
||||||
|
}
|
||||||
|
if (gameOver && !x20_gameOver)
|
||||||
|
for (int i=0 ; i<3 ; ++i) // Three draw loops to ensure password display
|
||||||
|
while (!NesEmuMainLoop()) {}
|
||||||
|
else
|
||||||
NesEmuMainLoop();
|
NesEmuMainLoop();
|
||||||
|
x20_gameOver = gameOver;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -801,10 +957,10 @@ void CNESEmulator::Draw(const zeus::CColor& mulColor, bool filtering)
|
||||||
CGraphics::DrawArray(0, 4);
|
CGraphics::DrawArray(0, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CNESEmulator::LoadState(const u8* state)
|
void CNESEmulator::LoadPassword(const u8* state)
|
||||||
{
|
{
|
||||||
memmove(x39_loadState, state, 18);
|
memmove(x39_passwordToNES, state, 18);
|
||||||
x38_stateLoaded = true;
|
x38_passwordPending = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,14 @@ namespace MP1
|
||||||
|
|
||||||
class CNESEmulator : public boo::IAudioVoiceCallback
|
class CNESEmulator : public boo::IAudioVoiceCallback
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
enum class EPasswordEntryState
|
||||||
|
{
|
||||||
|
NotPasswordScreen,
|
||||||
|
NotEntered,
|
||||||
|
Entered
|
||||||
|
};
|
||||||
|
private:
|
||||||
static bool EmulatorConstructed;
|
static bool EmulatorConstructed;
|
||||||
|
|
||||||
std::unique_ptr<u8[]> m_nesEmuPBuf;
|
std::unique_ptr<u8[]> m_nesEmuPBuf;
|
||||||
|
@ -49,26 +57,29 @@ class CNESEmulator : public boo::IAudioVoiceCallback
|
||||||
size_t m_posInBuf = 0;
|
size_t m_posInBuf = 0;
|
||||||
boo::ObjToken<boo::IAudioVoice> m_booVoice;
|
boo::ObjToken<boo::IAudioVoice> m_booVoice;
|
||||||
|
|
||||||
bool x20_wantsQuit = false;
|
bool x20_gameOver = false;
|
||||||
u8 x21_saveState[18];
|
u8 x21_passwordFromNES[18];
|
||||||
bool x34_wantsLoad = false;
|
EPasswordEntryState x34_passwordEntryState = EPasswordEntryState::NotPasswordScreen;
|
||||||
bool x38_stateLoaded = false;
|
bool x38_passwordPending = false;
|
||||||
u8 x39_loadState[18];
|
u8 x39_passwordToNES[18];
|
||||||
static void DecompressROM(u8* dataIn, u8* dataOut, u32 dataOutLen = 0x20000, u8 descrambleSeed = 0xe9,
|
static void DecompressROM(u8* dataIn, u8* dataOut, u32 dataOutLen = 0x20000, u8 descrambleSeed = 0xe9,
|
||||||
u32 checkDataLen = 0x1FFFC, u32 checksumMagic = 0xA663);
|
u32 checkDataLen = 0x1FFFC, u32 checksumMagic = 0xA663);
|
||||||
void InitializeEmulator();
|
void InitializeEmulator();
|
||||||
void DeinitializeEmulator();
|
void DeinitializeEmulator();
|
||||||
void NesEmuMainLoop();
|
bool NesEmuMainLoop();
|
||||||
|
static bool CheckForGameOver(const u8* vram, u8* passwordOut = nullptr);
|
||||||
|
static EPasswordEntryState CheckForPasswordEntryScreen(const uint8_t* vram);
|
||||||
|
static bool SetPasswordIntoEntryScreen(u8* vram, u8* wram, const u8* password);
|
||||||
public:
|
public:
|
||||||
CNESEmulator();
|
CNESEmulator();
|
||||||
~CNESEmulator();
|
~CNESEmulator();
|
||||||
void ProcessUserInput(const CFinalInput& input, int);
|
void ProcessUserInput(const CFinalInput& input, int);
|
||||||
void Update();
|
void Update();
|
||||||
void Draw(const zeus::CColor& mulColor, bool filtering);
|
void Draw(const zeus::CColor& mulColor, bool filtering);
|
||||||
void LoadState(const u8* state);
|
void LoadPassword(const u8* state);
|
||||||
const u8* GetSaveState() const { return x21_saveState; }
|
const u8* GetPassword() const { return x21_passwordFromNES; }
|
||||||
bool WantsQuit() const { return x20_wantsQuit; }
|
bool IsGameOver() const { return x20_gameOver; }
|
||||||
bool WantsLoad() const { return x34_wantsLoad; }
|
EPasswordEntryState GetPasswordEntryState() const { return x34_passwordEntryState; }
|
||||||
|
|
||||||
int audioUpdate();
|
int audioUpdate();
|
||||||
void preSupplyAudio(boo::IAudioVoice& voice, double dt) {}
|
void preSupplyAudio(boo::IAudioVoice& voice, double dt) {}
|
||||||
|
|
|
@ -0,0 +1,172 @@
|
||||||
|
#include "fixNES/apu.c"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Alternate apuCycle implementation to avoid processing multiple
|
||||||
|
* NES frames per URDE frame (costly and jarring to player).
|
||||||
|
*
|
||||||
|
* This implementation nominally fills 6/10 buffers, allowing
|
||||||
|
* emulation to "catch up" by having more buffer headroom available
|
||||||
|
* (and also reducing audio latency somewhat).
|
||||||
|
*
|
||||||
|
* URDE's NesEmuMainLoop uses emuSkipVsync as a signal to proceed
|
||||||
|
* with the emulation, allowing audio buffers to be pre-filled with
|
||||||
|
* generated tones independent of the emulated CPU. Granted, this
|
||||||
|
* compromises accuracy, but doesn't affect NEStroid's behavior and
|
||||||
|
* reduces audio discontinuities.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool apuCycleURDE()
|
||||||
|
{
|
||||||
|
if(curBufPos == apuBufSize)
|
||||||
|
{
|
||||||
|
int updateRes = audioUpdate();
|
||||||
|
if(updateRes == 0)
|
||||||
|
{
|
||||||
|
emuSkipFrame = false;
|
||||||
|
emuSkipVsync = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(updateRes > 6)
|
||||||
|
{
|
||||||
|
emuSkipVsync = true;
|
||||||
|
emuSkipFrame = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
emuSkipFrame = false;
|
||||||
|
if(updateRes > 4) // 6 buffers filled, stop here
|
||||||
|
emuSkipVsync = true;
|
||||||
|
else
|
||||||
|
emuSkipVsync = false;
|
||||||
|
}
|
||||||
|
curBufPos = 0;
|
||||||
|
}
|
||||||
|
uint8_t p1Out = lastP1Out, p2Out = lastP2Out,
|
||||||
|
triOut = lastTriOut, noiseOut = lastNoiseOut;
|
||||||
|
if(p1LengthCtr && (APU_IO_Reg[0x15] & P1_ENABLE))
|
||||||
|
{
|
||||||
|
if(p1seq[p1Cycle] && !p1Sweep.mute && freq1 >= 8 && freq1 < 0x7FF)
|
||||||
|
lastP1Out = p1Out = (p1Env.constant ? p1Env.vol : p1Env.decay);
|
||||||
|
else
|
||||||
|
p1Out = 0;
|
||||||
|
}
|
||||||
|
if(p2LengthCtr && (APU_IO_Reg[0x15] & P2_ENABLE))
|
||||||
|
{
|
||||||
|
if(p2seq[p2Cycle] && !p2Sweep.mute && freq2 >= 8 && freq2 < 0x7FF)
|
||||||
|
lastP2Out = p2Out = (p2Env.constant ? p2Env.vol : p2Env.decay);
|
||||||
|
else
|
||||||
|
p2Out = 0;
|
||||||
|
}
|
||||||
|
if(triLengthCtr && triCurLinearCtr && (APU_IO_Reg[0x15] & TRI_ENABLE))
|
||||||
|
{
|
||||||
|
if(triSeq[triCycle] && triFreq >= 2)
|
||||||
|
lastTriOut = triOut = triSeq[triCycle];
|
||||||
|
else
|
||||||
|
triOut = 0;
|
||||||
|
}
|
||||||
|
if(noiseLengthCtr && (APU_IO_Reg[0x15] & NOISE_ENABLE))
|
||||||
|
{
|
||||||
|
if((noiseShiftReg&1) == 0 && noiseFreq > 0)
|
||||||
|
lastNoiseOut = noiseOut = (noiseEnv.constant ? noiseEnv.vol : noiseEnv.decay);
|
||||||
|
else
|
||||||
|
noiseOut = 0;
|
||||||
|
}
|
||||||
|
#if AUDIO_FLOAT
|
||||||
|
float curIn = pulseLookupTbl[p1Out + p2Out] + tndLookupTbl[(3*triOut) + (2*noiseOut) + dmcVol];
|
||||||
|
//very rough still
|
||||||
|
if(vrc6enabled)
|
||||||
|
{
|
||||||
|
vrc6AudioCycle();
|
||||||
|
curIn += ((float)vrc6Out)*0.008f;
|
||||||
|
curIn *= 0.6667f;
|
||||||
|
}
|
||||||
|
if(fdsEnabled)
|
||||||
|
{
|
||||||
|
fdsAudioCycle();
|
||||||
|
curIn += ((float)fdsOut)*0.00617f;
|
||||||
|
curIn *= 0.75f;
|
||||||
|
}
|
||||||
|
if(mmc5enabled)
|
||||||
|
{
|
||||||
|
mmc5AudioCycle();
|
||||||
|
curIn += pulseLookupTbl[mmc5Out]+(mmc5pcm*0.002f);
|
||||||
|
curIn *= 0.75f;
|
||||||
|
}
|
||||||
|
if(vrc7enabled)
|
||||||
|
{
|
||||||
|
curIn += (((float)(vrc7Out>>7))/32768.f);
|
||||||
|
curIn *= 0.75f;
|
||||||
|
}
|
||||||
|
if(n163enabled)
|
||||||
|
{
|
||||||
|
curIn += ((float)n163Out)*0.0008f;
|
||||||
|
curIn *= 0.6667f;
|
||||||
|
}
|
||||||
|
if(s5Benabled)
|
||||||
|
{
|
||||||
|
s5BAudioCycle();
|
||||||
|
curIn += ((float)s5BOut)/32768.f;
|
||||||
|
curIn *= 0.6667f;
|
||||||
|
}
|
||||||
|
//amplify input
|
||||||
|
curIn *= 3.0f;
|
||||||
|
float curLPout = lastLPOut+(lpVal*(curIn-lastLPOut));
|
||||||
|
float curHPOut = hpVal*(lastHPOut+lastLPOut-curLPout);
|
||||||
|
//set output
|
||||||
|
apuOutBuf[curBufPos] = curHPOut;
|
||||||
|
lastLPOut = curLPout;
|
||||||
|
lastHPOut = curHPOut;
|
||||||
|
#else
|
||||||
|
int32_t curIn = pulseLookupTbl[p1Out + p2Out] + tndLookupTbl[(3*triOut) + (2*noiseOut) + dmcVol];
|
||||||
|
//very rough still
|
||||||
|
if(vrc6enabled)
|
||||||
|
{
|
||||||
|
vrc6AudioCycle();
|
||||||
|
curIn += ((int32_t)vrc6Out)*262;
|
||||||
|
curIn <<= 1; curIn /= 3;
|
||||||
|
}
|
||||||
|
if(fdsEnabled)
|
||||||
|
{
|
||||||
|
fdsAudioCycle();
|
||||||
|
curIn += ((int32_t)fdsOut)*202;
|
||||||
|
curIn *= 3; curIn >>= 2;
|
||||||
|
}
|
||||||
|
if(mmc5enabled)
|
||||||
|
{
|
||||||
|
mmc5AudioCycle();
|
||||||
|
curIn += pulseLookupTbl[mmc5Out]+(mmc5pcm<<6);
|
||||||
|
curIn *= 3; curIn >>= 2;
|
||||||
|
}
|
||||||
|
if(vrc7enabled)
|
||||||
|
{
|
||||||
|
curIn += vrc7Out>>7;
|
||||||
|
curIn *= 3; curIn >>= 2;
|
||||||
|
}
|
||||||
|
if(n163enabled)
|
||||||
|
{
|
||||||
|
curIn += n163Out*26;
|
||||||
|
curIn <<= 1; curIn /= 3;
|
||||||
|
}
|
||||||
|
if(s5Benabled)
|
||||||
|
{
|
||||||
|
s5BAudioCycle();
|
||||||
|
curIn += s5BOut;
|
||||||
|
curIn <<= 1; curIn /= 3;
|
||||||
|
}
|
||||||
|
//amplify input
|
||||||
|
curIn *= 3;
|
||||||
|
int32_t curOut;
|
||||||
|
//gen output
|
||||||
|
curOut = lastLPOut+((lpVal*(curIn-lastLPOut))>>15); //Set Lowpass Output
|
||||||
|
curIn = (lastHPOut+lastLPOut-curOut); //Set Highpass Input
|
||||||
|
curIn += (curIn>>31)&1; //Add Sign Bit for proper Downshift later
|
||||||
|
lastLPOut = curOut; //Save Lowpass Output
|
||||||
|
curOut = (hpVal*curIn)>>15; //Set Highpass Output
|
||||||
|
lastHPOut = curOut; //Save Highpass Output
|
||||||
|
//Save Clipped Highpass Output
|
||||||
|
apuOutBuf[curBufPos] = (curOut > 32767)?(32767):((curOut < -32768)?(-32768):curOut);
|
||||||
|
#endif
|
||||||
|
apuOutBuf[curBufPos+1] = apuOutBuf[curBufPos];
|
||||||
|
curBufPos+=2;
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -1 +1 @@
|
||||||
Subproject commit c0a679921ddb0e104aa35202bc9cdf0be82850b8
|
Subproject commit 01b5bde49a730eb287b18def7ed5e5f510cda7b5
|
|
@ -0,0 +1,8 @@
|
||||||
|
#include "fixNES/ppu.c"
|
||||||
|
|
||||||
|
/* Non-invasive way to access VRAM buffer directly */
|
||||||
|
|
||||||
|
uint8_t* ppuGetVRAM()
|
||||||
|
{
|
||||||
|
return PPU_VRAM;
|
||||||
|
}
|
|
@ -8,9 +8,8 @@
|
||||||
namespace urde
|
namespace urde
|
||||||
{
|
{
|
||||||
|
|
||||||
class CFinalInput
|
struct CFinalInput
|
||||||
{
|
{
|
||||||
friend class CStateManager;
|
|
||||||
float x0_dt;
|
float x0_dt;
|
||||||
u32 x4_controllerIdx;
|
u32 x4_controllerIdx;
|
||||||
float x8_anaLeftX;
|
float x8_anaLeftX;
|
||||||
|
@ -63,7 +62,6 @@ class CFinalInput
|
||||||
bool x2e_b30_PDPLeft:1;
|
bool x2e_b30_PDPLeft:1;
|
||||||
bool x2e_b31_PStart:1;
|
bool x2e_b31_PStart:1;
|
||||||
|
|
||||||
public:
|
|
||||||
CFinalInput();
|
CFinalInput();
|
||||||
CFinalInput(int cIdx, float dt,
|
CFinalInput(int cIdx, float dt,
|
||||||
const boo::DolphinControllerState& data,
|
const boo::DolphinControllerState& data,
|
||||||
|
|
|
@ -1036,8 +1036,8 @@ void CFrontEndUI::SFusionBonusFrame::Update(float dt, CSaveGameScreen* saveUI)
|
||||||
else if (x24_loadedFrame)
|
else if (x24_loadedFrame)
|
||||||
x24_loadedFrame->Update(dt);
|
x24_loadedFrame->Update(dt);
|
||||||
|
|
||||||
bool showFusionSuit = g_GameState->SystemOptions().GetPlayerLinkedFusion() &&
|
bool showFusionSuit = (g_GameState->SystemOptions().GetPlayerLinkedFusion() &&
|
||||||
g_GameState->SystemOptions().GetPlayerBeatNormalMode();
|
g_GameState->SystemOptions().GetPlayerBeatNormalMode()) || m_gbaOverride;
|
||||||
bool showFusionSuitProceed = showFusionSuit && x28_tablegroup_options->GetUserSelection() == 1;
|
bool showFusionSuitProceed = showFusionSuit && x28_tablegroup_options->GetUserSelection() == 1;
|
||||||
x2c_tablegroup_fusionsuit->SetIsActive(showFusionSuitProceed);
|
x2c_tablegroup_fusionsuit->SetIsActive(showFusionSuitProceed);
|
||||||
x2c_tablegroup_fusionsuit->SetIsVisible(showFusionSuitProceed);
|
x2c_tablegroup_fusionsuit->SetIsVisible(showFusionSuitProceed);
|
||||||
|
@ -1060,11 +1060,6 @@ void CFrontEndUI::SFusionBonusFrame::Update(float dt, CSaveGameScreen* saveUI)
|
||||||
instructionStr = g_MainStringTable->GetString(79); // You have not completed fusion
|
instructionStr = g_MainStringTable->GetString(79); // You have not completed fusion
|
||||||
else if (!g_GameState->SystemOptions().GetPlayerBeatFusion())
|
else if (!g_GameState->SystemOptions().GetPlayerBeatFusion())
|
||||||
instructionStr = g_MainStringTable->GetString(77); // To play NES Metroid
|
instructionStr = g_MainStringTable->GetString(77); // To play NES Metroid
|
||||||
else
|
|
||||||
{
|
|
||||||
instructionStr = u"NES Emulator currently unsupported";
|
|
||||||
x30_textpane_instructions.x0_panes[0]->TextSupport().SetFontColor(zeus::CColor::skYellow);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
x30_textpane_instructions.SetPairText(instructionStr);
|
x30_textpane_instructions.SetPairText(instructionStr);
|
||||||
|
@ -1102,15 +1097,22 @@ CFrontEndUI::SFusionBonusFrame::ProcessUserInput(const CFinalInput& input, CSave
|
||||||
}
|
}
|
||||||
else if (x24_loadedFrame)
|
else if (x24_loadedFrame)
|
||||||
{
|
{
|
||||||
bool showFusionSuit = g_GameState->SystemOptions().GetPlayerLinkedFusion() &&
|
CFinalInput useInput = input;
|
||||||
g_GameState->SystemOptions().GetPlayerBeatNormalMode();
|
if (input.PZ())
|
||||||
|
{
|
||||||
|
useInput.x2d_b28_PA = true;
|
||||||
|
m_gbaOverride = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool showFusionSuit = (g_GameState->SystemOptions().GetPlayerLinkedFusion() &&
|
||||||
|
g_GameState->SystemOptions().GetPlayerBeatNormalMode()) || m_gbaOverride;
|
||||||
if (m_touchBar.GetPhase() != CFrontEndUITouchBar::EPhase::FusionBonus)
|
if (m_touchBar.GetPhase() != CFrontEndUITouchBar::EPhase::FusionBonus)
|
||||||
{
|
{
|
||||||
m_touchBar.SetFusionBonusPhase(showFusionSuit &&
|
m_touchBar.SetFusionBonusPhase(showFusionSuit &&
|
||||||
g_GameState->SystemOptions().GetPlayerFusionSuitActive());
|
g_GameState->SystemOptions().GetPlayerFusionSuitActive());
|
||||||
}
|
}
|
||||||
|
|
||||||
x24_loadedFrame->ProcessUserInput(input);
|
x24_loadedFrame->ProcessUserInput(useInput);
|
||||||
|
|
||||||
switch (tbAction)
|
switch (tbAction)
|
||||||
{
|
{
|
||||||
|
@ -1196,7 +1198,7 @@ void CFrontEndUI::SFusionBonusFrame::DoAdvance(CGuiTableGroup* caller)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
/* Fusion Suit */
|
/* Fusion Suit */
|
||||||
if (x3a_mpNotComplete)
|
if (x3a_mpNotComplete || m_gbaOverride)
|
||||||
{
|
{
|
||||||
x3a_mpNotComplete = false;
|
x3a_mpNotComplete = false;
|
||||||
PlayAdvanceSfx();
|
PlayAdvanceSfx();
|
||||||
|
@ -1216,12 +1218,12 @@ void CFrontEndUI::SFusionBonusFrame::DoAdvance(CGuiTableGroup* caller)
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
/* NES Metroid */
|
/* NES Metroid */
|
||||||
if (x39_fusionNotComplete)
|
if (x39_fusionNotComplete && !m_gbaOverride)
|
||||||
{
|
{
|
||||||
x39_fusionNotComplete = false;
|
x39_fusionNotComplete = false;
|
||||||
PlayAdvanceSfx();
|
PlayAdvanceSfx();
|
||||||
}
|
}
|
||||||
else if (g_GameState->SystemOptions().GetPlayerBeatFusion())
|
else if (g_GameState->SystemOptions().GetPlayerBeatFusion() || m_gbaOverride)
|
||||||
{
|
{
|
||||||
//x8_action = EAction::None;
|
//x8_action = EAction::None;
|
||||||
CSfxManager::SfxStart(1094, 1.f, 0.f, false, 0x7f, false, kInvalidAreaId);
|
CSfxManager::SfxStart(1094, 1.f, 0.f, false, 0x7f, false, kInvalidAreaId);
|
||||||
|
@ -1427,6 +1429,9 @@ CFrontEndUI::SNesEmulatorFrame::SNesEmulatorFrame()
|
||||||
xc_textSupport = std::make_unique<CGuiTextSupport>(deface->id, props, zeus::CColor::skWhite,
|
xc_textSupport = std::make_unique<CGuiTextSupport>(deface->id, props, zeus::CColor::skWhite,
|
||||||
zeus::CColor::skBlack, zeus::CColor::skWhite,
|
zeus::CColor::skBlack, zeus::CColor::skWhite,
|
||||||
0, 0, g_SimplePool, CGuiWidget::EGuiModelDrawFlags::Alpha);
|
0, 0, g_SimplePool, CGuiWidget::EGuiModelDrawFlags::Alpha);
|
||||||
|
xc_textSupport->SetText(g_MainStringTable->GetString(103));
|
||||||
|
xc_textSupport->AutoSetExtent();
|
||||||
|
xc_textSupport->ClearRenderBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CFrontEndUI::SNesEmulatorFrame::SetMode(EMode mode)
|
void CFrontEndUI::SNesEmulatorFrame::SetMode(EMode mode)
|
||||||
|
@ -1498,9 +1503,9 @@ bool CFrontEndUI::SNesEmulatorFrame::Update(float dt, CSaveGameScreen* saveUi)
|
||||||
case EMode::Emulator:
|
case EMode::Emulator:
|
||||||
{
|
{
|
||||||
x4_nesEmu->Update();
|
x4_nesEmu->Update();
|
||||||
if (!x4_nesEmu->WantsQuit())
|
if (!x4_nesEmu->IsGameOver())
|
||||||
x14_emulationSuspended = false;
|
x14_emulationSuspended = false;
|
||||||
if (x4_nesEmu->WantsQuit() && !x14_emulationSuspended)
|
if (x4_nesEmu->IsGameOver() && !x14_emulationSuspended)
|
||||||
{
|
{
|
||||||
x14_emulationSuspended = true;
|
x14_emulationSuspended = true;
|
||||||
if (saveUi && !saveUi->IsSavingDisabled())
|
if (saveUi && !saveUi->IsSavingDisabled())
|
||||||
|
@ -1511,8 +1516,8 @@ bool CFrontEndUI::SNesEmulatorFrame::Update(float dt, CSaveGameScreen* saveUi)
|
||||||
SetMode(EMode::ContinuePlaying);
|
SetMode(EMode::ContinuePlaying);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (x4_nesEmu->WantsLoad() && saveUi)
|
if (x4_nesEmu->GetPasswordEntryState() == CNESEmulator::EPasswordEntryState::NotEntered && saveUi)
|
||||||
x4_nesEmu->LoadState(g_GameState->SystemOptions().GetNESState());
|
x4_nesEmu->LoadPassword(g_GameState->SystemOptions().GetNESState());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1523,7 +1528,7 @@ bool CFrontEndUI::SNesEmulatorFrame::Update(float dt, CSaveGameScreen* saveUi)
|
||||||
EQuitAction action = x8_quitScreen->Update(dt);
|
EQuitAction action = x8_quitScreen->Update(dt);
|
||||||
if (action == EQuitAction::Yes)
|
if (action == EQuitAction::Yes)
|
||||||
{
|
{
|
||||||
memmove(g_GameState->SystemOptions().GetNESState(), x4_nesEmu->GetSaveState(), 18);
|
memmove(g_GameState->SystemOptions().GetNESState(), x4_nesEmu->GetPassword(), 18);
|
||||||
saveUi->SaveNESState();
|
saveUi->SaveNESState();
|
||||||
SetMode(EMode::ContinuePlaying);
|
SetMode(EMode::ContinuePlaying);
|
||||||
}
|
}
|
||||||
|
@ -2533,7 +2538,7 @@ static const float AudioFadeTimeB[] =
|
||||||
|
|
||||||
CIOWin::EMessageReturn CFrontEndUI::Update(float dt, CArchitectureQueue& queue)
|
CIOWin::EMessageReturn CFrontEndUI::Update(float dt, CArchitectureQueue& queue)
|
||||||
{
|
{
|
||||||
if (xdc_saveUI && (x50_curScreen >= EScreen::FileSelect || true))
|
if (xdc_saveUI && x50_curScreen >= EScreen::FileSelect)
|
||||||
{
|
{
|
||||||
switch (xdc_saveUI->Update(dt))
|
switch (xdc_saveUI->Update(dt))
|
||||||
{
|
{
|
||||||
|
@ -2636,11 +2641,6 @@ CIOWin::EMessageReturn CFrontEndUI::Update(float dt, CArchitectureQueue& queue)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
xec_emuFrme = std::make_unique<SNesEmulatorFrame>();
|
|
||||||
xdc_saveUI->ResetCardDriver();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xd2_deferSlideShow)
|
if (xd2_deferSlideShow)
|
||||||
{
|
{
|
||||||
|
|
|
@ -249,6 +249,8 @@ public:
|
||||||
|
|
||||||
CFrontEndUITouchBar& m_touchBar;
|
CFrontEndUITouchBar& m_touchBar;
|
||||||
|
|
||||||
|
bool m_gbaOverride = false;
|
||||||
|
|
||||||
SFusionBonusFrame(CFrontEndUITouchBar& touchBar);
|
SFusionBonusFrame(CFrontEndUITouchBar& touchBar);
|
||||||
void FinishedLoading();
|
void FinishedLoading();
|
||||||
bool PumpLoad();
|
bool PumpLoad();
|
||||||
|
|
|
@ -36,6 +36,7 @@ void CQuitGameScreen::SetColors()
|
||||||
void CQuitGameScreen::FinishedLoading()
|
void CQuitGameScreen::FinishedLoading()
|
||||||
{
|
{
|
||||||
x10_loadedFrame = x4_frame.GetObj();
|
x10_loadedFrame = x4_frame.GetObj();
|
||||||
|
x10_loadedFrame->SetMaxAspect(1.33f);
|
||||||
|
|
||||||
x14_tablegroup_quitgame = static_cast<CGuiTableGroup*>(
|
x14_tablegroup_quitgame = static_cast<CGuiTableGroup*>(
|
||||||
x10_loadedFrame->FindWidget("tablegroup_quitgame"));
|
x10_loadedFrame->FindWidget("tablegroup_quitgame"));
|
||||||
|
|
2
hecl
2
hecl
|
@ -1 +1 @@
|
||||||
Subproject commit 3454e81f97ae951b85cd4a9e18bb800f6123163e
|
Subproject commit e46ab0523f0b46992465f2b541d93f7cb49c4c77
|
Loading…
Reference in New Issue