Support (read-only) WOZ1/WOZ2 images (#544) (PR #653)

Supports:
- all "woz test images" v1.3 (WOZ1, WOZ2) are working, except 3.5"
- additionally: Frogger (spiradisc), Choplifter (not Enhanced //e!), Lode Runner, Marble Madness, Skyfox.
- woz images can be .gz or .zip compressed (ie. same as other supported images)
- save-state

Limitations:
- read-only, so WOZ images are forced to be write-protected
  . as a result, games that need r/w images won't work (Stickybear Town Builder, Wizardry)
- 5.25" only (not 3.5")
This commit is contained in:
TomCh 2019-07-05 23:01:19 +01:00 committed by GitHub
parent 73ce127eef
commit 4bc75093b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1133 additions and 262 deletions

View File

@ -138,6 +138,7 @@ void LogFileTimeUntilFirstKeyReadReset(void)
// Log the time from emulation restart/reboot until the first key read: BIT $C000 // Log the time from emulation restart/reboot until the first key read: BIT $C000
// . AZTEC.DSK (DOS 3.3) does prior LDY $C000 reads, but the BIT $C000 is at the "Press any key" message // . AZTEC.DSK (DOS 3.3) does prior LDY $C000 reads, but the BIT $C000 is at the "Press any key" message
// . Phasor1.dsk / ProDOS 1.1.1: PC=E797: B1 50: LDA ($50),Y / "Select an Option:" message // . Phasor1.dsk / ProDOS 1.1.1: PC=E797: B1 50: LDA ($50),Y / "Select an Option:" message
// . Rescue Raiders v1.3,v1.5: PC=895: LDA $C000 / boot to intro
void LogFileTimeUntilFirstKeyRead(void) void LogFileTimeUntilFirstKeyRead(void)
{ {
if (!g_fh || bLogKeyReadDone) if (!g_fh || bLogKeyReadDone)
@ -145,6 +146,7 @@ void LogFileTimeUntilFirstKeyRead(void)
if ( (mem[regs.pc-3] != 0x2C) // AZTEC: bit $c000 if ( (mem[regs.pc-3] != 0x2C) // AZTEC: bit $c000
&& !((regs.pc-2) == 0xE797 && mem[regs.pc-2] == 0xB1 && mem[regs.pc-1] == 0x50) // Phasor1: lda ($50),y && !((regs.pc-2) == 0xE797 && mem[regs.pc-2] == 0xB1 && mem[regs.pc-1] == 0x50) // Phasor1: lda ($50),y
&& !((regs.pc-3) == 0x0895 && mem[regs.pc-3] == 0xAD) // Rescue Raiders v1.3,v1.5: lda $c000
) )
return; return;
@ -1638,7 +1640,7 @@ int APIENTRY WinMain(HINSTANCE passinstance, HINSTANCE, LPSTR lpCmdLine, int)
} }
// Need to test if it's safe to call ResetMachineState(). In the meantime, just call DiskReset(): // Need to test if it's safe to call ResetMachineState(). In the meantime, just call DiskReset():
sg_Disk2Card.Reset(); // Switch from a booting A][+ to a non-autostart A][, so need to turn off floppy motor sg_Disk2Card.Reset(true); // Switch from a booting A][+ to a non-autostart A][, so need to turn off floppy motor
LogFileOutput("Main: DiskReset()\n"); LogFileOutput("Main: DiskReset()\n");
HD_Reset(); // GH#515 HD_Reset(); // GH#515
LogFileOutput("Main: HDDReset()\n"); LogFileOutput("Main: HDDReset()\n");

View File

@ -3727,15 +3727,16 @@ Update_t CmdDisk ( int nArgs)
if (nArgs > 2) if (nArgs > 2)
goto _Help; goto _Help;
int drive = sg_Disk2Card.GetCurrentDrive() + 1;
char buffer[200] = ""; char buffer[200] = "";
ConsoleBufferPushFormat(buffer, "D%d at T$%X (%d), phase $%X, offset $%X, %s", ConsoleBufferPushFormat(buffer, "D%d at T$%s, phase $%s, offset $%X, mask $%02X, extraCycles %.2f, %s",
drive, sg_Disk2Card.GetCurrentDrive() + 1,
sg_Disk2Card.GetCurrentTrack(), sg_Disk2Card.GetCurrentTrackString().c_str(),
sg_Disk2Card.GetCurrentTrack(), sg_Disk2Card.GetCurrentPhaseString().c_str(),
sg_Disk2Card.GetCurrentPhase(),
sg_Disk2Card.GetCurrentOffset(), sg_Disk2Card.GetCurrentOffset(),
sg_Disk2Card.GetCurrentState()); sg_Disk2Card.GetCurrentLSSBitMask(),
sg_Disk2Card.GetCurrentExtraCycles(),
sg_Disk2Card.GetCurrentState()
);
return ConsoleUpdate(); return ConsoleUpdate();
} }

View File

