2168 lines
77 KiB
C
2168 lines
77 KiB
C
/*
|
|
Hatari - floppy_stx.c
|
|
|
|
This file is distributed under the GNU General Public License, version 2 or at
|
|
your option any later version. Read the file gpl.txt for details.
|
|
|
|
STX disk image support.
|
|
|
|
STX files are created using the program 'Pasti' made by Jorge Cwik (Ijor).
|
|
As no official documentation exists, this file is based on the reverse
|
|
engineering and docs made by the following people, mainly using Pasti 0.4b :
|
|
- Markus Fritze (Sarnau)
|
|
- P. Putnik
|
|
- Jean Louis Guerin (Dr CoolZic)
|
|
- Nicolas Pomarede
|
|
*/
|
|
const char floppy_stx_fileid[] = "Hatari floppy_stx.c : " __DATE__ " " __TIME__;
|
|
|
|
#include "main.h"
|
|
#include "file.h"
|
|
#include "floppy.h"
|
|
#include "floppy_stx.h"
|
|
#include "fdc.h"
|
|
#include "log.h"
|
|
#include "memorySnapShot.h"
|
|
#include "screen.h"
|
|
#include "video.h"
|
|
#include "cycles.h"
|
|
#include "str.h"
|
|
#include "utils.h"
|
|
|
|
|
|
#define STX_DEBUG_FLAG_STRUCTURE 1
|
|
#define STX_DEBUG_FLAG_DATA 2
|
|
|
|
#define STX_DEBUG_FLAG 0
|
|
// #define STX_DEBUG_FLAG ( STX_DEBUG_FLAG_STRUCTURE )
|
|
// #define STX_DEBUG_FLAG ( STX_DEBUG_FLAG_STRUCTURE | STX_DEBUG_FLAG_DATA )
|
|
|
|
|
|
#define FDC_DELAY_CYCLE_MFM_BIT ( 4 * 8 ) /* 4 us per bit, 8 MHz clock -> 32 cycles */
|
|
#define FDC_DELAY_CYCLE_MFM_BYTE ( 4 * 8 * 8 ) /* 4 us per bit, 8 bits per byte, 8 MHz clock -> 256 cycles */
|
|
|
|
#define FDC_TRACK_BYTES_STANDARD 6250
|
|
|
|
|
|
#define WD1772_SAVE_FILE_EXT ".wd1772"
|
|
#define WD1772_SAVE_FILE_ID "WD1772" /* 6 bytes */
|
|
#define WD1772_SAVE_VERSION 1
|
|
#define WD1772_SAVE_REVISION 0
|
|
#define WD1772_SAVE_SECTOR_ID "SECT" /* 4 bytes */
|
|
#define WD1772_SAVE_TRACK_ID "TRCK" /* 4 bytes */
|
|
|
|
|
|
typedef struct
|
|
{
|
|
STX_MAIN_STRUCT *ImageBuffer[ MAX_FLOPPYDRIVES ]; /* For the STX disk images */
|
|
|
|
Uint32 NextSectorStruct_Nbr; /* Sector Number in pSectorsStruct after a call to FDC_NextSectorID_FdcCycles_STX() */
|
|
Uint8 NextSector_ID_Field_TR; /* Track value in the next ID Field after a call to FDC_NextSectorID_FdcCycles_STX() */
|
|
Uint8 NextSector_ID_Field_SR; /* Sector value in the next ID Field after a call to FDC_NextSectorID_FdcCycles_STX() */
|
|
Uint8 NextSector_ID_Field_LEN; /* Sector's length in the next ID Field after a call to FDC_NextSectorID_FdcCycles_STX() */
|
|
Uint8 NextSector_ID_Field_CRC_OK; /* CRC OK or not in the next ID Field after a call to FDC_NextSectorID_FdcCycles_STX() */
|
|
|
|
} STX_STRUCT;
|
|
|
|
|
|
static STX_STRUCT STX_State; /* All variables related to the STX support */
|
|
|
|
static STX_SAVE_STRUCT STX_SaveStruct[ MAX_FLOPPYDRIVES ]; /* To save 'write sector' data */
|
|
|
|
|
|
|
|
/* Default timing table for Macrodos when revision=0 */
|
|
/* 1 unit of timing means 32 FDC cycles ; + 28 cycles every 16 bytes, so a standard block of 16 bytes */
|
|
/* should have a value of 0x7f or 0x80, which gives 4092-4124 cycles */
|
|
static Uint8 TimingDataDefault[] = {
|
|
0x00,0x7f,0x00,0x7f,0x00,0x7f,0x00,0x7f,0x00,0x7f,0x00,0x7f,0x00,0x7f,0x00,0x7f,
|
|
0x00,0x85,0x00,0x85,0x00,0x85,0x00,0x85,0x00,0x85,0x00,0x85,0x00,0x85,0x00,0x85,
|
|
0x00,0x79,0x00,0x79,0x00,0x79,0x00,0x79,0x00,0x79,0x00,0x79,0x00,0x79,0x00,0x79,
|
|
0x00,0x7f,0x00,0x7f,0x00,0x7f,0x00,0x7f,0x00,0x7f,0x00,0x7f,0x00,0x7f,0x00,0x7f
|
|
};
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* Local functions prototypes */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
static bool STX_LoadSaveFile ( int Drive , const char *FilenameSave );
|
|
static bool STX_LoadSaveFile_SECT ( int Drive, STX_SAVE_SECTOR_STRUCT *pStxSaveSector , Uint8 *p );
|
|
static bool STX_LoadSaveFile_TRCK ( int Drive , STX_SAVE_TRACK_STRUCT *pStxSaveTrack , Uint8 *p );
|
|
|
|
static bool STX_Insert_internal ( int Drive , const char *FilenameSTX , Uint8 *pImageBuffer , long ImageSize );
|
|
|
|
static Uint16 STX_ReadU16_LE ( Uint8 *p );
|
|
static Uint32 STX_ReadU32_LE ( Uint8 *p );
|
|
static Uint16 STX_ReadU16_BE ( Uint8 *p );
|
|
static Uint32 STX_ReadU32_BE ( Uint8 *p );
|
|
static void STX_WriteU16_BE ( Uint8 *p , Uint16 val );
|
|
static void STX_WriteU32_BE ( Uint8 *p , Uint32 val );
|
|
|
|
static void STX_FreeStruct ( STX_MAIN_STRUCT *pStxMain );
|
|
static void STX_FreeSaveStruct ( int Drive );
|
|
static void STX_FreeSaveSectorsStructAll ( STX_SAVE_SECTOR_STRUCT *pSaveSectorsStruct , Uint32 SaveSectorsCount );
|
|
static void STX_FreeSaveSectorsStruct ( STX_SAVE_SECTOR_STRUCT *pSaveSectorsStruct , int Nb );
|
|
static void STX_FreeSaveTracksStructAll ( STX_SAVE_TRACK_STRUCT *pSaveTracksStruct , Uint32 SaveTracksCount );
|
|
static void STX_FreeSaveTracksStruct ( STX_SAVE_TRACK_STRUCT *pSaveTracksStruct , int Nb );
|
|
|
|
static void STX_BuildSectorsSimple ( STX_TRACK_STRUCT *pStxTrack , Uint8 *p );
|
|
static Uint16 STX_BuildSectorID_CRC ( STX_SECTOR_STRUCT *pStxSector );
|
|
static STX_TRACK_STRUCT *STX_FindTrack ( Uint8 Drive , Uint8 Track , Uint8 Side );
|
|
static STX_SECTOR_STRUCT *STX_FindSector ( Uint8 Drive , Uint8 Track , Uint8 Side , Uint8 SectorStruct_Nb );
|
|
static STX_SECTOR_STRUCT *STX_FindSector_By_Position ( Uint8 Drive , Uint8 Track , Uint8 Side , Uint16 BitPosition );
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
|
|
*/
|
|
void STX_MemorySnapShot_Capture(bool bSave)
|
|
{
|
|
int Drive;
|
|
Uint32 i;
|
|
STX_SECTOR_STRUCT *pStxSector;
|
|
STX_TRACK_STRUCT *pStxTrack;
|
|
|
|
if ( bSave ) /* Saving snapshot */
|
|
{
|
|
MemorySnapShot_Store( &STX_State , sizeof (STX_State) );
|
|
|
|
/* Also save the 'write sector' and 'write track' buffers */
|
|
for ( Drive=0 ; Drive < MAX_FLOPPYDRIVES ; Drive++ )
|
|
{
|
|
/* Save the sectors' buffer */
|
|
MemorySnapShot_Store ( &STX_SaveStruct[ Drive ].SaveSectorsCount , sizeof ( STX_SaveStruct[ Drive ].SaveSectorsCount ) );
|
|
if ( STX_SaveStruct[ Drive ].SaveSectorsCount > 0 )
|
|
{
|
|
/* Save all sectors in the memory state */
|
|
/* For each sector, we save the structure, then the data */
|
|
for ( i=0 ; i<STX_SaveStruct[ Drive ].SaveSectorsCount ; i++ )
|
|
{
|
|
//Str_Dump_Hex_Ascii ( (char *) &STX_SaveStruct[ Drive ].pSaveSectorsStruct[ i ], sizeof( STX_SAVE_SECTOR_STRUCT ), 16, "" , stderr );
|
|
/* Save the structure */
|
|
MemorySnapShot_Store ( &STX_SaveStruct[ Drive ].pSaveSectorsStruct[ i ] , sizeof( STX_SAVE_SECTOR_STRUCT ) );
|
|
/* Save the sector's data */
|
|
MemorySnapShot_Store ( STX_SaveStruct[ Drive ].pSaveSectorsStruct[ i ].pData ,
|
|
STX_SaveStruct[ Drive ].pSaveSectorsStruct[ i ].SectorSize );
|
|
}
|
|
}
|
|
//fprintf ( stderr , "stx save write buffer drive=%d count=%d buf=%p\n" , Drive , STX_SaveStruct[ Drive ].SaveSectorsCount , STX_SaveStruct[ Drive ].pSaveSectorsStruct );
|
|
|
|
/* Save the tracks' buffer */
|
|
MemorySnapShot_Store ( &STX_SaveStruct[ Drive ].SaveTracksCount , sizeof ( STX_SaveStruct[ Drive ].SaveTracksCount ) );
|
|
if ( STX_SaveStruct[ Drive ].SaveTracksCount > 0 )
|
|
{
|
|
/* Save all tracks in the memory state */
|
|
/* For each track, we save the structure, then the data */
|
|
for ( i=0 ; i<STX_SaveStruct[ Drive ].SaveTracksCount ; i++ )
|
|
{
|
|
//Str_Dump_Hex_Ascii ( (char *) &STX_SaveStruct[ Drive ].pSaveTracksStruct[ i ], sizeof( STX_SAVE_TRACK_STRUCT ), 16, "" , stderr );
|
|
/* Save the structure */
|
|
MemorySnapShot_Store ( &STX_SaveStruct[ Drive ].pSaveTracksStruct[ i ] , sizeof( STX_SAVE_TRACK_STRUCT ) );
|
|
/* Save the track's data (as it was written, don't save the interpreted track) */
|
|
MemorySnapShot_Store ( STX_SaveStruct[ Drive ].pSaveTracksStruct[ i ].pDataWrite ,
|
|
STX_SaveStruct[ Drive ].pSaveTracksStruct[ i ].TrackSizeWrite );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
else /* Restoring snapshot */
|
|
{
|
|
MemorySnapShot_Store ( &STX_State , sizeof (STX_State) );
|
|
|
|
/* Call STX_Insert_internal to recompute STX_State */
|
|
/* (without loading an optional ".wd1772" file) */
|
|
for ( Drive=0 ; Drive < MAX_FLOPPYDRIVES ; Drive++ )
|
|
if ( EmulationDrives[Drive].ImageType == FLOPPY_IMAGE_TYPE_STX )
|
|
if ( STX_Insert_internal ( Drive , EmulationDrives[Drive].sFileName , EmulationDrives[Drive].pBuffer ,
|
|
EmulationDrives[Drive].nImageBytes ) == false )
|
|
{
|
|
Log_AlertDlg(LOG_ERROR, "Error restoring STX image %s in drive %d" ,
|
|
EmulationDrives[Drive].sFileName , Drive );
|
|
return;
|
|
}
|
|
|
|
/* Also restore the 'write sector' and 'write track' buffers */
|
|
for ( Drive=0 ; Drive < MAX_FLOPPYDRIVES ; Drive++ )
|
|
{
|
|
/* Restore the sectors' buffer */
|
|
MemorySnapShot_Store ( &STX_SaveStruct[ Drive ].SaveSectorsCount , sizeof ( STX_SaveStruct[ Drive ].SaveSectorsCount ) );
|
|
if ( STX_SaveStruct[ Drive ].SaveSectorsCount > 0 )
|
|
{
|
|
/* Alloc a buffer for all the sectors */
|
|
STX_SaveStruct[ Drive ].pSaveSectorsStruct = malloc ( STX_SaveStruct[ Drive ].SaveSectorsCount * sizeof ( STX_SAVE_SECTOR_STRUCT ) );
|
|
if ( !STX_SaveStruct[ Drive ].pSaveSectorsStruct )
|
|
{
|
|
Log_AlertDlg(LOG_ERROR, "Error restoring STX sectors save buffer malloc size=%d in drive %d" ,
|
|
STX_SaveStruct[ Drive ].SaveSectorsCount , Drive );
|
|
return;
|
|
}
|
|
|
|
/* Load all the sectors from the memory state */
|
|
/* For each sector, we load the structure, then the data */
|
|
for ( i=0 ; i<STX_SaveStruct[ Drive ].SaveSectorsCount ; i++ )
|
|
{
|
|
/* Load the structure */
|
|
MemorySnapShot_Store ( &STX_SaveStruct[ Drive ].pSaveSectorsStruct[ i ] , sizeof( STX_SAVE_SECTOR_STRUCT ) );
|
|
//Str_Dump_Hex_Ascii ( (char *) &STX_SaveStruct[ Drive ].pSaveSectorsStruct[ i ], sizeof( STX_SAVE_SECTOR_STRUCT ), 16, "" , stderr );
|
|
|
|
/* Load the sector's data */
|
|
STX_SaveStruct[ Drive ].pSaveSectorsStruct[ i ].pData = malloc ( STX_SaveStruct[ Drive ].pSaveSectorsStruct[ i ].SectorSize );
|
|
if ( !STX_SaveStruct[ Drive ].pSaveSectorsStruct[ i ].pData )
|
|
{
|
|
Log_AlertDlg(LOG_ERROR, "Error restoring STX save buffer for sector=%d in drive %d" ,
|
|
i , Drive );
|
|
return;
|
|
}
|
|
MemorySnapShot_Store ( STX_SaveStruct[ Drive ].pSaveSectorsStruct[ i ].pData ,
|
|
STX_SaveStruct[ Drive ].pSaveSectorsStruct[ i ].SectorSize );
|
|
|
|
/* Find the original sector to associate it with this saved sector */
|
|
pStxSector = STX_FindSector_By_Position ( Drive , STX_SaveStruct[ Drive ].pSaveSectorsStruct[ i ].Track ,
|
|
STX_SaveStruct[ Drive ].pSaveSectorsStruct[ i ].Side ,
|
|
STX_SaveStruct[ Drive ].pSaveSectorsStruct[ i ].BitPosition );
|
|
if ( !pStxSector )
|
|
{
|
|
Log_AlertDlg(LOG_ERROR, "Error restoring STX save buffer for sector=%d in drive %d" ,
|
|
i , Drive );
|
|
return;
|
|
}
|
|
pStxSector->SaveSectorIndex = i;
|
|
}
|
|
}
|
|
|
|
else
|
|
STX_SaveStruct[ Drive ].pSaveSectorsStruct = NULL;
|
|
//fprintf ( stderr , "stx load write buffer drive=%d count=%d buf=%p\n" , Drive , STX_SaveStruct[ Drive ].SaveSectorsCount , STX_SaveStruct[ Drive ].pSaveSectorsStruct );
|
|
|
|
/* Restore the tracks' buffer */
|
|
MemorySnapShot_Store ( &STX_SaveStruct[ Drive ].SaveTracksCount , sizeof ( STX_SaveStruct[ Drive ].SaveTracksCount ) );
|
|
if ( STX_SaveStruct[ Drive ].SaveTracksCount > 0 )
|
|
{
|
|
/* Alloc a buffer for all the tracks */
|
|
STX_SaveStruct[ Drive ].pSaveTracksStruct = malloc ( STX_SaveStruct[ Drive ].SaveTracksCount * sizeof ( STX_SAVE_TRACK_STRUCT ) );
|
|
if ( !STX_SaveStruct[ Drive ].pSaveTracksStruct )
|
|
{
|
|
Log_AlertDlg(LOG_ERROR, "Error restoring STX tracks save buffer malloc size=%d in drive %d" ,
|
|
STX_SaveStruct[ Drive ].SaveTracksCount , Drive );
|
|
return;
|
|
}
|
|
|
|
/* Load all the tracks from the memory state */
|
|
/* For each track, we load the structure, then the data */
|
|
for ( i=0 ; i<STX_SaveStruct[ Drive ].SaveTracksCount ; i++ )
|
|
{
|
|
/* Load the structure */
|
|
MemorySnapShot_Store ( &STX_SaveStruct[ Drive ].pSaveTracksStruct[ i ] , sizeof( STX_SAVE_TRACK_STRUCT ) );
|
|
//Str_Dump_Hex_Ascii ( (char *) &STX_SaveStruct[ Drive ].pSaveTracksStruct[ i ], sizeof( STX_SAVE_TRACK_STRUCT ), 16, "" , stderr );
|
|
|
|
/* Load the track's data (as it was written, don't load the interpreted track) */
|
|
STX_SaveStruct[ Drive ].pSaveTracksStruct[ i ].pDataWrite = malloc ( STX_SaveStruct[ Drive ].pSaveTracksStruct[ i ].TrackSizeWrite );
|
|
if ( !STX_SaveStruct[ Drive ].pSaveTracksStruct[ i ].pDataWrite )
|
|
{
|
|
Log_AlertDlg(LOG_ERROR, "Error restoring STX save buffer for track=%d in drive %d" ,
|
|
i , Drive );
|
|
return;
|
|
}
|
|
MemorySnapShot_Store ( STX_SaveStruct[ Drive ].pSaveTracksStruct[ i ].pDataWrite ,
|
|
STX_SaveStruct[ Drive ].pSaveTracksStruct[ i ].TrackSizeWrite );
|
|
|
|
STX_SaveStruct[ Drive ].pSaveTracksStruct[ i ].pDataRead = NULL; /* TODO : compute interpreted track */
|
|
STX_SaveStruct[ Drive ].pSaveTracksStruct[ i ].TrackSizeRead = 0; /* TODO : compute interpreted track */
|
|
|
|
/* Find the original track to associate it with this saved track */
|
|
pStxTrack = STX_FindTrack ( Drive , STX_SaveStruct[ Drive ].pSaveTracksStruct[ i ].Track ,
|
|
STX_SaveStruct[ Drive ].pSaveTracksStruct[ i ].Side );
|
|
if ( !pStxTrack )
|
|
{
|
|
Log_AlertDlg(LOG_ERROR, "Error restoring STX save buffer for track=%d in drive %d" ,
|
|
i , Drive );
|
|
return;
|
|
}
|
|
pStxTrack->SaveTrackIndex = i;
|
|
}
|
|
}
|
|
|
|
else
|
|
STX_SaveStruct[ Drive ].pSaveTracksStruct = NULL;
|
|
}
|
|
|
|
fprintf ( stderr , "stx load ok\n" );
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Does filename end with a .STX extension? If so, return true.
|
|
*/
|
|
bool STX_FileNameIsSTX(const char *pszFileName, bool bAllowGZ)
|
|
{
|
|
return(File_DoesFileExtensionMatch(pszFileName,".stx")
|
|
|| (bAllowGZ && File_DoesFileExtensionMatch(pszFileName,".stx.gz")));
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Create a filename to save modifications made to an STX file
|
|
* We replace the ".stx" or ".stx.gz" extension with ".wd1772"
|
|
* Return true if OK
|
|
*/
|
|
bool STX_FileNameToSave ( const char *FilenameSTX , char *FilenameSave )
|
|
{
|
|
if ( File_ChangeFileExtension ( FilenameSTX , ".stx.gz" , FilenameSave , WD1772_SAVE_FILE_EXT ) )
|
|
return true;
|
|
|
|
else if ( File_ChangeFileExtension ( FilenameSTX , ".stx" , FilenameSave , WD1772_SAVE_FILE_EXT ) )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Load .STX file into memory, set number of bytes loaded and return a pointer
|
|
* to the buffer.
|
|
*/
|
|
Uint8 *STX_ReadDisk(int Drive, const char *pszFileName, long *pImageSize, int *pImageType)
|
|
{
|
|
Uint8 *pSTXFile;
|
|
|
|
*pImageSize = 0;
|
|
|
|
/* Just load directly a buffer, and set ImageSize accordingly */
|
|
pSTXFile = File_Read(pszFileName, pImageSize, NULL);
|
|
if (!pSTXFile)
|
|
{
|
|
*pImageSize = 0;
|
|
return NULL;
|
|
}
|
|
|
|
/* Check the file's header is "RSY\0" */
|
|
if ( ( *pImageSize <= STX_HEADER_ID_LEN )
|
|
|| ( memcmp ( STX_HEADER_ID , pSTXFile , STX_HEADER_ID_LEN ) ) )
|
|
{
|
|
fprintf ( stderr , "Error : %s is not a valid STX image\n" , pszFileName );
|
|
free ( pSTXFile );
|
|
*pImageSize = 0;
|
|
return NULL;
|
|
}
|
|
|
|
*pImageType = FLOPPY_IMAGE_TYPE_STX;
|
|
return pSTXFile;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Save .STX file from memory buffer. Returns true if all OK.
|
|
* We create a file based on the initial filename by replacing the ".stx" extension
|
|
* with ".wd1172".
|
|
* We save all sectors, then all tracks.
|
|
* If there're no sector and no track to save, return true and don't create
|
|
* the save file
|
|
*/
|
|
bool STX_WriteDisk ( int Drive , const char *pszFileName , Uint8 *pBuffer , int ImageSize )
|
|
{
|
|
FILE *FileOut;
|
|
char FilenameSave[ FILENAME_MAX ];
|
|
Uint8 buf[ 100 ];
|
|
Uint8 *p;
|
|
Uint32 Sector;
|
|
Uint32 Track;
|
|
Uint32 BlockLen;
|
|
Uint32 SaveSectorsCount_real;
|
|
STX_SAVE_SECTOR_STRUCT *pStxSaveSector;
|
|
STX_SAVE_TRACK_STRUCT *pStxSaveTrack;
|
|
Uint32 i;
|
|
|
|
fprintf ( stderr , "stx write <%s>\n" , pszFileName );
|
|
|
|
|
|
/* We can only save if the filename ends with ".stx" (or ".stx.gz"), not if it's a ".zip" file */
|
|
if ( STX_FileNameIsSTX ( pszFileName , true ) == false )
|
|
{
|
|
Log_AlertDlg ( LOG_INFO , "WARNING : can't save changes made to this STX disk, bad file extension" );
|
|
return false;
|
|
}
|
|
|
|
/* Count the saved sectors that are really used */
|
|
SaveSectorsCount_real = 0;
|
|
i = 0;
|
|
while ( i < STX_SaveStruct[ Drive ].SaveSectorsCount )
|
|
if ( STX_SaveStruct[ Drive ].pSaveSectorsStruct[ i++ ].StructIsUsed != 0 )
|
|
SaveSectorsCount_real++;
|
|
|
|
/* Do we have data to save ? */
|
|
if ( ( SaveSectorsCount_real == 0 )
|
|
&& ( STX_SaveStruct[ Drive ].SaveTracksCount == 0 ) )
|
|
return true;
|
|
|
|
|
|
if ( STX_FileNameToSave ( pszFileName , FilenameSave ) == false )
|
|
{
|
|
fprintf ( stderr , "STX_WriteDisk drive=%d file=%s, error STX_FileNameToSave\n" , Drive , pszFileName );
|
|
return false;
|
|
}
|
|
fprintf ( stderr , "stx write <%s>\n" , FilenameSave );
|
|
|
|
|
|
FileOut = fopen ( FilenameSave , "wb+" );
|
|
if ( !FileOut )
|
|
{
|
|
fprintf ( stderr , "STX_WriteDisk drive=%d file=%s, error fopen\n" , Drive , pszFileName );
|
|
return false;
|
|
}
|
|
|
|
/* Write the file's header : 6 + 1 + 1 + 4 + 4 = 16 bytes */
|
|
p = buf;
|
|
strcpy ( (char *) p , WD1772_SAVE_FILE_ID ); /* +0 .. +5 */
|
|
p += strlen ( WD1772_SAVE_FILE_ID );
|
|
*p++ = WD1772_SAVE_VERSION; /* +6 */
|
|
*p++ = WD1772_SAVE_REVISION; /* +7 */
|
|
|
|
STX_WriteU32_BE ( p , SaveSectorsCount_real ); /* +8 ... +11 */
|
|
p += 4;
|
|
|
|
STX_WriteU32_BE ( p , STX_SaveStruct[ Drive ].SaveTracksCount ); /* +12 ... +15 */
|
|
p += 4;
|
|
|
|
if ( fwrite ( buf , p-buf , 1 , FileOut ) != 1 )
|
|
{
|
|
fprintf ( stderr , "STX_WriteDisk drive=%d file=%s, error fwrite header\n" , Drive , pszFileName );
|
|
fclose(FileOut);
|
|
return false;
|
|
}
|
|
|
|
|
|
/* Write the sectors' buffer */
|
|
Sector = 0;
|
|
while ( Sector < STX_SaveStruct[ Drive ].SaveSectorsCount )
|
|
{
|
|
pStxSaveSector = &STX_SaveStruct[ Drive ].pSaveSectorsStruct[ Sector ];
|
|
|
|
if ( pStxSaveSector->StructIsUsed == 0 )
|
|
{
|
|
Sector++;
|
|
continue; /* This structure is not used anymore, ignore it */
|
|
}
|
|
|
|
/* Build the sector's header : 20 bytes */
|
|
p = buf;
|
|
strcpy ( (char *) p , WD1772_SAVE_SECTOR_ID ); /* +0 .. +3 */
|
|
p += strlen ( WD1772_SAVE_SECTOR_ID );
|
|
|
|
BlockLen = 20-4 + pStxSaveSector->SectorSize;
|
|
STX_WriteU32_BE ( p , BlockLen ); /* +4 ... +7 */
|
|
p += 4;
|
|
|
|
*p++ = pStxSaveSector->Track; /* +8 */
|
|
*p++ = pStxSaveSector->Side; /* +9 */
|
|
STX_WriteU16_BE ( p , pStxSaveSector->BitPosition ); /* +10 ... +11 */
|
|
p += 2;
|
|
*p++ = pStxSaveSector->ID_Track; /* +12 */
|
|
*p++ = pStxSaveSector->ID_Head; /* +13 */
|
|
*p++ = pStxSaveSector->ID_Sector; /* +14 */
|
|
*p++ = pStxSaveSector->ID_Size; /* +15 */
|
|
STX_WriteU16_BE ( p , pStxSaveSector->ID_CRC ); /* +16 ... +17 */
|
|
p += 2;
|
|
|
|
STX_WriteU16_BE ( p , pStxSaveSector->SectorSize ); /* +18 ... +19 */
|
|
p += 2;
|
|
|
|
/* Write the header */
|
|
//Str_Dump_Hex_Ascii ( (char *) buf , p-buf, 16, "" , stderr );
|
|
if ( fwrite ( buf , p-buf , 1 , FileOut ) != 1 )
|
|
{
|
|
fprintf ( stderr , "STX_WriteDisk drive=%d file=%s, error fwrite sector header\n" , Drive , pszFileName );
|
|
fclose(FileOut);
|
|
return false;
|
|
}
|
|
|
|
/* Write the data */
|
|
//Str_Dump_Hex_Ascii ( (char *) pStxSaveSector->pData , pStxSaveSector->SectorSize, 16, "" , stderr );
|
|
if ( fwrite ( pStxSaveSector->pData , pStxSaveSector->SectorSize , 1 , FileOut ) != 1 )
|
|
{
|
|
fprintf ( stderr , "STX_WriteDisk drive=%d file=%s, error fwrite sector data\n" , Drive , pszFileName );
|
|
fclose(FileOut);
|
|
return false;
|
|
}
|
|
|
|
Sector++;
|
|
}
|
|
|
|
|
|
/* Write the tracks' buffer */
|
|
Track = 0;
|
|
while ( Track < STX_SaveStruct[ Drive ].SaveTracksCount )
|
|
{
|
|
pStxSaveTrack = &STX_SaveStruct[ Drive ].pSaveTracksStruct[ Track ];
|
|
|
|
/* Build the track's header : 12 bytes */
|
|
p = buf;
|
|
strcpy ( (char *) p , WD1772_SAVE_TRACK_ID ); /* +0 ... +3 */
|
|
p += strlen ( WD1772_SAVE_TRACK_ID );
|
|
|
|
BlockLen = 12-4 + pStxSaveTrack->TrackSizeWrite;
|
|
STX_WriteU32_BE ( p , BlockLen ); /* +4 ... +7 */
|
|
p += 4;
|
|
|
|
*p++ = pStxSaveTrack->Track; /* +8 */
|
|
*p++ = pStxSaveTrack->Side; /* +9 */
|
|
|
|
STX_WriteU16_BE ( p , pStxSaveTrack->TrackSizeWrite ); /* +10 ... +11 */
|
|
p += 2;
|
|
|
|
/* Write the header */
|
|
//Str_Dump_Hex_Ascii ( (char *) buf , p-buf, 16, "" , stderr );
|
|
if ( fwrite ( buf , p-buf , 1 , FileOut ) != 1 )
|
|
{
|
|
fprintf ( stderr , "STX_WriteDisk drive=%d file=%s, error fwrite track header\n" , Drive , pszFileName );
|
|
fclose(FileOut);
|
|
return false;
|
|
}
|
|
|
|
/* Write the data at +12 */
|
|
//Str_Dump_Hex_Ascii ( (char *) pStxSaveTrack->pDataWrite , pStxSaveTrack->TrackSizeWrite, 16, "" , stderr );
|
|
if ( fwrite ( pStxSaveTrack->pDataWrite , pStxSaveTrack->TrackSizeWrite , 1 , FileOut ) != 1 )
|
|
{
|
|
fprintf ( stderr , "STX_WriteDisk drive=%d file=%s, error fwrite track data\n" , Drive , pszFileName );
|
|
fclose(FileOut);
|
|
return false;
|
|
}
|
|
|
|
Track++;
|
|
}
|
|
|
|
|
|
fclose ( FileOut );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/*
|
|
* Load a ".wd1772" save file and add it to the STX structures
|
|
* Return true if OK.
|
|
*/
|
|
static bool STX_LoadSaveFile ( int Drive , const char *FilenameSave )
|
|
{
|
|
Uint8 *SaveFileBuffer;
|
|
long SaveFileSize;
|
|
Uint8 *p;
|
|
Uint8 *p_save;
|
|
Uint8 version , revision;
|
|
Uint32 SectorNb;
|
|
Uint32 TrackNb;
|
|
STX_SECTOR_STRUCT *pStxSector;
|
|
STX_TRACK_STRUCT *pStxTrack;
|
|
|
|
|
|
SaveFileBuffer = File_Read ( FilenameSave, &SaveFileSize, NULL );
|
|
if (!SaveFileBuffer)
|
|
{
|
|
fprintf ( stderr , "STX_LoadSaveFile drive=%d file=%s error\n" , Drive , FilenameSave );
|
|
return false;
|
|
}
|
|
|
|
p = SaveFileBuffer;
|
|
|
|
if ( strncmp ( (char *) p , WD1772_SAVE_FILE_ID , strlen ( WD1772_SAVE_FILE_ID ) ) ) /* +0 ... +5 */
|
|
{
|
|
fprintf ( stderr , "STX_LoadSaveFile drive=%d file=%s bad header\n" , Drive , FilenameSave );
|
|
free ( SaveFileBuffer );
|
|
return false;
|
|
}
|
|
p += strlen ( WD1772_SAVE_FILE_ID );
|
|
|
|
version = *p++; /* +6 */
|
|
revision = *p++; /* +7 */
|
|
if ( ( version != WD1772_SAVE_VERSION ) || ( revision != WD1772_SAVE_REVISION ) )
|
|
{
|
|
fprintf ( stderr , "STX_LoadSaveFile drive=%d file=%s bad version 0x%x revision 0x%x\n" , Drive , FilenameSave , version , revision );
|
|
free ( SaveFileBuffer );
|
|
return false;
|
|
}
|
|
|
|
STX_SaveStruct[ Drive ].SaveSectorsCount = STX_ReadU32_BE ( p ); /* +8 ... +11 */
|
|
p += 4;
|
|
|
|
STX_SaveStruct[ Drive ].SaveTracksCount = STX_ReadU32_BE ( p ); /* +12 ... +15 */
|
|
p += 4;
|
|
|
|
|
|
/* Alloc a buffer for all the sectors */
|
|
if ( STX_SaveStruct[ Drive ].SaveSectorsCount > 0 )
|
|
{
|
|
STX_SaveStruct[ Drive ].pSaveSectorsStruct = malloc ( STX_SaveStruct[ Drive ].SaveSectorsCount * sizeof ( STX_SAVE_SECTOR_STRUCT ) );
|
|
if ( !STX_SaveStruct[ Drive ].pSaveSectorsStruct )
|
|
{
|
|
Log_AlertDlg(LOG_ERROR, "Error loading STX sectors save file malloc size=%d in drive %d" ,
|
|
STX_SaveStruct[ Drive ].SaveSectorsCount , Drive );
|
|
STX_FreeSaveStruct ( Drive );
|
|
free ( SaveFileBuffer );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* Alloc a buffer for all the tracks */
|
|
if ( STX_SaveStruct[ Drive ].SaveTracksCount > 0 )
|
|
{
|
|
STX_SaveStruct[ Drive ].pSaveTracksStruct = malloc ( STX_SaveStruct[ Drive ].SaveTracksCount * sizeof ( STX_SAVE_TRACK_STRUCT ) );
|
|
if ( !STX_SaveStruct[ Drive ].pSaveTracksStruct )
|
|
{
|
|
Log_AlertDlg(LOG_ERROR, "Error loading STX tracks save file malloc size=%d in drive %d" ,
|
|
STX_SaveStruct[ Drive ].SaveTracksCount , Drive );
|
|
STX_FreeSaveStruct ( Drive );
|
|
free ( SaveFileBuffer );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
SectorNb = 0;
|
|
TrackNb = 0;
|
|
while ( p < SaveFileBuffer + SaveFileSize )
|
|
{
|
|
/* Start of a block */
|
|
p_save = p;
|
|
//Str_Dump_Hex_Ascii ( (char *) p , 32, 16, "" , stderr );
|
|
|
|
/* Check the name of this block */
|
|
if ( strncmp ( (char *) p , WD1772_SAVE_SECTOR_ID , 4 ) == 0 )
|
|
{
|
|
//fprintf ( stderr , "STX_LoadSaveFile drive=%d SECT block %d\n" , Drive , SectorNb );
|
|
if ( STX_LoadSaveFile_SECT ( Drive , &STX_SaveStruct[ Drive ].pSaveSectorsStruct[ SectorNb ] , p+4+4 ) == false )
|
|
{
|
|
Log_AlertDlg(LOG_ERROR, "Error loading STX save file SECT block %d in drive %d" ,
|
|
SectorNb , Drive );
|
|
STX_FreeSaveStruct ( Drive );
|
|
free ( SaveFileBuffer );
|
|
return false;
|
|
}
|
|
|
|
//Str_Dump_Hex_Ascii ( (char *) &STX_SaveStruct[ Drive ].pSaveSectorsStruct[ SectorNb ] , sizeof(STX_SAVE_SECTOR_STRUCT) , 16, "" , stderr );
|
|
//Str_Dump_Hex_Ascii ( (char *) STX_SaveStruct[ Drive ].pSaveSectorsStruct[ SectorNb ].pData , 32, 16, "" , stderr );
|
|
|
|
/* Find the original sector to associate it with this saved sector */
|
|
pStxSector = STX_FindSector_By_Position ( Drive , STX_SaveStruct[ Drive ].pSaveSectorsStruct[ SectorNb ].Track ,
|
|
STX_SaveStruct[ Drive ].pSaveSectorsStruct[ SectorNb ].Side ,
|
|
STX_SaveStruct[ Drive ].pSaveSectorsStruct[ SectorNb ].BitPosition );
|
|
if ( !pStxSector )
|
|
{
|
|
Log_AlertDlg(LOG_ERROR, "Error restoring STX save buffer for sector=%d in drive %d" ,
|
|
SectorNb , Drive );
|
|
STX_FreeSaveStruct ( Drive );
|
|
free ( SaveFileBuffer );
|
|
return false;
|
|
}
|
|
pStxSector->SaveSectorIndex = SectorNb;
|
|
|
|
SectorNb++;
|
|
}
|
|
|
|
else if ( strncmp ( (char *) p , WD1772_SAVE_TRACK_ID , 4 ) == 0 )
|
|
{
|
|
//fprintf ( stderr , "STX_LoadSaveFile drive=%d TRCK block %d\n" , Drive , TrackNb );
|
|
if ( STX_LoadSaveFile_TRCK ( Drive , &STX_SaveStruct[ Drive ].pSaveTracksStruct[ TrackNb ] , p+4+4 ) == false )
|
|
{
|
|
Log_AlertDlg(LOG_ERROR, "Error loading STX save file TRCK block %d in drive %d" ,
|
|
TrackNb , Drive );
|
|
STX_FreeSaveStruct ( Drive );
|
|
free ( SaveFileBuffer );
|
|
return false;
|
|
}
|
|
|
|
/* Find the original track to associate it with this saved track */
|
|
pStxTrack = STX_FindTrack ( Drive , STX_SaveStruct[ Drive ].pSaveTracksStruct[ TrackNb ].Track ,
|
|
STX_SaveStruct[ Drive ].pSaveTracksStruct[ TrackNb ].Side );
|
|
if ( !pStxTrack )
|
|
{
|
|
Log_AlertDlg(LOG_ERROR, "Error loading STX save file TRCK block %d in drive %d" ,
|
|
TrackNb , Drive );
|
|
STX_FreeSaveStruct ( Drive );
|
|
free ( SaveFileBuffer );
|
|
return false;
|
|
}
|
|
pStxTrack->SaveTrackIndex = TrackNb;
|
|
|
|
TrackNb++;
|
|
}
|
|
|
|
else
|
|
{
|
|
fprintf ( stderr , "STX_LoadSaveFile drive=%d file=%s, unknown block %4.4s, skipping\n" , Drive , FilenameSave , p );
|
|
}
|
|
|
|
/* Next block */
|
|
p = p_save + 4;
|
|
p += STX_ReadU32_BE ( p );
|
|
}
|
|
|
|
free ( SaveFileBuffer );
|
|
return true;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/*
|
|
* Parse the "SECT" block from a ".wd1772" save file
|
|
* Return true if OK.
|
|
*/
|
|
static bool STX_LoadSaveFile_SECT ( int Drive, STX_SAVE_SECTOR_STRUCT *pStxSaveSector , Uint8 *p )
|
|
{
|
|
pStxSaveSector->Track = *p++;
|
|
pStxSaveSector->Side = *p++;
|
|
|
|
pStxSaveSector->BitPosition = STX_ReadU16_BE ( p );
|
|
p += 2;
|
|
|
|
pStxSaveSector->ID_Track = *p++;
|
|
pStxSaveSector->ID_Head = *p++;
|
|
pStxSaveSector->ID_Sector = *p++;
|
|
pStxSaveSector->ID_Size = *p++;
|
|
pStxSaveSector->ID_CRC = STX_ReadU16_BE ( p );
|
|
p += 2;
|
|
|
|
pStxSaveSector->SectorSize = STX_ReadU16_BE ( p );
|
|
p += 2;
|
|
|
|
/* Copy the sector's data */
|
|
pStxSaveSector->pData = malloc ( pStxSaveSector->SectorSize );
|
|
if ( !pStxSaveSector->pData )
|
|
{
|
|
Log_AlertDlg(LOG_ERROR, "Error loading STX save buffer for track=%d side=%d bitposition=%d in drive %d" ,
|
|
pStxSaveSector->Track , pStxSaveSector->Side , pStxSaveSector->BitPosition , Drive );
|
|
return false;
|
|
}
|
|
|
|
memcpy ( pStxSaveSector->pData , p , pStxSaveSector->SectorSize );
|
|
|
|
pStxSaveSector->StructIsUsed = 1;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/*
|
|
* Parse the "TRCK" block from a ".wd1772" save file
|
|
* Return true if OK.
|
|
*/
|
|
static bool STX_LoadSaveFile_TRCK ( int Drive , STX_SAVE_TRACK_STRUCT *pStxSaveTrack , Uint8 *p )
|
|
{
|
|
pStxSaveTrack->Track = *p++;
|
|
pStxSaveTrack->Side = *p++;
|
|
|
|
pStxSaveTrack->TrackSizeWrite = STX_ReadU16_BE ( p );
|
|
p += 2;
|
|
|
|
/* Copy the track's data */
|
|
pStxSaveTrack->pDataWrite = malloc ( pStxSaveTrack->TrackSizeWrite );
|
|
if ( !pStxSaveTrack->pDataWrite )
|
|
{
|
|
Log_AlertDlg(LOG_ERROR, "Error loading STX save buffer for track=%d side=%d in drive %d" ,
|
|
pStxSaveTrack->Track , pStxSaveTrack->Side , Drive );
|
|
return false;
|
|
}
|
|
|
|
memcpy ( pStxSaveTrack->pDataWrite , p , pStxSaveTrack->TrackSizeWrite );
|
|
|
|
pStxSaveTrack->pDataRead = NULL; /* TODO : compute interpreted track */
|
|
pStxSaveTrack->TrackSizeRead = 0; /* TODO : compute interpreted track */
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/*
|
|
* Init variables used to handle STX images
|
|
*/
|
|
bool STX_Init ( void )
|
|
{
|
|
int i;
|
|
|
|
for ( i=0 ; i<MAX_FLOPPYDRIVES ; i++ )
|
|
{
|
|
STX_State.ImageBuffer[ i ] = NULL;
|
|
|
|
STX_SaveStruct[ i ].SaveSectorsCount = 0;
|
|
STX_SaveStruct[ i ].pSaveSectorsStruct = NULL;
|
|
STX_SaveStruct[ i ].SaveTracksCount = 0;
|
|
STX_SaveStruct[ i ].pSaveTracksStruct = NULL;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/*
|
|
* Init the ressources to handle the STX image inserted into a drive (0=A: 1=B:)
|
|
* We also look for an optional save file with the ".wd1772" extension.
|
|
* If this file exists, then we load it too.
|
|
*/
|
|
bool STX_Insert ( int Drive , const char *FilenameSTX , Uint8 *pImageBuffer , long ImageSize )
|
|
{
|
|
char FilenameSave[ FILENAME_MAX ];
|
|
|
|
/* Process the current STX image */
|
|
if ( STX_Insert_internal ( Drive , FilenameSTX , pImageBuffer , ImageSize ) == false )
|
|
return false;
|
|
|
|
/* Try to load an optional ".wd1772" save file. In case of error, we continue anyway with the current STX image */
|
|
if ( ( STX_FileNameToSave ( FilenameSTX , FilenameSave ) )
|
|
&& ( File_Exists ( FilenameSave ) ) )
|
|
{
|
|
fprintf ( stderr , "STX : STX_Insert drive=%d file=%s buf=%p size=%ld load wd1172 %s\n" , Drive , FilenameSTX , pImageBuffer , ImageSize , FilenameSave );
|
|
if ( STX_LoadSaveFile ( Drive , FilenameSave ) == false )
|
|
{
|
|
Log_AlertDlg ( LOG_ERROR , "Can't read the STX save file '%s'. Ignore it" , FilenameSave );
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/*
|
|
* Init the ressources to handle the STX image inserted into a drive (0=A: 1=B:)
|
|
* This function is used when restoring a memory snapshot and does not load
|
|
* an optional ".wd1772" save file (the saved data are already in the memory
|
|
* snapshot)
|
|
*/
|
|
static bool STX_Insert_internal ( int Drive , const char *FilenameSTX , Uint8 *pImageBuffer , long ImageSize )
|
|
{
|
|
fprintf ( stderr , "STX : STX_Insert_internal drive=%d file=%s buf=%p size=%ld\n" , Drive , FilenameSTX , pImageBuffer , ImageSize );
|
|
|
|
STX_State.ImageBuffer[ Drive ] = STX_BuildStruct ( pImageBuffer , STX_DEBUG_FLAG );
|
|
if ( STX_State.ImageBuffer[ Drive ] == NULL )
|
|
{
|
|
fprintf ( stderr , "STX : STX_Insert_internal drive=%d file=%s buf=%p size=%ld, error in STX_BuildStruct\n" , Drive , FilenameSTX , pImageBuffer , ImageSize );
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/*
|
|
* When ejecting a disk, free the ressources associated with an STX image
|
|
*/
|
|
bool STX_Eject ( int Drive )
|
|
{
|
|
fprintf ( stderr , "STX : STX_Eject drive=%d\n" , Drive );
|
|
|
|
if ( STX_State.ImageBuffer[ Drive ] )
|
|
{
|
|
STX_FreeStruct ( STX_State.ImageBuffer[ Drive ] );
|
|
STX_State.ImageBuffer[ Drive ] = NULL;
|
|
}
|
|
|
|
STX_FreeSaveStruct ( Drive );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/*
|
|
* Read words and longs stored in little endian order
|
|
*/
|
|
static Uint16 STX_ReadU16_LE ( Uint8 *p )
|
|
{
|
|
return (p[1]<<8) +p [0];
|
|
}
|
|
|
|
static Uint32 STX_ReadU32_LE ( Uint8 *p )
|
|
{
|
|
return (p[3]<<24) + (p[2]<<16) + (p[1]<<8) +p[0];
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/*
|
|
* Read words and longs stored in big endian order
|
|
*/
|
|
static Uint16 STX_ReadU16_BE ( Uint8 *p )
|
|
{
|
|
return (p[0]<<8) + p[1];
|
|
}
|
|
|
|
static Uint32 STX_ReadU32_BE ( Uint8 *p )
|
|
{
|
|
return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) +p[3];
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/*
|
|
* Store words and longs in big endian order
|
|
*/
|
|
static void STX_WriteU16_BE ( Uint8 *p , Uint16 val )
|
|
{
|
|
p[ 1 ] = val & 0xff;
|
|
val >>= 8;
|
|
p[ 0 ] = val & 0xff;
|
|
}
|
|
|
|
static void STX_WriteU32_BE ( Uint8 *p , Uint32 val )
|
|
{
|
|
p[ 3 ] = val & 0xff;
|
|
val >>= 8;
|
|
p[ 2 ] = val & 0xff;
|
|
val >>= 8;
|
|
p[ 1 ] = val & 0xff;
|
|
val >>= 8;
|
|
p[ 0 ] = val & 0xff;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Free all the memory allocated to store an STX file
|
|
*/
|
|
static void STX_FreeStruct ( STX_MAIN_STRUCT *pStxMain )
|
|
{
|
|
int Track;
|
|
|
|
if ( !pStxMain )
|
|
return;
|
|
|
|
for ( Track = 0 ; Track < pStxMain->TracksCount ; Track++ )
|
|
{
|
|
free ( (pStxMain->pTracksStruct[ Track ]).pSectorsStruct );
|
|
}
|
|
|
|
free ( pStxMain->pTracksStruct );
|
|
free ( pStxMain );
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Free all the memory allocated to store saved sectors / tracks
|
|
*/
|
|
static void STX_FreeSaveStruct ( int Drive )
|
|
{
|
|
if ( STX_SaveStruct[ Drive ].pSaveSectorsStruct )
|
|
{
|
|
STX_FreeSaveSectorsStructAll ( STX_SaveStruct[ Drive ].pSaveSectorsStruct , STX_SaveStruct[ Drive ].SaveSectorsCount );
|
|
STX_SaveStruct[ Drive ].SaveSectorsCount = 0;
|
|
STX_SaveStruct[ Drive ].pSaveSectorsStruct = NULL;
|
|
}
|
|
|
|
if ( STX_SaveStruct[ Drive ].pSaveTracksStruct )
|
|
{
|
|
STX_FreeSaveTracksStructAll ( STX_SaveStruct[ Drive ].pSaveTracksStruct , STX_SaveStruct[ Drive ].SaveTracksCount );
|
|
STX_SaveStruct[ Drive ].SaveTracksCount = 0;
|
|
STX_SaveStruct[ Drive ].pSaveTracksStruct = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Free the memory allocated to store all the STX_SAVE_SECTOR_STRUCT
|
|
*/
|
|
static void STX_FreeSaveSectorsStructAll ( STX_SAVE_SECTOR_STRUCT *pSaveSectorsStruct , Uint32 SaveSectorsCount )
|
|
{
|
|
Uint32 i;
|
|
|
|
if ( !pSaveSectorsStruct )
|
|
return;
|
|
|
|
for ( i = 0 ; i < SaveSectorsCount ; i++ )
|
|
{
|
|
STX_FreeSaveSectorsStruct ( pSaveSectorsStruct , i );
|
|
}
|
|
|
|
free ( pSaveSectorsStruct );
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Free the memory allocated to store one STX_SAVE_SECTOR_STRUCT
|
|
*/
|
|
static void STX_FreeSaveSectorsStruct ( STX_SAVE_SECTOR_STRUCT *pSaveSectorsStruct , int Nb )
|
|
{
|
|
if ( pSaveSectorsStruct[ Nb ].StructIsUsed == 0 )
|
|
return; /* This structure is already free */
|
|
|
|
if ( pSaveSectorsStruct[ Nb ].pData )
|
|
free ( pSaveSectorsStruct[ Nb ].pData );
|
|
|
|
pSaveSectorsStruct[ Nb ].StructIsUsed = 0;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Free the memory allocated to store all the STX_SAVE_TRACK_STRUCT
|
|
*/
|
|
static void STX_FreeSaveTracksStructAll ( STX_SAVE_TRACK_STRUCT *pSaveTracksStruct , Uint32 SaveTracksCount )
|
|
{
|
|
Uint32 i;
|
|
|
|
if ( !pSaveTracksStruct )
|
|
return;
|
|
|
|
for ( i = 0 ; i < SaveTracksCount ; i++ )
|
|
{
|
|
STX_FreeSaveTracksStruct ( pSaveTracksStruct , i );
|
|
}
|
|
|
|
free ( pSaveTracksStruct );
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Free the memory allocated to store one STX_SAVE_TRACK_STRUCT
|
|
*/
|
|
static void STX_FreeSaveTracksStruct ( STX_SAVE_TRACK_STRUCT *pSaveTracksStruct , int Nb )
|
|
{
|
|
if ( pSaveTracksStruct[ Nb ].pDataWrite )
|
|
free ( pSaveTracksStruct[ Nb ].pDataWrite );
|
|
if ( pSaveTracksStruct[ Nb ].pDataRead )
|
|
free ( pSaveTracksStruct[ Nb ].pDataRead );
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Parse an STX file.
|
|
* The file is in pFileBuffer and we dynamically allocate memory to store
|
|
* the components (main header, tracks, sectors).
|
|
* Some internal variables/pointers are also computed, to speed up
|
|
* data access when the FDC emulates an STX file.
|
|
*/
|
|
STX_MAIN_STRUCT *STX_BuildStruct ( Uint8 *pFileBuffer , int Debug )
|
|
{
|
|
|
|
STX_MAIN_STRUCT *pStxMain;
|
|
STX_TRACK_STRUCT *pStxTrack;
|
|
STX_SECTOR_STRUCT *pStxSector;
|
|
Uint8 *p;
|
|
Uint8 *p_cur;
|
|
int Track;
|
|
int Sector;
|
|
Uint8 *pFuzzyData;
|
|
Uint8 *pTimingData;
|
|
Uint32 MaxOffsetSectorEnd;
|
|
int VariableTimings;
|
|
|
|
pStxMain = malloc ( sizeof ( STX_MAIN_STRUCT ) );
|
|
if ( !pStxMain )
|
|
return NULL;
|
|
memset ( pStxMain , 0 , sizeof ( STX_MAIN_STRUCT ) );
|
|
|
|
p = pFileBuffer;
|
|
|
|
/* Read file's header */
|
|
memcpy ( pStxMain->FileID , p , 4 ); p += 4;
|
|
pStxMain->Version = STX_ReadU16_LE ( p ); p += 2;
|
|
pStxMain->ImagingTool = STX_ReadU16_LE ( p ); p += 2;
|
|
pStxMain->Reserved_1 = STX_ReadU16_LE ( p ); p += 2;
|
|
pStxMain->TracksCount = *p++;;
|
|
pStxMain->Revision = *p++;
|
|
pStxMain->Reserved_2 = STX_ReadU32_LE ( p ); p += 4;
|
|
|
|
if ( Debug & STX_DEBUG_FLAG_STRUCTURE )
|
|
fprintf ( stderr , "STX header ID='%.4s' Version=%4.4x ImagingTool=%4.4x Reserved1=%4.4x"
|
|
" TrackCount=%d Revision=%2.2x Reserved2=%x\n" , pStxMain->FileID , pStxMain->Version ,
|
|
pStxMain->ImagingTool , pStxMain->Reserved_1 , pStxMain->TracksCount , pStxMain->Revision ,
|
|
pStxMain->Reserved_2 );
|
|
|
|
pStxMain->WarnedWriteSector = false;
|
|
pStxMain->WarnedWriteTrack = false;
|
|
|
|
pStxTrack = malloc ( sizeof ( STX_TRACK_STRUCT ) * pStxMain->TracksCount );
|
|
if ( !pStxTrack )
|
|
{
|
|
STX_FreeStruct ( pStxMain );
|
|
return NULL;
|
|
}
|
|
memset ( pStxTrack , 0 , sizeof ( STX_TRACK_STRUCT ) * pStxMain->TracksCount );
|
|
pStxMain->pTracksStruct = pStxTrack;
|
|
|
|
/* Parse all the track blocks */
|
|
for ( Track = 0 ; Track < pStxMain->TracksCount ; Track++ )
|
|
{
|
|
p_cur = p;
|
|
|
|
pStxTrack->BlockSize = STX_ReadU32_LE ( p ); p += 4;
|
|
pStxTrack->FuzzySize = STX_ReadU32_LE ( p ); p += 4;
|
|
pStxTrack->SectorsCount = STX_ReadU16_LE ( p ); p += 2;
|
|
pStxTrack->Flags = STX_ReadU16_LE ( p ); p += 2;
|
|
pStxTrack->MFMSize = STX_ReadU16_LE ( p ); p += 2;
|
|
pStxTrack->TrackNumber = *p++;
|
|
pStxTrack->RecordType = *p++;
|
|
|
|
pStxTrack->SaveTrackIndex = -1;
|
|
|
|
if ( pStxTrack->SectorsCount == 0 ) /* No sector (track image only, or empty / non formatted track) */
|
|
{
|
|
pStxTrack->pSectorsStruct = NULL;
|
|
}
|
|
else
|
|
{
|
|
/* Track contains some sectors */
|
|
pStxSector = malloc ( sizeof ( STX_SECTOR_STRUCT ) * pStxTrack->SectorsCount );
|
|
if ( !pStxSector )
|
|
{
|
|
STX_FreeStruct ( pStxMain );
|
|
return NULL;
|
|
}
|
|
memset ( pStxSector , 0 , sizeof ( STX_SECTOR_STRUCT ) * pStxTrack->SectorsCount );
|
|
pStxTrack->pSectorsStruct = pStxSector;
|
|
|
|
/* Do we have some sector infos after the track header or only sector data ? */
|
|
if ( ( pStxTrack->Flags & STX_TRACK_FLAG_SECTOR_BLOCK ) == 0 )
|
|
{
|
|
/* The track only contains SectorsCount sectors of 512 bytes */
|
|
/* NOTE |NP] : in that case, pStxTrack->MFMSize seems to be in bits instead of bytes */
|
|
STX_BuildSectorsSimple ( pStxTrack , p );
|
|
goto next_track;
|
|
}
|
|
}
|
|
|
|
/* Start of the optional fuzzy bits data */
|
|
pStxTrack->pFuzzyData = p + pStxTrack->SectorsCount * STX_SECTOR_BLOCK_SIZE;
|
|
|
|
/* Start of the optional track data */
|
|
pStxTrack->pTrackData = pStxTrack->pFuzzyData + pStxTrack->FuzzySize;
|
|
|
|
if ( ( pStxTrack->Flags & STX_TRACK_FLAG_TRACK_IMAGE ) == 0 )
|
|
{
|
|
pStxTrack->TrackImageSyncPosition = 0;
|
|
pStxTrack->TrackImageSize = 0;
|
|
pStxTrack->pTrackImageData = NULL;
|
|
pStxTrack->pSectorsImageData = pStxTrack->pTrackData;
|
|
}
|
|
else if ( ( pStxTrack->Flags & STX_TRACK_FLAG_TRACK_IMAGE_SYNC ) == 0 ) /* Track with size+data */
|
|
{
|
|
pStxTrack->TrackImageSyncPosition = 0;
|
|
pStxTrack->TrackImageSize = STX_ReadU16_LE ( pStxTrack->pTrackData );
|
|
pStxTrack->pTrackImageData = pStxTrack->pTrackData + 2;
|
|
pStxTrack->pSectorsImageData = pStxTrack->pTrackImageData + pStxTrack->TrackImageSize;
|
|
}
|
|
else /* Track with sync offset + size + data */
|
|
{
|
|
pStxTrack->TrackImageSyncPosition = STX_ReadU16_LE ( pStxTrack->pTrackData );
|
|
pStxTrack->TrackImageSize = STX_ReadU16_LE ( pStxTrack->pTrackData + 2 );
|
|
pStxTrack->pTrackImageData = pStxTrack->pTrackData + 4;
|
|
pStxTrack->pSectorsImageData = pStxTrack->pTrackImageData + pStxTrack->TrackImageSize;
|
|
}
|
|
|
|
if ( pStxTrack->SectorsCount == 0 ) /* No sector (track image only, or empty / non formatted track) */
|
|
goto next_track;
|
|
|
|
/* Parse all the sectors in this track */
|
|
pFuzzyData = pStxTrack->pFuzzyData;
|
|
VariableTimings = 0;
|
|
MaxOffsetSectorEnd = 0;
|
|
for ( Sector = 0 ; Sector < pStxTrack->SectorsCount ; Sector++ )
|
|
{
|
|
pStxSector = &(pStxTrack->pSectorsStruct[ Sector ]);
|
|
|
|
pStxSector->DataOffset = STX_ReadU32_LE ( p ); p += 4;
|
|
pStxSector->BitPosition = STX_ReadU16_LE ( p ); p += 2;
|
|
pStxSector->ReadTime = STX_ReadU16_LE ( p ); p += 2;
|
|
pStxSector->ID_Track = *p++;
|
|
pStxSector->ID_Head = *p++;
|
|
pStxSector->ID_Sector = *p++;
|
|
pStxSector->ID_Size = *p++;
|
|
pStxSector->ID_CRC = ( p[0] << 8 ) | p[1] ; p +=2;
|
|
pStxSector->FDC_Status = *p++;
|
|
pStxSector->Reserved = *p++;
|
|
|
|
/* Check if sector has data */
|
|
if ( ( pStxSector->FDC_Status & STX_SECTOR_FLAG_RNF ) == 0 )
|
|
{
|
|
/* Check if SectorSize is valid (this is just a warning, we keep only bits 0-1 anyway) */
|
|
if ( pStxSector->ID_Size & ~FDC_SECTOR_SIZE_MASK )
|
|
{
|
|
// fprintf ( stderr , "STX : invalid ID_Size=%d on track %d sector %d\n" ,
|
|
// pStxSector->ID_Size , Track , Sector );
|
|
}
|
|
|
|
pStxSector->SectorSize = 128 << ( pStxSector->ID_Size & FDC_SECTOR_SIZE_MASK );
|
|
|
|
pStxSector->pData = pStxTrack->pTrackData + pStxSector->DataOffset;
|
|
if ( pStxSector->FDC_Status & STX_SECTOR_FLAG_FUZZY )
|
|
{
|
|
pStxSector->pFuzzyData = pFuzzyData;
|
|
pFuzzyData += pStxSector->SectorSize;
|
|
}
|
|
|
|
/* Max offset of the end of all sectors image in the track */
|
|
if ( MaxOffsetSectorEnd < pStxSector->DataOffset + pStxSector->SectorSize )
|
|
MaxOffsetSectorEnd = pStxSector->DataOffset + pStxSector->SectorSize;
|
|
|
|
if ( pStxSector->FDC_Status & STX_SECTOR_FLAG_VARIABLE_TIME )
|
|
VariableTimings = 1;
|
|
}
|
|
|
|
pStxSector->SaveSectorIndex = -1;
|
|
}
|
|
|
|
/* Start of the optional timings data, after the optional sectors image data */
|
|
pStxTrack->pTiming = pStxTrack->pTrackData + MaxOffsetSectorEnd;
|
|
if ( pStxTrack->pTiming < pStxTrack->pSectorsImageData ) /* If all sectors image were inside the track image */
|
|
pStxTrack->pTiming = pStxTrack->pSectorsImageData; /* then timings data are just after the track image */
|
|
|
|
if ( VariableTimings == 1 ) /* Track has at least one variable sector */
|
|
{
|
|
if ( pStxMain->Revision == 2 ) /* Specific timing table */
|
|
{
|
|
pStxTrack->TimingFlags = STX_ReadU16_LE ( pStxTrack->pTiming ); /* always '5' ? */
|
|
pStxTrack->TimingSize = STX_ReadU16_LE ( pStxTrack->pTiming + 2 );
|
|
pStxTrack->pTimingData = pStxTrack->pTiming + 4; /* 2 bytes of timing for each block of 16 bytes */
|
|
}
|
|
|
|
/* Compute the address of the timings data for each sector with variable timings */
|
|
pTimingData = pStxTrack->pTimingData;
|
|
for ( Sector = 0 ; Sector < pStxTrack->SectorsCount ; Sector++ )
|
|
{
|
|
pStxSector = &(pStxTrack->pSectorsStruct[ Sector ]);
|
|
pStxSector->pTimingData = NULL; /* No timings by default */
|
|
|
|
/* Check if sector has data + variable timings */
|
|
if ( ( ( pStxSector->FDC_Status & STX_SECTOR_FLAG_RNF ) == 0 )
|
|
&& ( pStxSector->FDC_Status & STX_SECTOR_FLAG_VARIABLE_TIME ) )
|
|
{
|
|
if ( pStxMain->Revision == 2 ) /* Specific table for revision 2 */
|
|
{
|
|
pStxSector->pTimingData = pTimingData;
|
|
pTimingData += ( pStxSector->SectorSize / 16 ) * 2;
|
|
}
|
|
else
|
|
pStxSector->pTimingData = TimingDataDefault; /* Fixed table for revision 0 */
|
|
}
|
|
}
|
|
}
|
|
|
|
next_track:
|
|
if ( Debug & STX_DEBUG_FLAG_STRUCTURE )
|
|
{
|
|
fprintf ( stderr , " track %3d BlockSize=%d FuzzySize=%d Sectors=%4.4x Flags=%4.4x"
|
|
" MFMSize=%d TrackNb=%2.2x Side=%d RecordType=%x"
|
|
" TrackImage=%s (%d bytes, sync=%4.4x) Timings=%d,%d\n" ,
|
|
Track , pStxTrack->BlockSize ,
|
|
pStxTrack->FuzzySize , pStxTrack->SectorsCount , pStxTrack->Flags , pStxTrack->MFMSize ,
|
|
pStxTrack->TrackNumber & 0x7f , ( pStxTrack->TrackNumber >> 7 ) & 0x01 , pStxTrack->RecordType ,
|
|
pStxTrack->pTrackImageData ? "yes" : "no" , pStxTrack->TrackImageSize , pStxTrack->TrackImageSyncPosition ,
|
|
pStxTrack->TimingFlags , pStxTrack->TimingSize );
|
|
|
|
if ( ( Debug & STX_DEBUG_FLAG_DATA ) && pStxTrack->pTrackImageData )
|
|
{
|
|
fprintf ( stderr , " track image data :\n" );
|
|
Str_Dump_Hex_Ascii ( (char *)pStxTrack->pTrackImageData , pStxTrack->TrackImageSize ,
|
|
16 , " " , stderr );
|
|
}
|
|
|
|
if ( pStxTrack->SectorsCount == 0 )
|
|
fprintf ( stderr , " no sector in this track, %s\n" ,
|
|
pStxTrack->pTrackImageData ? "only track image" : "track empty / not formatted" );
|
|
else
|
|
for ( Sector = 0 ; Sector < pStxTrack->SectorsCount ; Sector++ )
|
|
{
|
|
/* If the sector use the internal timing table, we print TimingsOffset=-1 */
|
|
pStxSector = &(pStxTrack->pSectorsStruct[ Sector ]);
|
|
fprintf ( stderr , " sector %2d DataOffset=%d BitPosition=%d ReadTime=%d"
|
|
" [track=%2.2x head=%2.2x sector=%2.2x size=%2.2x crc=%4.4x]"
|
|
" FdcStatus=%2.2x Reserved=%2.2x TimingsOffset=%d\n" ,
|
|
Sector , pStxSector->DataOffset , pStxSector->BitPosition ,
|
|
pStxSector->ReadTime , pStxSector->ID_Track , pStxSector->ID_Head ,
|
|
pStxSector->ID_Sector , pStxSector->ID_Size , pStxSector->ID_CRC ,
|
|
pStxSector->FDC_Status , pStxSector->Reserved ,
|
|
pStxSector->pTimingData ?
|
|
( pStxTrack->TimingSize > 0 ? (int)(pStxSector->pTimingData - pStxTrack->pTrackData) : -1 )
|
|
: 0 );
|
|
|
|
if ( ( Debug & STX_DEBUG_FLAG_DATA ) && pStxSector->pData )
|
|
{
|
|
fprintf ( stderr , " sector data :\n" );
|
|
Str_Dump_Hex_Ascii ( (char *)pStxSector->pData , pStxSector->SectorSize ,
|
|
16 , " " , stderr );
|
|
}
|
|
if ( ( Debug & STX_DEBUG_FLAG_DATA ) && pStxSector->pFuzzyData )
|
|
{
|
|
fprintf ( stderr , " fuzzy data :\n" );
|
|
Str_Dump_Hex_Ascii ( (char *)pStxSector->pFuzzyData , pStxSector->SectorSize ,
|
|
16 , " " , stderr );
|
|
}
|
|
if ( ( Debug & STX_DEBUG_FLAG_DATA ) && pStxSector->pTimingData )
|
|
{
|
|
fprintf ( stderr , " timing data :\n" );
|
|
Str_Dump_Hex_Ascii ( (char *)pStxSector->pTimingData , ( pStxSector->SectorSize / 16 ) * 2 ,
|
|
16 , " " , stderr );
|
|
}
|
|
}
|
|
}
|
|
|
|
p = p_cur + pStxTrack->BlockSize; /* Next Track block */
|
|
pStxTrack++;
|
|
}
|
|
|
|
|
|
return pStxMain;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* When a track only consists of the content of each 512 bytes sector and
|
|
* no timings informations, we must compute some default values for each
|
|
* sector, as well as the position of the corresponding 512 bytes of data.
|
|
* This is only used when storing unprotected tracks.
|
|
*/
|
|
static void STX_BuildSectorsSimple ( STX_TRACK_STRUCT *pStxTrack , Uint8 *p )
|
|
{
|
|
int Sector;
|
|
int BytePosition;
|
|
Uint16 CRC;
|
|
|
|
BytePosition = FDC_TRACK_LAYOUT_STANDARD_GAP1 + FDC_TRACK_LAYOUT_STANDARD_GAP2; /* Points to the 3x$A1 before the 1st IDAM $FE */
|
|
BytePosition += 4; /* Pasti seems to point after the 3x$A1 and the IDAM $FE */
|
|
|
|
for ( Sector = 0 ; Sector < pStxTrack->SectorsCount ; Sector++ )
|
|
{
|
|
pStxTrack->pSectorsStruct[ Sector ].SaveSectorIndex = -1;
|
|
pStxTrack->pSectorsStruct[ Sector ].DataOffset = 0;
|
|
pStxTrack->pSectorsStruct[ Sector ].BitPosition = BytePosition * 8;
|
|
pStxTrack->pSectorsStruct[ Sector ].ReadTime = 0;
|
|
|
|
/* Build the ID Field */
|
|
pStxTrack->pSectorsStruct[ Sector ].ID_Track = pStxTrack->TrackNumber & 0x7f;
|
|
pStxTrack->pSectorsStruct[ Sector ].ID_Head = ( pStxTrack->TrackNumber >> 7 ) & 0x01;
|
|
pStxTrack->pSectorsStruct[ Sector ].ID_Sector = Sector + 1;
|
|
pStxTrack->pSectorsStruct[ Sector ].ID_Size = FDC_SECTOR_SIZE_512;
|
|
CRC = STX_BuildSectorID_CRC ( &(pStxTrack->pSectorsStruct[ Sector ]) );
|
|
pStxTrack->pSectorsStruct[ Sector ].ID_CRC = CRC;
|
|
|
|
pStxTrack->pSectorsStruct[ Sector ].FDC_Status = 0;
|
|
pStxTrack->pSectorsStruct[ Sector ].Reserved = 0;
|
|
pStxTrack->pSectorsStruct[ Sector ].pData = p + Sector * 512;
|
|
pStxTrack->pSectorsStruct[ Sector ].SectorSize = 128 << pStxTrack->pSectorsStruct[ Sector ].ID_Size;
|
|
|
|
BytePosition += FDC_TRACK_LAYOUT_STANDARD_RAW_SECTOR_512;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Compute the CRC of the Address Field for a given sector.
|
|
*/
|
|
static Uint16 STX_BuildSectorID_CRC ( STX_SECTOR_STRUCT *pStxSector )
|
|
{
|
|
Uint16 CRC;
|
|
|
|
crc16_reset ( &CRC );
|
|
crc16_add_byte ( &CRC , 0xa1 );
|
|
crc16_add_byte ( &CRC , 0xa1 );
|
|
crc16_add_byte ( &CRC , 0xa1 );
|
|
crc16_add_byte ( &CRC , 0xfe );
|
|
crc16_add_byte ( &CRC , pStxSector->ID_Track );
|
|
crc16_add_byte ( &CRC , pStxSector->ID_Head );
|
|
crc16_add_byte ( &CRC , pStxSector->ID_Sector );
|
|
crc16_add_byte ( &CRC , pStxSector->ID_Size );
|
|
|
|
return CRC;
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Find a track in the floppy image inserted into a drive.
|
|
*/
|
|
static STX_TRACK_STRUCT *STX_FindTrack ( Uint8 Drive , Uint8 Track , Uint8 Side )
|
|
{
|
|
int i;
|
|
|
|
if ( STX_State.ImageBuffer[ Drive ] == NULL )
|
|
return NULL;
|
|
|
|
for ( i=0 ; i<STX_State.ImageBuffer[ Drive ]->TracksCount ; i++ )
|
|
if ( STX_State.ImageBuffer[ Drive ]->pTracksStruct[ i ].TrackNumber == ( ( Track & 0x7f ) | ( Side << 7 ) ) )
|
|
return &(STX_State.ImageBuffer[ Drive ]->pTracksStruct[ i ]);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Find a sector in the floppy image inserted into a drive.
|
|
* SectorStruct_Nb is a value set by a previous call to FDC_NextSectorID_FdcCycles_STX()
|
|
*/
|
|
static STX_SECTOR_STRUCT *STX_FindSector ( Uint8 Drive , Uint8 Track , Uint8 Side , Uint8 SectorStruct_Nb )
|
|
{
|
|
STX_TRACK_STRUCT *pStxTrack;
|
|
|
|
if ( STX_State.ImageBuffer[ Drive ] == NULL )
|
|
return NULL;
|
|
|
|
pStxTrack = STX_FindTrack ( Drive , Track , Side );
|
|
if ( pStxTrack == NULL )
|
|
return NULL;
|
|
|
|
if ( pStxTrack->pSectorsStruct == NULL )
|
|
return NULL;
|
|
|
|
return &(pStxTrack->pSectorsStruct[ SectorStruct_Nb ]);
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Find a sector in the floppy image inserted into a drive.
|
|
* The sector is identified by its BitPosition which is unique per track/side
|
|
*/
|
|
static STX_SECTOR_STRUCT *STX_FindSector_By_Position ( Uint8 Drive , Uint8 Track , Uint8 Side , Uint16 BitPosition )
|
|
{
|
|
STX_TRACK_STRUCT *pStxTrack;
|
|
int Sector;
|
|
|
|
if ( STX_State.ImageBuffer[ Drive ] == NULL )
|
|
return NULL;
|
|
|
|
pStxTrack = STX_FindTrack ( Drive , Track , Side );
|
|
if ( pStxTrack == NULL )
|
|
return NULL;
|
|
|
|
if ( pStxTrack->pSectorsStruct == NULL )
|
|
return NULL;
|
|
|
|
for ( Sector=0 ; Sector<pStxTrack->SectorsCount ; Sector++ )
|
|
if ( pStxTrack->pSectorsStruct[ Sector ].BitPosition == BitPosition )
|
|
return &(pStxTrack->pSectorsStruct[ Sector ]);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Return the number of FDC cycles to go from one index pulse to the next
|
|
* one on a given drive/track/side.
|
|
* We take the TrackSize into account to return this delay.
|
|
*/
|
|
Uint32 FDC_GetCyclesPerRev_FdcCycles_STX ( Uint8 Drive , Uint8 Track , Uint8 Side )
|
|
{
|
|
STX_TRACK_STRUCT *pStxTrack;
|
|
int TrackSize;
|
|
|
|
pStxTrack = STX_FindTrack ( Drive , Track , Side );
|
|
if ( pStxTrack == NULL )
|
|
TrackSize = FDC_TRACK_BYTES_STANDARD; /* Use a standard track length is track is not available */
|
|
|
|
else if ( pStxTrack->pTrackImageData )
|
|
TrackSize = pStxTrack->TrackImageSize;
|
|
else if ( ( pStxTrack->Flags & STX_TRACK_FLAG_SECTOR_BLOCK ) == 0 )
|
|
TrackSize = pStxTrack->MFMSize / 8; /* When the track contains only sector data, MFMSize is in bits */
|
|
else
|
|
TrackSize = pStxTrack->MFMSize;
|
|
|
|
//fprintf ( stderr , "fdc stx drive=%d track=0x%x side=%d size=%d\n" , Drive , Track, Side , TrackSize );
|
|
return TrackSize * FDC_DELAY_CYCLE_MFM_BYTE;
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Return the number of FDC cycles to wait before reaching the next
|
|
* sector's ID Field in the track ($A1 $A1 $A1 $FE TR SIDE SR LEN CRC1 CRC2)
|
|
* If no ID Field is found before the end of the track, we use the 1st
|
|
* ID Field of the track (which simulates a full spin of the floppy).
|
|
* We also store the next sector's number into NextSectorStruct_Nbr,
|
|
* the next sector's number into NextSector_ID_Field_SR, the next track's number
|
|
* into NextSector_ID_Field_TR, the next sector's lenght into
|
|
* NextSector_ID_Field_LEN and if the CRC is correct or not into NextSector_ID_Field_CRC_OK.
|
|
* This function assumes the sectors of each track are sorted in ascending order
|
|
* using BitPosition.
|
|
* If there's no available drive/floppy or no ID field in the track, we return -1
|
|
*/
|
|
int FDC_NextSectorID_FdcCycles_STX ( Uint8 Drive , Uint8 NumberOfHeads , Uint8 Track , Uint8 Side )
|
|
{
|
|
STX_TRACK_STRUCT *pStxTrack;
|
|
int CurrentPos_FdcCycles;
|
|
int i;
|
|
int Delay_FdcCycles;
|
|
int TrackSize;
|
|
|
|
CurrentPos_FdcCycles = FDC_IndexPulse_GetCurrentPos_FdcCycles ( NULL );
|
|
if ( CurrentPos_FdcCycles < 0 ) /* No drive/floppy available at the moment */
|
|
return -1;
|
|
|
|
if ( ( Side == 1 ) && ( NumberOfHeads == 1 ) ) /* Can't read side 1 on a single sided drive */
|
|
return -1;
|
|
|
|
pStxTrack = STX_FindTrack ( Drive , Track , Side );
|
|
if ( pStxTrack == NULL ) /* Track/Side don't exist in this STX image */
|
|
return -1;
|
|
|
|
if ( pStxTrack->SectorsCount == 0 ) /* No sector (track image only, or empty / non formatted track) */
|
|
return -1;
|
|
|
|
/* Compare CurrentPos_FdcCycles with each sector's position in ascending order */
|
|
for ( i=0 ; i<pStxTrack->SectorsCount ; i++ )
|
|
{
|
|
if ( CurrentPos_FdcCycles < (int)pStxTrack->pSectorsStruct[ i ].BitPosition*FDC_DELAY_CYCLE_MFM_BIT ) /* 1 bit = 32 cycles at 8 MHz */
|
|
break; /* We found the next sector */
|
|
}
|
|
|
|
if ( i == pStxTrack->SectorsCount ) /* CurrentPos_FdcCycles is after the last ID Field of this track */
|
|
{
|
|
/* Reach end of track (new index pulse), then go to 1st sector from current position */
|
|
if ( pStxTrack->pTrackImageData )
|
|
TrackSize = pStxTrack->TrackImageSize;
|
|
else if ( ( pStxTrack->Flags & STX_TRACK_FLAG_SECTOR_BLOCK ) == 0 )
|
|
TrackSize = pStxTrack->MFMSize / 8; /* When the track contains only sector data, MFMSize is in bits */
|
|
else
|
|
TrackSize = pStxTrack->MFMSize;
|
|
|
|
Delay_FdcCycles = TrackSize * FDC_DELAY_CYCLE_MFM_BYTE - CurrentPos_FdcCycles
|
|
+ pStxTrack->pSectorsStruct[ 0 ].BitPosition*FDC_DELAY_CYCLE_MFM_BIT;
|
|
STX_State.NextSectorStruct_Nbr = 0;
|
|
//fprintf ( stderr , "size=%d pos=%d pos0=%d delay=%d\n" , TrackSize, CurrentPos_FdcCycles, pStxTrack->pSectorsStruct[ 0 ].BitPosition , Delay_FdcCycles );
|
|
}
|
|
else /* There's an ID Field before end of track */
|
|
{
|
|
Delay_FdcCycles = (int)pStxTrack->pSectorsStruct[ i ].BitPosition*FDC_DELAY_CYCLE_MFM_BIT - CurrentPos_FdcCycles;
|
|
STX_State.NextSectorStruct_Nbr = i;
|
|
//fprintf ( stderr , "i=%d pos=%d posi=%d delay=%d\n" , i, CurrentPos_FdcCycles, pStxTrack->pSectorsStruct[ i ].BitPosition*FDC_DELAY_CYCLE_MFM_BIT , Delay_FdcCycles );
|
|
}
|
|
|
|
/* Store the value of the track/sector numbers in the next ID field */
|
|
STX_State.NextSector_ID_Field_TR = pStxTrack->pSectorsStruct[ STX_State.NextSectorStruct_Nbr ].ID_Track;
|
|
STX_State.NextSector_ID_Field_SR = pStxTrack->pSectorsStruct[ STX_State.NextSectorStruct_Nbr ].ID_Sector;
|
|
STX_State.NextSector_ID_Field_LEN = pStxTrack->pSectorsStruct[ STX_State.NextSectorStruct_Nbr ].ID_Size;
|
|
|
|
/* If RNF is set and CRC error is set, then this ID field has a CRC error */
|
|
if ( ( pStxTrack->pSectorsStruct[ STX_State.NextSectorStruct_Nbr ].FDC_Status & STX_SECTOR_FLAG_RNF )
|
|
&& ( pStxTrack->pSectorsStruct[ STX_State.NextSectorStruct_Nbr ].FDC_Status & STX_SECTOR_FLAG_CRC ) )
|
|
STX_State.NextSector_ID_Field_CRC_OK = 0; /* CRC bad */
|
|
else
|
|
STX_State.NextSector_ID_Field_CRC_OK = 1; /* CRC correct */
|
|
|
|
/* BitPosition in STX seems to point just after the IDAM $FE ; we need to point 4 bytes earlier at the 1st $A1 */
|
|
Delay_FdcCycles -= 4 * FDC_DELAY_CYCLE_MFM_BYTE; /* Correct delay to point to $A1 $A1 $A1 $FE */
|
|
|
|
//fprintf ( stderr , "fdc bytes next sector pos=%d delay=%d maxsr=%d nextsr=%d\n" , CurrentPos_FdcCycles, Delay_FdcCycles, pStxTrack->SectorsCount, STX_State.NextSectorStruct_Nbr );
|
|
return Delay_FdcCycles;
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Return the value of the track number in the next ID field set by
|
|
* FDC_NextSectorID_FdcCycles_STX.
|
|
*/
|
|
Uint8 FDC_NextSectorID_TR_STX ( void )
|
|
{
|
|
return STX_State.NextSector_ID_Field_TR;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Return the value of the sector number in the next ID field set by
|
|
* FDC_NextSectorID_FdcCycles_STX.
|
|
*/
|
|
Uint8 FDC_NextSectorID_SR_STX ( void )
|
|
{
|
|
return STX_State.NextSector_ID_Field_SR;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Return the value of the sector's length in the next ID field set by
|
|
* FDC_NextSectorID_FdcCycles_STX.
|
|
*/
|
|
Uint8 FDC_NextSectorID_LEN_STX ( void )
|
|
{
|
|
return STX_State.NextSector_ID_Field_LEN;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Return the status of the CRC in the next ID field set by
|
|
* FDC_NextSectorID_FdcCycles_STX.
|
|
* If '0', CRC is bad, else CRC is OK
|
|
*/
|
|
Uint8 FDC_NextSectorID_CRC_OK_STX ( void )
|
|
{
|
|
return STX_State.NextSector_ID_Field_CRC_OK;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read a sector from a floppy image in STX format (used in type II command)
|
|
* We return the sector NextSectorStruct_Nbr, whose value was set
|
|
* by the latest call to FDC_NextSectorID_FdcCycles_STX
|
|
* Each byte of the sector is added to the FDC buffer with a default timing
|
|
* (32 microsec) or a variable timing, depending on the sector's flags.
|
|
* Some sectors can also contains "fuzzy" bits.
|
|
* Special care must be taken to compute the timing of each byte, which can
|
|
* be a decimal value and must be rounded to the best possible integer.
|
|
*
|
|
* If the sector's data were changed by a 'write sector' command, then we assume
|
|
* a sector with no fuzzy byte and standard timings.
|
|
*
|
|
* Return RNF if sector was not found, else return CRC and RECORD_TYPE values
|
|
* for the status register.
|
|
*/
|
|
Uint8 FDC_ReadSector_STX ( Uint8 Drive , Uint8 Track , Uint8 Sector , Uint8 Side , int *pSectorSize )
|
|
{
|
|
STX_SECTOR_STRUCT *pStxSector;
|
|
int i;
|
|
Uint8 Byte;
|
|
Uint16 Timing;
|
|
Uint32 Sector_ReadTime;
|
|
double Total_cur; /* To compute closest integer timings for each byte */
|
|
double Total_prev;
|
|
Uint8 *pSector_WriteData;
|
|
|
|
pStxSector = STX_FindSector ( Drive , Track , Side , STX_State.NextSectorStruct_Nbr );
|
|
if ( pStxSector == NULL )
|
|
{
|
|
fprintf ( stderr , "FDC_ReadSector_STX drive=%d track=%d side=%d sector=%d returns null !\n" ,
|
|
Drive , Track , Side , STX_State.NextSectorStruct_Nbr );
|
|
return STX_SECTOR_FLAG_RNF; /* Should not happen if FDC_NextSectorID_FdcCycles_STX succeeded before */
|
|
}
|
|
|
|
/* If RNF is set, return FDC_STR_BIT_RNF */
|
|
if ( pStxSector->FDC_Status & STX_SECTOR_FLAG_RNF )
|
|
return STX_SECTOR_FLAG_RNF; /* RNF in FDC's status register */
|
|
|
|
*pSectorSize = pStxSector->SectorSize;
|
|
Sector_ReadTime = pStxSector->ReadTime;
|
|
|
|
/* Check if this sector was changed by a 'write sector' command */
|
|
/* If so, we use this recent buffer instead of the original STX content */
|
|
if (STX_SaveStruct[Drive].SaveSectorsCount > 0 && pStxSector->SaveSectorIndex >= 0)
|
|
{
|
|
pSector_WriteData = STX_SaveStruct[ Drive ].pSaveSectorsStruct[ pStxSector->SaveSectorIndex ].pData;
|
|
Sector_ReadTime = 0; /* Standard timings */
|
|
|
|
LOG_TRACE(TRACE_FDC, "fdc stx read sector drive=%d track=%d sect=%d side=%d using saved sector=%d\n" ,
|
|
Drive, Track, Sector, Side , pStxSector->SaveSectorIndex );
|
|
}
|
|
else
|
|
pSector_WriteData = NULL;
|
|
|
|
if ( Sector_ReadTime == 0 ) /* Sector has a standard delay (32 us per byte) */
|
|
Sector_ReadTime = 32 * pStxSector->SectorSize; /* Use the real standard value instead of 0 */
|
|
Sector_ReadTime *= 8; /* Convert delay in us to a number of FDC cycles at 8 MHz */
|
|
|
|
Total_prev = 0;
|
|
for ( i=0 ; i<pStxSector->SectorSize ; i++ )
|
|
{
|
|
/* Get the value of each byte, with possible fuzzy bits */
|
|
if ( pSector_WriteData == NULL ) /* Use original STX content */
|
|
{
|
|
Byte = pStxSector->pData[ i ];
|
|
if ( pStxSector->pFuzzyData )
|
|
Byte = ( Byte & pStxSector->pFuzzyData[ i ] ) | ( rand() & ~pStxSector->pFuzzyData[ i ] );
|
|
}
|
|
|
|
else /* Use data from 'write sector' */
|
|
Byte = pSector_WriteData[ i ];
|
|
|
|
/* Compute the timing in FDC cycles to transfer this byte */
|
|
if ( ( pStxSector->pTimingData ) /* Specific timing for each block of 16 bytes */
|
|
&& ( pSector_WriteData == NULL ) )
|
|
{
|
|
Timing = ( pStxSector->pTimingData[ ( i>>4 ) * 2 ] << 8 )
|
|
+ pStxSector->pTimingData[ ( i>>4 ) * 2 + 1 ]; /* Get big endian timing for this block of 16 bytes */
|
|
|
|
/* [NP] Formula to convert timing data comes from Pasti.prg 0.4b : */
|
|
/* 1 unit of timing = 32 FDC cycles at 8 MHz + 28 cycles to complete each block of 16 bytes */
|
|
Timing = Timing * 32 + 28;
|
|
|
|
if ( i % 16 == 0 ) Total_prev = 0; /* New block of 16 bytes */
|
|
Total_cur = ( (double)Timing * ( ( i % 16 ) + 1 ) ) / 16;
|
|
Timing = rint ( Total_cur - Total_prev );
|
|
Total_prev += Timing;
|
|
}
|
|
else /* Specific timing in us for the whole sector */
|
|
{
|
|
Total_cur = ( (double)Sector_ReadTime * ( i+1 ) ) / pStxSector->SectorSize;
|
|
Timing = rint ( Total_cur - Total_prev );
|
|
Total_prev += Timing;
|
|
}
|
|
|
|
/* Add the Byte to the buffer, Timing should be a number of FDC cycles at 8 MHz */
|
|
FDC_Buffer_Add_Timing ( Byte , Timing );
|
|
}
|
|
|
|
/* Return only bits 3 and 5 of the FDC_Status */
|
|
return pStxSector->FDC_Status & ( STX_SECTOR_FLAG_CRC | STX_SECTOR_FLAG_RECORD_TYPE );
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Write a sector to a floppy image in STX format (used in type II command)
|
|
*
|
|
* STX format doesn't support write command. For each 'write sector' we
|
|
* store the sector data in a dedicated buffer STX_SaveStruct[].pSaveSectorsStruct.
|
|
* When the sector is read later, we return the data from STX_SaveStruct[].pSaveSectorsStruct
|
|
* instead of returning the data from the original STX file.
|
|
*
|
|
* We only allow writing for sectors whose ID field has a correct CRC and
|
|
* where RNF is not set.
|
|
* Any valid size can be written : 128, 256, 512 or 1024 bytes
|
|
*
|
|
* NOTE : data will saved in memory snapshot, as well as in an additional
|
|
* file with the extension .wd1772.
|
|
*
|
|
* Return RNF if sector was not found or CRC if ID field has a CRC error.
|
|
* Return 0 if OK.
|
|
*/
|
|
Uint8 FDC_WriteSector_STX ( Uint8 Drive , Uint8 Track , Uint8 Sector , Uint8 Side , int SectorSize )
|
|
{
|
|
STX_SECTOR_STRUCT *pStxSector;
|
|
int i;
|
|
Uint8 *pSector_WriteData;
|
|
void *pNewBuf;
|
|
STX_SAVE_SECTOR_STRUCT *pStxSaveSector;
|
|
|
|
pStxSector = STX_FindSector ( Drive , Track , Side , STX_State.NextSectorStruct_Nbr );
|
|
if ( pStxSector == NULL )
|
|
{
|
|
fprintf ( stderr , "FDC_WriteSector_STX drive=%d track=%d side=%d sector=%d returns null !\n" ,
|
|
Drive , Track , Side , STX_State.NextSectorStruct_Nbr );
|
|
return STX_SECTOR_FLAG_RNF; /* Should not happen if FDC_NextSectorID_FdcCycles_STX succeeded before */
|
|
}
|
|
|
|
/* If RNF is set, return FDC_STR_BIT_RNF */
|
|
if ( pStxSector->FDC_Status & STX_SECTOR_FLAG_RNF )
|
|
return STX_SECTOR_FLAG_RNF; /* RNF in FDC's status register */
|
|
|
|
/* If CRC is set, return FDC_STR_BIT_RNF */
|
|
if ( pStxSector->FDC_Status & STX_SECTOR_FLAG_CRC )
|
|
return STX_SECTOR_FLAG_CRC; /* CRC in FDC's status register */
|
|
|
|
|
|
/* Check if this sector was already changed by a 'write sector' command */
|
|
/* If so, we use the same buffer. Else we alloc a new buffer for this sector */
|
|
if ( pStxSector->SaveSectorIndex < 0 )
|
|
{
|
|
//fprintf ( stderr , "realloc\n" );
|
|
/* Increase save buffer by 1 */
|
|
pNewBuf = realloc ( STX_SaveStruct[ Drive ].pSaveSectorsStruct ,
|
|
( STX_SaveStruct[ Drive ].SaveSectorsCount + 1 ) * sizeof ( STX_SAVE_SECTOR_STRUCT ) );
|
|
if ( pNewBuf == NULL )
|
|
{
|
|
fprintf ( stderr , "FDC_WriteSector_STX drive=%d track=%d side=%d sector=%d realloc error !\n" ,
|
|
Drive , Track , Side , STX_State.NextSectorStruct_Nbr );
|
|
return STX_SECTOR_FLAG_RNF;
|
|
}
|
|
|
|
/* Save the new buffer values */
|
|
STX_SaveStruct[ Drive ].pSaveSectorsStruct = (STX_SAVE_SECTOR_STRUCT *) pNewBuf;;
|
|
STX_SaveStruct[ Drive ].SaveSectorsCount++;
|
|
|
|
/* Create the new entry in pSaveSectorsStruct */
|
|
pNewBuf = malloc ( SectorSize );
|
|
if ( pNewBuf == NULL )
|
|
{
|
|
fprintf ( stderr , "FDC_WriteSector_STX drive=%d track=%d side=%d sector=%d malloc error !\n" ,
|
|
Drive , Track , Side , STX_State.NextSectorStruct_Nbr );
|
|
return STX_SECTOR_FLAG_RNF;
|
|
}
|
|
|
|
pStxSector->SaveSectorIndex = STX_SaveStruct[ Drive ].SaveSectorsCount - 1;
|
|
|
|
/* Fill the new SaveSectorStruct. We copy some of the original sector's values */
|
|
/* in the saved sector */
|
|
pStxSaveSector = &STX_SaveStruct[ Drive ].pSaveSectorsStruct[ pStxSector->SaveSectorIndex ];
|
|
|
|
pStxSaveSector->Track = Track;
|
|
pStxSaveSector->Side = Side;
|
|
pStxSaveSector->BitPosition = pStxSector->BitPosition;
|
|
pStxSaveSector->ID_Track = pStxSector->ID_Track;
|
|
pStxSaveSector->ID_Head = pStxSector->ID_Head;
|
|
pStxSaveSector->ID_Sector = pStxSector->ID_Sector;
|
|
pStxSaveSector->ID_Size = pStxSector->ID_Size;
|
|
pStxSaveSector->ID_CRC = pStxSector->ID_CRC;
|
|
|
|
pStxSaveSector->SectorSize = SectorSize;
|
|
pStxSaveSector->pData = (Uint8 *) pNewBuf;
|
|
|
|
pStxSaveSector->StructIsUsed = 1;
|
|
}
|
|
|
|
pSector_WriteData = STX_SaveStruct[ Drive ].pSaveSectorsStruct[ pStxSector->SaveSectorIndex ].pData;
|
|
|
|
/* Get the sector's data (ignore timings) */
|
|
for ( i=0 ; i<SectorSize ; i++ )
|
|
pSector_WriteData[ i ] = FDC_Buffer_Read_Byte_pos ( i );
|
|
|
|
//fprintf ( stderr , "write drive=%d track=%d side=%d sector=%d size=%d index=%d\n", Drive, Track, Side, Sector, SectorSize , pStxSector->SaveSectorIndex );
|
|
//Str_Dump_Hex_Ascii ( (char *) pSector_WriteData, SectorSize, 16, "" , stderr );
|
|
|
|
/* Warn that 'write sector' data will be lost or saved (if zipped or not) */
|
|
if ( STX_State.ImageBuffer[ Drive ]->WarnedWriteSector == false )
|
|
{
|
|
if ( File_DoesFileExtensionMatch ( EmulationDrives[ Drive ].sFileName , ".zip" ) )
|
|
Log_AlertDlg ( LOG_INFO , "WARNING : can't save changes made with 'write sector' to an STX disk inside a zip file" );
|
|
else
|
|
Log_AlertDlg ( LOG_INFO , "Changes made with 'write sector' to an STX disk will be saved into an additional .wd1772 file" );
|
|
STX_State.ImageBuffer[ Drive ]->WarnedWriteSector = true;
|
|
}
|
|
|
|
|
|
/* No error */
|
|
EmulationDrives[Drive].bContentsChanged = true;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read an address field from a floppy image in STX format (used in type III command)
|
|
* We return the address field NextSectorStruct_Nbr, whose value was set
|
|
* by the latest call to FDC_NextSectorID_FdcCycles_STX
|
|
* Each byte of the ID field is added to the FDC buffer with a default timing
|
|
* (32 microsec)
|
|
* Return 0 if OK, or a CRC error
|
|
*/
|
|
Uint8 FDC_ReadAddress_STX ( Uint8 Drive , Uint8 Track , Uint8 Sector , Uint8 Side )
|
|
{
|
|
STX_SECTOR_STRUCT *pStxSector;
|
|
|
|
pStxSector = STX_FindSector ( Drive , Track , Side , STX_State.NextSectorStruct_Nbr );
|
|
if ( pStxSector == NULL )
|
|
{
|
|
fprintf ( stderr , "FDC_ReadAddress_STX drive=%d track=%d side=%d sector=%d returns null !\n" ,
|
|
Drive , Track , Side , STX_State.NextSectorStruct_Nbr );
|
|
return STX_SECTOR_FLAG_RNF; /* Should not happen if FDC_NextSectorID_FdcCycles_STX succeeded before */
|
|
}
|
|
|
|
FDC_Buffer_Add ( pStxSector->ID_Track );
|
|
FDC_Buffer_Add ( pStxSector->ID_Head );
|
|
FDC_Buffer_Add ( pStxSector->ID_Sector );
|
|
FDC_Buffer_Add ( pStxSector->ID_Size );
|
|
FDC_Buffer_Add ( pStxSector->ID_CRC >> 8 );
|
|
FDC_Buffer_Add ( pStxSector->ID_CRC & 0xff );
|
|
|
|
/* If RNF is set and CRC error is set, then this ID field has a CRC error */
|
|
if ( ( pStxSector->FDC_Status & STX_SECTOR_FLAG_RNF ) && ( pStxSector->FDC_Status & STX_SECTOR_FLAG_CRC ) )
|
|
return STX_SECTOR_FLAG_CRC;
|
|
|
|
return 0; /* No error */
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read a track from a floppy image in STX format (used in type III command)
|
|
* This function is called after an index pulse was encountered, and it will
|
|
* always succeeds and fill the track buffer.
|
|
* If the Track/Side infos exist in the STX image, then the corresponding
|
|
* bytes from the track's image are returned.
|
|
* If these Track/Side infos don't exist, we return some random bytes
|
|
* (empty / not formatted track).
|
|
* If the Track/Side infos exist but there's no track's image, then we build
|
|
* a standard track by using the available sectors and standard GAP values.
|
|
* Return 0 if OK
|
|
*/
|
|
Uint8 FDC_ReadTrack_STX ( Uint8 Drive , Uint8 Track , Uint8 Side )
|
|
{
|
|
STX_TRACK_STRUCT *pStxTrack;
|
|
STX_SECTOR_STRUCT *pStxSector;
|
|
int i;
|
|
Uint16 Timing;
|
|
Uint32 Track_ReadTime;
|
|
double Total_cur; /* To compute closest integer timings for each byte */
|
|
double Total_prev;
|
|
int TrackSize;
|
|
int Sector;
|
|
int SectorSize;
|
|
Uint16 CRC;
|
|
Uint8 *pData;
|
|
Uint8 Byte;
|
|
|
|
if ( STX_State.ImageBuffer[ Drive ] == NULL )
|
|
{
|
|
fprintf ( stderr , "FDC_ReadTrack_STX drive=%d track=%d side=%d, no image buffer !\n" , Drive , Track , Side );
|
|
return STX_SECTOR_FLAG_RNF; /* Should not happen, just in case of a bug */
|
|
}
|
|
|
|
pStxTrack = STX_FindTrack ( Drive , Track , Side );
|
|
if ( pStxTrack == NULL ) /* Track/Side don't exist in this STX image */
|
|
{
|
|
fprintf ( stderr , "fdc stx : track info not found for read track drive=%d track=%d side=%d, returning random bytes\n" , Drive , Track , Side );
|
|
for ( i=0 ; i<FDC_GetBytesPerTrack ( Drive ) ; i++ )
|
|
FDC_Buffer_Add ( rand() & 0xff ); /* Fill the track buffer with random bytes */
|
|
return 0;
|
|
}
|
|
|
|
/* If the Track block contains a complete dump of the track image, use it directly */
|
|
/* The timing for each byte is the average timing based on TrackImageSize */
|
|
if ( pStxTrack->pTrackImageData )
|
|
{
|
|
Track_ReadTime = 8000000 / 5; /* 300 RPM, gives 5 RPS and 1600000 cycles per revolution at 8 MHz */
|
|
Total_prev = 0;
|
|
for ( i=0 ; i<pStxTrack->TrackImageSize ; i++ )
|
|
{
|
|
Total_cur = ( (double)Track_ReadTime * ( i+1 ) ) / pStxTrack->TrackImageSize;
|
|
Timing = rint ( Total_cur - Total_prev );
|
|
Total_prev += Timing;
|
|
/* Add each byte to the buffer, Timing should be a number of FDC cycles at 8 MHz */
|
|
FDC_Buffer_Add_Timing ( pStxTrack->pTrackImageData[ i ] , Timing );
|
|
}
|
|
}
|
|
|
|
/* If the track block doesn't contain a dump of the track image, we must build a track */
|
|
/* using the sector blocks and some standard GAP values */
|
|
/* [NP] NOTE : we build a track of pStxTrack->MFMSize bytes, as this seems to always be != 0 */
|
|
/* even for empty / not formatted track */
|
|
/* [NP] NOTE : instead of using standard GAP values, we could compute GAP based on pStxSector->BitPosition */
|
|
/* but this seems unnecessary, as a track image would certainly be present if precise GAP values */
|
|
/* were required */
|
|
else
|
|
{
|
|
TrackSize = pStxTrack->MFMSize;
|
|
if ( ( pStxTrack->Flags & STX_TRACK_FLAG_SECTOR_BLOCK ) == 0 )
|
|
TrackSize /= 8; /* When the track contains only sector data, MFMSize is in bits */
|
|
|
|
/* If there's no image for this track, and no sector as well, then track is empty / not formatted */
|
|
if ( pStxTrack->SectorsCount == 0 )
|
|
{
|
|
fprintf ( stderr , "fdc stx : no track image and no sector for read track drive=%d track=%d side=%d, building an unformatted track\n" , Drive , Track , Side );
|
|
for ( i=0 ; i<TrackSize ; i++ )
|
|
FDC_Buffer_Add ( rand() & 0xff ); /* Fill the track buffer with random bytes */
|
|
return 0;
|
|
}
|
|
|
|
/* Use the available sectors and add some default GAPs to build the track */
|
|
fprintf ( stderr , "fdc stx : no track image for read track drive=%d track=%d side=%d, building a standard track\n" , Drive , Track , Side );
|
|
|
|
for ( i=0 ; i<FDC_TRACK_LAYOUT_STANDARD_GAP1 ; i++ ) /* GAP1 */
|
|
FDC_Buffer_Add ( 0x4e );
|
|
|
|
for ( Sector=0 ; Sector < pStxTrack->SectorsCount ; Sector++ )
|
|
{
|
|
pStxSector = &(pStxTrack->pSectorsStruct[ Sector ]);
|
|
SectorSize = pStxSector->SectorSize;
|
|
|
|
/* Check that the data+GAPs for this sector will not be above track's length */
|
|
/* (in case we build a track with a high / non standard number of sectors) */
|
|
if ( FDC_Buffer_Get_Size () + SectorSize + FDC_TRACK_LAYOUT_STANDARD_GAP2 + 10 + FDC_TRACK_LAYOUT_STANDARD_GAP3a
|
|
+ FDC_TRACK_LAYOUT_STANDARD_GAP3b + 4 + 2 + FDC_TRACK_LAYOUT_STANDARD_GAP4 >= TrackSize )
|
|
{
|
|
fprintf ( stderr , "fdc stx : no track image for read track drive=%d track=%d side=%d, too many data sector=%d\n" , Drive , Track , Side , Sector );
|
|
break; /* Exit the loop and fill the rest of the track */
|
|
}
|
|
|
|
for ( i=0 ; i<FDC_TRACK_LAYOUT_STANDARD_GAP2 ; i++ ) /* GAP2 */
|
|
FDC_Buffer_Add ( 0x00 );
|
|
|
|
/* Add the ID field for the sector */
|
|
for ( i=0 ; i<3 ; i++ )
|
|
FDC_Buffer_Add ( 0xa1 ); /* SYNC (write $F5) */
|
|
FDC_Buffer_Add ( 0xfe ); /* Index Address Mark */
|
|
FDC_Buffer_Add ( pStxSector->ID_Track );
|
|
FDC_Buffer_Add ( pStxSector->ID_Head );
|
|
FDC_Buffer_Add ( pStxSector->ID_Sector );
|
|
FDC_Buffer_Add ( pStxSector->ID_Size );
|
|
FDC_Buffer_Add ( pStxSector->ID_CRC >> 8 );
|
|
FDC_Buffer_Add ( pStxSector->ID_CRC & 0xff );
|
|
|
|
for ( i=0 ; i<FDC_TRACK_LAYOUT_STANDARD_GAP3a ; i++ ) /* GAP3a */
|
|
FDC_Buffer_Add ( 0x4e );
|
|
for ( i=0 ; i<FDC_TRACK_LAYOUT_STANDARD_GAP3b ; i++ ) /* GAP3b */
|
|
FDC_Buffer_Add ( 0x00 );
|
|
|
|
/* Add the data for the sector + build the CRC */
|
|
crc16_reset ( &CRC );
|
|
for ( i=0 ; i<3 ; i++ )
|
|
{
|
|
FDC_Buffer_Add ( 0xa1 ); /* SYNC (write $F5) */
|
|
crc16_add_byte ( &CRC , 0xa1 );
|
|
}
|
|
|
|
FDC_Buffer_Add ( 0xfb ); /* Data Address Mark */
|
|
crc16_add_byte ( &CRC , 0xfb );
|
|
|
|
/* [NP] NOTE : when building the sector, we assume there's no specific timing or fuzzy bytes */
|
|
/* If it was not the case, there would certainly be a real track image (and STX format doesn't */
|
|
/* support fuzzy bytes or specific timing for a track image anyway) */
|
|
/* If the sector was changed by a 'write sector' command, we use the data from pSaveSectorsStruct */
|
|
if ( pStxSector->SaveSectorIndex < 0 ) /* Use original data from the STX */
|
|
pData = pStxSector->pData;
|
|
else /* Use data from the 'write sector' */
|
|
pData = STX_SaveStruct[ Drive ].pSaveSectorsStruct[ pStxSector->SaveSectorIndex ].pData;
|
|
|
|
for ( i=0 ; i<SectorSize ; i++ )
|
|
{
|
|
Byte = pData[ i ];
|
|
FDC_Buffer_Add ( Byte );
|
|
crc16_add_byte ( &CRC , Byte );
|
|
}
|
|
|
|
FDC_Buffer_Add ( CRC >> 8 ); /* CRC1 (write $F7) */
|
|
FDC_Buffer_Add ( CRC & 0xff ); /* CRC2 */
|
|
|
|
for ( i=0 ; i<FDC_TRACK_LAYOUT_STANDARD_GAP4 ; i++ ) /* GAP4 */
|
|
FDC_Buffer_Add ( 0x4e );
|
|
}
|
|
|
|
while ( FDC_Buffer_Get_Size () < TrackSize ) /* Complete the track buffer */
|
|
FDC_Buffer_Add ( 0x4e ); /* GAP5 */
|
|
}
|
|
|
|
return 0; /* No error */
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Write a track to a floppy image in STX format (used in type III command)
|
|
*
|
|
* STX format doesn't support write command. For each 'write track' we
|
|
* store the track data in a dedicated buffer STX_SaveStruct[].pSaveTracksStruct.
|
|
* When the track is read later, we return the data from STX_SaveStruct[].pSaveTracksStruct
|
|
* instead of returning the data from the original STX file.
|
|
*
|
|
* NOTE : data will saved in memory snapshot, as well as in an additional
|
|
* file with the extension .wd1772.
|
|
*
|
|
* Return 0 if track was written without error, or LOST_DATA if an error occurred
|
|
*/
|
|
Uint8 FDC_WriteTrack_STX ( Uint8 Drive , Uint8 Track , Uint8 Side , int TrackSize )
|
|
{
|
|
STX_TRACK_STRUCT *pStxTrack;
|
|
int i;
|
|
Uint8 *pTrack_DataWrite;
|
|
void *pNewBuf;
|
|
STX_SAVE_TRACK_STRUCT *pStxSaveTrack;
|
|
int Sector;
|
|
|
|
pStxTrack = STX_FindTrack ( Drive , Track , Side );
|
|
if ( pStxTrack == NULL )
|
|
{
|
|
fprintf ( stderr , "FDC_WriteTrack_STX drive=%d track=%d side=%d returns null !\n" ,
|
|
Drive , Track , Side );
|
|
return STX_SECTOR_FLAG_LOST_DATA;
|
|
}
|
|
|
|
/* Check if this track was already changed by a 'write track' command */
|
|
/* If so, we use the same structure. Else we alloc a new structure for this track */
|
|
if ( pStxTrack->SaveTrackIndex < 0 )
|
|
{
|
|
//fprintf ( stderr , "realloc\n" );
|
|
/* Increase save buffer by 1 */
|
|
pNewBuf = realloc ( STX_SaveStruct[ Drive ].pSaveTracksStruct ,
|
|
( STX_SaveStruct[ Drive ].SaveTracksCount + 1 ) * sizeof ( STX_SAVE_TRACK_STRUCT ) );
|
|
if ( pNewBuf == NULL )
|
|
{
|
|
fprintf ( stderr , "FDC_WriteTrack_STX drive=%d track=%d side=%d realloc error !\n" ,
|
|
Drive , Track , Side );
|
|
return STX_SECTOR_FLAG_LOST_DATA;
|
|
}
|
|
|
|
/* Save the new buffer values */
|
|
STX_SaveStruct[ Drive ].pSaveTracksStruct = (STX_SAVE_TRACK_STRUCT *) pNewBuf;;
|
|
STX_SaveStruct[ Drive ].SaveTracksCount++;
|
|
|
|
pStxTrack->SaveTrackIndex = STX_SaveStruct[ Drive ].SaveTracksCount - 1;
|
|
}
|
|
|
|
/* Use the same structure : free previous DataWrite buffer */
|
|
else
|
|
{
|
|
free ( STX_SaveStruct[ Drive ].pSaveTracksStruct[ pStxTrack->SaveTrackIndex ].pDataWrite );
|
|
STX_SaveStruct[ Drive ].pSaveTracksStruct[ pStxTrack->SaveTrackIndex ].pDataWrite = NULL;
|
|
/* TODO : also free pDataRead */
|
|
}
|
|
|
|
/* Create the new DataWrite buffer in pSaveTracksStruct */
|
|
pNewBuf = malloc ( TrackSize );
|
|
if ( pNewBuf == NULL )
|
|
{
|
|
fprintf ( stderr , "FDC_WriteTrack_STX drive=%d track=%d side=%d malloc error !\n" ,
|
|
Drive , Track , Side );
|
|
return STX_SECTOR_FLAG_LOST_DATA;
|
|
}
|
|
|
|
/* Fill the new SaveTrackStruct */
|
|
pStxSaveTrack = &STX_SaveStruct[ Drive ].pSaveTracksStruct[ pStxTrack->SaveTrackIndex ];
|
|
|
|
pStxSaveTrack->Track = Track;
|
|
pStxSaveTrack->Side = Side;
|
|
|
|
pStxSaveTrack->TrackSizeWrite = TrackSize;
|
|
pStxSaveTrack->pDataWrite = (Uint8 *) pNewBuf;
|
|
|
|
|
|
/* Get the track's data (ignore timings) */
|
|
pTrack_DataWrite = STX_SaveStruct[ Drive ].pSaveTracksStruct[ pStxTrack->SaveTrackIndex ].pDataWrite;
|
|
|
|
for ( i=0 ; i<pStxSaveTrack->TrackSizeWrite ; i++ )
|
|
pTrack_DataWrite[ i ] = FDC_Buffer_Read_Byte_pos ( i );
|
|
|
|
//fprintf ( stderr , "write drive=%d track=%d side=%d size=%d index=%d\n", Drive, Track, Side, pStxSaveTrack->TrackSizeWrite , pStxTrack->SaveTrackIndex );
|
|
//Str_Dump_Hex_Ascii ( (char *) pTrack_DataWrite, pStxSaveTrack->TrackSizeWrite, 16, "" , stderr );
|
|
|
|
// TODO : convert pDataWrite into pDataRead
|
|
pStxSaveTrack->TrackSizeRead = 0; /* TODO : compute interpreted track */
|
|
pStxSaveTrack->pDataRead = NULL; /* TODO : compute interpreted track */
|
|
|
|
|
|
/* If some sectors were already saved for that track, we must remove them */
|
|
/* as the 'write track' takes precedence over the previous 'write sector' */
|
|
for ( Sector=0 ; Sector < pStxTrack->SectorsCount ; Sector++ )
|
|
{
|
|
if ( pStxTrack->pSectorsStruct[ Sector ].SaveSectorIndex >= 0 )
|
|
{
|
|
STX_FreeSaveSectorsStruct ( STX_SaveStruct[ Drive ].pSaveSectorsStruct ,
|
|
pStxTrack->pSectorsStruct[ Sector ].SaveSectorIndex );
|
|
pStxTrack->pSectorsStruct[ Sector ].SaveSectorIndex = -1;
|
|
}
|
|
}
|
|
|
|
|
|
/* Warn that 'write track' data will be lost or saved (if zipped or not) */
|
|
if ( STX_State.ImageBuffer[ Drive ]->WarnedWriteTrack == false )
|
|
{
|
|
if ( File_DoesFileExtensionMatch ( EmulationDrives[ Drive ].sFileName , ".zip" ) )
|
|
Log_AlertDlg ( LOG_INFO , "WARNING : can't save changes made with 'write track' to an STX disk inside a zip file" );
|
|
else
|
|
Log_AlertDlg ( LOG_INFO , "Changes made with 'write track' to an STX disk will be saved into an additional .wd1772 file" );
|
|
STX_State.ImageBuffer[ Drive ]->WarnedWriteTrack = true;
|
|
}
|
|
|
|
|
|
/* No error */
|
|
EmulationDrives[Drive].bContentsChanged = true;
|
|
return 0;
|
|
}
|