Appalm/utils/dsk2pdb.c
2019-04-21 06:53:32 -07:00

686 lines
18 KiB
C
Executable File

#include <stdio.h>
#include <string.h>
#include <time.h>
#ifdef __APPLE_CC__
#include <stdlib.h>
#else
#include <malloc.h>
#endif
#define TRUE 1
#define FALSE 0
typedef unsigned char BYTE;
typedef unsigned ADDR;
typedef unsigned char UInt8;
typedef char Int8;
typedef unsigned short UInt16;
typedef short Int16;
typedef unsigned long UInt32;
typedef long Int32;
typedef unsigned long LocalID;
#ifdef __APPLE_CC__
#define BE_UINT8(b) ((UInt8)(b))
#define BE_UINT16(i) ((UInt16)(i))
#define BE_UINT32(i) ((UInt32)(i))
#define BE_INT8(b) ((Int8)(b))
#define BE_INT16(i) ((Int16)(i))
#define BE_INT32(i) ((Int32)(i))
#define BE_LOCALID(i) ((Int32)(i))
#else
#define BE_UINT8(b) ((UInt8)(b))
#define BE_UINT16(i) ((((UInt16)(i)&0xFF00)>>8)|(((UInt16)(i)&0xFF)<<8))
#define BE_UINT32(i) (((((UInt32)(i))>>24))|((((UInt32)(i))>>8)&0x0000FF00)|((((UInt32)(i))<<8)&0x00FF0000)|(((UInt32)(i))<<24))
#define BE_INT8(b) ((UInt8)(b))
#define BE_INT16(i) ((((UInt16)(i)&0xFF00)>>8)|(((UInt16)(i)&0xFF)<<8))
#define BE_INT32(i) (((((UInt32)(i))>>24))|((((UInt32)(i))>>8)&0x0000FF00)|((((UInt32)(i))<<8)&0x00FF0000)|(((UInt32)(i))<<24))
#define BE_LOCALID(i) (((((UInt32)(i))>>24))|((((UInt32)(i))>>8)&0x0000FF00)|((((UInt32)(i))<<8)&0x00FF0000)|(((UInt32)(i))<<24))
#endif
#ifdef _WIN32
#define RDSK 'RDSK'
#define DDSK 'DDSK'
#define Apl2 'Apl2'
#else
#define RDSK BE_UINT32('RDSK')
#define DDSK BE_UINT32('DDSK')
#define Apl2 BE_UINT32('Apl2')
#endif
typedef struct {
LocalID localChunkID;
UInt8 attributes;
UInt8 uniqueID[3];
} RecordEntryType;
typedef struct {
LocalID nextRecordListID;
UInt16 numRecords;
UInt16 firstEntry;
} RecordListType;
typedef struct {
UInt8 name[32];
UInt16 attributes;
UInt16 version;
UInt32 creationDate;
UInt32 modificationDate;
UInt32 lastBackupDate;
UInt32 modificationNumber;
LocalID appInfoID;
LocalID sortInfoID;
UInt32 type;
UInt32 creator;
UInt32 uniqueIDSeed;
RecordListType recordList;
} DatabaseHdrType;
#define SIGN_EXTEND(x) ((int)((char)x))
/*
* GCR encoding/decoding utility
*
*/
/*
* raw track
* this is obtained by
* 14.31818MHz / 14 / 32 / 8
*
*/
#define RAW_TRACK_BYTES 6192/*6320*/
#define DOS_TRACK_BYTES 4096
#define RAW_TRACK_BITS (RAW_TRACK_BYTES*8)
static FILE *diskimage;
static int write_mode;
static int write_protect;
static BYTE nibble[RAW_TRACK_BYTES];
static BYTE dos_track[4096];
static int position;
static int current_slot; /* Current slot we have open */
static int current_drive; /* Current drive we have open */
/* i.e to which diskimage points */
static int motor_on;
static int physical_track_no;
static int stepper_status;
static int track_buffer_valid=0;
static int track_buffer_dirty=0;
static int track_buffer_track=0;
static BYTE data_latch;
static BYTE address_latch;
static UInt32 LastAppleClock, AppleClock;
static BYTE boot_ROM[256];
char *DiskROM= "DISK.ROM";
static BYTE GCR_encoding_table[64] = {
0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6,
0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3,
0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC,
0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3,
0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE,
0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC,
0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF };
static BYTE GCR_decoding_table[256];
static int Swap_Bit[4] = { 0, 2, 1, 3 }; /* swap lower 2 bits */
static BYTE GCR_buffer[256];
static BYTE GCR_buffer2[86];
static int Position=0;
static BYTE *Track_Nibble;
/* physical sector no. to DOS 3.3 logical sector no. table */
static int Logical_Sector[16] = {
0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4,
0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF };
static int Physical_Sector[16];
/* static function prototypes */
static void init_GCR_table(void);
static BYTE gcr_read_nibble(void);
static void gcr_write_nibble( BYTE );
static void decode62( BYTE* );
static void encode62( BYTE* );
static void FM_encode( BYTE );
static BYTE FM_decode(void);
static void write_sync( int );
static int read_address_field( int*, int*, int* );
static void write_address_field( int, int, int );
static int read_data_field(void);
static void write_data_field(void);
#define FM_ENCODE(x) gcrWriteNibble( ((x) >> 1) | 0xAA );\
gcrWriteNibble( (x) | 0xAA )
static void init_GCR_table(void)
{
static int initialized = 0;
int i;
if ( !initialized ) {
for( i = 0; i < 64; i++ )
GCR_decoding_table[GCR_encoding_table[i]] = i;
for( i = 0; i < 16; i++ )
Physical_Sector[Logical_Sector[i]] = i;
initialized = 1;
}
}
static BYTE gcr_read_nibble(void)
{
BYTE data;
data = Track_Nibble[Position++];
if ( Position >= RAW_TRACK_BYTES )
Position = 0;
return data;
}
static void gcr_write_nibble( BYTE data )
{
Track_Nibble[Position++] = data;
if ( Position >= RAW_TRACK_BYTES )
Position = 0;
}
static void decode62( BYTE *page )
{
int i, j;
/* get 6 bits from GCR_buffer & 2 from GCR_buffer2 */
for( i = 0, j = 86; i < 256; i++ ) {
if ( --j < 0 ) j = 85;
page[i] = (GCR_buffer[i] << 2) | Swap_Bit[GCR_buffer2[j] & 0x03];
GCR_buffer2[j] >>= 2;
}
}
static void encode62( BYTE *page )
{
int i, j;
/* 86 * 3 = 258, so the first two byte are encoded twice */
GCR_buffer2[0] = Swap_Bit[page[1]&0x03];
GCR_buffer2[1] = Swap_Bit[page[0]&0x03];
/* save higher 6 bits in GCR_buffer and lower 2 bits in GCR_buffer2 */
for( i = 255, j = 2; i >= 0; i--, j = j == 85? 0: j + 1 ) {
GCR_buffer2[j] = (GCR_buffer2[j] << 2) | Swap_Bit[page[i]&0x03];
GCR_buffer[i] = page[i] >> 2;
}
/* clear off higher 2 bits of GCR_buffer2 set in the last call */
for( i = 0; i < 86; i++ )
GCR_buffer2[i] &= 0x3f;
}
/*
* write an FM encoded value, used in writing address fields
*/
static void FM_encode( BYTE data )
{
gcr_write_nibble( (data >> 1) | 0xAA );
gcr_write_nibble( data | 0xAA );
}
/*
* return an FM encoded value, used in reading address fields
*/
static BYTE FM_decode(void)
{
int tmp;
/* C does not specify order of operand evaluation, don't
* merge the following two expression into one
*/
tmp = (gcr_read_nibble() << 1) | 0x01;
return gcr_read_nibble() & tmp;
}
static void write_sync( int length )
{
while( length-- )
gcr_write_nibble( 0xFF );
}
/*
* read_address_field: try to read a address field in a track
* returns 1 if succeed, 0 otherwise
*/
static int read_address_field( int *volume, int *track, int *sector )
{
int max_try;
BYTE nibble;
max_try = 100;
while( --max_try ) {
nibble = gcr_read_nibble();
check_D5:
if ( nibble != 0xD5 )
continue;
nibble = gcr_read_nibble();
if ( nibble != 0xAA )
goto check_D5;
nibble = gcr_read_nibble();
if ( nibble != 0x96 )
goto check_D5;
*volume = FM_decode();
*track = FM_decode();
*sector = FM_decode();
return ( *volume ^ *track ^ *sector ) == FM_decode() &&
gcr_read_nibble() == 0xDE;
}
return 0;
}
static void write_address_field( int volume, int track, int sector )
{
/*
* write address mark
*/
gcr_write_nibble( 0xD5 );
gcr_write_nibble( 0xAA );
gcr_write_nibble( 0x96 );
/*
* write Volume, Track, Sector & Check-sum
*/
FM_encode( volume );
FM_encode( track );
FM_encode( sector );
FM_encode( volume ^ track ^ sector );
/*
* write epilogue
*/
gcr_write_nibble( 0xDE );
gcr_write_nibble( 0xAA );
gcr_write_nibble( 0xEB );
}
/*
* read_data_field: read_data_field into GCR_buffers, return 0 if fail
*/
static int read_data_field(void)
{
int i, max_try;
BYTE nibble, checksum;
/*
* read data mark
*/
max_try = 32;
while( --max_try ) {
nibble = gcr_read_nibble();
check_D5:
if ( nibble != 0xD5 )
continue;
nibble = gcr_read_nibble();
if ( nibble != 0xAA )
goto check_D5;
nibble = gcr_read_nibble();
if ( nibble == 0xAD )
break;
}
if ( !max_try ) /* fails to get address mark */
return 0;
for( i = 0x55, checksum = 0; i >= 0; i-- ) {
checksum ^= GCR_decoding_table[gcr_read_nibble()];
GCR_buffer2[i] = checksum;
}
for( i = 0; i < 256; i++ ) {
checksum ^= GCR_decoding_table[gcr_read_nibble()];
GCR_buffer[i] = checksum;
}
/* verify sector checksum */
if ( checksum ^ GCR_decoding_table[gcr_read_nibble()] )
return 0;
/* check epilogue */
return gcr_read_nibble() == 0xDE && gcr_read_nibble() == 0xAA;
}
static void write_data_field(void)
{
int i;
BYTE last, checksum;
/* write prologue */
gcr_write_nibble( 0xD5 );
gcr_write_nibble( 0xAA );
gcr_write_nibble( 0xAD );
/* write GCR encode data */
for( i = 0x55, last = 0; i >= 0; i-- ) {
checksum = last^ GCR_buffer2[i];
gcr_write_nibble( GCR_encoding_table[checksum] );
last = GCR_buffer2[i];
}
for( i = 0; i < 256; i++ ) {
checksum = last ^ GCR_buffer[i];
gcr_write_nibble( GCR_encoding_table[checksum] );
last = GCR_buffer[i];
}
/* write checksum and epilogue */
gcr_write_nibble( GCR_encoding_table[last] );
gcr_write_nibble( 0xDE );
gcr_write_nibble( 0xAA );
gcr_write_nibble( 0xEB );
}
void SectorsToNibbles( BYTE *sectors, BYTE *nibbles, int volume, int track )
{
int i;
init_GCR_table();
Track_Nibble = nibbles;
Position = 0;
/*write_sync( 128 );*/
for( i = 0; i < 16; i ++ ) {
encode62( sectors + Logical_Sector[i] * 0x100 );
write_sync( 16 );
write_address_field( volume, track, i );
write_sync( 8 );
write_data_field();
}
}
int NibblesToSectors( BYTE *nibbles, BYTE *sectors, int volume, int track )
{
int i, scanned[16], max_try, sectors_read;
int vv, tt, ss; /* volume, track no. and sector no. */
FILE *fp;
init_GCR_table();
Track_Nibble = nibbles;
Position = 0;
for( i = 0; i < 16; i++ )
scanned[i] = 0;
sectors_read = 0;
max_try = 200;
while( --max_try ) {
if ( !read_address_field( &vv, &tt, &ss ) )
continue;
if ( (volume && vv != volume ) || tt != track || ss < 0 || ss > 15 ){
printf("phy sector %d address field invalid\n", ss );
continue; /* invalid values for vv, tt and ss, try again */
}
ss = Logical_Sector[ss];
if ( scanned[ss] ) /* sector has been read */
continue;
if ( read_data_field() ) {
decode62( sectors + ss * 0x100 );
scanned[ss] = 1; /* this sector's ok */
sectors_read++;
}
else {
printf("fail reading data field of logical sector %d\n", ss );
}
}
/* if has failed to read any one sector, report error */
if ( sectors_read == 16 )
return 1;
else {
printf( "sectos_read = %d\n",sectors_read);
for( i = 0; i < 16; i++ )
if ( !scanned[i] )
printf( "sector %d(%d) corrupted\n", i, Physical_Sector[i] );
if((fp = fopen( ".track", "w"))!=NULL) {
fwrite( nibbles, 1, RAW_TRACK_BYTES, fp );
fclose(fp);
}
return 0;
}
}
void load_track_buffer(void)
{
int logical_track;
if (!diskimage)
return;
if ( physical_track_no & 0x3 ) {
fprintf( stderr, "Cannot read half track %g!\n",
physical_track_no * 0.25 );
}
logical_track = (physical_track_no+1)>>2;
fseek( diskimage, logical_track * DOS_TRACK_BYTES, 0L );
fread( dos_track, 1, DOS_TRACK_BYTES, diskimage );
SectorsToNibbles( dos_track, nibble, 254, logical_track );
track_buffer_track = physical_track_no;
#ifdef DEBUG
printf( "load track %g\n", track_buffer_track*.25 );
#endif
track_buffer_dirty = 0;
track_buffer_valid = 1;
}
BYTE read_nibble(void)
{
BYTE data;
static flag;
if ( !track_buffer_valid ) {
load_track_buffer();
}
flag = !flag;
if ( flag )
return 0;
data = nibble[position++];
if ( position >= RAW_TRACK_BYTES )
position = 0;
LastAppleClock = AppleClock;
return data;
}
/* wkt -- still somehow need to stop diskRead from
* reading when we don't have a disk to read
*/
int mount_disk( char *filename)
{
if ( diskimage ) unmount_disk();
write_protect = 0;
diskimage = fopen( filename, "rb+" );
if ( !diskimage ) {
write_protect = 1;
diskimage = fopen( filename, "rb" );
}
if (!diskimage) {
fprintf( stderr, "Fail to mount disk %s\n", filename );
return -1;
}
else {
#ifdef DEBUG
fprintf( stderr, "Mount disk %s\n", filename );
fprintf( stderr, "write protected = %d\n", write_protect );
#endif
current_slot=6; current_drive=0;
load_track_buffer();
}
return 0;
}
int unmount_disk()
{
if ( diskimage ) {
fclose( diskimage );
diskimage = NULL;
}
return 0;
}
int main(int argc, char **argv)
{
FILE *pdb;
int tracks, sectors, file_size, header_size, track_size, i, j, cvt2raw, argfilename;
char pdbfile[64], *p;
DatabaseHdrType pdb_header;
RecordEntryType pdb_record_entries[40];
int now;
/*
* Strip leading pathname from executable name.
*/
for (p = argv[0]; *p; p++)
{
if (*p == '/' || *p == '\\')
argv[0] = p + 1;
}
if (!strcmp(argv[0], "dsk2pdb") || !strcmp(argv[0], "dsk2pdb.exe") || !strcmp(argv[0], "DSK2PDB.EXE"))
{
if (argc >= 2)
{
argfilename = 1;
cvt2raw = 0;
if (argv[1][0] == '-')
{
argfilename = 2;
if (argv[1][1] == 'r')
cvt2raw = 1;
}
if (mount_disk(argv[argfilename]))
{
fprintf(stderr, "Unable to mount disk file %s.\n", argv[1]);
exit(1);
}
tracks = 35;
sectors = 16;
fseek(diskimage, 0, SEEK_END);
file_size = ftell(diskimage);
if (argc > argfilename + 1)
{
strcpy(pdbfile, argv[argfilename + 1]);
}
else
{
/*
* Strip leading pathname from DSK name.
*/
for (p = argv[argfilename]; *p; p++)
{
if (*p == '/' || *p == '\\')
argv[argfilename] = p + 1;
}
strcpy(pdbfile, argv[argfilename]);
strcat(pdbfile, ".pdb");
}
if (!(pdb = fopen(pdbfile, "wb")))
{
fprintf(stderr, "Unable to create PDB file %s.\n", argv[2]);
exit(1);
}
header_size = 78/*sizeof(DatabaseHdrType)*/ + 8/*sizeof(RecordEntryType)*/ * tracks + 2;
track_size = cvt2raw ? RAW_TRACK_BYTES : (file_size / tracks);
now = time(NULL);
memset(&pdb_header, 0, sizeof(DatabaseHdrType));
memset(&pdb_record_entries, 0, sizeof(RecordEntryType) * tracks);
if (strlen(argv[argfilename]) >= 31)
argv[argfilename][31] = '\0';
strcpy(pdb_header.name, argv[argfilename]);
pdb_header.modificationDate = BE_UINT32(now);
pdb_header.creationDate = BE_UINT32(now);
pdb_header.type = (track_size == DOS_TRACK_BYTES) ? DDSK : RDSK;
pdb_header.creator = Apl2;
pdb_header.recordList.numRecords = BE_UINT16(tracks);
for (i = 0; i < tracks; i++)
{
j = header_size + i * track_size;
pdb_record_entries[i].localChunkID = BE_LOCALID(j);
pdb_record_entries[i].attributes = 0x40;
pdb_record_entries[i].uniqueID[0] = i;
}
fwrite(&pdb_header, 78/*sizeof(DatabaseHdrType)*/, 1, pdb);
fwrite(&pdb_record_entries, 8/*sizeof(RecordEntryType)*/, tracks, pdb);
i = 0;
fwrite(&i, 2, 1, pdb); /* Filler */
if (cvt2raw)
{
for (physical_track_no = 0; physical_track_no < tracks * 4; physical_track_no += 4)
{
load_track_buffer();
fwrite(nibble, RAW_TRACK_BYTES, 1, pdb);
}
}
else
{
unsigned char *all_tracks = malloc(track_size * tracks);
fseek(diskimage, 0L, 0L);
fread(all_tracks, track_size, tracks, diskimage);
fwrite(all_tracks, track_size, tracks, pdb);
free(all_tracks);
}
unmount_disk();
fclose(pdb);
}
else
//fprintf(stderr, "Usage: %s <DSK file> <PDB file> [sectors/track (16)] [total tracks (35)]\n", argv[0]);
fprintf(stderr, "Usage: %s <DSK file> [PDB file]\n", argv[0]);
}
else if (!strcmp(argv[0], "pdb2dsk") || !strcmp(argv[0], "pdb2dsk.exe") || !strcmp(argv[0], "PDB2DSK.EXE"))
{
if (argc >= 2)
{
if (!(pdb = fopen(argv[1], "rb")))
{
fprintf(stderr, "Unable to open PDB file %s.\n", argv[1]);
exit(1);
}
if (argc < 3)
{
fread(&pdb_header, 78/*sizeof(DatabaseHdrType)*/, 1, pdb);
pdb_header.name[31] = '\0';
if (!(diskimage = fopen(pdb_header.name, "wb")))
{
fprintf(stderr, "Unable to open DSK file %s.\n", argv[2]);
exit(1);
}
}
else
{
if (!(diskimage = fopen(argv[2], "wb")))
{
fprintf(stderr, "Unable to open DSK file %s.\n", argv[2]);
exit(1);
}
}
fseek(pdb, 78+8*35+2, 0L);
while (fread(&i, 1, 1, pdb) > 0)
{
fwrite(&i, 1, 1, diskimage);
}
fclose(pdb);
fclose(diskimage);
}
else
//fprintf(stderr, "Usage: %s <DSK file> <PDB file> [sectors/track (16)] [total tracks (35)]\n", argv[0]);
fprintf(stderr, "Usage: %s <PDB file> [DSK file]\n", argv[0]);
}
else
fprintf(stderr, "Unknown launch name: %s\n", argv[0]);
return (0);
}