summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/lib/formats/z80ne_dsk.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/lib/formats/z80ne_dsk.c')
-rw-r--r--trunk/src/lib/formats/z80ne_dsk.c848
1 files changed, 848 insertions, 0 deletions
diff --git a/trunk/src/lib/formats/z80ne_dsk.c b/trunk/src/lib/formats/z80ne_dsk.c
new file mode 100644
index 00000000000..720687d8272
--- /dev/null
+++ b/trunk/src/lib/formats/z80ne_dsk.c
@@ -0,0 +1,848 @@
+/*********************************************************************
+
+ formats/z80ne_dsk.c
+
+ Nuova Elettronica Z80NE disk images
+
+*********************************************************************/
+
+#include <assert.h>
+#include "z80ne_dsk.h"
+#include "basicdsk.h"
+#include "imageutl.h"
+
+/* -----------------------------------------------------------------------
+ * DMK file format
+ *
+ * David M. Keil's disk image format is aptly called an 'on disk' image
+ * format. This means that whatever written to the disk is encoded into
+ * the image file. IDAMS, sector headers, trailing CRCs, and intra-sector
+ * spacing. *
+ *
+ * HEADER DESCRIPTION:
+ *
+ * Offset Bytes Field Description
+ * ------ ----- ------------ -----------
+ * 0 1 write_prot 0xff = Write Protected, 0x00 = R/W
+ * 1 1 tracks Number of tracks
+ * 2 2 track_length Bytes per track on DMK file (little endian)
+ * 4 1 disk_options Miscellaneous flags
+ * bit 0-3: Unused
+ * bit 4: Sides on DMK file
+ * 1=single, 0=double
+ * bit 5: Unused
+ * bit 6: Single Density DMK file
+ * 1=single (bytes written once
+ * on IDAMs)
+ * 0=double (bytes written
+ * two times for SD IDAMs)
+ * 5 7 reserved Reserved for future use
+ * 12 4 real_disk_code If this is 0x12345678 (little endian)
+ * then access a real disk drive
+ * (unsupported)
+ *
+ * Each track begins with a track TOC, consisting of 64 little endian 16-bit
+ * integers. Each integer has the following format:
+ * bit 0-13: Offset from beginning of track to 'FE' byte of IDAM
+ * Note these are always sorted from first to last. All empty
+ * entries are 0x00
+ * bit 14: Undefined (reserved)
+ * bit 15: Sector double density (0=SD 1=DD)
+ * If not set and SD DMK flag is set then bytes are written
+ * once.
+ * ----------------------------------------------------------------------- */
+
+struct dmk_tag
+{
+ int heads;
+ int tracks;
+ UINT32 track_size;
+};
+
+/* DMK file structure
+ * See WD1711 documentation
+ */
+/* DMK file header */
+#define DMK_HEADER_LEN 16 /* DMK header */
+ /* For every track (40) */
+ #define DMK_TOC_LEN 128 /* 64 16-bit relative offsets to IDAMs */
+ #define DMK_GAP1_LEN 32 /* GAP1 (26 bytes 0xFF and 6 bytes 0x00) */
+
+ /* For every sector (10) 301 bytes */
+ #define DMK_IDAM_LEN 7 /* IDAM (0xFE, Track, 0x00, Sector, 0x00, CRChi, CRClo) */
+ #define DMK_ID_GAP_LEN 17 /* GAP2 (11 bytes 0xFF and 6 bytes 0x00) */
+ #define DMK_DAM_LEN 1 /* Data Address Mark (0xFB) */
+ #define DMK_DATA_LEN 256 /* Data */
+ #define DMK_DATA_CRC_LEN 2 /* Data CRC (CRChi, CRClo) */
+ #define DMK_DATA_GAP_LEN 18 /* GAP3 (12 bytes 0xFF and 6 bytes 0x00) (not for last sector) */
+
+ #define DMK_EXTRA_TRACK_LENGTH (176) /* GAP4 (192 bytes 0xFF) */
+/* End of DMK file structure */
+
+#define dmk_idam_type(x) (x)[0]
+#define dmk_idam_track(x) (x)[1]
+#define dmk_idam_side(x) (x)[2]
+#define dmk_idam_sector(x) (x)[3]
+#define dmk_idam_sectorlength(x) (x)[4]
+#define dmk_idam_crc(x) (((x)[5] << 8) + (x)[6])
+#define dmk_idam_set_crc(x, crc) (x)[5] = ((crc) >> 8); (x)[6] = ((crc) >> 0);
+
+static const char needle_data[] = "\x00\x00\x00\x00\x00\x00\x00\x00\xFB\xFB";
+static const char needle_deleted_data_fa[] = "\x00\x00\x00\x00\x00\x00\x00\x00\xFA\xFA";
+static const char needle_deleted_data_f8[] = "\x00\x00\x00\x00\x00\x00\x00\x00\xF8\xF8";
+
+
+static struct dmk_tag *get_dmk_tag(floppy_image_legacy *floppy)
+{
+ return (dmk_tag *)floppy_tag(floppy);
+}
+
+
+static floperr_t z80ne_dmk_get_track_offset(floppy_image_legacy *floppy, int head, int track, UINT64 *offset)
+{
+ struct dmk_tag *tag = get_dmk_tag(floppy);
+
+ if ((head < 0) || (head >= tag->heads) || (track < 0) || (track >= tag->tracks))
+ return FLOPPY_ERROR_SEEKERROR;
+
+ *offset = track;
+ *offset *= tag->heads;
+ *offset += head;
+ *offset *= tag->track_size;
+ *offset += DMK_HEADER_LEN;
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+static UINT32 z80ne_dmk_min_track_size(int sectors, int sector_length)
+{
+ int sector_physical_length;
+ sector_physical_length = (DMK_IDAM_LEN + DMK_ID_GAP_LEN + DMK_DAM_LEN + sector_length + DMK_DATA_CRC_LEN + DMK_DATA_GAP_LEN);
+ return DMK_TOC_LEN + 2 * (DMK_GAP1_LEN + (sectors * sector_physical_length) - DMK_DATA_GAP_LEN + DMK_EXTRA_TRACK_LENGTH);
+}
+
+
+
+static floperr_t z80ne_dmk_read_track(floppy_image_legacy *floppy, int head, int track, UINT64 offset, void *buffer, size_t buflen)
+{
+ floperr_t err;
+ UINT64 track_offset;
+
+ err = z80ne_dmk_get_track_offset(floppy, head, track, &track_offset);
+ if (err)
+ return err;
+
+ floppy_image_read(floppy, buffer, offset + track_offset, buflen);
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+static floperr_t z80ne_dmk_write_track(floppy_image_legacy *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buflen)
+{
+ floperr_t err;
+ UINT64 track_offset;
+
+ err = z80ne_dmk_get_track_offset(floppy, head, track, &track_offset);
+ if (err)
+ return err;
+
+ floppy_image_write(floppy, buffer, offset + track_offset, buflen);
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+static floperr_t z80ne_dmk_get_track_data_offset(floppy_image_legacy *floppy, int head, int track, UINT64 *offset)
+{
+ *offset = DMK_TOC_LEN + 1;
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+static floperr_t z80ne_dmk_format_track(floppy_image_legacy *floppy, int head, int track, option_resolution *params)
+{
+ int sectors;
+ int sector_length;
+ int interleave;
+ int first_sector_id;
+ floperr_t err;
+ int physical_sector;
+ int logical_sector;
+ int track_position;
+ UINT16 idam_offset;
+ UINT16 crc;
+ UINT8 *track_data;
+ void *track_data_v;
+ UINT32 max_track_size;
+ int *sector_map = NULL;
+ UINT8 *local_sector = NULL;
+ int local_sector_size;
+ int sector_position;
+ int i;
+
+ err = FLOPPY_ERROR_SUCCESS;
+
+ sectors = option_resolution_lookup_int(params, PARAM_SECTORS);
+ sector_length = option_resolution_lookup_int(params, PARAM_SECTOR_LENGTH);
+ interleave = option_resolution_lookup_int(params, PARAM_INTERLEAVE);
+ first_sector_id = option_resolution_lookup_int(params, PARAM_FIRST_SECTOR_ID);
+
+ max_track_size = get_dmk_tag(floppy)->track_size;
+
+ if (sectors > DMK_TOC_LEN/2)
+ {
+ err = FLOPPY_ERROR_INTERNAL;
+ goto done;
+ }
+
+ if (max_track_size < z80ne_dmk_min_track_size(sectors, sector_length))
+ {
+ err = FLOPPY_ERROR_NOSPACE;
+ goto done;
+ }
+
+ err = floppy_load_track(floppy, head, track, TRUE, &track_data_v, NULL);
+ if (err)
+ goto done;
+ track_data = (UINT8 *) track_data_v;
+
+ /* set up sector map */
+ sector_map = (int*)malloc(sectors * sizeof(*sector_map));
+ if (!sector_map)
+ {
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+ memset(sector_map, 0xFF, sectors * sizeof(*sector_map));
+
+ physical_sector = 0;
+ for (logical_sector = 0; logical_sector < sectors; logical_sector++)
+ {
+ while(sector_map[physical_sector] >= 0)
+ {
+ physical_sector++;
+ physical_sector %= sectors;
+ }
+
+ sector_map[physical_sector] = logical_sector + first_sector_id;
+ physical_sector += interleave + 1;
+ physical_sector %= sectors;
+ }
+
+ /* set up a local physical sector template */
+ local_sector_size = (DMK_IDAM_LEN +
+ DMK_ID_GAP_LEN +
+ DMK_DAM_LEN +
+ sector_length +
+ DMK_DATA_CRC_LEN +
+ DMK_DATA_GAP_LEN);
+ local_sector = (UINT8*)malloc(local_sector_size);
+ if (!local_sector)
+ {
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+ memset(local_sector, 0x00, local_sector_size);
+
+ /* set up track table of contents */
+ physical_sector = 0;
+
+ /* write GAP1 (track lead in) */
+ memset(&track_data[DMK_TOC_LEN], 0xFF, 52);
+ memset(&track_data[DMK_TOC_LEN+52], 0x00, 12);
+ track_position = DMK_TOC_LEN + DMK_GAP1_LEN * 2;
+
+ /*
+ * prepare template in local sector
+ */
+
+ /* IDAM */
+ sector_position = 0;
+ dmk_idam_type( &local_sector[sector_position]) = 0xFE;
+ dmk_idam_track( &local_sector[sector_position]) = track;
+ dmk_idam_side( &local_sector[sector_position]) = head;
+ dmk_idam_sector( &local_sector[sector_position]) = 0; /* replace in sector instances */
+ dmk_idam_sectorlength( &local_sector[sector_position]) = compute_log2(sector_length / 128);
+ dmk_idam_set_crc( &local_sector[sector_position], 0); /* replace in sector instances */
+ sector_position += 7;
+
+ /* GAP2 (ID GAP) */
+ memset(&local_sector[sector_position], 0xFF, 11);
+ sector_position += 11;
+ memset(&local_sector[sector_position], 0x00, 6);
+ sector_position += 6;
+
+ /* sector body */
+ local_sector[sector_position] = 0xFB;
+ memset(&local_sector[sector_position + 1], floppy_get_filler(floppy), sector_length);
+ crc = ccitt_crc16(0xffff, &local_sector[sector_position], sector_length + 1);
+ local_sector[sector_position + sector_length + 1] = (UINT8) (crc >> 8);
+ local_sector[sector_position + sector_length + 2] = (UINT8) (crc >> 0);
+ sector_position += sector_length + 3;
+
+ /* GAP3 (Data GAP) */
+ memset(&local_sector[sector_position], 0xFF, 12);
+ sector_position += 12;
+ memset(&local_sector[sector_position], 0x00, 6);
+ sector_position += 6;
+
+ /*
+ * start of track data
+ */
+ while(physical_sector < DMK_TOC_LEN/2)
+ {
+ if (physical_sector >= sectors)
+ {
+ /* no more sectors */
+ idam_offset = 0;
+ }
+ else
+ {
+ /* this is a sector */
+ idam_offset = track_position;
+ logical_sector = sector_map[physical_sector];
+
+ /* update IDAM in local sector template */
+ dmk_idam_sector( &local_sector[0]) = logical_sector; /* update sector number in sector instance */
+ crc = ccitt_crc16(0xffff, &local_sector[0], DMK_IDAM_LEN - 2); /* and recalculate crc */
+ dmk_idam_set_crc( &local_sector[0], crc);
+
+ /* write local sector in track doubling every byte (DMK double density mode) */
+ for(i=0; i<local_sector_size; i++)
+ track_data[track_position + i*2] = track_data[track_position + (i*2) + 1] = local_sector[i];
+ track_position += (2 * local_sector_size);
+ if (physical_sector == (sectors-1)) /* on last sector remove GAP3 */
+ track_position -= (2 * DMK_DATA_GAP_LEN);
+ }
+
+ /* write the TOC entry */
+ track_data[physical_sector * 2 + 0] = (UINT8) (idam_offset >> 0);
+ track_data[physical_sector * 2 + 1] = (UINT8) (idam_offset >> 8);
+
+ physical_sector++;
+ }
+
+ /* write track footer GAP4 */
+ assert(max_track_size >= (UINT32)track_position);
+ memset(&track_data[track_position], 0xFF, max_track_size - track_position);
+
+done:
+ if (sector_map)
+ free(sector_map);
+ if (local_sector)
+ free(local_sector);
+
+ return err;
+}
+
+
+
+static int z80ne_dmk_get_heads_per_disk(floppy_image_legacy *floppy)
+{
+ return get_dmk_tag(floppy)->heads;
+}
+
+
+
+static int z80ne_dmk_get_tracks_per_disk(floppy_image_legacy *floppy)
+{
+ return get_dmk_tag(floppy)->tracks;
+}
+
+
+
+static UINT32 z80ne_dmk_get_track_size(floppy_image_legacy *floppy, int head, int track)
+{
+ return get_dmk_tag(floppy)->track_size;
+}
+
+static UINT8 * my_memmem(UINT8 *haystack, size_t haystacklen,
+ UINT8 *needle, size_t needlelen)
+{
+ register UINT8 *h = haystack;
+ register UINT8 *n = needle;
+ register size_t hl = haystacklen;
+ register size_t nl = needlelen;
+ register size_t i;
+
+ if (nl == 0) return haystack; /* The first occurrence of the empty string is deemed to occur at
+ the beginning of the string. */
+ if (hl < nl)
+ return NULL;
+
+ for( i = 0; (i < hl) && (i + nl <= hl ); i++ )
+ if (h[i] == *n) /* first match */
+ if ( !memcmp(&h[i]+1, n + 1, nl - 1) )
+ return (haystack+i); /* returns a pointer to the substring */
+
+ return (UINT8 *)NULL; /* not found */
+}
+
+
+
+static floperr_t z80ne_dmk_seek_sector_in_track(floppy_image_legacy *floppy, int head, int track, int sector, int sector_is_index, int dirtify, UINT8 **sector_data, UINT32 *sector_length)
+{
+ struct dmk_tag *tag = get_dmk_tag(floppy);
+ floperr_t err;
+ size_t idam_offset = 0;
+ UINT16 calculated_crc;
+ size_t i, j;
+ size_t offs;
+ /*int state;*/
+ UINT8 *track_data;
+ void *track_data_v;
+ size_t track_length;
+ size_t sec_len;
+ UINT8 *local_idam = NULL;
+ int local_idam_size;
+ UINT8 *sec_data;
+
+
+ err = FLOPPY_ERROR_SUCCESS;
+
+ err = floppy_load_track(floppy, head, track, dirtify, &track_data_v, &track_length);
+ if (err)
+ goto done;
+ track_data = (UINT8 *) track_data_v;
+
+ /* set up a local IDAM space */
+ local_idam_size = (DMK_IDAM_LEN +
+ DMK_ID_GAP_LEN +
+ DMK_DAM_LEN +
+ DMK_DATA_GAP_LEN);
+ local_idam = (UINT8*)malloc(local_idam_size);
+ if (!local_idam)
+ {
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+ memset(local_idam, 0x00, local_idam_size);
+
+ /* search for matching IDAM */
+ for (i = 0; i < DMK_TOC_LEN / 2; i++)
+ {
+ idam_offset = track_data[i * 2 + 1];
+ idam_offset <<= 8;
+ idam_offset |= track_data[i * 2 + 0];
+ idam_offset &= 0x3FFF;
+
+ if (idam_offset == 0)
+ {
+ /* we've reached the end of the road */
+ i = DMK_TOC_LEN;
+ break;
+ }
+
+ if ((idam_offset + DMK_IDAM_LEN * 2) >= tag->track_size)
+ continue;
+
+ /* overflowing the track? */
+ if ((idam_offset + local_idam_size * 2) >= tag->track_size)
+ {
+ err = FLOPPY_ERROR_SEEKERROR;
+ goto done;
+ }
+
+ /* create a local copy of sector of IDAM */
+ for(j = 0; j < (DMK_IDAM_LEN + DMK_ID_GAP_LEN + DMK_DAM_LEN); j++)
+ local_idam[j] = track_data[idam_offset + j*2];
+
+ /* from now on all operations are done in local copy of IDAM */
+ calculated_crc = ccitt_crc16(0xffff, &local_idam[0], DMK_IDAM_LEN - 2);
+
+ /* check IDAM integrity and check for matching sector */
+ if (calculated_crc == dmk_idam_crc(&local_idam[0]))
+ {
+ if (sector_is_index)
+ {
+ /* the sector is indexed; decrement the index and go */
+ if (sector-- == 0)
+ break;
+ }
+ else
+ {
+ /* check for matching sector ID */
+ if (sector == dmk_idam_sector(&local_idam[0]))
+ break;
+ }
+ }
+ }
+
+ if (i >= DMK_TOC_LEN / 2)
+ {
+ err = FLOPPY_ERROR_SEEKERROR;
+ goto done;
+ }
+
+ /* we found a matching sector ID */
+ /*state = 0;*/
+ offs = idam_offset + 2 * DMK_IDAM_LEN;
+
+ /* find pattern 0x0000FB; this represents the start of a data sector */
+ sec_data = my_memmem(&track_data[offs], 4 * (DMK_ID_GAP_LEN + DMK_DAM_LEN), (UINT8 *)needle_data, 10);
+ if(!sec_data)
+ {
+ /* find pattern 0x0000FA; this represents the start of a deleted data sector (used for system sectors) */
+ sec_data = my_memmem(&track_data[offs], 4 * (DMK_ID_GAP_LEN + DMK_DAM_LEN), (UINT8 *)needle_deleted_data_fa, 10);
+ }
+ if(!sec_data)
+ {
+ /* find pattern 0x0000F8; this represents the start of a deleted data sector (used for system sectors) */
+ sec_data = my_memmem(&track_data[offs], 4 * (DMK_ID_GAP_LEN + DMK_DAM_LEN), (UINT8 *)needle_deleted_data_f8, 10);
+ }
+
+ if(sec_data)
+ sec_data += 10;
+ else
+ {
+ err = FLOPPY_ERROR_SEEKERROR;
+ goto done;
+ }
+ offs = sec_data - &track_data[offs];
+
+ sec_len = 128 << dmk_idam_sectorlength(&local_idam[0]);
+
+ if ((offs + 2 * sec_len) > track_length)
+ {
+ err = FLOPPY_ERROR_INVALIDIMAGE;
+ goto done;
+ }
+
+ if (sector_data)
+ *sector_data = sec_data;
+ if (sector_length)
+ *sector_length = sec_len;
+done:
+ if (local_idam)
+ free(local_idam);
+ return err;
+}
+
+
+
+static floperr_t z80ne_dmk_get_sector_length(floppy_image_legacy *floppy, int head, int track, int sector, UINT32 *sector_length)
+{
+ return z80ne_dmk_seek_sector_in_track(floppy, head, track, sector, FALSE, FALSE, NULL, sector_length);
+}
+
+
+
+static floperr_t z80ne_dmk_get_indexed_sector_info(floppy_image_legacy *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags)
+{
+ floperr_t err;
+ size_t idam_offset;
+ UINT8 *track_data;
+ void *track_data_v;
+ unsigned long dam;
+
+ if (sector_index * 2 >= DMK_TOC_LEN)
+ return FLOPPY_ERROR_SEEKERROR;
+
+ err = floppy_load_track(floppy, head, track, FALSE, &track_data_v, NULL);
+ if (err)
+ return err;
+ track_data = (UINT8 *) track_data_v;
+
+ idam_offset = track_data[sector_index * 2 + 1];
+ idam_offset <<= 8;
+ idam_offset |= track_data[sector_index * 2 + 0];
+ idam_offset &= 0x3FFF;
+
+ if (idam_offset == 0)
+ return FLOPPY_ERROR_SEEKERROR;
+
+ /* determine type of data sector */
+ /* find pattern 0x0000FB; this represents the start of a data sector */
+ if ( my_memmem(&track_data[idam_offset+14], 4 * (DMK_ID_GAP_LEN + DMK_DAM_LEN), (UINT8 *)needle_data, 10) )
+ dam = 0;
+ /* find pattern 0x0000FA; this represents the start of a deleted data sector (used for system sectors) */
+ else if ( my_memmem(&track_data[idam_offset+14], 4 * (DMK_ID_GAP_LEN + DMK_DAM_LEN), (UINT8 *)needle_deleted_data_fa, 10) )
+ dam = ID_FLAG_DELETED_DATA;
+ /* find pattern 0x0000F8; this represents the start of a deleted data sector (used for system sectors) */
+ else if ( my_memmem(&track_data[idam_offset+14], 4 * (DMK_ID_GAP_LEN + DMK_DAM_LEN), (UINT8 *)needle_deleted_data_f8, 10) )
+ dam = ID_FLAG_DELETED_DATA;
+ else
+ return FLOPPY_ERROR_SEEKERROR;
+
+ if (cylinder)
+ *cylinder = (&track_data[idam_offset])[2];
+ if (side)
+ *side = (&track_data[idam_offset])[4];
+ if (sector)
+ *sector = (&track_data[idam_offset])[6];
+ if (sector_length)
+ *sector_length = 128 << (&track_data[idam_offset])[8];
+ if (flags)
+ *flags = dam;
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+static floperr_t internal_z80ne_dmk_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, int sector_is_index, void *buffer, size_t buflen)
+{
+ floperr_t err;
+ UINT32 sector_length;
+ UINT16 crc_on_disk;
+ UINT16 calculated_crc;
+ UINT8 *sector_data = NULL;
+ UINT8 *local_sector = NULL;
+ int local_sector_size;
+ int i;
+
+ err = FLOPPY_ERROR_SUCCESS;
+
+ /* get sector length and data pointer */
+ err = z80ne_dmk_seek_sector_in_track(floppy, head, track, sector, sector_is_index, FALSE, &sector_data, &sector_length);
+ if (err)
+ goto done;
+
+ /* set up a local physical sector space (DAM + data + crc + GAP) */
+ local_sector_size = (DMK_DAM_LEN + sector_length + DMK_DATA_CRC_LEN + DMK_DATA_GAP_LEN);
+ local_sector = (UINT8*)malloc(local_sector_size);
+ if (!local_sector)
+ {
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+ memset(local_sector, 0x00, local_sector_size);
+
+ /* get sector data */
+ /* create a local copy of sector data including DAM (for crc calculation) */
+ for(i = 0; i < (DMK_DAM_LEN + sector_length + DMK_DATA_CRC_LEN); i++)
+ local_sector[i] = (sector_data - 2)[i*2];
+
+ crc_on_disk = local_sector[sector_length + 0 + 1];
+ crc_on_disk <<= 8;
+ crc_on_disk += local_sector[sector_length + 1 + 1];
+
+ /* crc calculated including DAM */
+ calculated_crc = ccitt_crc16(0xffff, local_sector, sector_length+1);
+ if (calculated_crc != crc_on_disk)
+ {
+ err = FLOPPY_ERROR_INVALIDIMAGE;
+ goto done;
+ }
+
+ /* copy local sector data excluding DAM */
+ memcpy(buffer, local_sector+1, MIN(sector_length, buflen));
+
+ done:
+ if (local_sector)
+ free(local_sector);
+ return err;
+}
+
+
+
+static floperr_t internal_z80ne_dmk_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, int sector_is_index, const void *buffer, size_t buflen, int ddam)
+{
+ floperr_t err;
+ UINT32 sector_length;
+ UINT8 *sector_data;
+ UINT16 crc;
+ UINT8 *local_sector = NULL;
+ int local_sector_size;
+ int i;
+
+ err = FLOPPY_ERROR_SUCCESS;
+
+ /* get sector length */
+ err = z80ne_dmk_seek_sector_in_track(floppy, head, track, sector, sector_is_index, TRUE, &sector_data, &sector_length);
+ if (err)
+ goto done;
+
+ /* set up a local physical sector space */
+ local_sector_size = (DMK_DAM_LEN + sector_length + DMK_DATA_CRC_LEN + DMK_DATA_GAP_LEN);
+ local_sector = (UINT8*)malloc(local_sector_size);
+ if (!local_sector)
+ {
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+ memset(local_sector, 0x00, local_sector_size);
+ if(!ddam)
+ local_sector[0] = 0xFB;
+ else
+ local_sector[0] = 0xFA;
+
+ if (buflen > sector_length)
+ {
+ err = FLOPPY_ERROR_INTERNAL;
+ goto done;
+ }
+
+ memcpy(&local_sector[1], buffer, buflen);
+
+ crc = ccitt_crc16(0xffff, local_sector, DMK_DAM_LEN + sector_length);
+ local_sector[DMK_DAM_LEN + sector_length + 0] = crc >> 8;
+ local_sector[DMK_DAM_LEN + sector_length + 1] = crc >> 0;
+
+ /* write local sector in track doubling every byte (DMK double density mode) */
+ /* write DAM or DDAM too */
+ sector_data -= 2;
+ for(i=0; i < (DMK_DAM_LEN + sector_length + DMK_DATA_CRC_LEN); i++)
+ sector_data[i*2] = sector_data[(i*2)+1] = local_sector[i];
+
+ done:
+ if (local_sector)
+ free(local_sector);
+ return err;
+}
+
+
+
+static floperr_t z80ne_dmk_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen)
+{
+ return internal_z80ne_dmk_read_sector(floppy, head, track, sector, FALSE, buffer, buflen);
+}
+
+static floperr_t z80ne_dmk_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam)
+{
+ return internal_z80ne_dmk_write_sector(floppy, head, track, sector, FALSE, buffer, buflen, ddam);
+}
+
+static floperr_t z80ne_dmk_read_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen)
+{
+ return internal_z80ne_dmk_read_sector(floppy, head, track, sector, TRUE, buffer, buflen);
+}
+
+static floperr_t z80ne_dmk_write_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam)
+{
+ return internal_z80ne_dmk_write_sector(floppy, head, track, sector, TRUE, buffer, buflen, ddam);
+}
+
+
+
+static void z80ne_dmk_interpret_header(floppy_image_legacy *floppy, int *heads, int *tracks, int * sectors, int *track_size)
+{
+ UINT8 header[DMK_HEADER_LEN + DMK_TOC_LEN];
+ int i;
+ int idam_offset;
+ int sects;
+
+ floppy_image_read(floppy, header, 0, DMK_HEADER_LEN + DMK_TOC_LEN);
+
+ if (tracks)
+ *tracks = header[1];
+ if (heads)
+ *heads = (header[4] & 0x10) ? 1 : 2;
+ if (track_size)
+ *track_size = ((int) header[3]) * 0x100 + header[2];
+ if(sectors)
+ {
+ /* count sectors on first track */
+ sects = 0;
+ for (i = 0; i < DMK_TOC_LEN / 2; i++)
+ {
+ idam_offset = header[DMK_HEADER_LEN + i * 2 + 1];
+ idam_offset <<= 8;
+ idam_offset |= header[DMK_HEADER_LEN + i * 2 + 0];
+ idam_offset &= 0x3FFF;
+
+ if (idam_offset == 0)
+ break;
+ else
+ sects++;
+ }
+ *sectors = sects;
+ }
+
+}
+
+
+
+FLOPPY_CONSTRUCT(z80ne_dmk_construct)
+{
+ struct FloppyCallbacks *callbacks;
+ struct dmk_tag *tag;
+ UINT8 header[DMK_HEADER_LEN];
+ int heads, tracks, track_size, sectors, sector_length;
+
+ if (params)
+ {
+ heads = option_resolution_lookup_int(params, PARAM_HEADS);
+ tracks = option_resolution_lookup_int(params, PARAM_TRACKS);
+ sectors = option_resolution_lookup_int(params, PARAM_SECTORS);
+ sector_length = option_resolution_lookup_int(params, PARAM_SECTOR_LENGTH);
+
+ track_size = z80ne_dmk_min_track_size(sectors, sector_length);
+
+ memset(header, 0, sizeof(header));
+ header[1] = tracks;
+ header[2] = track_size >> 0;
+ header[3] = track_size >> 8;
+ header[4] = (heads == 2) ? 0x00 : 0x10;
+
+ floppy_image_write(floppy, header, 0, sizeof(header));
+ }
+ else
+ {
+ z80ne_dmk_interpret_header(floppy, &heads, &tracks, &sectors, &track_size);
+ }
+
+ tag = (dmk_tag*)floppy_create_tag(floppy, sizeof(struct dmk_tag));
+ if (!tag)
+ return FLOPPY_ERROR_OUTOFMEMORY;
+ tag->heads = heads;
+ tag->track_size = track_size;
+ tag->tracks = tracks;
+
+ callbacks = floppy_callbacks(floppy);
+ callbacks->read_track = z80ne_dmk_read_track;
+ callbacks->write_track = z80ne_dmk_write_track;
+ callbacks->get_track_data_offset = z80ne_dmk_get_track_data_offset;
+ callbacks->format_track = z80ne_dmk_format_track;
+ callbacks->get_heads_per_disk = z80ne_dmk_get_heads_per_disk;
+ callbacks->get_tracks_per_disk = z80ne_dmk_get_tracks_per_disk;
+ callbacks->get_track_size = z80ne_dmk_get_track_size;
+ callbacks->get_sector_length = z80ne_dmk_get_sector_length;
+ callbacks->get_indexed_sector_info = z80ne_dmk_get_indexed_sector_info;
+ callbacks->read_sector = z80ne_dmk_read_sector;
+ callbacks->write_sector = z80ne_dmk_write_sector;
+ callbacks->read_indexed_sector = z80ne_dmk_read_indexed_sector;
+ callbacks->write_indexed_sector = z80ne_dmk_write_indexed_sector;
+
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+FLOPPY_IDENTIFY(z80ne_dmk_identify)
+{
+ int heads, tracks, sectors, track_size;
+ UINT64 size, expected_size;
+
+ size = floppy_image_size(floppy);
+ z80ne_dmk_interpret_header(floppy, &heads, &tracks, &sectors, &track_size);
+ expected_size = DMK_HEADER_LEN + (heads * tracks * track_size);
+ *vote = (size == expected_size) ? 90 : 0;
+ *vote += (sectors == 10) ? 10 : 0;
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+/* ----------------------------------------------------------------------- */
+
+LEGACY_FLOPPY_OPTIONS_START( z80ne )
+ /*
+ * Single side
+ * Single density
+ * FM, 125 kbit/s,
+ * 40 tracks, track ID: 0..39
+ * 10 sectors/track, sector ID: 0..9
+ * 256 bytes/sector
+ * Interleave 1 (sector ID sequence on track: 0 5 1 6 2 7 3 8 4 9)
+ * 3125 byte/track unformatted
+ * Rotation speed 300 rpm (5 rps)
+ */
+ LEGACY_FLOPPY_OPTION( z80ne_dmk, "zmk", "Z80NE DMK disk image", z80ne_dmk_identify, z80ne_dmk_construct, NULL,
+ HEADS([1])
+ TRACKS([40])
+ SECTORS([10])
+ SECTOR_LENGTH([256])
+ INTERLEAVE([1])
+ FIRST_SECTOR_ID([0]))
+LEGACY_FLOPPY_OPTIONS_END