@ -56,18 +56,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Disk2InterfaceCard::Disk2InterfaceCard(void) Disk2InterfaceCard::Disk2InterfaceCard(void)
{ {
m_currDrive = 0; ResetSwitches();
m_floppyLatch = 0; m_floppyLatch = 0;
m_floppyMotorOn = 0;
m_floppyLoadMode = 0;
m_floppyWriteMode = 0;
m_phases = 0;
m_saveDiskImage = true; // Save the DiskImage name to Registry m_saveDiskImage = true; // Save the DiskImage name to Registry
m_slot = 0; m_slot = 0;
m_diskLastCycle = 0; m_diskLastCycle = 0;
m_diskLastReadLatchCycle = 0; m_diskLastReadLatchCycle = 0;
m_enhanceDisk = true; m_enhanceDisk = true;
ResetLogicStateSequencer();
// Debug: // Debug:
#if LOG_DISK_NIBBLES_USE_RUNTIME_VAR #if LOG_DISK_NIBBLES_USE_RUNTIME_VAR
m_bLogDisk_NibblesRW = false; m_bLogDisk_NibblesRW = false;
@ -82,11 +81,40 @@ bool Disk2InterfaceCard::GetEnhanceDisk(void) { return m_enhanceDisk; }
void Disk2InterfaceCard::SetEnhanceDisk(bool bEnhanceDisk) { m_enhanceDisk = bEnhanceDisk; } void Disk2InterfaceCard::SetEnhanceDisk(bool bEnhanceDisk) { m_enhanceDisk = bEnhanceDisk; }
int Disk2InterfaceCard::GetCurrentDrive(void) { return m_currDrive; } int Disk2InterfaceCard::GetCurrentDrive(void) { return m_currDrive; }
int Disk2InterfaceCard::GetCurrentTrack(void) { return m_floppyDrive[m_currDrive].m_track; } int Disk2InterfaceCard::GetCurrentTrack(void) { return ImagePhaseToTrack(m_floppyDrive[m_currDrive].m_disk.m_imagehandle, m_floppyDrive[m_currDrive].m_phasePrecise, false); }
int Disk2InterfaceCard::GetCurrentPhase(void) { return m_floppyDrive[m_currDrive].m_phase; } float Disk2InterfaceCard::GetCurrentPhase(void) { return m_floppyDrive[m_currDrive].m_phasePrecise; }
int Disk2InterfaceCard::GetCurrentOffset(void) { return m_floppyDrive[m_currDrive].m_disk.m_byte; } int Disk2InterfaceCard::GetCurrentOffset(void) { return m_floppyDrive[m_currDrive].m_disk.m_byte; }
int Disk2InterfaceCard::GetTrack(const int drive) { return m_floppyDrive[drive].m_track; } BYTE Disk2InterfaceCard::GetCurrentLSSBitMask(void) { return m_floppyDrive[m_currDrive].m_disk.m_bitMask; }
double Disk2InterfaceCard::GetCurrentExtraCycles(void) { return m_floppyDrive[m_currDrive].m_disk.m_extraCycles; }
int Disk2InterfaceCard::GetTrack(const int drive) { return ImagePhaseToTrack(m_floppyDrive[drive].m_disk.m_imagehandle, m_floppyDrive[m_currDrive].m_phasePrecise, false); }
std::string Disk2InterfaceCard::GetCurrentTrackString(void)
{
const UINT trackInt = (UINT)(m_floppyDrive[m_currDrive].m_phasePrecise / 2);
const float trackFrac = (m_floppyDrive[m_currDrive].m_phasePrecise / 2) - (float)trackInt;
char szInt[8] = "";
sprintf(szInt, "%02X", trackInt); // "$NN"
char szFrac[8] = "";
sprintf(szFrac, "%.02f", trackFrac); // "0.nn"
return std::string(szInt) + std::string(szFrac+1);
}
std::string Disk2InterfaceCard::GetCurrentPhaseString(void)
{
const UINT phaseInt = (UINT)(m_floppyDrive[m_currDrive].m_phasePrecise);
const float phaseFrac = m_floppyDrive[m_currDrive].m_phasePrecise - (float)phaseInt;
char szInt[8] = "";
sprintf(szInt, "%02X", phaseInt); // "$NN"
char szFrac[8] = "";
sprintf(szFrac, "%.02f", phaseFrac); // "0.nn"
return std::string(szInt) + std::string(szFrac+1);
}
LPCTSTR Disk2InterfaceCard::GetCurrentState(void) LPCTSTR Disk2InterfaceCard::GetCurrentState(void)
{ {
if (m_floppyDrive[m_currDrive].m_disk.m_imagehandle == NULL) if (m_floppyDrive[m_currDrive].m_disk.m_imagehandle == NULL)
@ -175,7 +203,7 @@ void Disk2InterfaceCard::SaveLastDiskImage(const int drive)
//=========================================================================== //===========================================================================
// Called by ControlMotor() & Enable() // Called by ControlMotor() & Enable()
void Disk2InterfaceCard::CheckSpinning(const ULONG nExecutedCycles) void Disk2InterfaceCard::CheckSpinning(const ULONG uExecutedCycles)
{ {
DWORD modechange = (m_floppyMotorOn && !m_floppyDrive[m_currDrive].m_spinning); DWORD modechange = (m_floppyMotorOn && !m_floppyDrive[m_currDrive].m_spinning);
@ -188,7 +216,7 @@ void Disk2InterfaceCard::CheckSpinning(const ULONG nExecutedCycles)
if (modechange) if (modechange)
{ {
// Set m_diskLastCycle when motor changes: not spinning (ie. off for 1 sec) -> on // Set m_diskLastCycle when motor changes: not spinning (ie. off for 1 sec) -> on
CpuCalcCycles(nExecutedCycles); CpuCalcCycles(uExecutedCycles);
m_diskLastCycle = g_nCumulativeCycles; m_diskLastCycle = g_nCumulativeCycles;
} }
} }
@ -237,7 +265,7 @@ void Disk2InterfaceCard::AllocTrack(const int drive)
//=========================================================================== //===========================================================================
void Disk2InterfaceCard::ReadTrack(const int drive) void Disk2InterfaceCard::ReadTrack(const int drive, ULONG uExecutedCycles)
{ {
if (! IsDriveValid( drive )) if (! IsDriveValid( drive ))
return; return;
@ -245,8 +273,9 @@ void Disk2InterfaceCard::ReadTrack(const int drive)
FloppyDrive* pDrive = &m_floppyDrive[ drive ]; FloppyDrive* pDrive = &m_floppyDrive[ drive ];
FloppyDisk* pFloppy = &pDrive->m_disk; FloppyDisk* pFloppy = &pDrive->m_disk;
if (pDrive->m_track >= ImageGetNumTracks(pFloppy->m_imagehandle)) if (ImagePhaseToTrack(pFloppy->m_imagehandle, pDrive->m_phasePrecise, false) >= ImageGetNumTracks(pFloppy->m_imagehandle))
{ {
_ASSERT(0); // What can cause this? Add a comment to replace this assert.
pFloppy->m_trackimagedata = false; pFloppy->m_trackimagedata = false;
return; return;
} }
@ -257,17 +286,45 @@ void Disk2InterfaceCard::ReadTrack(const int drive)
if (pFloppy->m_trackimage && pFloppy->m_imagehandle) if (pFloppy->m_trackimage && pFloppy->m_imagehandle)
{ {
#if LOG_DISK_TRACKS #if LOG_DISK_TRACKS
LOG_DISK("track $%02X%s read\r\n", pDrive->m_track, (pDrive->m_phase & 1) ? ".5" : " "); CpuCalcCycles(uExecutedCycles);
const ULONG cycleDelta = (ULONG)(g_nCumulativeCycles - pDrive->m_lastStepperCycle);
LOG_DISK("track $%s read (time since last stepper %.3fms)\r\n", GetCurrentTrackString().c_str(), ((float)cycleDelta) / (CLK_6502 / 1000.0));
#endif #endif
const UINT32 currentPosition = pFloppy->m_byte;
const UINT32 currentTrackLength = pFloppy->m_nibbles;
ImageReadTrack( ImageReadTrack(
pFloppy->m_imagehandle, pFloppy->m_imagehandle,
pDrive->m_track, pDrive->m_phasePrecise,
pDrive->m_phase,
pFloppy->m_trackimage, pFloppy->m_trackimage,
&pFloppy->m_nibbles, &pFloppy->m_nibbles,
&pFloppy->m_bitCount,
m_enhanceDisk); m_enhanceDisk);
pFloppy->m_byte = 0; if (!ImageIsWOZ(pFloppy->m_imagehandle) || (currentTrackLength == 0))
{
pFloppy->m_byte = 0;
}
else
{
_ASSERT(pFloppy->m_nibbles && pFloppy->m_bitCount);
if (pFloppy->m_nibbles == 0 || pFloppy->m_bitCount == 0)
{
pFloppy->m_nibbles = 1;
pFloppy->m_bitCount = 8;
}
pFloppy->m_byte = (currentPosition * pFloppy->m_nibbles) / currentTrackLength; // Ref: WOZ-1.01
if (pFloppy->m_byte == (pFloppy->m_nibbles-1)) // Last nibble may not be complete, so advance by 1 nibble
pFloppy->m_byte = 0;
pFloppy->m_bitOffset = pFloppy->m_byte*8;
pFloppy->m_bitMask = 1 << 7;
pFloppy->m_extraCycles = 0.0;
pDrive->m_headWindow = 0;
}
pFloppy->m_trackimagedata = (pFloppy->m_nibbles != 0); pFloppy->m_trackimagedata = (pFloppy->m_nibbles != 0);
} }
} }
@ -308,8 +365,11 @@ void Disk2InterfaceCard::WriteTrack(const int drive)
FloppyDrive* pDrive = &m_floppyDrive[ drive ]; FloppyDrive* pDrive = &m_floppyDrive[ drive ];
FloppyDisk* pFloppy = &pDrive->m_disk; FloppyDisk* pFloppy = &pDrive->m_disk;
if (pDrive->m_track >= ImageGetNumTracks(pFloppy->m_imagehandle)) if (ImagePhaseToTrack(pFloppy->m_imagehandle, pDrive->m_phasePrecise, false) >= ImageGetNumTracks(pFloppy->m_imagehandle))
{
_ASSERT(0); // What can cause this? Add a comment to replace this assert.
return; return;
}
if (pFloppy->m_bWriteProtected) if (pFloppy->m_bWriteProtected)
return; return;
@ -317,12 +377,11 @@ void Disk2InterfaceCard::WriteTrack(const int drive)
if (pFloppy->m_trackimage && pFloppy->m_imagehandle) if (pFloppy->m_trackimage && pFloppy->m_imagehandle)
{ {
#if LOG_DISK_TRACKS #if LOG_DISK_TRACKS
LOG_DISK("track $%02X%s write\r\n", pDrive->m_track, (pDrive->m_phase & 0) ? ".5" : " "); // TODO: hard-coded to whole tracks - see below (nickw) LOG_DISK("track $%s write\r\n", GetCurrentTrackString().c_str());
#endif #endif
ImageWriteTrack( ImageWriteTrack(
pFloppy->m_imagehandle, pFloppy->m_imagehandle,
pDrive->m_track, pDrive->m_phasePrecise,
pDrive->m_phase, // TODO: this should never be used; it's the current phase (half-track), not that of the track to be written (nickw)
pFloppy->m_trackimage, pFloppy->m_trackimage,
pFloppy->m_nibbles); pFloppy->m_nibbles);
} }
@ -359,7 +418,7 @@ void __stdcall Disk2InterfaceCard::ControlMotor(WORD, WORD address, BYTE, BYTE,
m_floppyMotorOn = newState; m_floppyMotorOn = newState;
// NB. Motor off doesn't reset the Command Decoder like reset. (UTAIIe figures 9.7 & 9.8 chip C2) // NB. Motor off doesn't reset the Command Decoder like reset. (UTAIIe figures 9.7 & 9.8 chip C2)
// - so it doesn't reset this state: m_floppyLoadMode, m_floppyWriteMode, m_phases // - so it doesn't reset this state: m_floppyLoadMode, m_floppyWriteMode, m_magnetStates
#if LOG_DISK_MOTOR #if LOG_DISK_MOTOR
LOG_DISK("motor %s\r\n", (m_floppyMotorOn) ? "on" : "off"); LOG_DISK("motor %s\r\n", (m_floppyMotorOn) ? "on" : "off");
#endif #endif
@ -388,68 +447,75 @@ void __stdcall Disk2InterfaceCard::ControlStepper(WORD, WORD address, BYTE, BYTE
#endif #endif
} }
int phase = (address >> 1) & 3; // update phases (magnet states)
int phase_bit = (1 << phase); {
const int phase = (address >> 1) & 3;
const int phase_bit = (1 << phase);
#if 1 // update the magnet states
// update the magnet states if (address & 1)
if (address & 1) m_magnetStates |= phase_bit; // phase on
{ else
// phase on m_magnetStates &= ~phase_bit; // phase off
m_phases |= phase_bit;
}
else
{
// phase off
m_phases &= ~phase_bit;
} }
CpuCalcCycles(uExecutedCycles);
#if LOG_DISK_PHASES
const ULONG cycleDelta = (ULONG)(g_nCumulativeCycles - pDrive->m_lastStepperCycle);
#endif
pDrive->m_lastStepperCycle = g_nCumulativeCycles;
// check for any stepping effect from a magnet // check for any stepping effect from a magnet
// - move only when the magnet opposite the cog is off // - move only when the magnet opposite the cog is off
// - move in the direction of an adjacent magnet if one is on // - move in the direction of an adjacent magnet if one is on
// - do not move if both adjacent magnets are on // - do not move if both adjacent magnets are on (ie. quarter track)
// momentum and timing are not accounted for ... maybe one day! // momentum and timing are not accounted for ... maybe one day!
int direction = 0; int direction = 0;
if (m_phases & (1 << ((pDrive->m_phase + 1) & 3))) if (m_magnetStates & (1 << ((pDrive->m_phase + 1) & 3)))
direction += 1; direction += 1;
if (m_phases & (1 << ((pDrive->m_phase + 3) & 3))) if (m_magnetStates & (1 << ((pDrive->m_phase + 3) & 3)))
direction -= 1; direction -= 1;
// apply magnet step, if any // Only calculate quarterDirection for WOZ, as NIB/DSK don't support half phases.
if (direction) int quarterDirection = 0;
if (ImageIsWOZ(pFloppy->m_imagehandle))
{ {
pDrive->m_phase = MAX(0, MIN(79, pDrive->m_phase + direction)); if ((m_magnetStates == 0xC || // 1100
const int nNumTracksInImage = ImageGetNumTracks(pFloppy->m_imagehandle); m_magnetStates == 0x6 || // 0110
const int newtrack = (nNumTracksInImage == 0) ? 0 m_magnetStates == 0x3 || // 0011
: MIN(nNumTracksInImage-1, pDrive->m_phase >> 1); // (round half tracks down) m_magnetStates == 0x9)) // 1001
if (newtrack != pDrive->m_track)
{ {
FlushCurrentTrack(m_currDrive); quarterDirection = direction;
pDrive->m_track = newtrack; direction = 0;
pFloppy->m_trackimagedata = false;
m_formatTrack.DriveNotWritingTrack();
} }
// Feature Request #201 Show track status
// https://github.com/AppleWin/AppleWin/issues/201
FrameDrawDiskStatus( (HDC)0 );
} }
#else
// substitute alternate stepping code here to test pDrive->m_phase = MAX(0, MIN(79, pDrive->m_phase + direction));
#endif float newPhasePrecise = (float)(pDrive->m_phase) + (float)quarterDirection * 0.5f;
if (newPhasePrecise < 0)
newPhasePrecise = 0;
// apply magnet step, if any
if (newPhasePrecise != pDrive->m_phasePrecise)
{
FlushCurrentTrack(m_currDrive);
pDrive->m_phasePrecise = newPhasePrecise;
pFloppy->m_trackimagedata = false;
m_formatTrack.DriveNotWritingTrack();
FrameDrawDiskStatus((HDC)0); // Show track status (GH#201)
}
#if LOG_DISK_PHASES #if LOG_DISK_PHASES
LOG_DISK("track $%02X%s phases %d%d%d%d phase %d %s address $%4X\r\n", LOG_DISK("track $%s magnet-states %d%d%d%d phase %d %s address $%4X last-stepper %.3fms\r\n",
pDrive->m_phase >> 1, GetCurrentTrackString().c_str(),
(pDrive->m_phase & 1) ? ".5" : " ", (m_magnetStates >> 3) & 1,
(m_phases >> 3) & 1, (m_magnetStates >> 2) & 1,
(m_phases >> 2) & 1, (m_magnetStates >> 1) & 1,
(m_phases >> 1) & 1, (m_magnetStates >> 0) & 1,
(m_phases >> 0) & 1, m_magnetStates,
phase,
(address & 1) ? "on " : "off", (address & 1) ? "on " : "off",
address); address,
((float)cycleDelta)/(CLK_6502/1000.0));
#endif #endif
} }
@ -797,14 +863,14 @@ bool Disk2InterfaceCard::LogWriteCheckSyncFF(ULONG& uCycleDelta)
//=========================================================================== //===========================================================================
void __stdcall Disk2InterfaceCard::ReadWrite(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles) void __stdcall Disk2InterfaceCard::ReadWrite(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG uExecutedCycles)
{ {
/* m_floppyLoadMode = 0; */ /* m_floppyLoadMode = 0; */
FloppyDrive* pDrive = &m_floppyDrive[m_currDrive]; FloppyDrive* pDrive = &m_floppyDrive[m_currDrive];
FloppyDisk* pFloppy = &pDrive->m_disk; FloppyDisk* pFloppy = &pDrive->m_disk;
if (!pFloppy->m_trackimagedata && pFloppy->m_imagehandle) if (!pFloppy->m_trackimagedata && pFloppy->m_imagehandle)
ReadTrack(m_currDrive); ReadTrack(m_currDrive, uExecutedCycles);
if (!pFloppy->m_trackimagedata) if (!pFloppy->m_trackimagedata)
{ {
@ -814,7 +880,7 @@ void __stdcall Disk2InterfaceCard::ReadWrite(WORD pc, WORD addr, BYTE bWrite, BY
// Improve precision of "authentic" drive mode - GH#125 // Improve precision of "authentic" drive mode - GH#125
UINT uSpinNibbleCount = 0; UINT uSpinNibbleCount = 0;
CpuCalcCycles(nExecutedCycles); // g_nCumulativeCycles required for uSpinNibbleCount & LogWriteCheckSyncFF() CpuCalcCycles(uExecutedCycles); // g_nCumulativeCycles required for uSpinNibbleCount & LogWriteCheckSyncFF()
if (!m_enhanceDisk && pDrive->m_spinning) if (!m_enhanceDisk && pDrive->m_spinning)
{ {
@ -877,6 +943,9 @@ void __stdcall Disk2InterfaceCard::ReadWrite(WORD pc, WORD addr, BYTE bWrite, BY
} }
else if (!pFloppy->m_bWriteProtected) // && m_floppyWriteMode else if (!pFloppy->m_bWriteProtected) // && m_floppyWriteMode
{ {
if (!pDrive->m_spinning)
return; // If not spinning then only 1 bit-cell gets written?
*(pFloppy->m_trackimage + pFloppy->m_byte) = m_floppyLatch; *(pFloppy->m_trackimage + pFloppy->m_byte) = m_floppyLatch;
pFloppy->m_trackimagedirty = true; pFloppy->m_trackimagedirty = true;
@ -911,17 +980,247 @@ void __stdcall Disk2InterfaceCard::ReadWrite(WORD pc, WORD addr, BYTE bWrite, BY
//=========================================================================== //===========================================================================
void Disk2InterfaceCard::ResetLogicStateSequencer(void)
{
m_shiftReg = 0;
m_latchDelay = 0;
m_resetSequencer = true;
m_dbgLatchDelayedCnt = 0;
}
void Disk2InterfaceCard::UpdateBitStreamPositionAndDiskCycle(const ULONG uExecutedCycles)
{
FloppyDisk& floppy = m_floppyDrive[m_currDrive].m_disk;
CpuCalcCycles(uExecutedCycles);
const UINT bitCellDelta = GetBitCellDelta(ImageGetOptimalBitTiming(floppy.m_imagehandle));
UpdateBitStreamPosition(floppy, bitCellDelta);
m_diskLastCycle = g_nCumulativeCycles;
}
UINT Disk2InterfaceCard::GetBitCellDelta(const BYTE optimalBitTiming)
{
FloppyDisk& floppy = m_floppyDrive[m_currDrive].m_disk;
// NB. m_extraCycles is needed to retain accuracy:
// . Read latch #1: 0-> 9: cycleDelta= 9, bitCellDelta=2, extraCycles=1
// . Read latch #2: 11->20: cycleDelta=11, bitCellDelta=2, extraCycles=3
// . Overall: 0->20: cycleDelta=20, bitCellDelta=5, extraCycles=0
UINT bitCellDelta;
#if 0
if (optimalBitTiming == 32)
{
const ULONG cycleDelta = (ULONG)(g_nCumulativeCycles - m_diskLastCycle) + (BYTE) m_extraCycles;
bitCellDelta = cycleDelta / 4; // DIV 4 for 4us per bit-cell
m_extraCycles = cycleDelta & 3; // MOD 4 : remainder carried forward for next time
}
else
#endif
{
const double cycleDelta = (double)(g_nCumulativeCycles - m_diskLastCycle) + floppy.m_extraCycles;
const double bitTime = 0.125 * (double)optimalBitTiming; // 125ns units
bitCellDelta = (UINT) floor( cycleDelta / bitTime );
floppy.m_extraCycles = (double)cycleDelta - ((double)bitCellDelta * bitTime);
}
return bitCellDelta;
}
void Disk2InterfaceCard::UpdateBitStreamPosition(FloppyDisk& floppy, const ULONG bitCellDelta)
{
_ASSERT(floppy.m_bitCount); // Should never happen - ReadTrack() will handle this
if (floppy.m_bitCount == 0)
return;
floppy.m_bitOffset += bitCellDelta;
if (floppy.m_bitOffset >= floppy.m_bitCount)
floppy.m_bitOffset %= floppy.m_bitCount;
UpdateBitStreamOffsets(floppy);
}
void Disk2InterfaceCard::UpdateBitStreamOffsets(FloppyDisk& floppy)
{
floppy.m_byte = floppy.m_bitOffset / 8;
const UINT remainder = 7 - (floppy.m_bitOffset & 7);
floppy.m_bitMask = 1 << remainder;
}
void __stdcall Disk2InterfaceCard::ReadWriteWOZ(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG uExecutedCycles)
{
/* m_floppyLoadMode = 0; */
FloppyDrive& drive = m_floppyDrive[m_currDrive];
FloppyDisk& floppy = drive.m_disk;
if (!floppy.m_trackimagedata && floppy.m_imagehandle)
ReadTrack(m_currDrive, uExecutedCycles);
if (!floppy.m_trackimagedata)
{
_ASSERT(0); // Can't happen for WOZ - ReadTrack() should return an empty track
m_floppyLatch = 0xFF;
return;
}
// Don't change latch if drive off after 1 second drive-off delay (UTAIIe page 9-13)
// "DRIVES OFF forces the data register to hold its present state." (UTAIIe page 9-12)
// Note: Sherwood Forest sets shift mode and reads with the drive off.
// TODO: And same for a write?
if (!drive.m_spinning) // GH#599
return;
CpuCalcCycles(uExecutedCycles);
// Skipping forward a large amount of bitcells means the bitstream will very likely be out-of-sync.
// The first 1-bit will produce a latch nibble, and this 1-bit is unlikely to be the nibble's high bit.
// So we need to ensure we run enough bits through the sequencer to re-sync.
// NB. For Planetfall 13 bitcells(NG) / 14 bitcells(OK)
const UINT significantBitCells = 50; // 5x 10-bit sync FF nibbles
UINT bitCellDelta = GetBitCellDelta(ImageGetOptimalBitTiming(floppy.m_imagehandle));
UINT bitCellRemainder;
if (bitCellDelta <= significantBitCells)
{
bitCellRemainder = bitCellDelta;
}
else
{
bitCellRemainder = significantBitCells;
bitCellDelta -= significantBitCells;
UpdateBitStreamPosition(floppy, bitCellDelta);
m_latchDelay = 0;
}
m_diskLastCycle = g_nCumulativeCycles;
//
if (!m_floppyWriteMode)
{
// m_diskLastReadLatchCycle = g_nCumulativeCycles; // Not used by WOZ (only by NIB)
#if LOG_DISK_NIBBLES_READ
bool newLatchData = false;
#endif
for (UINT i = 0; i < bitCellRemainder; i++)
{
BYTE n = floppy.m_trackimage[floppy.m_byte];
drive.m_headWindow <<= 1;
drive.m_headWindow |= (n & floppy.m_bitMask) ? 1 : 0;
BYTE outputBit = (drive.m_headWindow & 0xf) ? (drive.m_headWindow >> 1) & 1
: rand() & 1;
floppy.m_bitMask >>= 1;
if (!floppy.m_bitMask)
{
floppy.m_bitMask = 1 << 7;
floppy.m_byte++;
}
floppy.m_bitOffset++;
if (floppy.m_bitOffset == floppy.m_bitCount)
{
floppy.m_bitMask = 1 << 7;
floppy.m_bitOffset = 0;
floppy.m_byte = 0;
}
if (m_resetSequencer)
{
m_resetSequencer = false; // LSS takes some cycles to reset (ref?)
continue;
}
//
m_shiftReg <<= 1;
m_shiftReg |= outputBit;
if (m_latchDelay)
{
m_latchDelay -= 4;
if (m_latchDelay < 0)
m_latchDelay = 0;
if (m_shiftReg)
{
m_dbgLatchDelayedCnt = 0;
}
else // m_shiftReg==0
{
if (m_latchDelay == 0)
m_latchDelay = 4; // extend for another 4us
m_dbgLatchDelayedCnt++;
#if LOG_DISK_NIBBLES_READ
if (m_dbgLatchDelayedCnt >= 3)
{
LOG_DISK("read: latch held due to 0: PC=%04X, cnt=%02X\r\n", regs.pc, m_dbgLatchDelayedCnt);
}
#endif
}
}
if (!m_latchDelay)
{
#if LOG_DISK_NIBBLES_READ
if (newLatchData)
{
LOG_DISK("read skipped latch data: %04X = %02X\r\n", floppy.m_byte, m_floppyLatch);
newLatchData = false;
}
#endif
m_floppyLatch = m_shiftReg;
if (m_shiftReg & 0x80)
{
m_latchDelay = 7;
m_shiftReg = 0;
#if LOG_DISK_NIBBLES_READ
// May not actually be read by 6502 (eg. Prologue's CHKSUM 4&4 nibble pair), but still pass to the log's nibble reader
m_formatTrack.DecodeLatchNibbleRead(m_floppyLatch);
newLatchData = true;
#endif
}
}
}
#if LOG_DISK_NIBBLES_READ
if (m_floppyLatch & 0x80)
{
#if LOG_DISK_NIBBLES_USE_RUNTIME_VAR
if (m_bLogDisk_NibblesRW)
#endif
{
LOG_DISK("read %04X = %02X\r\n", floppy.m_byte, m_floppyLatch);
}
}
#endif
}
else if (!floppy.m_bWriteProtected) // && m_floppyWriteMode
{
//TODO
}
// Show track status (GH#201) - NB. Prevent flooding of forcing UI to redraw!!!
if ((floppy.m_byte & 0xFF) == 0)
FrameDrawDiskStatus( (HDC)0 );
}
//===========================================================================
void Disk2InterfaceCard::Reset(const bool bIsPowerCycle/*=false*/) void Disk2InterfaceCard::Reset(const bool bIsPowerCycle/*=false*/)
{ {
// RESET forces all switches off (UTAIIe Table 9.1) // RESET forces all switches off (UTAIIe Table 9.1)
m_currDrive = 0; ResetSwitches();
m_floppyMotorOn = 0;
m_floppyLoadMode = 0;
m_floppyWriteMode = 0;
m_phases = 0;
m_formatTrack.Reset(); m_formatTrack.Reset();
ResetLogicStateSequencer();
if (bIsPowerCycle) // GH#460 if (bIsPowerCycle) // GH#460
{ {
// NB. This doesn't affect the drive head (ie. drive's track position) // NB. This doesn't affect the drive head (ie. drive's track position)
@ -937,6 +1236,15 @@ void Disk2InterfaceCard::Reset(const bool bIsPowerCycle/*=false*/)
} }
} }
void Disk2InterfaceCard::ResetSwitches(void)
{
m_currDrive = 0;
m_floppyMotorOn = 0;
m_floppyLoadMode = 0;
m_floppyWriteMode = 0;
m_magnetStates = 0;
}
//=========================================================================== //===========================================================================
bool Disk2InterfaceCard::UserSelectNewDiskImage(const int drive, LPCSTR pszFilename/*=""*/) bool Disk2InterfaceCard::UserSelectNewDiskImage(const int drive, LPCSTR pszFilename/*=""*/)
@ -958,8 +1266,8 @@ bool Disk2InterfaceCard::UserSelectNewDiskImage(const int drive, LPCSTR pszFilen
ofn.lStructSize = sizeof(OPENFILENAME); ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = g_hFrameWindow; ofn.hwndOwner = g_hFrameWindow;
ofn.hInstance = g_hInstance; ofn.hInstance = g_hInstance;
ofn.lpstrFilter = TEXT("All Images\0*.bin;*.do;*.dsk;*.nib;*.po;*.gz;*.zip;*.2mg;*.2img;*.iie;*.apl\0") ofn.lpstrFilter = TEXT("All Images\0*.bin;*.do;*.dsk;*.nib;*.po;*.gz;*.woz;*.zip;*.2mg;*.2img;*.iie;*.apl\0")
TEXT("Disk Images (*.bin,*.do,*.dsk,*.nib,*.po,*.gz,*.zip,*.2mg,*.2img,*.iie)\0*.bin;*.do;*.dsk;*.nib;*.po;*.gz;*.zip;*.2mg;*.2img;*.iie\0") TEXT("Disk Images (*.bin,*.do,*.dsk,*.nib,*.po,*.gz,*.woz,*.zip,*.2mg,*.2img,*.iie)\0*.bin;*.do;*.dsk;*.nib;*.po;*.gz;*.woz;*.zip;*.2mg;*.2img;*.iie\0")
TEXT("All Files\0*.*\0"); TEXT("All Files\0*.*\0");
ofn.lpstrFile = filename; ofn.lpstrFile = filename;
ofn.nMaxFile = MAX_PATH; ofn.nMaxFile = MAX_PATH;
@ -990,7 +1298,7 @@ bool Disk2InterfaceCard::UserSelectNewDiskImage(const int drive, LPCSTR pszFilen
//=========================================================================== //===========================================================================
void __stdcall Disk2InterfaceCard::LoadWriteProtect(WORD, WORD, BYTE write, BYTE value, ULONG) void __stdcall Disk2InterfaceCard::LoadWriteProtect(WORD, WORD, BYTE write, BYTE value, ULONG uExecutedCycles)
{ {
/* m_floppyLoadMode = 1; */ /* m_floppyLoadMode = 1; */
@ -1007,11 +1315,22 @@ void __stdcall Disk2InterfaceCard::LoadWriteProtect(WORD, WORD, BYTE write, BYTE
// . write mode doesn't prevent reading write protect (GH#537): // . write mode doesn't prevent reading write protect (GH#537):
// "If for some reason the above write protect check were entered with the READ/WRITE switch in WRITE, // "If for some reason the above write protect check were entered with the READ/WRITE switch in WRITE,
// the write protect switch would still be read correctly" (UTAIIe page 9-21) // the write protect switch would still be read correctly" (UTAIIe page 9-21)
// . Sequencer "SR" (Shift Right) command only loads QA (bit7) of data register (UTAIIe page 9-21)
if (m_floppyDrive[m_currDrive].m_disk.m_bWriteProtected) if (m_floppyDrive[m_currDrive].m_disk.m_bWriteProtected)
m_floppyLatch |= 0x80; m_floppyLatch |= 0x80;
else else
m_floppyLatch &= 0x7F; m_floppyLatch &= 0x7F;
} }
if (ImageIsWOZ(m_floppyDrive[m_currDrive].m_disk.m_imagehandle))
{
#if LOG_DISK_NIBBLES_READ
LOG_DISK("reset LSS: ~PC=%04X\r\n", regs.pc);
#endif
ResetLogicStateSequencer(); // reset sequencer (Ref: WOZ-1.01)
// m_latchDelay = 7; // TODO: Treat like a regular $C0EC latch load?
UpdateBitStreamPositionAndDiskCycle(uExecutedCycles); // Fix E7-copy protection
}
} }
//=========================================================================== //===========================================================================
@ -1181,6 +1500,9 @@ BYTE __stdcall Disk2InterfaceCard::IORead(WORD pc, WORD addr, BYTE bWrite, BYTE
UINT uSlot = ((addr & 0xff) >> 4) - 8; UINT uSlot = ((addr & 0xff) >> 4) - 8;
Disk2InterfaceCard* pCard = (Disk2InterfaceCard*) MemGetSlotParameters(uSlot); Disk2InterfaceCard* pCard = (Disk2InterfaceCard*) MemGetSlotParameters(uSlot);
ImageInfo* pImage = pCard->m_floppyDrive[pCard->m_currDrive].m_disk.m_imagehandle;
bool isWOZ = ImageIsWOZ(pImage);
switch (addr & 0xF) switch (addr & 0xF)
{ {
case 0x0: pCard->ControlStepper(pc, addr, bWrite, d, nExecutedCycles); break; case 0x0: pCard->ControlStepper(pc, addr, bWrite, d, nExecutedCycles); break;
@ -1195,7 +1517,9 @@ BYTE __stdcall Disk2InterfaceCard::IORead(WORD pc, WORD addr, BYTE bWrite, BYTE
case 0x9: pCard->ControlMotor(pc, addr, bWrite, d, nExecutedCycles); break; case 0x9: pCard->ControlMotor(pc, addr, bWrite, d, nExecutedCycles); break;
case 0xA: pCard->Enable(pc, addr, bWrite, d, nExecutedCycles); break; case 0xA: pCard->Enable(pc, addr, bWrite, d, nExecutedCycles); break;
case 0xB: pCard->Enable(pc, addr, bWrite, d, nExecutedCycles); break; case 0xB: pCard->Enable(pc, addr, bWrite, d, nExecutedCycles); break;
case 0xC: pCard->ReadWrite(pc, addr, bWrite, d, nExecutedCycles); break; case 0xC: if (!isWOZ) pCard->ReadWrite(pc, addr, bWrite, d, nExecutedCycles);
else pCard->ReadWriteWOZ(pc, addr, bWrite, d, nExecutedCycles);
break;
case 0xD: pCard->LoadWriteProtect(pc, addr, bWrite, d, nExecutedCycles); break; case 0xD: pCard->LoadWriteProtect(pc, addr, bWrite, d, nExecutedCycles); break;
case 0xE: pCard->SetReadMode(pc, addr, bWrite, d, nExecutedCycles); break; case 0xE: pCard->SetReadMode(pc, addr, bWrite, d, nExecutedCycles); break;
case 0xF: pCard->SetWriteMode(pc, addr, bWrite, d, nExecutedCycles); break; case 0xF: pCard->SetWriteMode(pc, addr, bWrite, d, nExecutedCycles); break;
@ -1213,6 +1537,9 @@ BYTE __stdcall Disk2InterfaceCard::IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE
UINT uSlot = ((addr & 0xff) >> 4) - 8; UINT uSlot = ((addr & 0xff) >> 4) - 8;
Disk2InterfaceCard* pCard = (Disk2InterfaceCard*) MemGetSlotParameters(uSlot); Disk2InterfaceCard* pCard = (Disk2InterfaceCard*) MemGetSlotParameters(uSlot);
ImageInfo* pImage = pCard->m_floppyDrive[pCard->m_currDrive].m_disk.m_imagehandle;
bool isWOZ = ImageIsWOZ(pImage);
switch (addr & 0xF) switch (addr & 0xF)
{ {
case 0x0: pCard->ControlStepper(pc, addr, bWrite, d, nExecutedCycles); break; case 0x0: pCard->ControlStepper(pc, addr, bWrite, d, nExecutedCycles); break;
@ -1227,7 +1554,9 @@ BYTE __stdcall Disk2InterfaceCard::IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE
case 0x9: pCard->ControlMotor(pc, addr, bWrite, d, nExecutedCycles); break; case 0x9: pCard->ControlMotor(pc, addr, bWrite, d, nExecutedCycles); break;
case 0xA: pCard->Enable(pc, addr, bWrite, d, nExecutedCycles); break; case 0xA: pCard->Enable(pc, addr, bWrite, d, nExecutedCycles); break;
case 0xB: pCard->Enable(pc, addr, bWrite, d, nExecutedCycles); break; case 0xB: pCard->Enable(pc, addr, bWrite, d, nExecutedCycles); break;
case 0xC: pCard->ReadWrite(pc, addr, bWrite, d, nExecutedCycles); break; case 0xC: if (!isWOZ) pCard->ReadWrite(pc, addr, bWrite, d, nExecutedCycles);
else pCard->ReadWriteWOZ(pc, addr, bWrite, d, nExecutedCycles);
break;
case 0xD: pCard->LoadWriteProtect(pc, addr, bWrite, d, nExecutedCycles); break; case 0xD: pCard->LoadWriteProtect(pc, addr, bWrite, d, nExecutedCycles); break;
case 0xE: pCard->SetReadMode(pc, addr, bWrite, d, nExecutedCycles); break; case 0xE: pCard->SetReadMode(pc, addr, bWrite, d, nExecutedCycles); break;
case 0xF: pCard->SetWriteMode(pc, addr, bWrite, d, nExecutedCycles); break; case 0xF: pCard->SetWriteMode(pc, addr, bWrite, d, nExecutedCycles); break;
@ -1246,7 +1575,9 @@ BYTE __stdcall Disk2InterfaceCard::IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE
// Unit version history: // Unit version history:
// 2: Added: Format Track state & DiskLastCycle // 2: Added: Format Track state & DiskLastCycle
// 3: Added: DiskLastReadLatchCycle // 3: Added: DiskLastReadLatchCycle
static const UINT kUNIT_VERSION = 3; // 4: Added: WOZ state
// Split up 'Unit' putting some state into a new 'Floppy'
static const UINT kUNIT_VERSION = 4;
#define SS_YAML_VALUE_CARD_DISK2 "Disk][" #define SS_YAML_VALUE_CARD_DISK2 "Disk]["
@ -1259,16 +1590,27 @@ static const UINT kUNIT_VERSION = 3;
#define SS_YAML_KEY_FLOPPY_WRITE_MODE "Floppy Write Mode" #define SS_YAML_KEY_FLOPPY_WRITE_MODE "Floppy Write Mode"
#define SS_YAML_KEY_LAST_CYCLE "Last Cycle" #define SS_YAML_KEY_LAST_CYCLE "Last Cycle"
#define SS_YAML_KEY_LAST_READ_LATCH_CYCLE "Last Read Latch Cycle" #define SS_YAML_KEY_LAST_READ_LATCH_CYCLE "Last Read Latch Cycle"
#define SS_YAML_KEY_LSS_SHIFT_REG "LSS Shift Reg"
#define SS_YAML_KEY_LSS_LATCH_DELAY "LSS Latch Delay"
#define SS_YAML_KEY_LSS_RESET_SEQUENCER "LSS Reset Sequencer"
#define SS_YAML_KEY_DISK2UNIT "Unit" #define SS_YAML_KEY_DISK2UNIT "Unit"
#define SS_YAML_KEY_FILENAME "Filename" #define SS_YAML_KEY_FILENAME "Filename"
#define SS_YAML_KEY_TRACK "Track"
#define SS_YAML_KEY_PHASE "Phase" #define SS_YAML_KEY_PHASE "Phase"
#define SS_YAML_KEY_PHASE_PRECISE "Phase (precise)"
#define SS_YAML_KEY_TRACK "Track" // deprecated at v4
#define SS_YAML_KEY_HEAD_WINDOW "Head Window"
#define SS_YAML_KEY_LAST_STEPPER_CYCLE "Last Stepper Cycle"
#define SS_YAML_KEY_FLOPPY "Floppy"
#define SS_YAML_KEY_BYTE "Byte" #define SS_YAML_KEY_BYTE "Byte"
#define SS_YAML_KEY_NIBBLES "Nibbles"
#define SS_YAML_KEY_BIT_OFFSET "Bit Offset"
#define SS_YAML_KEY_BIT_COUNT "Bit Count"
#define SS_YAML_KEY_EXTRA_CYCLES "Extra Cycles"
#define SS_YAML_KEY_WRITE_PROTECTED "Write Protected" #define SS_YAML_KEY_WRITE_PROTECTED "Write Protected"
#define SS_YAML_KEY_SPINNING "Spinning" #define SS_YAML_KEY_SPINNING "Spinning"
#define SS_YAML_KEY_WRITE_LIGHT "Write Light" #define SS_YAML_KEY_WRITE_LIGHT "Write Light"
#define SS_YAML_KEY_NIBBLES "Nibbles"
#define SS_YAML_KEY_TRACK_IMAGE_DATA "Track Image Data" #define SS_YAML_KEY_TRACK_IMAGE_DATA "Track Image Data"
#define SS_YAML_KEY_TRACK_IMAGE_DIRTY "Track Image Dirty" #define SS_YAML_KEY_TRACK_IMAGE_DIRTY "Track Image Dirty"
#define SS_YAML_KEY_TRACK_IMAGE "Track Image" #define SS_YAML_KEY_TRACK_IMAGE "Track Image"
@ -1279,17 +1621,16 @@ std::string Disk2InterfaceCard::GetSnapshotCardName(void)
return name; return name;
} }
void Disk2InterfaceCard::SaveSnapshotDisk2Unit(YamlSaveHelper& yamlSaveHelper, UINT unit) void Disk2InterfaceCard::SaveSnapshotFloppy(YamlSaveHelper& yamlSaveHelper, UINT unit)
{ {
YamlSaveHelper::Label label(yamlSaveHelper, "%s%d:\n", SS_YAML_KEY_DISK2UNIT, unit); YamlSaveHelper::Label label(yamlSaveHelper, "%s:\n", SS_YAML_KEY_FLOPPY);
yamlSaveHelper.SaveString(SS_YAML_KEY_FILENAME, m_floppyDrive[unit].m_disk.m_fullname); yamlSaveHelper.SaveString(SS_YAML_KEY_FILENAME, m_floppyDrive[unit].m_disk.m_fullname);
yamlSaveHelper.SaveUint(SS_YAML_KEY_TRACK, m_floppyDrive[unit].m_track);
yamlSaveHelper.SaveUint(SS_YAML_KEY_PHASE, m_floppyDrive[unit].m_phase);
yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_BYTE, m_floppyDrive[unit].m_disk.m_byte); yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_BYTE, m_floppyDrive[unit].m_disk.m_byte);
yamlSaveHelper.SaveBool(SS_YAML_KEY_WRITE_PROTECTED, m_floppyDrive[unit].m_disk.m_bWriteProtected);
yamlSaveHelper.SaveUint(SS_YAML_KEY_SPINNING, m_floppyDrive[unit].m_spinning);
yamlSaveHelper.SaveUint(SS_YAML_KEY_WRITE_LIGHT, m_floppyDrive[unit].m_writelight);
yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_NIBBLES, m_floppyDrive[unit].m_disk.m_nibbles); yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_NIBBLES, m_floppyDrive[unit].m_disk.m_nibbles);
yamlSaveHelper.SaveHexUint32(SS_YAML_KEY_BIT_OFFSET, m_floppyDrive[unit].m_disk.m_bitOffset); // v4
yamlSaveHelper.SaveHexUint32(SS_YAML_KEY_BIT_COUNT, m_floppyDrive[unit].m_disk.m_bitCount); // v4
yamlSaveHelper.SaveDouble(SS_YAML_KEY_EXTRA_CYCLES, m_floppyDrive[unit].m_disk.m_extraCycles); // v4
yamlSaveHelper.SaveBool(SS_YAML_KEY_WRITE_PROTECTED, m_floppyDrive[unit].m_disk.m_bWriteProtected);
yamlSaveHelper.SaveUint(SS_YAML_KEY_TRACK_IMAGE_DATA, m_floppyDrive[unit].m_disk.m_trackimagedata); yamlSaveHelper.SaveUint(SS_YAML_KEY_TRACK_IMAGE_DATA, m_floppyDrive[unit].m_disk.m_trackimagedata);
yamlSaveHelper.SaveUint(SS_YAML_KEY_TRACK_IMAGE_DIRTY, m_floppyDrive[unit].m_disk.m_trackimagedirty); yamlSaveHelper.SaveUint(SS_YAML_KEY_TRACK_IMAGE_DIRTY, m_floppyDrive[unit].m_disk.m_trackimagedirty);
@ -1300,13 +1641,26 @@ void Disk2InterfaceCard::SaveSnapshotDisk2Unit(YamlSaveHelper& yamlSaveHelper, U
} }
} }
void Disk2InterfaceCard::SaveSnapshotDriveUnit(YamlSaveHelper& yamlSaveHelper, UINT unit)
{
YamlSaveHelper::Label label(yamlSaveHelper, "%s%d:\n", SS_YAML_KEY_DISK2UNIT, unit);
yamlSaveHelper.SaveUint(SS_YAML_KEY_PHASE, m_floppyDrive[unit].m_phase);
yamlSaveHelper.SaveFloat(SS_YAML_KEY_PHASE_PRECISE, m_floppyDrive[unit].m_phasePrecise); // v4
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_HEAD_WINDOW, m_floppyDrive[unit].m_headWindow); // v4
yamlSaveHelper.SaveHexUint64(SS_YAML_KEY_LAST_STEPPER_CYCLE, m_floppyDrive[unit].m_lastStepperCycle); // v4
yamlSaveHelper.SaveUint(SS_YAML_KEY_SPINNING, m_floppyDrive[unit].m_spinning);
yamlSaveHelper.SaveUint(SS_YAML_KEY_WRITE_LIGHT, m_floppyDrive[unit].m_writelight);
SaveSnapshotFloppy(yamlSaveHelper, unit);
}
void Disk2InterfaceCard::SaveSnapshot(class YamlSaveHelper& yamlSaveHelper) void Disk2InterfaceCard::SaveSnapshot(class YamlSaveHelper& yamlSaveHelper)
{ {
YamlSaveHelper::Slot slot(yamlSaveHelper, GetSnapshotCardName(), m_slot, kUNIT_VERSION); YamlSaveHelper::Slot slot(yamlSaveHelper, GetSnapshotCardName(), m_slot, kUNIT_VERSION);
YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE); YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE);
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_PHASES, m_phases);
yamlSaveHelper.SaveUint(SS_YAML_KEY_CURRENT_DRIVE, m_currDrive); yamlSaveHelper.SaveUint(SS_YAML_KEY_CURRENT_DRIVE, m_currDrive);
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_PHASES, m_magnetStates);
yamlSaveHelper.SaveBool(SS_YAML_KEY_DISK_ACCESSED, false); // deprecated yamlSaveHelper.SaveBool(SS_YAML_KEY_DISK_ACCESSED, false); // deprecated
yamlSaveHelper.SaveBool(SS_YAML_KEY_ENHANCE_DISK, m_enhanceDisk); yamlSaveHelper.SaveBool(SS_YAML_KEY_ENHANCE_DISK, m_enhanceDisk);
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_FLOPPY_LATCH, m_floppyLatch); yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_FLOPPY_LATCH, m_floppyLatch);
@ -1314,29 +1668,24 @@ void Disk2InterfaceCard::SaveSnapshot(class YamlSaveHelper& yamlSaveHelper)
yamlSaveHelper.SaveBool(SS_YAML_KEY_FLOPPY_WRITE_MODE, m_floppyWriteMode == TRUE); yamlSaveHelper.SaveBool(SS_YAML_KEY_FLOPPY_WRITE_MODE, m_floppyWriteMode == TRUE);
yamlSaveHelper.SaveHexUint64(SS_YAML_KEY_LAST_CYCLE, m_diskLastCycle); // v2 yamlSaveHelper.SaveHexUint64(SS_YAML_KEY_LAST_CYCLE, m_diskLastCycle); // v2
yamlSaveHelper.SaveHexUint64(SS_YAML_KEY_LAST_READ_LATCH_CYCLE, m_diskLastReadLatchCycle); // v3 yamlSaveHelper.SaveHexUint64(SS_YAML_KEY_LAST_READ_LATCH_CYCLE, m_diskLastReadLatchCycle); // v3
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_LSS_SHIFT_REG, m_shiftReg); // v4
yamlSaveHelper.SaveInt(SS_YAML_KEY_LSS_LATCH_DELAY, m_latchDelay); // v4
yamlSaveHelper.SaveBool(SS_YAML_KEY_LSS_RESET_SEQUENCER, m_resetSequencer); // v4
m_formatTrack.SaveSnapshot(yamlSaveHelper); // v2 m_formatTrack.SaveSnapshot(yamlSaveHelper); // v2
SaveSnapshotDisk2Unit(yamlSaveHelper, DRIVE_1); SaveSnapshotDriveUnit(yamlSaveHelper, DRIVE_1);
SaveSnapshotDisk2Unit(yamlSaveHelper, DRIVE_2); SaveSnapshotDriveUnit(yamlSaveHelper, DRIVE_2);
} }
void Disk2InterfaceCard::LoadSnapshotDriveUnit(YamlLoadHelper& yamlLoadHelper, UINT unit) bool Disk2InterfaceCard::LoadSnapshotFloppy(YamlLoadHelper& yamlLoadHelper, UINT unit, UINT version, std::vector<BYTE>& track)
{ {
std::string disk2UnitName = std::string(SS_YAML_KEY_DISK2UNIT) + (unit == DRIVE_1 ? std::string("0") : std::string("1"));
if (!yamlLoadHelper.GetSubMap(disk2UnitName))
throw std::string("Card: Expected key: ") + disk2UnitName;
bool bImageError = false;
m_floppyDrive[unit].m_disk.m_fullname[0] = 0;
m_floppyDrive[unit].m_disk.m_imagename[0] = 0;
m_floppyDrive[unit].m_disk.m_bWriteProtected = false; // Default to false (until image is successfully loaded below)
std::string filename = yamlLoadHelper.LoadString(SS_YAML_KEY_FILENAME); std::string filename = yamlLoadHelper.LoadString(SS_YAML_KEY_FILENAME);
if (!filename.empty()) bool bImageError = filename.empty();
if (!bImageError)
{ {
DWORD dwAttributes = GetFileAttributes(filename.c_str()); DWORD dwAttributes = GetFileAttributes(filename.c_str());
if(dwAttributes == INVALID_FILE_ATTRIBUTES) if (dwAttributes == INVALID_FILE_ATTRIBUTES)
{ {
// Get user to browse for file // Get user to browse for file
UserSelectNewDiskImage(unit, filename.c_str()); UserSelectNewDiskImage(unit, filename.c_str());
@ -1347,38 +1696,106 @@ void Disk2InterfaceCard::LoadSnapshotDriveUnit(YamlLoadHelper& yamlLoadHelper, U
bImageError = (dwAttributes == INVALID_FILE_ATTRIBUTES); bImageError = (dwAttributes == INVALID_FILE_ATTRIBUTES);
if (!bImageError) if (!bImageError)
{ {
if(InsertDisk(unit, filename.c_str(), dwAttributes & FILE_ATTRIBUTE_READONLY, IMAGE_DONT_CREATE) != eIMAGE_ERROR_NONE) if (InsertDisk(unit, filename.c_str(), dwAttributes & FILE_ATTRIBUTE_READONLY, IMAGE_DONT_CREATE) != eIMAGE_ERROR_NONE)
bImageError = true; bImageError = true;
// DiskInsert() zeros m_floppyDrive[unit], then sets up: // DiskInsert() zeros m_floppyDrive[unit], then sets up:
// . imagename // . m_imagename
// . fullname // . m_fullname
// . writeprotected // . m_bWriteProtected
} }
} }
m_floppyDrive[unit].m_track = yamlLoadHelper.LoadUint(SS_YAML_KEY_TRACK);
m_floppyDrive[unit].m_phase = yamlLoadHelper.LoadUint(SS_YAML_KEY_PHASE);
m_floppyDrive[unit].m_disk.m_byte = yamlLoadHelper.LoadUint(SS_YAML_KEY_BYTE);
yamlLoadHelper.LoadBool(SS_YAML_KEY_WRITE_PROTECTED); // Consume yamlLoadHelper.LoadBool(SS_YAML_KEY_WRITE_PROTECTED); // Consume
m_floppyDrive[unit].m_spinning = yamlLoadHelper.LoadUint(SS_YAML_KEY_SPINNING); m_floppyDrive[unit].m_disk.m_byte = yamlLoadHelper.LoadUint(SS_YAML_KEY_BYTE);
m_floppyDrive[unit].m_writelight = yamlLoadHelper.LoadUint(SS_YAML_KEY_WRITE_LIGHT); m_floppyDrive[unit].m_disk.m_nibbles = yamlLoadHelper.LoadUint(SS_YAML_KEY_NIBBLES);
m_floppyDrive[unit].m_disk.m_nibbles = yamlLoadHelper.LoadUint(SS_YAML_KEY_NIBBLES); m_floppyDrive[unit].m_disk.m_trackimagedata = yamlLoadHelper.LoadUint(SS_YAML_KEY_TRACK_IMAGE_DATA) ? true : false;
m_floppyDrive[unit].m_disk.m_trackimagedata = yamlLoadHelper.LoadUint(SS_YAML_KEY_TRACK_IMAGE_DATA) ? true : false; m_floppyDrive[unit].m_disk.m_trackimagedirty = yamlLoadHelper.LoadUint(SS_YAML_KEY_TRACK_IMAGE_DIRTY) ? true : false;
m_floppyDrive[unit].m_disk.m_trackimagedirty = yamlLoadHelper.LoadUint(SS_YAML_KEY_TRACK_IMAGE_DIRTY) ? true : false;
if (version >= 4)
{
m_floppyDrive[unit].m_disk.m_bitOffset = yamlLoadHelper.LoadUint(SS_YAML_KEY_BIT_OFFSET);
m_floppyDrive[unit].m_disk.m_bitCount = yamlLoadHelper.LoadUint(SS_YAML_KEY_BIT_COUNT);
m_floppyDrive[unit].m_disk.m_extraCycles = yamlLoadHelper.LoadDouble(SS_YAML_KEY_EXTRA_CYCLES);
if (m_floppyDrive[unit].m_disk.m_bitCount && (m_floppyDrive[unit].m_disk.m_bitOffset >= m_floppyDrive[unit].m_disk.m_bitCount))
throw std::string("Disk image: bitOffset >= bitCount");
if (ImageIsWOZ(m_floppyDrive[unit].m_disk.m_imagehandle))
UpdateBitStreamOffsets(m_floppyDrive[unit].m_disk); // overwrites m_byte, inits m_bitMask
}
std::vector<BYTE> track(NIBBLES_PER_TRACK);
if (yamlLoadHelper.GetSubMap(SS_YAML_KEY_TRACK_IMAGE)) if (yamlLoadHelper.GetSubMap(SS_YAML_KEY_TRACK_IMAGE))
{ {
yamlLoadHelper.LoadMemory(&track[0], NIBBLES_PER_TRACK); yamlLoadHelper.LoadMemory(&track[0], NIBBLES_PER_TRACK);
yamlLoadHelper.PopMap(); yamlLoadHelper.PopMap();
} }
return bImageError;
}
bool Disk2InterfaceCard::LoadSnapshotDriveUnitv3(YamlLoadHelper& yamlLoadHelper, UINT unit, UINT version, std::vector<BYTE>& track)
{
_ASSERT(version <= 3);
std::string disk2UnitName = std::string(SS_YAML_KEY_DISK2UNIT) + (unit == DRIVE_1 ? std::string("0") : std::string("1"));
if (!yamlLoadHelper.GetSubMap(disk2UnitName))
throw std::string("Card: Expected key: ") + disk2UnitName;
bool bImageError = LoadSnapshotFloppy(yamlLoadHelper, unit, version, track);
yamlLoadHelper.LoadUint(SS_YAML_KEY_TRACK); // consume
m_floppyDrive[unit].m_phase = yamlLoadHelper.LoadUint(SS_YAML_KEY_PHASE);
m_floppyDrive[unit].m_phasePrecise = (float) m_floppyDrive[unit].m_phase;
m_floppyDrive[unit].m_spinning = yamlLoadHelper.LoadUint(SS_YAML_KEY_SPINNING);
m_floppyDrive[unit].m_writelight = yamlLoadHelper.LoadUint(SS_YAML_KEY_WRITE_LIGHT);
yamlLoadHelper.PopMap();
return bImageError;
}
bool Disk2InterfaceCard::LoadSnapshotDriveUnitv4(YamlLoadHelper& yamlLoadHelper, UINT unit, UINT version, std::vector<BYTE>& track)
{
_ASSERT(version >= 4);
std::string disk2UnitName = std::string(SS_YAML_KEY_DISK2UNIT) + (unit == DRIVE_1 ? std::string("0") : std::string("1"));
if (!yamlLoadHelper.GetSubMap(disk2UnitName))
throw std::string("Card: Expected key: ") + disk2UnitName;
if (!yamlLoadHelper.GetSubMap(SS_YAML_KEY_FLOPPY))
throw std::string("Card: Expected key: ") + SS_YAML_KEY_FLOPPY;
bool bImageError = LoadSnapshotFloppy(yamlLoadHelper, unit, version, track);
yamlLoadHelper.PopMap(); yamlLoadHelper.PopMap();
// //
if (!filename.empty() && !bImageError) m_floppyDrive[unit].m_phase = yamlLoadHelper.LoadUint(SS_YAML_KEY_PHASE);
m_floppyDrive[unit].m_phasePrecise = yamlLoadHelper.LoadFloat(SS_YAML_KEY_PHASE_PRECISE);
m_floppyDrive[unit].m_headWindow = yamlLoadHelper.LoadUint(SS_YAML_KEY_HEAD_WINDOW) & 0xf;
m_floppyDrive[unit].m_lastStepperCycle = yamlLoadHelper.LoadUint64(SS_YAML_KEY_LAST_STEPPER_CYCLE);
m_floppyDrive[unit].m_spinning = yamlLoadHelper.LoadUint(SS_YAML_KEY_SPINNING);
m_floppyDrive[unit].m_writelight = yamlLoadHelper.LoadUint(SS_YAML_KEY_WRITE_LIGHT);
yamlLoadHelper.PopMap();
return bImageError;
}
void Disk2InterfaceCard::LoadSnapshotDriveUnit(YamlLoadHelper& yamlLoadHelper, UINT unit, UINT version)
{
bool bImageError = false;
std::vector<BYTE> track(NIBBLES_PER_TRACK);
if (version <= 3)
bImageError = LoadSnapshotDriveUnitv3(yamlLoadHelper, unit, version, track);
else
bImageError = LoadSnapshotDriveUnitv4(yamlLoadHelper, unit, version, track);
if (!bImageError)
{ {
if ((m_floppyDrive[unit].m_disk.m_trackimage == NULL) && m_floppyDrive[unit].m_disk.m_nibbles) if ((m_floppyDrive[unit].m_disk.m_trackimage == NULL) && m_floppyDrive[unit].m_disk.m_nibbles)
AllocTrack(unit); AllocTrack(unit);
@ -1391,9 +1808,9 @@ void Disk2InterfaceCard::LoadSnapshotDriveUnit(YamlLoadHelper& yamlLoadHelper, U
if (bImageError) if (bImageError)
{ {
m_floppyDrive[unit].m_disk.m_trackimagedata = false; m_floppyDrive[unit].m_disk.m_trackimagedata = false;
m_floppyDrive[unit].m_disk.m_trackimagedirty = false; m_floppyDrive[unit].m_disk.m_trackimagedirty = false;
m_floppyDrive[unit].m_disk.m_nibbles = 0; m_floppyDrive[unit].m_disk.m_nibbles = 0;
} }
} }
@ -1405,8 +1822,8 @@ bool Disk2InterfaceCard::LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT
if (version < 1 || version > kUNIT_VERSION) if (version < 1 || version > kUNIT_VERSION)
throw std::string("Card: wrong version"); throw std::string("Card: wrong version");
m_phases = yamlLoadHelper.LoadUint(SS_YAML_KEY_PHASES); m_currDrive = yamlLoadHelper.LoadUint(SS_YAML_KEY_CURRENT_DRIVE);
m_currDrive = yamlLoadHelper.LoadUint(SS_YAML_KEY_CURRENT_DRIVE); m_magnetStates = yamlLoadHelper.LoadUint(SS_YAML_KEY_PHASES);
(void) yamlLoadHelper.LoadBool(SS_YAML_KEY_DISK_ACCESSED); // deprecated - but retrieve the value to avoid the "State: Unknown key (Disk Accessed)" warning (void) yamlLoadHelper.LoadBool(SS_YAML_KEY_DISK_ACCESSED); // deprecated - but retrieve the value to avoid the "State: Unknown key (Disk Accessed)" warning
m_enhanceDisk = yamlLoadHelper.LoadBool(SS_YAML_KEY_ENHANCE_DISK); m_enhanceDisk = yamlLoadHelper.LoadBool(SS_YAML_KEY_ENHANCE_DISK);
m_floppyLatch = yamlLoadHelper.LoadUint(SS_YAML_KEY_FLOPPY_LATCH); m_floppyLatch = yamlLoadHelper.LoadUint(SS_YAML_KEY_FLOPPY_LATCH);
@ -1424,6 +1841,13 @@ bool Disk2InterfaceCard::LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT
m_diskLastReadLatchCycle = yamlLoadHelper.LoadUint64(SS_YAML_KEY_LAST_READ_LATCH_CYCLE); m_diskLastReadLatchCycle = yamlLoadHelper.LoadUint64(SS_YAML_KEY_LAST_READ_LATCH_CYCLE);
} }
if (version >= 4)
{
m_shiftReg = yamlLoadHelper.LoadUint(SS_YAML_KEY_LSS_SHIFT_REG) & 0xff;
m_latchDelay = yamlLoadHelper.LoadInt(SS_YAML_KEY_LSS_LATCH_DELAY);
m_resetSequencer = yamlLoadHelper.LoadBool(SS_YAML_KEY_LSS_RESET_SEQUENCER);
}
// Eject all disks first in case Drive-2 contains disk to be inserted into Drive-1 // Eject all disks first in case Drive-2 contains disk to be inserted into Drive-1
for (UINT i=0; i<NUM_DRIVES; i++) for (UINT i=0; i<NUM_DRIVES; i++)
{ {
@ -1431,8 +1855,8 @@ bool Disk2InterfaceCard::LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT
m_floppyDrive[i].clear(); m_floppyDrive[i].clear();
} }
LoadSnapshotDriveUnit(yamlLoadHelper, DRIVE_1); LoadSnapshotDriveUnit(yamlLoadHelper, DRIVE_1, version);
LoadSnapshotDriveUnit(yamlLoadHelper, DRIVE_2); LoadSnapshotDriveUnit(yamlLoadHelper, DRIVE_2, version);
FrameRefreshStatus(DRAW_LEDS | DRAW_BUTTON_DRIVES); FrameRefreshStatus(DRAW_LEDS | DRAW_BUTTON_DRIVES);

View File

@ -61,6 +61,10 @@ public:
// //
m_byte = 0; m_byte = 0;
m_nibbles = 0; m_nibbles = 0;
m_bitOffset = 0;
m_bitCount = 0;
m_bitMask = 1 << 7;
m_extraCycles = 0.0;
m_trackimage = NULL; m_trackimage = NULL;
m_trackimagedata = false; m_trackimagedata = false;
m_trackimagedirty = false; m_trackimagedirty = false;
@ -72,8 +76,12 @@ public:
std::string m_strFilenameInZip; // "" or <FILENAME.EXT> std::string m_strFilenameInZip; // "" or <FILENAME.EXT>
ImageInfo* m_imagehandle; // Init'd by InsertDisk() -> ImageOpen() ImageInfo* m_imagehandle; // Init'd by InsertDisk() -> ImageOpen()
bool m_bWriteProtected; bool m_bWriteProtected;
int m_byte; int m_byte; // byte offset
int m_nibbles; // Init'd by ReadTrack() -> ImageReadTrack() int m_nibbles; // # nibbles in track / Init'd by ReadTrack() -> ImageReadTrack()
UINT m_bitOffset; // bit offset
UINT m_bitCount; // # bits in track
BYTE m_bitMask;
double m_extraCycles;
LPBYTE m_trackimage; LPBYTE m_trackimage;
bool m_trackimagedata; bool m_trackimagedata;
bool m_trackimagedirty; bool m_trackimagedirty;
@ -91,16 +99,20 @@ public:
void clear() void clear()
{ {
m_phasePrecise = 0;
m_phase = 0; m_phase = 0;
m_track = 0; m_lastStepperCycle = 0;
m_headWindow = 0;
m_spinning = 0; m_spinning = 0;
m_writelight = 0; m_writelight = 0;
m_disk.clear(); m_disk.clear();
} }
public: public:
int m_phase; float m_phasePrecise; // Phase precise to half a phase (aka quarter track)
int m_track; int m_phase; // Integral phase number
unsigned __int64 m_lastStepperCycle;
BYTE m_headWindow;
DWORD m_spinning; DWORD m_spinning;
DWORD m_writelight; DWORD m_writelight;
FloppyDisk m_disk; FloppyDisk m_disk;
@ -132,10 +144,14 @@ public:
bool GetProtect(const int drive); bool GetProtect(const int drive);
void SetProtect(const int drive, const bool bWriteProtect); void SetProtect(const int drive, const bool bWriteProtect);
int GetCurrentDrive(void); int GetCurrentDrive(void);
int GetCurrentTrack(); int GetCurrentTrack(void);
int GetTrack(const int drive); float GetCurrentPhase(void);
int GetCurrentPhase(void);
int GetCurrentOffset(void); int GetCurrentOffset(void);
BYTE GetCurrentLSSBitMask(void);
double GetCurrentExtraCycles(void);
int GetTrack(const int drive);
std::string GetCurrentTrackString(void);
std::string GetCurrentPhaseString(void);
LPCTSTR GetCurrentState(void); LPCTSTR GetCurrentState(void);
bool UserSelectNewDiskImage(const int drive, LPCSTR pszFilename=""); bool UserSelectNewDiskImage(const int drive, LPCSTR pszFilename="");
void UpdateDriveState(DWORD cycles); void UpdateDriveState(DWORD cycles);
@ -158,21 +174,32 @@ public:
static BYTE __stdcall IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles); static BYTE __stdcall IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles);
private: private:
void CheckSpinning(const ULONG nExecutedCycles); void ResetSwitches(void);
void CheckSpinning(const ULONG uExecutedCycles);
Disk_Status_e GetDriveLightStatus(const int drive); Disk_Status_e GetDriveLightStatus(const int drive);
bool IsDriveValid(const int drive); bool IsDriveValid(const int drive);
void AllocTrack(const int drive); void AllocTrack(const int drive);
void ReadTrack(const int drive); void ReadTrack(const int drive, ULONG uExecutedCycles);
void RemoveDisk(const int drive); void RemoveDisk(const int drive);
void WriteTrack(const int drive); void WriteTrack(const int drive);
LPCTSTR DiskGetFullPathName(const int drive); LPCTSTR DiskGetFullPathName(const int drive);
void SaveSnapshotDisk2Unit(YamlSaveHelper& yamlSaveHelper, UINT unit); void ResetLogicStateSequencer(void);
void LoadSnapshotDriveUnit(YamlLoadHelper& yamlLoadHelper, UINT unit); void UpdateBitStreamPositionAndDiskCycle(const ULONG uExecutedCycles);
UINT GetBitCellDelta(const BYTE optimalBitTiming);
void UpdateBitStreamPosition(FloppyDisk& floppy, const ULONG bitCellDelta);
void UpdateBitStreamOffsets(FloppyDisk& floppy);
void SaveSnapshotFloppy(YamlSaveHelper& yamlSaveHelper, UINT unit);
void SaveSnapshotDriveUnit(YamlSaveHelper& yamlSaveHelper, UINT unit);
bool LoadSnapshotFloppy(YamlLoadHelper& yamlLoadHelper, UINT unit, UINT version, std::vector<BYTE>& track);
bool LoadSnapshotDriveUnitv3(YamlLoadHelper& yamlLoadHelper, UINT unit, UINT version, std::vector<BYTE>& track);
bool LoadSnapshotDriveUnitv4(YamlLoadHelper& yamlLoadHelper, UINT unit, UINT version, std::vector<BYTE>& track);
void LoadSnapshotDriveUnit(YamlLoadHelper& yamlLoadHelper, UINT unit, UINT version);
void __stdcall ControlStepper(WORD, WORD address, BYTE, BYTE, ULONG uExecutedCycles); void __stdcall ControlStepper(WORD, WORD address, BYTE, BYTE, ULONG uExecutedCycles);
void __stdcall ControlMotor(WORD, WORD address, BYTE, BYTE, ULONG uExecutedCycles); void __stdcall ControlMotor(WORD, WORD address, BYTE, BYTE, ULONG uExecutedCycles);
void __stdcall Enable(WORD, WORD address, BYTE, BYTE, ULONG uExecutedCycles); void __stdcall Enable(WORD, WORD address, BYTE, BYTE, ULONG uExecutedCycles);
void __stdcall ReadWrite(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles); void __stdcall ReadWrite(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG uExecutedCycles);
void __stdcall ReadWriteWOZ(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG uExecutedCycles);
void __stdcall LoadWriteProtect(WORD, WORD, BYTE write, BYTE value, ULONG); void __stdcall LoadWriteProtect(WORD, WORD, BYTE write, BYTE value, ULONG);
void __stdcall SetReadMode(WORD, WORD, BYTE, BYTE, ULONG); void __stdcall SetReadMode(WORD, WORD, BYTE, BYTE, ULONG);
void __stdcall SetWriteMode(WORD, WORD, BYTE, BYTE, ULONG uExecutedCycles); void __stdcall SetWriteMode(WORD, WORD, BYTE, BYTE, ULONG uExecutedCycles);
@ -189,7 +216,11 @@ private:
BOOL m_floppyMotorOn; BOOL m_floppyMotorOn;
BOOL m_floppyLoadMode; // for efficiency this is not used; it's extremely unlikely to affect emulation (nickw) BOOL m_floppyLoadMode; // for efficiency this is not used; it's extremely unlikely to affect emulation (nickw)
BOOL m_floppyWriteMode; BOOL m_floppyWriteMode;
WORD m_phases; // state bits for stepper magnet phases 0 - 3
// Although the magnets are a property of the drive, their state is a property of the controller card,
// since the magnets will only be on for whichever of the 2 drives is currently selected.
WORD m_magnetStates; // state bits for stepper motor magnet states (phases 0 - 3)
bool m_saveDiskImage; bool m_saveDiskImage;
UINT m_slot; UINT m_slot;
unsigned __int64 m_diskLastCycle; unsigned __int64 m_diskLastCycle;
@ -197,8 +228,14 @@ private:
FormatTrack m_formatTrack; FormatTrack m_formatTrack;
bool m_enhanceDisk; bool m_enhanceDisk;
static const UINT SPINNING_CYCLES = 20000*64; // 1280000 cycles = 1.25s static const UINT SPINNING_CYCLES = 1000*1000; // 1M cycles = ~1.000s
static const UINT WRITELIGHT_CYCLES = 20000*64; // 1280000 cycles = 1.25s static const UINT WRITELIGHT_CYCLES = 1000*1000; // 1M cycles = ~1.000s
// Logic State Sequencer (for WOZ):
BYTE m_shiftReg;
int m_latchDelay;
bool m_resetSequencer;
UINT m_dbgLatchDelayedCnt;
// Debug: // Debug:
#if LOG_DISK_NIBBLES_USE_RUNTIME_VAR #if LOG_DISK_NIBBLES_USE_RUNTIME_VAR

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
#define NIBBLES_PER_TRACK 0x1A00 #define NIBBLES_PER_TRACK_NIB 0x1A00
#define NIBBLES_PER_TRACK_WOZ2 0x1A18 // 6680
#define NIBBLES_PER_TRACK NIBBLES_PER_TRACK_WOZ2 // MAX(NIBBLES_PER_TRACK_NIB, NIBBLES_PER_TRACK_WOZ2)
const UINT NUM_SECTORS = 16; const UINT NUM_SECTORS = 16;

View File

@ -264,17 +264,18 @@ void FormatTrack::DecodeLatchNibble(BYTE floppylatch, bool bIsWrite, bool bIsSyn
m_VolTrkSecChk[i] = ((m_VolTrkSecChk4and4[i*2] & 0x55) << 1) | (m_VolTrkSecChk4and4[i*2+1] & 0x55); m_VolTrkSecChk[i] = ((m_VolTrkSecChk4and4[i*2] & 0x55) << 1) | (m_VolTrkSecChk4and4[i*2+1] & 0x55);
#if LOG_DISK_NIBBLES_READ #if LOG_DISK_NIBBLES_READ
const bool chk = (m_VolTrkSecChk[0] ^ m_VolTrkSecChk[1] ^ m_VolTrkSecChk[2] ^ m_VolTrkSecChk[3]) == 0;
if (!bIsWrite) if (!bIsWrite)
{ {
BYTE addrPrologue = m_bAddressPrologueIsDOS3_2 ? (BYTE)kADDR_PROLOGUE_DOS3_2 : (BYTE)kADDR_PROLOGUE_DOS3_3; BYTE addrPrologue = m_bAddressPrologueIsDOS3_2 ? (BYTE)kADDR_PROLOGUE_DOS3_2 : (BYTE)kADDR_PROLOGUE_DOS3_3;
LOG_DISK("read D5AA%02X detected - Vol:%02X Trk:%02X Sec:%02X Chk:%02X\r\n", addrPrologue, m_VolTrkSecChk[0], m_VolTrkSecChk[1], m_VolTrkSecChk[2], m_VolTrkSecChk[3]); LOG_DISK("read D5AA%02X detected - Vol:%02X Trk:%02X Sec:%02X Chk:%02X %s\r\n", addrPrologue, m_VolTrkSecChk[0], m_VolTrkSecChk[1], m_VolTrkSecChk[2], m_VolTrkSecChk[3], chk?"":"(bad)");
} }
#endif #endif
#if LOG_DISK_NIBBLES_WRITE #if LOG_DISK_NIBBLES_WRITE
if (bIsWrite) if (bIsWrite)
{ {
BYTE addrPrologue = m_bAddressPrologueIsDOS3_2 ? (BYTE)kADDR_PROLOGUE_DOS3_2 : (BYTE)kADDR_PROLOGUE_DOS3_3; BYTE addrPrologue = m_bAddressPrologueIsDOS3_2 ? (BYTE)kADDR_PROLOGUE_DOS3_2 : (BYTE)kADDR_PROLOGUE_DOS3_3;
LOG_DISK("write D5AA%02X detected - Vol:%02X Trk:%02X Sec:%02X Chk:%02X\r\n", addrPrologue, m_VolTrkSecChk[0], m_VolTrkSecChk[1], m_VolTrkSecChk[2], m_VolTrkSecChk[3]); LOG_DISK("write D5AA%02X detected - Vol:%02X Trk:%02X Sec:%02X Chk:%02X %s\r\n", addrPrologue, m_VolTrkSecChk[0], m_VolTrkSecChk[1], m_VolTrkSecChk[2], m_VolTrkSecChk[3], chk?"":"(bad)");
} }
#endif #endif

View File

@ -28,6 +28,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "StdAfx.h" #include "StdAfx.h"
#include "Common.h"
#include "DiskImage.h" #include "DiskImage.h"
#include "DiskImageHelper.h" #include "DiskImageHelper.h"
@ -152,19 +153,21 @@ void ImageInitialize(void)
//=========================================================================== //===========================================================================
void ImageReadTrack( ImageInfo* const pImageInfo, void ImageReadTrack( ImageInfo* const pImageInfo,
const int nTrack, float phase, // phase [0..79] +/- 0.5
const int nQuarterTrack,
LPBYTE pTrackImageBuffer, LPBYTE pTrackImageBuffer,
int* pNibbles, int* pNibbles,
UINT* pBitCount,
bool enhanceDisk) bool enhanceDisk)
{ {
_ASSERT(nTrack >= 0); _ASSERT(phase >= 0);
if (nTrack < 0) if (phase < 0)
return; phase = 0;
if (pImageInfo->pImageType->AllowRW() && pImageInfo->ValidTrack[nTrack]) const UINT track = pImageInfo->pImageType->PhaseToTrack(phase);
if (pImageInfo->pImageType->AllowRW() && pImageInfo->ValidTrack[track])
{ {
pImageInfo->pImageType->Read(pImageInfo, nTrack, nQuarterTrack, pTrackImageBuffer, pNibbles, enhanceDisk); pImageInfo->pImageType->Read(pImageInfo, phase, pTrackImageBuffer, pNibbles, pBitCount, enhanceDisk);
} }
else else
{ {
@ -176,19 +179,20 @@ void ImageReadTrack( ImageInfo* const pImageInfo,
//=========================================================================== //===========================================================================
void ImageWriteTrack( ImageInfo* const pImageInfo, void ImageWriteTrack( ImageInfo* const pImageInfo,
const int nTrack, float phase, // phase [0..79] +/- 0.5
const int nQuarterTrack, LPBYTE pTrackImageBuffer,
LPBYTE pTrackImage,
const int nNibbles) const int nNibbles)
{ {
_ASSERT(nTrack >= 0); _ASSERT(phase >= 0);
if (nTrack < 0) if (phase < 0)
return; phase = 0;
const UINT track = pImageInfo->pImageType->PhaseToTrack(phase);
if (pImageInfo->pImageType->AllowRW() && !pImageInfo->bWriteProtected) if (pImageInfo->pImageType->AllowRW() && !pImageInfo->bWriteProtected)
{ {
pImageInfo->pImageType->Write(pImageInfo, nTrack, nQuarterTrack, pTrackImage, nNibbles); pImageInfo->pImageType->Write(pImageInfo, phase, pTrackImageBuffer, nNibbles);
pImageInfo->ValidTrack[nTrack] = 1; pImageInfo->ValidTrack[track] = 1;
} }
} }
@ -220,7 +224,7 @@ bool ImageWriteBlock( ImageInfo* const pImageInfo,
//=========================================================================== //===========================================================================
int ImageGetNumTracks(ImageInfo* const pImageInfo) UINT ImageGetNumTracks(ImageInfo* const pImageInfo)
{ {
return pImageInfo ? pImageInfo->uNumTracks : 0; return pImageInfo ? pImageInfo->uNumTracks : 0;
} }
@ -246,6 +250,33 @@ UINT ImageGetImageSize(ImageInfo* const pImageInfo)
return pImageInfo ? pImageInfo->uImageSize : 0; return pImageInfo ? pImageInfo->uImageSize : 0;
} }
bool ImageIsWOZ(ImageInfo* const pImageInfo)
{
return pImageInfo ? (pImageInfo->pImageType->GetType() == eImageWOZ1 || pImageInfo->pImageType->GetType() == eImageWOZ2) : false;
}
BYTE ImageGetOptimalBitTiming(ImageInfo* const pImageInfo)
{
return pImageInfo ? pImageInfo->optimalBitTiming : 32;
}
UINT ImagePhaseToTrack(ImageInfo* const pImageInfo, const float phase, const bool limit/*=true*/)
{
if (!pImageInfo)
return 0;
UINT track = pImageInfo->pImageType->PhaseToTrack(phase);
if (limit)
{
const UINT numTracksInImage = ImageGetNumTracks(pImageInfo);
track = (numTracksInImage == 0) ? 0
: MIN(numTracksInImage - 1, track);
}
return track;
}
void GetImageTitle(LPCTSTR pPathname, TCHAR* pImageName, TCHAR* pFullName) void GetImageTitle(LPCTSTR pPathname, TCHAR* pImageName, TCHAR* pFullName)
{ {
TCHAR imagetitle[ MAX_DISK_FULL_NAME+1 ]; TCHAR imagetitle[ MAX_DISK_FULL_NAME+1 ];

View File

@ -71,15 +71,18 @@ BOOL ImageBoot(ImageInfo* const pImageInfo);
void ImageDestroy(void); void ImageDestroy(void);
void ImageInitialize(void); void ImageInitialize(void);
void ImageReadTrack(ImageInfo* const pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImageBuffer, int* pNibbles, bool enhanceDisk); void ImageReadTrack(ImageInfo* const pImageInfo, float phase, LPBYTE pTrackImageBuffer, int* pNibbles, UINT* pBitCount, bool enhanceDisk);
void ImageWriteTrack(ImageInfo* const pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImage, int nNibbles); void ImageWriteTrack(ImageInfo* const pImageInfo, float phase, LPBYTE pTrackImageBuffer, int nNibbles);
bool ImageReadBlock(ImageInfo* const pImageInfo, UINT nBlock, LPBYTE pBlockBuffer); bool ImageReadBlock(ImageInfo* const pImageInfo, UINT nBlock, LPBYTE pBlockBuffer);
bool ImageWriteBlock(ImageInfo* const pImageInfo, UINT nBlock, LPBYTE pBlockBuffer); bool ImageWriteBlock(ImageInfo* const pImageInfo, UINT nBlock, LPBYTE pBlockBuffer);
int ImageGetNumTracks(ImageInfo* const pImageInfo); UINT ImageGetNumTracks(ImageInfo* const pImageInfo);
bool ImageIsWriteProtected(ImageInfo* const pImageInfo); bool ImageIsWriteProtected(ImageInfo* const pImageInfo);
bool ImageIsMultiFileZip(ImageInfo* const pImageInfo); bool ImageIsMultiFileZip(ImageInfo* const pImageInfo);
const char* ImageGetPathname(ImageInfo* const pImageInfo); const char* ImageGetPathname(ImageInfo* const pImageInfo);
UINT ImageGetImageSize(ImageInfo* const pImageInfo); UINT ImageGetImageSize(ImageInfo* const pImageInfo);
bool ImageIsWOZ(ImageInfo* const pImageInfo);
BYTE ImageGetOptimalBitTiming(ImageInfo* const pImageInfo);
UINT ImagePhaseToTrack(ImageInfo* const pImageInfo, const float phase, const bool limit=true);
void GetImageTitle(LPCTSTR pPathname, TCHAR* pImageName, TCHAR* pFullName); void GetImageTitle(LPCTSTR pPathname, TCHAR* pImageName, TCHAR* pFullName);

View File

@ -70,8 +70,8 @@ LPBYTE CImageBase::ms_pWorkBuffer = NULL;
bool CImageBase::ReadTrack(ImageInfo* pImageInfo, const int nTrack, LPBYTE pTrackBuffer, const UINT uTrackSize) bool CImageBase::ReadTrack(ImageInfo* pImageInfo, const int nTrack, LPBYTE pTrackBuffer, const UINT uTrackSize)
{ {
const long Offset = pImageInfo->uOffset + nTrack * uTrackSize; const long offset = pImageInfo->uOffset + nTrack * uTrackSize;
memcpy(pTrackBuffer, &pImageInfo->pImageBuffer[Offset], uTrackSize); memcpy(pTrackBuffer, &pImageInfo->pImageBuffer[offset], uTrackSize);
return true; return true;
} }
@ -630,18 +630,20 @@ public:
return ePossibleMatch; return ePossibleMatch;
} }
virtual void Read(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImageBuffer, int* pNibbles, bool enhanceDisk) virtual void Read(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int* pNibbles, UINT* pBitCount, bool enhanceDisk)
{ {
ReadTrack(pImageInfo, nTrack, ms_pWorkBuffer, TRACK_DENIBBLIZED_SIZE); const UINT track = PhaseToTrack(phase);
*pNibbles = NibblizeTrack(pTrackImageBuffer, eDOSOrder, nTrack); ReadTrack(pImageInfo, track, ms_pWorkBuffer, TRACK_DENIBBLIZED_SIZE);
*pNibbles = NibblizeTrack(pTrackImageBuffer, eDOSOrder, track);
if (!enhanceDisk) if (!enhanceDisk)
SkewTrack(nTrack, *pNibbles, pTrackImageBuffer); SkewTrack(track, *pNibbles, pTrackImageBuffer);
} }
virtual void Write(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImage, int nNibbles) virtual void Write(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int nNibbles)
{ {
DenibblizeTrack(pTrackImage, eDOSOrder, nNibbles); const UINT track = PhaseToTrack(phase);
WriteTrack(pImageInfo, nTrack, ms_pWorkBuffer, TRACK_DENIBBLIZED_SIZE); DenibblizeTrack(pTrackImageBuffer, eDOSOrder, nNibbles);
WriteTrack(pImageInfo, track, ms_pWorkBuffer, TRACK_DENIBBLIZED_SIZE);
} }
virtual bool AllowCreate(void) { return true; } virtual bool AllowCreate(void) { return true; }
@ -696,23 +698,25 @@ public:
return ePossibleMatch; return ePossibleMatch;
} }
virtual void Read(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImageBuffer, int* pNibbles, bool enhanceDisk) virtual void Read(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int* pNibbles, UINT* pBitCount, bool enhanceDisk)
{ {
ReadTrack(pImageInfo, nTrack, ms_pWorkBuffer, TRACK_DENIBBLIZED_SIZE); const UINT track = PhaseToTrack(phase);
*pNibbles = NibblizeTrack(pTrackImageBuffer, eProDOSOrder, nTrack); ReadTrack(pImageInfo, track, ms_pWorkBuffer, TRACK_DENIBBLIZED_SIZE);
*pNibbles = NibblizeTrack(pTrackImageBuffer, eProDOSOrder, track);
if (!enhanceDisk) if (!enhanceDisk)
SkewTrack(nTrack, *pNibbles, pTrackImageBuffer); SkewTrack(track, *pNibbles, pTrackImageBuffer);
} }
virtual void Write(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImage, int nNibbles) virtual void Write(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int nNibbles)
{ {
DenibblizeTrack(pTrackImage, eProDOSOrder, nNibbles); const UINT track = PhaseToTrack(phase);
WriteTrack(pImageInfo, nTrack, ms_pWorkBuffer, TRACK_DENIBBLIZED_SIZE); DenibblizeTrack(pTrackImageBuffer, eProDOSOrder, nNibbles);
WriteTrack(pImageInfo, track, ms_pWorkBuffer, TRACK_DENIBBLIZED_SIZE);
} }
virtual eImageType GetType(void) { return eImagePO; } virtual eImageType GetType(void) { return eImagePO; }
virtual const char* GetCreateExtensions(void) { return ".po"; } virtual const char* GetCreateExtensions(void) { return ".po"; }
virtual const char* GetRejectExtensions(void) { return ".do;.iie;.nib;.prg"; } virtual const char* GetRejectExtensions(void) { return ".do;.iie;.nib;.prg;.woz"; }
}; };
//------------------------------------- //-------------------------------------
@ -724,7 +728,7 @@ public:
CNib1Image(void) {} CNib1Image(void) {}
virtual ~CNib1Image(void) {} virtual ~CNib1Image(void) {}
static const UINT NIB1_TRACK_SIZE = NIBBLES_PER_TRACK; static const UINT NIB1_TRACK_SIZE = NIBBLES_PER_TRACK_NIB;
virtual eDetectResult Detect(const LPBYTE pImage, const DWORD dwImageSize, const TCHAR* pszExt) virtual eDetectResult Detect(const LPBYTE pImage, const DWORD dwImageSize, const TCHAR* pszExt)
{ {
@ -735,16 +739,18 @@ public:
return eMatch; return eMatch;
} }
virtual void Read(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImageBuffer, int* pNibbles, bool enhanceDisk) virtual void Read(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int* pNibbles, UINT* pBitCount, bool enhanceDisk)
{ {
ReadTrack(pImageInfo, nTrack, pTrackImageBuffer, NIB1_TRACK_SIZE); const UINT track = PhaseToTrack(phase);
ReadTrack(pImageInfo, track, pTrackImageBuffer, NIB1_TRACK_SIZE);
*pNibbles = NIB1_TRACK_SIZE; *pNibbles = NIB1_TRACK_SIZE;
} }
virtual void Write(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImage, int nNibbles) virtual void Write(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int nNibbles)
{ {
_ASSERT(nNibbles == NIB1_TRACK_SIZE); // Must be true - as nNibbles gets init'd by ImageReadTrace() _ASSERT(nNibbles == NIB1_TRACK_SIZE); // Must be true - as nNibbles gets init'd by ImageReadTrace()
WriteTrack(pImageInfo, nTrack, pTrackImage, nNibbles); const UINT track = PhaseToTrack(phase);
WriteTrack(pImageInfo, track, pTrackImageBuffer, nNibbles);
} }
virtual bool AllowCreate(void) { return true; } virtual bool AllowCreate(void) { return true; }
@ -752,7 +758,7 @@ public:
virtual eImageType GetType(void) { return eImageNIB1; } virtual eImageType GetType(void) { return eImageNIB1; }
virtual const char* GetCreateExtensions(void) { return ".nib"; } virtual const char* GetCreateExtensions(void) { return ".nib"; }
virtual const char* GetRejectExtensions(void) { return ".do;.iie;.po;.prg"; } virtual const char* GetRejectExtensions(void) { return ".do;.iie;.po;.prg;.woz"; }
}; };
//------------------------------------- //-------------------------------------
@ -775,21 +781,23 @@ public:
return eMatch; return eMatch;
} }
virtual void Read(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImageBuffer, int* pNibbles, bool enhanceDisk) virtual void Read(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int* pNibbles, UINT* pBitCount, bool enhanceDisk)
{ {
ReadTrack(pImageInfo, nTrack, pTrackImageBuffer, NIB2_TRACK_SIZE); const UINT track = PhaseToTrack(phase);
ReadTrack(pImageInfo, track, pTrackImageBuffer, NIB2_TRACK_SIZE);
*pNibbles = NIB2_TRACK_SIZE; *pNibbles = NIB2_TRACK_SIZE;
} }
virtual void Write(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImage, int nNibbles) virtual void Write(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int nNibbles)
{ {
_ASSERT(nNibbles == NIB2_TRACK_SIZE); // Must be true - as nNibbles gets init'd by ImageReadTrace() _ASSERT(nNibbles == NIB2_TRACK_SIZE); // Must be true - as nNibbles gets init'd by ImageReadTrace()
WriteTrack(pImageInfo, nTrack, pTrackImage, nNibbles); const UINT track = PhaseToTrack(phase);
WriteTrack(pImageInfo, track, pTrackImageBuffer, nNibbles);
} }
virtual eImageType GetType(void) { return eImageNIB2; } virtual eImageType GetType(void) { return eImageNIB2; }
virtual const char* GetCreateExtensions(void) { return ".nb2"; } virtual const char* GetCreateExtensions(void) { return ".nb2"; }
virtual const char* GetRejectExtensions(void) { return ".do;.iie;.po;.prg;.2mg;.2img"; } virtual const char* GetRejectExtensions(void) { return ".do;.iie;.po;.prg;.woz;.2mg;.2img"; }
}; };
//------------------------------------- //-------------------------------------
@ -851,8 +859,10 @@ public:
return eMatch; return eMatch;
} }
virtual void Read(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImageBuffer, int* pNibbles, bool enhanceDisk) virtual void Read(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int* pNibbles, UINT* pBitCount, bool enhanceDisk)
{ {
UINT track = PhaseToTrack(phase);
// IF WE HAVEN'T ALREADY DONE SO, READ THE IMAGE FILE HEADER // IF WE HAVEN'T ALREADY DONE SO, READ THE IMAGE FILE HEADER
if (!m_pHeader) if (!m_pHeader)
{ {
@ -872,19 +882,19 @@ public:
if (*(m_pHeader+13) <= 2) if (*(m_pHeader+13) <= 2)
{ {
ConvertSectorOrder(m_pHeader+14); ConvertSectorOrder(m_pHeader+14);
SetFilePointer(pImageInfo->hFile, nTrack*TRACK_DENIBBLIZED_SIZE+30, NULL, FILE_BEGIN); SetFilePointer(pImageInfo->hFile, track*TRACK_DENIBBLIZED_SIZE+30, NULL, FILE_BEGIN);
ZeroMemory(ms_pWorkBuffer, TRACK_DENIBBLIZED_SIZE); ZeroMemory(ms_pWorkBuffer, TRACK_DENIBBLIZED_SIZE);
DWORD bytesread; DWORD bytesread;
ReadFile(pImageInfo->hFile, ms_pWorkBuffer, TRACK_DENIBBLIZED_SIZE, &bytesread, NULL); ReadFile(pImageInfo->hFile, ms_pWorkBuffer, TRACK_DENIBBLIZED_SIZE, &bytesread, NULL);
*pNibbles = NibblizeTrack(pTrackImageBuffer, eSIMSYSTEMOrder, nTrack); *pNibbles = NibblizeTrack(pTrackImageBuffer, eSIMSYSTEMOrder, track);
} }
// OTHERWISE, IF THIS IMAGE CONTAINS NIBBLE INFORMATION, READ IT DIRECTLY INTO THE TRACK BUFFER // OTHERWISE, IF THIS IMAGE CONTAINS NIBBLE INFORMATION, READ IT DIRECTLY INTO THE TRACK BUFFER
else else
{ {
*pNibbles = *(LPWORD)(m_pHeader+nTrack*2+14); *pNibbles = *(LPWORD)(m_pHeader+track*2+14);
LONG Offset = 88; LONG Offset = 88;
while (nTrack--) while (track--)
Offset += *(LPWORD)(m_pHeader+nTrack*2+14); Offset += *(LPWORD)(m_pHeader+track*2+14);
SetFilePointer(pImageInfo->hFile, Offset, NULL,FILE_BEGIN); SetFilePointer(pImageInfo->hFile, Offset, NULL,FILE_BEGIN);
ZeroMemory(pTrackImageBuffer, *pNibbles); ZeroMemory(pTrackImageBuffer, *pNibbles);
DWORD dwBytesRead; DWORD dwBytesRead;
@ -892,14 +902,14 @@ public:
} }
} }
virtual void Write(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImage, int nNibbles) virtual void Write(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int nNibbles)
{ {
// note: unimplemented // note: unimplemented
} }
virtual eImageType GetType(void) { return eImageIIE; } virtual eImageType GetType(void) { return eImageIIE; }
virtual const char* GetCreateExtensions(void) { return ".iie"; } virtual const char* GetCreateExtensions(void) { return ".iie"; }
virtual const char* GetRejectExtensions(void) { return ".do.;.nib;.po;.prg;.2mg;.2img"; } virtual const char* GetRejectExtensions(void) { return ".do.;.nib;.po;.prg;.woz;.2mg;.2img"; }
private: private:
void ConvertSectorOrder(LPBYTE sourceorder) void ConvertSectorOrder(LPBYTE sourceorder)
@ -973,7 +983,7 @@ public:
virtual eImageType GetType(void) { return eImageAPL; } virtual eImageType GetType(void) { return eImageAPL; }
virtual const char* GetCreateExtensions(void) { return ".apl"; } virtual const char* GetCreateExtensions(void) { return ".apl"; }
virtual const char* GetRejectExtensions(void) { return ".do;.dsk;.iie;.nib;.po;.2mg;.2img"; } virtual const char* GetRejectExtensions(void) { return ".do;.dsk;.iie;.nib;.po;.woz;.2mg;.2img"; }
}; };
//------------------------------------- //-------------------------------------
@ -1024,7 +1034,154 @@ public:
virtual eImageType GetType(void) { return eImagePRG; } virtual eImageType GetType(void) { return eImagePRG; }
virtual const char* GetCreateExtensions(void) { return ".prg"; } virtual const char* GetCreateExtensions(void) { return ".prg"; }
virtual const char* GetRejectExtensions(void) { return ".do;.dsk;.iie;.nib;.po;.2mg;.2img"; } virtual const char* GetRejectExtensions(void) { return ".do;.dsk;.iie;.nib;.po;.woz;.2mg;.2img"; }
};
//-------------------------------------
class CWOZEmptyTrack
{
public:
CWOZEmptyTrack(void)
{
m_pWOZEmptyTrack = new BYTE[CWOZHelper::EMPTY_TRACK_SIZE];
srand(1); // Use a fixed seed for determinism
for (UINT i = 0; i < CWOZHelper::EMPTY_TRACK_SIZE; i++)
{
BYTE n = 0;
for (UINT j = 0; j < 8; j++)
{
if (rand() < ((RAND_MAX * 3) / 10)) // ~30% of buffer are 1 bits
n |= 1 << j;
}
m_pWOZEmptyTrack[i] = n;
}
}
virtual ~CWOZEmptyTrack(void) { delete m_pWOZEmptyTrack; }
void ReadEmptyTrack(LPBYTE pTrackImageBuffer, int* pNibbles, UINT* pBitCount)
{
memcpy(pTrackImageBuffer, m_pWOZEmptyTrack, CWOZHelper::EMPTY_TRACK_SIZE);
*pNibbles = CWOZHelper::EMPTY_TRACK_SIZE;
*pBitCount = CWOZHelper::EMPTY_TRACK_SIZE * 8;
return;
}
private:
BYTE* m_pWOZEmptyTrack;
};
//-------------------------------------
class CWOZ1Image : public CImageBase, private CWOZEmptyTrack
{
public:
CWOZ1Image(void) {}
virtual ~CWOZ1Image(void) {}
virtual eDetectResult Detect(const LPBYTE pImage, const DWORD dwImageSize, const TCHAR* pszExt)
{
CWOZHelper::WOZHeader* pWozHdr = (CWOZHelper::WOZHeader*) pImage;
if (pWozHdr->id1 != CWOZHelper::ID1_WOZ1 || pWozHdr->id2 != CWOZHelper::ID2)
return eMismatch;
if (pWozHdr->crc32)
{
// TODO: check crc
}
m_uNumTracksInImage = CWOZHelper::MAX_TRACKS_5_25;
return eMatch;
}
virtual void Read(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int* pNibbles, UINT* pBitCount, bool enhanceDisk)
{
BYTE*& pTrackMap = pImageInfo->pTrackMap;
const int trackFromTMAP = pTrackMap[(UINT)(phase * 2)];
if (trackFromTMAP == 0xFF)
return ReadEmptyTrack(pTrackImageBuffer, pNibbles, pBitCount);
ReadTrack(pImageInfo, trackFromTMAP, pTrackImageBuffer, CWOZHelper::WOZ1_TRACK_SIZE);
CWOZHelper::TRKv1* pTRK = (CWOZHelper::TRKv1*) &pTrackImageBuffer[CWOZHelper::WOZ1_TRK_OFFSET];
*pNibbles = pTRK->bytesUsed;
*pBitCount = pTRK->bitCount;
}
virtual void Write(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int nNibbles)
{
// TODO
_ASSERT(0);
}
// TODO: Uncomment and fix-up if we want to allow .woz image creation (eg. for INIT or FORMAT)
// virtual bool AllowCreate(void) { return true; }
// virtual UINT GetImageSizeForCreate(void) { return 0; }//TODO
virtual eImageType GetType(void) { return eImageWOZ1; }
virtual const char* GetCreateExtensions(void) { return ".woz"; }
virtual const char* GetRejectExtensions(void) { return ".do;.dsk;.nib;.iie;.po;.prg"; }
};
//-------------------------------------
class CWOZ2Image : public CImageBase, private CWOZEmptyTrack
{
public:
CWOZ2Image(void) {}
virtual ~CWOZ2Image(void) {}
virtual eDetectResult Detect(const LPBYTE pImage, const DWORD dwImageSize, const TCHAR* pszExt)
{
CWOZHelper::WOZHeader* pWozHdr = (CWOZHelper::WOZHeader*) pImage;
if (pWozHdr->id1 != CWOZHelper::ID1_WOZ2 || pWozHdr->id2 != CWOZHelper::ID2)
return eMismatch;
if (pWozHdr->crc32)
{
// TODO: check crc
}
m_uNumTracksInImage = CWOZHelper::MAX_TRACKS_5_25;
return eMatch;
}
virtual void Read(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int* pNibbles, UINT* pBitCount, bool enhanceDisk)
{
BYTE*& pTrackMap = pImageInfo->pTrackMap;
const int trackFromTMAP = pTrackMap[(UINT)(phase * 2)];
if (trackFromTMAP == 0xFF)
return ReadEmptyTrack(pTrackImageBuffer, pNibbles, pBitCount);
CWOZHelper::TRKv2* pTRKS = (CWOZHelper::TRKv2*) &pImageInfo->pImageBuffer[pImageInfo->uOffset];
CWOZHelper::TRKv2* pTRK = &pTRKS[trackFromTMAP];
*pBitCount = pTRK->bitCount;
*pNibbles = (pTRK->bitCount+7) / 8;
_ASSERT(*pNibbles <= NIBBLES_PER_TRACK_WOZ2);
if (*pNibbles > NIBBLES_PER_TRACK_WOZ2)
return ReadEmptyTrack(pTrackImageBuffer, pNibbles, pBitCount); // TODO: Enlarge track buffer, but for now just return an empty track
memcpy(pTrackImageBuffer, &pImageInfo->pImageBuffer[pTRK->startBlock*512], *pNibbles);
}
virtual void Write(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int nNibbles)
{
// TODO
_ASSERT(0);
}
// TODO: Uncomment and fix-up if we want to allow .woz image creation (eg. for INIT or FORMAT)
// virtual bool AllowCreate(void) { return true; }
// virtual UINT GetImageSizeForCreate(void) { return 0; }//TODO
virtual eImageType GetType(void) { return eImageWOZ2; }
virtual const char* GetCreateExtensions(void) { return ".woz"; }
virtual const char* GetRejectExtensions(void) { return ".do;.dsk;.nib;.iie;.po;.prg"; }
}; };
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -1048,6 +1205,8 @@ eDetectResult CMacBinaryHelper::DetectHdr(LPBYTE& pImage, DWORD& dwImageSize, DW
return eMismatch; return eMismatch;
} }
//-----------------------------------------------------------------------------
eDetectResult C2IMGHelper::DetectHdr(LPBYTE& pImage, DWORD& dwImageSize, DWORD& dwOffset) eDetectResult C2IMGHelper::DetectHdr(LPBYTE& pImage, DWORD& dwImageSize, DWORD& dwOffset)
{ {
Header2IMG* pHdr = (Header2IMG*) pImage; Header2IMG* pHdr = (Header2IMG*) pImage;
@ -1099,7 +1258,7 @@ eDetectResult C2IMGHelper::DetectHdr(LPBYTE& pImage, DWORD& dwImageSize, DWORD&
break; break;
case e2IMGFormatNIBData: case e2IMGFormatNIBData:
{ {
if (pHdr->DiskDataLength != TRACKS_STANDARD*NIBBLES_PER_TRACK) if (pHdr->DiskDataLength != TRACKS_STANDARD*NIBBLES_PER_TRACK_NIB)
return eMismatch; return eMismatch;
} }
break; break;
@ -1126,11 +1285,59 @@ bool C2IMGHelper::IsLocked(void)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Pre: already matched the WOZ header
eDetectResult CWOZHelper::ProcessChunks(const LPBYTE pImage, const DWORD dwImageSize, DWORD& dwOffset, BYTE*& pTrackMap)
{
UINT32* pImage32 = (uint32_t*) (pImage + sizeof(WOZHeader));
UINT32 imageSizeRemaining = dwImageSize - sizeof(WOZHeader);
while(imageSizeRemaining > 8)
{
UINT32 chunkId = *pImage32++;
UINT32 chunkSize = *pImage32++;
imageSizeRemaining -= 8;
switch(chunkId)
{
case INFO_CHUNK_ID:
m_pInfo = (InfoChunkv2*)(pImage32-2);
if (m_pInfo->v1.version > InfoChunk::maxSupportedVersion)
return eMismatch;
if (m_pInfo->v1.diskType != InfoChunk::diskType5_25)
return eMismatch;
break;
case TMAP_CHUNK_ID:
pTrackMap = (uint8_t*)pImage32;
break;
case TRKS_CHUNK_ID:
dwOffset = dwImageSize - imageSizeRemaining; // offset into image of track data
break;
case WRIT_CHUNK_ID: // WOZ v2 (optional)
break;
case META_CHUNK_ID: // (optional)
break;
default: // no idea what this chunk is, so skip it
_ASSERT(0);
break;
}
pImage32 = (UINT32*) ((BYTE*)pImage32 + chunkSize);
imageSizeRemaining -= chunkSize;
_ASSERT(imageSizeRemaining >= 0);
if (imageSizeRemaining < 0)
return eMismatch;
}
return eMatch;
}
//-----------------------------------------------------------------------------
// NB. Of the 6 cases (floppy/harddisk x gzip/zip/normal) only harddisk-normal isn't read entirely to memory // NB. Of the 6 cases (floppy/harddisk x gzip/zip/normal) only harddisk-normal isn't read entirely to memory
// - harddisk-normal-create also doesn't create a max size image-buffer // - harddisk-normal-create also doesn't create a max size image-buffer
// DETERMINE THE FILE'S EXTENSION AND CONVERT IT TO LOWERCASE // DETERMINE THE FILE'S EXTENSION AND CONVERT IT TO LOWERCASE
void GetCharLowerExt(TCHAR* pszExt, LPCTSTR pszImageFilename, const UINT uExtSize) void CImageHelperBase::GetCharLowerExt(TCHAR* pszExt, LPCTSTR pszImageFilename, const UINT uExtSize)
{ {
LPCTSTR pImageFileExt = pszImageFilename; LPCTSTR pImageFileExt = pszImageFilename;
@ -1146,7 +1353,7 @@ void GetCharLowerExt(TCHAR* pszExt, LPCTSTR pszImageFilename, const UINT uExtSiz
CharLowerBuff(pszExt, _tcslen(pszExt)); CharLowerBuff(pszExt, _tcslen(pszExt));
} }
void GetCharLowerExt2(TCHAR* pszExt, LPCTSTR pszImageFilename, const UINT uExtSize) void CImageHelperBase::GetCharLowerExt2(TCHAR* pszExt, LPCTSTR pszImageFilename, const UINT uExtSize)
{ {
TCHAR szFilename[MAX_PATH]; TCHAR szFilename[MAX_PATH];
_tcsncpy(szFilename, pszImageFilename, MAX_PATH); _tcsncpy(szFilename, pszImageFilename, MAX_PATH);
@ -1187,7 +1394,7 @@ ImageError_e CImageHelperBase::CheckGZipFile(LPCTSTR pszImageFilename, ImageInfo
DWORD dwSize = nLen; DWORD dwSize = nLen;
DWORD dwOffset = 0; DWORD dwOffset = 0;
CImageBase* pImageType = Detect(pImageInfo->pImageBuffer, dwSize, szExt, dwOffset, &pImageInfo->bWriteProtected); CImageBase* pImageType = Detect(pImageInfo->pImageBuffer, dwSize, szExt, dwOffset, pImageInfo->bWriteProtected, pImageInfo->pTrackMap, pImageInfo->optimalBitTiming);
if (!pImageType) if (!pImageType)
return eIMAGE_ERROR_UNSUPPORTED; return eIMAGE_ERROR_UNSUPPORTED;
@ -1196,11 +1403,7 @@ ImageError_e CImageHelperBase::CheckGZipFile(LPCTSTR pszImageFilename, ImageInfo
if (Type == eImageAPL || Type == eImageIIE || Type == eImagePRG) if (Type == eImageAPL || Type == eImageIIE || Type == eImagePRG)
return eIMAGE_ERROR_UNSUPPORTED; return eIMAGE_ERROR_UNSUPPORTED;
pImageInfo->FileType = eFileGZip; SetImageInfo(pImageInfo, eFileGZip, dwOffset, pImageType, dwSize);
pImageInfo->uOffset = dwOffset;
pImageInfo->pImageType = pImageType;
pImageInfo->uImageSize = dwSize;
return eIMAGE_ERROR_NONE; return eIMAGE_ERROR_NONE;
} }
@ -1283,7 +1486,7 @@ ImageError_e CImageHelperBase::CheckZipFile(LPCTSTR pszImageFilename, ImageInfo*
DWORD dwSize = nLen; DWORD dwSize = nLen;
DWORD dwOffset = 0; DWORD dwOffset = 0;
CImageBase* pImageType = Detect(pImageInfo->pImageBuffer, dwSize, szExt, dwOffset, &pImageInfo->bWriteProtected); CImageBase* pImageType = Detect(pImageInfo->pImageBuffer, dwSize, szExt, dwOffset, pImageInfo->bWriteProtected, pImageInfo->pTrackMap, pImageInfo->optimalBitTiming);
if (!pImageType) if (!pImageType)
{ {
@ -1300,11 +1503,7 @@ ImageError_e CImageHelperBase::CheckZipFile(LPCTSTR pszImageFilename, ImageInfo*
if (global_info.number_entry > 1) if (global_info.number_entry > 1)
pImageInfo->bWriteProtected = 1; // Zip archives with multiple files are read-only (for now) pImageInfo->bWriteProtected = 1; // Zip archives with multiple files are read-only (for now)
pImageInfo->FileType = eFileZip; SetImageInfo(pImageInfo, eFileZip, dwOffset, pImageType, dwSize);
pImageInfo->uOffset = dwOffset;
pImageInfo->pImageType = pImageType;
pImageInfo->uImageSize = dwSize;
return eIMAGE_ERROR_NONE; return eIMAGE_ERROR_NONE;
} }
@ -1384,7 +1583,7 @@ ImageError_e CImageHelperBase::CheckNormalFile(LPCTSTR pszImageFilename, ImageIn
return eIMAGE_ERROR_BAD_SIZE; return eIMAGE_ERROR_BAD_SIZE;
} }
pImageType = Detect(pImageInfo->pImageBuffer, dwSize, szExt, dwOffset, &pImageInfo->bWriteProtected); pImageType = Detect(pImageInfo->pImageBuffer, dwSize, szExt, dwOffset, pImageInfo->bWriteProtected, pImageInfo->pTrackMap, pImageInfo->optimalBitTiming);
if (bTempDetectBuffer) if (bTempDetectBuffer)
{ {
delete [] pImageInfo->pImageBuffer; delete [] pImageInfo->pImageBuffer;
@ -1437,12 +1636,18 @@ ImageError_e CImageHelperBase::CheckNormalFile(LPCTSTR pszImageFilename, ImageIn
return eIMAGE_ERROR_UNSUPPORTED; return eIMAGE_ERROR_UNSUPPORTED;
} }
pImageInfo->FileType = eFileNormal; SetImageInfo(pImageInfo, eFileNormal, dwOffset, pImageType, dwSize);
return eIMAGE_ERROR_NONE;
}
//-------------------------------------
void CImageHelperBase::SetImageInfo(ImageInfo* pImageInfo, FileType_e eFileGZip, DWORD dwOffset, CImageBase* pImageType, DWORD dwSize)
{
pImageInfo->FileType = eFileGZip;
pImageInfo->uOffset = dwOffset; pImageInfo->uOffset = dwOffset;
pImageInfo->pImageType = pImageType; pImageInfo->pImageType = pImageType;
pImageInfo->uImageSize = dwSize; pImageInfo->uImageSize = dwSize;
return eIMAGE_ERROR_NONE;
} }
//------------------------------------- //-------------------------------------
@ -1517,54 +1722,68 @@ CDiskImageHelper::CDiskImageHelper(void) :
m_vecImageTypes.push_back( new CIIeImage ); m_vecImageTypes.push_back( new CIIeImage );
m_vecImageTypes.push_back( new CAplImage ); m_vecImageTypes.push_back( new CAplImage );
m_vecImageTypes.push_back( new CPrgImage ); m_vecImageTypes.push_back( new CPrgImage );
m_vecImageTypes.push_back( new CWOZ1Image );
m_vecImageTypes.push_back( new CWOZ2Image );
} }
CImageBase* CDiskImageHelper::Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* pszExt, DWORD& dwOffset, bool* pWriteProtected_) CImageBase* CDiskImageHelper::Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* pszExt, DWORD& dwOffset, bool& writeProtected, BYTE*& pTrackMap, BYTE& optimalBitTiming)
{ {
dwOffset = 0; dwOffset = 0;
m_MacBinaryHelper.DetectHdr(pImage, dwSize, dwOffset); m_MacBinaryHelper.DetectHdr(pImage, dwSize, dwOffset);
m_Result2IMG = m_2IMGHelper.DetectHdr(pImage, dwSize, dwOffset); m_Result2IMG = m_2IMGHelper.DetectHdr(pImage, dwSize, dwOffset);
// CALL THE DETECTION FUNCTIONS IN ORDER, LOOKING FOR A MATCH // CALL THE DETECTION FUNCTIONS IN ORDER, LOOKING FOR A MATCH
eImageType ImageType = eImageUNKNOWN; eImageType imageType = eImageUNKNOWN;
eImageType PossibleType = eImageUNKNOWN; eImageType possibleType = eImageUNKNOWN;
if (m_Result2IMG == eMatch) if (m_Result2IMG == eMatch)
{ {
if (m_2IMGHelper.IsImageFormatDOS33()) if (m_2IMGHelper.IsImageFormatDOS33())
ImageType = eImageDO; imageType = eImageDO;
else if (m_2IMGHelper.IsImageFormatProDOS()) else if (m_2IMGHelper.IsImageFormatProDOS())
ImageType = eImagePO; imageType = eImagePO;
if (ImageType != eImageUNKNOWN) if (imageType != eImageUNKNOWN)
{ {
CImageBase* pImageType = GetImage(ImageType); CImageBase* pImageType = GetImage(imageType);
if (!pImageType || !pImageType->IsValidImageSize(dwSize)) if (!pImageType || !pImageType->IsValidImageSize(dwSize))
ImageType = eImageUNKNOWN; imageType = eImageUNKNOWN;
} }
} }
if (ImageType == eImageUNKNOWN) if (imageType == eImageUNKNOWN)
{ {
for (UINT uLoop=0; uLoop < GetNumImages() && ImageType == eImageUNKNOWN; uLoop++) for (UINT uLoop=0; uLoop < GetNumImages() && imageType == eImageUNKNOWN; uLoop++)
{ {
if (*pszExt && _tcsstr(GetImage(uLoop)->GetRejectExtensions(), pszExt)) if (*pszExt && _tcsstr(GetImage(uLoop)->GetRejectExtensions(), pszExt))
continue; continue;
eDetectResult Result = GetImage(uLoop)->Detect(pImage, dwSize, pszExt); eDetectResult Result = GetImage(uLoop)->Detect(pImage, dwSize, pszExt);
if (Result == eMatch) if (Result == eMatch)
ImageType = GetImage(uLoop)->GetType(); imageType = GetImage(uLoop)->GetType();
else if ((Result == ePossibleMatch) && (PossibleType == eImageUNKNOWN)) else if ((Result == ePossibleMatch) && (possibleType == eImageUNKNOWN))
PossibleType = GetImage(uLoop)->GetType(); possibleType = GetImage(uLoop)->GetType();
} }
} }
if (ImageType == eImageUNKNOWN) if (imageType == eImageUNKNOWN)
ImageType = PossibleType; imageType = possibleType;
CImageBase* pImageType = GetImage(ImageType); CImageBase* pImageType = GetImage(imageType);
if (!pImageType)
return NULL;
if (pImageType) if (imageType == eImageWOZ1 || imageType == eImageWOZ2)
{
if (m_WOZHelper.ProcessChunks(pImage, dwSize, dwOffset, pTrackMap) != eMatch)
return NULL;
// if (m_WOZHelper.IsWriteProtected() && !writeProtected) // Force write-protected until writing is supported
writeProtected = true;
optimalBitTiming = m_WOZHelper.GetOptimalBitTiming();
}
else
{ {
if (pImageType->AllowRW()) if (pImageType->AllowRW())
{ {
@ -1578,8 +1797,8 @@ CImageBase* CDiskImageHelper::Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* p
{ {
pImageType->SetVolumeNumber( m_2IMGHelper.GetVolumeNumber() ); pImageType->SetVolumeNumber( m_2IMGHelper.GetVolumeNumber() );
if (m_2IMGHelper.IsLocked() && !*pWriteProtected_) if (m_2IMGHelper.IsLocked() && !writeProtected)
*pWriteProtected_ = 1; writeProtected = true;
} }
else else
{ {
@ -1634,7 +1853,7 @@ CHardDiskImageHelper::CHardDiskImageHelper(void) :
m_vecImageTypes.push_back( new CHDVImage ); m_vecImageTypes.push_back( new CHDVImage );
} }
CImageBase* CHardDiskImageHelper::Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* pszExt, DWORD& dwOffset, bool* pWriteProtected_) CImageBase* CHardDiskImageHelper::Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* pszExt, DWORD& dwOffset, bool& writeProtected, BYTE*& pTrackMap, BYTE& optimalBitTiming)
{ {
dwOffset = 0; dwOffset = 0;
m_Result2IMG = m_2IMGHelper.DetectHdr(pImage, dwSize, dwOffset); m_Result2IMG = m_2IMGHelper.DetectHdr(pImage, dwSize, dwOffset);
@ -1659,11 +1878,14 @@ CImageBase* CHardDiskImageHelper::Detect(LPBYTE pImage, DWORD dwSize, const TCHA
{ {
if (m_Result2IMG == eMatch) if (m_Result2IMG == eMatch)
{ {
if (m_2IMGHelper.IsLocked() && !*pWriteProtected_) if (m_2IMGHelper.IsLocked() && !writeProtected)
*pWriteProtected_ = 1; writeProtected = true;
} }
} }
pTrackMap = 0; // TODO: WOZ
optimalBitTiming = 0; // TODO: WOZ
return pImageType; return pImageType;
} }

View File

@ -10,7 +10,7 @@
#define ZIP_SUFFIX_LEN (sizeof(ZIP_SUFFIX)-1) #define ZIP_SUFFIX_LEN (sizeof(ZIP_SUFFIX)-1)
enum eImageType {eImageUNKNOWN, eImageDO, eImagePO, eImageNIB1, eImageNIB2, eImageHDV, eImageIIE, eImageAPL, eImagePRG}; enum eImageType {eImageUNKNOWN, eImageDO, eImagePO, eImageNIB1, eImageNIB2, eImageHDV, eImageIIE, eImageAPL, eImagePRG, eImageWOZ1, eImageWOZ2};
enum eDetectResult {eMismatch, ePossibleMatch, eMatch}; enum eDetectResult {eMismatch, ePossibleMatch, eMatch};
class CImageBase; class CImageBase;
@ -35,6 +35,8 @@ struct ImageInfo
BYTE ValidTrack[TRACKS_MAX]; BYTE ValidTrack[TRACKS_MAX];
UINT uNumTracks; UINT uNumTracks;
BYTE* pImageBuffer; BYTE* pImageBuffer;
BYTE* pTrackMap; // WOZ only
BYTE optimalBitTiming; // WOZ only
}; };
//------------------------------------- //-------------------------------------
@ -54,9 +56,9 @@ public:
virtual bool Boot(ImageInfo* pImageInfo) { return false; } virtual bool Boot(ImageInfo* pImageInfo) { return false; }
virtual eDetectResult Detect(const LPBYTE pImage, const DWORD dwImageSize, const TCHAR* pszExt) = 0; virtual eDetectResult Detect(const LPBYTE pImage, const DWORD dwImageSize, const TCHAR* pszExt) = 0;
virtual void Read(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImageBuffer, int* pNibbles, bool enhanceDisk) { } virtual void Read(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int* pNibbles, UINT* pBitCount, bool enhanceDisk) { }
virtual bool Read(ImageInfo* pImageInfo, UINT nBlock, LPBYTE pBlockBuffer) { return false; } virtual bool Read(ImageInfo* pImageInfo, UINT nBlock, LPBYTE pBlockBuffer) { return false; }
virtual void Write(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImage, int nNibbles) { } virtual void Write(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int nNibbles) { }
virtual bool Write(ImageInfo* pImageInfo, UINT nBlock, LPBYTE pBlockBuffer) { return false; } virtual bool Write(ImageInfo* pImageInfo, UINT nBlock, LPBYTE pBlockBuffer) { return false; }
virtual bool AllowBoot(void) { return false; } // Only: APL and PRG virtual bool AllowBoot(void) { return false; } // Only: APL and PRG
@ -71,6 +73,11 @@ public:
void SetVolumeNumber(const BYTE uVolumeNumber) { m_uVolumeNumber = uVolumeNumber; } void SetVolumeNumber(const BYTE uVolumeNumber) { m_uVolumeNumber = uVolumeNumber; }
bool IsValidImageSize(const DWORD uImageSize); bool IsValidImageSize(const DWORD uImageSize);
// To accurately convert a half phase (quarter track) back to a track (round half tracks down), use: ceil(phase)/2, eg:
// . phase=4,+1 half phase = phase 4.5 => ceil(4.5)/2 = track 2 (OK)
// . phase=4,-1 half phase = phase 3.5 => ceil(3.5)/2 = track 2 (OK)
UINT PhaseToTrack(const float phase) { return ((UINT)ceil(phase)) >> 1; }
enum SectorOrder_e {eProDOSOrder, eDOSOrder, eSIMSYSTEMOrder, NUM_SECTOR_ORDERS}; enum SectorOrder_e {eProDOSOrder, eDOSOrder, eSIMSYSTEMOrder, NUM_SECTOR_ORDERS};
protected: protected:
@ -122,7 +129,7 @@ private:
// http://apple2.org.za/gswv/a2zine/Docs/DiskImage_2MG_Info.txt // http://apple2.org.za/gswv/a2zine/Docs/DiskImage_2MG_Info.txt
#pragma pack(push) #pragma pack(push)
#pragma pack(1) // Ensure Header2IMG is packed #pragma pack(1) // Ensure Header2IMG & WOZ structs are packed
class C2IMGHelper : public CHdrHelper class C2IMGHelper : public CHdrHelper
{ {
@ -181,6 +188,98 @@ private:
bool m_bIsFloppy; bool m_bIsFloppy;
}; };
class CWOZHelper : public CHdrHelper
{
public:
CWOZHelper() :
m_pInfo(NULL)
{}
virtual ~CWOZHelper(void) {}
virtual eDetectResult DetectHdr(LPBYTE& pImage, DWORD& dwImageSize, DWORD& dwOffset) { _ASSERT(0); return eMismatch; }
virtual UINT GetMaxHdrSize(void) { return sizeof(WOZHeader); }
eDetectResult ProcessChunks(const LPBYTE pImage, const DWORD dwImageSize, DWORD& dwOffset, BYTE*& pTrackMap);
bool IsWriteProtected(void) { return m_pInfo->v1.writeProtected == 1; }
BYTE GetOptimalBitTiming(void) { return (m_pInfo->v1.version == 1) ? CWOZHelper::InfoChunkv2::optimalBitTiming5_25 : m_pInfo->optimalBitTiming; }
static const UINT32 ID1_WOZ1 = '1ZOW'; // 'WOZ1'
static const UINT32 ID1_WOZ2 = '2ZOW'; // 'WOZ2'
static const UINT32 ID2 = 0x0A0D0AFF;
struct WOZHeader
{
UINT32 id1; // 'WOZ1' or 'WOZ2'
UINT32 id2;
UINT32 crc32;
};
static const UINT32 MAX_TRACKS_5_25 = 40;
static const UINT32 WOZ1_TRACK_SIZE = 6656; // 0x1A00
static const UINT32 WOZ1_TRK_OFFSET = 6646;
static const UINT32 EMPTY_TRACK_SIZE = 6400;
struct TRKv1
{
UINT16 bytesUsed;
UINT16 bitCount;
UINT16 splicePoint;
BYTE spliceNibble;
BYTE spliceBitCount;
UINT16 reserved;
};
struct TRKv2
{
UINT16 startBlock; // relative to start of file
UINT16 blockCount; // number of blocks for this BITS data
UINT32 bitCount;
};
private:
static const UINT32 INFO_CHUNK_ID = 'OFNI'; // 'INFO'
static const UINT32 TMAP_CHUNK_ID = 'PAMT'; // 'TMAP'
static const UINT32 TRKS_CHUNK_ID = 'SKRT'; // 'TRKS'
static const UINT32 WRIT_CHUNK_ID = 'TIRW'; // 'WRIT' - WOZv2
static const UINT32 META_CHUNK_ID = 'ATEM'; // 'META'
struct InfoChunk
{
UINT32 id;
UINT32 size;
BYTE version;
BYTE diskType;
BYTE writeProtected; // 1 = Floppy is write protected
BYTE synchronized; // 1 = Cross track sync was used during imaging
BYTE cleaned; // 1 = MC3470 fake bits have been removed
BYTE creator[32]; // Name of software that created the WOZ file.
// String in UTF-8. No BOM. Padded to 32 bytes
// using space character (0x20).
static const BYTE maxSupportedVersion = 2;
static const BYTE diskType5_25 = 1;
static const BYTE diskType3_5 = 2;
};
struct InfoChunkv2
{
InfoChunk v1;
BYTE diskSides; // 5.25 will always be 1; 3.5 can be 1 or 2
BYTE bootSectorFormat;
BYTE optimalBitTiming; // in 125ns increments (And a standard bit rate for 5.25 disk would be 32 (4us))
UINT16 compatibleHardware;
UINT16 requiredRAM; // in K (1024 bytes)
UINT16 largestTrack; // in blocks (512 bytes)
static const BYTE bootUnknown = 0;
static const BYTE bootSector16 = 1;
static const BYTE bootSector13 = 2;
static const BYTE bootSectorBoth = 3;
static const BYTE optimalBitTiming5_25 = 32;
};
InfoChunkv2* m_pInfo;
};
#pragma pack(pop) #pragma pack(pop)
//------------------------------------- //-------------------------------------
@ -190,7 +289,8 @@ class CImageHelperBase
public: public:
CImageHelperBase(const bool bIsFloppy) : CImageHelperBase(const bool bIsFloppy) :
m_2IMGHelper(bIsFloppy), m_2IMGHelper(bIsFloppy),
m_Result2IMG(eMismatch) m_Result2IMG(eMismatch),
m_WOZHelper()
{ {
} }
virtual ~CImageHelperBase(void) virtual ~CImageHelperBase(void)
@ -202,7 +302,7 @@ public:
ImageError_e Open(LPCTSTR pszImageFilename, ImageInfo* pImageInfo, const bool bCreateIfNecessary, std::string& strFilenameInZip); ImageError_e Open(LPCTSTR pszImageFilename, ImageInfo* pImageInfo, const bool bCreateIfNecessary, std::string& strFilenameInZip);
void Close(ImageInfo* pImageInfo, const bool bDeleteFile); void Close(ImageInfo* pImageInfo, const bool bDeleteFile);
virtual CImageBase* Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* pszExt, DWORD& dwOffset, bool* pWriteProtected_) = 0; virtual CImageBase* Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* pszExt, DWORD& dwOffset, bool& writeProtected, BYTE*& pTrackMap, BYTE& optimalBitTiming) = 0;
virtual CImageBase* GetImageForCreation(const TCHAR* pszExt, DWORD* pCreateImageSize) = 0; virtual CImageBase* GetImageForCreation(const TCHAR* pszExt, DWORD* pCreateImageSize) = 0;
virtual UINT GetMaxImageSize(void) = 0; virtual UINT GetMaxImageSize(void) = 0;
virtual UINT GetMinDetectSize(const UINT uImageSize, bool* pTempDetectBuffer) = 0; virtual UINT GetMinDetectSize(const UINT uImageSize, bool* pTempDetectBuffer) = 0;
@ -211,6 +311,9 @@ protected:
ImageError_e CheckGZipFile(LPCTSTR pszImageFilename, ImageInfo* pImageInfo); ImageError_e CheckGZipFile(LPCTSTR pszImageFilename, ImageInfo* pImageInfo);
ImageError_e CheckZipFile(LPCTSTR pszImageFilename, ImageInfo* pImageInfo, std::string& strFilenameInZip); ImageError_e CheckZipFile(LPCTSTR pszImageFilename, ImageInfo* pImageInfo, std::string& strFilenameInZip);
ImageError_e CheckNormalFile(LPCTSTR pszImageFilename, ImageInfo* pImageInfo, const bool bCreateIfNecessary); ImageError_e CheckNormalFile(LPCTSTR pszImageFilename, ImageInfo* pImageInfo, const bool bCreateIfNecessary);
void GetCharLowerExt(TCHAR* pszExt, LPCTSTR pszImageFilename, const UINT uExtSize);
void GetCharLowerExt2(TCHAR* pszExt, LPCTSTR pszImageFilename, const UINT uExtSize);
void SetImageInfo(ImageInfo* pImageInfo, FileType_e eFileGZip, DWORD dwOffset, CImageBase* pImageType, DWORD dwSize);
UINT GetNumImages(void) { return m_vecImageTypes.size(); }; UINT GetNumImages(void) { return m_vecImageTypes.size(); };
CImageBase* GetImage(UINT uIndex) { _ASSERT(uIndex<GetNumImages()); return m_vecImageTypes[uIndex]; } CImageBase* GetImage(UINT uIndex) { _ASSERT(uIndex<GetNumImages()); return m_vecImageTypes[uIndex]; }
@ -233,6 +336,7 @@ protected:
C2IMGHelper m_2IMGHelper; C2IMGHelper m_2IMGHelper;
eDetectResult m_Result2IMG; eDetectResult m_Result2IMG;
CWOZHelper m_WOZHelper;
}; };
//------------------------------------- //-------------------------------------
@ -243,7 +347,7 @@ public:
CDiskImageHelper(void); CDiskImageHelper(void);
virtual ~CDiskImageHelper(void) {} virtual ~CDiskImageHelper(void) {}
virtual CImageBase* Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* pszExt, DWORD& dwOffset, bool* pWriteProtected_); virtual CImageBase* Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* pszExt, DWORD& dwOffset, bool& writeProtected, BYTE*& pTrackMap, BYTE& optimalBitTiming);
virtual CImageBase* GetImageForCreation(const TCHAR* pszExt, DWORD* pCreateImageSize); virtual CImageBase* GetImageForCreation(const TCHAR* pszExt, DWORD* pCreateImageSize);
virtual UINT GetMaxImageSize(void); virtual UINT GetMaxImageSize(void);
virtual UINT GetMinDetectSize(const UINT uImageSize, bool* pTempDetectBuffer); virtual UINT GetMinDetectSize(const UINT uImageSize, bool* pTempDetectBuffer);
@ -269,7 +373,7 @@ public:
CHardDiskImageHelper(void); CHardDiskImageHelper(void);
virtual ~CHardDiskImageHelper(void) {} virtual ~CHardDiskImageHelper(void) {}
virtual CImageBase* Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* pszExt, DWORD& dwOffset, bool* pWriteProtected_); virtual CImageBase* Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* pszExt, DWORD& dwOffset, bool& writeProtected, BYTE*& pTrackMap, BYTE& optimalBitTiming);
virtual CImageBase* GetImageForCreation(const TCHAR* pszExt, DWORD* pCreateImageSize); virtual CImageBase* GetImageForCreation(const TCHAR* pszExt, DWORD* pCreateImageSize);
virtual UINT GetMaxImageSize(void); virtual UINT GetMaxImageSize(void);
virtual UINT GetMinDetectSize(const UINT uImageSize, bool* pTempDetectBuffer); virtual UINT GetMinDetectSize(const UINT uImageSize, bool* pTempDetectBuffer);

