mirror of
https://github.com/Michaelangel007/apple2_prodos_utils.git
synced 2024-12-29 18:29:31 +00:00
332 lines
8.1 KiB
C++
332 lines
8.1 KiB
C++
#define DEBUG_DSK 0
|
|
#define DEBUG_DSK_LOAD 0
|
|
#define DEBUG_DSK_SAVE 0
|
|
#define DEBUG_DSK_INTERLEAVE 0
|
|
|
|
#ifdef __WIN32
|
|
#else
|
|
// OSX / BSD / Linux
|
|
#define stricmp strcasecmp
|
|
#endif
|
|
|
|
// --- Buffer ---
|
|
|
|
// 5 1/4" 140K (single sided)
|
|
const size_t DSK_SIZE_514 = 256 * 16 * 35; // 143,360
|
|
|
|
// 3 1/2" = 800K (double sided)
|
|
const size_t DSK_SIZE_312 = 2 * 400 * 1024; // 819,200
|
|
|
|
// 32 MB Hard Disk
|
|
const size_t DSK_SIZE_32M = 32 * 1024 * 1024;
|
|
|
|
const int DSK_SECTOR_SIZE = 256;
|
|
|
|
|
|
size_t gnDskSize = 0;
|
|
uint8_t gaDsk[ DSK_SIZE_32M ];
|
|
|
|
uint16_t DskGet16( int offset )
|
|
{
|
|
uint16_t n = 0
|
|
| (((uint16_t)gaDsk[ offset + 0 ]) << 0)
|
|
| (((uint16_t)gaDsk[ offset + 1 ]) << 8)
|
|
;
|
|
return n;
|
|
}
|
|
|
|
uint32_t DskGet24( int offset )
|
|
{
|
|
uint32_t n = 0
|
|
| (((uint32_t)gaDsk[ offset + 0 ]) << 0)
|
|
| (((uint32_t)gaDsk[ offset + 1 ]) << 8)
|
|
| (((uint32_t)gaDsk[ offset + 2 ]) << 16)
|
|
;
|
|
return n;
|
|
}
|
|
|
|
void DskPut16( int offset, int val )
|
|
{
|
|
gaDsk[ offset + 0 ] = (val >> 0) & 0xFF;
|
|
gaDsk[ offset + 1 ] = (val >> 8) & 0xFF;
|
|
}
|
|
|
|
void DskPut24( int offset, int val )
|
|
{
|
|
gaDsk[ offset + 0 ] = (val >> 0) & 0xFF;
|
|
gaDsk[ offset + 1 ] = (val >> 8) & 0xFF;
|
|
gaDsk[ offset + 2 ] = (val >>16) & 0xFF;
|
|
}
|
|
|
|
|
|
// --- Name ---
|
|
|
|
const char *gpDskName = NULL;
|
|
|
|
|
|
// --- Sector Interleave ---
|
|
|
|
enum SectorOrder_e
|
|
{
|
|
INTERLEAVE_AUTO_DETECT = 0,
|
|
INTERLEAVE_DOS33_ORDER,
|
|
INTERLEAVE_PRODOS_ORDER
|
|
};
|
|
int giInterleaveLastUsed = INTERLEAVE_AUTO_DETECT;
|
|
|
|
// Map Physical <-> Logical
|
|
const uint8_t gaInterleave_DSK[ 16 ] =
|
|
{
|
|
//0 1 2 3 4 5 6 7 8 9 A B C D E F // logical order
|
|
0x0,0xE,0xD,0xC,0xB,0xA,0x9,0x8,0x7,0x6,0x5,0x4,0x3,0x2,0x1,0xF // physical order
|
|
};
|
|
|
|
void Interleave_Forward( int iSector, size_t *src_, size_t *dst_ )
|
|
{
|
|
*src_ = gaInterleave_DSK[ iSector ]*DSK_SECTOR_SIZE;
|
|
*dst_ = iSector *DSK_SECTOR_SIZE; // linearize
|
|
};
|
|
void Interleave_Reverse( int iSector, size_t *src_, size_t *dst_ )
|
|
{
|
|
*src_ = iSector *DSK_SECTOR_SIZE; // un-linearize
|
|
*dst_ = gaInterleave_DSK[ iSector ]*DSK_SECTOR_SIZE;
|
|
}
|
|
|
|
|
|
// --- Size ---
|
|
|
|
size_t File_Size( FILE *pFile )
|
|
{
|
|
fseek( pFile, 0L, SEEK_END );
|
|
size_t size = ftell( pFile );
|
|
fseek( pFile, 0L, SEEK_SET );
|
|
|
|
return size;
|
|
}
|
|
|
|
int DSK_GetNumTracks()
|
|
{
|
|
return gnDskSize / (16 * DSK_SECTOR_SIZE);
|
|
}
|
|
|
|
|
|
// @returns true if able to determine interleave
|
|
// ========================================================================
|
|
bool DskGetInterleave( const char *dsk_filename )
|
|
{
|
|
if( !dsk_filename )
|
|
return false;
|
|
|
|
// .do = DOS
|
|
// .po = PRO
|
|
// .dsk = DOS
|
|
const size_t nLen = strlen( dsk_filename );
|
|
giInterleaveLastUsed = INTERLEAVE_AUTO_DETECT;
|
|
|
|
if( nLen < 1 )
|
|
return false;
|
|
|
|
if( giInterleaveLastUsed == INTERLEAVE_AUTO_DETECT )
|
|
{
|
|
const char *pExt = file_GetExtension( dsk_filename );
|
|
if( pExt )
|
|
{
|
|
if( stricmp( pExt, ".do" ) == 0 ) giInterleaveLastUsed = INTERLEAVE_DOS33_ORDER ;
|
|
if( stricmp( pExt, ".po" ) == 0 ) giInterleaveLastUsed = INTERLEAVE_PRODOS_ORDER;
|
|
if( stricmp( pExt, ".dsk") == 0 ) giInterleaveLastUsed = INTERLEAVE_DOS33_ORDER ;
|
|
}
|
|
|
|
#if DEBUG_DSK_INTERLEAVE
|
|
printf( "DskLoad() auto-detect interleave: " );
|
|
if( giInterleaveLastUsed == INTERLEAVE_DOS33_ORDER ) printf( "DOS3.3 \n" );
|
|
if( giInterleaveLastUsed == INTERLEAVE_PRODOS_ORDER) printf( "ProDOS \n" );
|
|
if( giInterleaveLastUsed == INTERLEAVE_AUTO_DETECT ) printf( "unknown\n" );
|
|
#endif
|
|
}
|
|
|
|
#if 0
|
|
if( giInterleaveLastUsed == INTERLEAVE_AUTO_DETECT )
|
|
{
|
|
printf( "WARNING: Defaulting to DOS 3.3 sector interleave\n" );
|
|
giInterleaveLastUsed = INTERLEAVE_DOS33_ORDER;
|
|
}
|
|
#endif
|
|
|
|
return giInterleaveLastUsed != INTERLEAVE_AUTO_DETECT;
|
|
}
|
|
|
|
|
|
// @return true = OK, false = ERROR
|
|
// ========================================================================
|
|
bool DskLoad( const char *dsk_filename, SectorOrder_e sector_order )
|
|
{
|
|
if( !dsk_filename )
|
|
return false;
|
|
|
|
int valid = 0;
|
|
|
|
FILE *pFile = fopen( dsk_filename, "rb" );
|
|
if( pFile )
|
|
{
|
|
gnDskSize = File_Size( pFile );
|
|
|
|
#if DEBUG_DSK_LOAD
|
|
printf( "==========\n" );
|
|
printf( "gnDskSize: %06X\n", gnDskSize );
|
|
printf( "==========\n" );
|
|
#endif
|
|
|
|
if( gnDskSize > DSK_SIZE_32M )
|
|
{
|
|
printf( "ERROR: Disk size %s > %s !\n", itoa_comma( gnDskSize ), itoa_comma( DSK_SIZE_32M ) );
|
|
gnDskSize = 0;
|
|
valid = -1;
|
|
}
|
|
else
|
|
{
|
|
if( giInterleaveLastUsed == INTERLEAVE_PRODOS_ORDER )
|
|
fread( gaDsk, 1, gnDskSize, pFile );
|
|
else
|
|
if( giInterleaveLastUsed == INTERLEAVE_DOS33_ORDER )
|
|
{
|
|
int nTracks = DSK_GetNumTracks();
|
|
size_t offset = 0;
|
|
|
|
#if DEBUG_DSK_LOAD
|
|
printf( "Tracks: %d\n", nTracks );
|
|
#endif
|
|
|
|
for( int iTracks = 0; iTracks < nTracks; iTracks++ )
|
|
{
|
|
for( int iSector = 0; iSector < 16; iSector++ )
|
|
{
|
|
size_t src;
|
|
size_t dst;
|
|
Interleave_Forward( iSector, &src, &dst );
|
|
|
|
#if DEBUG_DSK_LOAD
|
|
if( iTracks == 0 )
|
|
printf( "T0S%X -> %06X\n", iSector, src );
|
|
#endif
|
|
fseek( pFile, offset + src, SEEK_SET );
|
|
fread( &gaDsk[ offset + dst ], 1, DSK_SECTOR_SIZE, pFile );
|
|
}
|
|
|
|
offset += 16 * DSK_SECTOR_SIZE;
|
|
}
|
|
}
|
|
valid = 1;
|
|
}
|
|
|
|
fclose( pFile );
|
|
};
|
|
|
|
#if DEBUG_DSK_LOAD
|
|
printf( "DEBUG: DISK: Read Valid: %d\n", valid );
|
|
#endif
|
|
|
|
return (valid == 1);
|
|
}
|
|
|
|
|
|
// ========================================================================
|
|
bool DskSave()
|
|
{
|
|
FILE *pFile = fopen( gpDskName, "w+b" );
|
|
|
|
if( !pFile )
|
|
{
|
|
printf( "ERROR: Couldn't write to: %s\n", gpDskName );
|
|
}
|
|
|
|
fseek( pFile, 0L, SEEK_SET );
|
|
|
|
if( giInterleaveLastUsed == INTERLEAVE_PRODOS_ORDER )
|
|
{
|
|
int wrote = fwrite( gaDsk, 1, gnDskSize, pFile );
|
|
(void) wrote;
|
|
#if DEBUG_DSK_SAVE
|
|
printf( "DskSave() wrote .po: %d\n", wrote );
|
|
#endif
|
|
}
|
|
else
|
|
if( giInterleaveLastUsed == INTERLEAVE_DOS33_ORDER )
|
|
{
|
|
int nTracks = DSK_GetNumTracks();
|
|
size_t offset = 0;
|
|
|
|
#if DEBUG_DSK_SAVE
|
|
printf( "Tracks: %d\n", nTracks );
|
|
#endif
|
|
|
|
for( int iTracks = 0; iTracks < nTracks; iTracks++ )
|
|
{
|
|
size_t dst = offset;
|
|
|
|
for( int iSector = 0; iSector < 16; iSector++ )
|
|
{
|
|
size_t src;
|
|
size_t dst;
|
|
Interleave_Reverse( iSector, &src, &dst );
|
|
|
|
#if DEBUG_DSK_SAVE
|
|
if( iTracks == 0 )
|
|
printf( "%06X -> T0S%X\n", dst, iSector );
|
|
#endif
|
|
|
|
fseek( pFile, offset + dst, SEEK_SET );
|
|
fwrite( &gaDsk[ offset + src ], 1, DSK_SECTOR_SIZE, pFile );
|
|
}
|
|
|
|
offset += 16 * DSK_SECTOR_SIZE;
|
|
}
|
|
}
|
|
|
|
fclose( pFile );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
// ========================================================================
|
|
void DskDump( int start, int end )
|
|
{
|
|
printf( "----: 0 1 2 3 4 5 6 7 - 8 9 A B C D E F |0123456789ABCDEF|\n" );
|
|
|
|
while( start < end )
|
|
{
|
|
printf( "%04X: ", start );
|
|
|
|
for( int byte = 0; byte < 16; byte++ )
|
|
{
|
|
if( byte == 8 )
|
|
printf("- " );
|
|
printf( "%02X ", gaDsk[ start + byte ] );
|
|
}
|
|
|
|
printf( "|" );
|
|
|
|
for( int byte = 0; byte < 16; byte++ )
|
|
{
|
|
uint8_t c = gaDsk[ start + byte ];
|
|
c &= 0x7F;
|
|
|
|
if( c < 0x20 )
|
|
c = '.';
|
|
if( c == 0x7F )
|
|
c = '.';
|
|
|
|
|
|
printf( "%c", c );
|
|
}
|
|
|
|
printf( "|\n" );
|
|
|
|
start += 16;
|
|
}
|
|
|
|
printf( "\n" );
|
|
}
|
|
|