apple2_prodos_utils/generic.disk.cpp
2023-06-12 23:13:05 -07:00

352 lines
8.6 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 ];
uint8_t gaTmp[ DSK_SECTOR_SIZE * 2 ];
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;
}
int DskGetIndexBlock( int offset, int index )
{
int block = 0
| (gaDsk[ offset + index + 0 ] << 0)
| (gaDsk[ offset + index + 256 ] << 8)
;
return block;
}
/*
000:lo0 lo1 lo2 ...
100:hi0 hi1 hi2 ...
*/
void DskPutIndexBlock( int offset, int index, int block )
{
gaDsk[ offset + index + 0 ] = (block >> 0) & 0xFF;
gaDsk[ offset + index + 256 ] = (block >> 8) & 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" );
}