View File

@ -574,7 +574,7 @@ void KeybLoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version)
keycode = (BYTE) yamlLoadHelper.LoadUint(SS_YAML_KEY_LASTKEY); keycode = (BYTE) yamlLoadHelper.LoadUint(SS_YAML_KEY_LASTKEY);
if (version == 2) if (version >= 2)
keywaiting = (BOOL) yamlLoadHelper.LoadBool(SS_YAML_KEY_KEYWAITING); keywaiting = (BOOL) yamlLoadHelper.LoadBool(SS_YAML_KEY_KEYWAITING);
yamlLoadHelper.PopMap(); yamlLoadHelper.PopMap();

View File

@ -1745,6 +1745,8 @@ void MemReset()
g_eExpansionRomType = eExpRomNull; g_eExpansionRomType = eExpRomNull;
g_uPeripheralRomSlot = 0; g_uPeripheralRomSlot = 0;
ZeroMemory(memdirty, 0x100);
// //
int iByte; int iByte;

View File

@ -92,7 +92,7 @@ struct DISK2_Unit
DWORD spinning; DWORD spinning;
DWORD writelight; DWORD writelight;
int nibbles; int nibbles;
BYTE nTrack[NIBBLES_PER_TRACK]; BYTE nTrack[NIBBLES_PER_TRACK_NIB];
}; };
struct SS_CARD_DISK2 struct SS_CARD_DISK2

