summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/lib/formats/flopimg.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/lib/formats/flopimg.c')
-rw-r--r--trunk/src/lib/formats/flopimg.c2305
1 files changed, 2305 insertions, 0 deletions
diff --git a/trunk/src/lib/formats/flopimg.c b/trunk/src/lib/formats/flopimg.c
new file mode 100644
index 00000000000..c416d2c4ca0
--- /dev/null
+++ b/trunk/src/lib/formats/flopimg.c
@@ -0,0 +1,2305 @@
+/*********************************************************************
+
+ flopimg.c
+
+ Floppy disk image abstraction code
+
+*********************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <assert.h>
+#include <limits.h>
+
+#include "emu.h"
+#include "osdcore.h"
+#include "ioprocs.h"
+#include "flopimg.h"
+#include "pool.h"
+#include "imageutl.h"
+
+#define TRACK_LOADED 0x01
+#define TRACK_DIRTY 0x02
+
+
+struct _floppy_image
+{
+ struct io_generic io;
+
+ const struct FloppyFormat *floppy_option;
+ struct FloppyCallbacks format;
+
+ /* loaded track stuff */
+ int loaded_track_head;
+ int loaded_track_index;
+ UINT32 loaded_track_size;
+ void *loaded_track_data;
+ UINT8 loaded_track_status;
+ UINT8 flags;
+
+ /* tagging system */
+ object_pool *tags;
+ void *tag_data;
+};
+
+
+
+struct _floppy_params
+{
+ int param;
+ int value;
+};
+
+
+
+static floperr_t floppy_track_unload(floppy_image_legacy *floppy);
+
+OPTION_GUIDE_START(floppy_option_guide)
+ OPTION_INT('H', "heads", "Heads")
+ OPTION_INT('T', "tracks", "Tracks")
+ OPTION_INT('S', "sectors", "Sectors")
+ OPTION_INT('L', "sectorlength", "Sector Bytes")
+ OPTION_INT('I', "interleave", "Interleave")
+ OPTION_INT('F', "firstsectorid", "First Sector")
+OPTION_GUIDE_END
+
+
+static void floppy_close_internal(floppy_image_legacy *floppy, int close_file);
+
+/*********************************************************************
+ opening, closing and creating of floppy images
+*********************************************************************/
+
+/* basic floppy_image_legacy initialization common to floppy_open() and floppy_create() */
+static floppy_image_legacy *floppy_init(void *fp, const struct io_procs *procs, int flags)
+{
+ floppy_image_legacy *floppy;
+
+ floppy = (floppy_image_legacy *)malloc(sizeof(struct _floppy_image));
+ if (!floppy)
+ return NULL;
+
+ memset(floppy, 0, sizeof(*floppy));
+ floppy->tags = pool_alloc_lib(NULL);
+ floppy->tag_data = NULL;
+ floppy->io.file = fp;
+ floppy->io.procs = procs;
+ floppy->io.filler = 0xFF;
+ floppy->flags = (UINT8) flags;
+ return floppy;
+}
+
+
+
+/* main code for identifying and maybe opening a disk image; not exposed
+ * directly because this function is big and hideous */
+static floperr_t floppy_open_internal(void *fp, const struct io_procs *procs, const char *extension,
+ const struct FloppyFormat *floppy_options, int max_options, int flags, floppy_image_legacy **outfloppy,
+ int *outoption)
+{
+ floperr_t err;
+ floppy_image_legacy *floppy;
+ int best_option = -1;
+ int best_vote = 0;
+ int vote;
+ size_t i;
+
+ floppy = floppy_init(fp, procs, flags);
+ if (!floppy)
+ {
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+
+ /* vote on the best format */
+ for (i = 0; (i < max_options) && floppy_options[i].construct; i++)
+ {
+ if (!extension || !floppy_options[i].extensions || image_find_extension(floppy_options[i].extensions, extension))
+ {
+ if (floppy_options[i].identify)
+ {
+ vote = 0;
+ err = floppy_options[i].identify(floppy, &floppy_options[i], &vote);
+ if (err)
+ goto done;
+ }
+ else
+ {
+ vote = 1;
+ }
+
+ /* is this option a better one? */
+ if (vote > best_vote)
+ {
+ best_vote = vote;
+ best_option = i;
+ }
+ }
+ }
+
+ /* did we find a format? */
+ if (best_option == -1)
+ {
+ err = FLOPPY_ERROR_INVALIDIMAGE;
+ goto done;
+ }
+
+ if (outfloppy)
+ {
+ /* call the format constructor */
+ err = floppy_options[best_option].construct(floppy, &floppy_options[best_option], NULL);
+ if (err)
+ goto done;
+
+ floppy->floppy_option = &floppy_options[best_option];
+ }
+ if (best_vote != 100)
+ {
+ printf("Loading image that is not 100%% recognized\n");
+ }
+ err = FLOPPY_ERROR_SUCCESS;
+
+done:
+ /* if we have a floppy disk and we either errored or are not keeping it, close it */
+ if (floppy && (!outfloppy || err))
+ {
+ floppy_close_internal(floppy, FALSE);
+ floppy = NULL;
+ }
+
+ if (outoption)
+ *outoption = err ? -1 : best_option;
+ if (outfloppy)
+ *outfloppy = floppy;
+ return err;
+}
+
+
+
+floperr_t floppy_identify(void *fp, const struct io_procs *procs, const char *extension,
+ const struct FloppyFormat *formats, int *identified_format)
+{
+ return floppy_open_internal(fp, procs, extension, formats, INT_MAX, FLOPPY_FLAGS_READONLY, NULL, identified_format);
+}
+
+
+
+floperr_t floppy_open(void *fp, const struct io_procs *procs, const char *extension,
+ const struct FloppyFormat *format, int flags, floppy_image_legacy **outfloppy)
+{
+ return floppy_open_internal(fp, procs, extension, format, 1, flags, outfloppy, NULL);
+}
+
+
+
+floperr_t floppy_open_choices(void *fp, const struct io_procs *procs, const char *extension,
+ const struct FloppyFormat *formats, int flags, floppy_image_legacy **outfloppy)
+{
+ return floppy_open_internal(fp, procs, extension, formats, INT_MAX, flags, outfloppy, NULL);
+}
+
+
+
+static floperr_t option_to_floppy_error(optreserr_t oerr)
+{
+ floperr_t err;
+ switch(oerr) {
+ case OPTIONRESOLUTION_ERROR_SUCCESS:
+ err = FLOPPY_ERROR_SUCCESS;
+ break;
+ case OPTIONRESOLUTION_ERROR_OUTOFMEMORY:
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ break;
+ case OPTIONRESOLUTION_ERROR_PARAMOUTOFRANGE:
+ case OPTIONRESOLUTION_ERROR_PARAMNOTSPECIFIED:
+ case OPTIONRESOLUTION_ERROR_PARAMNOTFOUND:
+ case OPTIONRESOLUTION_ERROR_PARAMALREADYSPECIFIED:
+ case OPTIONRESOLUTION_ERROR_BADPARAM:
+ case OPTIONRESOLUTION_ERROR_SYNTAX:
+ default:
+ err = FLOPPY_ERROR_INTERNAL;
+ break;
+ };
+ return err;
+}
+
+
+
+floperr_t floppy_create(void *fp, const struct io_procs *procs, const struct FloppyFormat *format, option_resolution *parameters, floppy_image_legacy **outfloppy)
+{
+ floppy_image_legacy *floppy = NULL;
+ optreserr_t oerr;
+ floperr_t err;
+ int heads, tracks, h, t;
+ option_resolution *alloc_resolution = NULL;
+
+ assert(format);
+
+ /* create the new image */
+ floppy = floppy_init(fp, procs, 0);
+ if (!floppy)
+ {
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+
+ /* if this format expects creation parameters and none were specified, create some */
+ if (!parameters && format->param_guidelines)
+ {
+ alloc_resolution = option_resolution_create(floppy_option_guide, format->param_guidelines);
+ if (!alloc_resolution)
+ {
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+ parameters = alloc_resolution;
+ }
+
+ /* finish the parameters, if specified */
+ if (parameters)
+ {
+ oerr = option_resolution_finish(parameters);
+ if (oerr)
+ {
+ err = option_to_floppy_error(oerr);
+ goto done;
+ }
+ }
+
+ /* call the format constructor */
+ err = format->construct(floppy, format, parameters);
+ if (err)
+ goto done;
+
+ /* format the disk, ignoring if formatting not implemented */
+ if (floppy->format.format_track)
+ {
+ heads = floppy_get_heads_per_disk(floppy);
+ tracks = floppy_get_tracks_per_disk(floppy);
+
+ for (h = 0; h < heads; h++)
+ {
+ for (t = 0; t < tracks; t++)
+ {
+ err = floppy->format.format_track(floppy, h, t, parameters);
+ if (err)
+ goto done;
+ }
+ }
+ }
+
+ /* call the post_format function, if present */
+ if (floppy->format.post_format)
+ {
+ err = floppy->format.post_format(floppy, parameters);
+ if (err)
+ goto done;
+ }
+
+ floppy->floppy_option = format;
+ err = FLOPPY_ERROR_SUCCESS;
+
+done:
+ if (err && floppy)
+ {
+ floppy_close_internal(floppy, FALSE);
+ floppy = NULL;
+ }
+
+ if (outfloppy)
+ *outfloppy = floppy;
+ else if (floppy)
+ floppy_close_internal(floppy, FALSE);
+
+ if (alloc_resolution)
+ option_resolution_close(alloc_resolution);
+ return err;
+}
+
+
+
+static void floppy_close_internal(floppy_image_legacy *floppy, int close_file)
+{
+ if (floppy) {
+ floppy_track_unload(floppy);
+
+ if(floppy->floppy_option && floppy->floppy_option->destruct)
+ floppy->floppy_option->destruct(floppy, floppy->floppy_option);
+ if (close_file)
+ io_generic_close(&floppy->io);
+ if (floppy->loaded_track_data)
+ free(floppy->loaded_track_data);
+ pool_free_lib(floppy->tags);
+
+ free(floppy);
+ }
+}
+
+
+
+void floppy_close(floppy_image_legacy *floppy)
+{
+ floppy_close_internal(floppy, TRUE);
+}
+
+
+
+/*********************************************************************
+ functions useful in format constructors
+*********************************************************************/
+
+struct FloppyCallbacks *floppy_callbacks(floppy_image_legacy *floppy)
+{
+ assert(floppy);
+ return &floppy->format;
+}
+
+
+
+void *floppy_tag(floppy_image_legacy *floppy)
+{
+ assert(floppy);
+ return floppy->tag_data;
+}
+
+
+
+void *floppy_create_tag(floppy_image_legacy *floppy, size_t tagsize)
+{
+ floppy->tag_data = pool_malloc_lib(floppy->tags,tagsize);
+ return floppy->tag_data;
+}
+
+
+
+UINT8 floppy_get_filler(floppy_image_legacy *floppy)
+{
+ return floppy->io.filler;
+}
+
+
+
+void floppy_set_filler(floppy_image_legacy *floppy, UINT8 filler)
+{
+ floppy->io.filler = filler;
+}
+
+
+
+/*********************************************************************
+ calls for accessing the raw disk image
+*********************************************************************/
+
+void floppy_image_read(floppy_image_legacy *floppy, void *buffer, UINT64 offset, size_t length)
+{
+ io_generic_read(&floppy->io, buffer, offset, length);
+}
+
+
+
+void floppy_image_write(floppy_image_legacy *floppy, const void *buffer, UINT64 offset, size_t length)
+{
+ io_generic_write(&floppy->io, buffer, offset, length);
+}
+
+
+
+void floppy_image_write_filler(floppy_image_legacy *floppy, UINT8 filler, UINT64 offset, size_t length)
+{
+ io_generic_write_filler(&floppy->io, filler, offset, length);
+}
+
+
+
+UINT64 floppy_image_size(floppy_image_legacy *floppy)
+{
+ return io_generic_size(&floppy->io);
+}
+
+
+
+/*********************************************************************
+ calls for accessing disk image data
+*********************************************************************/
+
+static floperr_t floppy_readwrite_sector(floppy_image_legacy *floppy, int head, int track, int sector, int offset,
+ void *buffer, size_t buffer_len, int writing, int indexed, int ddam)
+{
+ floperr_t err;
+ const struct FloppyCallbacks *fmt;
+ size_t this_buffer_len;
+ UINT8 *alloc_buf = NULL;
+ UINT32 sector_length;
+ UINT8 *buffer_ptr = (UINT8 *)buffer;
+ floperr_t (*read_sector)(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen);
+ floperr_t (*write_sector)(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam);
+
+ fmt = floppy_callbacks(floppy);
+
+ /* choose proper calls for indexed vs non-indexed */
+ if (indexed)
+ {
+ read_sector = fmt->read_indexed_sector;
+ write_sector = fmt->write_indexed_sector;
+ if (!fmt->get_indexed_sector_info)
+ {
+ err = FLOPPY_ERROR_UNSUPPORTED;
+ goto done;
+ }
+ }
+ else
+ {
+ read_sector = fmt->read_sector;
+ write_sector = fmt->write_sector;
+ if (!fmt->get_sector_length)
+ {
+ err = FLOPPY_ERROR_UNSUPPORTED;
+ goto done;
+ }
+ }
+
+ /* check to make sure that the operation is supported */
+ if (!read_sector || (writing && !write_sector))
+ {
+ err = FLOPPY_ERROR_UNSUPPORTED;
+ goto done;
+ }
+
+ /* main loop */
+ while(buffer_len > 0)
+ {
+ /* find out the size of this sector */
+ if (indexed)
+ err = fmt->get_indexed_sector_info(floppy, head, track, sector, NULL, NULL, NULL, &sector_length, NULL);
+ else
+ err = fmt->get_sector_length(floppy, head, track, sector, &sector_length);
+ if (err)
+ goto done;
+
+ /* do we even do anything with this sector? */
+ if (offset < sector_length)
+ {
+ /* ok we will be doing something */
+ if ((offset > 0) || (buffer_len < sector_length))
+ {
+ /* we will be doing an partial read/write; in other words we
+ * will not be reading/writing a full sector */
+ if (alloc_buf) free(alloc_buf);
+ alloc_buf = (UINT8*)malloc(sector_length);
+ if (!alloc_buf)
+ {
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+
+ /* read the sector (we need to do this even when writing */
+ err = read_sector(floppy, head, track, sector, alloc_buf, sector_length);
+ if (err)
+ goto done;
+
+ this_buffer_len = MIN(buffer_len, sector_length - offset);
+
+ if (writing)
+ {
+ memcpy(alloc_buf + offset, buffer_ptr, this_buffer_len);
+
+ err = write_sector(floppy, head, track, sector, alloc_buf, sector_length, ddam);
+ if (err)
+ goto done;
+ }
+ else
+ {
+ memcpy(buffer_ptr, alloc_buf + offset, this_buffer_len);
+ }
+ offset += this_buffer_len;
+ offset %= sector_length;
+ }
+ else
+ {
+ this_buffer_len = sector_length;
+
+ if (writing)
+ err = write_sector(floppy, head, track, sector, buffer_ptr, sector_length, ddam);
+ else
+ err = read_sector(floppy, head, track, sector, buffer_ptr, sector_length);
+ if (err)
+ goto done;
+ }
+ }
+ else
+ {
+ /* skip this sector */
+ offset -= sector_length;
+ this_buffer_len = 0;
+ }
+
+ buffer_ptr += this_buffer_len;
+ buffer_len -= this_buffer_len;
+ sector++;
+ }
+
+ err = FLOPPY_ERROR_SUCCESS;
+
+done:
+ if (alloc_buf)
+ free(alloc_buf);
+ return err;
+}
+
+
+
+floperr_t floppy_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, int offset, void *buffer, size_t buffer_len)
+{
+ return floppy_readwrite_sector(floppy, head, track, sector, offset, buffer, buffer_len, FALSE, FALSE, 0);
+}
+
+
+
+floperr_t floppy_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, int offset, const void *buffer, size_t buffer_len, int ddam)
+{
+ return floppy_readwrite_sector(floppy, head, track, sector, offset, (void *) buffer, buffer_len, TRUE, FALSE, ddam);
+}
+
+
+
+floperr_t floppy_read_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector_index, int offset, void *buffer, size_t buffer_len)
+{
+ return floppy_readwrite_sector(floppy, head, track, sector_index, offset, buffer, buffer_len, FALSE, TRUE, 0);
+}
+
+
+
+floperr_t floppy_write_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector_index, int offset, const void *buffer, size_t buffer_len, int ddam)
+{
+ return floppy_readwrite_sector(floppy, head, track, sector_index, offset, (void *) buffer, buffer_len, TRUE, TRUE, ddam);
+}
+
+
+static floperr_t floppy_get_track_data_offset(floppy_image_legacy *floppy, int head, int track, UINT64 *offset)
+{
+ floperr_t err;
+ const struct FloppyCallbacks *callbacks;
+
+ *offset = 0;
+ callbacks = floppy_callbacks(floppy);
+ if (callbacks->get_track_data_offset)
+ {
+ err = callbacks->get_track_data_offset(floppy, head, track, offset);
+ if (err)
+ return err;
+ }
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+static floperr_t floppy_read_track_offset(floppy_image_legacy *floppy, int head, int track, UINT64 offset, void *buffer, size_t buffer_len)
+{
+ floperr_t err;
+ const struct FloppyCallbacks *format;
+
+ format = floppy_callbacks(floppy);
+
+ if (!format->read_track)
+ return FLOPPY_ERROR_UNSUPPORTED;
+
+ err = floppy_track_unload(floppy);
+ if (err)
+ return err;
+
+ err = format->read_track(floppy, head, track, offset, buffer, buffer_len);
+ if (err)
+ return err;
+
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+floperr_t floppy_read_track(floppy_image_legacy *floppy, int head, int track, void *buffer, size_t buffer_len)
+{
+ return floppy_read_track_offset(floppy, head, track, 0, buffer, buffer_len);
+}
+
+
+
+floperr_t floppy_read_track_data(floppy_image_legacy *floppy, int head, int track, void *buffer, size_t buffer_len)
+{
+ floperr_t err;
+ UINT64 offset;
+
+ err = floppy_get_track_data_offset(floppy, head, track, &offset);
+ if (err)
+ return err;
+
+ return floppy_read_track_offset(floppy, head, track, offset, buffer, buffer_len);
+}
+
+
+
+static floperr_t floppy_write_track_offset(floppy_image_legacy *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buffer_len)
+{
+ floperr_t err;
+
+ /* track writing supported? */
+ if (!floppy_callbacks(floppy)->write_track)
+ return FLOPPY_ERROR_UNSUPPORTED;
+
+ /* read only? */
+ if (floppy->flags & FLOPPY_FLAGS_READONLY)
+ return FLOPPY_ERROR_READONLY;
+
+ err = floppy_track_unload(floppy);
+ if (err)
+ return err;
+
+ err = floppy_callbacks(floppy)->write_track(floppy, head, track, offset, buffer, buffer_len);
+ if (err)
+ return err;
+
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+floperr_t floppy_write_track(floppy_image_legacy *floppy, int head, int track, const void *buffer, size_t buffer_len)
+{
+ return floppy_write_track_offset(floppy, head, track, 0, buffer, buffer_len);
+}
+
+
+
+floperr_t floppy_write_track_data(floppy_image_legacy *floppy, int head, int track, const void *buffer, size_t buffer_len)
+{
+ floperr_t err;
+ UINT64 offset;
+
+ err = floppy_get_track_data_offset(floppy, head, track, &offset);
+ if (err)
+ return err;
+
+ return floppy_write_track_offset(floppy, head, track, offset, buffer, buffer_len);
+}
+
+
+
+floperr_t floppy_format_track(floppy_image_legacy *floppy, int head, int track, option_resolution *parameters)
+{
+ floperr_t err;
+ struct FloppyCallbacks *format;
+ option_resolution *alloc_resolution = NULL;
+ optreserr_t oerr;
+
+ /* supported? */
+ format = floppy_callbacks(floppy);
+ if (!format->format_track)
+ {
+ err = FLOPPY_ERROR_UNSUPPORTED;
+ goto done;
+ }
+
+ /* create a dummy resolution; if no parameters were specified */
+ if (!parameters)
+ {
+ alloc_resolution = option_resolution_create(floppy_option_guide, floppy->floppy_option->param_guidelines);
+ if (!alloc_resolution)
+ {
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+ parameters = alloc_resolution;
+ }
+
+ oerr = option_resolution_finish(parameters);
+ if (oerr)
+ {
+ err = option_to_floppy_error(oerr);
+ goto done;
+ }
+
+ err = format->format_track(floppy, head, track, parameters);
+ if (err)
+ goto done;
+
+done:
+ if (alloc_resolution)
+ option_resolution_close(alloc_resolution);
+ return err;
+}
+
+
+
+int floppy_get_tracks_per_disk(floppy_image_legacy *floppy)
+{
+ return floppy_callbacks(floppy)->get_tracks_per_disk(floppy);
+}
+
+
+
+int floppy_get_heads_per_disk(floppy_image_legacy *floppy)
+{
+ return floppy_callbacks(floppy)->get_heads_per_disk(floppy);
+}
+
+
+
+UINT32 floppy_get_track_size(floppy_image_legacy *floppy, int head, int track)
+{
+ const struct FloppyCallbacks *fmt;
+
+ fmt = floppy_callbacks(floppy);
+ if (!fmt->get_track_size)
+ return 0;
+
+ return fmt->get_track_size(floppy, head, track);
+}
+
+
+
+floperr_t floppy_get_sector_length(floppy_image_legacy *floppy, int head, int track, int sector, UINT32 *sector_length)
+{
+ const struct FloppyCallbacks *fmt;
+
+ fmt = floppy_callbacks(floppy);
+ if (!fmt->get_sector_length)
+ return FLOPPY_ERROR_UNSUPPORTED;
+
+ return fmt->get_sector_length(floppy, head, track, sector, sector_length);
+}
+
+
+
+floperr_t floppy_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)
+{
+ const struct FloppyCallbacks *fmt;
+
+ fmt = floppy_callbacks(floppy);
+ if (!fmt->get_indexed_sector_info)
+ return FLOPPY_ERROR_UNSUPPORTED;
+
+ return fmt->get_indexed_sector_info(floppy, head, track, sector_index, cylinder, side, sector, sector_length, flags);
+}
+
+
+
+floperr_t floppy_get_sector_count(floppy_image_legacy *floppy, int head, int track, int *sector_count)
+{
+ floperr_t err;
+ int sector_index = 0;
+
+ do
+ {
+ err = floppy_get_indexed_sector_info(floppy, head, track, sector_index, NULL, NULL, NULL, NULL, NULL);
+ if (!err)
+ sector_index++;
+ }
+ while(!err);
+
+ if (sector_index && (err == FLOPPY_ERROR_SEEKERROR))
+ err = FLOPPY_ERROR_SUCCESS;
+ if (sector_count)
+ *sector_count = err ? 0 : sector_index;
+ return err;
+}
+
+
+
+int floppy_is_read_only(floppy_image_legacy *floppy)
+{
+ return floppy->flags & FLOPPY_FLAGS_READONLY;
+}
+
+
+
+UINT8 floppy_random_byte(floppy_image_legacy *floppy)
+{
+ /* can't use mame_rand(); this might not be in the core */
+#ifdef rand
+#undef rand
+#endif
+ return rand();
+}
+
+
+
+/*********************************************************************
+ calls for track based IO
+*********************************************************************/
+
+floperr_t floppy_load_track(floppy_image_legacy *floppy, int head, int track, int dirtify, void **track_data, size_t *track_length)
+{
+ floperr_t err;
+ void *new_loaded_track_data;
+ UINT32 track_size;
+
+ /* have we already loaded this track? */
+ if (((floppy->loaded_track_status & TRACK_LOADED) == 0) || (head != floppy->loaded_track_head) || (track != floppy->loaded_track_index))
+ {
+ err = floppy_track_unload(floppy);
+ if (err)
+ goto error;
+
+ track_size = floppy_callbacks(floppy)->get_track_size(floppy, head, track);
+
+ if (floppy->loaded_track_data) free(floppy->loaded_track_data);
+ new_loaded_track_data = malloc(track_size);
+ if (!new_loaded_track_data)
+ {
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ goto error;
+ }
+
+ floppy->loaded_track_data = new_loaded_track_data;
+ floppy->loaded_track_size = track_size;
+ floppy->loaded_track_head = head;
+ floppy->loaded_track_index = track;
+
+ err = floppy_callbacks(floppy)->read_track(floppy, floppy->loaded_track_head, floppy->loaded_track_index, 0, floppy->loaded_track_data, floppy->loaded_track_size);
+ if (err)
+ goto error;
+
+ floppy->loaded_track_status |= TRACK_LOADED | (dirtify ? TRACK_DIRTY : 0);
+ }
+ else
+ floppy->loaded_track_status |= (dirtify ? TRACK_DIRTY : 0);
+
+ if (track_data)
+ *track_data = floppy->loaded_track_data;
+ if (track_length)
+ *track_length = floppy->loaded_track_size;
+ return FLOPPY_ERROR_SUCCESS;
+
+error:
+ if (track_data)
+ *track_data = NULL;
+ if (track_length)
+ *track_length = 0;
+ return err;
+}
+
+
+
+static floperr_t floppy_track_unload(floppy_image_legacy *floppy)
+{
+ int err;
+ if (floppy->loaded_track_status & TRACK_DIRTY)
+ {
+ err = floppy_callbacks(floppy)->write_track(floppy, floppy->loaded_track_head, floppy->loaded_track_index, 0, floppy->loaded_track_data, floppy->loaded_track_size);
+ if (err)
+ return (floperr_t)err;
+ }
+
+ floppy->loaded_track_status &= ~(TRACK_LOADED | TRACK_DIRTY);
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+/*********************************************************************
+ accessors for meta information about the image
+*********************************************************************/
+
+const char *floppy_format_description(floppy_image_legacy *floppy)
+{
+ return floppy->floppy_option->description;
+}
+
+
+
+/*********************************************************************
+ misc calls
+*********************************************************************/
+
+const char *floppy_error(floperr_t err)
+{
+ static const char *const error_messages[] =
+ {
+ "The operation completed successfully",
+ "Fatal internal error",
+ "This operation is unsupported",
+ "Out of memory",
+ "Seek error",
+ "Invalid image",
+ "Attempted to write to read only image",
+ "No space left on image",
+ "Parameter out of range",
+ "Required parameter not specified"
+ };
+
+ if ((err < 0) || (err >= ARRAY_LENGTH(error_messages)))
+ return NULL;
+ return error_messages[err];
+}
+
+
+LEGACY_FLOPPY_OPTIONS_START(default)
+LEGACY_FLOPPY_OPTIONS_END
+
+
+/***************************************************************************
+
+ New implementation
+
+****************************************************************************
+
+ Copyright Olivier Galibert
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name 'MAME' nor the names of its contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+****************************************************************************/
+
+
+floppy_image::floppy_image(int _tracks, int _heads, UINT32 _form_factor)
+{
+ tracks = _tracks;
+ heads = _heads;
+
+ form_factor = _form_factor;
+ variant = 0;
+
+ memset(cell_data, 0, sizeof(cell_data));
+ memset(track_size, 0, sizeof(track_size));
+ memset(track_alloc_size, 0, sizeof(track_alloc_size));
+ memset(write_splice, 0, sizeof(write_splice));
+}
+
+floppy_image::~floppy_image()
+{
+ for (int i=0;i<tracks;i++) {
+ for (int j=0;j<heads;j++) {
+ global_free(cell_data[(i<<1) + j]);
+ }
+ }
+}
+
+void floppy_image::get_maximal_geometry(int &_tracks, int &_heads)
+{
+ _tracks = tracks;
+ _heads = heads;
+}
+
+void floppy_image::get_actual_geometry(int &_tracks, int &_heads)
+{
+ int maxt = tracks-1, maxh = heads-1;
+
+ while(maxt >= 0) {
+ for(int i=0; i<=maxh; i++)
+ if(get_track_size(maxt, i))
+ goto track_done;
+ maxt--;
+ }
+ track_done:
+ if(maxt >= 0)
+ while(maxh >= 0) {
+ for(int i=0; i<=maxt; i++)
+ if(get_track_size(i, maxh))
+ goto head_done;
+ maxh--;
+ }
+ head_done:
+ _tracks = maxt+1;
+ _heads = maxh+1;
+}
+
+void floppy_image::ensure_alloc(int track, int head)
+{
+ if(track_size[track][head] > track_alloc_size[track][head]) {
+ UINT32 new_size = track_size[track][head]*11/10;
+ UINT32 *new_array = global_alloc_array(UINT32, new_size);
+ if(track_alloc_size[track][head]) {
+ memcpy(new_array, cell_data[track][head], track_alloc_size[track][head]*4);
+ global_free(cell_data[track][head]);
+ }
+ cell_data[track][head] = new_array;
+ track_alloc_size[track][head] = new_size;
+ }
+}
+
+const char *floppy_image::get_variant_name(UINT32 form_factor, UINT32 variant)
+{
+ switch(variant) {
+ case SSSD: return "Single side, single density";
+ case SSDD: return "Single side, double density";
+ case DSDD: return "Double side, double density";
+ case DSHD: return "Double side, high density";
+ case DSED: return "Double side, extended density";
+ }
+ return "Unknown";
+}
+
+floppy_image_format_t::floppy_image_format_t()
+{
+ next = 0;
+}
+
+floppy_image_format_t::~floppy_image_format_t()
+{
+}
+
+void floppy_image_format_t::append(floppy_image_format_t *_next)
+{
+ if(next)
+ next->append(_next);
+ else
+ next = _next;
+}
+
+bool floppy_image_format_t::save(io_generic *, floppy_image *)
+{
+ return false;
+}
+
+bool floppy_image_format_t::extension_matches(const char *file_name) const
+{
+ const char *ext = strrchr(file_name, '.');
+ if(!ext)
+ return false;
+ ext++;
+ int elen = strlen(ext);
+ const char *rext = extensions();
+ for(;;) {
+ const char *next = strchr(rext, ',');
+ int rlen = next ? next - rext : strlen(rext);
+ if(rlen == elen && !memcmp(ext, rext, rlen))
+ return true;
+ if(next)
+ rext = next+1;
+ else
+ break;
+ }
+ return false;
+}
+
+bool floppy_image_format_t::type_no_data(int type) const
+{
+ return type == CRC_CCITT_START ||
+ type == CRC_AMIGA_START ||
+ type == CRC_MACHEAD_START ||
+ type == CRC_END ||
+ type == SECTOR_LOOP_START ||
+ type == SECTOR_LOOP_END ||
+ type == END;
+}
+
+bool floppy_image_format_t::type_data_mfm(int type, int p1, const gen_crc_info *crcs) const
+{
+ return !type_no_data(type) &&
+ type != RAW &&
+ type != RAWBITS &&
+ (type != CRC || (crcs[p1].type != CRC_CCITT && crcs[p1].type != CRC_AMIGA));
+}
+
+void floppy_image_format_t::collect_crcs(const desc_e *desc, gen_crc_info *crcs)
+{
+ memset(crcs, 0, MAX_CRC_COUNT * sizeof(*crcs));
+ for(int i=0; i != MAX_CRC_COUNT; i++)
+ crcs[i].write = -1;
+
+ for(int i=0; desc[i].type != END; i++)
+ switch(desc[i].type) {
+ case CRC_CCITT_START:
+ crcs[desc[i].p1].type = CRC_CCITT;
+ break;
+ case CRC_AMIGA_START:
+ crcs[desc[i].p1].type = CRC_AMIGA;
+ break;
+ case CRC_MACHEAD_START:
+ crcs[desc[i].p1].type = CRC_MACHEAD;
+ break;
+ }
+
+ for(int i=0; desc[i].type != END; i++)
+ if(desc[i].type == CRC) {
+ int j;
+ for(j = i+1; desc[j].type != END && type_no_data(desc[j].type); j++);
+ crcs[desc[i].p1].fixup_mfm_clock = type_data_mfm(desc[j].type, desc[j].p1, crcs);
+ }
+}
+
+int floppy_image_format_t::crc_cells_size(int type) const
+{
+ switch(type) {
+ case CRC_CCITT: return 32;
+ case CRC_AMIGA: return 64;
+ case CRC_MACHEAD: return 8;
+ default: return 0;
+ }
+}
+
+bool floppy_image_format_t::bit_r(const UINT32 *buffer, int offset)
+{
+ return (buffer[offset] & floppy_image::MG_MASK) == MG_1;
+}
+
+UINT32 floppy_image_format_t::bitn_r(const UINT32 *buffer, int offset, int count)
+{
+ UINT32 r = 0;
+ for(int i=0; i<count; i++)
+ r = (r << 1) | (UINT32) bit_r(buffer, offset+i);
+ return r;
+}
+
+void floppy_image_format_t::bit_w(UINT32 *buffer, int offset, bool val, UINT32 size)
+{
+ buffer[offset] = (val ? MG_1 : MG_0) | size;
+}
+
+void floppy_image_format_t::raw_w(UINT32 *buffer, int &offset, int n, UINT32 val, UINT32 size)
+{
+ for(int i=n-1; i>=0; i--)
+ bit_w(buffer, offset++, (val >> i) & 1, size);
+}
+
+void floppy_image_format_t::mfm_w(UINT32 *buffer, int &offset, int n, UINT32 val, UINT32 size)
+{
+ int prec = offset ? bit_r(buffer, offset-1) : 0;
+ for(int i=n-1; i>=0; i--) {
+ int bit = (val >> i) & 1;
+ bit_w(buffer, offset++, !(prec || bit), size);
+ bit_w(buffer, offset++, bit, size);
+ prec = bit;
+ }
+}
+
+void floppy_image_format_t::mfm_half_w(UINT32 *buffer, int &offset, int start_bit, UINT32 val, UINT32 size)
+{
+ int prec = offset ? bit_r(buffer, offset-1) : 0;
+ for(int i=start_bit; i>=0; i-=2) {
+ int bit = (val >> i) & 1;
+ bit_w(buffer, offset++, !(prec || bit), size);
+ bit_w(buffer, offset++, bit, size);
+ prec = bit;
+ }
+}
+
+void floppy_image_format_t::fixup_crc_amiga(UINT32 *buffer, const gen_crc_info *crc)
+{
+ UINT16 res = 0;
+ int size = crc->end - crc->start;
+ for(int i=1; i<size; i+=2)
+ if(bit_r(buffer, crc->start + i))
+ res = res ^ (0x8000 >> ((i >> 1) & 15));
+ int offset = crc->write;
+ mfm_w(buffer, offset, 16, 0);
+ mfm_w(buffer, offset, 16, res);
+}
+
+UINT16 floppy_image_format_t::calc_crc_ccitt(const UINT32 *buffer, int start, int end)
+{
+ UINT32 res = 0xffff;
+ int size = end - start;
+ for(int i=1; i<size; i+=2) {
+ res <<= 1;
+ if(bit_r(buffer, start + i))
+ res ^= 0x10000;
+ if(res & 0x10000)
+ res ^= 0x11021;
+ }
+ return res;
+}
+
+void floppy_image_format_t::fixup_crc_ccitt(UINT32 *buffer, const gen_crc_info *crc)
+{
+ int offset = crc->write;
+ mfm_w(buffer, offset, 16, calc_crc_ccitt(buffer, crc->start, crc->end));
+}
+
+void floppy_image_format_t::fixup_crc_machead(UINT32 *buffer, const gen_crc_info *crc)
+{
+ UINT8 v = 0;
+ for(int o = crc->start; o < crc->end; o+=8)
+ v = v ^ gcr6bw_tb[bitn_r(buffer, o, 8)];
+ int offset = crc->write;
+ raw_w(buffer, offset, 8, gcr6fw_tb[v]);
+}
+
+void floppy_image_format_t::fixup_crcs(UINT32 *buffer, gen_crc_info *crcs)
+{
+ for(int i=0; i != MAX_CRC_COUNT; i++)
+ if(crcs[i].write != -1) {
+ switch(crcs[i].type) {
+ case CRC_AMIGA: fixup_crc_amiga(buffer, crcs+i); break;
+ case CRC_CCITT: fixup_crc_ccitt(buffer, crcs+i); break;
+ case CRC_MACHEAD: fixup_crc_machead(buffer, crcs+i); break;
+ }
+ if(crcs[i].fixup_mfm_clock) {
+ int offset = crcs[i].write + crc_cells_size(crcs[i].type);
+ bit_w(buffer, offset, !((offset ? bit_r(buffer, offset-1) : false) || bit_r(buffer, offset+1)));
+ }
+ crcs[i].write = -1;
+ }
+}
+
+UINT32 floppy_image_format_t::gcr6_encode(UINT8 va, UINT8 vb, UINT8 vc)
+{
+ UINT32 r;
+ r = gcr6fw_tb[((va >> 2) & 0x30) | ((vb >> 4) & 0x0c) | ((vc >> 6) & 0x03)] << 24;
+ r |= gcr6fw_tb[va & 0x3f] << 16;
+ r |= gcr6fw_tb[vb & 0x3f] << 8;
+ r |= gcr6fw_tb[vc & 0x3f];
+ return r;
+}
+
+void floppy_image_format_t::gcr6_decode(UINT8 e0, UINT8 e1, UINT8 e2, UINT8 e3, UINT8 &va, UINT8 &vb, UINT8 &vc)
+{
+ e0 = gcr6bw_tb[e0];
+ e1 = gcr6bw_tb[e1];
+ e2 = gcr6bw_tb[e2];
+ e3 = gcr6bw_tb[e3];
+
+ va = ((e0 << 2) & 0xc0) | e1;
+ vb = ((e0 << 4) & 0xc0) | e2;
+ vc = ((e0 << 6) & 0xc0) | e3;
+}
+
+UINT16 floppy_image_format_t::gcr4_encode(UINT8 va)
+{
+ return (va << 7) | va | 0xaaaa;
+}
+
+UINT8 floppy_image_format_t::gcr4_decode(UINT8 e0, UINT8 e1)
+{
+ return ((e0 << 1) & 0xaa) | (e1 & 0x55);
+}
+
+
+int floppy_image_format_t::calc_sector_index(int num, int interleave, int skew, int total_sectors, int track_head)
+{
+ int i = 0;
+ int sec = 0;
+ // use interleave
+ while (i != num)
+ {
+ i++;
+ i += interleave;
+ i %= total_sectors;
+ sec++;
+ }
+ // use skew param
+ sec -= track_head * skew;
+ sec %= total_sectors;
+ if (sec < 0) sec += total_sectors;
+ return sec;
+}
+
+void floppy_image_format_t::generate_track(const desc_e *desc, int track, int head, const desc_s *sect, int sect_count, int track_size, floppy_image *image)
+{
+ UINT32 *buffer = global_alloc_array_clear(UINT32, track_size);
+
+ gen_crc_info crcs[MAX_CRC_COUNT];
+ collect_crcs(desc, crcs);
+
+ int offset = 0;
+ int index = 0;
+ int sector_loop_start = 0;
+ int sector_idx = 0;
+ int sector_cnt = 0;
+ int sector_limit = 0;
+ int sector_interleave = 0;
+ int sector_skew = 0;
+
+ while(desc[index].type != END) {
+ // printf("%d.%d.%d (%d) - %d %d\n", desc[index].type, desc[index].p1, desc[index].p2, index, offset, offset/8);
+ switch(desc[index].type) {
+ case MFM:
+ for(int i=0; i<desc[index].p2; i++)
+ mfm_w(buffer, offset, 8, desc[index].p1);
+ break;
+
+ case MFMBITS:
+ mfm_w(buffer, offset, desc[index].p2, desc[index].p1);
+ break;
+
+ case RAW:
+ for(int i=0; i<desc[index].p2; i++)
+ raw_w(buffer, offset, 16, desc[index].p1);
+ break;
+
+ case RAWBITS:
+ raw_w(buffer, offset, desc[index].p2, desc[index].p1);
+ break;
+
+ case TRACK_ID:
+ mfm_w(buffer, offset, 8, track);
+ break;
+
+ case TRACK_ID_GCR6:
+ raw_w(buffer, offset, 8, gcr6fw_tb[track & 0x3f]);
+ break;
+
+ case HEAD_ID:
+ mfm_w(buffer, offset, 8, head);
+ break;
+
+ case TRACK_HEAD_ID_GCR6:
+ raw_w(buffer, offset, 8, gcr6fw_tb[(track & 0x40 ? 1 : 0) | (head ? 0x20 : 0)]);
+ break;
+
+ case SECTOR_ID:
+ mfm_w(buffer, offset, 8, sect[sector_idx].sector_id);
+ break;
+
+ case SECTOR_ID_GCR6:
+ raw_w(buffer, offset, 8, gcr6fw_tb[sect[sector_idx].sector_id]);
+ break;
+
+ case SIZE_ID: {
+ int size = sect[sector_idx].size;
+ int id;
+ for(id = 0; size > 128; size >>=1, id++);
+ mfm_w(buffer, offset, 8, id);
+ break;
+ }
+
+ case SECTOR_INFO_GCR6:
+ raw_w(buffer, offset, 8, gcr6fw_tb[sect[sector_idx].sector_info]);
+ break;
+
+ case OFFSET_ID_O:
+ mfm_half_w(buffer, offset, 7, track*2+head);
+ break;
+
+ case OFFSET_ID_E:
+ mfm_half_w(buffer, offset, 6, track*2+head);
+ break;
+
+ case SECTOR_ID_O:
+ mfm_half_w(buffer, offset, 7, sector_idx);
+ break;
+
+ case SECTOR_ID_E:
+ mfm_half_w(buffer, offset, 6, sector_idx);
+ break;
+
+ case REMAIN_O:
+ mfm_half_w(buffer, offset, 7, desc[index].p1 - sector_idx);
+ break;
+
+ case REMAIN_E:
+ mfm_half_w(buffer, offset, 6, desc[index].p1 - sector_idx);
+ break;
+
+ case SECTOR_LOOP_START:
+ fixup_crcs(buffer, crcs);
+ sector_loop_start = index;
+ sector_idx = desc[index].p1;
+ sector_cnt = sector_idx;
+ sector_limit = desc[index].p2 == -1 ? sector_idx+sect_count-1 : desc[index].p2;
+ sector_idx = calc_sector_index(sector_cnt,sector_interleave,sector_skew,sector_limit+1,track*2 + head);
+ break;
+
+ case SECTOR_LOOP_END:
+ fixup_crcs(buffer, crcs);
+ if(sector_cnt < sector_limit) {
+ sector_cnt++;
+ sector_idx = calc_sector_index(sector_cnt,sector_interleave,sector_skew,sector_limit+1,track*2 + head);
+ index = sector_loop_start;
+ }
+ break;
+
+ case SECTOR_INTERLEAVE_SKEW:
+ sector_interleave = desc[index].p1;
+ sector_skew = desc[index].p2;
+ break;
+
+ case CRC_AMIGA_START:
+ case CRC_CCITT_START:
+ case CRC_MACHEAD_START:
+ crcs[desc[index].p1].start = offset;
+ break;
+
+ case CRC_END:
+ crcs[desc[index].p1].end = offset;
+ break;
+
+ case CRC:
+ crcs[desc[index].p1].write = offset;
+ offset += crc_cells_size(crcs[desc[index].p1].type);
+ break;
+
+ case SECTOR_DATA: {
+ const desc_s *csect = sect + (desc[index].p1 >= 0 ? desc[index].p1 : sector_idx);
+ for(int i=0; i != csect->size; i++)
+ mfm_w(buffer, offset, 8, csect->data[i]);
+ break;
+ }
+
+ case SECTOR_DATA_O: {
+ const desc_s *csect = sect + (desc[index].p1 >= 0 ? desc[index].p1 : sector_idx);
+ for(int i=0; i != csect->size; i++)
+ mfm_half_w(buffer, offset, 7, csect->data[i]);
+ break;
+ }
+
+ case SECTOR_DATA_E: {
+ const desc_s *csect = sect + (desc[index].p1 >= 0 ? desc[index].p1 : sector_idx);
+ for(int i=0; i != csect->size; i++)
+ mfm_half_w(buffer, offset, 6, csect->data[i]);
+ break;
+ }
+
+ case SECTOR_DATA_MAC: {
+ const desc_s *csect = sect + (desc[index].p1 >= 0 ? desc[index].p1 : sector_idx);
+ const UINT8 *data = csect->data;
+ int size = csect->size;
+ UINT8 ca = 0, cb = 0, cc = 0;
+ for(int i=0; i < size; i+=3) {
+ int dt = size-i;
+ UINT8 va = data[i];
+ UINT8 vb = dt > 1 ? data[i+1] : 0;
+ UINT8 vc = dt > 2 ? data[i+2] : 0;
+
+ cc = (cc << 1) | (cc >> 7);
+ int suma = ca + va + (cc & 1);
+ ca = suma;
+ va = va ^ cc;
+ int sumb = cb + vb + (suma >> 8);
+ cb = sumb;
+ vb = vb ^ ca;
+ cc = cc + vc + (sumb >> 8);
+ vc = vc ^ cb;
+
+ int nb = dt > 2 ? 32 : dt > 1 ? 24 : 16;
+ raw_w(buffer, offset, nb, gcr6_encode(va, vb, vc) >> (32-nb));
+ }
+ raw_w(buffer, offset, 32, gcr6_encode(ca, cb, cc));
+ break;
+ }
+
+ default:
+ printf("%d.%d.%d (%d) unhandled\n", desc[index].type, desc[index].p1, desc[index].p2, index);
+ break;
+ }
+ index++;
+ }
+
+ if(offset != track_size)
+ throw emu_fatalerror("Wrong track size in generate_track, expected %d, got %d\n", track_size, offset);
+
+ fixup_crcs(buffer, crcs);
+
+ generate_track_from_levels(track, head, buffer, track_size, 0, image);
+ global_free(buffer);
+}
+
+void floppy_image_format_t::normalize_times(UINT32 *buffer, int bitlen)
+{
+ unsigned int total_sum = 0;
+ for(int i=0; i != bitlen; i++)
+ total_sum += buffer[i] & floppy_image::TIME_MASK;
+
+ unsigned int current_sum = 0;
+ for(int i=0; i != bitlen; i++) {
+ UINT32 time = buffer[i] & floppy_image::TIME_MASK;
+ buffer[i] = (buffer[i] & floppy_image::MG_MASK) | (200000000ULL * current_sum / total_sum);
+ current_sum += time;
+ }
+}
+
+void floppy_image_format_t::generate_track_from_bitstream(int track, int head, const UINT8 *trackbuf, int track_size, floppy_image *image)
+{
+ // Maximal number of cells which happens when the buffer is all 1
+ image->set_track_size(track, head, track_size+1);
+ UINT32 *dest = image->get_buffer(track, head);
+ UINT32 *base = dest;
+
+ UINT32 cbit = floppy_image::MG_A;
+ UINT32 count = 0;
+ for(int i=0; i != track_size; i++)
+ if(trackbuf[i >> 3] & (0x80 >> (i & 7))) {
+ *dest++ = cbit | (count+1);
+ cbit = cbit == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A;
+ count = 1;
+ } else
+ count += 2;
+
+ if(count)
+ *dest++ = cbit | count;
+
+ int size = dest - base;
+ normalize_times(base, size);
+ image->set_track_size(track, head, size);
+ image->set_write_splice_position(track, head, 0);
+}
+
+void floppy_image_format_t::generate_track_from_levels(int track, int head, UINT32 *trackbuf, int track_size, int splice_pos, floppy_image *image)
+{
+ // Retrieve the angular splice pos before messing with the data
+ splice_pos = splice_pos % track_size;
+ UINT32 splice_angular_pos = trackbuf[splice_pos] & floppy_image::TIME_MASK;
+
+ // Check if we need to invert a cell to get an even number of
+ // transitions on the whole track
+ //
+ // Also check if all MG values are valid
+
+ int transition_count = 0;
+ for(int i=0; i<track_size; i++) {
+ switch(trackbuf[i] & floppy_image::MG_MASK) {
+ case MG_1:
+ transition_count++;
+ break;
+
+ case MG_W:
+ throw emu_fatalerror("Weak bits not yet handled, track %d head %d\n", track, head);
+ case MG_0:
+ case floppy_image::MG_N:
+ case floppy_image::MG_D:
+ break;
+
+ case floppy_image::MG_A:
+ case floppy_image::MG_B:
+ default:
+ throw emu_fatalerror("Incorrect MG information in generate_track_from_levels, track %d head %d\n", track, head);
+ }
+ }
+
+ if(transition_count & 1) {
+ int pos = splice_pos;
+ while((trackbuf[pos] & floppy_image::MG_MASK) != MG_0 && (trackbuf[pos] & floppy_image::MG_MASK) != MG_1) {
+ pos++;
+ if(pos == track_size)
+ pos = 0;
+ if(pos == splice_pos)
+ goto meh;
+ }
+ if((trackbuf[pos] & floppy_image::MG_MASK) == MG_0)
+ trackbuf[pos] = (trackbuf[pos] & floppy_image::TIME_MASK) | MG_1;
+ else
+ trackbuf[pos] = (trackbuf[pos] & floppy_image::TIME_MASK) | MG_0;
+
+ meh:
+ ;
+
+ }
+
+ // Maximal number of cells which happens when the buffer is all MG_1/MG_N alternated, which would be 3/2
+ image->set_track_size(track, head, track_size*2);
+ UINT32 *dest = image->get_buffer(track, head);
+ UINT32 *base = dest;
+
+ UINT32 cbit = floppy_image::MG_A;
+ UINT32 count = 0;
+ for(int i=0; i<track_size; i++) {
+ UINT32 bit = trackbuf[i] & floppy_image::MG_MASK;
+ UINT32 time = trackbuf[i] & floppy_image::TIME_MASK;
+ if(bit == MG_0) {
+ count += time;
+ continue;
+ }
+ if(bit == MG_1) {
+ count += time >> 1;
+ *dest++ = cbit | count;
+ cbit = cbit == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A;
+ count = time - (time >> 1);
+ continue;
+ }
+ *dest++ = cbit | count;
+ *dest++ = trackbuf[i];
+ count = 0;
+ }
+
+ if(count)
+ *dest++ = cbit | count;
+
+ int size = dest - base;
+ normalize_times(base, size);
+ image->set_track_size(track, head, size);
+ image->set_write_splice_position(track, head, splice_angular_pos);
+}
+
+const UINT8 floppy_image_format_t::gcr6fw_tb[0x40] =
+{
+ 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,
+};
+
+const UINT8 floppy_image_format_t::gcr6bw_tb[0x100] =
+{
+ // 0 1 2 3 4 5 6 7 8 9 a b c d e f
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x03, 0x00, 0x04, 0x05, 0x06,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x08, 0x00, 0x00, 0x00, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+ 0x00, 0x00, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x00, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x1c, 0x1d, 0x1e,
+ 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x20, 0x21, 0x00, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x2a, 0x2b, 0x00, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32,
+ 0x00, 0x00, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x00, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+};
+
+// Atari ST Fastcopy Pro layouts
+
+#define SECTOR_42_HEADER(cid) \
+ { CRC_CCITT_START, cid }, \
+ { RAW, 0x4489, 3 }, \
+ { MFM, 0xfe, 1 }, \
+ { TRACK_ID }, \
+ { HEAD_ID }, \
+ { MFM, 0x42, 1 }, \
+ { MFM, 0x02, 1 }, \
+ { CRC_END, cid }, \
+ { CRC, cid }
+
+#define NORMAL_SECTOR(cid) \
+ { CRC_CCITT_START, cid }, \
+ { RAW, 0x4489, 3 }, \
+ { MFM, 0xfe, 1 }, \
+ { TRACK_ID }, \
+ { HEAD_ID }, \
+ { SECTOR_ID }, \
+ { SIZE_ID }, \
+ { CRC_END, cid }, \
+ { CRC, cid }, \
+ { MFM, 0x4e, 22 }, \
+ { MFM, 0x00, 12 }, \
+ { CRC_CCITT_START, cid+1 }, \
+ { RAW, 0x4489, 3 }, \
+ { MFM, 0xfb, 1 }, \
+ { SECTOR_DATA, -1 }, \
+ { CRC_END, cid+1 }, \
+ { CRC, cid+1 }
+
+const floppy_image_format_t::desc_e floppy_image_format_t::atari_st_fcp_9[] = {
+ { MFM, 0x4e, 501 },
+ { MFM, 0x00, 12 },
+
+ SECTOR_42_HEADER(1),
+
+ { MFM, 0x4e, 22 },
+ { MFM, 0x00, 12 },
+
+ { SECTOR_LOOP_START, 0, 8 },
+ NORMAL_SECTOR(2),
+ { MFM, 0x4e, 40 },
+ { MFM, 0x00, 12 },
+ { SECTOR_LOOP_END },
+
+ SECTOR_42_HEADER(4),
+
+ { MFM, 0x4e, 157 },
+
+ { END }
+};
+
+const floppy_image_format_t::desc_e floppy_image_format_t::atari_st_fcp_10_0[] = {
+ { MFM, 0x4e, 46 },
+ SECTOR_42_HEADER(1),
+ { MFM, 0x4e, 5 },
+
+ { SECTOR_LOOP_START, 0, 9 },
+ { MFM, 0x00, 12 },
+ NORMAL_SECTOR(4),
+ { MFM, 0x4e, 40 },
+ { SECTOR_LOOP_END },
+
+ { MFM, 0x4e, 49 },
+ { END }
+};
+
+const floppy_image_format_t::desc_e floppy_image_format_t::atari_st_fcp_10_1[] = {
+ { MFM, 0x4e, 20 },
+ { SECTOR_LOOP_START, 9, 9 },
+ { MFM, 0x4e, 40 },
+ { MFM, 0x00, 12 },
+ NORMAL_SECTOR(1),
+ { SECTOR_LOOP_END },
+
+ { MFM, 0x4e, 26 },
+ SECTOR_42_HEADER(3),
+ { MFM, 0x4e, 5 },
+
+ { SECTOR_LOOP_START, 0, 8 },
+ { MFM, 0x00, 12 },
+ NORMAL_SECTOR(4),
+ { MFM, 0x4e, 40 },
+ { SECTOR_LOOP_END },
+
+ { MFM, 0x4e, 49 },
+ { END },
+};
+
+const floppy_image_format_t::desc_e floppy_image_format_t::atari_st_fcp_10_2[] = {
+ { MFM, 0x4e, 20 },
+ { SECTOR_LOOP_START, 8, 9 },
+ { MFM, 0x4e, 40 },
+ { MFM, 0x00, 12 },
+ NORMAL_SECTOR(1),
+ { SECTOR_LOOP_END },
+
+ { MFM, 0x4e, 26 },
+ SECTOR_42_HEADER(3),
+ { MFM, 0x4e, 5 },
+
+ { SECTOR_LOOP_START, 0, 7 },
+ { MFM, 0x00, 12 },
+ NORMAL_SECTOR(4),
+ { MFM, 0x4e, 40 },
+ { SECTOR_LOOP_END },
+
+ { MFM, 0x4e, 49 },
+ { END },
+};
+
+const floppy_image_format_t::desc_e floppy_image_format_t::atari_st_fcp_10_3[] = {
+ { MFM, 0x4e, 20 },
+ { SECTOR_LOOP_START, 7, 9 },
+ { MFM, 0x4e, 40 },
+ { MFM, 0x00, 12 },
+ NORMAL_SECTOR(1),
+ { SECTOR_LOOP_END },
+
+ { MFM, 0x4e, 26 },
+ SECTOR_42_HEADER(3),
+ { MFM, 0x4e, 5 },
+
+ { SECTOR_LOOP_START, 0, 6 },
+ { MFM, 0x00, 12 },
+ NORMAL_SECTOR(4),
+ { MFM, 0x4e, 40 },
+ { SECTOR_LOOP_END },
+
+ { MFM, 0x4e, 49 },
+ { END },
+};
+
+const floppy_image_format_t::desc_e floppy_image_format_t::atari_st_fcp_10_4[] = {
+ { MFM, 0x4e, 20 },
+ { SECTOR_LOOP_START, 6, 9 },
+ { MFM, 0x4e, 40 },
+ { MFM, 0x00, 12 },
+ NORMAL_SECTOR(1),
+ { SECTOR_LOOP_END },
+
+ { MFM, 0x4e, 26 },
+ SECTOR_42_HEADER(3),
+ { MFM, 0x4e, 5 },
+
+ { SECTOR_LOOP_START, 0, 5 },
+ { MFM, 0x00, 12 },
+ NORMAL_SECTOR(4),
+ { MFM, 0x4e, 40 },
+ { SECTOR_LOOP_END },
+
+ { MFM, 0x4e, 49 },
+ { END },
+};
+
+const floppy_image_format_t::desc_e floppy_image_format_t::atari_st_fcp_10_5[] = {
+ { MFM, 0x4e, 20 },
+ { SECTOR_LOOP_START, 5, 9 },
+ { MFM, 0x4e, 40 },
+ { MFM, 0x00, 12 },
+ NORMAL_SECTOR(1),
+ { SECTOR_LOOP_END },
+
+ { MFM, 0x4e, 26 },
+ SECTOR_42_HEADER(3),
+ { MFM, 0x4e, 5 },
+
+ { SECTOR_LOOP_START, 0, 4 },
+ { MFM, 0x00, 12 },
+ NORMAL_SECTOR(4),
+ { MFM, 0x4e, 40 },
+ { SECTOR_LOOP_END },
+
+ { MFM, 0x4e, 49 },
+ { END },
+};
+
+const floppy_image_format_t::desc_e floppy_image_format_t::atari_st_fcp_10_6[] = {
+ { MFM, 0x4e, 20 },
+ { SECTOR_LOOP_START, 4, 9 },
+ { MFM, 0x4e, 40 },
+ { MFM, 0x00, 12 },
+ NORMAL_SECTOR(1),
+ { SECTOR_LOOP_END },
+
+ { MFM, 0x4e, 26 },
+ SECTOR_42_HEADER(3),
+ { MFM, 0x4e, 5 },
+
+ { SECTOR_LOOP_START, 0, 3 },
+ { MFM, 0x00, 12 },
+ NORMAL_SECTOR(4),
+ { MFM, 0x4e, 40 },
+ { SECTOR_LOOP_END },
+
+ { MFM, 0x4e, 49 },
+ { END },
+};
+
+const floppy_image_format_t::desc_e floppy_image_format_t::atari_st_fcp_10_7[] = {
+ { MFM, 0x4e, 20 },
+ { SECTOR_LOOP_START, 3, 9 },
+ { MFM, 0x4e, 40 },
+ { MFM, 0x00, 12 },
+ NORMAL_SECTOR(1),
+ { SECTOR_LOOP_END },
+
+ { MFM, 0x4e, 26 },
+ SECTOR_42_HEADER(3),
+ { MFM, 0x4e, 5 },
+
+ { SECTOR_LOOP_START, 0, 2 },
+ { MFM, 0x00, 12 },
+ NORMAL_SECTOR(4),
+ { MFM, 0x4e, 40 },
+ { SECTOR_LOOP_END },
+
+ { MFM, 0x4e, 49 },
+ { END },
+};
+
+const floppy_image_format_t::desc_e floppy_image_format_t::atari_st_fcp_10_8[] = {
+ { MFM, 0x4e, 20 },
+ { SECTOR_LOOP_START, 2, 9 },
+ { MFM, 0x4e, 40 },
+ { MFM, 0x00, 12 },
+ NORMAL_SECTOR(1),
+ { SECTOR_LOOP_END },
+
+ { MFM, 0x4e, 26 },
+ SECTOR_42_HEADER(3),
+ { MFM, 0x4e, 5 },
+
+ { SECTOR_LOOP_START, 0, 1 },
+ { MFM, 0x00, 12 },
+ NORMAL_SECTOR(4),
+ { MFM, 0x4e, 40 },
+ { SECTOR_LOOP_END },
+
+ { MFM, 0x4e, 49 },
+ { END },
+};
+
+const floppy_image_format_t::desc_e floppy_image_format_t::atari_st_fcp_10_9[] = {
+ { MFM, 0x4e, 20 },
+ { SECTOR_LOOP_START, 1, 9 },
+ { MFM, 0x4e, 40 },
+ { MFM, 0x00, 12 },
+ NORMAL_SECTOR(1),
+ { SECTOR_LOOP_END },
+
+ { MFM, 0x4e, 26 },
+ SECTOR_42_HEADER(3),
+ { MFM, 0x4e, 5 },
+
+ { SECTOR_LOOP_START, 0, 0 },
+ { MFM, 0x00, 12 },
+ NORMAL_SECTOR(4),
+ { MFM, 0x4e, 40 },
+ { SECTOR_LOOP_END },
+
+ { MFM, 0x4e, 49 },
+ { END },
+};
+
+const floppy_image_format_t::desc_e *const floppy_image_format_t::atari_st_fcp_10[10] = {
+ atari_st_fcp_10_0,
+ atari_st_fcp_10_1,
+ atari_st_fcp_10_2,
+ atari_st_fcp_10_3,
+ atari_st_fcp_10_4,
+ atari_st_fcp_10_5,
+ atari_st_fcp_10_6,
+ atari_st_fcp_10_7,
+ atari_st_fcp_10_8,
+ atari_st_fcp_10_9,
+};
+
+
+const floppy_image_format_t::desc_e floppy_image_format_t::atari_st_fcp_11[] = {
+ { MFM, 0x4e, 1 },
+ { SECTOR_INTERLEAVE_SKEW, 1, 1},
+ { SECTOR_LOOP_START, 0, 10 }, { MFM, 0x4e, 2 }, { MFM, 0x00, 2 }, NORMAL_SECTOR( 1), { SECTOR_LOOP_END },
+ { MFM, 0x4e, 23 },
+ { END },
+};
+
+#undef SECTOR_42_HEADER
+#undef NORMAL_SECTOR
+
+const floppy_image_format_t::desc_e *floppy_image_format_t::atari_st_fcp_get_desc(int track, int head, int head_count, int sect_count)
+{
+ switch(sect_count) {
+ case 9:
+ return atari_st_fcp_9;
+ case 10:
+ return atari_st_fcp_10[(track*head_count + head) % 10];
+ case 11:
+ return atari_st_fcp_11;
+ }
+ return 0;
+}
+
+// Amiga layouts
+
+const floppy_image_format_t::desc_e floppy_image_format_t::amiga_11[] = {
+ { SECTOR_LOOP_START, 0, 10 },
+ { MFM, 0x00, 2 },
+ { RAW, 0x4489, 2 },
+ { CRC_AMIGA_START, 1 },
+ { MFMBITS, 0xf, 4 },
+ { OFFSET_ID_O },
+ { SECTOR_ID_O },
+ { REMAIN_O, 11 },
+ { MFMBITS, 0xf, 4 },
+ { OFFSET_ID_E },
+ { SECTOR_ID_E },
+ { REMAIN_E, 11 },
+ { MFM, 0x00, 16 },
+ { CRC_END, 1 },
+ { CRC, 1 },
+ { CRC, 2 },
+ { CRC_AMIGA_START, 2 },
+ { SECTOR_DATA_O, -1 },
+ { SECTOR_DATA_E, -1 },
+ { CRC_END, 2 },
+ { SECTOR_LOOP_END },
+ { MFM, 0x00, 266 },
+ { END }
+};
+
+const floppy_image_format_t::desc_e floppy_image_format_t::amiga_22[] = {
+ { SECTOR_LOOP_START, 0, 21 },
+ { MFM, 0x00, 2 },
+ { RAW, 0x4489, 2 },
+ { CRC_AMIGA_START, 1 },
+ { MFMBITS, 0xf, 4 },
+ { OFFSET_ID_O },
+ { SECTOR_ID_O },
+ { REMAIN_O, 11 },
+ { MFMBITS, 0xf, 4 },
+ { OFFSET_ID_E },
+ { SECTOR_ID_E },
+ { REMAIN_E, 11 },
+ { MFM, 0x00, 16 },
+ { CRC_END, 1 },
+ { CRC, 1 },
+ { CRC, 2 },
+ { CRC_AMIGA_START, 2 },
+ { SECTOR_DATA_O, -1 },
+ { SECTOR_DATA_E, -1 },
+ { CRC_END, 2 },
+ { SECTOR_LOOP_END },
+ { MFM, 0x00, 532 },
+ { END }
+};
+
+void floppy_image_format_t::generate_bitstream_from_track(int track, int head, int cell_size, UINT8 *trackbuf, int &track_size, floppy_image *image)
+{
+ int tsize = image->get_track_size(track, head);
+ if(!tsize || tsize == 1) {
+ // Unformatted track
+ track_size = 200000000/cell_size;
+ memset(trackbuf, 0, (track_size+7)/8);
+ return;
+ }
+
+ // Start at the write splice
+ const UINT32 *tbuf = image->get_buffer(track, head);
+ UINT32 splice = image->get_write_splice_position(track, head);
+ int cur_pos = splice;
+ int cur_entry = 0;
+ while(cur_entry < tsize-1 && (tbuf[cur_entry+1] & floppy_image::TIME_MASK) < cur_pos)
+ cur_entry++;
+
+ int cur_bit = 0;
+
+ int period = cell_size;
+ int period_adjust_base = period * 0.05;
+
+ int min_period = int(cell_size*0.75);
+ int max_period = int(cell_size*1.25);
+ int phase_adjust = 0;
+ int freq_hist = 0;
+
+ UINT32 scanned = 0;
+ while(scanned < 200000000) {
+ // Note that all magnetic cell type changes are considered
+ // edges. No randomness added for neutral/damaged cells
+ int edge = tbuf[cur_entry] & floppy_image::TIME_MASK;
+ if(edge < cur_pos)
+ edge += 200000000;
+ int next = cur_pos + period + phase_adjust;
+ scanned += period + phase_adjust;
+
+ if(edge >= next) {
+ // No transition in the window means 0 and pll in free run mode
+ trackbuf[cur_bit >> 3] &= ~(0x80 >> (cur_bit & 7));
+ cur_bit++;
+ phase_adjust = 0;
+
+ } else {
+ // Transition in the window means 1, and the pll is adjusted
+ trackbuf[cur_bit >> 3] |= 0x80 >> (cur_bit & 7);
+ cur_bit++;
+
+ int delta = edge - (next - period/2);
+
+ phase_adjust = 0.65*delta;
+
+ if(delta < 0) {
+ if(freq_hist < 0)
+ freq_hist--;
+ else
+ freq_hist = -1;
+ } else if(delta > 0) {
+ if(freq_hist > 0)
+ freq_hist++;
+ else
+ freq_hist = 1;
+ } else
+ freq_hist = 0;
+
+ if(freq_hist) {
+ int afh = freq_hist < 0 ? -freq_hist : freq_hist;
+ if(afh > 1) {
+ int aper = period_adjust_base*delta/period;
+ if(!aper)
+ aper = freq_hist < 0 ? -1 : 1;
+ period += aper;
+
+ if(period < min_period)
+ period = min_period;
+ else if(period > max_period)
+ period = max_period;
+ }
+ }
+ }
+
+ cur_pos = next;
+ if(cur_pos >= 200000000) {
+ cur_pos -= 200000000;
+ cur_entry = 0;
+ }
+ while(cur_entry < tsize-1 && (tbuf[cur_entry] & floppy_image::TIME_MASK) < cur_pos)
+ cur_entry++;
+
+ // Wrap around
+ if(cur_entry == tsize-1 &&
+ (tbuf[cur_entry] & floppy_image::TIME_MASK) < cur_pos) {
+ // Wrap to index 0 or 1 depending on whether there is a transition exactly at the index hole
+ cur_entry = (tbuf[tsize-1] & floppy_image::MG_MASK) != (tbuf[0] & floppy_image::MG_MASK) ?
+ 0 : 1;
+ }
+ }
+ // Clean the leftover bottom bits just in case
+ trackbuf[cur_bit >> 3] &= ~(0x7f >> (cur_bit & 7));
+ track_size = cur_bit;
+}
+
+int floppy_image_format_t::sbit_r(const UINT8 *bitstream, int pos)
+{
+ return (bitstream[pos >> 3] & (0x80 >> (pos & 7))) != 0;
+}
+
+int floppy_image_format_t::sbit_rp(const UINT8 *bitstream, int &pos, int track_size)
+{
+ int res = sbit_r(bitstream, pos);
+ pos ++;
+ if(pos == track_size)
+ pos = 0;
+ return res;
+}
+
+UINT8 floppy_image_format_t::sbyte_mfm_r(const UINT8 *bitstream, int &pos, int track_size)
+{
+ UINT8 res = 0;
+ for(int i=0; i<8; i++) {
+ sbit_rp(bitstream, pos, track_size);
+ if(sbit_rp(bitstream, pos, track_size))
+ res |= 0x80 >> i;
+ }
+ return res;
+}
+
+
+void floppy_image_format_t::extract_sectors_from_bitstream_mfm_pc(const UINT8 *bitstream, int track_size, desc_xs *sectors, UINT8 *sectdata, int sectdata_size)
+{
+ memset(sectors, 0, 256*sizeof(desc_xs));
+
+ // Don't bother if it's just too small
+ if(track_size < 100)
+ return;
+
+ // Start by detecting all id and data blocks
+
+ // If 100 is not enough, that track is too funky to be worth
+ // bothering anyway
+
+ int idblk[100], dblk[100];
+ int idblk_count = 0, dblk_count = 0;
+
+ // Precharge the shift register to detect over-the-index stuff
+ UINT16 shift_reg = 0;
+ for(int i=0; i<16; i++)
+ if(sbit_r(bitstream, track_size-16+i))
+ shift_reg |= 0x8000 >> i;
+
+ // Scan the bitstream for sync marks and follow them to check for
+ // blocks
+ for(int i=0; i<track_size; i++) {
+ shift_reg = (shift_reg << 1) | sbit_r(bitstream, i);
+ if(shift_reg == 0x4489) {
+ UINT16 header;
+ int pos = i+1;
+ do {
+ header = 0;
+ for(int j=0; j<16; j++)
+ if(sbit_rp(bitstream, pos, track_size))
+ header |= 0x8000 >> j;
+ // Accept strings of sync marks as long and they're not wrapping
+
+ // Wrapping ones have already been take into account
+ // thanks to the precharging
+ } while(header == 0x4489 && pos > i);
+
+ // fe, ff
+ if(header == 0x5554 || header == 0x5555) {
+ if(idblk_count < 100)
+ idblk[idblk_count++] = pos;
+ i = pos-1;
+ }
+ // fa, fb, fc, fd
+ if(header == 0x5544 || header == 0x5545 || header == 0x5553 || header == 0x5551) {
+ if(dblk_count < 100)
+ dblk[dblk_count++] = pos;
+ i = pos-1;
+ }
+ }
+ }
+
+ // Then extract the sectors
+ int sectdata_pos = 0;
+ for(int i=0; i<idblk_count; i++) {
+ int pos = idblk[i];
+ UINT8 track = sbyte_mfm_r(bitstream, pos, track_size);
+ UINT8 head = sbyte_mfm_r(bitstream, pos, track_size);
+ UINT8 sector = sbyte_mfm_r(bitstream, pos, track_size);
+ UINT8 size = sbyte_mfm_r(bitstream, pos, track_size);
+
+ if(size >= 8)
+ continue;
+ int ssize = 128 << size;
+
+ // If we don't have enough space for a sector's data, skip it
+ if(ssize + sectdata_pos > sectdata_size)
+ continue;
+
+ // Start of IDAM and DAM are supposed to be exactly 704 cells
+ // apart. Of course the hardware is tolerant. Accept +/- 128
+ // cells of shift.
+
+ int d_index;
+ for(d_index = 0; d_index < dblk_count; d_index++) {
+ int delta = dblk[d_index] - idblk[i];
+ if(delta >= 704-128 && delta <= 704+128)
+ break;
+ }
+ if(d_index == dblk_count)
+ continue;
+
+ pos = dblk[d_index];
+
+ sectors[sector].track = track;
+ sectors[sector].head = head;
+ sectors[sector].size = ssize;
+ sectors[sector].data = sectdata + sectdata_pos;
+ for(int j=0; j<ssize; j++)
+ sectdata[sectdata_pos++] = sbyte_mfm_r(bitstream, pos, track_size);
+ }
+}
+
+void floppy_image_format_t::get_geometry_mfm_pc(floppy_image *image, int cell_size, int &track_count, int &head_count, int &sector_count)
+{
+ image->get_actual_geometry(track_count, head_count);
+
+ if(!track_count) {
+ sector_count = 0;
+ return;
+ }
+
+ UINT8 bitstream[500000/8];
+ UINT8 sectdata[50000];
+ desc_xs sectors[256];
+ int track_size;
+
+ // Extract an arbitrary track to get an idea of the number of
+ // sectors
+
+ // 20 was rarely used for protections, not near the start like
+ // 0-10, not near the end like 70+, no special effects on sync
+ // like 33
+
+ generate_bitstream_from_track(track_count > 20 ? 20 : 0, 0, cell_size, bitstream, track_size, image);
+ extract_sectors_from_bitstream_mfm_pc(bitstream, track_size, sectors, sectdata, sizeof(sectdata));
+
+ for(sector_count = 44; sector_count > 0 && !sectors[sector_count].data; sector_count--);
+}
+
+
+void floppy_image_format_t::get_track_data_mfm_pc(int track, int head, floppy_image *image, int cell_size, int sector_size, int sector_count, UINT8 *sectdata)
+{
+ UINT8 bitstream[500000/8];
+ UINT8 sectbuf[50000];
+ desc_xs sectors[256];
+ int track_size;
+
+ generate_bitstream_from_track(track, head, cell_size, bitstream, track_size, image);
+ extract_sectors_from_bitstream_mfm_pc(bitstream, track_size, sectors, sectbuf, sizeof(sectbuf));
+ for(int sector=1; sector <= sector_count; sector++) {
+ UINT8 *sd = sectdata + (sector-1)*sector_size;
+ if(sectors[sector].data && sectors[sector].track == track && sectors[sector].head == head) {
+ int asize = sectors[sector].size;
+ if(asize > sector_size)
+ asize = sector_size;
+ memcpy(sd, sectors[sector].data, asize);
+ if(asize < sector_size)
+ memset(sd+asize, 0, sector_size-asize);
+ } else
+ memset(sd, 0, sector_size);
+ }
+}
+