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