diff options
author | 2015-11-08 12:56:12 +0100 | |
---|---|---|
committer | 2015-11-08 12:56:12 +0100 | |
commit | 7c19aac60e12d6f5ea301bdb34d7826a01e0b06f (patch) | |
tree | f310d86aa2c6bfc19d115307dedde4eb0cd52dad /src/lib/formats/flopimg.cpp | |
parent | a57b46ae933badd7441ce1644711dbb851e2b504 (diff) |
Rename *.c -> *.cpp in our source (nw)
Diffstat (limited to 'src/lib/formats/flopimg.cpp')
-rw-r--r-- | src/lib/formats/flopimg.cpp | 2960 |
1 files changed, 2960 insertions, 0 deletions
diff --git a/src/lib/formats/flopimg.cpp b/src/lib/formats/flopimg.cpp new file mode 100644 index 00000000000..e66df68cdbc --- /dev/null +++ b/src/lib/formats/flopimg.cpp @@ -0,0 +1,2960 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/********************************************************************* + + flopimg.c + + Floppy disk image abstraction code + +*********************************************************************/ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <limits.h> +#include <assert.h> + +#include "emu.h" // emu_fatalerror +#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_legacy +{ + 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(floppy_image_legacy)); + 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; + dynamic_buffer alloc_buf; + 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, §or_length, NULL); + else + err = fmt->get_sector_length(floppy, head, track, sector, §or_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 */ + alloc_buf.resize(sector_length); + + /* read the sector (we need to do this even when writing */ + err = read_sector(floppy, head, track, sector, &alloc_buf[0], 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[0], 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: + 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 + + +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert +/*************************************************************************** + + New implementation + +****************************************************************************/ + + +floppy_image::floppy_image(int _tracks, int _heads, UINT32 _form_factor) +{ + tracks = _tracks; + heads = _heads; + + form_factor = _form_factor; + variant = 0; + + track_array.resize(tracks*4+1); + for(int i=0; i<tracks*4+1; i++) + track_array[i].resize(heads); +} + +floppy_image::~floppy_image() +{ +} + +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)*4, maxh = heads-1; + + while(maxt >= 0) { + for(int i=0; i<=maxh; i++) + if(!track_array[maxt][i].cell_data.empty()) + goto track_done; + maxt--; + } + track_done: + if(maxt >= 0) + while(maxh >= 0) { + for(int i=0; i<=maxt; i++) + if(!track_array[i][maxh].cell_data.empty()) + goto head_done; + maxh--; + } + head_done: + _tracks = (maxt+4)/4; + _heads = maxh+1; +} + +int floppy_image::get_resolution() const +{ + int mask = 0; + for(int i=0; i<=(tracks-1)*4; i++) + for(int j=0; j<heads; j++) + if(!track_array[i][j].cell_data.empty()) + mask |= 1 << (i & 3); + if(mask & 0xa) + return 2; + if(mask & 0x4) + return 1; + return 0; +} + +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 SSQD: return "Single side, quad density"; + case DSDD: return "Double side, double density"; + case DSQD: return "Double side, quad 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_ext = strchr(rext, ','); + int rlen = next_ext ? next_ext - rext : strlen(rext); + if(rlen == elen && !memcmp(ext, rext, rlen)) + return true; + if(next_ext) + next_ext = next_ext +1; + else + break; + } + return false; +} + +bool floppy_image_format_t::type_no_data(int type) const +{ + return + type == CRC_CCITT_START || + type == CRC_CCITT_FM_START || + type == CRC_AMIGA_START || + type == CRC_CBM_START || + type == CRC_MACHEAD_START || + type == CRC_FCS_START || + type == CRC_VICTOR_HDR_START || + type == CRC_VICTOR_DATA_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 == MFM || + type == MFMBITS || + type == TRACK_ID || + type == HEAD_ID || + type == HEAD_ID_SWAP || + type == SECTOR_ID || + type == SIZE_ID || + type == OFFSET_ID_O || + type == OFFSET_ID_E || + type == SECTOR_ID_O || + type == SECTOR_ID_E || + type == REMAIN_O || + type == REMAIN_E || + type == SECTOR_DATA || + type == SECTOR_DATA_O || + type == SECTOR_DATA_E || + (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_CCITT_FM_START: + crcs[desc[i].p1].type = CRC_CCITT_FM; + break; + case CRC_AMIGA_START: + crcs[desc[i].p1].type = CRC_AMIGA; + break; + case CRC_CBM_START: + crcs[desc[i].p1].type = CRC_CBM; + break; + case CRC_MACHEAD_START: + crcs[desc[i].p1].type = CRC_MACHEAD; + break; + case CRC_FCS_START: + crcs[desc[i].p1].type = CRC_FCS; + break; + case CRC_VICTOR_HDR_START: + crcs[desc[i].p1].type = CRC_VICTOR_HDR; + break; + case CRC_VICTOR_DATA_START: + crcs[desc[i].p1].type = CRC_VICTOR_DATA; + 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_CCITT_FM: return 32; + case CRC_AMIGA: return 64; + case CRC_CBM: return 10; + case CRC_MACHEAD: return 8; + case CRC_FCS: return 20; + case CRC_VICTOR_HDR: return 10; + case CRC_VICTOR_DATA: return 20; + default: return 0; + } +} + +bool floppy_image_format_t::bit_r(const std::vector<UINT32> &buffer, int offset) +{ + return (buffer[offset] & floppy_image::MG_MASK) == MG_1; +} + +UINT32 floppy_image_format_t::bitn_r(const std::vector<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(std::vector<UINT32> &buffer, bool val, UINT32 size, int offset) +{ + buffer[offset] = (val ? MG_1 : MG_0) | size; +} + +void floppy_image_format_t::bit_w(std::vector<UINT32> &buffer, bool val, UINT32 size) +{ + buffer.push_back((val ? MG_1 : MG_0) | size); +} + +void floppy_image_format_t::raw_w(std::vector<UINT32> &buffer, int n, UINT32 val, UINT32 size) +{ + for(int i=n-1; i>=0; i--) + bit_w(buffer, (val >> i) & 1, size); +} + +void floppy_image_format_t::raw_w(std::vector<UINT32> &buffer, int n, UINT32 val, UINT32 size, int offset) +{ + for(int i=n-1; i>=0; i--) + bit_w(buffer, (val >> i) & 1, size, offset++); +} + +void floppy_image_format_t::mfm_w(std::vector<UINT32> &buffer, int n, UINT32 val, UINT32 size) +{ + int prec = buffer.empty() ? 0 : bit_r(buffer, buffer.size()-1); + for(int i=n-1; i>=0; i--) { + int bit = (val >> i) & 1; + bit_w(buffer, !(prec || bit), size); + bit_w(buffer, bit, size); + prec = bit; + } +} + +void floppy_image_format_t::mfm_w(std::vector<UINT32> &buffer, int n, UINT32 val, UINT32 size, int offset) +{ + 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, !(prec || bit), size, offset++); + bit_w(buffer, bit, size, offset++); + prec = bit; + } +} + +void floppy_image_format_t::fm_w(std::vector<UINT32> &buffer, int n, UINT32 val, UINT32 size) +{ + for(int i=n-1; i>=0; i--) { + int bit = (val >> i) & 1; + bit_w(buffer, true, size); + bit_w(buffer, bit, size); + } +} + +void floppy_image_format_t::fm_w(std::vector<UINT32> &buffer, int n, UINT32 val, UINT32 size, int offset) +{ + for(int i=n-1; i>=0; i--) { + int bit = (val >> i) & 1; + bit_w(buffer, true, size, offset++); + bit_w(buffer, bit, size, offset++); + } +} + +void floppy_image_format_t::mfm_half_w(std::vector<UINT32> &buffer, int start_bit, UINT32 val, UINT32 size) +{ + int prec = buffer.empty() ? 0 : bit_r(buffer, buffer.size()-1); + for(int i=start_bit; i>=0; i-=2) { + int bit = (val >> i) & 1; + bit_w(buffer, !(prec || bit), size); + bit_w(buffer, bit, size); + prec = bit; + } +} + +void floppy_image_format_t::gcr5_w(std::vector<UINT32> &buffer, UINT8 val, UINT32 size) +{ + UINT32 e0 = gcr5fw_tb[val >> 4]; + UINT32 e1 = gcr5fw_tb[val & 0x0f]; + raw_w(buffer, 5, e0, size); + raw_w(buffer, 5, e1, size); +} + +void floppy_image_format_t::gcr5_w(std::vector<UINT32> &buffer, UINT8 val, UINT32 size, int offset) +{ + UINT32 e0 = gcr5fw_tb[val >> 4]; + UINT32 e1 = gcr5fw_tb[val & 0x0f]; + raw_w(buffer, 5, e0, size, offset); + raw_w(buffer, 5, e1, size, offset+5); +} + +void floppy_image_format_t::_8n1_w(std::vector<UINT32> &buffer, int n, UINT32 val, UINT32 size) +{ + bit_w(buffer, 0, size); + for(int i=n-1; i>=0; i--) { + int bit = (val >> i) & 1; + bit_w(buffer, bit, size); + } + bit_w(buffer, 1, size); +} + +void floppy_image_format_t::fixup_crc_amiga(std::vector<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)); + mfm_w(buffer, 16, 0, 1000, crc->write); + mfm_w(buffer, 16, res, 1000, crc->write+32); +} + +void floppy_image_format_t::fixup_crc_cbm(std::vector<UINT32> &buffer, const gen_crc_info *crc) +{ + UINT8 v = 0; + for(int o = crc->start; o < crc->end; o+=10) { + v = v ^ (gcr5bw_tb[bitn_r(buffer, o, 5)] << 4); + v = v ^ gcr5bw_tb[bitn_r(buffer, o+5, 5)]; + } + gcr5_w(buffer, v, 1000, crc->write); +} + +UINT16 floppy_image_format_t::calc_crc_ccitt(const std::vector<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(std::vector<UINT32> &buffer, const gen_crc_info *crc) +{ + mfm_w(buffer, 16, calc_crc_ccitt(buffer, crc->start, crc->end), 1000, crc->write); +} + +void floppy_image_format_t::fixup_crc_ccitt_fm(std::vector<UINT32> &buffer, const gen_crc_info *crc) +{ + fm_w(buffer, 16, calc_crc_ccitt(buffer, crc->start, crc->end), 1000, crc->write); +} + +void floppy_image_format_t::fixup_crc_machead(std::vector<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)]; + raw_w(buffer, 8, gcr6fw_tb[v], 1000, crc->write); +} + +void floppy_image_format_t::fixup_crc_fcs(std::vector<UINT32> &buffer, const gen_crc_info *crc) +{ + // TODO +} + +void floppy_image_format_t::fixup_crc_victor_header(std::vector<UINT32> &buffer, const gen_crc_info *crc) +{ + UINT8 v = 0; + for(int o = crc->start; o < crc->end; o+=10) + v += ((gcr5bw_tb[bitn_r(buffer, o, 5)] << 4) | gcr5bw_tb[bitn_r(buffer, o+5, 5)]); + gcr5_w(buffer, v, 1000, crc->write); +} + +void floppy_image_format_t::fixup_crc_victor_data(std::vector<UINT32> &buffer, const gen_crc_info *crc) +{ + UINT16 v = 0; + for(int o = crc->start; o < crc->end; o+=10) + v += ((gcr5bw_tb[bitn_r(buffer, o, 5)] << 4) | gcr5bw_tb[bitn_r(buffer, o+5, 5)]); + gcr5_w(buffer, v & 0xff, 1000, crc->write); + gcr5_w(buffer, v >> 8, 1000, crc->write+10); +} + +void floppy_image_format_t::fixup_crcs(std::vector<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_CBM: fixup_crc_cbm(buffer, crcs+i); break; + case CRC_CCITT: fixup_crc_ccitt(buffer, crcs+i); break; + case CRC_CCITT_FM: fixup_crc_ccitt_fm(buffer, crcs+i); break; + case CRC_MACHEAD: fixup_crc_machead(buffer, crcs+i); break; + case CRC_FCS: fixup_crc_fcs(buffer, crcs+i); break; + case CRC_VICTOR_HDR: fixup_crc_victor_header(buffer, crcs+i); break; + case CRC_VICTOR_DATA: fixup_crc_victor_data(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 ? bit_r(buffer, offset-1) : false) || bit_r(buffer, offset+1)), 1000, offset); + } + 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++; + // This line prevents lock-ups of the emulator when the interleave is not appropriate + if (sec > total_sectors) + throw emu_fatalerror("Format error: interleave %d not appropriate for %d sectors per track\n", interleave, total_sectors); + } + // 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) +{ + std::vector<UINT32> buffer; + + gen_crc_info crcs[MAX_CRC_COUNT]; + collect_crcs(desc, crcs); + + 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) { + switch(desc[index].type) { + case FM: + for(int i=0; i<desc[index].p2; i++) + fm_w(buffer, 8, desc[index].p1); + break; + + case MFM: + for(int i=0; i<desc[index].p2; i++) + mfm_w(buffer, 8, desc[index].p1); + break; + + case MFMBITS: + mfm_w(buffer, desc[index].p2, desc[index].p1); + break; + + case GCR5: + for(int i=0; i<desc[index].p2; i++) + gcr5_w(buffer, desc[index].p1); + break; + + case _8N1: + for(int i=0; i<desc[index].p2; i++) + _8n1_w(buffer, 8, desc[index].p1); + break; + + case RAW: + for(int i=0; i<desc[index].p2; i++) + raw_w(buffer, 16, desc[index].p1); + break; + + case RAWBYTE: + for(int i=0; i<desc[index].p2; i++) + raw_w(buffer, 8, desc[index].p1); + break; + + case RAWBITS: + raw_w(buffer, desc[index].p2, desc[index].p1); + break; + + case SYNC_GCR5: + for(int i=0; i<desc[index].p1; i++) + raw_w(buffer, 10, 0xffff); + break; + + case TRACK_ID: + mfm_w(buffer, 8, track); + break; + + case TRACK_ID_FM: + fm_w(buffer, 8, track); + break; + + case TRACK_ID_DOS2_GCR5: + gcr5_w(buffer, 1 + (track >> 1) + (head * 35)); + break; + + case TRACK_ID_DOS25_GCR5: + gcr5_w(buffer, 1 + track + (head * 77)); + break; + + case TRACK_ID_GCR6: + raw_w(buffer, 8, gcr6fw_tb[track & 0x3f]); + break; + + case TRACK_ID_8N1: + _8n1_w(buffer, 8, track); + break; + + case TRACK_ID_VICTOR_GCR5: + gcr5_w(buffer, track + (head * 0x80)); + break; + + case HEAD_ID: + mfm_w(buffer, 8, head); + break; + + case HEAD_ID_FM: + fm_w(buffer, 8, head); + break; + + case HEAD_ID_SWAP: + mfm_w(buffer, 8, !head); + break; + + case TRACK_HEAD_ID_GCR6: + raw_w(buffer, 8, gcr6fw_tb[(track & 0x40 ? 1 : 0) | (head ? 0x20 : 0)]); + break; + + case SECTOR_ID: + mfm_w(buffer, 8, sect[sector_idx].sector_id); + break; + + case SECTOR_ID_FM: + fm_w(buffer, 8, sect[sector_idx].sector_id); + break; + + case SECTOR_ID_GCR5: + gcr5_w(buffer, sect[sector_idx].sector_id); + break; + + case SECTOR_ID_GCR6: + raw_w(buffer, 8, gcr6fw_tb[sect[sector_idx].sector_id]); + break; + + case SECTOR_ID_8N1: + _8n1_w(buffer, 8, 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, 8, id); + break; + } + + case SIZE_ID_FM: { + int size = sect[sector_idx].size; + int id; + for(id = 0; size > 128; size >>=1, id++); + fm_w(buffer, 8, id); + break; + } + + case SECTOR_INFO_GCR6: + raw_w(buffer, 8, gcr6fw_tb[sect[sector_idx].sector_info]); + break; + + case OFFSET_ID_O: + mfm_half_w(buffer, 7, track*2+head); + break; + + case OFFSET_ID_E: + mfm_half_w(buffer, 6, track*2+head); + break; + + case SECTOR_ID_O: + mfm_half_w(buffer, 7, sector_idx); + break; + + case SECTOR_ID_E: + mfm_half_w(buffer, 6, sector_idx); + break; + + case REMAIN_O: + mfm_half_w(buffer, 7, desc[index].p1 - sector_idx); + break; + + case REMAIN_E: + mfm_half_w(buffer, 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_CBM_START: + case CRC_CCITT_START: + case CRC_CCITT_FM_START: + case CRC_MACHEAD_START: + case CRC_FCS_START: + case CRC_VICTOR_HDR_START: + case CRC_VICTOR_DATA_START: + crcs[desc[index].p1].start = buffer.size(); + break; + + case CRC_END: + crcs[desc[index].p1].end = buffer.size(); + break; + + case CRC: + crcs[desc[index].p1].write = buffer.size(); + buffer.resize(buffer.size() + 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, 8, csect->data[i]); + break; + } + + case SECTOR_DATA_FM: { + const desc_s *csect = sect + (desc[index].p1 >= 0 ? desc[index].p1 : sector_idx); + for(int i=0; i != csect->size; i++) + fm_w(buffer, 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, 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, 6, csect->data[i]); + break; + } + + case SECTOR_DATA_GCR5: { + const desc_s *csect = sect + (desc[index].p1 >= 0 ? desc[index].p1 : sector_idx); + for(int i=0; i != csect->size; i++) + gcr5_w(buffer, 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, nb, gcr6_encode(va, vb, vc) >> (32-nb)); + } + raw_w(buffer, 32, gcr6_encode(ca, cb, cc)); + break; + } + + case SECTOR_DATA_8N1: { + const desc_s *csect = sect + (desc[index].p1 >= 0 ? desc[index].p1 : sector_idx); + for(int i=0; i != csect->size; i++) + _8n1_w(buffer, 8, csect->data[i]); + break; + } + + default: + printf("%d.%d.%d (%d) unhandled\n", desc[index].type, desc[index].p1, desc[index].p2, index); + break; + } + index++; + } + + if(int(buffer.size()) != track_size) + throw emu_fatalerror("Wrong track size in generate_track, expected %d, got %d\n", track_size, int(buffer.size())); + + fixup_crcs(buffer, crcs); + + generate_track_from_levels(track, head, buffer, 0, image); +} + +void floppy_image_format_t::normalize_times(std::vector<UINT32> &buffer) +{ + unsigned int total_sum = 0; + for(unsigned int i=0; i != buffer.size(); i++) + total_sum += buffer[i] & floppy_image::TIME_MASK; + + unsigned int current_sum = 0; + for(unsigned int i=0; i != buffer.size(); 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, int subtrack) +{ + // Maximal number of cells which happens when the buffer is all 1 + std::vector<UINT32> &dest = image->get_buffer(track, head, subtrack); + dest.clear(); + + 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.push_back(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.push_back(cbit | count); + + normalize_times(dest); + image->set_write_splice_position(track, head, 0, subtrack); +} + +void floppy_image_format_t::generate_track_from_levels(int track, int head, std::vector<UINT32> &trackbuf, int splice_pos, floppy_image *image) +{ + // Retrieve the angular splice pos before messing with the data + splice_pos = splice_pos % trackbuf.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(unsigned int i=0; i<trackbuf.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 == int(trackbuf.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 + std::vector<UINT32> &dest = image->get_buffer(track, head); + dest.clear(); + + UINT32 cbit = floppy_image::MG_A; + UINT32 count = 0; + for(unsigned int i=0; i<trackbuf.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.push_back(cbit | count); + cbit = cbit == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A; + count = time - (time >> 1); + continue; + } + dest.push_back(cbit | count); + dest.push_back(trackbuf[i]); + count = 0; + } + + if(count) + dest.push_back(cbit | count); + + normalize_times(dest); + image->set_write_splice_position(track, head, splice_angular_pos); +} + +const UINT8 floppy_image_format_t::gcr5fw_tb[0x10] = +{ + 0x0a, 0x0b, 0x12, 0x13, 0x0e, 0x0f, 0x16, 0x17, + 0x09, 0x19, 0x1a, 0x1b, 0x0d, 0x1d, 0x1e, 0x15 +}; + +const UINT8 floppy_image_format_t::gcr5bw_tb[0x20] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x01, 0x00, 0x0c, 0x04, 0x05, + 0x00, 0x00, 0x02, 0x03, 0x00, 0x0f, 0x06, 0x07, + 0x00, 0x09, 0x0a, 0x0b, 0x00, 0x0d, 0x0e, 0x00 +}; + +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 subtrack) +{ + std::vector<UINT32> &tbuf = image->get_buffer(track, head, subtrack); + if(tbuf.size() <= 1) { + // Unformatted track + track_size = 200000000/cell_size; + memset(trackbuf, 0, (track_size+7)/8); + return; + } + + // Start at the write splice + UINT32 splice = image->get_write_splice_position(track, head, subtrack); + int cur_pos = splice; + int cur_entry = 0; + while(cur_entry < int(tbuf.size())-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 < int(tbuf.size())-1 && (tbuf[cur_entry] & floppy_image::TIME_MASK) < cur_pos) + cur_entry++; + + // Wrap around + if(cur_entry == int(tbuf.size())-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[int(tbuf.size())-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; +} + +UINT8 floppy_image_format_t::sbyte_gcr5_r(const UINT8 *bitstream, int &pos, int track_size) +{ + UINT16 gcr = 0; + for(int i=0; i<10; i++) { + if(sbit_rp(bitstream, pos, track_size)) + gcr |= 0x200 >> i; + } + + return (gcr5bw_tb[gcr >> 5] << 4) | gcr5bw_tb[gcr & 0x1f]; +} + +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; + } + // f8, f9, fa, fb + if(header == 0x554a || header == 0x5549 || header == 0x5544 || header == 0x5545) { + 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 in normal format or 1008 cells apart in perpendicular + // format. 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 <= 1008+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 §or_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); + } +} + + +void floppy_image_format_t::extract_sectors_from_bitstream_fm_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 + // We scan for address marks only, as index marks are not mandatory, + // and many formats actually do not use them + + for(int i=0; i<track_size; i++) { + shift_reg = (shift_reg << 1) | sbit_r(bitstream, i); + + // fe + if(shift_reg == 0xf57e) { // address mark + if(idblk_count < 100) + idblk[idblk_count++] = i+1; + } + // f8, f9, fa, fb + if(shift_reg == 0xf56a || shift_reg == 0xf56b || + shift_reg == 0xf56e || shift_reg == 0xf56f) { // data mark + if(dblk_count < 100) + dblk[dblk_count++] = i+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 384 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 >= 384-128 && delta <= 384+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_fm_pc(floppy_image *image, int cell_size, int &track_count, int &head_count, int §or_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_fm_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_fm_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_fm_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); + } +} + +int floppy_image_format_t::calc_default_pc_gap3_size(UINT32 form_factor, int sector_size) +{ + return + form_factor == floppy_image::FF_8 ? 25 : + sector_size < 512 ? + (form_factor == floppy_image::FF_35 ? 54 : 50) : + (form_factor == floppy_image::FF_35 ? 84 : 80); +} + +void floppy_image_format_t::build_wd_track_fm(int track, int head, floppy_image *image, int cell_count, int sector_count, const desc_pc_sector *sects, int gap_3, int gap_1, int gap_2) +{ + build_pc_track_fm(track, head, image, cell_count, sector_count, sects, gap_3, -1, gap_1, gap_2); +} + +void floppy_image_format_t::build_wd_track_mfm(int track, int head, floppy_image *image, int cell_count, int sector_count, const desc_pc_sector *sects, int gap_3, int gap_1, int gap_2) +{ + build_pc_track_mfm(track, head, image, cell_count, sector_count, sects, gap_3, -1, gap_1, gap_2); +} + +void floppy_image_format_t::build_pc_track_fm(int track, int head, floppy_image *image, int cell_count, int sector_count, const desc_pc_sector *sects, int gap_3, int gap_4a, int gap_1, int gap_2) +{ + std::vector<UINT32> track_data; + + // gap 4a , IAM and gap 1 + if(gap_4a != -1) { + for(int i=0; i<gap_4a; i++) fm_w(track_data, 8, 0xff); + for(int i=0; i< 6; i++) fm_w(track_data, 8, 0x00); + raw_w(track_data, 16, 0xf77a); + } + for(int i=0; i<gap_1; i++) fm_w(track_data, 8, 0xff); + + int total_size = 0; + for(int i=0; i<sector_count; i++) + total_size += sects[i].actual_size; + + unsigned int etpos = track_data.size() + (sector_count*(6+5+2+gap_2+6+1+2) + total_size)*16; + + if(etpos > cell_count) + throw emu_fatalerror("Incorrect layout on track %d head %d, expected_size=%d, current_size=%d", track, head, cell_count, etpos); + + if(etpos + gap_3*16*(sector_count-1) > cell_count) + gap_3 = (cell_count - etpos) / 16 / (sector_count-1); + + // Build the track + for(int i=0; i<sector_count; i++) { + UINT16 crc; + // sync and IDAM and gap 2 + for(int j=0; j< 6; j++) fm_w(track_data, 8, 0x00); + + unsigned int cpos = track_data.size(); + raw_w(track_data, 16, 0xf57e); + fm_w (track_data, 8, sects[i].track); + fm_w (track_data, 8, sects[i].head); + fm_w (track_data, 8, sects[i].sector); + fm_w (track_data, 8, sects[i].size); + crc = calc_crc_ccitt(track_data, cpos, track_data.size()); + fm_w (track_data, 16, crc); + for(int j=0; j<gap_2; j++) fm_w(track_data, 8, 0xff); + + if(!sects[i].data) + for(int j=0; j<6+1+sects[i].actual_size+2+(i != sector_count-1 ? gap_3 : 0); j++) fm_w(track_data, 8, 0xff); + + else { + // sync, DAM, data and gap 3 + for(int j=0; j< 6; j++) fm_w(track_data, 8, 0x00); + cpos = track_data.size(); + raw_w(track_data, 16, sects[i].deleted ? 0xf56a : 0xf56f); + for(int j=0; j<sects[i].actual_size; j++) fm_w(track_data, 8, sects[i].data[j]); + crc = calc_crc_ccitt(track_data, cpos, track_data.size()); + if(sects[i].bad_crc) + crc = 0xffff^crc; + fm_w(track_data, 16, crc); + if(i != sector_count-1) + for(int j=0; j<gap_3; j++) fm_w(track_data, 8, 0xff); + } + } + + // Gap 4b + + while(int(track_data.size()) < cell_count-15) fm_w(track_data, 8, 0xff); + raw_w(track_data, cell_count-int(track_data.size()), 0xffff >> (16+int(track_data.size())-cell_count)); + + generate_track_from_levels(track, head, track_data, 0, image); +} + +void floppy_image_format_t::build_pc_track_mfm(int track, int head, floppy_image *image, int cell_count, int sector_count, const desc_pc_sector *sects, int gap_3, int gap_4a, int gap_1, int gap_2) +{ + std::vector<UINT32> track_data; + + // gap 4a , IAM and gap 1 + if(gap_4a != -1) { + for(int i=0; i<gap_4a; i++) mfm_w(track_data, 8, 0x4e); + for(int i=0; i<12; i++) mfm_w(track_data, 8, 0x00); + for(int i=0; i< 3; i++) raw_w(track_data, 16, 0x5224); + mfm_w(track_data, 8, 0xfc); + } + for(int i=0; i<gap_1; i++) mfm_w(track_data, 8, 0x4e); + + int total_size = 0; + for(int i=0; i<sector_count; i++) + total_size += sects[i].actual_size; + + int etpos = int(track_data.size()) + (sector_count*(12+3+5+2+gap_2+12+3+1+2) + total_size)*16; + + if(etpos > cell_count) + throw emu_fatalerror("Incorrect layout on track %d head %d, expected_size=%d, current_size=%d", track, head, cell_count, etpos); + + if(etpos + gap_3*16*(sector_count-1) > cell_count) + gap_3 = (cell_count - etpos) / 16 / (sector_count-1); + + // Build the track + for(int i=0; i<sector_count; i++) { + UINT16 crc; + // sync and IDAM and gap 2 + for(int j=0; j<12; j++) mfm_w(track_data, 8, 0x00); + unsigned int cpos = track_data.size(); + for(int j=0; j< 3; j++) raw_w(track_data, 16, 0x4489); + mfm_w(track_data, 8, 0xfe); + mfm_w(track_data, 8, sects[i].track); + mfm_w(track_data, 8, sects[i].head); + mfm_w(track_data, 8, sects[i].sector); + mfm_w(track_data, 8, sects[i].size); + crc = calc_crc_ccitt(track_data, cpos, track_data.size()); + mfm_w(track_data, 16, crc); + for(int j=0; j<gap_2; j++) mfm_w(track_data, 8, 0x4e); + + if(!sects[i].data) + for(int j=0; j<12+4+sects[i].actual_size+2+(i != sector_count-1 ? gap_3 : 0); j++) mfm_w(track_data, 8, 0x4e); + + else { + // sync, DAM, data and gap 3 + for(int j=0; j<12; j++) mfm_w(track_data, 8, 0x00); + cpos = track_data.size(); + for(int j=0; j< 3; j++) raw_w(track_data, 16, 0x4489); + mfm_w(track_data, 8, sects[i].deleted ? 0xf8 : 0xfb); + for(int j=0; j<sects[i].actual_size; j++) mfm_w(track_data, 8, sects[i].data[j]); + crc = calc_crc_ccitt(track_data, cpos, track_data.size()); + if(sects[i].bad_crc) + crc = 0xffff^crc; + mfm_w(track_data, 16, crc); + if(i != sector_count-1) + for(int j=0; j<gap_3; j++) mfm_w(track_data, 8, 0x4e); + } + } + + // Gap 4b + + while(int(track_data.size()) < cell_count-15) mfm_w(track_data, 8, 0x4e); + raw_w(track_data, cell_count-int(track_data.size()), 0x9254 >> (16+int(track_data.size())-cell_count)); + + generate_track_from_levels(track, head, track_data, 0, image); +} + +void floppy_image_format_t::extract_sectors_from_bitstream_gcr5(const UINT8 *bitstream, int track_size, desc_xs *sectors, UINT8 *sectdata, int sectdata_size, int head, int tracks) +{ + 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 + int hblk[100], dblk[100]; + int hblk_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 + bool sync = false; + for(int i=0; i<track_size; i++) { + int bit = sbit_r(bitstream, i); + shift_reg = ((shift_reg << 1) | bit) & 0x3ff; + + if (sync && !bit) { + UINT8 id = sbyte_gcr5_r(bitstream, i, track_size); + + switch (id) { + case 0x08: + if(hblk_count < 100) + hblk[hblk_count++] = i-10; + break; + + case 0x07: + if(dblk_count < 100) + dblk[dblk_count++] = i-10; + break; + } + } + + sync = (shift_reg == 0x3ff); + } + + // Then extract the sectors + int sectdata_pos = 0; + for(int i=0; i<hblk_count; i++) { + int pos = hblk[i]; + ATTR_UNUSED UINT8 block_id = sbyte_gcr5_r(bitstream, pos, track_size); + UINT8 crc = sbyte_gcr5_r(bitstream, pos, track_size); + UINT8 sector = sbyte_gcr5_r(bitstream, pos, track_size); + UINT8 track = sbyte_gcr5_r(bitstream, pos, track_size); + UINT8 id2 = sbyte_gcr5_r(bitstream, pos, track_size); + UINT8 id1 = sbyte_gcr5_r(bitstream, pos, track_size); + + if (crc ^ sector ^ track ^ id2 ^ id1) { + // header crc mismatch + } + + pos = dblk[i]; + block_id = sbyte_gcr5_r(bitstream, pos, track_size); + + if (track > tracks) track -= tracks; + sectors[sector].track = track; + sectors[sector].head = head; + sectors[sector].size = 256; + sectors[sector].data = sectdata + sectdata_pos; + UINT8 data_crc = 0; + for(int j=0; j<sectors[sector].size; j++) { + UINT8 data = sbyte_gcr5_r(bitstream, pos, track_size); + data_crc ^= data; + sectdata[sectdata_pos++] = data; + } + data_crc ^= sbyte_gcr5_r(bitstream, pos, track_size); + if (data_crc) { + // data crc mismatch + } + } +} + +void floppy_image_format_t::extract_sectors_from_bitstream_victor_gcr5(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 + int hblk[100], dblk[100]; + int hblk_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 + bool sync = false; + for(int i=0; i<track_size; i++) { + int bit = sbit_r(bitstream, i); + shift_reg = ((shift_reg << 1) | bit) & 0x3ff; + + if (sync && !bit) { + UINT8 id = sbyte_gcr5_r(bitstream, i, track_size); + + switch (id) { + case 0x07: + if(hblk_count < 100) + hblk[hblk_count++] = i-10; + break; + + case 0x08: + if(dblk_count < 100) + dblk[dblk_count++] = i-10; + break; + } + } + + sync = (shift_reg == 0x3ff); + } + + // Then extract the sectors + int sectdata_pos = 0; + for(int i=0; i<hblk_count; i++) { + int pos = hblk[i]; + ATTR_UNUSED UINT8 block_id = sbyte_gcr5_r(bitstream, pos, track_size); + UINT8 track = sbyte_gcr5_r(bitstream, pos, track_size); + UINT8 sector = sbyte_gcr5_r(bitstream, pos, track_size); + + pos = dblk[i]; + block_id = sbyte_gcr5_r(bitstream, pos, track_size); + + sectors[sector].track = track & 0x7f; + sectors[sector].head = BIT(track, 7); + sectors[sector].size = 512; + sectors[sector].data = sectdata + sectdata_pos; + for(int j=0; j<sectors[sector].size; j++) { + UINT8 data = sbyte_gcr5_r(bitstream, pos, track_size); + sectdata[sectdata_pos++] = data; + } + } +} |