summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/lib/formats/d64_dsk.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/lib/formats/d64_dsk.c')
-rw-r--r--trunk/src/lib/formats/d64_dsk.c856
1 files changed, 856 insertions, 0 deletions
diff --git a/trunk/src/lib/formats/d64_dsk.c b/trunk/src/lib/formats/d64_dsk.c
new file mode 100644
index 00000000000..f894fa89971
--- /dev/null
+++ b/trunk/src/lib/formats/d64_dsk.c
@@ -0,0 +1,856 @@
+/*********************************************************************
+
+ formats/d64_dsk.c
+
+ Floppy format code for Commodore 1541/2040/8050 disk images
+
+*********************************************************************/
+
+/*
+
+ TODO:
+
+ - write to disk
+ - disk errors 24, 25, 26, 28, 74
+ - variable gaps
+
+*/
+
+#define XTAL_16MHz 16000000
+#define XTAL_12MHz 12000000
+#include "g64_dsk.h"
+#include "flopimg.h"
+#include "d64_dsk.h"
+
+/***************************************************************************
+ PARAMETERS
+***************************************************************************/
+
+#define LOG 1
+
+#define MAX_HEADS 2
+#define MAX_TRACKS 84
+#define MAX_ERROR_SECTORS 4166
+#define SECTOR_SIZE 256
+#define SECTOR_SIZE_GCR 368
+
+#define INVALID_OFFSET 0xbadbad
+
+#define D64_SIZE_35_TRACKS 174848
+#define D64_SIZE_35_TRACKS_WITH_ERRORS 175531
+#define D64_SIZE_40_TRACKS 196608
+#define D64_SIZE_40_TRACKS_WITH_ERRORS 197376
+#define D64_SIZE_42_TRACKS 205312
+#define D64_SIZE_42_TRACKS_WITH_ERRORS 206114
+#define D67_SIZE_35_TRACKS 176640
+#define D71_SIZE_70_TRACKS 349696
+#define D71_SIZE_70_TRACKS_WITH_ERRORS 351062
+#define D80_SIZE_77_TRACKS 533248
+#define D80_SIZE_77_TRACKS_WITH_ERRORS 535331
+#define D82_SIZE_154_TRACKS 1066496
+#define D82_SIZE_154_TRACKS_WITH_ERRORS 1070662
+
+enum
+{
+ DOS1,
+ DOS2,
+ DOS25
+};
+
+static const char *const DOS_VERSION[] = { "1.0", "2.0", "2.5" };
+
+enum
+{
+ ERROR_00 = 1,
+ ERROR_20, /* header block not found */
+ ERROR_21, /* no sync character */
+ ERROR_22, /* data block not present */
+ ERROR_23, /* checksum error in data block */
+ ERROR_24, /* write verify (on format) UNIMPLEMENTED */
+ ERROR_25, /* write verify error UNIMPLEMENTED */
+ ERROR_26, /* write protect on UNIMPLEMENTED */
+ ERROR_27, /* checksum error in header block */
+ ERROR_28, /* write error UNIMPLEMENTED */
+ ERROR_29, /* disk ID mismatch */
+ ERROR_74, /* disk not ready (no device 1) UNIMPLEMENTED */
+};
+
+static const char *const ERROR_CODE[] = { "00", "00", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "74" };
+
+static const UINT8 bin_2_gcr[] =
+{
+ 0x0a, 0x0b, 0x12, 0x13, 0x0e, 0x0f, 0x16, 0x17,
+ 0x09, 0x19, 0x1a, 0x1b, 0x0d, 0x1d, 0x1e, 0x15
+};
+
+/* This could be of use if we ever implement saving in .d64 format, to convert back GCR -> d64 */
+/*
+static const int gcr_2_bin[] =
+{
+ -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ -1, 0x08, 0x00, 0x01,
+ -1, 0x0c, 0x04, 0x05,
+ -1, -1, 0x02, 0x03,
+ -1, 0x0f, 0x06, 0x07,
+ -1, 0x09, 0x0a, 0x0b,
+ -1, 0x0d, 0x0e, -1
+};
+*/
+
+static const int DOS1_SECTORS_PER_TRACK[] =
+{
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 20, 20, 20, 20, 20, 20, 20,
+ 18, 18, 18, 18, 18, 18,
+ 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17,
+ 17, 17
+};
+
+static const int DOS1_SPEED_ZONE[] =
+{
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0
+};
+
+static const int DOS2_SECTORS_PER_TRACK[] =
+{
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 19, 19, 19, 19, 19, 19, 19,
+ 18, 18, 18, 18, 18, 18,
+ 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17,
+ 17, 17
+};
+
+static const int DOS25_SECTORS_PER_TRACK[] =
+{
+ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, /* 1-39 */
+ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, /* 40-53 */
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, /* 54-64 */
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, /* 65-77 */
+ 23, 23, 23, 23, 23, 23, 23 /* 78-84 */
+};
+
+static const int DOS25_SPEED_ZONE[] =
+{
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 1-39 */
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 40-53 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 54-64 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 65-77 */
+ 0, 0, 0, 0, 0, 0, 0 /* 78-84 */
+};
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+struct d64dsk_tag
+{
+ int dos; /* CBM DOS version */
+ int heads; /* number of physical heads */
+ int tracks; /* number of physical tracks */
+ int dos_tracks; /* number of logical tracks */
+ int track_offset[MAX_HEADS][MAX_TRACKS]; /* offset within image for each physical track */
+ UINT32 speed_zone[MAX_TRACKS]; /* speed zone for each physical track */
+ bool has_errors; /* flag to check for available error codes */
+ UINT8 error[MAX_ERROR_SECTORS]; /* error code for each logical sector */
+ int error_offset[MAX_HEADS][MAX_TRACKS]; /* offset within error array for sector 0 of each logical track */
+
+ UINT8 id1, id2; /* DOS disk format ID */
+};
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+INLINE struct d64dsk_tag *get_tag(floppy_image_legacy *floppy)
+{
+ return (d64dsk_tag *)floppy_tag(floppy);
+}
+
+INLINE float get_dos_track(int track)
+{
+ return ((float)track / 2) + 1;
+}
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ d64_get_heads_per_disk - returns the number
+ of heads in the disk image
+-------------------------------------------------*/
+
+static int d64_get_heads_per_disk(floppy_image_legacy *floppy)
+{
+ return get_tag(floppy)->heads;
+}
+
+/*-------------------------------------------------
+ d64_get_tracks_per_disk - returns the number
+ of DOS tracks in the disk image
+-------------------------------------------------*/
+
+static int d64_get_tracks_per_disk(floppy_image_legacy *floppy)
+{
+ return get_tag(floppy)->tracks;
+}
+
+/*-------------------------------------------------
+ d64_get_sectors_per_track - returns the number
+ of sectors per given track
+-------------------------------------------------*/
+
+static int d64_get_sectors_per_track(floppy_image_legacy *floppy, int head, int track)
+{
+ int sectors_per_track = 0;
+
+ switch (get_tag(floppy)->dos)
+ {
+ case DOS1: sectors_per_track = DOS1_SECTORS_PER_TRACK[track / 2]; break;
+ case DOS2: sectors_per_track = DOS2_SECTORS_PER_TRACK[track / 2]; break;
+ case DOS25: sectors_per_track = DOS25_SECTORS_PER_TRACK[track]; break;
+ }
+
+ return sectors_per_track;
+}
+
+/*-------------------------------------------------
+ get_track_offset - returns the offset within
+ the disk image for a given track
+-------------------------------------------------*/
+
+static floperr_t get_track_offset(floppy_image_legacy *floppy, int head, int track, UINT64 *offset)
+{
+ struct d64dsk_tag *tag = get_tag(floppy);
+ UINT64 offs = 0;
+
+ if ((track < 0) || (track >= tag->tracks))
+ return FLOPPY_ERROR_SEEKERROR;
+
+ offs = tag->track_offset[head][track];
+
+ if (offset)
+ *offset = offs;
+
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+/*-------------------------------------------------
+ d64_get_track_size - returns the track size
+-------------------------------------------------*/
+
+static UINT32 d64_get_track_size(floppy_image_legacy *floppy, int head, int track)
+{
+ struct d64dsk_tag *tag = get_tag(floppy);
+
+ if (tag->track_offset[head][track] == INVALID_OFFSET)
+ return 0;
+
+ /* determine number of sectors per track */
+ int sectors_per_track = d64_get_sectors_per_track(floppy, head, track);
+
+ /* allocate temporary GCR track data buffer */
+ UINT32 track_length = sectors_per_track * SECTOR_SIZE_GCR;
+
+ return track_length;
+}
+
+/*-------------------------------------------------
+ get_sector_error_code - returns the error
+ code for the given sector
+-------------------------------------------------*/
+
+static int get_sector_error_code(floppy_image_legacy *floppy, int head, int dos_track, int sector)
+{
+ struct d64dsk_tag *tag = get_tag(floppy);
+
+ if (!tag->has_errors)
+ return ERROR_00;
+
+ int sector_error = tag->error[tag->error_offset[head][dos_track] + sector]; // TODO index out of bounds!!!
+
+ if (sector_error != ERROR_00)
+ {
+ LOG_FORMATS("D64 error %s head %u track %u sector %u\n", ERROR_CODE[sector_error], head, dos_track, sector);
+ }
+
+ return sector_error;
+}
+
+/*-------------------------------------------------
+ gcr_double_2_gcr - GCR decodes given data
+-------------------------------------------------*/
+
+/* gcr_double_2_gcr takes 4 bytes (a, b, c, d) and shuffles their nibbles to obtain 5 bytes in dest */
+/* The result is basically res = (enc(a) << 15) | (enc(b) << 10) | (enc(c) << 5) | enc(d)
+ * with res being 5 bytes long and enc(x) being the GCR encode of x.
+ * In fact, we store the result as five separate bytes in the dest argument
+ */
+
+/*
+
+ Commodore GCR format
+
+Original Encoded
+4 bits 5 bits
+
+0000 -> 01010 = 0x0a
+0001 -> 01011 = 0x0b
+0010 -> 10010 = 0x12
+0011 -> 10011 = 0x13
+0100 -> 01110 = 0x0e
+0101 -> 01111 = 0x0f
+0110 -> 10110 = 0x16
+0111 -> 10111 = 0x17
+1000 -> 01001 = 0x09
+1001 -> 11001 = 0x19
+1010 -> 11010 = 0x1a
+1011 -> 11011 = 0x1b
+1100 -> 01101 = 0x0d
+1101 -> 11101 = 0x1d
+1110 -> 11110 = 0x1e
+1111 -> 10101 = 0x15
+
+We use the encoded values in bytes because we use them to encode
+groups of 4 bytes into groups of 5 bytes, below.
+
+*/
+
+static void gcr_double_2_gcr(UINT8 a, UINT8 b, UINT8 c, UINT8 d, UINT8 *dest)
+{
+ UINT8 gcr[8];
+
+ /* Encode each nibble to 5 bits */
+ gcr[0] = bin_2_gcr[a >> 4];
+ gcr[1] = bin_2_gcr[a & 0x0f];
+ gcr[2] = bin_2_gcr[b >> 4];
+ gcr[3] = bin_2_gcr[b & 0x0f];
+ gcr[4] = bin_2_gcr[c >> 4];
+ gcr[5] = bin_2_gcr[c & 0x0f];
+ gcr[6] = bin_2_gcr[d >> 4];
+ gcr[7] = bin_2_gcr[d & 0x0f];
+
+ /* Re-order the encoded data to only keep the 5 lower bits of each byte */
+ dest[0] = (gcr[0] << 3) | (gcr[1] >> 2);
+ dest[1] = (gcr[1] << 6) | (gcr[2] << 1) | (gcr[3] >> 4);
+ dest[2] = (gcr[3] << 4) | (gcr[4] >> 1);
+ dest[3] = (gcr[4] << 7) | (gcr[5] << 2) | (gcr[6] >> 3);
+ dest[4] = (gcr[6] << 5) | gcr[7];
+}
+
+/*-------------------------------------------------
+ d64_read_track - reads a full track from the
+ disk image
+-------------------------------------------------*/
+
+/*
+
+ Commodore sector format
+
+ SYNC FF * 5
+ 08
+ CHECKSUM sector ^ track ^ id2 ^ id1
+ SECTOR 0..20 (2040), 0..28 (8050)
+ TRACK 1..35 (2040), 1..77 (8050), 1..70 (1571)
+ ID2
+ ID1
+ GAP 1 55 * 9 (2040), 55 * 8 (1541)
+
+ SYNC FF * 5
+ 07
+ NEXT TRACK
+ NEXT SECTOR
+ 254 BYTES OF DATA
+ CHECKSUM
+ GAP 2 55 * 8..19
+
+*/
+
+static floperr_t d64_read_track(floppy_image_legacy *floppy, int head, int track, UINT64 offset, void *buffer, size_t buflen)
+{
+ struct d64dsk_tag *tag = get_tag(floppy);
+ floperr_t err;
+ UINT64 track_offset;
+
+ /* get track offset */
+ err = get_track_offset(floppy, head, track, &track_offset);
+
+ if (err)
+ return err;
+
+ if (track_offset != INVALID_OFFSET)
+ {
+ UINT8 id1 = tag->id1;
+ UINT8 id2 = tag->id2;
+ int sectors_per_track;
+ UINT16 d64_track_size;
+ UINT8 *d64_track_data;
+ UINT16 gcr_track_size;
+ UINT8 *gcr_track_data;
+ UINT64 gcr_pos = 0;
+
+ /* determine logical track number */
+ int dos_track = get_dos_track(track);
+
+ if (tag->dos == DOS25)
+ {
+ dos_track = track + 1;
+ }
+
+ /* logical track numbers continue on the flip side */
+ if (head == 1) dos_track += tag->dos_tracks;
+
+ /* determine number of sectors per track */
+ sectors_per_track = d64_get_sectors_per_track(floppy, head, track);
+
+ /* allocate D64 track data buffer */
+ d64_track_size = sectors_per_track * SECTOR_SIZE;
+ d64_track_data = (UINT8 *)alloca(d64_track_size);
+
+ /* allocate temporary GCR track data buffer */
+ gcr_track_size = sectors_per_track * SECTOR_SIZE_GCR;
+ gcr_track_data = (UINT8 *)alloca(gcr_track_size);
+
+ if (buflen < gcr_track_size) { printf("D64 track buffer too small: %u!", (UINT32)buflen); exit(-1); }
+
+ /* read D64 track data */
+ floppy_image_read(floppy, d64_track_data, track_offset, d64_track_size);
+
+ /* GCR encode D64 sector data */
+ for (int sector = 0; sector < sectors_per_track; sector++)
+ {
+ // here we convert the sector data to gcr directly!
+ // IMPORTANT: errors in reading sectors can modify e.g. header info $01 & $05
+ int sector_error = get_sector_error_code(floppy, head, track, sector);
+
+ /* first we set the position at which sector data starts in the image */
+ UINT64 d64_pos = sector * SECTOR_SIZE;
+ int i;
+
+ /*
+ 1. Header sync FF FF FF FF FF (40 'on' bits, not GCR encoded)
+ 2. Header info 52 54 B5 29 4B 7A 5E 95 55 55 (10 GCR bytes)
+ 3. Header gap 55 55 55 55 55 55 55 55 55 (9 bytes, never read)
+ 4. Data sync FF FF FF FF FF (40 'on' bits, not GCR encoded)
+ 5. Data block 55...4A (325 GCR bytes)
+ 6. Inter-sector gap 55 55 55 55...55 55 (4 to 19 bytes, never read)
+ */
+
+ if (sector_error == ERROR_29)
+ id1 ^= 0xff;
+
+ /* Header sync */
+ if (sector_error != ERROR_21)
+ {
+ for (i = 0; i < 5; i++)
+ gcr_track_data[gcr_pos + i] = 0xff;
+ gcr_pos += 5;
+ }
+
+ /* Header info */
+ /* These are 8 bytes unencoded, which become 10 bytes encoded */
+ // $00 - header block ID ($08) // this byte can be modified by error code 20 -> 0xff
+ // $01 - header block checksum (EOR of $02-$05) // this byte can be modified by error code 27 -> ^ 0xff
+ // $02 - Sector# of data block
+ // $03 - Track# of data block
+ UINT8 header_block_id = (sector_error == ERROR_20) ? 0xff : 0x08;
+ UINT8 header_block_checksum = sector ^ dos_track ^ id2 ^ id1;
+
+ if (sector_error == ERROR_27)
+ header_block_checksum ^= 0xff;
+
+ gcr_double_2_gcr(header_block_id, header_block_checksum, sector, dos_track, gcr_track_data + gcr_pos);
+ gcr_pos += 5;
+
+ // $04 - Format ID byte #2
+ // $05 - Format ID byte #1
+ // $06 - $0F ("off" byte)
+ // $07 - $0F ("off" byte)
+ gcr_double_2_gcr(id2, id1, 0x0f, 0x0f, gcr_track_data + gcr_pos);
+ gcr_pos += 5;
+
+ /* Header gap */
+ for (i = 0; i < 9; i++)
+ gcr_track_data[gcr_pos + i] = 0x55;
+ gcr_pos += 9;
+
+ /* Data sync */
+ for (i = 0; i < 5; i++)
+ gcr_track_data[gcr_pos + i] = 0xff;
+ gcr_pos += 5;
+
+ /* Data block */
+ UINT8 data_block_id = (sector_error == ERROR_22) ? 0xff : 0x07;
+
+ // we first need to calculate the checksum of the 256 bytes of the sector
+ UINT8 sector_checksum = d64_track_data[d64_pos];
+ for (i = 1; i < 256; i++)
+ sector_checksum ^= d64_track_data[d64_pos + i];
+
+ if (sector_error == ERROR_23)
+ sector_checksum ^= 0xff;
+
+ /*
+ $00 - data block ID ($07)
+ $01-100 - 256 bytes sector data
+ $101 - data block checksum (EOR of $01-100)
+ $102-103 - $00 ("off" bytes, to make the sector size a multiple of 5)
+ */
+ gcr_double_2_gcr(data_block_id, d64_track_data[d64_pos], d64_track_data[d64_pos + 1], d64_track_data[d64_pos + 2], gcr_track_data + gcr_pos);
+ gcr_pos += 5;
+
+ for (i = 1; i < 64; i++)
+ {
+ gcr_double_2_gcr(d64_track_data[d64_pos + 4 * i - 1], d64_track_data[d64_pos + 4 * i],
+ d64_track_data[d64_pos + 4 * i + 1], d64_track_data[d64_pos + 4 * i + 2], gcr_track_data + gcr_pos);
+ gcr_pos += 5;
+ }
+
+ gcr_double_2_gcr(d64_track_data[d64_pos + 255], sector_checksum, 0x00, 0x00, gcr_track_data + gcr_pos);
+ gcr_pos += 5;
+
+ /* Inter-sector gap */
+ // "In tests that the author conducted on a real 1541 disk, gap sizes of 8 to 19 bytes were seen."
+ // Here we put 14 as an average...
+ for (i = 0; i < 14; i++)
+ gcr_track_data[gcr_pos + i] = 0x55;
+ gcr_pos += 14;
+ }
+
+ /* copy GCR track data to buffer */
+ memcpy(buffer, gcr_track_data, gcr_track_size);
+ }
+ else /* half tracks */
+ {
+ /* set track length to 0 */
+ memset(buffer, 0, buflen);
+ }
+
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+/*-------------------------------------------------
+ d64_write_track - writes a full track to the
+ disk image
+-------------------------------------------------*/
+
+static floperr_t d64_write_track(floppy_image_legacy *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buflen)
+{
+ return FLOPPY_ERROR_UNSUPPORTED;
+}
+
+/*-------------------------------------------------
+ d64_identify - identifies the disk image
+-------------------------------------------------*/
+
+static void d64_identify(floppy_image_legacy *floppy, int *dos, int *heads, int *tracks, bool *has_errors)
+{
+ switch (floppy_image_size(floppy))
+ {
+ /* 2040/3040 */
+ case D67_SIZE_35_TRACKS: *dos = DOS1; *heads = 1; *tracks = 35; *has_errors = false; break;
+
+ /* 4040/2031/1541/1551 */
+ case D64_SIZE_35_TRACKS: *dos = DOS2; *heads = 1; *tracks = 35; *has_errors = false; break;
+ case D64_SIZE_35_TRACKS_WITH_ERRORS: *dos = DOS2; *heads = 1; *tracks = 35; *has_errors = true; break;
+ case D64_SIZE_40_TRACKS: *dos = DOS2; *heads = 1; *tracks = 40; *has_errors = false; break;
+ case D64_SIZE_40_TRACKS_WITH_ERRORS: *dos = DOS2; *heads = 1; *tracks = 40; *has_errors = true; break;
+ case D64_SIZE_42_TRACKS: *dos = DOS2; *heads = 1; *tracks = 42; *has_errors = false; break;
+ case D64_SIZE_42_TRACKS_WITH_ERRORS: *dos = DOS2; *heads = 1; *tracks = 42; *has_errors = true; break;
+
+ /* 1571 */
+ case D71_SIZE_70_TRACKS: *dos = DOS2; *heads = 2; *tracks = 35; *has_errors = false; break;
+ case D71_SIZE_70_TRACKS_WITH_ERRORS: *dos = DOS2; *heads = 2; *tracks = 35; *has_errors = true; break;
+
+ /* 8050 */
+ case D80_SIZE_77_TRACKS: *dos = DOS25; *heads = 1; *tracks = 77; *has_errors = false; break;
+ case D80_SIZE_77_TRACKS_WITH_ERRORS: *dos = DOS25; *heads = 1; *tracks = 77; *has_errors = true; break;
+
+ /* 8250/SFD1001 */
+ case D82_SIZE_154_TRACKS: *dos = DOS25; *heads = 2; *tracks = 77; *has_errors = false; break;
+ case D82_SIZE_154_TRACKS_WITH_ERRORS: *dos = DOS25; *heads = 2; *tracks = 77; *has_errors = true; break;
+ }
+}
+
+/*-------------------------------------------------
+ FLOPPY_IDENTIFY( d64_dsk_identify )
+-------------------------------------------------*/
+
+FLOPPY_IDENTIFY( d64_dsk_identify )
+{
+ int dos = 0, heads, tracks;
+ bool has_errors = false;
+
+ *vote = 0;
+
+ d64_identify(floppy, &dos, &heads, &tracks, &has_errors);
+
+ if (dos == DOS2 && heads == 1)
+ {
+ *vote = 100;
+ }
+
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+/*-------------------------------------------------
+ FLOPPY_IDENTIFY( d67_dsk_identify )
+-------------------------------------------------*/
+
+FLOPPY_IDENTIFY( d67_dsk_identify )
+{
+ *vote = 0;
+
+ if (floppy_image_size(floppy) == D67_SIZE_35_TRACKS)
+ {
+ *vote = 100;
+ }
+
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+/*-------------------------------------------------
+ FLOPPY_IDENTIFY( d71_dsk_identify )
+-------------------------------------------------*/
+
+FLOPPY_IDENTIFY( d71_dsk_identify )
+{
+ int heads = 0, tracks = 0, dos = -1;
+ bool has_errors = false;
+
+ *vote = 0;
+
+ d64_identify(floppy, &dos, &heads, &tracks, &has_errors);
+
+ if (dos == DOS2 && heads == 2)
+ {
+ *vote = 100;
+ }
+
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+/*-------------------------------------------------
+ FLOPPY_IDENTIFY( d80_dsk_identify )
+-------------------------------------------------*/
+
+FLOPPY_IDENTIFY( d80_dsk_identify )
+{
+ int heads = 0, tracks = 0, dos = -1;
+ bool has_errors = false;
+ *vote = 0;
+
+ d64_identify(floppy, &dos, &heads, &tracks, &has_errors);
+
+ if (dos == DOS25 && heads == 1)
+ {
+ *vote = 100;
+ }
+
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+/*-------------------------------------------------
+ FLOPPY_IDENTIFY( d82_dsk_identify )
+-------------------------------------------------*/
+
+FLOPPY_IDENTIFY( d82_dsk_identify )
+{
+ int heads = 0, tracks = 0, dos = -1;
+ bool has_errors = false;
+ *vote = 0;
+
+ d64_identify(floppy, &dos, &heads, &tracks, &has_errors);
+
+ if (dos == DOS25 && heads == 2)
+ {
+ *vote = 100;
+ }
+
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+/*-------------------------------------------------
+ FLOPPY_CONSTRUCT( d64_dsk_construct )
+-------------------------------------------------*/
+
+FLOPPY_CONSTRUCT( d64_dsk_construct )
+{
+ struct FloppyCallbacks *callbacks;
+ struct d64dsk_tag *tag;
+ UINT8 id[2];
+
+ int track_offset = 0;
+ int head, track;
+
+ int heads = 0, dos_tracks = 0, dos = 0;
+ bool has_errors = 0;
+ int errors_size = 0;
+
+ if (params)
+ {
+ /* create not supported */
+ return FLOPPY_ERROR_UNSUPPORTED;
+ }
+
+ tag = (struct d64dsk_tag *) floppy_create_tag(floppy, sizeof(struct d64dsk_tag));
+
+ if (!tag) return FLOPPY_ERROR_OUTOFMEMORY;
+
+ /* identify image type */
+ d64_identify(floppy, &dos, &heads, &dos_tracks, &has_errors);
+
+ tag->dos = dos;
+ tag->heads = heads;
+ tag->tracks = MAX_TRACKS;
+ tag->dos_tracks = dos_tracks;
+ tag->has_errors = has_errors;
+
+ if (LOG)
+ {
+ LOG_FORMATS("D64 size: %04x\n", (UINT32)floppy_image_size(floppy));
+ LOG_FORMATS("D64 heads: %u\n", heads);
+ LOG_FORMATS("D64 tracks: %u\n", dos_tracks);
+ LOG_FORMATS("D64 DOS version: %s\n", DOS_VERSION[dos]);
+ LOG_FORMATS("D64 error codes: %s\n", has_errors ? "yes" : "no");
+ }
+
+ /* clear track data offsets */
+ for (head = 0; head < MAX_HEADS; head++)
+ {
+ for (track = 0; track < MAX_TRACKS; track++)
+ {
+ tag->track_offset[head][track] = INVALID_OFFSET;
+ }
+ }
+
+ /* determine track data offsets */
+ for (head = 0; head < heads; head++)
+ {
+ for (track = 0; track < tag->tracks; track++)
+ {
+ if (dos == DOS25)
+ {
+ if (track >= dos_tracks)
+ {
+ /* track out of range */
+ tag->track_offset[head][track] = INVALID_OFFSET;
+ }
+ else
+ {
+ tag->track_offset[head][track] = track_offset;
+ tag->error_offset[head][track] = errors_size;
+
+ track_offset += DOS25_SECTORS_PER_TRACK[track] * SECTOR_SIZE;
+ /* also store an error entry for each sector */
+ errors_size += DOS25_SECTORS_PER_TRACK[track];
+
+ if (LOG)
+ {
+ LOG_FORMATS("D64 head %u track %u data offset: %04x\n", head, track + 1, tag->track_offset[head][track]);
+ if (has_errors) LOG_FORMATS("D64 head %u track %u error offset: %04x\n", head, track + 1, tag->error_offset[head][track]);
+ }
+ }
+ }
+ else
+ {
+ if ((track % 2) || ((track / 2) >= dos_tracks))
+ {
+ /* half track or out of range */
+ tag->track_offset[head][track] = INVALID_OFFSET;
+ }
+ else
+ {
+ /* full track */
+ tag->track_offset[head][track] = track_offset;
+ tag->error_offset[head][track] = errors_size;
+
+ if (dos == DOS1)
+ {
+ track_offset += DOS1_SECTORS_PER_TRACK[track / 2] * SECTOR_SIZE;
+ /* also store an error entry for each sector */
+ errors_size += DOS1_SECTORS_PER_TRACK[track / 2];
+ }
+ else
+ {
+ track_offset += DOS2_SECTORS_PER_TRACK[track / 2] * SECTOR_SIZE;
+ /* also store an error entry for each sector */
+ errors_size += DOS2_SECTORS_PER_TRACK[track / 2];
+ }
+
+ if (LOG)
+ {
+ LOG_FORMATS("D64 head %u track %.1f data offset: %04x\n", head, get_dos_track(track), tag->track_offset[head][track]);
+ if (has_errors) LOG_FORMATS("D64 head %u track %.1f error offset: %04x\n", head, get_dos_track(track), tag->error_offset[head][track]);
+ }
+ }
+ }
+ }
+ }
+
+ /* determine speed zones */
+ for (track = 0; track < tag->tracks; track++)
+ {
+ if (dos == DOS25)
+ {
+ tag->speed_zone[track] = DOS25_SPEED_ZONE[track];
+
+ if (LOG) LOG_FORMATS("D64 track %u speed zone: %u\n", track + 1, tag->speed_zone[track]);
+ }
+ else
+ {
+ tag->speed_zone[track] = DOS1_SPEED_ZONE[track / 2];
+
+ if (LOG) LOG_FORMATS("D64 track %.1f speed zone: %u\n", get_dos_track(track), tag->speed_zone[track]);
+ }
+ }
+
+ /* read format ID from directory */
+ /*
+ id1, id2 are the same for extended d64 (i.e. with error tables), for d67 and for d71
+
+ for d81 they are at track 40 bytes 0x17 & 0x18
+ for d80 & d82 they are at track 39 bytes 0x18 & 0x19
+ */
+ if (dos == DOS25)
+ floppy_image_read(floppy, id, tag->track_offset[0][38] + 0x18, 2);
+ else
+ floppy_image_read(floppy, id, tag->track_offset[0][34] + 0xa2, 2);
+
+ tag->id1 = id[0];
+ tag->id2 = id[1];
+
+ if (LOG) LOG_FORMATS("D64 format ID: %02x%02x\n", id[0], id[1]);
+
+ /* read errors */
+ if (tag->has_errors)
+ {
+ if (LOG) LOG_FORMATS("D64 error blocks: %u %u\n", errors_size, track_offset);
+ floppy_image_read(floppy, tag->error, track_offset, errors_size);
+ }
+ else
+ {
+ memset(tag->error, ERROR_00, MAX_ERROR_SECTORS);
+ }
+
+ /* set callbacks */
+ callbacks = floppy_callbacks(floppy);
+
+ callbacks->read_track = d64_read_track;
+ callbacks->write_track = d64_write_track;
+ callbacks->get_heads_per_disk = d64_get_heads_per_disk;
+ callbacks->get_tracks_per_disk = d64_get_tracks_per_disk;
+ callbacks->get_track_size = d64_get_track_size;
+
+ return FLOPPY_ERROR_SUCCESS;
+}