diff --git a/source/CmdLine.cpp b/source/CmdLine.cpp index 12077d05..8d93d8ca 100644 --- a/source/CmdLine.cpp +++ b/source/CmdLine.cpp @@ -549,6 +549,10 @@ bool ProcessCmdLine(LPSTR lpCmdLine) lpNextArg = GetNextArg(lpNextArg); g_cmdLine.wavFileMockingboard = lpCmdLine; } + else if (strcmp(lpCmdLine, "-no-disk2-stepper-defer") == 0) // a debug switch (likely to be removed in a future version) + { + g_cmdLine.noDisk2StepperDefer = true; + } else // unsupported { LogFileOutput("Unsupported arg: %s\n", lpCmdLine); diff --git a/source/CmdLine.h b/source/CmdLine.h index 9bb4853d..0532cb42 100644 --- a/source/CmdLine.h +++ b/source/CmdLine.h @@ -22,6 +22,7 @@ struct CmdLine snesMaxAltControllerType[1] = false; supportDCD = false; enableDumpToRealPrinter = false; + noDisk2StepperDefer = false; szImageName_harddisk[HARDDISK_1] = NULL; szImageName_harddisk[HARDDISK_2] = NULL; szSnapshotName = NULL; @@ -63,6 +64,7 @@ struct CmdLine bool snesMaxAltControllerType[2]; bool supportDCD; bool enableDumpToRealPrinter; + bool noDisk2StepperDefer; // debug SS_CARDTYPE slotInsert[NUM_SLOTS]; LPCSTR szImageName_drive[NUM_SLOTS][NUM_DRIVES]; bool driveConnected[NUM_SLOTS][NUM_DRIVES]; diff --git a/source/Disk.cpp b/source/Disk.cpp index 5ac09cba..deaedb01 100644 --- a/source/Disk.cpp +++ b/source/Disk.cpp @@ -36,6 +36,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "Interface.h" #include "Core.h" +#include "CardManager.h" #include "CPU.h" #include "DiskImage.h" #include "Log.h" @@ -58,7 +59,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA const BYTE Disk2InterfaceCard::m_T00S00Pattern[] = {0xD5,0xAA,0x96,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xDE}; Disk2InterfaceCard::Disk2InterfaceCard(UINT slot) : - Card(CT_Disk2, slot) + Card(CT_Disk2, slot), + m_syncEvent(slot, 0, SyncEventCallback) // use slot# as "unique" id for Disk2InterfaceCards { if (m_slot != 5 && m_slot != 6) // fixme ThrowErrorInvalidSlot(); @@ -71,6 +73,9 @@ Disk2InterfaceCard::Disk2InterfaceCard(UINT slot) : m_diskLastReadLatchCycle = 0; m_enhanceDisk = true; m_is13SectorFirmware = false; + m_deferredStepperEvent = false; + m_deferredStepperAddress = 0; + m_deferredStepperCumulativeCycles = 0; ResetLogicStateSequencer(); @@ -88,6 +93,9 @@ Disk2InterfaceCard::~Disk2InterfaceCard(void) { EjectDiskInternal(DRIVE_1); EjectDiskInternal(DRIVE_2); + + if (m_syncEvent.m_active) + g_SynchronousEventMgr.Remove(m_syncEvent.m_id); } bool Disk2InterfaceCard::GetEnhanceDisk(void) { return m_enhanceDisk; } @@ -455,7 +463,6 @@ void __stdcall Disk2InterfaceCard::ControlMotor(WORD, WORD address, BYTE, BYTE, void __stdcall Disk2InterfaceCard::ControlStepper(WORD, WORD address, BYTE, BYTE, ULONG uExecutedCycles) { FloppyDrive* pDrive = &m_floppyDrive[m_currDrive]; - FloppyDisk* pFloppy = &pDrive->m_disk; if (!m_floppyMotorOn) // GH#525 { @@ -484,16 +491,76 @@ void __stdcall Disk2InterfaceCard::ControlStepper(WORD, WORD address, BYTE, BYTE m_magnetStates &= ~phase_bit; // phase off } -#if LOG_DISK_PHASES - const ULONG cycleDelta = (ULONG)(g_nCumulativeCycles - pDrive->m_lastStepperCycle); -#endif - pDrive->m_lastStepperCycle = g_nCumulativeCycles; + if (!GetCardMgr().GetDisk2CardMgr().IsStepperDeferred()) + { + m_deferredStepperAddress = address; + m_deferredStepperCumulativeCycles = g_nCumulativeCycles; + ControlStepperDeferred(); + return; + } + + if (m_syncEvent.m_active) + { + // Check for adjacent magnets being turned off/on in a very short interval (10 cycles is purely based on A2osX). (GH#1110) + // . also ProDOS rapidly turning off all 4 magnets. + g_SynchronousEventMgr.Remove(m_syncEvent.m_id); + m_deferredStepperEvent = false; + + int addrDelta = (m_deferredStepperAddress & 7) - (address & 7); + if (addrDelta < 0) addrDelta = -addrDelta; + if (addrDelta == 2 || addrDelta == 6) // adjacent magnets: both turned off or both turned on + { + if ((address & 1) == 0) // adjacent magnets off + { + // 2 adjacent magnets off in quick succession don't move the cog (GH#1110) + ControlStepperLogging(m_deferredStepperAddress, m_deferredStepperCumulativeCycles); + ControlStepperLogging(address, g_nCumulativeCycles); + return; + } + else // adjacent magnets on + { + // do nothing for now (TODO: check this) + } + } + + // complete the deferred stepper event + ControlStepperDeferred(); + } + + // defer the effect of changing the phase + m_deferredStepperAddress = address; + m_deferredStepperCumulativeCycles = g_nCumulativeCycles; + InsertSyncEvent(); + m_deferredStepperEvent = true; +} + +void Disk2InterfaceCard::InsertSyncEvent(void) +{ + m_syncEvent.m_cyclesRemaining = 10; // NB. same cycle delay for magnet off and on - but perhaps they take different times? + g_SynchronousEventMgr.Insert(&m_syncEvent); +} + +int Disk2InterfaceCard::SyncEventCallback(int id, int cycles, ULONG uExecutedCycles) +{ + Disk2InterfaceCard& disk2Card = dynamic_cast(GetCardMgr().GetRef(id)); + disk2Card.ControlStepperDeferred(); + return 0; // Don't repeat event +} + +void Disk2InterfaceCard::ControlStepperDeferred(void) +{ + m_deferredStepperEvent = false; + const WORD address = m_deferredStepperAddress; + + FloppyDrive* pDrive = &m_floppyDrive[m_currDrive]; + FloppyDisk* pFloppy = &pDrive->m_disk; // check for any stepping effect from a magnet // - move only when the magnet opposite the cog is off // - move in the direction of an adjacent magnet if one is on // - do not move if both adjacent magnets are on (ie. quarter track) - // momentum and timing are not accounted for ... maybe one day! + // - timing is accounted for in the case when "two phases [are] turned off in rapid sequence" (UTAIIe page 9-13) (GH#1110) + // momentum is not accounted for ... maybe one day! int direction = 0; if (m_magnetStates & (1 << ((pDrive->m_phase + 1) & 3))) direction += 1; @@ -529,9 +596,21 @@ void __stdcall Disk2InterfaceCard::ControlStepper(WORD, WORD address, BYTE, BYTE GetFrame().FrameDrawDiskStatus(); // Show track status (GH#201) } + ControlStepperLogging(address, m_deferredStepperCumulativeCycles); +} + +void Disk2InterfaceCard::ControlStepperLogging(WORD address, unsigned __int64 cumulativeCycles) +{ + FloppyDrive* pDrive = &m_floppyDrive[m_currDrive]; + +#if LOG_DISK_PHASES + const ULONG cycleDelta = (ULONG)(cumulativeCycles - pDrive->m_lastStepperCycle); +#endif + pDrive->m_lastStepperCycle = cumulativeCycles; // NB. Persisted to save-state + #if LOG_DISK_PHASES LOG_DISK("%08X: track $%s magnet-states %d%d%d%d phase %d %s address $%4X last-stepper %.3fms\r\n", - (UINT32)g_nCumulativeCycles, + (UINT32)cumulativeCycles, GetCurrentTrackString().c_str(), (m_magnetStates >> 3) & 1, (m_magnetStates >> 2) & 1, @@ -540,7 +619,7 @@ void __stdcall Disk2InterfaceCard::ControlStepper(WORD, WORD address, BYTE, BYTE (address >> 1) & 3, // phase (address & 1) ? "on " : "off", address, - ((float)cycleDelta)/(CLK_6502_NTSC/1000.0)); + ((float)cycleDelta) / (CLK_6502_NTSC / 1000.0)); #endif } @@ -1908,7 +1987,8 @@ BYTE __stdcall Disk2InterfaceCard::IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE // 5: Added: Sequencer Function // 6: Added: Drive Connected & Motor On Cycle // 7: Deprecated SS_YAML_KEY_LSS_RESET_SEQUENCER, SS_YAML_KEY_DISK_ACCESSED -static const UINT kUNIT_VERSION = 7; +// 8: Added: deferred stepper: event, address & cycle +static const UINT kUNIT_VERSION = 8; #define SS_YAML_VALUE_CARD_DISK2 "Disk][" @@ -1925,6 +2005,9 @@ static const UINT kUNIT_VERSION = 7; #define SS_YAML_KEY_LSS_LATCH_DELAY "LSS Latch Delay" #define SS_YAML_KEY_LSS_RESET_SEQUENCER "LSS Reset Sequencer" // deprecated at v7 #define SS_YAML_KEY_LSS_SEQUENCER_FUNCTION "LSS Sequencer Function" +#define SS_YAML_KEY_DEFERRED_STEPPER_EVENT "Deferred Stepper Event" +#define SS_YAML_KEY_DEFERRED_STEPPER_ADDRESS "Deferred Stepper Address" +#define SS_YAML_KEY_DEFERRED_STEPPER_CYCLE "Deferred Stepper Cycle" #define SS_YAML_KEY_DISK2UNIT "Unit" #define SS_YAML_KEY_DRIVE_CONNECTED "Drive Connected" @@ -2005,6 +2088,9 @@ void Disk2InterfaceCard::SaveSnapshot(YamlSaveHelper& yamlSaveHelper) yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_LSS_SHIFT_REG, m_shiftReg); // v4 yamlSaveHelper.SaveInt(SS_YAML_KEY_LSS_LATCH_DELAY, m_latchDelay); // v4 yamlSaveHelper.SaveInt(SS_YAML_KEY_LSS_SEQUENCER_FUNCTION, m_seqFunc.function); // v5 + yamlSaveHelper.SaveBool(SS_YAML_KEY_DEFERRED_STEPPER_EVENT, m_deferredStepperEvent); // v8 + yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_DEFERRED_STEPPER_ADDRESS, m_deferredStepperAddress); // v8 + yamlSaveHelper.SaveHexUint64(SS_YAML_KEY_DEFERRED_STEPPER_CYCLE, m_deferredStepperCumulativeCycles); // v8 m_formatTrack.SaveSnapshot(yamlSaveHelper); // v2 SaveSnapshotDriveUnit(yamlSaveHelper, DRIVE_1); @@ -2202,6 +2288,13 @@ bool Disk2InterfaceCard::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT versi (void) yamlLoadHelper.LoadBool(SS_YAML_KEY_DISK_ACCESSED); // deprecated - but retrieve the value to avoid the "State: Unknown key (Disk Accessed)" warning } + if (version >= 8) + { + m_deferredStepperEvent = yamlLoadHelper.LoadBool(SS_YAML_KEY_DEFERRED_STEPPER_EVENT); + m_deferredStepperAddress = yamlLoadHelper.LoadUint(SS_YAML_KEY_DEFERRED_STEPPER_ADDRESS); + m_deferredStepperCumulativeCycles = yamlLoadHelper.LoadUint64(SS_YAML_KEY_DEFERRED_STEPPER_CYCLE); + } + // Eject all disks first in case Drive-2 contains disk to be inserted into Drive-1 for (UINT i=0; i