View File

@ -342,6 +342,34 @@ std::string YamlLoadHelper::LoadString(const std::string& key)
return value; return value;
} }
float YamlLoadHelper::LoadFloat(const std::string key)
{
bool bFound;
std::string value = m_yamlHelper.GetMapValue(*m_pMapYaml, key, bFound);
if (value == "")
{
m_bDoGetMapRemainder = false;
throw std::string(m_currentMapName + ": Missing: " + key);
}
#if (_MSC_VER >= 1900)
return strtof(value.c_str(), NULL); // MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015 version 14.0)
#else
return (float) strtod(value.c_str(), NULL); // NB. strtof() requires VS2015
#endif
}
double YamlLoadHelper::LoadDouble(const std::string key)
{
bool bFound;
std::string value = m_yamlHelper.GetMapValue(*m_pMapYaml, key, bFound);
if (value == "")
{
m_bDoGetMapRemainder = false;
throw std::string(m_currentMapName + ": Missing: " + key);
}
return strtod(value.c_str(), NULL);
}
void YamlLoadHelper::LoadMemory(const LPBYTE pMemBase, const size_t size) void YamlLoadHelper::LoadMemory(const LPBYTE pMemBase, const size_t size)
{ {
m_yamlHelper.LoadMemory(*m_pMapYaml, pMemBase, size); m_yamlHelper.LoadMemory(*m_pMapYaml, pMemBase, size);
@ -371,7 +399,7 @@ void YamlSaveHelper::SaveUint(const char* key, UINT value)
void YamlSaveHelper::SaveHexUint4(const char* key, UINT value) void YamlSaveHelper::SaveHexUint4(const char* key, UINT value)
{ {
Save("%s: 0x%01X\n", key, value); Save("%s: 0x%01X\n", key, value & 0xf);
} }
void YamlSaveHelper::SaveHexUint8(const char* key, UINT value) void YamlSaveHelper::SaveHexUint8(const char* key, UINT value)
@ -414,6 +442,16 @@ void YamlSaveHelper::SaveString(const char* key, const char* value)
Save("%s: %s\n", key, (value[0] != 0) ? value : "\"\""); Save("%s: %s\n", key, (value[0] != 0) ? value : "\"\"");
} }
void YamlSaveHelper::SaveFloat(const char* key, float value)
{
Save("%s: %f\n", key, value);
}
void YamlSaveHelper::SaveDouble(const char* key, double value)
{
Save("%s: %f\n", key, value);
}
// Pre: uMemSize must be multiple of 8 // Pre: uMemSize must be multiple of 8
void YamlSaveHelper::SaveMemory(const LPBYTE pMemBase, const UINT uMemSize) void YamlSaveHelper::SaveMemory(const LPBYTE pMemBase, const UINT uMemSize)
{ {

View File

@ -97,6 +97,8 @@ public:
bool LoadBool(const std::string key); bool LoadBool(const std::string key);
std::string LoadString_NoThrow(const std::string& key, bool& bFound); std::string LoadString_NoThrow(const std::string& key, bool& bFound);
std::string LoadString(const std::string& key); std::string LoadString(const std::string& key);
float LoadFloat(const std::string key);
double LoadDouble(const std::string key);
void LoadMemory(const LPBYTE pMemBase, const size_t size); void LoadMemory(const LPBYTE pMemBase, const size_t size);
bool GetSubMap(const std::string key) bool GetSubMap(const std::string key)
@ -213,7 +215,9 @@ public:
void SaveHexUint32(const char* key, UINT value); void SaveHexUint32(const char* key, UINT value);
void SaveHexUint64(const char* key, UINT64 value); void SaveHexUint64(const char* key, UINT64 value);
void SaveBool(const char* key, bool value); void SaveBool(const char* key, bool value);
void SaveString(const char* key, const char* value); void SaveString(const char* key, const char* value);
void SaveFloat(const char* key, float value);
void SaveDouble(const char* key, double value);
void SaveMemory(const LPBYTE pMemBase, const UINT uMemSize); void SaveMemory(const LPBYTE pMemBase, const UINT uMemSize);
class Label class Label