diff --git a/AppleWinExpress2008.vcproj b/AppleWinExpress2008.vcproj
index 0402dd56..69a050f9 100644
--- a/AppleWinExpress2008.vcproj
+++ b/AppleWinExpress2008.vcproj
@@ -26,7 +26,6 @@
>
+
+
+
+
@@ -850,6 +856,10 @@
RelativePath=".\source\DiskImageHelper.h"
>
+
+
diff --git a/AppleWinExpress2013.vcxproj b/AppleWinExpress2013.vcxproj
index bedd9492..63aa1764 100644
--- a/AppleWinExpress2013.vcxproj
+++ b/AppleWinExpress2013.vcxproj
@@ -63,8 +63,10 @@
+
+
@@ -145,6 +147,7 @@
+
@@ -366,7 +369,7 @@
Windows
true
- htmlhelp.lib;comctl32.lib;ddraw.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;Advapi32.lib;shell32.lib;Comdlg32.lib;ole32.lib;wsock32.lib;%(AdditionalDependencies)
+ htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;%(AdditionalDependencies)
"type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='X86' publicKeyToken='6595b64144ccf1df' language='*'"
@@ -390,7 +393,7 @@
Windows
true
- htmlhelp.lib;comctl32.lib;ddraw.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;Advapi32.lib;shell32.lib;Comdlg32.lib;ole32.lib;wsock32.lib;%(AdditionalDependencies)
+ htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;%(AdditionalDependencies)
"type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='X86' publicKeyToken='6595b64144ccf1df' language='*'"
@@ -419,7 +422,7 @@
true
true
true
- htmlhelp.lib;comctl32.lib;ddraw.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;Advapi32.lib;shell32.lib;Comdlg32.lib;ole32.lib;wsock32.lib;%(AdditionalDependencies)
+ htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;%(AdditionalDependencies)
UseLinkTimeCodeGeneration
"type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='X86' publicKeyToken='6595b64144ccf1df' language='*'"
@@ -449,7 +452,7 @@
true
true
true
- htmlhelp.lib;comctl32.lib;ddraw.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;Advapi32.lib;shell32.lib;Comdlg32.lib;ole32.lib;wsock32.lib;%(AdditionalDependencies)
+ htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;advapi32.lib;shell32.lib;comdlg32.lib;ole32.lib;wsock32.lib;%(AdditionalDependencies)
UseLinkTimeCodeGeneration
"type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='X86' publicKeyToken='6595b64144ccf1df' language='*'"
diff --git a/AppleWinExpress2013.vcxproj.filters b/AppleWinExpress2013.vcxproj.filters
index be9fb2c9..d1376eee 100644
--- a/AppleWinExpress2013.vcxproj.filters
+++ b/AppleWinExpress2013.vcxproj.filters
@@ -58,6 +58,9 @@
Source Files\Disk
+
+ Source Files\Disk
+
Source Files\Disk
@@ -270,12 +273,18 @@
Source Files\Disk
+
+ Source Files\Disk
+
Source Files\Disk
Source Files\Disk
+
+ Source Files\Disk
+
Source Files\Disk
diff --git a/AppleWinExpress2015.vcxproj b/AppleWinExpress2015.vcxproj
index fc0c6adc..4467abb4 100644
--- a/AppleWinExpress2015.vcxproj
+++ b/AppleWinExpress2015.vcxproj
@@ -63,8 +63,10 @@
+
+
@@ -145,6 +147,7 @@
+
@@ -366,7 +369,7 @@
Windows
true
- htmlhelp.lib;comctl32.lib;ddraw.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;Advapi32.lib;shell32.lib;Comdlg32.lib;ole32.lib;wsock32.lib;%(AdditionalDependencies)
+ htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;Advapi32.lib;shell32.lib;Comdlg32.lib;ole32.lib;wsock32.lib;%(AdditionalDependencies)
"type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='X86' publicKeyToken='6595b64144ccf1df' language='*'"
@@ -390,7 +393,7 @@
Windows
true
- htmlhelp.lib;comctl32.lib;ddraw.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;Advapi32.lib;shell32.lib;Comdlg32.lib;ole32.lib;wsock32.lib;%(AdditionalDependencies)
+ htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;Advapi32.lib;shell32.lib;Comdlg32.lib;ole32.lib;wsock32.lib;%(AdditionalDependencies)
"type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='X86' publicKeyToken='6595b64144ccf1df' language='*'"
@@ -419,7 +422,7 @@
true
true
true
- htmlhelp.lib;comctl32.lib;ddraw.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;Advapi32.lib;shell32.lib;Comdlg32.lib;ole32.lib;wsock32.lib;%(AdditionalDependencies)
+ htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;Advapi32.lib;shell32.lib;Comdlg32.lib;ole32.lib;wsock32.lib;%(AdditionalDependencies)
UseLinkTimeCodeGeneration
"type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='X86' publicKeyToken='6595b64144ccf1df' language='*'"
@@ -449,7 +452,7 @@
true
true
true
- htmlhelp.lib;comctl32.lib;ddraw.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;Advapi32.lib;shell32.lib;Comdlg32.lib;ole32.lib;wsock32.lib;%(AdditionalDependencies)
+ htmlhelp.lib;comctl32.lib;winmm.lib;dsound.lib;dxguid.lib;version.lib;strmiids.lib;dinput8.lib;user32.lib;gdi32.lib;Advapi32.lib;shell32.lib;Comdlg32.lib;ole32.lib;wsock32.lib;%(AdditionalDependencies)
UseLinkTimeCodeGeneration
"type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='X86' publicKeyToken='6595b64144ccf1df' language='*'"
diff --git a/AppleWinExpress2015.vcxproj.filters b/AppleWinExpress2015.vcxproj.filters
index be9fb2c9..d1376eee 100644
--- a/AppleWinExpress2015.vcxproj.filters
+++ b/AppleWinExpress2015.vcxproj.filters
@@ -58,6 +58,9 @@
Source Files\Disk
+
+ Source Files\Disk
+
Source Files\Disk
@@ -270,12 +273,18 @@
Source Files\Disk
+
+ Source Files\Disk
+
Source Files\Disk
Source Files\Disk
+
+ Source Files\Disk
+
Source Files\Disk
diff --git a/AppleWinExpress2017.vcxproj b/AppleWinExpress2017.vcxproj
index 0a7b30fc..c1e10c1e 100644
--- a/AppleWinExpress2017.vcxproj
+++ b/AppleWinExpress2017.vcxproj
@@ -63,8 +63,10 @@
+
+
@@ -145,6 +147,7 @@
+
diff --git a/AppleWinExpress2017.vcxproj.filters b/AppleWinExpress2017.vcxproj.filters
index c18a2c2c..baba23fc 100644
--- a/AppleWinExpress2017.vcxproj.filters
+++ b/AppleWinExpress2017.vcxproj.filters
@@ -58,6 +58,9 @@
Source Files\Disk
+
+ Source Files\Disk
+
Source Files\Disk
@@ -270,12 +273,18 @@
Source Files\Disk
+
+ Source Files\Disk
+
Source Files\Disk
Source Files\Disk
+
+ Source Files\Disk
+
Source Files\Disk
diff --git a/source/Applewin.cpp b/source/Applewin.cpp
index 1668b8fd..26f0827a 100644
--- a/source/Applewin.cpp
+++ b/source/Applewin.cpp
@@ -299,7 +299,7 @@ static void ContinueExecution(void)
const DWORD uActualCyclesExecuted = CpuExecute(uCyclesToExecute, bVideoUpdate);
g_dwCyclesThisFrame += uActualCyclesExecuted;
- DiskUpdatePosition(uActualCyclesExecuted);
+ DiskUpdateDriveState(uActualCyclesExecuted);
JoyUpdateButtonLatch(nExecutionPeriodUsec); // Button latch time is independent of CPU clock frequency
sg_SSC.CommUpdate(uActualCyclesExecuted);
diff --git a/source/Debugger/Debug.cpp b/source/Debugger/Debug.cpp
index 39ca069e..69ccb862 100644
--- a/source/Debugger/Debug.cpp
+++ b/source/Debugger/Debug.cpp
@@ -4109,6 +4109,9 @@ static TCHAR g_sMemoryLoadSaveFileName[ MAX_PATH ] = TEXT("");
//===========================================================================
Update_t CmdConfigGetDebugDir (int nArgs)
{
+ if( nArgs != 0 )
+ return Help_Arg_1( CMD_CONFIG_GET_DEBUG_DIR );
+
TCHAR sPath[ MAX_PATH + 8 ];
// TODO: debugger dir has no ` CONSOLE_COLOR_ESCAPE_CHAR ?!?!
ConsoleBufferPushFormat( sPath, "Path: %s", g_sCurrentDir );
@@ -4120,27 +4123,48 @@ Update_t CmdConfigGetDebugDir (int nArgs)
//===========================================================================
Update_t CmdConfigSetDebugDir (int nArgs)
{
- //if( nArgs > 2 )
- // return;
+ if ( nArgs > 1 )
+ return Help_Arg_1( CMD_CONFIG_SET_DEBUG_DIR );
+
+ if ( nArgs == 0 )
+ return CmdConfigGetDebugDir(0);
- // PWD directory
#if _WIN32
// http://msdn.microsoft.com/en-us/library/aa365530(VS.85).aspx
- TCHAR sPath[ MAX_PATH + 1 ];
- _tcscpy( sPath, g_sCurrentDir ); // TODO: debugger dir has no ` CONSOLE_COLOR_ESCAPE_CHAR ?!?!
- _tcscat( sPath, g_aArgs[ 1 ].sArg );
- if( SetCurrentImageDir( sPath ) )
+ // TODO: Support paths prefixed with "\\?\" (for long/unicode pathnames)
+ if (strncmp("\\\\?\\", g_aArgs[1].sArg, 4) == 0)
+ return Help_Arg_1( CMD_CONFIG_SET_DEBUG_DIR );
+
+ TCHAR sPath[ MAX_PATH + 1 ];
+
+ if (g_aArgs[1].sArg[1] == ':') // Absolute
+ {
+ _tcscpy( sPath, g_aArgs[1].sArg );
+ }
+ else if (g_aArgs[1].sArg[0] == '\\') // Absolute
+ {
+ if (g_sCurrentDir[1] == ':')
+ {
+ _tcsncpy( sPath, g_sCurrentDir, 2 ); // Prefix with drive letter & colon
+ sPath[2] = 0;
+ }
+ _tcscat( sPath, g_aArgs[1].sArg );
+ }
+ else // Relative
+ {
+ // TODO: Support ".." - currently just appends (which still works)
+ _tcscpy( sPath, g_sCurrentDir ); // TODO: debugger dir has no ` CONSOLE_COLOR_ESCAPE_CHAR ?!?!
+ _tcscat( sPath, g_aArgs[1].sArg );
+ }
+
+ if ( SetCurrentImageDir( sPath ) )
nArgs = 0; // intentional fall into
#else
#error "Need chdir() implemented"
#endif
- // PWD
- if( nArgs == 0 )
- return CmdConfigGetDebugDir(0);
-
- return ConsoleUpdate();
+ return CmdConfigGetDebugDir(0); // Show the new PWD
}
diff --git a/source/Disk.cpp b/source/Disk.cpp
index 05761012..42d2474e 100644
--- a/source/Disk.cpp
+++ b/source/Disk.cpp
@@ -33,7 +33,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "SaveState_Structs_v1.h"
#include "AppleWin.h"
+#include "CPU.h"
#include "Disk.h"
+#include "DiskLog.h"
+#include "DiskFormatTrack.h"
#include "DiskImage.h"
#include "Frame.h"
#include "Log.h"
@@ -44,73 +47,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "..\resource\resource.h"
-#define LOG_DISK_ENABLED 0
-#define LOG_DISK_TRACKS 1
-#define LOG_DISK_MOTOR 0
-#define LOG_DISK_PHASES 0
-#define LOG_DISK_NIBBLES 0
-
-// __VA_ARGS__ not supported on MSVC++ .NET 7.x
-#if (LOG_DISK_ENABLED)
- #if !defined(_VC71)
- #define LOG_DISK(format, ...) LOG(format, __VA_ARGS__)
- #else
- #define LOG_DISK LogOutput
- #endif
-#else
- #if !defined(_VC71)
- #define LOG_DISK(...)
- #else
- #define LOG_DISK(x)
- #endif
+#if LOG_DISK_NIBBLES_USE_RUNTIME_VAR
+static bool g_bLogDisk_NibblesRW = false; // From VS Debugger, change this to true/false during runtime for precise nibble logging
#endif
// Public _________________________________________________________________________________________
- BOOL enhancedisk = 1; // TODO: Make static & add accessor funcs
+BOOL enhancedisk = 1; // TODO: Make static & add accessor funcs
// Private ________________________________________________________________________________________
- struct Disk_t
- {
- TCHAR imagename[ MAX_DISK_IMAGE_NAME + 1 ]; // (ie. no extension)
- TCHAR fullname [ MAX_DISK_FULL_NAME + 1 ]; // or : This is persisted to the snapshot file
- std::string strFilenameInZip; // "" or
- ImageInfo* imagehandle; // Init'd by DiskInsert() -> ImageOpen()
- bool bWriteProtected;
- //
- int track;
- LPBYTE trackimage;
- int phase;
- int byte;
- BOOL trackimagedata;
- BOOL trackimagedirty;
- DWORD spinning;
- DWORD writelight;
- int nibbles; // Init'd by ReadTrack() -> ImageReadTrack()
-
- const Disk_t& operator= (const Disk_t& other)
- {
- memcpy(imagename, other.imagename, sizeof(imagename));
- memcpy(fullname , other.fullname, sizeof(fullname));
- strFilenameInZip = other.strFilenameInZip;
- imagehandle = other.imagehandle;
- bWriteProtected = other.bWriteProtected;
- track = other.track;
- trackimage = other.trackimage;
- phase = other.phase;
- byte = other.byte;
- trackimagedata = other.trackimagedata;
- trackimagedirty = other.trackimagedirty;
- spinning = other.spinning;
- writelight = other.writelight;
- nibbles = other.nibbles;
- return *this;
- }
- };
-
static WORD currdrive = 0;
-static BOOL diskaccessed = 0;
static Disk_t g_aFloppyDisk[NUM_DRIVES];
static BYTE floppylatch = 0;
static BOOL floppymotoron = 0;
@@ -119,8 +66,10 @@ static BOOL floppywritemode = 0;
static WORD phases = 0; // state bits for stepper magnet phases 0 - 3
static bool g_bSaveDiskImage = true; // Save the DiskImage name to Registry
static UINT g_uSlot = 0;
+static unsigned __int64 g_uDiskLastCycle = 0;
+static FormatTrack g_formatTrack;
-static void CheckSpinning();
+static void CheckSpinning(const ULONG nCyclesLeft);
static Disk_Status_e GetDriveLightStatus( const int iDrive );
static bool IsDriveValid( const int iDrive );
static void ReadTrack (int drive);
@@ -231,7 +180,8 @@ void Disk_SaveLastDiskImage(const int iDrive)
//===========================================================================
-static void CheckSpinning(void)
+// Called by DiskControlMotor() & DiskEnable()
+static void CheckSpinning(const ULONG nCyclesLeft)
{
DWORD modechange = (floppymotoron && !g_aFloppyDisk[currdrive].spinning);
@@ -239,8 +189,14 @@ static void CheckSpinning(void)
g_aFloppyDisk[currdrive].spinning = SPINNING_CYCLES;
if (modechange)
- //FrameRefreshStatus(DRAW_LEDS);
FrameDrawDiskLEDS( (HDC)0 );
+
+ if (modechange)
+ {
+ // Set g_uDiskLastCycle when motor changes: not spinning (ie. off for 1 sec) -> on
+ CpuCalcCycles(nCyclesLeft);
+ g_uDiskLastCycle = g_nCumulativeCycles;
+ }
}
//===========================================================================
@@ -403,11 +359,16 @@ void DiskBoot(void)
static void __stdcall DiskControlMotor(WORD, WORD address, BYTE, BYTE, ULONG uExecutedCycles)
{
- floppymotoron = address & 1;
+ BOOL newState = address & 1;
+
+ if (newState != floppymotoron) // motor changed state
+ g_formatTrack.DriveNotWritingTrack();
+
+ floppymotoron = newState;
#if LOG_DISK_MOTOR
LOG_DISK("motor %s\r\n", (floppymotoron) ? "on" : "off");
#endif
- CheckSpinning();
+ CheckSpinning(uExecutedCycles);
}
//===========================================================================
@@ -454,6 +415,8 @@ static void __stdcall DiskControlStepper(WORD, WORD address, BYTE, BYTE, ULONG u
DiskFlushCurrentTrack(currdrive);
fptr->track = newtrack;
fptr->trackimagedata = 0;
+
+ g_formatTrack.DriveNotWritingTrack();
}
// Feature Request #201 Show track status
@@ -463,6 +426,7 @@ static void __stdcall DiskControlStepper(WORD, WORD address, BYTE, BYTE, ULONG u
#else
// substitute alternate stepping code here to test
#endif
+
#if LOG_DISK_PHASES
LOG_DISK("track $%02X%s phases %d%d%d%d phase %d %s address $%4X\r\n",
fptr->phase >> 1,
@@ -495,9 +459,12 @@ void DiskDestroy(void)
static void __stdcall DiskEnable(WORD, WORD address, BYTE, BYTE, ULONG uExecutedCycles)
{
currdrive = address & 1;
+#if LOG_DISK_ENABLE_DRIVE
+ LOG_DISK("enable drive: %d\r\n", currdrive);
+#endif
g_aFloppyDisk[!currdrive].spinning = 0;
g_aFloppyDisk[!currdrive].writelight = 0;
- CheckSpinning();
+ CheckSpinning(uExecutedCycles);
}
//===========================================================================
@@ -609,7 +576,8 @@ ImageError_e DiskInsert(const int iDrive, LPCTSTR pszImageFilename, const bool b
if (Error == eIMAGE_ERROR_NONE && ImageIsMultiFileZip(fptr->imagehandle))
{
TCHAR szText[100+MAX_PATH];
- wsprintf(szText, "Only the first file in a multi-file zip is supported\nUse disk image '%s' ?", fptr->strFilenameInZip.c_str());
+ szText[sizeof(szText)-1] = 0;
+ _snprintf(szText, sizeof(szText)-1, "Only the first file in a multi-file zip is supported\nUse disk image '%s' ?", fptr->strFilenameInZip.c_str());
int nRes = MessageBox(g_hFrameWindow, szText, TEXT("Multi-Zip Warning"), MB_ICONWARNING | MB_YESNO | MB_SETFOREGROUND);
if (nRes == IDNO)
{
@@ -645,45 +613,51 @@ BOOL DiskIsSpinning(void)
void DiskNotifyInvalidImage(const int iDrive, LPCTSTR pszImageFilename, const ImageError_e Error)
{
TCHAR szBuffer[MAX_PATH+128];
+ szBuffer[sizeof(szBuffer)-1] = 0;
switch (Error)
{
case eIMAGE_ERROR_UNABLE_TO_OPEN:
case eIMAGE_ERROR_UNABLE_TO_OPEN_GZ:
case eIMAGE_ERROR_UNABLE_TO_OPEN_ZIP:
- wsprintf(
+ _snprintf(
szBuffer,
+ sizeof(szBuffer)-1,
TEXT("Unable to open the file %s."),
pszImageFilename);
break;
case eIMAGE_ERROR_BAD_SIZE:
- wsprintf(
+ _snprintf(
szBuffer,
+ sizeof(szBuffer)-1,
TEXT("Unable to use the file %s\nbecause the ")
TEXT("disk image is an unsupported size."),
pszImageFilename);
break;
case eIMAGE_ERROR_BAD_FILE:
- wsprintf(
+ _snprintf(
szBuffer,
+ sizeof(szBuffer)-1,
TEXT("Unable to use the file %s\nbecause the ")
TEXT("OS can't access it."),
pszImageFilename);
break;
case eIMAGE_ERROR_UNSUPPORTED:
- wsprintf(
+ _snprintf(
szBuffer,
+ sizeof(szBuffer)-1,
TEXT("Unable to use the file %s\nbecause the ")
TEXT("disk image format is not recognized."),
pszImageFilename);
break;
case eIMAGE_ERROR_UNSUPPORTED_HDV:
- wsprintf(
+ _snprintf(
szBuffer,
+ sizeof(szBuffer)-1,
TEXT("Unable to use the file %s\n")
TEXT("because this UniDisk 3.5/Apple IIGS/hard-disk image is not supported.\n")
TEXT("Try inserting as a hard-disk image instead."),
@@ -691,8 +665,9 @@ void DiskNotifyInvalidImage(const int iDrive, LPCTSTR pszImageFilename, const Im
break;
case eIMAGE_ERROR_UNSUPPORTED_MULTI_ZIP:
- wsprintf(
+ _snprintf(
szBuffer,
+ sizeof(szBuffer)-1,
TEXT("Unable to use the file %s\nbecause the ")
TEXT("first file (%s) in this multi-zip archive is not recognized.\n")
TEXT("Try unzipping and using the disk images directly.\n"),
@@ -702,20 +677,38 @@ void DiskNotifyInvalidImage(const int iDrive, LPCTSTR pszImageFilename, const Im
case eIMAGE_ERROR_GZ:
case eIMAGE_ERROR_ZIP:
- wsprintf(
+ _snprintf(
szBuffer,
+ sizeof(szBuffer)-1,
TEXT("Unable to use the compressed file %s\nbecause the ")
TEXT("compressed disk image is corrupt/unsupported."),
pszImageFilename);
break;
case eIMAGE_ERROR_FAILED_TO_GET_PATHNAME:
- wsprintf(
+ _snprintf(
szBuffer,
+ sizeof(szBuffer)-1,
TEXT("Unable to GetFullPathName() for the file: %s."),
pszImageFilename);
break;
-
+
+ case eIMAGE_ERROR_ZEROLENGTH_WRITEPROTECTED:
+ _snprintf(
+ szBuffer,
+ sizeof(szBuffer)-1,
+ TEXT("Unsupported zero-length write-protected file: %s."),
+ pszImageFilename);
+ break;
+
+ case eIMAGE_ERROR_FAILED_TO_INIT_ZEROLENGTH:
+ _snprintf(
+ szBuffer,
+ sizeof(szBuffer)-1,
+ TEXT("Failed to resize the zero-length file: %s."),
+ pszImageFilename);
+ break;
+
default:
// IGNORE OTHER ERRORS SILENTLY
return;
@@ -779,13 +772,45 @@ bool Disk_IsDriveEmpty(const int iDrive)
//===========================================================================
+#if LOG_DISK_NIBBLES_WRITE
+static UINT64 g_uWriteLastCycle = 0;
+static UINT g_uSyncFFCount = 0;
+
+static bool LogWriteCheckSyncFF(BYTE floppylatch, ULONG& uCycleDelta)
+{
+ bool bIsSyncFF = false;
+
+ if (g_uWriteLastCycle == 0) // Reset to 0 when write mode is enabled
+ {
+ uCycleDelta = 0;
+ if (floppylatch == 0xFF)
+ {
+ g_uSyncFFCount = 0;
+ bIsSyncFF = true;
+ }
+ }
+ else
+ {
+ uCycleDelta = (ULONG) (g_nCumulativeCycles - g_uWriteLastCycle);
+ if (floppylatch == 0xFF && uCycleDelta > 32)
+ {
+ g_uSyncFFCount++;
+ bIsSyncFF = true;
+ }
+ }
+
+ g_uWriteLastCycle = g_nCumulativeCycles;
+ return bIsSyncFF;
+}
+#endif
+
+//===========================================================================
+
static void __stdcall DiskReadWrite(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nCyclesLeft)
{
/* floppyloadmode = 0; */
Disk_t * fptr = &g_aFloppyDisk[currdrive];
- diskaccessed = 1;
-
if (!fptr->trackimagedata && fptr->imagehandle)
ReadTrack(currdrive);
@@ -795,19 +820,68 @@ static void __stdcall DiskReadWrite(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULO
return;
}
+ // Improve precision of "authentic" drive mode - GH#125
+ UINT uSpinNibbleCount = 0;
+ CpuCalcCycles(nCyclesLeft); // g_nCumulativeCycles required for uSpinNibbleCount & LogWriteCheckSyncFF()
+
+ if (!enhancedisk && fptr->spinning)
+ {
+ const ULONG nCycleDiff = (ULONG) (g_nCumulativeCycles - g_uDiskLastCycle);
+ g_uDiskLastCycle = g_nCumulativeCycles;
+
+ if (nCycleDiff > 40)
+ {
+ // 40 cycles for a write of a 10-bit 0xFF sync byte
+ uSpinNibbleCount = nCycleDiff >> 5; // ...but divide by 32 (not 40)
+
+ ULONG uWrapOffset = uSpinNibbleCount % fptr->nibbles;
+ fptr->byte += uWrapOffset;
+ if (fptr->byte >= fptr->nibbles)
+ fptr->byte -= fptr->nibbles;
+
+#if LOG_DISK_NIBBLES_SPIN
+ UINT uCompleteRevolutions = uSpinNibbleCount / fptr->nibbles;
+ LOG_DISK("spin: revs=%d, nibbles=%d\r\n", uCompleteRevolutions, uWrapOffset);
+#endif
+ }
+ }
+
// Should really test for drive off - after 1 second drive off delay (UTAIIe page 9-13)
// but Sherwood Forest sets shift mode and reads with the drive off, so don't check for now
if (!floppywritemode)
{
floppylatch = *(fptr->trackimage + fptr->byte);
-#if LOG_DISK_NIBBLES
- LOG_DISK("read %4X = %2X\r\n", fptr->byte, floppylatch);
+
+#if LOG_DISK_NIBBLES_READ
+ #if LOG_DISK_NIBBLES_USE_RUNTIME_VAR
+ if (g_bLogDisk_NibblesRW)
+ #endif
+ {
+ LOG_DISK("read %04X = %02X\r\n", fptr->byte, floppylatch);
+ }
+
+ g_formatTrack.DecodeLatchNibbleRead(floppylatch);
#endif
}
else if (!fptr->bWriteProtected) // && floppywritemode
{
*(fptr->trackimage + fptr->byte) = floppylatch;
fptr->trackimagedirty = 1;
+
+ g_formatTrack.DecodeLatchNibbleWrite(floppylatch, uSpinNibbleCount, fptr); // GH#125
+
+#if LOG_DISK_NIBBLES_WRITE
+ #if LOG_DISK_NIBBLES_USE_RUNTIME_VAR
+ if (g_bLogDisk_NibblesRW)
+ #endif
+ {
+ ULONG uCycleDelta = 0;
+ if (!LogWriteCheckSyncFF(floppylatch, uCycleDelta))
+ LOG_DISK("write %04X = %02X (cy=+%d)\r\n", fptr->byte, floppylatch, uCycleDelta);
+ else
+ LOG_DISK("write %04X = %02X (cy=+%d) sync #%d\r\n", fptr->byte, floppylatch, uCycleDelta, g_uSyncFFCount);
+ }
+#endif
}
if (++fptr->byte >= fptr->nibbles)
@@ -831,6 +905,8 @@ void DiskReset(const bool bIsPowerCycle/*=false*/)
floppywritemode = 0;
phases = 0;
+ g_formatTrack.Reset();
+
if (bIsPowerCycle) // GH#460
{
g_aFloppyDisk[DRIVE_1].spinning = 0;
@@ -838,7 +914,7 @@ void DiskReset(const bool bIsPowerCycle/*=false*/)
g_aFloppyDisk[DRIVE_2].spinning = 0;
g_aFloppyDisk[DRIVE_2].writelight = 0;
- FrameRefreshStatus(DRAW_LEDS,false);
+ FrameRefreshStatus(DRAW_LEDS, false);
}
}
@@ -924,6 +1000,12 @@ static void __stdcall DiskLoadWriteProtect(WORD, WORD, BYTE write, BYTE value, U
static void __stdcall DiskSetReadMode(WORD, WORD, BYTE, BYTE, ULONG)
{
floppywritemode = 0;
+
+ g_formatTrack.DriveSwitchedToReadMode(&g_aFloppyDisk[currdrive]);
+
+#if LOG_DISK_RW_MODE
+ LOG_DISK("rw mode: read\r\n");
+#endif
}
//===========================================================================
@@ -931,28 +1013,36 @@ static void __stdcall DiskSetReadMode(WORD, WORD, BYTE, BYTE, ULONG)
static void __stdcall DiskSetWriteMode(WORD, WORD, BYTE, BYTE, ULONG uExecutedCycles)
{
floppywritemode = 1;
+
+ g_formatTrack.DriveSwitchedToWriteMode(g_aFloppyDisk[currdrive].byte);
+
BOOL modechange = !g_aFloppyDisk[currdrive].writelight;
+#if LOG_DISK_RW_MODE
+ LOG_DISK("rw mode: write (mode changed=%d)\r\n", modechange ? 1 : 0);
+#endif
+#if LOG_DISK_NIBBLES_WRITE
+ g_uWriteLastCycle = 0;
+#endif
+
g_aFloppyDisk[currdrive].writelight = WRITELIGHT_CYCLES;
+
if (modechange)
- {
- //FrameRefreshStatus(DRAW_LEDS);
FrameDrawDiskLEDS( (HDC)0 );
- }
}
//===========================================================================
-void DiskUpdatePosition(DWORD cycles)
+void DiskUpdateDriveState(DWORD cycles)
{
int loop = NUM_DRIVES;
while (loop--)
{
Disk_t * fptr = &g_aFloppyDisk[loop];
- if (fptr->spinning && !floppymotoron) {
+ if (fptr->spinning && !floppymotoron)
+ {
if (!(fptr->spinning -= MIN(fptr->spinning, cycles)))
{
- // FrameRefreshStatus(DRAW_LEDS);
FrameDrawDiskLEDS( (HDC)0 );
FrameDrawDiskStatus( (HDC)0 );
}
@@ -966,21 +1056,11 @@ void DiskUpdatePosition(DWORD cycles)
{
if (!(fptr->writelight -= MIN(fptr->writelight, cycles)))
{
- //FrameRefreshStatus(DRAW_LEDS);
FrameDrawDiskLEDS( (HDC)0 );
FrameDrawDiskStatus( (HDC)0 );
}
}
-
- if ((!enhancedisk) && (!diskaccessed) && fptr->spinning)
- {
- fptr->byte += (cycles >> 5);
- if (fptr->byte >= fptr->nibbles)
- fptr->byte -= fptr->nibbles;
- }
}
-
- diskaccessed = 0;
}
//===========================================================================
@@ -1027,7 +1107,7 @@ bool DiskDriveSwap(void)
Disk_SaveLastDiskImage(DRIVE_1);
Disk_SaveLastDiskImage(DRIVE_2);
- FrameRefreshStatus(DRAW_LEDS | DRAW_BUTTON_DRIVES, false );
+ FrameRefreshStatus(DRAW_LEDS | DRAW_BUTTON_DRIVES, false);
return true;
}
@@ -1142,7 +1222,7 @@ int DiskSetSnapshot_v1(const SS_CARD_DISK2* const pSS)
phases = pSS->phases;
currdrive = pSS->currdrive;
- diskaccessed = pSS->diskaccessed;
+ //diskaccessed = pSS->diskaccessed; // deprecated
enhancedisk = pSS->enhancedisk;
floppylatch = pSS->floppylatch;
floppymotoron = pSS->floppymotoron;
@@ -1222,6 +1302,10 @@ int DiskSetSnapshot_v1(const SS_CARD_DISK2* const pSS)
//===========================================================================
+// Unit version history:
+// 2: Added: Format Track state & DiskLastCycle
+static const UINT kUNIT_VERSION = 2;
+
#define SS_YAML_VALUE_CARD_DISK2 "Disk]["
#define SS_YAML_KEY_PHASES "Phases"
@@ -1231,6 +1315,7 @@ int DiskSetSnapshot_v1(const SS_CARD_DISK2* const pSS)
#define SS_YAML_KEY_FLOPPY_LATCH "Floppy Latch"
#define SS_YAML_KEY_FLOPPY_MOTOR_ON "Floppy Motor On"
#define SS_YAML_KEY_FLOPPY_WRITE_MODE "Floppy Write Mode"
+#define SS_YAML_KEY_LAST_CYCLE "Last Cycle"
#define SS_YAML_KEY_DISK2UNIT "Unit"
#define SS_YAML_KEY_FILENAME "Filename"
@@ -1274,16 +1359,18 @@ static void DiskSaveSnapshotDisk2Unit(YamlSaveHelper& yamlSaveHelper, UINT unit)
void DiskSaveSnapshot(class YamlSaveHelper& yamlSaveHelper)
{
- YamlSaveHelper::Slot slot(yamlSaveHelper, DiskGetSnapshotCardName(), g_uSlot, 1);
+ YamlSaveHelper::Slot slot(yamlSaveHelper, DiskGetSnapshotCardName(), g_uSlot, kUNIT_VERSION);
YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE);
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_PHASES, phases);
yamlSaveHelper.SaveUint(SS_YAML_KEY_CURRENT_DRIVE, currdrive);
- yamlSaveHelper.SaveBool(SS_YAML_KEY_DISK_ACCESSED, diskaccessed == TRUE);
+ yamlSaveHelper.SaveBool(SS_YAML_KEY_DISK_ACCESSED, false); // deprecated
yamlSaveHelper.SaveBool(SS_YAML_KEY_ENHANCE_DISK, enhancedisk == TRUE);
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_FLOPPY_LATCH, floppylatch);
yamlSaveHelper.SaveBool(SS_YAML_KEY_FLOPPY_MOTOR_ON, floppymotoron == TRUE);
yamlSaveHelper.SaveBool(SS_YAML_KEY_FLOPPY_WRITE_MODE, floppywritemode == TRUE);
+ yamlSaveHelper.SaveHexUint64(SS_YAML_KEY_LAST_CYCLE, g_uDiskLastCycle); // v2
+ g_formatTrack.SaveSnapshot(yamlSaveHelper); // v2
DiskSaveSnapshotDisk2Unit(yamlSaveHelper, DRIVE_1);
DiskSaveSnapshotDisk2Unit(yamlSaveHelper, DRIVE_2);
@@ -1373,17 +1460,23 @@ bool DiskLoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT slot, UINT vers
if (slot != 6) // fixme
throw std::string("Card: wrong slot");
- if (version != 1)
+ if (version < 1 || version > kUNIT_VERSION)
throw std::string("Card: wrong version");
phases = yamlLoadHelper.LoadUint(SS_YAML_KEY_PHASES);
currdrive = yamlLoadHelper.LoadUint(SS_YAML_KEY_CURRENT_DRIVE);
- diskaccessed = yamlLoadHelper.LoadBool(SS_YAML_KEY_DISK_ACCESSED);
+ (void) yamlLoadHelper.LoadBool(SS_YAML_KEY_DISK_ACCESSED); // deprecated - but retrieve the value to avoid the "State: Unknown key (Disk Accessed)" warning
enhancedisk = yamlLoadHelper.LoadBool(SS_YAML_KEY_ENHANCE_DISK);
floppylatch = yamlLoadHelper.LoadUint(SS_YAML_KEY_FLOPPY_LATCH);
floppymotoron = yamlLoadHelper.LoadBool(SS_YAML_KEY_FLOPPY_MOTOR_ON);
floppywritemode = yamlLoadHelper.LoadBool(SS_YAML_KEY_FLOPPY_WRITE_MODE);
+ if (version >= 2)
+ {
+ g_uDiskLastCycle = yamlLoadHelper.LoadUint64(SS_YAML_KEY_LAST_CYCLE);
+ g_formatTrack.LoadSnapshot(yamlLoadHelper);
+ }
+
// Eject all disks first in case Drive-2 contains disk to be inserted into Drive-1
for(UINT i=0; i (ie. no extension)
+ TCHAR fullname [ MAX_DISK_FULL_NAME + 1 ]; // or : This is persisted to the snapshot file
+ std::string strFilenameInZip; // "" or
+ ImageInfo* imagehandle; // Init'd by DiskInsert() -> ImageOpen()
+ bool bWriteProtected;
+ //
+ int track;
+ LPBYTE trackimage;
+ int phase;
+ int byte;
+ BOOL trackimagedata;
+ BOOL trackimagedirty;
+ DWORD spinning;
+ DWORD writelight;
+ int nibbles; // Init'd by ReadTrack() -> ImageReadTrack()
+
+ const Disk_t& operator= (const Disk_t& other)
+ {
+ memcpy(imagename, other.imagename, sizeof(imagename));
+ memcpy(fullname , other.fullname, sizeof(fullname));
+ strFilenameInZip = other.strFilenameInZip;
+ imagehandle = other.imagehandle;
+ bWriteProtected = other.bWriteProtected;
+ track = other.track;
+ trackimage = other.trackimage;
+ phase = other.phase;
+ byte = other.byte;
+ trackimagedata = other.trackimagedata;
+ trackimagedirty = other.trackimagedirty;
+ spinning = other.spinning;
+ writelight = other.writelight;
+ nibbles = other.nibbles;
+ return *this;
+ }
+};
diff --git a/source/DiskFormatTrack.cpp b/source/DiskFormatTrack.cpp
new file mode 100644
index 00000000..5e821b04
--- /dev/null
+++ b/source/DiskFormatTrack.cpp
@@ -0,0 +1,340 @@
+/*
+AppleWin : An Apple //e emulator for Windows
+
+Copyright (C) 1994-1996, Michael O'Brien
+Copyright (C) 1999-2001, Oliver Schmidt
+Copyright (C) 2002-2005, Tom Charlesworth
+Copyright (C) 2006-2017, Tom Charlesworth, Michael Pohoreski, Nick Westgate
+
+AppleWin is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+AppleWin is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with AppleWin; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/* Description: Disk][ format track support
+ *
+ * Author: Various
+ */
+
+#include "StdAfx.h"
+
+#include "Disk.h"
+#include "DiskLog.h"
+#include "DiskFormatTrack.h"
+#include "Log.h"
+#include "YamlHelper.h"
+
+// Occurs on these conditions:
+// . ctor
+// . disk][ reset
+void FormatTrack::Reset(void)
+{
+ memset(m_VolTrkSecChk, 0, sizeof(m_VolTrkSecChk));
+ memset(m_VolTrkSecChk4and4, 0, sizeof(m_VolTrkSecChk4and4));
+ m_trackState = TS_GAP1;
+ m_uLast3Bytes = 0;
+ m_4and4idx = 0;
+
+ DriveNotWritingTrack();
+}
+
+// Occurs on these conditions:
+// . spin > 2 bytes (empirical from inspecting DOS3.3-INIT/ProDOS-FORMAT operations)
+// . drive stepper track change
+// . drive motor state change
+// . switch to read mode after having written a complete track
+void FormatTrack::DriveNotWritingTrack(void)
+{
+ m_bmWrittenSectorAddrFields = 0x0000;
+ m_WriteTrackStartIndex = 0;
+ m_WriteTrackHasWrapped = false;
+ m_WriteDataFieldPrologueCount = 0;
+
+#if LOG_DISK_NIBBLES_WRITE_TRACK_GAPS
+ m_DbgGap1Size = 0;
+ m_DbgGap2Size = 0;
+ m_DbgGap3Size = -1; // Data Field's epilogue has an extra 0xFF, which isn't part of the Gap3 sync-FF field
+#endif
+}
+
+void FormatTrack::UpdateOnWriteLatch(UINT uSpinNibbleCount, const Disk_t* const fptr)
+{
+ if (fptr->bWriteProtected)
+ return;
+
+ if (m_bmWrittenSectorAddrFields == 0x0000)
+ {
+ if (m_WriteTrackStartIndex == (UINT)-1) // waiting for 1st write?
+ m_WriteTrackStartIndex = fptr->byte;
+ return;
+ }
+
+ // Written at least 1 sector
+
+ if (m_WriteTrackHasWrapped)
+ return;
+
+ if (uSpinNibbleCount > 2)
+ {
+ _ASSERT(0); // Not seen this case yet
+ DriveNotWritingTrack();
+ return;
+ }
+
+ UINT uTrackIndex = fptr->byte;
+ const UINT& kTrackMaxNibbles = fptr->nibbles;
+
+ // NB. spin in write mode is only max 1-2 bytes
+ do
+ {
+ if (m_WriteTrackStartIndex == uTrackIndex) // disk has completed a revolution
+ {
+ // Occurs for: .dsk & enhance=0|1 & ProDOS-FORMAT/DOS3.3-INIT with big gap3 size (as trackimage is only 0x18F0 in size)
+ m_WriteTrackHasWrapped = true;
+
+ // Now wait until drive switched from write to read mode
+ return;
+ }
+
+ uTrackIndex = (uTrackIndex+1) % kTrackMaxNibbles;
+ }
+ while (uSpinNibbleCount--);
+}
+
+void FormatTrack::DriveSwitchedToReadMode(Disk_t* const fptr)
+{
+ if (m_bmWrittenSectorAddrFields != 0xFFFF || m_WriteDataFieldPrologueCount != 16) // written all 16 sectors?
+ return;
+
+ // Zero-fill the remainder of the track buffer:
+ // Either up to 0x18F0 (if less than 0x18F0) or up to 0x1A00 (for .nib).
+
+ // When there's no spin (enhanced=1), then 0x18F0 is still too long, eg: (see GH#125)
+ // Gap1 = 0x81, Gap3 = 0x1D (29), TrackSize = 0x1963
+ // Gap1 = 0x81, Gap3 = 0x1C (28), TrackSize = 0x1954
+ // Gap1 = 0x81, Gap3 = 0x1B (27), TrackSize = 0x1945
+ // Gap1 = 0x81, Gap3 = 0x1A (26), TrackSize = 0x1936
+ //
+ // 0x1936 - 0x81(gap1) = 0x18B5
+ // So need a track size of 0x18B0 (rounding down)
+ const UINT kShortTrackLen = 0x18B0;
+
+ LPBYTE TrackBuffer = fptr->trackimage;
+ const UINT kLongTrackLen = fptr->nibbles;
+
+ UINT uWriteTrackEndIndex = fptr->byte;
+ UINT uWrittenTrackSize = m_WriteTrackHasWrapped ? kLongTrackLen : 0;
+
+ if (m_WriteTrackStartIndex <= uWriteTrackEndIndex)
+ uWrittenTrackSize += uWriteTrackEndIndex - m_WriteTrackStartIndex;
+ else
+ uWrittenTrackSize += (kLongTrackLen - m_WriteTrackStartIndex) + uWriteTrackEndIndex;
+
+#if LOG_DISK_NIBBLES_WRITE_TRACK_GAPS
+ LOG_DISK("Gap1 = 0x%02X, Gap2 = 0x%02X, Gap3 = 0x%02X (%02d), TrackSize = 0x%04X\n", m_DbgGap1Size, m_DbgGap2Size, m_DbgGap3Size, m_DbgGap3Size, uWrittenTrackSize);
+#endif
+
+ if (uWrittenTrackSize > kShortTrackLen)
+ uWrittenTrackSize = kShortTrackLen;
+
+ int iSrc = uWriteTrackEndIndex - uWrittenTrackSize; // Rewind to start of non-overwritten part of short track
+ if (iSrc < 0)
+ iSrc += kLongTrackLen;
+
+ // S < E: | S-------E |
+ // S > E: |----E S---|
+ if ((UINT)iSrc < uWriteTrackEndIndex)
+ {
+ memset(&TrackBuffer[0], 0, iSrc);
+ memset(&TrackBuffer[uWriteTrackEndIndex], 0, kLongTrackLen - uWriteTrackEndIndex);
+ }
+ else
+ {
+ // NB. For the iSrc == uWriteTrackEndIndex case: memset() size=0
+ memset(&TrackBuffer[uWriteTrackEndIndex], 0, iSrc - uWriteTrackEndIndex);
+ }
+
+ // NB. No need to skip the zero nibbles, as INIT/FORMAT's 'read latch until high bit' loop will just consume these
+
+ DriveNotWritingTrack();
+}
+
+void FormatTrack::DriveSwitchedToWriteMode(UINT uTrackIndex)
+{
+ DecodeLatchNibbleReset();
+
+ if (m_bmWrittenSectorAddrFields == 0x0000) // written no sectors
+ {
+ m_WriteTrackStartIndex = (UINT)-1; // wait for 1st write
+ }
+}
+
+void FormatTrack::DecodeLatchNibbleReset(void)
+{
+ // ProDOS: switches to write mode between Address Field & Gap2; and between Data Field & Gap3
+ // . so if at TS_GAP2_START then stay at TS_GAP2_START
+ if (m_trackState != TS_GAP2_START)
+ m_trackState = (m_bmWrittenSectorAddrFields == 0x0000) ? TS_GAP1 : TS_GAP3;
+ m_uLast3Bytes = 0;
+ m_4and4idx = 0;
+}
+
+// This is just for debug/logging: used to output when a new Address Field has been read
+void FormatTrack::DecodeLatchNibbleRead(BYTE floppylatch)
+{
+ DecodeLatchNibble(floppylatch, false);
+}
+
+void FormatTrack::DecodeLatchNibbleWrite(BYTE floppylatch, UINT uSpinNibbleCount, const Disk_t* const fptr)
+{
+ DecodeLatchNibble(floppylatch, true);
+ UpdateOnWriteLatch(uSpinNibbleCount, fptr);
+}
+
+void FormatTrack::DecodeLatchNibble(BYTE floppylatch, BOOL bIsWrite)
+{
+ m_uLast3Bytes <<= 8;
+ m_uLast3Bytes |= floppylatch;
+ m_uLast3Bytes &= 0xFFFFFF;
+
+ if (m_trackState == TS_GAP2_START && bIsWrite) // NB. bIsWrite, as there's a read between writing Addr Field & Gap2
+ m_trackState = TS_GAP2;
+
+#if LOG_DISK_NIBBLES_WRITE_TRACK_GAPS
+ if (floppylatch == 0xFF && bIsWrite && (m_trackState == TS_GAP1 || m_trackState == TS_GAP2 || m_trackState == TS_GAP3))
+ {
+ if (m_bmWrittenSectorAddrFields == 0x0000 && m_trackState == TS_GAP1)
+ m_DbgGap1Size++;
+ else if (m_bmWrittenSectorAddrFields == 0x0001 && m_trackState == TS_GAP2)
+ m_DbgGap2Size++; // Only count Gap2 after sector0 Addr Field (assume other inter-sectors gap2's have same count)
+ else if (m_bmWrittenSectorAddrFields == 0x0001 && m_trackState == TS_GAP3)
+ m_DbgGap3Size++; // Only count Gap3 between sector0 & 1 (assume other inter-sectors gap3's have same count)
+ return;
+ }
+#endif
+
+ // Beneath Apple ProDOS 3-14: NB. $D5 and $AA are reserved (never written as data)
+ if (m_uLast3Bytes == 0xD5AA96)
+ {
+ m_trackState = TS_ADDRFIELD;
+ m_4and4idx = 0;
+ return;
+ }
+
+ // NB. If TS_ADDRFIELD && m_4and4idx == 8, then writing Address Field's epilogue
+ if (m_trackState == TS_ADDRFIELD && m_4and4idx < 8)
+ {
+ m_VolTrkSecChk4and4[m_4and4idx++] = floppylatch;
+
+ if (m_4and4idx == 8)
+ {
+ for (UINT i=0; i<4; i++)
+ m_VolTrkSecChk[i] = ((m_VolTrkSecChk4and4[i*2] & 0x55) << 1) | (m_VolTrkSecChk4and4[i*2+1] & 0x55);
+
+#if LOG_DISK_NIBBLES_READ
+ if (!bIsWrite)
+ {
+ LOG_DISK("read D5AA96 detected - Vol:%02X Trk:%02X Sec:%02X Chk:%02X\r\n", m_VolTrkSecChk[0], m_VolTrkSecChk[1], m_VolTrkSecChk[2], m_VolTrkSecChk[3]);
+ }
+#endif
+#if LOG_DISK_NIBBLES_WRITE
+ if (bIsWrite)
+ {
+ LOG_DISK("write D5AA96 detected - Vol:%02X Trk:%02X Sec:%02X Chk:%02X\r\n", m_VolTrkSecChk[0], m_VolTrkSecChk[1], m_VolTrkSecChk[2], m_VolTrkSecChk[3]);
+ }
+#endif
+
+ if (bIsWrite)
+ {
+ // NB. Address Field only written when formatting track
+ BYTE sector = m_VolTrkSecChk[2];
+ _ASSERT( sector <= 15 );
+ if (sector > 15) // Ignore exotic formats with >16 sectors!
+ return;
+ _ASSERT( (m_bmWrittenSectorAddrFields & (1<= nibbles)
tempoffset = 0;
}
-
+
if (byteval[2] == 0x96)
{
sector = ((*(ms_pWorkBuffer+TRACK_DENIBBLIZED_SIZE+4) & 0x55) << 1)
| (*(ms_pWorkBuffer+TRACK_DENIBBLIZED_SIZE+5) & 0x55);
+
+#ifdef _DEBUG
+ _ASSERT( sector <= 15 );
+ if (partsleft != 0) // Don't need this if partsleft is initialised to 32 (not 33)
+ {
+ _ASSERT( (bmWrittenSectorAddrFields & (1<bWriteProtected = 1;
+ pImageInfo->bWriteProtected = true;
}
if ((hFile == INVALID_HANDLE_VALUE) && bCreateIfNecessary)
@@ -1364,11 +1381,34 @@ ImageError_e CImageHelperBase::CheckNormalFile(LPCTSTR pszImageFilename, ImageIn
}
else // Create (or pre-existing zero-length file)
{
+ if (pImageInfo->bWriteProtected)
+ return eIMAGE_ERROR_ZEROLENGTH_WRITEPROTECTED; // Can't be formatted, so return error
+
pImageType = GetImageForCreation(szExt, &dwSize);
if (pImageType && dwSize)
{
pImageInfo->pImageBuffer = new BYTE [dwSize];
- ZeroMemory(pImageInfo->pImageBuffer, dwSize);
+
+ if (pImageType->GetType() != eImageNIB1)
+ {
+ ZeroMemory(pImageInfo->pImageBuffer, dwSize);
+ }
+ else
+ {
+ // Fill zero-length image buffer with alternating high-bit-set nibbles (GH#196, GH#338)
+ for (UINT i=0; ipImageBuffer[i+0] = 0x80; // bit7 set, but 0x80 is an invalid nibble
+ pImageInfo->pImageBuffer[i+1] = 0x81; // bit7 set, but 0x81 is an invalid nibble
+ }
+ }
+
+ // As a convenience, resize the file to the complete size (GH#506)
+ // - this also means that a save-state done mid-way through a format won't reference an image file with a partial size (GH#494)
+ DWORD dwBytesWritten = 0;
+ BOOL res = WriteFile(hFile, pImageInfo->pImageBuffer, dwSize, &dwBytesWritten, NULL);
+ if (!res || dwBytesWritten != dwSize)
+ return eIMAGE_ERROR_FAILED_TO_INIT_ZEROLENGTH;
}
}
diff --git a/source/DiskLog.h b/source/DiskLog.h
new file mode 100644
index 00000000..76af7eab
--- /dev/null
+++ b/source/DiskLog.h
@@ -0,0 +1,27 @@
+#define LOG_DISK_ENABLED 0 // Master enable
+
+#define LOG_DISK_TRACKS 1
+#define LOG_DISK_MOTOR 1
+#define LOG_DISK_PHASES 0
+#define LOG_DISK_RW_MODE 1
+#define LOG_DISK_ENABLE_DRIVE 1
+#define LOG_DISK_NIBBLES_SPIN 1
+#define LOG_DISK_NIBBLES_READ 1
+#define LOG_DISK_NIBBLES_WRITE 1
+#define LOG_DISK_NIBBLES_WRITE_TRACK_GAPS 1 // Gap1, Gap2 & Gap3 info when writing a track
+#define LOG_DISK_NIBBLES_USE_RUNTIME_VAR 1
+
+// __VA_ARGS__ not supported on MSVC++ .NET 7.x
+#if (LOG_DISK_ENABLED)
+ #if !defined(_VC71)
+ #define LOG_DISK(format, ...) LOG(format, __VA_ARGS__)
+ #else
+ #define LOG_DISK LogOutput
+ #endif
+#else
+ #if !defined(_VC71)
+ #define LOG_DISK(...)
+ #else
+ #define LOG_DISK(x)
+ #endif
+#endif
diff --git a/source/Video.cpp b/source/Video.cpp
index 9bb02fd6..e93473f5 100644
--- a/source/Video.cpp
+++ b/source/Video.cpp
@@ -301,7 +301,7 @@ void VideoBenchmark () {
while (cycles > 0) {
DWORD executedcycles = CpuExecute(103, true);
cycles -= executedcycles;
- DiskUpdatePosition(executedcycles);
+ DiskUpdateDriveState(executedcycles);
JoyUpdateButtonLatch(executedcycles);
}
}
diff --git a/source/YamlHelper.cpp b/source/YamlHelper.cpp
index 223dfe77..56b519fd 100644
--- a/source/YamlHelper.cpp
+++ b/source/YamlHelper.cpp
@@ -389,6 +389,11 @@ void YamlSaveHelper::SaveHexUint16(const char* key, UINT value)
Save("%s: 0x%04X\n", key, value);
}
+void YamlSaveHelper::SaveHexUint24(const char* key, UINT value)
+{
+ Save("%s: 0x%06X\n", key, value);
+}
+
void YamlSaveHelper::SaveHexUint32(const char* key, UINT value)
{
Save("%s: 0x%08X\n", key, value);
diff --git a/source/YamlHelper.h b/source/YamlHelper.h
index 31e871ca..c96cca2c 100644
--- a/source/YamlHelper.h
+++ b/source/YamlHelper.h
@@ -209,6 +209,7 @@ public:
void SaveHexUint8(const char* key, UINT value);
void SaveHexUint12(const char* key, UINT value);
void SaveHexUint16(const char* key, UINT value);
+ void SaveHexUint24(const char* key, UINT value);
void SaveHexUint32(const char* key, UINT value);
void SaveHexUint64(const char* key, UINT64 value);
void SaveBool(const char* key, bool value);