Fix one shot paddle timers (fixes #985)

This commit is contained in:
tomcw 2021-10-04 22:08:37 +01:00
parent e43d188e0f
commit aa7d0cf240
3 changed files with 51 additions and 20 deletions

View File

@ -99,10 +99,10 @@ static DWORD joytype[2] = {J0C_JOYSTICK1, J1C_DISABLED}; // Emulation
static BOOL setbutton[3] = {0,0,0}; // Used when a mouse button is pressed/released
static int xpos[2] = {PDL_CENTRAL,PDL_CENTRAL};
static int ypos[2] = {PDL_CENTRAL,PDL_CENTRAL};
static int xpos[2] = { PDL_MAX,PDL_MAX };
static int ypos[2] = { PDL_MAX,PDL_MAX };
static unsigned __int64 g_nJoyCntrResetCycle = 0; // Abs cycle that joystick counters were reset
static UINT64 g_paddleInactiveCycle[4] = { 0 }; // Abs cycle that each paddle becomes inactive after PTRIG strobe
static short g_nPdlTrimX = 0;
static short g_nPdlTrimY = 0;
@ -671,20 +671,13 @@ static const double PDL_CNTR_INTERVAL = 2816.0 / 255.0; // 11.04 (From KEGS)
BYTE __stdcall JoyReadPosition(WORD programcounter, WORD address, BYTE, BYTE, ULONG nExecutedCycles)
{
int nJoyNum = (address & 2) ? 1 : 0; // $C064..$C067
CpuCalcCycles(nExecutedCycles);
ULONG nPdlPos = (address & 1) ? ypos[nJoyNum] : xpos[nJoyNum];
// This is from KEGS. It helps games like Championship Lode Runner & Boulderdash
if(nPdlPos >= 255)
nPdlPos = 280;
BOOL nPdlCntrActive = g_nCumulativeCycles <= (g_nJoyCntrResetCycle + (unsigned __int64) ((double)nPdlPos * PDL_CNTR_INTERVAL));
BOOL nPdlCntrActive = g_nCumulativeCycles <= g_paddleInactiveCycle[address & 3];
// If no joystick connected, then this is always active (GH#778)
if (joyinfo[joytype[nJoyNum]] == DEVICE_NONE)
const UINT joyNum = (address & 2) ? 1 : 0; // $C064..$C067
if (joyinfo[joytype[joyNum]] == DEVICE_NONE)
nPdlCntrActive = TRUE;
return MemReadFloatingBus(nPdlCntrActive, nExecutedCycles);
@ -702,12 +695,27 @@ void JoyReset()
void JoyResetPosition(ULONG nExecutedCycles)
{
CpuCalcCycles(nExecutedCycles);
g_nJoyCntrResetCycle = g_nCumulativeCycles;
if(joyinfo[joytype[0]] == DEVICE_JOYSTICK)
CheckJoystick0();
if((joyinfo[joytype[1]] == DEVICE_JOYSTICK) || (joyinfo[joytype[1]] == DEVICE_JOYSTICK_THUMBSTICK2))
CheckJoystick1();
// If any of the timers are still running then strobe has no effect (GH#985)
for (UINT pdl = 0; pdl < 4; pdl++)
{
if (g_nCumulativeCycles <= g_paddleInactiveCycle[pdl])
continue;
const UINT joyNum = (pdl & 2) ? 1 : 0;
UINT pdlPos = (pdl & 1) ? ypos[joyNum] : xpos[joyNum];
// This is from KEGS. It helps games like Championship Lode Runner & Boulderdash
if (pdlPos >= 255)
pdlPos = 280;
g_paddleInactiveCycle[pdl] = g_nCumulativeCycles + (UINT64)((double)pdlPos * PDL_CNTR_INTERVAL);
}
}
//===========================================================================
@ -990,6 +998,7 @@ void JoyportControl(const UINT uControl)
#define SS_YAML_KEY_JOY0TRIMY "Joystick0 TrimY"
#define SS_YAML_KEY_JOY1TRIMX "Joystick1 TrimX"
#define SS_YAML_KEY_JOY1TRIMY "Joystick1 TrimY"
#define SS_YAML_KEY_PDL_INACTIVE_CYCLE "Paddle%1d Inactive Cycle"
static std::string JoyGetSnapshotStructName(void)
{
@ -1000,23 +1009,44 @@ static std::string JoyGetSnapshotStructName(void)
void JoySaveSnapshot(YamlSaveHelper& yamlSaveHelper)
{
YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", JoyGetSnapshotStructName().c_str());
yamlSaveHelper.SaveHexUint64(SS_YAML_KEY_COUNTERRESETCYCLE, g_nJoyCntrResetCycle);
yamlSaveHelper.SaveInt(SS_YAML_KEY_JOY0TRIMX, JoyGetTrim(true));
yamlSaveHelper.SaveInt(SS_YAML_KEY_JOY0TRIMY, JoyGetTrim(false));
yamlSaveHelper.Save("%s: %d # not implemented yet\n", SS_YAML_KEY_JOY1TRIMX, 0); // not implemented yet
yamlSaveHelper.Save("%s: %d # not implemented yet\n", SS_YAML_KEY_JOY1TRIMY, 0); // not implemented yet
for (UINT n = 0; n < 4; n++)
{
char str[sizeof(SS_YAML_KEY_PDL_INACTIVE_CYCLE)+1];
sprintf_s(str, sizeof(str), SS_YAML_KEY_PDL_INACTIVE_CYCLE, n);
yamlSaveHelper.SaveHexUint64(str, g_paddleInactiveCycle[n]);
}
}
void JoyLoadSnapshot(YamlLoadHelper& yamlLoadHelper)
void JoyLoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version)
{
if (!yamlLoadHelper.GetSubMap(JoyGetSnapshotStructName()))
return;
g_nJoyCntrResetCycle = yamlLoadHelper.LoadUint64(SS_YAML_KEY_COUNTERRESETCYCLE);
JoySetTrim(yamlLoadHelper.LoadInt(SS_YAML_KEY_JOY0TRIMX), true);
JoySetTrim(yamlLoadHelper.LoadInt(SS_YAML_KEY_JOY0TRIMY), false);
yamlLoadHelper.LoadInt(SS_YAML_KEY_JOY1TRIMX); // dump value
yamlLoadHelper.LoadInt(SS_YAML_KEY_JOY1TRIMY); // dump value
if (version >= 7)
{
for (UINT n = 0; n < 4; n++)
{
char str[sizeof(SS_YAML_KEY_PDL_INACTIVE_CYCLE) + 1];
sprintf_s(str, sizeof(str), SS_YAML_KEY_PDL_INACTIVE_CYCLE, n);
g_paddleInactiveCycle[n] = yamlLoadHelper.LoadUint64(str);
}
}
else
{
UINT64 resetCycle = yamlLoadHelper.LoadUint64(SS_YAML_KEY_COUNTERRESETCYCLE);
for (UINT n = 0; n < 4; n++)
g_paddleInactiveCycle[n] = resetCycle;
}
yamlLoadHelper.PopMap();
}

View File

@ -28,7 +28,7 @@ void JoyportControl(const UINT uControl);
void JoySetHookAltKeys(bool hook);
void JoySetButtonVirtualKey(UINT button, UINT virtKey);
void JoySaveSnapshot(class YamlSaveHelper& yamlSaveHelper);
void JoyLoadSnapshot(class YamlLoadHelper& yamlLoadHelper);
void JoyLoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT version);
BYTE __stdcall JoyReadButton(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles);
BYTE __stdcall JoyReadPosition(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles);

View File

@ -74,7 +74,8 @@ static YamlHelper yamlHelper;
// v4: Extended: video (added 'Video Refresh Rate')
// v5: Extended: cpu (added 'Defer IRQ By 1 Opcode')
// v6: Added 'Unit Miscellaneous' for NoSlotClock(NSC)
#define UNIT_APPLE2_VER 6
// v7: Extended: joystick (added 'Paddle Inactive Cycle')
#define UNIT_APPLE2_VER 7
#define UNIT_SLOTS_VER 1
@ -283,7 +284,7 @@ static void ParseUnitApple2(YamlLoadHelper& yamlLoadHelper, UINT version)
CpuLoadSnapshot(yamlLoadHelper, version); // NB. Overrides default main CPU type
m_ConfigNew.m_CpuType = GetMainCpu();
JoyLoadSnapshot(yamlLoadHelper);
JoyLoadSnapshot(yamlLoadHelper, version);
KeybLoadSnapshot(yamlLoadHelper, version);
SpkrLoadSnapshot(yamlLoadHelper);
GetVideo().VideoLoadSnapshot(yamlLoadHelper, version);