2006-02-25 20:50:29 +00:00
/*
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
2019-04-16 19:30:54 +00:00
Copyright ( C ) 2006 - 2019 , Tom Charlesworth , Michael Pohoreski , Nick Westgate
2006-02-25 20:50:29 +00:00
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
*
* Author : Various
2015-09-13 10:39:58 +00:00
*
2017-05-14 10:33:07 +00:00
* In comments , UTAIIe is an abbreviation for a reference to " Understanding the Apple //e " by James Sather
2006-02-25 20:50:29 +00:00
*/
# include "StdAfx.h"
2014-08-13 20:30:35 +00:00
2020-11-11 21:15:27 +00:00
# include "Disk.h"
2015-02-13 22:40:53 +00:00
# include "SaveState_Structs_v1.h"
2020-11-29 17:30:06 +00:00
# include "Interface.h"
2020-11-26 21:50:06 +00:00
# include "Core.h"
2022-06-20 19:40:39 +00:00
# include "CardManager.h"
2018-01-14 18:01:22 +00:00
# include "CPU.h"
2010-01-03 18:43:08 +00:00
# include "DiskImage.h"
2015-09-13 10:39:58 +00:00
# include "Log.h"
2014-08-13 20:30:35 +00:00
# include "Memory.h"
# include "Registry.h"
2020-10-25 17:14:23 +00:00
# include "SaveState.h"
2015-12-05 16:50:27 +00:00
# include "YamlHelper.h"
2014-08-13 20:30:35 +00:00
2018-02-24 15:12:40 +00:00
# include "../resource/resource.h"
2007-05-28 11:16:42 +00:00
2019-04-11 21:34:40 +00:00
// About m_enhanceDisk:
// . In general m_enhanceDisk==false is used for authentic disk access speed, whereas m_enhanceDisk==true is for enhanced speed.
// Details:
// . if false: Used by ImageReadTrack() to skew the sectors in a track (for .do, .dsk, .po 5.25" images).
// . if true && m_floppyMotorOn, then this is a condition for full-speed (unthrottled) emulation mode.
// . if false && I/O ReadWrite($C0EC) && drive is spinning, then advance the track buffer's nibble index (to simulate spinning).
// Also m_enhanceDisk is persisted to the save-state, so it's an attribute of the DiskII interface card.
2021-04-10 14:31:54 +00:00
// NB. Non-standard 4&4, with Vol=0x00 and Chk=0x00 (only a few match, eg. Wasteland, Legacy of the Ancients, Planetfall, Border Zone & Wizardry). [*1]
const BYTE Disk2InterfaceCard : : m_T00S00Pattern [ ] = { 0xD5 , 0xAA , 0x96 , 0xAA , 0xAA , 0xAA , 0xAA , 0xAA , 0xAA , 0xAA , 0xAA , 0xDE } ;
2020-05-25 19:21:36 +00:00
Disk2InterfaceCard : : Disk2InterfaceCard ( UINT slot ) :
2022-06-20 19:40:39 +00:00
Card ( CT_Disk2 , slot ) ,
m_syncEvent ( slot , 0 , SyncEventCallback ) // use slot# as "unique" id for Disk2InterfaceCards
2018-02-25 13:38:04 +00:00
{
2021-11-25 20:23:21 +00:00
if ( m_slot ! = 5 & & m_slot ! = 6 ) // fixme
2022-01-30 21:25:40 +00:00
ThrowErrorInvalidSlot ( ) ;
2021-11-25 20:23:21 +00:00
2019-07-05 22:01:19 +00:00
ResetSwitches ( ) ;
2019-04-07 14:54:26 +00:00
m_floppyLatch = 0 ;
m_saveDiskImage = true ; // Save the DiskImage name to Registry
m_diskLastCycle = 0 ;
m_diskLastReadLatchCycle = 0 ;
m_enhanceDisk = true ;
2020-02-22 11:38:25 +00:00
m_is13SectorFirmware = false ;
2022-10-05 20:29:57 +00:00
m_force13SectorFirmware = false ;
2022-06-20 19:40:39 +00:00
m_deferredStepperEvent = false ;
m_deferredStepperAddress = 0 ;
m_deferredStepperCumulativeCycles = 0 ;
2018-02-25 15:09:25 +00:00
2019-07-05 22:01:19 +00:00
ResetLogicStateSequencer ( ) ;
2019-04-07 14:32:24 +00:00
// Debug:
# if LOG_DISK_NIBBLES_USE_RUNTIME_VAR
2019-04-07 14:54:26 +00:00
m_bLogDisk_NibblesRW = false ;
2019-04-07 14:32:24 +00:00
# endif
# if LOG_DISK_NIBBLES_WRITE
2019-04-07 14:54:26 +00:00
m_uWriteLastCycle = 0 ;
m_uSyncFFCount = 0 ;
2019-04-07 14:32:24 +00:00
# endif
}
2010-01-03 18:43:08 +00:00
2019-12-19 19:42:30 +00:00
Disk2InterfaceCard : : ~ Disk2InterfaceCard ( void )
{
EjectDiskInternal ( DRIVE_1 ) ;
EjectDiskInternal ( DRIVE_2 ) ;
2022-06-20 19:40:39 +00:00
if ( m_syncEvent . m_active )
g_SynchronousEventMgr . Remove ( m_syncEvent . m_id ) ;
2019-12-19 19:42:30 +00:00
}
2019-04-14 16:00:15 +00:00
bool Disk2InterfaceCard : : GetEnhanceDisk ( void ) { return m_enhanceDisk ; }
void Disk2InterfaceCard : : SetEnhanceDisk ( bool bEnhanceDisk ) { m_enhanceDisk = bEnhanceDisk ; }
2018-02-25 15:09:25 +00:00
2019-04-14 16:00:15 +00:00
int Disk2InterfaceCard : : GetCurrentDrive ( void ) { return m_currDrive ; }
2019-07-05 22:01:19 +00:00
int Disk2InterfaceCard : : GetCurrentTrack ( void ) { return ImagePhaseToTrack ( m_floppyDrive [ m_currDrive ] . m_disk . m_imagehandle , m_floppyDrive [ m_currDrive ] . m_phasePrecise , false ) ; }
float Disk2InterfaceCard : : GetCurrentPhase ( void ) { return m_floppyDrive [ m_currDrive ] . m_phasePrecise ; }
2022-09-19 10:00:34 +00:00
UINT Disk2InterfaceCard : : GetCurrentBitOffset ( void ) { return m_floppyDrive [ m_currDrive ] . m_disk . m_bitOffset ; }
2019-07-05 22:01:19 +00:00
double Disk2InterfaceCard : : GetCurrentExtraCycles ( void ) { return m_floppyDrive [ m_currDrive ] . m_disk . m_extraCycles ; }
2022-11-17 21:08:11 +00:00
float Disk2InterfaceCard : : GetPhase ( const int drive ) { return m_floppyDrive [ drive ] . m_phasePrecise ; }
2019-07-09 21:18:36 +00:00
int Disk2InterfaceCard : : GetTrack ( const int drive ) { return ImagePhaseToTrack ( m_floppyDrive [ drive ] . m_disk . m_imagehandle , m_floppyDrive [ drive ] . m_phasePrecise , false ) ; }
2019-07-05 22:01:19 +00:00
2022-11-17 21:08:11 +00:00
std : : string Disk2InterfaceCard : : FormatIntFracString ( float phase , bool hex )
2019-07-05 22:01:19 +00:00
{
2022-03-02 21:10:41 +00:00
const UINT phaseInt = ( UINT ) phase ;
const UINT phaseFrac = ( UINT ) ( ( phase - ( float ) phaseInt ) * 100 + 0.5 ) ;
2019-07-05 22:01:19 +00:00
2022-11-17 21:08:11 +00:00
if ( hex )
return StrFormat ( " %02X.%02d " , phaseInt , phaseFrac ) ; // (hex)"NN.nn"
else
return StrFormat ( " %02d.%02d " , phaseInt , phaseFrac ) ; // (dec)"NN.nn"
2022-03-02 21:10:41 +00:00
}
2019-07-05 22:01:19 +00:00
2022-03-02 21:10:41 +00:00
std : : string Disk2InterfaceCard : : GetCurrentTrackString ( void )
{
2022-11-17 21:08:11 +00:00
return FormatIntFracString ( m_floppyDrive [ m_currDrive ] . m_phasePrecise / 2 , true ) ;
2019-07-05 22:01:19 +00:00
}
std : : string Disk2InterfaceCard : : GetCurrentPhaseString ( void )
{
2022-11-17 21:08:11 +00:00
return FormatIntFracString ( m_floppyDrive [ m_currDrive ] . m_phasePrecise , true ) ;
2019-07-05 22:01:19 +00:00
}
2022-03-02 21:10:41 +00:00
2019-04-14 16:00:15 +00:00
LPCTSTR Disk2InterfaceCard : : GetCurrentState ( void )
2013-11-02 21:24:45 +00:00
{
2019-04-14 15:58:49 +00:00
if ( m_floppyDrive [ m_currDrive ] . m_disk . m_imagehandle = = NULL )
2013-11-02 21:24:45 +00:00
return " Empty " ;
2019-04-07 14:54:26 +00:00
if ( ! m_floppyMotorOn )
2013-11-02 21:24:45 +00:00
{
2019-04-14 15:47:41 +00:00
if ( m_floppyDrive [ m_currDrive ] . m_spinning > 0 )
2013-11-02 21:24:45 +00:00
return " Off (spinning) " ;
else
return " Off " ;
}
2019-10-05 08:53:02 +00:00
else if ( m_seqFunc . writeMode )
2013-11-02 21:24:45 +00:00
{
2019-04-14 15:58:49 +00:00
if ( m_floppyDrive [ m_currDrive ] . m_disk . m_bWriteProtected )
2013-11-02 21:24:45 +00:00
return " Writing (write protected) " ;
2015-09-13 10:39:58 +00:00
else
return " Writing " ;
2013-11-02 21:24:45 +00:00
}
else
{
2019-10-05 08:53:02 +00:00
/*if (m_seqFunc.loadMode)
2015-09-13 10:39:58 +00:00
{
2019-04-07 14:54:26 +00:00
if ( m_floppyDrive [ m_currDrive ] . disk . bWriteProtected )
2015-09-13 10:39:58 +00:00
return " Reading write protect state (write protected) " ;
else
return " Reading write protect state (not write protected) " ;
}
else */
return " Reading " ;
2013-11-02 21:24:45 +00:00
}
}
//===========================================================================
2019-04-14 16:00:15 +00:00
void Disk2InterfaceCard : : LoadLastDiskImage ( const int drive )
2008-08-31 04:31:35 +00:00
{
2019-04-08 15:54:11 +00:00
_ASSERT ( drive = = DRIVE_1 | | drive = = DRIVE_2 ) ;
2013-12-06 21:10:41 +00:00
2021-08-29 10:39:51 +00:00
const std : : string regKey = ( drive = = DRIVE_1 )
2021-09-17 19:43:10 +00:00
? REGVALUE_LAST_DISK_1
: REGVALUE_LAST_DISK_2 ;
2008-08-31 04:31:35 +00:00
2021-08-29 10:39:51 +00:00
char pathname [ MAX_PATH ] ;
2022-02-16 20:14:40 +00:00
std : : string regSection = RegGetConfigSlotSection ( m_slot ) ;
2021-11-09 21:04:57 +00:00
if ( RegLoadString ( regSection . c_str ( ) , regKey . c_str ( ) , TRUE , pathname , MAX_PATH , TEXT ( " " ) ) & & ( pathname [ 0 ] ! = 0 ) )
2008-08-31 04:31:35 +00:00
{
2019-04-07 14:54:26 +00:00
m_saveDiskImage = false ;
2021-11-09 21:04:57 +00:00
ImageError_e error = InsertDisk ( drive , pathname , IMAGE_USE_FILES_WRITE_PROTECT_STATUS , IMAGE_DONT_CREATE ) ;
2019-04-07 14:54:26 +00:00
m_saveDiskImage = true ;
2021-11-09 21:04:57 +00:00
if ( error ! = eIMAGE_ERROR_NONE )
{
NotifyInvalidImage ( drive , pathname , error ) ;
EjectDisk ( drive ) ;
}
2008-08-31 04:31:35 +00:00
}
}
//===========================================================================
2010-01-03 18:43:08 +00:00
2019-04-14 16:00:15 +00:00
void Disk2InterfaceCard : : SaveLastDiskImage ( const int drive )
2008-08-31 04:31:35 +00:00
{
2019-04-08 15:54:11 +00:00
_ASSERT ( drive = = DRIVE_1 | | drive = = DRIVE_2 ) ;
2008-08-31 04:31:35 +00:00
2019-04-07 14:54:26 +00:00
if ( ! m_saveDiskImage )
2013-12-06 21:10:41 +00:00
return ;
2022-02-16 20:14:40 +00:00
std : : string regSection = RegGetConfigSlotSection ( m_slot ) ;
2021-08-29 10:39:51 +00:00
RegSaveValue ( regSection . c_str ( ) , REGVALUE_CARD_TYPE , TRUE , CT_Disk2 ) ;
2013-12-06 21:10:41 +00:00
2021-08-29 10:39:51 +00:00
const std : : string regKey = ( drive = = DRIVE_1 )
2021-09-17 19:43:10 +00:00
? REGVALUE_LAST_DISK_1
: REGVALUE_LAST_DISK_2 ;
2021-08-29 10:39:51 +00:00
const std : : string & pathName = DiskGetFullPathName ( drive ) ;
RegSaveString ( regSection . c_str ( ) , regKey . c_str ( ) , TRUE , pathName ) ;
2015-04-11 21:24:54 +00:00
//
2021-08-29 10:39:51 +00:00
// For now, only update 'Starting Directory' for slot6 & drive1
// . otherwise you'll get inconsistent results if you set drive1, then drive2 (and the images were in different folders)
if ( m_slot ! = SLOT6 | | drive ! = DRIVE_1 )
return ;
2019-08-09 03:50:29 +00:00
TCHAR szPathName [ MAX_PATH ] ;
2021-08-29 10:39:51 +00:00
StringCbCopy ( szPathName , MAX_PATH , pathName . c_str ( ) ) ;
TCHAR * slash = _tcsrchr ( szPathName , PATH_SEPARATOR ) ;
2019-08-09 03:50:29 +00:00
if ( slash ! = NULL )
2015-04-11 21:24:54 +00:00
{
2019-08-09 03:50:29 +00:00
slash [ 1 ] = ' \0 ' ;
2021-08-29 10:39:51 +00:00
RegSaveString ( REG_PREFS , REGVALUE_PREF_START_DIR , 1 , szPathName ) ;
2015-04-11 21:24:54 +00:00
}
2008-08-31 04:31:35 +00:00
}
2006-02-25 20:50:29 +00:00
//===========================================================================
2010-01-03 18:43:08 +00:00
2019-04-14 16:41:26 +00:00
// Called by ControlMotor() & Enable()
2020-12-12 20:05:29 +00:00
void Disk2InterfaceCard : : CheckSpinning ( const bool stateChanged , const ULONG uExecutedCycles )
2010-01-03 18:43:08 +00:00
{
2020-12-12 20:05:29 +00:00
bool modeChanged = m_floppyMotorOn & & ! m_floppyDrive [ m_currDrive ] . m_spinning ;
2014-07-26 20:23:57 +00:00
2020-12-12 20:05:29 +00:00
if ( m_floppyMotorOn & & IsDriveConnected ( m_currDrive ) )
2019-04-14 15:47:41 +00:00
m_floppyDrive [ m_currDrive ] . m_spinning = SPINNING_CYCLES ;
2014-07-26 20:23:57 +00:00
2020-12-12 20:05:29 +00:00
if ( modeChanged )
2021-01-10 16:33:06 +00:00
GetFrame ( ) . FrameDrawDiskLEDS ( ) ;
2018-01-14 18:01:22 +00:00
2020-12-12 20:05:29 +00:00
if ( modeChanged )
2018-01-14 18:01:22 +00:00
{
2019-04-07 14:54:26 +00:00
// Set m_diskLastCycle when motor changes: not spinning (ie. off for 1 sec) -> on
m_diskLastCycle = g_nCumulativeCycles ;
2018-01-14 18:01:22 +00:00
}
2020-12-12 20:05:29 +00:00
if ( m_floppyMotorOn & & stateChanged )
{
// Set m_motorOnCycle when: motor changes to on, or the other drive is enabled (and motor is on)
m_floppyDrive [ m_currDrive ] . m_motorOnCycle = g_nCumulativeCycles ;
}
2006-02-25 20:50:29 +00:00
}
2006-02-28 18:37:47 +00:00
//===========================================================================
2010-01-03 18:43:08 +00:00
2019-04-14 16:00:15 +00:00
bool Disk2InterfaceCard : : IsDriveValid ( const int drive )
2010-01-03 18:43:08 +00:00
{
2019-04-08 15:54:11 +00:00
return ( drive > = 0 & & drive < NUM_DRIVES ) ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2019-12-31 12:07:45 +00:00
void Disk2InterfaceCard : : AllocTrack ( const int drive , const UINT minSize /*=NIBBLES_PER_TRACK*/ )
2006-02-25 20:50:29 +00:00
{
2019-04-14 15:58:49 +00:00
FloppyDisk * pFloppy = & m_floppyDrive [ drive ] . m_disk ;
2019-12-31 12:07:45 +00:00
const UINT maxNibblesPerTrack = ImageGetMaxNibblesPerTrack ( m_floppyDrive [ drive ] . m_disk . m_imagehandle ) ;
pFloppy - > m_trackimage = new BYTE [ MAX ( minSize , maxNibblesPerTrack ) ] ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2010-01-03 18:43:08 +00:00
2019-07-05 22:01:19 +00:00
void Disk2InterfaceCard : : ReadTrack ( const int drive , ULONG uExecutedCycles )
2006-02-26 02:02:57 +00:00
{
2019-09-30 19:39:47 +00:00
if ( ! IsDriveValid ( drive ) )
2006-02-26 02:02:57 +00:00
return ;
2019-04-14 15:47:41 +00:00
FloppyDrive * pDrive = & m_floppyDrive [ drive ] ;
2019-04-14 15:58:49 +00:00
FloppyDisk * pFloppy = & pDrive - > m_disk ;
2006-02-26 02:02:57 +00:00
2019-07-05 22:01:19 +00:00
if ( ImagePhaseToTrack ( pFloppy - > m_imagehandle , pDrive - > m_phasePrecise , false ) > = ImageGetNumTracks ( pFloppy - > m_imagehandle ) )
2006-02-26 02:02:57 +00:00
{
2019-07-05 22:01:19 +00:00
_ASSERT ( 0 ) ; // What can cause this? Add a comment to replace this assert.
2020-02-09 21:23:15 +00:00
// Boot with DOS 3.3 Master in D1
// Create a blank disk in D2
// INIT HELLO,D2
// RUN HELLO
// F2 to reboot DOS 3.3 Master
// RUN HELLO,D2
2019-04-14 15:58:49 +00:00
pFloppy - > m_trackimagedata = false ;
2006-02-26 02:02:57 +00:00
return ;
}
2019-04-14 15:58:49 +00:00
if ( ! pFloppy - > m_trackimage )
2019-04-08 15:54:11 +00:00
AllocTrack ( drive ) ;
2006-02-26 02:02:57 +00:00
2019-04-14 15:58:49 +00:00
if ( pFloppy - > m_trackimage & & pFloppy - > m_imagehandle )
2006-02-26 02:02:57 +00:00
{
2022-01-29 21:27:03 +00:00
if ( ImageIsWOZ ( pFloppy - > m_imagehandle ) )
{
// Update bitStream position for *current* track before re-calc'ing position for new track
UINT bitCellDelta = GetBitCellDelta ( uExecutedCycles ) ;
UpdateBitStreamPosition ( * pFloppy , bitCellDelta ) ;
}
2022-08-21 10:48:42 +00:00
if ( ImageIsWOZ ( pFloppy - > m_imagehandle ) & & ( pFloppy - > m_bitCount = = 0 ) )
{
// WOZ: m_bitCount only ever 0 on initial power on
pFloppy - > m_bitOffset = 0 ;
pFloppy - > m_bitCount = 8 ;
}
const UINT32 currentBitPosition = pFloppy - > m_bitOffset ;
const UINT32 currentBitTrackLength = pFloppy - > m_bitCount ;
2019-07-05 22:01:19 +00:00
2006-02-26 02:02:57 +00:00
ImageReadTrack (
2019-04-14 15:58:49 +00:00
pFloppy - > m_imagehandle ,
2019-07-05 22:01:19 +00:00
pDrive - > m_phasePrecise ,
2019-04-14 15:58:49 +00:00
pFloppy - > m_trackimage ,
& pFloppy - > m_nibbles ,
2019-07-05 22:01:19 +00:00
& pFloppy - > m_bitCount ,
2019-04-07 14:54:26 +00:00
m_enhanceDisk ) ;
2006-02-26 02:02:57 +00:00
2022-08-21 10:48:42 +00:00
if ( ! ImageIsWOZ ( pFloppy - > m_imagehandle ) )
2019-07-05 22:01:19 +00:00
{
pFloppy - > m_byte = 0 ;
}
else
{
2022-09-19 10:00:34 +00:00
// NB. This function is only called for a new track when there's a latch read, ie. only for *even* DEVICE SELECT I/O accesses.
// . So when seeking across tracks (ie. sequencing through the magnet phases), then not all (quarter) tracks will need reading.
// . eg. for 'Balance of Power'(GH#1022), for seek T00->T35: this only reads: 00.00, 00.25, 00.75, 01.25, 01.75, ... 34.25, 34.75, 35.00 (skipping the NN.00, NN.50 tracks).
// . And so the bitOffset "round-up" below isn't called for every track.
// TODO: consider forcing this function be be called for every track (and appropriately adjust the "round-up" amount - ie. halve it)
2019-07-05 22:01:19 +00:00
_ASSERT ( pFloppy - > m_nibbles & & pFloppy - > m_bitCount ) ;
if ( pFloppy - > m_nibbles = = 0 | | pFloppy - > m_bitCount = = 0 )
{
pFloppy - > m_nibbles = 1 ;
pFloppy - > m_bitCount = 8 ;
}
2022-08-21 10:48:42 +00:00
pFloppy - > m_bitOffset = ( currentBitPosition * pFloppy - > m_bitCount ) / currentBitTrackLength ; // Ref: WOZ-1.01
pFloppy - > m_bitOffset + = 7 ; // Round-up for sensitive cross-track sync check (GH#1022)
if ( pFloppy - > m_bitOffset > = pFloppy - > m_bitCount )
pFloppy - > m_bitOffset = 0 ;
2022-09-19 10:00:34 +00:00
# if LOG_DISK_WOZ_READTRACK
LOG_DISK ( " T%05.2f: %04X->%04X, Len=%04X \n " , pDrive - > m_phasePrecise / 2 , currentBitPosition , pFloppy - > m_bitOffset , pFloppy - > m_bitCount ) ;
# endif
2019-07-05 22:01:19 +00:00
2022-08-21 10:48:42 +00:00
pFloppy - > m_byte = pFloppy - > m_bitOffset / 8 ;
pFloppy - > m_bitMask = 1 < < ( 7 - ( pFloppy - > m_bitOffset % 8 ) ) ;
2019-07-05 22:01:19 +00:00
pFloppy - > m_extraCycles = 0.0 ;
pDrive - > m_headWindow = 0 ;
2022-09-19 10:00:34 +00:00
FindTrackSeamWOZ ( * pFloppy , pDrive - > m_phasePrecise / 2 ) ;
2019-07-05 22:01:19 +00:00
}
2019-04-14 15:58:49 +00:00
pFloppy - > m_trackimagedata = ( pFloppy - > m_nibbles ! = 0 ) ;
2022-09-19 10:00:34 +00:00
pFloppy - > m_initialBitOffset = pFloppy - > m_bitOffset ;
pFloppy - > m_revs = 0 ;
2006-02-26 02:02:57 +00:00
}
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2010-01-03 18:43:08 +00:00
2019-12-19 19:42:30 +00:00
void Disk2InterfaceCard : : EjectDiskInternal ( const int drive )
2006-02-26 02:02:57 +00:00
{
2019-04-14 15:58:49 +00:00
FloppyDisk * pFloppy = & m_floppyDrive [ drive ] . m_disk ;
2006-02-26 02:02:57 +00:00
2019-04-14 15:58:49 +00:00
if ( pFloppy - > m_imagehandle )
2006-02-26 02:02:57 +00:00
{
2019-04-08 15:54:11 +00:00
FlushCurrentTrack ( drive ) ;
2006-02-26 02:02:57 +00:00
2019-04-14 15:58:49 +00:00
ImageClose ( pFloppy - > m_imagehandle ) ;
pFloppy - > m_imagehandle = NULL ;
2006-02-26 02:02:57 +00:00
}
2019-04-14 15:58:49 +00:00
if ( pFloppy - > m_trackimage )
2006-02-26 02:02:57 +00:00
{
2019-12-31 12:07:45 +00:00
delete [ ] pFloppy - > m_trackimage ;
2019-12-19 19:42:30 +00:00
pFloppy - > m_trackimage = NULL ;
2019-04-14 15:58:49 +00:00
pFloppy - > m_trackimagedata = false ;
2006-02-26 02:02:57 +00:00
}
2019-09-07 08:02:39 +00:00
pFloppy - > m_imagename . clear ( ) ;
pFloppy - > m_fullname . clear ( ) ;
2019-04-14 15:58:49 +00:00
pFloppy - > m_strFilenameInZip = " " ;
2019-12-19 19:42:30 +00:00
}
void Disk2InterfaceCard : : EjectDisk ( const int drive )
{
if ( ! IsDriveValid ( drive ) )
return ;
2008-08-31 04:31:35 +00:00
2019-12-19 19:42:30 +00:00
EjectDiskInternal ( drive ) ;
2020-10-25 17:14:23 +00:00
Snapshot_UpdatePath ( ) ;
2019-12-19 19:42:30 +00:00
SaveLastDiskImage ( drive ) ;
2021-01-03 16:21:24 +00:00
GetFrame ( ) . Video_ResetScreenshotCounter ( " " ) ;
2006-02-25 20:50:29 +00:00
}
2020-12-12 20:49:46 +00:00
void Disk2InterfaceCard : : UnplugDrive ( const int drive )
{
if ( ! IsDriveValid ( drive ) )
return ;
EjectDisk ( drive ) ;
m_floppyDrive [ drive ] . m_isConnected = false ;
}
2006-02-25 20:50:29 +00:00
//===========================================================================
2010-01-03 18:43:08 +00:00
2019-04-14 16:00:15 +00:00
void Disk2InterfaceCard : : WriteTrack ( const int drive )
2006-02-26 02:02:57 +00:00
{
2019-04-14 15:47:41 +00:00
FloppyDrive * pDrive = & m_floppyDrive [ drive ] ;
2019-04-14 15:58:49 +00:00
FloppyDisk * pFloppy = & pDrive - > m_disk ;
2006-02-26 02:02:57 +00:00
2019-07-05 22:01:19 +00:00
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.
2006-02-26 02:02:57 +00:00
return ;
2019-07-05 22:01:19 +00:00
}
2006-02-26 02:02:57 +00:00
2019-04-14 15:58:49 +00:00
if ( pFloppy - > m_bWriteProtected )
2006-02-26 02:02:57 +00:00
return ;
2019-04-14 15:58:49 +00:00
if ( pFloppy - > m_trackimage & & pFloppy - > m_imagehandle )
2015-09-13 10:39:58 +00:00
{
# if LOG_DISK_TRACKS
2019-07-05 22:01:19 +00:00
LOG_DISK ( " track $%s write \r \n " , GetCurrentTrackString ( ) . c_str ( ) ) ;
2015-09-13 10:39:58 +00:00
# endif
2006-02-26 02:02:57 +00:00
ImageWriteTrack (
2019-04-14 15:58:49 +00:00
pFloppy - > m_imagehandle ,
2019-07-05 22:01:19 +00:00
pDrive - > m_phasePrecise ,
2019-04-14 15:58:49 +00:00
pFloppy - > m_trackimage ,
pFloppy - > m_nibbles ) ;
2015-09-13 10:39:58 +00:00
}
2006-02-26 02:02:57 +00:00
2019-04-14 15:58:49 +00:00
pFloppy - > m_trackimagedirty = false ;
2018-04-02 17:21:18 +00:00
}
2019-04-14 16:00:15 +00:00
void Disk2InterfaceCard : : FlushCurrentTrack ( const int drive )
2018-04-02 17:21:18 +00:00
{
2019-04-14 15:58:49 +00:00
FloppyDisk * pFloppy = & m_floppyDrive [ drive ] . m_disk ;
2018-04-02 17:21:18 +00:00
2019-04-14 15:58:49 +00:00
if ( pFloppy - > m_trackimage & & pFloppy - > m_trackimagedirty )
2019-04-08 15:54:11 +00:00
WriteTrack ( drive ) ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2019-04-14 16:00:15 +00:00
void Disk2InterfaceCard : : Boot ( void )
2010-01-03 18:43:08 +00:00
{
// THIS FUNCTION RELOADS A PROGRAM IMAGE IF ONE IS LOADED IN DRIVE ONE.
// IF A DISK IMAGE OR NO IMAGE IS LOADED IN DRIVE ONE, IT DOES NOTHING.
2019-04-14 15:58:49 +00:00
if ( m_floppyDrive [ 0 ] . m_disk . m_imagehandle & & ImageBoot ( m_floppyDrive [ 0 ] . m_disk . m_imagehandle ) )
2019-04-07 14:54:26 +00:00
m_floppyMotorOn = 0 ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2010-01-03 18:43:08 +00:00
2019-04-14 16:00:15 +00:00
void __stdcall Disk2InterfaceCard : : ControlMotor ( WORD , WORD address , BYTE , BYTE , ULONG uExecutedCycles )
2010-01-03 18:43:08 +00:00
{
2018-01-14 18:01:22 +00:00
BOOL newState = address & 1 ;
2020-12-12 20:05:29 +00:00
bool stateChanged = ( newState ! = m_floppyMotorOn ) ;
2018-01-14 18:01:22 +00:00
2020-12-12 20:05:29 +00:00
if ( stateChanged )
{
m_floppyMotorOn = newState ;
2019-04-07 14:54:26 +00:00
m_formatTrack . DriveNotWritingTrack ( ) ;
2020-12-12 20:05:29 +00:00
}
2018-01-14 18:01:22 +00:00
2018-01-28 12:30:54 +00:00
// NB. Motor off doesn't reset the Command Decoder like reset. (UTAIIe figures 9.7 & 9.8 chip C2)
2019-10-05 08:53:02 +00:00
// - so it doesn't reset this state: m_seqFunc, m_magnetStates
2015-09-13 10:39:58 +00:00
# if LOG_DISK_MOTOR
2019-09-30 19:39:47 +00:00
LOG_DISK ( " %08X: motor %s \r \n " , ( UINT32 ) g_nCumulativeCycles , ( m_floppyMotorOn ) ? " on " : " off " ) ;
2015-09-13 10:39:58 +00:00
# endif
2020-12-12 20:05:29 +00:00
CheckSpinning ( stateChanged , uExecutedCycles ) ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2010-01-03 18:43:08 +00:00
2019-04-14 16:00:15 +00:00
void __stdcall Disk2InterfaceCard : : ControlStepper ( WORD , WORD address , BYTE , BYTE , ULONG uExecutedCycles )
2007-03-23 22:26:35 +00:00
{
2019-04-14 15:47:41 +00:00
FloppyDrive * pDrive = & m_floppyDrive [ m_currDrive ] ;
2018-01-28 12:21:28 +00:00
2019-04-07 14:54:26 +00:00
if ( ! m_floppyMotorOn ) // GH#525
2018-01-27 17:02:33 +00:00
{
2019-04-14 15:47:41 +00:00
if ( ! pDrive - > m_spinning )
2018-01-28 12:21:28 +00:00
{
2018-01-27 17:02:33 +00:00
# if LOG_DISK_PHASES
2018-01-28 12:21:28 +00:00
LOG_DISK ( " stepper accessed whilst motor is off and not spinning \r \n " ) ;
# endif
return ;
}
# if LOG_DISK_PHASES
LOG_DISK ( " stepper accessed whilst motor is off, but still spinning \r \n " ) ;
2018-01-27 17:02:33 +00:00
# endif
}
2019-07-05 22:01:19 +00:00
// update phases (magnet states)
2010-01-03 18:43:08 +00:00
{
2019-07-05 22:01:19 +00:00
const int phase = ( address > > 1 ) & 3 ;
const int phase_bit = ( 1 < < phase ) ;
// update the magnet states
if ( address & 1 )
m_magnetStates | = phase_bit ; // phase on
else
m_magnetStates & = ~ phase_bit ; // phase off
2010-01-03 18:43:08 +00:00
}
2007-03-23 22:26:35 +00:00
2022-06-20 19:40:39 +00:00
if ( ! GetCardMgr ( ) . GetDisk2CardMgr ( ) . IsStepperDeferred ( ) )
{
m_deferredStepperAddress = address ;
m_deferredStepperCumulativeCycles = g_nCumulativeCycles ;
ControlStepperDeferred ( ) ;
return ;
}
if ( m_syncEvent . m_active )
{
// Check for adjacent magnets being turned off/on in a very short interval (10 cycles is purely based on A2osX). (GH#1110)
g_SynchronousEventMgr . Remove ( m_syncEvent . m_id ) ;
m_deferredStepperEvent = false ;
int addrDelta = ( m_deferredStepperAddress & 7 ) - ( address & 7 ) ;
if ( addrDelta < 0 ) addrDelta = - addrDelta ;
if ( addrDelta = = 2 | | addrDelta = = 6 ) // adjacent magnets: both turned off or both turned on
{
if ( ( address & 1 ) = = 0 ) // adjacent magnets off
{
// 2 adjacent magnets off in quick succession don't move the cog (GH#1110)
2022-06-21 20:19:24 +00:00
// . also DOS3.2, Pascal and ProDOS rapidly turning off all 4 magnets.
2022-06-20 19:40:39 +00:00
ControlStepperLogging ( m_deferredStepperAddress , m_deferredStepperCumulativeCycles ) ;
ControlStepperLogging ( address , g_nCumulativeCycles ) ;
return ;
}
2022-06-21 20:19:24 +00:00
else // adjacent magnets turned on
2022-06-20 19:40:39 +00:00
{
2022-06-21 20:19:24 +00:00
// take no action - can't find any titles that ever do this!
const std : : string msg = " Disk: ControlStepper() - adjacent magnets turned on \n " ;
2022-06-24 21:00:31 +00:00
LogOutput ( " %s " , msg . c_str ( ) ) ;
LogFileOutput ( " %s " , msg . c_str ( ) ) ;
2022-06-20 19:40:39 +00:00
}
}
// complete the deferred stepper event
2022-06-21 20:19:24 +00:00
// eg. Glutton, EDD III - both just combinations of turning off all 4 magnets
2022-06-20 19:40:39 +00:00
ControlStepperDeferred ( ) ;
}
// defer the effect of changing the phase
m_deferredStepperAddress = address ;
m_deferredStepperCumulativeCycles = g_nCumulativeCycles ;
InsertSyncEvent ( ) ;
m_deferredStepperEvent = true ;
}
void Disk2InterfaceCard : : InsertSyncEvent ( void )
{
m_syncEvent . m_cyclesRemaining = 10 ; // NB. same cycle delay for magnet off and on - but perhaps they take different times?
g_SynchronousEventMgr . Insert ( & m_syncEvent ) ;
}
int Disk2InterfaceCard : : SyncEventCallback ( int id , int cycles , ULONG uExecutedCycles )
{
Disk2InterfaceCard & disk2Card = dynamic_cast < Disk2InterfaceCard & > ( GetCardMgr ( ) . GetRef ( id ) ) ;
disk2Card . ControlStepperDeferred ( ) ;
return 0 ; // Don't repeat event
}
void Disk2InterfaceCard : : ControlStepperDeferred ( void )
{
m_deferredStepperEvent = false ;
const WORD address = m_deferredStepperAddress ;
FloppyDrive * pDrive = & m_floppyDrive [ m_currDrive ] ;
FloppyDisk * pFloppy = & pDrive - > m_disk ;
2019-07-05 22:01:19 +00:00
2010-01-03 18:43:08 +00:00
// check for any stepping effect from a magnet
// - move only when the magnet opposite the cog is off
// - move in the direction of an adjacent magnet if one is on
2019-07-05 22:01:19 +00:00
// - do not move if both adjacent magnets are on (ie. quarter track)
2022-06-20 19:40:39 +00:00
// - timing is accounted for in the case when "two phases [are] turned off in rapid sequence" (UTAIIe page 9-13) (GH#1110)
// momentum is not accounted for ... maybe one day!
2010-01-03 18:43:08 +00:00
int direction = 0 ;
2019-07-05 22:01:19 +00:00
if ( m_magnetStates & ( 1 < < ( ( pDrive - > m_phase + 1 ) & 3 ) ) )
2010-01-03 18:43:08 +00:00
direction + = 1 ;
2019-07-05 22:01:19 +00:00
if ( m_magnetStates & ( 1 < < ( ( pDrive - > m_phase + 3 ) & 3 ) ) )
2010-01-03 18:43:08 +00:00
direction - = 1 ;
2019-07-05 22:01:19 +00:00
// Only calculate quarterDirection for WOZ, as NIB/DSK don't support half phases.
int quarterDirection = 0 ;
if ( ImageIsWOZ ( pFloppy - > m_imagehandle ) )
2010-01-03 18:43:08 +00:00
{
2019-07-05 22:01:19 +00:00
if ( ( m_magnetStates = = 0xC | | // 1100
m_magnetStates = = 0x6 | | // 0110
m_magnetStates = = 0x3 | | // 0011
m_magnetStates = = 0x9 ) ) // 1001
2010-01-03 18:43:08 +00:00
{
2019-07-05 22:01:19 +00:00
quarterDirection = direction ;
direction = 0 ;
2010-01-03 18:43:08 +00:00
}
2019-07-05 22:01:19 +00:00
}
2014-07-24 01:08:52 +00:00
2019-07-05 22:01:19 +00:00
pDrive - > m_phase = MAX ( 0 , MIN ( 79 , pDrive - > m_phase + direction ) ) ;
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 ( ) ;
2021-01-10 16:33:06 +00:00
GetFrame ( ) . FrameDrawDiskStatus ( ) ; // Show track status (GH#201)
2010-01-03 18:43:08 +00:00
}
2018-01-14 18:01:22 +00:00
2022-06-20 19:40:39 +00:00
ControlStepperLogging ( address , m_deferredStepperCumulativeCycles ) ;
}
void Disk2InterfaceCard : : ControlStepperLogging ( WORD address , unsigned __int64 cumulativeCycles )
{
FloppyDrive * pDrive = & m_floppyDrive [ m_currDrive ] ;
# if LOG_DISK_PHASES
const ULONG cycleDelta = ( ULONG ) ( cumulativeCycles - pDrive - > m_lastStepperCycle ) ;
# endif
pDrive - > m_lastStepperCycle = cumulativeCycles ; // NB. Persisted to save-state
2015-09-13 10:39:58 +00:00
# if LOG_DISK_PHASES
2019-09-30 19:39:47 +00:00
LOG_DISK ( " %08X: track $%s magnet-states %d%d%d%d phase %d %s address $%4X last-stepper %.3fms \r \n " ,
2022-06-20 19:40:39 +00:00
( UINT32 ) cumulativeCycles ,
2019-07-05 22:01:19 +00:00
GetCurrentTrackString ( ) . c_str ( ) ,
( m_magnetStates > > 3 ) & 1 ,
( m_magnetStates > > 2 ) & 1 ,
( m_magnetStates > > 1 ) & 1 ,
( m_magnetStates > > 0 ) & 1 ,
2019-07-08 20:14:31 +00:00
( address > > 1 ) & 3 , // phase
2015-09-13 10:39:58 +00:00
( address & 1 ) ? " on " : " off " ,
2019-07-05 22:01:19 +00:00
address ,
2022-06-20 19:40:39 +00:00
( ( float ) cycleDelta ) / ( CLK_6502_NTSC / 1000.0 ) ) ;
2011-05-10 20:26:47 +00:00
# endif
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2010-01-03 18:43:08 +00:00
2019-04-14 16:00:15 +00:00
void Disk2InterfaceCard : : Destroy ( void )
2006-02-26 02:02:57 +00:00
{
2019-04-07 14:54:26 +00:00
m_saveDiskImage = false ;
2019-12-19 19:42:30 +00:00
EjectDisk ( DRIVE_1 ) ;
2008-08-31 04:31:35 +00:00
2019-04-07 14:54:26 +00:00
m_saveDiskImage = false ;
2019-12-19 19:42:30 +00:00
EjectDisk ( DRIVE_2 ) ;
2008-08-31 04:31:35 +00:00
2019-04-07 14:54:26 +00:00
m_saveDiskImage = true ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2022-01-29 21:27:03 +00:00
bool __stdcall Disk2InterfaceCard : : Enable ( WORD , WORD address , BYTE , BYTE , ULONG uExecutedCycles )
2010-01-03 18:43:08 +00:00
{
2020-12-12 20:05:29 +00:00
WORD newDrive = address & 1 ;
bool stateChanged = ( newDrive ! = m_currDrive ) ;
m_currDrive = newDrive ;
2018-01-14 18:01:22 +00:00
# if LOG_DISK_ENABLE_DRIVE
2019-09-30 19:39:47 +00:00
LOG_DISK ( " %08X: enable drive: %d \r \n " , ( UINT32 ) g_nCumulativeCycles , m_currDrive ) ;
2018-01-14 18:01:22 +00:00
# endif
2019-04-14 15:47:41 +00:00
m_floppyDrive [ ! m_currDrive ] . m_spinning = 0 ;
m_floppyDrive [ ! m_currDrive ] . m_writelight = 0 ;
2020-12-12 20:05:29 +00:00
CheckSpinning ( stateChanged , uExecutedCycles ) ;
2022-01-29 21:27:03 +00:00
return ImageIsWOZ ( m_floppyDrive [ m_currDrive ] . m_disk . m_imagehandle ) ; // Drive may've changed, so image-type may've changed
2010-01-03 18:43:08 +00:00
}
2006-02-26 02:02:57 +00:00
//===========================================================================
2010-01-03 18:43:08 +00:00
// Return the filename
// . Used by Drive Buttons' tooltips
2019-09-07 08:02:39 +00:00
const std : : string & Disk2InterfaceCard : : GetFullDiskFilename ( const int drive )
2010-01-03 18:43:08 +00:00
{
2019-04-14 15:58:49 +00:00
if ( ! m_floppyDrive [ drive ] . m_disk . m_strFilenameInZip . empty ( ) )
2019-09-07 08:02:39 +00:00
return m_floppyDrive [ drive ] . m_disk . m_strFilenameInZip ;
2006-02-28 18:37:47 +00:00
2019-04-08 15:54:11 +00:00
return GetFullName ( drive ) ;
2010-01-03 18:43:08 +00:00
}
2008-06-20 23:47:25 +00:00
2019-04-09 18:29:58 +00:00
// Return the file or zip name
// . Used by Property Sheet Page (Disk)
2019-09-07 08:02:39 +00:00
const std : : string & Disk2InterfaceCard : : GetFullName ( const int drive )
2015-02-13 22:40:53 +00:00
{
2019-04-14 15:58:49 +00:00
return m_floppyDrive [ drive ] . m_disk . m_fullname ;
2015-02-13 22:40:53 +00:00
}
2010-01-03 18:43:08 +00:00
// Return the imagename
// . Used by Drive Button's icons & Property Sheet Page (Save snapshot)
2019-09-07 08:02:39 +00:00
const std : : string & Disk2InterfaceCard : : GetBaseName ( const int drive )
2010-01-03 18:43:08 +00:00
{
2019-04-14 15:58:49 +00:00
return m_floppyDrive [ drive ] . m_disk . m_imagename ;
2010-01-03 18:43:08 +00:00
}
2019-04-09 18:29:58 +00:00
2020-10-25 17:14:23 +00:00
void Disk2InterfaceCard : : GetFilenameAndPathForSaveState ( std : : string & filename , std : : string & path )
{
filename = " " ;
path = " " ;
for ( UINT i = DRIVE_1 ; i < = DRIVE_2 ; i + + )
{
if ( IsDriveEmpty ( i ) )
continue ;
filename = GetBaseName ( i ) ;
std : : string pathname = DiskGetFullPathName ( i ) ;
2021-05-19 20:10:22 +00:00
int idx = pathname . find_last_of ( PATH_SEPARATOR ) ;
2020-10-25 17:14:23 +00:00
if ( idx > = 0 & & idx + 1 < ( int ) pathname . length ( ) ) // path exists?
{
path = pathname . substr ( 0 , idx + 1 ) ;
return ;
}
_ASSERT ( 0 ) ;
break ;
}
}
2019-09-07 08:02:39 +00:00
const std : : string & Disk2InterfaceCard : : DiskGetFullPathName ( const int drive )
2019-04-09 18:29:58 +00:00
{
2019-04-14 15:58:49 +00:00
return ImageGetPathname ( m_floppyDrive [ drive ] . m_disk . m_imagehandle ) ;
2019-04-09 18:29:58 +00:00
}
2006-02-25 20:50:29 +00:00
//===========================================================================
2010-01-03 18:43:08 +00:00
2020-02-22 11:38:25 +00:00
Disk_Status_e Disk2InterfaceCard : : GetDriveLightStatus ( const int drive )
{
if ( IsDriveValid ( drive ) )
{
FloppyDrive * pDrive = & m_floppyDrive [ drive ] ;
if ( pDrive - > m_spinning )
{
if ( pDrive - > m_disk . m_bWriteProtected )
return DISK_STATUS_PROT ;
if ( pDrive - > m_writelight )
return DISK_STATUS_WRITE ;
else
return DISK_STATUS_READ ;
}
else
{
return DISK_STATUS_OFF ;
}
}
return DISK_STATUS_OFF ;
}
2019-04-14 16:00:15 +00:00
void Disk2InterfaceCard : : GetLightStatus ( Disk_Status_e * pDisk1Status , Disk_Status_e * pDisk2Status )
2006-02-28 18:37:47 +00:00
{
2018-02-25 13:38:04 +00:00
if ( pDisk1Status )
* pDisk1Status = GetDriveLightStatus ( DRIVE_1 ) ;
2014-07-26 20:23:57 +00:00
2018-02-25 13:38:04 +00:00
if ( pDisk2Status )
* pDisk2Status = GetDriveLightStatus ( DRIVE_2 ) ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2007-05-28 11:16:42 +00:00
2021-07-15 18:23:01 +00:00
// Pre: pathname likely to include path (but can also just be filename)
ImageError_e Disk2InterfaceCard : : InsertDisk ( const int drive , const std : : string & pathname , const bool bForceWriteProtected , const bool bCreateIfNecessary )
2008-06-20 23:47:25 +00:00
{
2019-04-14 15:47:41 +00:00
FloppyDrive * pDrive = & m_floppyDrive [ drive ] ;
2019-04-14 15:58:49 +00:00
FloppyDisk * pFloppy = & pDrive - > m_disk ;
2018-02-25 13:38:04 +00:00
2019-04-14 15:58:49 +00:00
if ( pFloppy - > m_imagehandle )
2019-12-19 19:42:30 +00:00
EjectDisk ( drive ) ;
2008-06-20 23:47:25 +00:00
2019-04-16 20:11:47 +00:00
// Reset the disk's attributes, but preserve the drive's attributes (GH#138/Platoon, GH#640)
// . Changing the disk (in the drive) doesn't affect the drive's attributes.
2019-04-16 20:13:55 +00:00
pFloppy - > clear ( ) ;
2008-06-20 23:47:25 +00:00
2021-07-15 18:23:01 +00:00
const DWORD dwAttributes = GetFileAttributes ( pathname . c_str ( ) ) ;
2020-02-11 21:29:27 +00:00
if ( dwAttributes = = INVALID_FILE_ATTRIBUTES )
pFloppy - > m_bWriteProtected = false ; // Assume this is a new file to create (so it must be write-enabled to allow it to be formatted)
2010-01-03 18:43:08 +00:00
else
2019-04-14 15:58:49 +00:00
pFloppy - > m_bWriteProtected = bForceWriteProtected ? true : ( dwAttributes & FILE_ATTRIBUTE_READONLY ) ;
2010-01-03 18:43:08 +00:00
2015-02-13 22:40:53 +00:00
// Check if image is being used by the other drive, and if so remove it in order so it can be swapped
{
2019-09-07 08:02:39 +00:00
const std : : string & pszOtherPathname = DiskGetFullPathName ( ! drive ) ;
2015-02-13 22:40:53 +00:00
char szCurrentPathname [ MAX_PATH ] ;
2021-07-15 18:23:01 +00:00
DWORD uNameLen = GetFullPathName ( pathname . c_str ( ) , MAX_PATH , szCurrentPathname , NULL ) ;
2015-02-13 22:40:53 +00:00
if ( uNameLen = = 0 | | uNameLen > = MAX_PATH )
2021-07-15 18:23:01 +00:00
strcpy_s ( szCurrentPathname , MAX_PATH , pathname . c_str ( ) ) ;
2015-02-13 22:40:53 +00:00
2019-09-07 08:02:39 +00:00
if ( ! strcmp ( pszOtherPathname . c_str ( ) , szCurrentPathname ) )
2015-02-13 22:40:53 +00:00
{
2019-04-08 15:54:11 +00:00
EjectDisk ( ! drive ) ;
2021-01-10 16:33:06 +00:00
GetFrame ( ) . FrameRefreshStatus ( DRAW_LEDS | DRAW_BUTTON_DRIVES | DRAW_DISK_STATUS ) ;
2015-02-13 22:40:53 +00:00
}
2014-10-01 23:18:46 +00:00
}
2021-07-15 18:23:01 +00:00
ImageError_e Error = ImageOpen ( pathname ,
2019-04-14 15:58:49 +00:00
& pFloppy - > m_imagehandle ,
& pFloppy - > m_bWriteProtected ,
2010-01-03 18:43:08 +00:00
bCreateIfNecessary ,
2019-04-14 15:58:49 +00:00
pFloppy - > m_strFilenameInZip ) ;
2010-01-03 18:43:08 +00:00
2019-04-14 15:58:49 +00:00
if ( Error = = eIMAGE_ERROR_NONE & & ImageIsMultiFileZip ( pFloppy - > m_imagehandle ) )
2010-01-03 18:43:08 +00:00
{
2022-02-15 18:48:20 +00:00
std : : string strText = StrFormat ( " Only the first file in a multi-file zip is supported \n "
" Use disk image '%s' ? " ,
pFloppy - > m_strFilenameInZip . c_str ( ) ) ;
int nRes = GetFrame ( ) . FrameMessageBox ( strText . c_str ( ) , " Multi-Zip Warning " , MB_ICONWARNING | MB_YESNO | MB_SETFOREGROUND ) ;
2010-01-03 18:43:08 +00:00
if ( nRes = = IDNO )
{
2019-12-19 19:42:30 +00:00
EjectDisk ( drive ) ;
2010-01-03 18:43:08 +00:00
Error = eIMAGE_ERROR_REJECTED_MULTI_ZIP ;
}
}
2008-06-20 23:47:25 +00:00
2010-01-03 18:43:08 +00:00
if ( Error = = eIMAGE_ERROR_NONE )
2008-06-20 23:47:25 +00:00
{
2021-07-15 18:23:01 +00:00
GetImageTitle ( pathname . c_str ( ) , pFloppy - > m_imagename , pFloppy - > m_fullname ) ;
2020-10-25 17:14:23 +00:00
Snapshot_UpdatePath ( ) ;
2021-01-03 16:21:24 +00:00
GetFrame ( ) . Video_ResetScreenshotCounter ( pFloppy - > m_imagename ) ;
2020-02-22 11:38:25 +00:00
if ( g_nAppMode = = MODE_LOGO )
InitFirmware ( GetCxRomPeripheral ( ) ) ;
2008-08-31 04:31:35 +00:00
}
else
{
2021-01-03 16:21:24 +00:00
GetFrame ( ) . Video_ResetScreenshotCounter ( " " ) ;
2008-06-20 23:47:25 +00:00
}
2019-04-08 15:54:11 +00:00
SaveLastDiskImage ( drive ) ;
2008-08-31 04:31:35 +00:00
2010-01-03 18:43:08 +00:00
return Error ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2010-01-03 18:43:08 +00:00
2019-04-14 16:00:15 +00:00
bool Disk2InterfaceCard : : IsConditionForFullSpeed ( void )
2018-02-27 21:07:16 +00:00
{
2019-04-07 14:54:26 +00:00
return m_floppyMotorOn & & m_enhanceDisk ;
2018-02-27 21:07:16 +00:00
}
2006-02-25 20:50:29 +00:00
//===========================================================================
2010-01-03 18:43:08 +00:00
2019-04-14 16:00:15 +00:00
void Disk2InterfaceCard : : NotifyInvalidImage ( const int drive , LPCTSTR pszImageFilename , const ImageError_e Error )
2006-02-26 02:02:57 +00:00
{
2022-02-15 18:48:20 +00:00
std : : string strText ;
2006-02-26 02:02:57 +00:00
2010-01-03 18:43:08 +00:00
switch ( Error )
2006-06-27 02:34:46 +00:00
{
2010-01-03 18:43:08 +00:00
case eIMAGE_ERROR_UNABLE_TO_OPEN :
case eIMAGE_ERROR_UNABLE_TO_OPEN_GZ :
case eIMAGE_ERROR_UNABLE_TO_OPEN_ZIP :
2022-02-15 18:48:20 +00:00
strText = StrFormat ( " Unable to open the file %s. " ,
pszImageFilename ) ;
2010-01-03 18:43:08 +00:00
break ;
case eIMAGE_ERROR_BAD_SIZE :
2022-02-15 18:48:20 +00:00
strText = StrFormat ( " Unable to use the file %s \n "
" because the disk image is an unsupported size. " ,
pszImageFilename ) ;
2010-01-03 18:43:08 +00:00
break ;
case eIMAGE_ERROR_BAD_FILE :
2022-02-15 18:48:20 +00:00
strText = StrFormat ( " Unable to use the file %s \n "
" because the OS can't access it. " ,
pszImageFilename ) ;
2006-06-27 02:34:46 +00:00
break ;
2010-01-03 18:43:08 +00:00
case eIMAGE_ERROR_UNSUPPORTED :
2022-02-15 18:48:20 +00:00
strText = StrFormat ( " Unable to use the file %s \n "
" because the disk image format is not recognized. " ,
pszImageFilename ) ;
2010-01-03 18:43:08 +00:00
break ;
case eIMAGE_ERROR_UNSUPPORTED_HDV :
2022-02-15 18:48:20 +00:00
strText = StrFormat ( " Unable to use the file %s \n "
" because this UniDisk 3.5/Apple IIGS/hard-disk image is not supported. \n "
" Try inserting as a hard-disk image instead. " ,
pszImageFilename ) ;
2010-01-03 18:43:08 +00:00
break ;
case eIMAGE_ERROR_GZ :
case eIMAGE_ERROR_ZIP :
2022-02-15 18:48:20 +00:00
strText = StrFormat ( " Unable to use the compressed file %s \n "
" because the compressed disk image is corrupt/unsupported. " ,
pszImageFilename ) ;
2006-06-27 02:34:46 +00:00
break ;
2015-02-13 22:40:53 +00:00
case eIMAGE_ERROR_FAILED_TO_GET_PATHNAME :
2022-02-15 18:48:20 +00:00
strText = StrFormat ( " Unable to GetFullPathName() for the file: %s. " ,
pszImageFilename ) ;
2015-02-13 22:40:53 +00:00
break ;
2018-01-14 18:01:22 +00:00
case eIMAGE_ERROR_ZEROLENGTH_WRITEPROTECTED :
2022-02-15 18:48:20 +00:00
strText = StrFormat ( " Unsupported zero-length write-protected file: %s. " ,
pszImageFilename ) ;
2018-01-14 18:01:22 +00:00
break ;
case eIMAGE_ERROR_FAILED_TO_INIT_ZEROLENGTH :
2022-02-15 18:48:20 +00:00
strText = StrFormat ( " Failed to resize the zero-length file: %s. " ,
pszImageFilename ) ;
2018-01-14 18:01:22 +00:00
break ;
2006-06-27 02:34:46 +00:00
default :
// IGNORE OTHER ERRORS SILENTLY
return ;
}
2006-02-25 20:50:29 +00:00
2022-02-15 18:48:20 +00:00
GetFrame ( ) . FrameMessageBox ( strText . c_str ( ) ,
g_pAppTitle . c_str ( ) ,
MB_ICONEXCLAMATION | MB_SETFOREGROUND ) ;
2006-02-25 20:50:29 +00:00
}
2006-02-26 02:02:57 +00:00
//===========================================================================
2010-01-03 18:43:08 +00:00
2019-04-14 16:00:15 +00:00
bool Disk2InterfaceCard : : GetProtect ( const int drive )
2006-03-07 18:14:09 +00:00
{
2021-10-18 20:13:37 +00:00
if ( ! IsDriveValid ( drive ) )
return true ;
2018-02-25 13:38:04 +00:00
2021-10-18 20:13:37 +00:00
return m_floppyDrive [ drive ] . m_disk . m_bWriteProtected ;
2006-03-07 18:14:09 +00:00
}
//===========================================================================
2010-01-03 18:43:08 +00:00
2019-04-14 16:00:15 +00:00
void Disk2InterfaceCard : : SetProtect ( const int drive , const bool bWriteProtect )
2006-02-26 02:02:57 +00:00
{
2021-10-18 20:13:37 +00:00
if ( ! IsDriveValid ( drive ) )
return ;
m_floppyDrive [ drive ] . m_disk . m_bWriteProtected = bWriteProtect ;
2006-02-26 02:02:57 +00:00
}
2006-02-25 20:50:29 +00:00
//===========================================================================
2010-01-03 18:43:08 +00:00
2021-10-18 20:13:37 +00:00
bool Disk2InterfaceCard : : IsDriveEmpty ( const int drive )
2010-01-03 18:43:08 +00:00
{
2019-04-08 15:54:11 +00:00
if ( ! IsDriveValid ( drive ) )
2010-01-03 18:43:08 +00:00
return true ;
2021-10-18 20:13:37 +00:00
return m_floppyDrive [ drive ] . m_disk . m_imagehandle = = NULL ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2007-05-28 11:16:42 +00:00
2021-10-18 20:13:37 +00:00
bool Disk2InterfaceCard : : IsWozImageInDrive ( const int drive )
2007-05-28 11:16:42 +00:00
{
2019-04-08 15:54:11 +00:00
if ( ! IsDriveValid ( drive ) )
2021-10-18 20:13:37 +00:00
return false ;
2010-01-03 18:43:08 +00:00
2021-10-18 20:13:37 +00:00
return ImageIsWOZ ( m_floppyDrive [ drive ] . m_disk . m_imagehandle ) ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2010-01-03 18:43:08 +00:00
2018-01-14 18:01:22 +00:00
# if LOG_DISK_NIBBLES_WRITE
2019-04-14 16:00:15 +00:00
bool Disk2InterfaceCard : : LogWriteCheckSyncFF ( ULONG & uCycleDelta )
2018-01-14 18:01:22 +00:00
{
bool bIsSyncFF = false ;
2019-04-07 14:54:26 +00:00
if ( m_uWriteLastCycle = = 0 ) // Reset to 0 when write mode is enabled
2018-01-14 18:01:22 +00:00
{
uCycleDelta = 0 ;
2019-04-07 14:54:26 +00:00
if ( m_floppyLatch = = 0xFF )
2018-01-14 18:01:22 +00:00
{
2019-04-07 14:54:26 +00:00
m_uSyncFFCount = 0 ;
2018-01-14 18:01:22 +00:00
bIsSyncFF = true ;
}
}
else
{
2019-04-07 14:54:26 +00:00
uCycleDelta = ( ULONG ) ( g_nCumulativeCycles - m_uWriteLastCycle ) ;
if ( m_floppyLatch = = 0xFF & & uCycleDelta > 32 )
2018-01-14 18:01:22 +00:00
{
2019-04-07 14:54:26 +00:00
m_uSyncFFCount + + ;
2018-01-14 18:01:22 +00:00
bIsSyncFF = true ;
}
}
2019-04-07 14:54:26 +00:00
m_uWriteLastCycle = g_nCumulativeCycles ;
2018-01-14 18:01:22 +00:00
return bIsSyncFF ;
}
# endif
//===========================================================================
2020-12-12 20:05:29 +00:00
void Disk2InterfaceCard : : UpdateLatchForEmptyDrive ( FloppyDrive * pDrive )
{
if ( ! pDrive - > m_isConnected )
{
m_floppyLatch = 0x80 ; // GH#864
return ;
}
// Drive connected
if ( ( g_nCumulativeCycles - pDrive - > m_motorOnCycle ) < MOTOR_ON_UNTIL_LSS_STABLE_CYCLES )
m_floppyLatch = 0x80 ; // GH#864
else
m_floppyLatch = rand ( ) & 0xFF ; // GH#748
}
2019-07-05 22:01:19 +00:00
void __stdcall Disk2InterfaceCard : : ReadWrite ( WORD pc , WORD addr , BYTE bWrite , BYTE d , ULONG uExecutedCycles )
2006-02-25 20:50:29 +00:00
{
2019-04-14 15:47:41 +00:00
FloppyDrive * pDrive = & m_floppyDrive [ m_currDrive ] ;
2019-04-14 15:58:49 +00:00
FloppyDisk * pFloppy = & pDrive - > m_disk ;
2010-01-03 18:43:08 +00:00
2019-04-14 15:58:49 +00:00
if ( ! pFloppy - > m_trackimagedata & & pFloppy - > m_imagehandle )
2019-07-05 22:01:19 +00:00
ReadTrack ( m_currDrive , uExecutedCycles ) ;
2010-01-03 18:43:08 +00:00
2019-04-14 15:58:49 +00:00
if ( ! pFloppy - > m_trackimagedata )
2020-12-12 20:05:29 +00:00
return UpdateLatchForEmptyDrive ( pDrive ) ;
2006-02-25 20:50:29 +00:00
2018-01-14 18:01:22 +00:00
// Improve precision of "authentic" drive mode - GH#125
UINT uSpinNibbleCount = 0 ;
2019-04-14 15:47:41 +00:00
if ( ! m_enhanceDisk & & pDrive - > m_spinning )
2018-01-14 18:01:22 +00:00
{
2019-04-07 14:54:26 +00:00
const ULONG nCycleDiff = ( ULONG ) ( g_nCumulativeCycles - m_diskLastCycle ) ;
m_diskLastCycle = g_nCumulativeCycles ;
2018-01-14 18:01:22 +00:00
if ( nCycleDiff > 40 )
{
// 40 cycles for a write of a 10-bit 0xFF sync byte
uSpinNibbleCount = nCycleDiff > > 5 ; // ...but divide by 32 (not 40)
2019-04-14 15:58:49 +00:00
ULONG uWrapOffset = uSpinNibbleCount % pFloppy - > m_nibbles ;
pFloppy - > m_byte + = uWrapOffset ;
if ( pFloppy - > m_byte > = pFloppy - > m_nibbles )
pFloppy - > m_byte - = pFloppy - > m_nibbles ;
2018-01-14 18:01:22 +00:00
# if LOG_DISK_NIBBLES_SPIN
2019-04-14 15:58:49 +00:00
UINT uCompleteRevolutions = uSpinNibbleCount / pFloppy - > m_nibbles ;
2018-01-14 18:01:22 +00:00
LOG_DISK ( " spin: revs=%d, nibbles=%d \r \n " , uCompleteRevolutions , uWrapOffset ) ;
# endif
}
}
2019-10-05 08:53:02 +00:00
if ( ! m_seqFunc . writeMode )
2015-09-13 10:39:58 +00:00
{
2018-12-03 17:38:52 +00:00
// 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.
2019-04-14 15:47:41 +00:00
if ( ! pDrive - > m_spinning ) // GH#599
2018-12-03 17:38:52 +00:00
return ;
2019-04-07 14:54:26 +00:00
const ULONG nReadCycleDiff = ( ULONG ) ( g_nCumulativeCycles - m_diskLastReadLatchCycle ) ;
2018-10-02 21:08:54 +00:00
// Support partial nibble read if disk reads are very close: (GH#582)
// . 6 cycles (1st->2nd read) for DOS 3.3 / $BD34: "read with delays to see if disk is spinning." (Beneath Apple DOS)
// . 6 cycles (1st->2nd read) for Curse of the Azure Bonds (loop to see if disk is spinning)
2019-12-08 16:23:29 +00:00
// . 25 cycles or higher fails for Legacy of the Ancients (GH#733)
2018-10-02 21:08:54 +00:00
// . 31 cycles is the max for a partial 8-bit nibble
2019-12-08 16:23:29 +00:00
const ULONG kReadAccessThreshold = 6 ; // Same for enhanced/authentic modes
2018-10-02 21:08:54 +00:00
if ( nReadCycleDiff < = kReadAccessThreshold )
{
UINT invalidBits = 8 - ( nReadCycleDiff / 4 ) ; // 4 cycles per bit-cell
2019-04-14 15:58:49 +00:00
m_floppyLatch = * ( pFloppy - > m_trackimage + pFloppy - > m_byte ) > > invalidBits ;
2019-04-07 14:54:26 +00:00
return ; // Early return so don't update: m_diskLastReadLatchCycle & pFloppy->byte
2018-10-02 21:08:54 +00:00
}
2019-04-14 15:58:49 +00:00
m_floppyLatch = * ( pFloppy - > m_trackimage + pFloppy - > m_byte ) ;
2019-04-07 14:54:26 +00:00
m_diskLastReadLatchCycle = g_nCumulativeCycles ;
2018-01-14 18:01:22 +00:00
# if LOG_DISK_NIBBLES_READ
# if LOG_DISK_NIBBLES_USE_RUNTIME_VAR
2019-04-07 14:54:26 +00:00
if ( m_bLogDisk_NibblesRW )
2018-01-14 18:01:22 +00:00
# endif
{
2019-04-14 15:58:49 +00:00
LOG_DISK ( " read %04X = %02X \r \n " , pFloppy - > m_byte , m_floppyLatch ) ;
2018-01-14 18:01:22 +00:00
}
2019-04-07 14:54:26 +00:00
m_formatTrack . DecodeLatchNibbleRead ( m_floppyLatch ) ;
2015-09-13 10:39:58 +00:00
# endif
}
2019-10-05 08:53:02 +00:00
else if ( ! pFloppy - > m_bWriteProtected ) // && m_seqFunc.writeMode
2015-09-13 10:39:58 +00:00
{
2019-07-05 22:01:19 +00:00
if ( ! pDrive - > m_spinning )
return ; // If not spinning then only 1 bit-cell gets written?
2019-04-14 15:58:49 +00:00
* ( pFloppy - > m_trackimage + pFloppy - > m_byte ) = m_floppyLatch ;
pFloppy - > m_trackimagedirty = true ;
2018-01-14 18:01:22 +00:00
2018-02-15 21:54:15 +00:00
bool bIsSyncFF = false ;
# if LOG_DISK_NIBBLES_WRITE
ULONG uCycleDelta = 0 ;
2018-02-24 13:18:26 +00:00
bIsSyncFF = LogWriteCheckSyncFF ( uCycleDelta ) ;
2018-02-15 21:54:15 +00:00
# endif
2019-04-07 14:54:26 +00:00
m_formatTrack . DecodeLatchNibbleWrite ( m_floppyLatch , uSpinNibbleCount , pFloppy , bIsSyncFF ) ; // GH#125
2018-01-14 18:01:22 +00:00
# if LOG_DISK_NIBBLES_WRITE
# if LOG_DISK_NIBBLES_USE_RUNTIME_VAR
2019-04-07 14:54:26 +00:00
if ( m_bLogDisk_NibblesRW )
2018-01-14 18:01:22 +00:00
# endif
{
2018-02-15 21:54:15 +00:00
if ( ! bIsSyncFF )
2019-04-14 15:58:49 +00:00
LOG_DISK ( " write %04X = %02X (cy=+%d) \r \n " , pFloppy - > m_byte , m_floppyLatch , uCycleDelta ) ;
2018-01-14 18:01:22 +00:00
else
2019-04-14 15:58:49 +00:00
LOG_DISK ( " write %04X = %02X (cy=+%d) sync #%d \r \n " , pFloppy - > m_byte , m_floppyLatch , uCycleDelta , m_uSyncFFCount ) ;
2018-01-14 18:01:22 +00:00
}
# endif
2015-09-13 10:39:58 +00:00
}
2006-02-25 20:50:29 +00:00
2019-04-14 15:58:49 +00:00
if ( + + pFloppy - > m_byte > = pFloppy - > m_nibbles )
pFloppy - > m_byte = 0 ;
2006-02-25 20:50:29 +00:00
2018-12-03 17:38:52 +00:00
// Show track status (GH#201) - NB. Prevent flooding of forcing UI to redraw!!!
2019-04-14 15:58:49 +00:00
if ( ( pFloppy - > m_byte & 0xFF ) = = 0 )
2021-01-10 16:33:06 +00:00
GetFrame ( ) . FrameDrawDiskStatus ( ) ;
2010-01-03 18:43:08 +00:00
}
//===========================================================================
2019-07-05 22:01:19 +00:00
void Disk2InterfaceCard : : ResetLogicStateSequencer ( void )
{
m_shiftReg = 0 ;
m_latchDelay = 0 ;
2020-02-09 21:23:15 +00:00
m_writeStarted = false ;
2019-07-05 22:01:19 +00:00
m_dbgLatchDelayedCnt = 0 ;
2021-04-10 14:31:54 +00:00
m_T00S00PatternIdx = 0 ;
m_foundT00S00Pattern = false ;
2019-07-05 22:01:19 +00:00
}
2020-02-09 21:23:15 +00:00
UINT Disk2InterfaceCard : : GetBitCellDelta ( const ULONG uExecutedCycles )
2019-07-05 22:01:19 +00:00
{
FloppyDisk & floppy = m_floppyDrive [ m_currDrive ] . m_disk ;
2020-02-09 21:23:15 +00:00
const BYTE optimalBitTiming = ImageGetOptimalBitTiming ( floppy . m_imagehandle ) ;
2019-07-05 22:01:19 +00:00
// NB. m_extraCycles is needed to retain accuracy:
2019-07-08 20:14:31 +00:00
// . Read latch #1: 0-> 9: cycleDelta= 9, bitCellDelta=2, extraCycles=1
// . Read latch #2: 9->20: cycleDelta=11, bitCellDelta=2, extraCycles=3
// . Overall: 0->20: cycleDelta=20, bitCellDelta=5, extraCycles=0
2019-07-05 22:01:19 +00:00
UINT bitCellDelta ;
#if 0
if ( optimalBitTiming = = 32 )
{
2020-02-09 21:23:15 +00:00
const ULONG cycleDelta = ( ULONG ) ( g_nCumulativeCycles - m_diskLastCycle ) + ( BYTE ) floppy . m_extraCycles ;
2019-07-05 22:01:19 +00:00
bitCellDelta = cycleDelta / 4 ; // DIV 4 for 4us per bit-cell
2020-02-09 21:23:15 +00:00
floppy . m_extraCycles = cycleDelta & 3 ; // MOD 4 : remainder carried forward for next time
2019-07-05 22:01:19 +00:00
}
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 ) ;
}
2020-02-09 21:23:15 +00:00
// NB. actual m_diskLastCycle for the last bitCell is minus floppy.m_extraCycles
// - but don't need this value; and it's correctly accounted for in this function.
m_diskLastCycle = g_nCumulativeCycles ;
2019-07-05 22:01:19 +00:00
return bitCellDelta ;
}
void Disk2InterfaceCard : : UpdateBitStreamPosition ( FloppyDisk & floppy , const ULONG bitCellDelta )
{
2020-02-22 12:04:13 +00:00
if ( floppy . m_bitCount = = 0 ) // Repro: Boot DOS3.3(WOZ), eject+reinsert disk, CALL-151, C0E9 N C0ED ; motor-on & LoadWriteProtect()
2019-07-05 22:01:19 +00:00
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 ;
}
2020-02-09 21:23:15 +00:00
__forceinline void Disk2InterfaceCard : : IncBitStream ( FloppyDisk & floppy )
{
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 ;
}
2022-09-19 10:00:34 +00:00
if ( floppy . m_bitOffset = = floppy . m_initialBitOffset )
floppy . m_revs + + ;
2020-02-09 21:23:15 +00:00
}
2021-04-10 14:31:54 +00:00
void Disk2InterfaceCard : : PreJitterCheck ( int phase , BYTE latch )
{
if ( phase ! = 0 | | ( latch & 0x80 ) = = 0 )
return ;
if ( latch = = m_T00S00Pattern [ m_T00S00PatternIdx ] )
{
m_T00S00PatternIdx + + ;
if ( m_T00S00PatternIdx = = sizeof ( m_T00S00Pattern ) )
m_foundT00S00Pattern = true ; // 6502 code has just read latch nibbles for T$00,S$00 address prologue
}
else
{
m_T00S00PatternIdx = 0 ;
}
}
// GH#930: After T$00,S$00 randomly skip 1 bit-cell.
// . PreJitterCheck() condition met && skipped a big number of bit-cells.
// . Fix is just for 'Wasteland' and 'Legacy of the Ancients' (but shouldn't interfere with any other woz images).
// . NB. This is likely to be the transition from DiskII firmware ($C6xx) to user-code ($801),
// so skipping 1 bit-cell here shouldn't matter.
// . And (see comment [*1]) the T00S00 pattern only matches a handful of titles.
void Disk2InterfaceCard : : AddJitter ( int phase , FloppyDisk & floppy )
{
if ( phase = = 0 & & m_foundT00S00Pattern )
{
if ( rand ( ) < RAND_THRESHOLD ( 1 , 10 ) )
{
LogOutput ( " Disk: T$00 jitter - slip 1 bitcell (PC=%04X) \n " , regs . pc ) ;
IncBitStream ( floppy ) ;
}
else
{
LogOutput ( " Disk: T$00 jitter - *** SKIP *** (PC=%04X) \n " , regs . pc ) ;
}
}
m_T00S00PatternIdx = 0 ;
m_foundT00S00Pattern = false ;
}
2022-09-19 10:00:34 +00:00
// GH#1125: For T$21 (track 33.0) or above (and sufficiently long sync FF/10 run-length), then randomly skip 1 bit-cell at the start of the FF/2 track seam.
// Example of high sync FF/10 run-lengths for tracks 33.0+:
// . Accolade Comics:114, Silent Service:117, Wings of Fury:140, Wizardry I:127, Wizardry III:283
// NB. Restrict to higher FF/10 run-lengths to limit the titles affected by this jitter.
void Disk2InterfaceCard : : AddTrackSeamJitter ( float phasePrecise , FloppyDisk & floppy )
{
if ( phasePrecise > = ( 33.0 * 2 ) & & floppy . m_longestSyncFFRunLength > 110 )
{
if ( floppy . m_bitOffset = = floppy . m_longestSyncFFBitOffsetStart )
{
if ( rand ( ) < RAND_THRESHOLD ( 5 , 10 ) )
{
LogOutput ( " Disk: T%05.2f jitter - slip 1 bitcell (revs=%d) (PC=%04X) \n " , phasePrecise / 2 , floppy . m_revs , regs . pc ) ;
IncBitStream ( floppy ) ;
}
else
{
LogOutput ( " Disk: T%05.2f jitter - *** SKIP *** (revs=%d) (PC=%04X) \n " , phasePrecise / 2 , floppy . m_revs , regs . pc ) ;
}
}
}
}
2020-02-09 21:23:15 +00:00
void __stdcall Disk2InterfaceCard : : DataLatchReadWriteWOZ ( WORD pc , WORD addr , BYTE bWrite , ULONG uExecutedCycles )
2019-07-05 22:01:19 +00:00
{
2020-02-09 21:23:15 +00:00
_ASSERT ( m_seqFunc . function ! = dataShiftWrite ) ;
2019-07-05 22:01:19 +00:00
FloppyDrive & drive = m_floppyDrive [ m_currDrive ] ;
FloppyDisk & floppy = drive . m_disk ;
if ( ! floppy . m_trackimagedata & & floppy . m_imagehandle )
2022-08-20 11:54:14 +00:00
{
2019-07-05 22:01:19 +00:00
ReadTrack ( m_currDrive , uExecutedCycles ) ;
2022-08-20 11:54:14 +00:00
// NB. ReadTrack() has called GetBitCellDelta(), so the subsequent call to GetBitCellDelta() below just returns bitCellDelta==0
// So could just return at this point.
}
2019-07-05 22:01:19 +00:00
if ( ! floppy . m_trackimagedata )
{
_ASSERT ( 0 ) ; // Can't happen for WOZ - ReadTrack() should return an empty track
2020-12-12 20:05:29 +00:00
return UpdateLatchForEmptyDrive ( & drive ) ;
2019-07-05 22:01:19 +00:00
}
// 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
2019-08-03 17:21:41 +00:00
return ;
2019-07-05 22:01:19 +00:00
// 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.
2022-01-29 21:27:03 +00:00
const UINT significantBitCells = 100 ; // eg. long stream of weak bits and/or 5x 10-bit sync FF nibbles (GH#1020)
2020-02-09 21:23:15 +00:00
UINT bitCellDelta = GetBitCellDelta ( uExecutedCycles ) ;
2019-07-05 22:01:19 +00:00
UINT bitCellRemainder ;
if ( bitCellDelta < = significantBitCells )
{
bitCellRemainder = bitCellDelta ;
}
else
{
bitCellRemainder = significantBitCells ;
bitCellDelta - = significantBitCells ;
UpdateBitStreamPosition ( floppy , bitCellDelta ) ;
m_latchDelay = 0 ;
2019-09-30 19:39:47 +00:00
drive . m_headWindow = 0 ;
2021-04-10 14:31:54 +00:00
AddJitter ( drive . m_phase , floppy ) ; // Only call when skipping a big number of bit-cells (ie. >significantBitCells)
2019-07-05 22:01:19 +00:00
}
2019-08-03 17:21:41 +00:00
if ( ! bWrite )
2019-10-05 08:53:02 +00:00
{
if ( m_seqFunc . function ! = readSequencing )
2020-02-09 21:23:15 +00:00
{
_ASSERT ( m_seqFunc . function = = checkWriteProtAndInitWrite ) ;
UpdateBitStreamPosition ( floppy , bitCellRemainder ) ;
2019-10-05 08:53:02 +00:00
return ;
2020-02-09 21:23:15 +00:00
}
2019-10-05 08:53:02 +00:00
2019-08-03 17:21:41 +00:00
DataLatchReadWOZ ( pc , addr , bitCellRemainder ) ;
2021-04-10 14:31:54 +00:00
PreJitterCheck ( drive . m_phase , m_floppyLatch ) ; // Pre: m_floppyLatch just updated
2019-10-05 08:53:02 +00:00
}
2019-08-03 17:21:41 +00:00
else
2019-10-05 08:53:02 +00:00
{
2020-02-09 21:23:15 +00:00
_ASSERT ( m_seqFunc . function = = dataLoadWrite ) ;
DataLoadWriteWOZ ( pc , addr , bitCellRemainder ) ;
2019-10-05 08:53:02 +00:00
}
2019-08-03 17:21:41 +00:00
// Show track status (GH#201) - NB. Prevent flooding of forcing UI to redraw!!!
if ( ( floppy . m_byte & 0xFF ) = = 0 )
2021-01-10 16:33:06 +00:00
GetFrame ( ) . FrameDrawDiskStatus ( ) ;
2019-07-30 21:41:28 +00:00
}
2019-08-03 17:10:39 +00:00
void Disk2InterfaceCard : : DataLatchReadWOZ ( WORD pc , WORD addr , UINT bitCellRemainder )
2019-07-30 21:41:28 +00:00
{
// m_diskLastReadLatchCycle = g_nCumulativeCycles; // Not used by WOZ (only by NIB)
2019-07-05 22:01:19 +00:00
# if LOG_DISK_NIBBLES_READ
2019-07-30 21:41:28 +00:00
bool newLatchData = false ;
2019-07-05 22:01:19 +00:00
# endif
2019-07-30 21:41:28 +00:00
FloppyDrive & drive = m_floppyDrive [ m_currDrive ] ;
FloppyDisk & floppy = drive . m_disk ;
2019-07-05 22:01:19 +00:00
2019-08-03 16:51:19 +00:00
# if _DEBUG
static int dbgWOZ = 0 ;
if ( dbgWOZ )
{
2019-09-30 19:39:47 +00:00
dbgWOZ = 0 ;
2019-12-30 19:52:49 +00:00
DumpTrackWOZ ( floppy ) ; // Enable as necessary
2019-08-03 16:51:19 +00:00
}
# endif
2019-07-30 21:41:28 +00:00
for ( UINT i = 0 ; i < bitCellRemainder ; i + + )
{
BYTE n = floppy . m_trackimage [ floppy . m_byte ] ;
2019-07-05 22:01:19 +00:00
2019-07-30 21:41:28 +00:00
drive . m_headWindow < < = 1 ;
drive . m_headWindow | = ( n & floppy . m_bitMask ) ? 1 : 0 ;
BYTE outputBit = ( drive . m_headWindow & 0xf ) ? ( drive . m_headWindow > > 1 ) & 1
2020-11-10 20:33:55 +00:00
: ( rand ( ) < RAND_THRESHOLD ( 3 , 10 ) ) ? 1 : 0 ; // ~30% chance of a 1 bit (Ref: WOZ-2.0)
2019-07-05 22:01:19 +00:00
2020-02-09 21:23:15 +00:00
IncBitStream ( floppy ) ;
2019-07-05 22:01:19 +00:00
2022-09-19 10:00:34 +00:00
AddTrackSeamJitter ( drive . m_phasePrecise , floppy ) ;
2019-07-30 21:41:28 +00:00
m_shiftReg < < = 1 ;
m_shiftReg | = outputBit ;
if ( m_latchDelay )
{
m_latchDelay - = 4 ;
if ( m_latchDelay < 0 )
m_latchDelay = 0 ;
2019-07-05 22:01:19 +00:00
2019-07-30 21:41:28 +00:00
if ( m_shiftReg )
2019-07-05 22:01:19 +00:00
{
2019-07-30 21:41:28 +00:00
m_dbgLatchDelayedCnt = 0 ;
}
else // m_shiftReg==0
{
m_latchDelay + = 4 ; // extend by 4us (so 7us again) - GH#662
2019-07-05 22:01:19 +00:00
2019-07-30 21:41:28 +00:00
m_dbgLatchDelayedCnt + + ;
# if LOG_DISK_NIBBLES_READ
if ( m_dbgLatchDelayedCnt > = 3 )
2019-07-05 22:01:19 +00:00
{
2019-07-30 21:41:28 +00:00
LOG_DISK ( " read: latch held due to 0: PC=%04X, cnt=%02X \r \n " , regs . pc , m_dbgLatchDelayedCnt ) ;
2019-07-05 22:01:19 +00:00
}
# endif
}
2019-07-30 21:41:28 +00:00
}
2019-07-05 22:01:19 +00:00
2019-07-30 21:41:28 +00:00
if ( ! m_latchDelay )
{
2019-07-05 22:01:19 +00:00
# if LOG_DISK_NIBBLES_READ
2019-07-30 21:41:28 +00:00
if ( newLatchData )
{
LOG_DISK ( " read skipped latch data: %04X = %02X \r \n " , floppy . m_byte , m_floppyLatch ) ;
newLatchData = false ;
}
2019-07-05 22:01:19 +00:00
# endif
2019-07-30 21:41:28 +00:00
m_floppyLatch = m_shiftReg ;
2019-07-05 22:01:19 +00:00
2019-07-30 21:41:28 +00:00
if ( m_shiftReg & 0x80 )
{
m_latchDelay = 7 ;
m_shiftReg = 0 ;
2019-07-05 22:01:19 +00:00
# if LOG_DISK_NIBBLES_READ
2019-07-30 21:41:28 +00:00
// 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 ;
2019-07-05 22:01:19 +00:00
# endif
}
}
2019-12-30 19:52:49 +00:00
} // for
2019-07-05 22:01:19 +00:00
# if LOG_DISK_NIBBLES_READ
2019-07-30 21:41:28 +00:00
if ( m_floppyLatch & 0x80 )
{
# if LOG_DISK_NIBBLES_USE_RUNTIME_VAR
if ( m_bLogDisk_NibblesRW )
# endif
2019-07-05 22:01:19 +00:00
{
2019-07-30 21:41:28 +00:00
LOG_DISK ( " read %04X = %02X \r \n " , floppy . m_byte , m_floppyLatch ) ;
2019-07-05 22:01:19 +00:00
}
}
2019-07-30 21:41:28 +00:00
# endif
}
2020-02-09 21:23:15 +00:00
void Disk2InterfaceCard : : DataLoadWriteWOZ ( WORD pc , WORD addr , UINT bitCellRemainder )
2019-07-30 21:41:28 +00:00
{
2020-02-09 21:23:15 +00:00
_ASSERT ( m_seqFunc . function = = dataLoadWrite ) ;
2019-07-30 21:41:28 +00:00
FloppyDrive & drive = m_floppyDrive [ m_currDrive ] ;
FloppyDisk & floppy = drive . m_disk ;
2020-02-09 21:23:15 +00:00
if ( floppy . m_bWriteProtected )
{
_ASSERT ( 0 ) ; // Must be a bug in the 6502 code for this to occur!
UpdateBitStreamPosition ( floppy , bitCellRemainder ) ;
return ;
}
if ( ! m_writeStarted )
UpdateBitStreamPosition ( floppy , bitCellRemainder ) ; // skip over bitCells before switching to write mode
m_writeStarted = true ;
# if LOG_DISK_WOZ_LOADWRITE
LOG_DISK ( " load shiftReg with %02X (was: %02X) \n " , m_floppyLatch , m_shiftReg ) ;
# endif
m_shiftReg = m_floppyLatch ;
2022-09-19 10:00:34 +00:00
floppy . m_longestSyncFFBitOffsetStart = - 1 ; // invalidate the track seam location after a write
2020-02-09 21:23:15 +00:00
}
void Disk2InterfaceCard : : DataShiftWriteWOZ ( WORD pc , WORD addr , ULONG uExecutedCycles )
{
_ASSERT ( m_seqFunc . function = = dataShiftWrite ) ;
FloppyDrive & drive = m_floppyDrive [ m_currDrive ] ;
FloppyDisk & floppy = drive . m_disk ;
const UINT bitCellRemainder = GetBitCellDelta ( uExecutedCycles ) ;
if ( floppy . m_bWriteProtected )
{
_ASSERT ( 0 ) ; // Must be a bug in the 6502 code for this to occur!
UpdateBitStreamPosition ( floppy , bitCellRemainder ) ;
return ;
}
2022-08-30 20:43:20 +00:00
if ( ! drive . m_spinning )
return ;
if ( ! floppy . m_trackimagedata ) // GH#1126
return ;
2020-02-09 21:23:15 +00:00
# if LOG_DISK_WOZ_SHIFTWRITE
LOG_DISK ( " T$%02X, bitOffset=%04X: %02X (%d bits) \n " , drive . m_phase / 2 , floppy . m_bitOffset , m_shiftReg , bitCellRemainder ) ;
# endif
for ( UINT i = 0 ; i < bitCellRemainder ; i + + )
2019-07-05 22:01:19 +00:00
{
2020-02-09 21:23:15 +00:00
BYTE outputBit = m_shiftReg & 0x80 ;
m_shiftReg < < = 1 ;
BYTE n = floppy . m_trackimage [ floppy . m_byte ] ;
n & = ~ floppy . m_bitMask ;
if ( outputBit ) n | = floppy . m_bitMask ;
floppy . m_trackimage [ floppy . m_byte ] = n ;
IncBitStream ( floppy ) ;
2019-07-05 22:01:19 +00:00
}
2020-02-09 21:23:15 +00:00
floppy . m_trackimagedirty = true ;
2019-08-03 17:10:39 +00:00
}
2019-07-05 22:01:19 +00:00
//===========================================================================
2022-09-19 10:00:34 +00:00
// For now all that's needed is this basic case:
// . find [start,end] of longest run of FF/10 sync nibbles
void Disk2InterfaceCard : : FindTrackSeamWOZ ( FloppyDisk & floppy , float track )
{
const UINT oldBitOffset = floppy . m_bitOffset ; // Save current state
BYTE shiftReg = 0 ;
UINT zeroCount = 0 ;
int startBitOffset = - 1 ; // NB. change this to start of first FF/10
floppy . m_bitOffset = 0 ;
UpdateBitStreamOffsets ( floppy ) ;
int nibbleStartBitOffset = - 1 ;
int syncFFStartBitOffset = - 1 ;
int syncFFRunLength = 0 ;
int longestSyncFFStartBitOffset = - 1 ;
int longestSyncFFRunLength = 0 ;
floppy . m_longestSyncFFBitOffsetStart = - 1 ;
while ( 1 )
{
BYTE n = floppy . m_trackimage [ floppy . m_byte ] ;
BYTE outputBit = ( n & floppy . m_bitMask ) ? 1 : 0 ;
IncBitStream ( floppy ) ;
if ( ( startBitOffset < 0 & & floppy . m_bitOffset = = 0 ) | | ( startBitOffset = = floppy . m_bitOffset ) ) // done complete track?
break ;
if ( shiftReg & 0x80 )
{
if ( outputBit = = 0 ) // zero, so LSS holds nibble in latch
{
zeroCount + + ;
continue ;
}
// else: start of next nibble
if ( shiftReg = = 0xff & & zeroCount = = 2 )
{
if ( syncFFStartBitOffset < 0 )
syncFFStartBitOffset = nibbleStartBitOffset ;
syncFFRunLength + + ;
}
if ( ( shiftReg ! = 0xff | | zeroCount ! = 2 ) & & syncFFStartBitOffset > = 0 )
{
// Longest FF/2 run could straddle end/start of track's bit buffer
if ( startBitOffset < 0 )
startBitOffset = nibbleStartBitOffset ;
if ( longestSyncFFRunLength < syncFFRunLength )
{
longestSyncFFStartBitOffset = syncFFStartBitOffset ;
longestSyncFFRunLength = syncFFRunLength ;
}
syncFFStartBitOffset = - 1 ;
syncFFRunLength = 0 ;
}
shiftReg = 0 ;
zeroCount = 0 ;
}
shiftReg < < = 1 ;
shiftReg | = outputBit ;
if ( shiftReg = = 0x01 )
{
nibbleStartBitOffset = floppy . m_bitOffset - 1 ;
if ( nibbleStartBitOffset < 0 ) nibbleStartBitOffset + = floppy . m_bitCount ;
}
}
if ( longestSyncFFRunLength )
{
const int longestSyncFFBitOffsetEnd = ( longestSyncFFStartBitOffset + longestSyncFFRunLength * 10 - 1 ) % floppy . m_bitCount ;
# if LOG_DISK_WOZ_TRACK_SEAM
LOG_DISK ( " Track seam: T%05.2f: FF/10 (run=%d), start=%04X, end=%04X \n " , track , longestSyncFFRunLength , longestSyncFFStartBitOffset , longestSyncFFBitOffsetEnd ) ;
# endif
floppy . m_longestSyncFFBitOffsetStart = longestSyncFFStartBitOffset ;
floppy . m_longestSyncFFRunLength = longestSyncFFRunLength ;
}
else
{
# if LOG_DISK_WOZ_TRACK_SEAM
LOG_DISK ( " Track seam: T%05.2f: FF/10 (none) \n " , track ) ;
# endif
}
// Restore state
floppy . m_bitOffset = oldBitOffset ;
UpdateBitStreamOffsets ( floppy ) ;
}
//===========================================================================
2019-08-03 16:51:19 +00:00
# ifdef _DEBUG
// Dump nibbles from current position bitstream wraps to same position
2021-02-20 12:32:29 +00:00
// NB. Need to define LOG_DISK_NIBBLES_READ so that GetReadD5AAxxDetectedString() works.
2019-08-03 16:51:19 +00:00
void Disk2InterfaceCard : : DumpTrackWOZ ( FloppyDisk floppy ) // pass a copy of m_floppy
{
2020-02-23 13:19:38 +00:00
FormatTrack formatTrack ( true ) ;
2019-08-03 16:51:19 +00:00
BYTE shiftReg = 0 ;
2019-09-30 19:39:47 +00:00
UINT zeroCount = 0 ;
2019-08-03 16:51:19 +00:00
UINT nibbleCount = 0 ;
2022-09-19 10:00:34 +00:00
const UINT startBitOffset = 0 ; // NB. may need to tweak this offset, since the bitstream is a circular buffer
2020-02-23 13:19:38 +00:00
floppy . m_bitOffset = startBitOffset ;
2022-09-19 10:00:34 +00:00
UpdateBitStreamOffsets ( floppy ) ;
2019-08-03 16:51:19 +00:00
2022-09-19 10:00:34 +00:00
int nibbleStartBitOffset = - 1 ;
2019-08-03 16:51:19 +00:00
2019-12-20 12:49:02 +00:00
bool newLine = true ;
2022-09-19 10:00:34 +00:00
bool doneLastBit = false ;
2019-12-20 12:49:02 +00:00
2019-08-03 16:51:19 +00:00
while ( 1 )
{
2022-09-19 10:00:34 +00:00
if ( newLine & & nibbleStartBitOffset > = 0 )
2019-12-20 12:49:02 +00:00
{
newLine = false ;
2022-09-19 10:00:34 +00:00
LogOutput ( " %04X: " , nibbleStartBitOffset ) ;
nibbleStartBitOffset = - 1 ;
2019-12-20 12:49:02 +00:00
}
2019-08-03 16:51:19 +00:00
BYTE n = floppy . m_trackimage [ floppy . m_byte ] ;
BYTE outputBit = ( n & floppy . m_bitMask ) ? 1 : 0 ;
2021-02-20 12:32:29 +00:00
IncBitStream ( floppy ) ;
2019-08-03 16:51:19 +00:00
2021-02-20 12:32:29 +00:00
if ( startBitOffset = = floppy . m_bitOffset ) // done complete track?
2022-09-19 10:00:34 +00:00
doneLastBit = true ;
else if ( doneLastBit )
2019-08-03 16:51:19 +00:00
break ;
2022-09-19 10:00:34 +00:00
if ( shiftReg & 0x80 )
2019-09-30 19:39:47 +00:00
{
2022-09-19 10:00:34 +00:00
if ( outputBit = = 0 ) // zero, so LSS holds nibble in latch
{
zeroCount + + ;
continue ;
}
2019-08-03 16:51:19 +00:00
2022-09-19 10:00:34 +00:00
// else: start of next nibble
2019-08-03 16:51:19 +00:00
2022-09-19 10:00:34 +00:00
nibbleCount + + ;
2019-08-03 16:51:19 +00:00
2022-09-19 10:00:34 +00:00
char syncBits = zeroCount < = 9 ? ' 0 ' + zeroCount : ' + ' ;
if ( zeroCount = = 0 ) LogOutput ( " %02X " , shiftReg ) ;
else LogOutput ( " %02X(%c) " , shiftReg, syncBits) ;
2020-02-23 13:19:38 +00:00
2022-09-19 10:00:34 +00:00
formatTrack . DecodeLatchNibbleRead ( shiftReg ) ;
2020-02-23 13:19:38 +00:00
2022-09-19 10:00:34 +00:00
if ( ( nibbleCount % 32 ) = = 0 )
2020-02-23 13:19:38 +00:00
{
2022-09-19 10:00:34 +00:00
std : : string strReadDetected = formatTrack . GetReadD5AAxxDetectedString ( ) ;
if ( ! strReadDetected . empty ( ) )
{
OutputDebugString ( " \t ; " ) ;
OutputDebugString ( strReadDetected . c_str ( ) ) ;
}
OutputDebugString ( " \n " ) ;
newLine = true ;
2020-02-23 13:19:38 +00:00
}
2022-09-19 10:00:34 +00:00
shiftReg = 0 ;
zeroCount = 0 ;
2019-12-20 12:49:02 +00:00
}
2019-08-03 16:51:19 +00:00
2022-09-19 10:00:34 +00:00
shiftReg < < = 1 ;
shiftReg | = outputBit ;
2021-02-20 12:32:29 +00:00
2022-09-19 10:00:34 +00:00
if ( shiftReg = = 0x01 )
{
nibbleStartBitOffset = floppy . m_bitOffset - 1 ;
if ( nibbleStartBitOffset < 0 ) nibbleStartBitOffset + = floppy . m_bitCount ;
}
2021-02-20 12:32:29 +00:00
}
// Output any partial nibble
2022-09-19 10:00:34 +00:00
if ( shiftReg & 0x80 )
{
LogOutput ( " %02X " , shiftReg ) ;
// Output any remaining zeroCount
if ( zeroCount )
{
char syncBits = zeroCount < = 9 ? ' 0 ' + zeroCount : ' + ' ;
LogOutput ( " (%c) " , syncBits ) ;
}
}
else if ( shiftReg )
2021-02-20 12:32:29 +00:00
{
2022-02-13 21:37:05 +00:00
LogOutput ( " %02X/Partial Nibble " , shiftReg ) ;
2021-02-20 12:32:29 +00:00
}
2020-02-23 13:19:38 +00:00
// Output any remaining "read D5AAxx detected"
if ( nibbleCount % 32 )
{
std : : string strReadDetected = formatTrack . GetReadD5AAxxDetectedString ( ) ;
if ( ! strReadDetected . empty ( ) )
{
OutputDebugString ( " \t ; " ) ;
OutputDebugString ( strReadDetected . c_str ( ) ) ;
}
OutputDebugString ( " \n " ) ;
}
2019-08-03 16:51:19 +00:00
}
# endif
//===========================================================================
2019-12-19 19:42:30 +00:00
void Disk2InterfaceCard : : Reset ( const bool bIsPowerCycle )
2010-01-03 18:43:08 +00:00
{
2017-05-14 10:33:07 +00:00
// RESET forces all switches off (UTAIIe Table 9.1)
2019-07-05 22:01:19 +00:00
ResetSwitches ( ) ;
2017-08-11 20:45:07 +00:00
2019-04-07 14:54:26 +00:00
m_formatTrack . Reset ( ) ;
2018-01-14 18:01:22 +00:00
2019-07-05 22:01:19 +00:00
ResetLogicStateSequencer ( ) ;
2017-08-12 10:50:31 +00:00
if ( bIsPowerCycle ) // GH#460
2017-08-11 20:45:07 +00:00
{
2018-10-02 21:08:54 +00:00
// NB. This doesn't affect the drive head (ie. drive's track position)
// . The initial machine start-up state is track=0, but after a power-cycle the track could be any value.
// . (For DiskII firmware, this results in a subtle extra latch read in this latter case, for the track!=0 case)
2019-04-14 15:47:41 +00:00
m_floppyDrive [ DRIVE_1 ] . m_spinning = 0 ;
m_floppyDrive [ DRIVE_1 ] . m_writelight = 0 ;
m_floppyDrive [ DRIVE_2 ] . m_spinning = 0 ;
m_floppyDrive [ DRIVE_2 ] . m_writelight = 0 ;
2017-08-11 20:45:07 +00:00
2021-01-10 16:33:06 +00:00
GetFrame ( ) . FrameRefreshStatus ( DRAW_LEDS ) ;
2017-08-11 20:45:07 +00:00
}
2020-02-22 11:38:25 +00:00
InitFirmware ( GetCxRomPeripheral ( ) ) ;
2021-01-10 16:33:06 +00:00
GetFrame ( ) . FrameRefreshStatus ( DRAW_TITLE ) ;
2010-01-03 18:43:08 +00:00
}
2019-07-05 22:01:19 +00:00
void Disk2InterfaceCard : : ResetSwitches ( void )
{
m_currDrive = 0 ;
m_floppyMotorOn = 0 ;
m_magnetStates = 0 ;
2019-10-05 08:53:02 +00:00
m_seqFunc . function = readSequencing ;
2019-07-05 22:01:19 +00:00
}
2010-01-03 18:43:08 +00:00
//===========================================================================
2019-04-14 16:00:15 +00:00
bool Disk2InterfaceCard : : UserSelectNewDiskImage ( const int drive , LPCSTR pszFilename /*=""*/ )
2010-01-03 18:43:08 +00:00
{
2020-12-12 20:49:46 +00:00
if ( ! IsDriveConnected ( drive ) )
{
2021-01-19 20:37:43 +00:00
GetFrame ( ) . FrameMessageBox ( " Drive not connected! " , " Insert disk " , MB_ICONEXCLAMATION | MB_SETFOREGROUND | MB_OK ) ;
2020-12-12 20:49:46 +00:00
return false ;
}
2019-08-09 03:50:29 +00:00
TCHAR directory [ MAX_PATH ] ;
TCHAR filename [ MAX_PATH ] ;
2010-01-03 18:43:08 +00:00
2019-08-09 03:50:29 +00:00
StringCbCopy ( filename , MAX_PATH , pszFilename ) ;
2010-01-03 18:43:08 +00:00
2020-10-25 17:14:23 +00:00
RegLoadString ( TEXT ( REG_PREFS ) , TEXT ( REGVALUE_PREF_START_DIR ) , 1 , directory , MAX_PATH , TEXT ( " " ) ) ;
2022-02-15 18:48:20 +00:00
std : : string title = StrFormat ( " Select Disk Image For Drive %d " , drive + 1 ) ;
2010-01-03 18:43:08 +00:00
OPENFILENAME ofn ;
2020-12-10 21:08:15 +00:00
memset ( & ofn , 0 , sizeof ( OPENFILENAME ) ) ;
2010-01-03 18:43:08 +00:00
ofn . lStructSize = sizeof ( OPENFILENAME ) ;
2020-12-24 15:08:50 +00:00
ofn . hwndOwner = GetFrame ( ) . g_hFrameWindow ;
ofn . hInstance = GetFrame ( ) . g_hInstance ;
2019-07-05 22:01:19 +00:00
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,*.woz,*.zip,*.2mg,*.2img,*.iie) \0 *.bin;*.do;*.dsk;*.nib;*.po;*.gz;*.woz;*.zip;*.2mg;*.2img;*.iie \0 " )
2010-01-17 18:43:06 +00:00
TEXT ( " All Files \0 *.* \0 " ) ;
2010-01-03 18:43:08 +00:00
ofn . lpstrFile = filename ;
ofn . nMaxFile = MAX_PATH ;
ofn . lpstrInitialDir = directory ;
ofn . Flags = OFN_PATHMUSTEXIST ;
2022-02-15 18:48:20 +00:00
ofn . lpstrTitle = title . c_str ( ) ;
2006-02-25 20:50:29 +00:00
2015-02-13 22:40:53 +00:00
bool bRes = false ;
2008-08-31 04:31:35 +00:00
if ( GetOpenFileName ( & ofn ) )
2006-02-25 20:50:29 +00:00
{
2008-08-31 04:31:35 +00:00
if ( ( ! ofn . nFileExtension ) | | ! filename [ ofn . nFileExtension ] )
2019-08-09 03:50:29 +00:00
StringCbCat ( filename , MAX_PATH , TEXT ( " .dsk " ) ) ;
2008-08-31 04:31:35 +00:00
2019-04-08 15:54:11 +00:00
ImageError_e Error = InsertDisk ( drive , filename , ofn . Flags & OFN_READONLY , IMAGE_CREATE ) ;
2010-01-03 18:43:08 +00:00
if ( Error = = eIMAGE_ERROR_NONE )
2008-08-31 04:31:35 +00:00
{
2015-02-13 22:40:53 +00:00
bRes = true ;
2008-08-31 04:31:35 +00:00
}
else
{
2019-04-08 15:54:11 +00:00
NotifyInvalidImage ( drive , filename , Error ) ;
2008-08-31 04:31:35 +00:00
}
2006-02-25 20:50:29 +00:00
}
2015-02-13 22:40:53 +00:00
return bRes ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2010-01-03 18:43:08 +00:00
2019-07-05 22:01:19 +00:00
void __stdcall Disk2InterfaceCard : : LoadWriteProtect ( WORD , WORD , BYTE write , BYTE value , ULONG uExecutedCycles )
2018-02-11 14:52:21 +00:00
{
2018-12-03 17:38:52 +00:00
// 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: Gemstone Warrior sets load mode with the drive off.
2019-04-14 15:47:41 +00:00
if ( ! m_floppyDrive [ m_currDrive ] . m_spinning ) // GH#599
2018-12-03 17:38:52 +00:00
return ;
2019-09-30 20:29:58 +00:00
// Notes:
2022-04-23 17:13:15 +00:00
// . Only READ-LOAD mode ($C08E,X & $C08D,X) can issue the SR (shift write-protect) operation - UTAIIe page 9-20, fig 9.11
2019-09-30 20:29:58 +00:00
// . Phase 1 on also forces write protect in the Disk II drive (UTAIIe page 9-7) but we don't implement that.
// . 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,
// the write protect switch would still be read correctly" (UTAIIe page 9-21)
2022-04-18 20:13:41 +00:00
// . Sequencer "SR" (Shift Right) command shifts the data register right and loads QA (bit7) with write protect (UTAIIe page 9-21)
2019-10-05 08:53:02 +00:00
// . A read or write will shift 'write protect' in QA.
2022-04-18 20:13:41 +00:00
// . The LSS saturates the data register before the CPU can read an intermediate value: so set to 0xFF or 0x00 (GH#1078)
2020-02-09 21:23:15 +00:00
FloppyDisk & floppy = m_floppyDrive [ m_currDrive ] . m_disk ;
if ( floppy . m_bWriteProtected )
2022-04-18 20:13:41 +00:00
m_floppyLatch = 0xFF ;
2019-09-30 20:29:58 +00:00
else
2022-04-18 20:13:41 +00:00
m_floppyLatch = 0x00 ;
2019-07-05 22:01:19 +00:00
2020-02-09 21:23:15 +00:00
if ( m_writeStarted ) // Prevent ResetLogicStateSequencer() from resetting m_writeStarted
return ;
if ( ImageIsWOZ ( floppy . m_imagehandle ) )
2019-07-05 22:01:19 +00:00
{
# if LOG_DISK_NIBBLES_READ
2019-09-30 19:39:47 +00:00
LOG_DISK ( " %08X: reset LSS: ~PC=%04X \r \n " , ( UINT32 ) g_nCumulativeCycles , regs . pc ) ;
2019-07-05 22:01:19 +00:00
# endif
2020-02-09 21:23:15 +00:00
const UINT bitCellDelta = GetBitCellDelta ( uExecutedCycles ) ;
UpdateBitStreamPosition ( floppy , bitCellDelta ) ; // Fix E7-copy protection
2022-04-23 17:13:15 +00:00
ResetLogicStateSequencer ( ) ; // "Set the sequencer to State 0" (UTAIIe page 9-21)
2019-07-05 22:01:19 +00:00
}
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2010-01-03 18:43:08 +00:00
2019-09-30 19:39:47 +00:00
void __stdcall Disk2InterfaceCard : : SetReadMode ( WORD , WORD , BYTE , BYTE , ULONG uExecutedCycles )
2010-01-03 18:43:08 +00:00
{
2019-04-14 15:47:41 +00:00
m_formatTrack . DriveSwitchedToReadMode ( & m_floppyDrive [ m_currDrive ] . m_disk ) ;
2018-01-14 18:01:22 +00:00
# if LOG_DISK_RW_MODE
2019-09-30 19:39:47 +00:00
LOG_DISK ( " %08X: rw mode: read \r \n " , ( UINT32 ) g_nCumulativeCycles ) ;
2018-01-14 18:01:22 +00:00
# endif
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2010-01-03 18:43:08 +00:00
2019-04-14 16:00:15 +00:00
void __stdcall Disk2InterfaceCard : : SetWriteMode ( WORD , WORD , BYTE , BYTE , ULONG uExecutedCycles )
2010-01-03 18:43:08 +00:00
{
2019-04-14 15:58:49 +00:00
m_formatTrack . DriveSwitchedToWriteMode ( m_floppyDrive [ m_currDrive ] . m_disk . m_byte ) ;
2018-01-14 18:01:22 +00:00
2019-04-14 15:47:41 +00:00
BOOL modechange = ! m_floppyDrive [ m_currDrive ] . m_writelight ;
2018-01-14 18:01:22 +00:00
# if LOG_DISK_RW_MODE
LOG_DISK ( " rw mode: write (mode changed=%d) \r \n " , modechange ? 1 : 0 ) ;
# endif
# if LOG_DISK_NIBBLES_WRITE
2019-04-07 14:54:26 +00:00
m_uWriteLastCycle = 0 ;
2018-01-14 18:01:22 +00:00
# endif
2019-04-14 15:47:41 +00:00
m_floppyDrive [ m_currDrive ] . m_writelight = WRITELIGHT_CYCLES ;
2018-01-14 18:01:22 +00:00
2010-01-03 18:43:08 +00:00
if ( modechange )
2021-01-10 16:33:06 +00:00
GetFrame ( ) . FrameDrawDiskLEDS ( ) ;
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2010-01-03 18:43:08 +00:00
2021-11-13 18:13:01 +00:00
void Disk2InterfaceCard : : Update ( const ULONG cycles )
2010-01-03 18:43:08 +00:00
{
int loop = NUM_DRIVES ;
while ( loop - - )
{
2019-04-14 15:47:41 +00:00
FloppyDrive * pDrive = & m_floppyDrive [ loop ] ;
2010-01-03 18:43:08 +00:00
2019-04-14 15:47:41 +00:00
if ( pDrive - > m_spinning & & ! m_floppyMotorOn )
2018-01-14 18:01:22 +00:00
{
2019-04-14 15:47:41 +00:00
if ( ! ( pDrive - > m_spinning - = MIN ( pDrive - > m_spinning , cycles ) ) )
2014-07-24 01:08:52 +00:00
{
2021-01-10 16:33:06 +00:00
GetFrame ( ) . FrameDrawDiskLEDS ( ) ;
GetFrame ( ) . FrameDrawDiskStatus ( ) ;
2014-07-24 01:08:52 +00:00
}
2010-01-03 18:43:08 +00:00
}
2019-10-05 08:53:02 +00:00
if ( m_seqFunc . writeMode & & ( m_currDrive = = loop ) & & pDrive - > m_spinning )
2010-01-03 18:43:08 +00:00
{
2019-04-14 15:47:41 +00:00
pDrive - > m_writelight = WRITELIGHT_CYCLES ;
2010-01-03 18:43:08 +00:00
}
2019-04-14 15:47:41 +00:00
else if ( pDrive - > m_writelight )
2010-01-03 18:43:08 +00:00
{
2019-04-14 15:47:41 +00:00
if ( ! ( pDrive - > m_writelight - = MIN ( pDrive - > m_writelight , cycles ) ) )
2014-07-24 01:08:52 +00:00
{
2021-01-10 16:33:06 +00:00
GetFrame ( ) . FrameDrawDiskLEDS ( ) ;
GetFrame ( ) . FrameDrawDiskStatus ( ) ;
2014-07-24 01:08:52 +00:00
}
2010-01-03 18:43:08 +00:00
}
}
2006-02-25 20:50:29 +00:00
}
//===========================================================================
2019-04-14 16:00:15 +00:00
bool Disk2InterfaceCard : : DriveSwap ( void )
2006-02-25 20:50:29 +00:00
{
// Refuse to swap if either Disk][ is active
2017-05-11 15:34:57 +00:00
// TODO: if Shift-Click then FORCE drive swap to bypass message
2019-04-14 15:47:41 +00:00
if ( m_floppyDrive [ DRIVE_1 ] . m_spinning | | m_floppyDrive [ DRIVE_2 ] . m_spinning )
2017-05-11 15:34:57 +00:00
{
// 1.26.2.4 Prompt when trying to swap disks while drive is on instead of silently failing
2021-01-19 20:37:43 +00:00
int status = GetFrame ( ) . FrameMessageBox (
2017-05-11 15:34:57 +00:00
" WARNING: \n "
" \n "
" \t Attempting to swap a disk while a drive is on \n "
" \t \t --> is NOT recommended <-- \n "
" \t as this will most likely read/write incorrect data! \n "
" \n "
" If the other drive is empty then swapping is harmless. The "
2017-05-22 21:04:34 +00:00
" computer will appear to 'hang' trying to read non-existent data but "
2017-05-11 15:34:57 +00:00
" you can safely swap disks once more to restore the original disk. \n "
" \n "
" Do you still wish to swap disks? " ,
" Trying to swap a disk while a drive is on ... " ,
MB_ICONWARNING | MB_YESNOCANCEL
) ;
switch ( status )
{
case IDNO :
case IDCANCEL :
return false ;
default :
break ; // User is OK with swapping disks so let them proceed at their own risk
}
}
2006-02-25 20:50:29 +00:00
2019-04-08 09:41:47 +00:00
FlushCurrentTrack ( DRIVE_1 ) ;
FlushCurrentTrack ( DRIVE_2 ) ;
2018-04-02 17:21:18 +00:00
2006-02-25 20:50:29 +00:00
// Swap disks between drives
2013-12-06 21:10:41 +00:00
// . NB. We swap trackimage ptrs (so don't need to swap the buffers' data)
2019-04-14 15:47:41 +00:00
std : : swap ( m_floppyDrive [ DRIVE_1 ] . m_disk , m_floppyDrive [ DRIVE_2 ] . m_disk ) ;
2018-04-02 17:21:18 +00:00
// Invalidate the trackimage so that a read latch will re-read the track for the new floppy (GH#543)
2019-04-14 15:58:49 +00:00
m_floppyDrive [ DRIVE_1 ] . m_disk . m_trackimagedata = false ;
m_floppyDrive [ DRIVE_2 ] . m_disk . m_trackimagedata = false ;
2006-02-25 20:50:29 +00:00
2019-04-08 09:41:47 +00:00
SaveLastDiskImage ( DRIVE_1 ) ;
SaveLastDiskImage ( DRIVE_2 ) ;
2006-02-25 20:50:29 +00:00
2021-01-10 16:33:06 +00:00
GetFrame ( ) . FrameRefreshStatus ( DRAW_LEDS | DRAW_BUTTON_DRIVES ) ;
2006-02-25 20:50:29 +00:00
return true ;
}
//===========================================================================
2021-01-19 20:37:43 +00:00
bool Disk2InterfaceCard : : GetFirmware ( WORD lpNameId , BYTE * pDst )
2007-05-28 11:16:42 +00:00
{
2021-01-19 20:37:43 +00:00
BYTE * pData = GetFrame ( ) . GetResource ( lpNameId , " FIRMWARE " , DISK2_FW_SIZE ) ;
2020-02-22 11:38:25 +00:00
if ( ! pData )
return false ;
memcpy ( pDst , pData , DISK2_FW_SIZE ) ;
return true ;
}
void Disk2InterfaceCard : : InitFirmware ( LPBYTE pCxRomPeripheral )
{
if ( pCxRomPeripheral = = NULL )
2007-05-28 11:16:42 +00:00
return ;
2020-02-22 11:38:25 +00:00
ImageInfo * pImage = m_floppyDrive [ DRIVE_1 ] . m_disk . m_imagehandle ;
2022-10-05 20:29:57 +00:00
if ( m_force13SectorFirmware )
m_is13SectorFirmware = true ;
else
m_is13SectorFirmware = ImageIsBootSectorFormatSector13 ( pImage ) ;
2020-02-22 11:38:25 +00:00
if ( m_is13SectorFirmware )
memcpy ( pCxRomPeripheral + m_slot * APPLE_SLOT_SIZE , m_13SectorFirmware , DISK2_FW_SIZE ) ;
else
memcpy ( pCxRomPeripheral + m_slot * APPLE_SLOT_SIZE , m_16SectorFirmware , DISK2_FW_SIZE ) ;
}
2021-11-01 20:39:26 +00:00
void Disk2InterfaceCard : : InitializeIO ( LPBYTE pCxRomPeripheral )
2020-02-22 11:38:25 +00:00
{
2021-01-19 20:37:43 +00:00
bool res = GetFirmware ( IDR_DISK2_13SECTOR_FW , m_13SectorFirmware ) ;
2020-02-22 11:38:25 +00:00
_ASSERT ( res ) ;
2021-01-19 20:37:43 +00:00
res = GetFirmware ( IDR_DISK2_16SECTOR_FW , m_16SectorFirmware ) ;
2020-02-22 11:38:25 +00:00
_ASSERT ( res ) ;
2007-05-28 11:16:42 +00:00
2015-09-13 10:39:58 +00:00
// Note: We used to disable the track stepping delay in the Disk II controller firmware by
2009-02-01 10:33:37 +00:00
// patching $C64C with $A9,$00,$EA. Now not doing this since:
// . Authentic Speed should be authentic
// . Enhanced Speed runs emulation unthrottled, so removing the delay has negligible effect
2009-02-03 02:24:50 +00:00
// . Patching the firmware breaks the ADC checksum used by "The CIA Files" (Tricky Dick)
2015-09-13 10:39:58 +00:00
// . In this case we can patch to compensate for an ADC or EOR checksum but not both (nickw)
2007-05-28 11:16:42 +00:00
2021-11-01 20:12:42 +00:00
RegisterIoHandler ( m_slot , & Disk2InterfaceCard : : IORead , & Disk2InterfaceCard : : IOWrite , NULL , NULL , this , NULL ) ;
2020-02-22 11:38:25 +00:00
InitFirmware ( pCxRomPeripheral ) ;
2007-05-28 11:16:42 +00:00
}
//===========================================================================
2022-04-23 17:13:15 +00:00
void Disk2InterfaceCard : : SetSequencerFunction ( WORD addr , ULONG executedCycles )
2019-09-30 20:29:58 +00:00
{
if ( ( addr & 0xf ) < 0xc )
return ;
2022-04-23 17:13:15 +00:00
const SEQFUNC oldSeqFunc = m_seqFunc . function ;
2019-10-13 09:25:33 +00:00
switch ( ( addr & 3 ) ^ 2 )
2019-09-30 20:29:58 +00:00
{
2022-04-23 17:13:15 +00:00
case 0 : m_seqFunc . writeMode = 0 ; break ; // $C08E,X (sequence addr A3 input)
case 1 : m_seqFunc . writeMode = 1 ; break ; // $C08F,X (sequence addr A3 input)
case 2 : m_seqFunc . loadMode = 0 ; break ; // $C08C,X (sequence addr A2 input)
case 3 : m_seqFunc . loadMode = 1 ; break ; // $C08D,X (sequence addr A2 input)
2019-09-30 20:29:58 +00:00
}
2020-02-09 21:23:15 +00:00
if ( ! m_seqFunc . writeMode )
m_writeStarted = false ;
2022-04-23 17:13:15 +00:00
if ( oldSeqFunc = = checkWriteProtAndInitWrite & & m_seqFunc . function ! = checkWriteProtAndInitWrite )
{
// Use up remaining cycles before switching out of "checkWriteProtAndInitWrite" mode
// Done when checking write-protect, but also for bit-slip (eg. E7) copy-protections
FloppyDisk & floppy = m_floppyDrive [ m_currDrive ] . m_disk ;
if ( ImageIsWOZ ( floppy . m_imagehandle ) )
{
const UINT bitCellDelta = GetBitCellDelta ( executedCycles ) ;
UpdateBitStreamPosition ( floppy , bitCellDelta ) ;
}
}
2019-09-30 20:29:58 +00:00
}
2019-04-14 16:00:15 +00:00
BYTE __stdcall Disk2InterfaceCard : : IORead ( WORD pc , WORD addr , BYTE bWrite , BYTE d , ULONG nExecutedCycles )
2007-05-28 11:16:42 +00:00
{
2020-11-14 14:54:39 +00:00
CpuCalcCycles ( nExecutedCycles ) ; // g_nCumulativeCycles needed by most Disk I/O functions
2019-04-07 13:22:05 +00:00
UINT uSlot = ( ( addr & 0xff ) > > 4 ) - 8 ;
2019-04-14 16:00:15 +00:00
Disk2InterfaceCard * pCard = ( Disk2InterfaceCard * ) MemGetSlotParameters ( uSlot ) ;
2019-04-07 13:22:05 +00:00
2019-07-05 22:01:19 +00:00
ImageInfo * pImage = pCard - > m_floppyDrive [ pCard - > m_currDrive ] . m_disk . m_imagehandle ;
bool isWOZ = ImageIsWOZ ( pImage ) ;
2020-02-09 21:23:15 +00:00
if ( isWOZ & & pCard - > m_seqFunc . function = = dataShiftWrite ) // Occurs at end of sector write ($C0EE)
pCard - > DataShiftWriteWOZ ( pc , addr , nExecutedCycles ) ; // Finish any previous write
2022-04-23 17:13:15 +00:00
pCard - > SetSequencerFunction ( addr , nExecutedCycles ) ;
2019-09-30 20:29:58 +00:00
2015-09-13 10:39:58 +00:00
switch ( addr & 0xF )
2007-05-28 11:16:42 +00:00
{
2019-04-08 09:41:47 +00:00
case 0x0 : pCard - > ControlStepper ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
case 0x1 : pCard - > ControlStepper ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
case 0x2 : pCard - > ControlStepper ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
case 0x3 : pCard - > ControlStepper ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
case 0x4 : pCard - > ControlStepper ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
case 0x5 : pCard - > ControlStepper ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
case 0x6 : pCard - > ControlStepper ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
case 0x7 : pCard - > ControlStepper ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
case 0x8 : pCard - > ControlMotor ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
case 0x9 : pCard - > ControlMotor ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
2022-01-29 21:27:03 +00:00
case 0xA : isWOZ = pCard - > Enable ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
case 0xB : isWOZ = pCard - > Enable ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
2019-07-30 21:41:28 +00:00
case 0xC : if ( ! isWOZ ) pCard - > ReadWrite ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
2019-04-08 09:41:47 +00:00
case 0xD : pCard - > LoadWriteProtect ( 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 ;
2007-05-28 11:16:42 +00:00
}
2017-05-14 10:33:07 +00:00
// only even addresses return the latch (UTAIIe Table 9.1)
2015-09-13 10:39:58 +00:00
if ( ! ( addr & 1 ) )
2019-07-30 21:41:28 +00:00
{
2020-02-09 21:23:15 +00:00
if ( isWOZ & & pCard - > m_seqFunc . function ! = dataShiftWrite )
pCard - > DataLatchReadWriteWOZ ( pc , addr , bWrite , nExecutedCycles ) ;
2019-07-30 21:41:28 +00:00
2019-04-07 14:54:26 +00:00
return pCard - > m_floppyLatch ;
2019-07-30 21:41:28 +00:00
}
return MemReadFloatingBus ( nExecutedCycles ) ;
2007-05-28 11:16:42 +00:00
}
2019-04-14 16:00:15 +00:00
BYTE __stdcall Disk2InterfaceCard : : IOWrite ( WORD pc , WORD addr , BYTE bWrite , BYTE d , ULONG nExecutedCycles )
2007-05-28 11:16:42 +00:00
{
2020-11-14 14:54:39 +00:00
CpuCalcCycles ( nExecutedCycles ) ; // g_nCumulativeCycles needed by most Disk I/O functions
2019-04-07 13:22:05 +00:00
UINT uSlot = ( ( addr & 0xff ) > > 4 ) - 8 ;
2019-04-14 16:00:15 +00:00
Disk2InterfaceCard * pCard = ( Disk2InterfaceCard * ) MemGetSlotParameters ( uSlot ) ;
2019-04-07 13:22:05 +00:00
2019-07-05 22:01:19 +00:00
ImageInfo * pImage = pCard - > m_floppyDrive [ pCard - > m_currDrive ] . m_disk . m_imagehandle ;
bool isWOZ = ImageIsWOZ ( pImage ) ;
2020-02-09 21:23:15 +00:00
if ( isWOZ & & pCard - > m_seqFunc . function = = dataShiftWrite )
pCard - > DataShiftWriteWOZ ( pc , addr , nExecutedCycles ) ; // Finish any previous write
2022-04-23 17:13:15 +00:00
pCard - > SetSequencerFunction ( addr , nExecutedCycles ) ;
2019-09-30 20:29:58 +00:00
2015-09-13 10:39:58 +00:00
switch ( addr & 0xF )
2007-05-28 11:16:42 +00:00
{
2019-04-08 09:41:47 +00:00
case 0x0 : pCard - > ControlStepper ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
case 0x1 : pCard - > ControlStepper ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
case 0x2 : pCard - > ControlStepper ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
case 0x3 : pCard - > ControlStepper ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
case 0x4 : pCard - > ControlStepper ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
case 0x5 : pCard - > ControlStepper ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
case 0x6 : pCard - > ControlStepper ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
case 0x7 : pCard - > ControlStepper ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
case 0x8 : pCard - > ControlMotor ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
case 0x9 : pCard - > ControlMotor ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
2022-01-29 21:27:03 +00:00
case 0xA : isWOZ = pCard - > Enable ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
case 0xB : isWOZ = pCard - > Enable ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
2019-07-30 21:41:28 +00:00
case 0xC : if ( ! isWOZ ) pCard - > ReadWrite ( pc , addr , bWrite , d , nExecutedCycles ) ; break ;
2019-04-08 09:41:47 +00:00
case 0xD : pCard - > LoadWriteProtect ( 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 ;
2007-05-28 11:16:42 +00:00
}
2015-09-13 10:39:58 +00:00
// any address writes the latch via sequencer LD command (74LS323 datasheet)
2019-10-05 08:53:02 +00:00
if ( pCard - > m_seqFunc . function = = dataLoadWrite )
2015-09-13 10:39:58 +00:00
{
2019-04-07 14:54:26 +00:00
pCard - > m_floppyLatch = d ;
2019-07-30 21:41:28 +00:00
if ( isWOZ )
2020-02-09 21:23:15 +00:00
pCard - > DataLatchReadWriteWOZ ( pc , addr , bWrite , nExecutedCycles ) ;
2015-09-13 10:39:58 +00:00
}
2019-07-30 21:41:28 +00:00
2007-05-28 11:16:42 +00:00
return 0 ;
}
//===========================================================================
2019-02-02 15:51:27 +00:00
// Unit version history:
2018-01-14 18:01:22 +00:00
// 2: Added: Format Track state & DiskLastCycle
2018-10-02 21:08:54 +00:00
// 3: Added: DiskLastReadLatchCycle
2019-07-05 22:01:19 +00:00
// 4: Added: WOZ state
// Split up 'Unit' putting some state into a new 'Floppy'
2019-10-05 08:53:02 +00:00
// 5: Added: Sequencer Function
2020-12-12 20:05:29 +00:00
// 6: Added: Drive Connected & Motor On Cycle
2022-04-23 17:13:15 +00:00
// 7: Deprecated SS_YAML_KEY_LSS_RESET_SEQUENCER, SS_YAML_KEY_DISK_ACCESSED
2022-06-20 19:40:39 +00:00
// 8: Added: deferred stepper: event, address & cycle
static const UINT kUNIT_VERSION = 8 ;
2018-01-14 18:01:22 +00:00
2015-12-05 16:50:27 +00:00
# define SS_YAML_VALUE_CARD_DISK2 "Disk]["
# define SS_YAML_KEY_PHASES "Phases"
# define SS_YAML_KEY_CURRENT_DRIVE "Current Drive"
2022-04-23 17:13:15 +00:00
# define SS_YAML_KEY_DISK_ACCESSED "Disk Accessed" // deprecated at v7
2015-12-05 16:50:27 +00:00
# define SS_YAML_KEY_ENHANCE_DISK "Enhance Disk"
# define SS_YAML_KEY_FLOPPY_LATCH "Floppy Latch"
# define SS_YAML_KEY_FLOPPY_MOTOR_ON "Floppy Motor On"
2019-10-05 08:53:02 +00:00
# define SS_YAML_KEY_FLOPPY_WRITE_MODE "Floppy Write Mode" // deprecated at v5
2018-01-14 18:01:22 +00:00
# define SS_YAML_KEY_LAST_CYCLE "Last Cycle"
2018-10-02 21:08:54 +00:00
# define SS_YAML_KEY_LAST_READ_LATCH_CYCLE "Last Read Latch Cycle"
2019-07-05 22:01:19 +00:00
# define SS_YAML_KEY_LSS_SHIFT_REG "LSS Shift Reg"
# define SS_YAML_KEY_LSS_LATCH_DELAY "LSS Latch Delay"
2022-04-23 17:13:15 +00:00
# define SS_YAML_KEY_LSS_RESET_SEQUENCER "LSS Reset Sequencer" // deprecated at v7
2019-10-05 08:53:02 +00:00
# define SS_YAML_KEY_LSS_SEQUENCER_FUNCTION "LSS Sequencer Function"
2022-06-20 19:40:39 +00:00
# define SS_YAML_KEY_DEFERRED_STEPPER_EVENT "Deferred Stepper Event"
# define SS_YAML_KEY_DEFERRED_STEPPER_ADDRESS "Deferred Stepper Address"
# define SS_YAML_KEY_DEFERRED_STEPPER_CYCLE "Deferred Stepper Cycle"
2015-12-05 16:50:27 +00:00
2016-02-15 22:33:38 +00:00
# define SS_YAML_KEY_DISK2UNIT "Unit"
2020-12-12 20:05:29 +00:00
# define SS_YAML_KEY_DRIVE_CONNECTED "Drive Connected"
2015-12-05 16:50:27 +00:00
# define SS_YAML_KEY_PHASE "Phase"
2019-07-05 22:01:19 +00:00
# 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"
2020-12-12 20:05:29 +00:00
# define SS_YAML_KEY_MOTOR_ON_CYCLE "Motor On Cycle"
2019-07-05 22:01:19 +00:00
# define SS_YAML_KEY_FLOPPY "Floppy"
2020-12-12 20:05:29 +00:00
# define SS_YAML_KEY_FILENAME "Filename"
2015-12-05 16:50:27 +00:00
# define SS_YAML_KEY_BYTE "Byte"
2019-07-05 22:01:19 +00:00
# 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"
2015-12-05 16:50:27 +00:00
# define SS_YAML_KEY_WRITE_PROTECTED "Write Protected"
# define SS_YAML_KEY_SPINNING "Spinning"
# define SS_YAML_KEY_WRITE_LIGHT "Write Light"
# 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 "Track Image"
2022-02-28 20:52:18 +00:00
const std : : string & Disk2InterfaceCard : : GetSnapshotCardName ( void )
2015-12-05 16:50:27 +00:00
{
static const std : : string name ( SS_YAML_VALUE_CARD_DISK2 ) ;
return name ;
}
2019-07-05 22:01:19 +00:00
void Disk2InterfaceCard : : SaveSnapshotFloppy ( YamlSaveHelper & yamlSaveHelper , UINT unit )
2015-12-05 16:50:27 +00:00
{
2019-07-05 22:01:19 +00:00
YamlSaveHelper : : Label label ( yamlSaveHelper , " %s: \n " , SS_YAML_KEY_FLOPPY ) ;
2019-04-14 15:58:49 +00:00
yamlSaveHelper . SaveString ( SS_YAML_KEY_FILENAME , m_floppyDrive [ unit ] . m_disk . m_fullname ) ;
yamlSaveHelper . SaveHexUint16 ( SS_YAML_KEY_BYTE , m_floppyDrive [ unit ] . m_disk . m_byte ) ;
yamlSaveHelper . SaveHexUint16 ( SS_YAML_KEY_NIBBLES , m_floppyDrive [ unit ] . m_disk . m_nibbles ) ;
2019-07-05 22:01:19 +00:00
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 ) ;
2019-04-14 15:58:49 +00:00
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 ) ;
2019-04-14 15:47:41 +00:00
2019-04-14 15:58:49 +00:00
if ( m_floppyDrive [ unit ] . m_disk . m_trackimage )
2015-12-05 16:50:27 +00:00
{
YamlSaveHelper : : Label image ( yamlSaveHelper , " %s: \n " , SS_YAML_KEY_TRACK_IMAGE ) ;
2019-12-31 12:07:45 +00:00
yamlSaveHelper . SaveMemory ( m_floppyDrive [ unit ] . m_disk . m_trackimage , ImageGetMaxNibblesPerTrack ( m_floppyDrive [ unit ] . m_disk . m_imagehandle ) ) ;
2015-12-05 16:50:27 +00:00
}
}
2019-07-05 22:01:19 +00:00
void Disk2InterfaceCard : : SaveSnapshotDriveUnit ( YamlSaveHelper & yamlSaveHelper , UINT unit )
{
YamlSaveHelper : : Label label ( yamlSaveHelper , " %s%d: \n " , SS_YAML_KEY_DISK2UNIT , unit ) ;
2020-12-12 20:05:29 +00:00
yamlSaveHelper . SaveBool ( SS_YAML_KEY_DRIVE_CONNECTED , m_floppyDrive [ unit ] . m_isConnected ) ;
2019-07-05 22:01:19 +00:00
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
2020-12-12 20:05:29 +00:00
yamlSaveHelper . SaveHexUint64 ( SS_YAML_KEY_MOTOR_ON_CYCLE , m_floppyDrive [ unit ] . m_motorOnCycle ) ; // v6
2019-07-05 22:01:19 +00:00
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 ) ;
}
2021-11-25 20:23:21 +00:00
void Disk2InterfaceCard : : SaveSnapshot ( YamlSaveHelper & yamlSaveHelper )
2015-12-05 16:50:27 +00:00
{
2019-04-08 09:41:47 +00:00
YamlSaveHelper : : Slot slot ( yamlSaveHelper , GetSnapshotCardName ( ) , m_slot , kUNIT_VERSION ) ;
2015-12-05 16:50:27 +00:00
YamlSaveHelper : : Label state ( yamlSaveHelper , " %s: \n " , SS_YAML_KEY_STATE ) ;
2019-04-07 14:54:26 +00:00
yamlSaveHelper . SaveUint ( SS_YAML_KEY_CURRENT_DRIVE , m_currDrive ) ;
2019-07-05 22:01:19 +00:00
yamlSaveHelper . SaveHexUint4 ( SS_YAML_KEY_PHASES , m_magnetStates ) ;
2019-04-07 14:54:26 +00:00
yamlSaveHelper . SaveBool ( SS_YAML_KEY_ENHANCE_DISK , m_enhanceDisk ) ;
yamlSaveHelper . SaveHexUint8 ( SS_YAML_KEY_FLOPPY_LATCH , m_floppyLatch ) ;
yamlSaveHelper . SaveBool ( SS_YAML_KEY_FLOPPY_MOTOR_ON , m_floppyMotorOn = = TRUE ) ;
yamlSaveHelper . SaveHexUint64 ( SS_YAML_KEY_LAST_CYCLE , m_diskLastCycle ) ; // v2
yamlSaveHelper . SaveHexUint64 ( SS_YAML_KEY_LAST_READ_LATCH_CYCLE , m_diskLastReadLatchCycle ) ; // v3
2019-07-05 22:01:19 +00:00
yamlSaveHelper . SaveHexUint8 ( SS_YAML_KEY_LSS_SHIFT_REG , m_shiftReg ) ; // v4
yamlSaveHelper . SaveInt ( SS_YAML_KEY_LSS_LATCH_DELAY , m_latchDelay ) ; // v4
2019-10-05 08:53:02 +00:00
yamlSaveHelper . SaveInt ( SS_YAML_KEY_LSS_SEQUENCER_FUNCTION , m_seqFunc . function ) ; // v5
2022-06-20 19:40:39 +00:00
yamlSaveHelper . SaveBool ( SS_YAML_KEY_DEFERRED_STEPPER_EVENT , m_deferredStepperEvent ) ; // v8
yamlSaveHelper . SaveHexUint16 ( SS_YAML_KEY_DEFERRED_STEPPER_ADDRESS , m_deferredStepperAddress ) ; // v8
yamlSaveHelper . SaveHexUint64 ( SS_YAML_KEY_DEFERRED_STEPPER_CYCLE , m_deferredStepperCumulativeCycles ) ; // v8
2019-04-07 14:54:26 +00:00
m_formatTrack . SaveSnapshot ( yamlSaveHelper ) ; // v2
2015-12-05 16:50:27 +00:00
2019-07-05 22:01:19 +00:00
SaveSnapshotDriveUnit ( yamlSaveHelper , DRIVE_1 ) ;
SaveSnapshotDriveUnit ( yamlSaveHelper , DRIVE_2 ) ;
2015-12-05 16:50:27 +00:00
}
2019-07-05 22:01:19 +00:00
bool Disk2InterfaceCard : : LoadSnapshotFloppy ( YamlLoadHelper & yamlLoadHelper , UINT unit , UINT version , std : : vector < BYTE > & track )
2015-12-05 16:50:27 +00:00
{
2016-02-24 22:38:59 +00:00
std : : string filename = yamlLoadHelper . LoadString ( SS_YAML_KEY_FILENAME ) ;
2019-07-05 22:01:19 +00:00
bool bImageError = filename . empty ( ) ;
if ( ! bImageError )
2015-12-05 16:50:27 +00:00
{
DWORD dwAttributes = GetFileAttributes ( filename . c_str ( ) ) ;
2019-07-05 22:01:19 +00:00
if ( dwAttributes = = INVALID_FILE_ATTRIBUTES )
2015-12-05 16:50:27 +00:00
{
// Get user to browse for file
2019-04-08 09:41:47 +00:00
UserSelectNewDiskImage ( unit , filename . c_str ( ) ) ;
2015-12-05 16:50:27 +00:00
dwAttributes = GetFileAttributes ( filename . c_str ( ) ) ;
}
bImageError = ( dwAttributes = = INVALID_FILE_ATTRIBUTES ) ;
if ( ! bImageError )
{
2021-07-25 10:55:25 +00:00
if ( InsertDisk ( unit , filename , dwAttributes & FILE_ATTRIBUTE_READONLY , IMAGE_DONT_CREATE ) ! = eIMAGE_ERROR_NONE )
2015-12-05 16:50:27 +00:00
bImageError = true ;
2019-12-31 12:07:45 +00:00
// InsertDisk() zeros m_floppyDrive[unit], then sets up:
2019-07-05 22:01:19 +00:00
// . m_imagename
// . m_fullname
// . m_bWriteProtected
2015-12-05 16:50:27 +00:00
}
}
2016-03-01 22:31:17 +00:00
yamlLoadHelper . LoadBool ( SS_YAML_KEY_WRITE_PROTECTED ) ; // Consume
2019-07-05 22:01:19 +00:00
m_floppyDrive [ unit ] . m_disk . m_byte = yamlLoadHelper . LoadUint ( SS_YAML_KEY_BYTE ) ;
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_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 ) )
2021-12-18 16:37:28 +00:00
throw std : : runtime_error ( " Disk image: bitOffset >= bitCount " ) ;
2019-07-05 22:01:19 +00:00
if ( ImageIsWOZ ( m_floppyDrive [ unit ] . m_disk . m_imagehandle ) )
UpdateBitStreamOffsets ( m_floppyDrive [ unit ] . m_disk ) ; // overwrites m_byte, inits m_bitMask
}
2015-12-05 16:50:27 +00:00
2016-02-14 16:01:30 +00:00
if ( yamlLoadHelper . GetSubMap ( SS_YAML_KEY_TRACK_IMAGE ) )
{
2019-12-31 12:52:10 +00:00
yamlLoadHelper . LoadMemory ( track , ImageGetMaxNibblesPerTrack ( m_floppyDrive [ unit ] . m_disk . m_imagehandle ) ) ;
2016-02-14 16:01:30 +00:00
yamlLoadHelper . PopMap ( ) ;
}
2015-12-05 16:50:27 +00:00
2019-07-05 22:01:19 +00:00
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 ) )
2021-12-18 16:37:28 +00:00
throw std : : runtime_error ( " Card: Expected key: " + disk2UnitName ) ;
2019-07-05 22:01:19 +00:00
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 ) )
2021-12-18 16:37:28 +00:00
throw std : : runtime_error ( " Card: Expected key: " + disk2UnitName ) ;
2019-07-05 22:01:19 +00:00
if ( ! yamlLoadHelper . GetSubMap ( SS_YAML_KEY_FLOPPY ) )
2021-12-18 16:37:28 +00:00
throw std : : runtime_error ( " Card: Expected key: " SS_YAML_KEY_FLOPPY ) ;
2019-07-05 22:01:19 +00:00
bool bImageError = LoadSnapshotFloppy ( yamlLoadHelper , unit , version , track ) ;
2015-12-05 16:50:27 +00:00
yamlLoadHelper . PopMap ( ) ;
//
2019-07-05 22:01:19 +00:00
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 ) ;
2020-12-12 20:05:29 +00:00
if ( version > = 6 )
{
m_floppyDrive [ unit ] . m_isConnected = yamlLoadHelper . LoadBool ( SS_YAML_KEY_DRIVE_CONNECTED ) ;
m_floppyDrive [ unit ] . m_motorOnCycle = yamlLoadHelper . LoadUint64 ( SS_YAML_KEY_MOTOR_ON_CYCLE ) ;
}
2019-07-05 22:01:19 +00:00
yamlLoadHelper . PopMap ( ) ;
return bImageError ;
}
void Disk2InterfaceCard : : LoadSnapshotDriveUnit ( YamlLoadHelper & yamlLoadHelper , UINT unit , UINT version )
{
bool bImageError = false ;
2019-12-31 12:07:45 +00:00
std : : vector < BYTE > track ( NIBBLES_PER_TRACK ) ; // Default size - may expand vector after loading disk image (eg. WOZ Info.largestTrack)
2019-07-05 22:01:19 +00:00
if ( version < = 3 )
bImageError = LoadSnapshotDriveUnitv3 ( yamlLoadHelper , unit , version , track ) ;
else
bImageError = LoadSnapshotDriveUnitv4 ( yamlLoadHelper , unit , version , track ) ;
if ( ! bImageError )
2015-12-05 16:50:27 +00:00
{
2019-04-14 15:58:49 +00:00
if ( ( m_floppyDrive [ unit ] . m_disk . m_trackimage = = NULL ) & & m_floppyDrive [ unit ] . m_disk . m_nibbles )
2019-12-31 12:07:45 +00:00
AllocTrack ( unit , track . size ( ) ) ;
2015-12-05 16:50:27 +00:00
2019-04-14 15:58:49 +00:00
if ( m_floppyDrive [ unit ] . m_disk . m_trackimage = = NULL )
2015-12-05 16:50:27 +00:00
bImageError = true ;
else
2019-12-31 12:07:45 +00:00
memcpy ( m_floppyDrive [ unit ] . m_disk . m_trackimage , & track [ 0 ] , track . size ( ) ) ;
2015-12-05 16:50:27 +00:00
}
if ( bImageError )
{
2019-07-05 22:01:19 +00:00
m_floppyDrive [ unit ] . m_disk . m_trackimagedata = false ;
m_floppyDrive [ unit ] . m_disk . m_trackimagedirty = false ;
m_floppyDrive [ unit ] . m_disk . m_nibbles = 0 ;
2015-12-05 16:50:27 +00:00
}
}
2021-11-25 20:23:21 +00:00
bool Disk2InterfaceCard : : LoadSnapshot ( YamlLoadHelper & yamlLoadHelper , UINT version )
2015-12-05 16:50:27 +00:00
{
2018-01-14 18:01:22 +00:00
if ( version < 1 | | version > kUNIT_VERSION )
2022-01-30 21:25:40 +00:00
ThrowErrorInvalidVersion ( version ) ;
2015-12-05 16:50:27 +00:00
2019-07-05 22:01:19 +00:00
m_currDrive = yamlLoadHelper . LoadUint ( SS_YAML_KEY_CURRENT_DRIVE ) ;
m_magnetStates = yamlLoadHelper . LoadUint ( SS_YAML_KEY_PHASES ) ;
2019-04-07 14:54:26 +00:00
m_enhanceDisk = yamlLoadHelper . LoadBool ( SS_YAML_KEY_ENHANCE_DISK ) ;
m_floppyLatch = yamlLoadHelper . LoadUint ( SS_YAML_KEY_FLOPPY_LATCH ) ;
2019-04-08 09:41:47 +00:00
m_floppyMotorOn = yamlLoadHelper . LoadBool ( SS_YAML_KEY_FLOPPY_MOTOR_ON ) ;
2015-12-05 16:50:27 +00:00
2018-01-14 18:01:22 +00:00
if ( version > = 2 )
{
2019-04-07 14:54:26 +00:00
m_diskLastCycle = yamlLoadHelper . LoadUint64 ( SS_YAML_KEY_LAST_CYCLE ) ;
m_formatTrack . LoadSnapshot ( yamlLoadHelper ) ;
2018-01-14 18:01:22 +00:00
}
2018-10-02 21:08:54 +00:00
if ( version > = 3 )
{
2019-04-07 14:54:26 +00:00
m_diskLastReadLatchCycle = yamlLoadHelper . LoadUint64 ( SS_YAML_KEY_LAST_READ_LATCH_CYCLE ) ;
2018-10-02 21:08:54 +00:00
}
2019-07-05 22:01:19 +00:00
if ( version > = 4 )
{
m_shiftReg = yamlLoadHelper . LoadUint ( SS_YAML_KEY_LSS_SHIFT_REG ) & 0xff ;
m_latchDelay = yamlLoadHelper . LoadInt ( SS_YAML_KEY_LSS_LATCH_DELAY ) ;
2022-04-23 17:13:15 +00:00
}
if ( version > = 4 & & version < = 6 )
{
( void ) yamlLoadHelper . LoadBool ( SS_YAML_KEY_LSS_RESET_SEQUENCER ) ; // deprecated
2019-07-05 22:01:19 +00:00
}
2019-10-05 08:53:02 +00:00
if ( version > = 5 )
{
m_seqFunc . function = ( SEQFUNC ) yamlLoadHelper . LoadInt ( SS_YAML_KEY_LSS_SEQUENCER_FUNCTION ) ;
}
2019-10-06 15:39:43 +00:00
else
{
m_seqFunc . writeMode = yamlLoadHelper . LoadBool ( SS_YAML_KEY_FLOPPY_WRITE_MODE ) ? 1 : 0 ;
m_seqFunc . loadMode = 0 ; // Wasn't saved until v5
}
2019-10-05 08:53:02 +00:00
2022-04-23 17:13:15 +00:00
if ( version < = 6 )
{
( void ) yamlLoadHelper . LoadBool ( SS_YAML_KEY_DISK_ACCESSED ) ; // deprecated - but retrieve the value to avoid the "State: Unknown key (Disk Accessed)" warning
}
2022-06-20 19:40:39 +00:00
if ( version > = 8 )
{
m_deferredStepperEvent = yamlLoadHelper . LoadBool ( SS_YAML_KEY_DEFERRED_STEPPER_EVENT ) ;
m_deferredStepperAddress = yamlLoadHelper . LoadUint ( SS_YAML_KEY_DEFERRED_STEPPER_ADDRESS ) ;
m_deferredStepperCumulativeCycles = yamlLoadHelper . LoadUint64 ( SS_YAML_KEY_DEFERRED_STEPPER_CYCLE ) ;
}
2015-12-05 16:50:27 +00:00
// Eject all disks first in case Drive-2 contains disk to be inserted into Drive-1
2019-04-08 09:41:47 +00:00
for ( UINT i = 0 ; i < NUM_DRIVES ; i + + )
2015-12-05 16:50:27 +00:00
{
2019-04-08 09:41:47 +00:00
EjectDisk ( i ) ; // Remove any disk & update Registry to reflect empty drive
2019-04-07 14:54:26 +00:00
m_floppyDrive [ i ] . clear ( ) ;
2015-12-05 16:50:27 +00:00
}
2019-07-05 22:01:19 +00:00
LoadSnapshotDriveUnit ( yamlLoadHelper , DRIVE_1 , version ) ;
LoadSnapshotDriveUnit ( yamlLoadHelper , DRIVE_2 , version ) ;
2015-12-05 16:50:27 +00:00
2021-01-10 16:33:06 +00:00
GetFrame ( ) . FrameRefreshStatus ( DRAW_LEDS | DRAW_BUTTON_DRIVES | DRAW_DISK_STATUS ) ;
2015-12-05 16:50:27 +00:00
2022-06-20 19:40:39 +00:00
if ( m_deferredStepperEvent )
InsertSyncEvent ( ) ;
2015-12-05 16:50:27 +00:00
return true ;
}