Support two adjacent phases turned off in rapid sequence (#1110, PR #1116)

Fix for A2osX low-level format for 48-trackl, 192K floppy image
This commit is contained in:
TomCh 2022-06-20 20:40:39 +01:00 committed by GitHub
parent b3e617f1e5
commit de173b2729
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 136 additions and 11 deletions

View File

@ -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);

View File

@ -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];

View File

@ -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<Disk2InterfaceCard&>(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<NUM_DRIVES; i++)
{
@ -2214,5 +2307,8 @@ bool Disk2InterfaceCard::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT versi
GetFrame().FrameRefreshStatus(DRAW_LEDS | DRAW_BUTTON_DRIVES | DRAW_DISK_STATUS);
if (m_deferredStepperEvent)
InsertSyncEvent();
return true;
}

View File

@ -27,6 +27,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "DiskLog.h"
#include "DiskFormatTrack.h"
#include "DiskImage.h"
#include "SynchronousEventManager.h"
enum Drive_e
{
@ -206,6 +207,10 @@ private:
bool GetFirmware(WORD lpNameId, BYTE* pDst);
void InitFirmware(LPBYTE pCxRomPeripheral);
void UpdateLatchForEmptyDrive(FloppyDrive* pDrive);
void InsertSyncEvent(void);
static int SyncEventCallback(int id, int cycles, ULONG uExecutedCycles);
void ControlStepperDeferred(void);
void ControlStepperLogging(WORD address, unsigned __int64 cumulativeCycles);
void PreJitterCheck(int phase, BYTE latch);
void AddJitter(int phase, FloppyDisk& floppy);
@ -275,6 +280,11 @@ private:
SEQUENCER_FUNCTION m_seqFunc;
UINT m_dbgLatchDelayedCnt;
bool m_deferredStepperEvent;
WORD m_deferredStepperAddress;
unsigned __int64 m_deferredStepperCumulativeCycles;
SyncEvent m_syncEvent;
// Jitter (GH#930)
static const BYTE m_T00S00Pattern[];
UINT m_T00S00PatternIdx;

View File

@ -143,3 +143,8 @@ void Disk2CardManager::GetFilenameAndPathForSaveState(std::string& filename, std
}
}
}
void Disk2CardManager::SetStepperDefer(bool defer)
{
m_stepperDeferred = defer;
}

View File

@ -3,7 +3,7 @@
class Disk2CardManager
{
public:
Disk2CardManager(void) {}
Disk2CardManager(void) : m_stepperDeferred(true) {}
~Disk2CardManager(void) {}
bool IsConditionForFullSpeed(void);
@ -15,4 +15,9 @@ public:
void Destroy(void);
bool IsAnyFirmware13Sector(void);
void GetFilenameAndPathForSaveState(std::string& filename, std::string& path);
void SetStepperDefer(bool defer);
bool IsStepperDeferred(void) { return m_stepperDeferred; }
private:
bool m_stepperDeferred; // debug: can disable via cmd-line
};

View File

@ -833,6 +833,9 @@ static void RepeatInitialization(void)
if (g_cmdLine.bRemoveNoSlotClock)
MemRemoveNoSlotClock();
if (g_cmdLine.noDisk2StepperDefer)
GetCardMgr().GetDisk2CardMgr().SetStepperDefer(false);
// Call DebugInitialize() after SetCurrentImageDir()
DebugInitialize();
LogFileOutput("Main: DebugInitialize()\n");