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