// license:BSD-3-Clause // copyright-holders:Nathan Woods /********************************************************************* flopimg.h Floppy disk image abstraction code *********************************************************************/ #ifndef FLOPIMG_H #define FLOPIMG_H #include "osdcore.h" #include "ioprocs.h" #include "opresolv.h" #include "coretmpl.h" #ifndef LOG_FORMATS #define LOG_FORMATS if (0) printf #endif /*************************************************************************** Constants ***************************************************************************/ #define FLOPPY_FLAGS_READWRITE 0 #define FLOPPY_FLAGS_READONLY 1 /* sector has a deleted data address mark */ #define ID_FLAG_DELETED_DATA 0x0001 /* CRC error in id field */ #define ID_FLAG_CRC_ERROR_IN_ID_FIELD 0x0002 /* CRC error in data field */ #define ID_FLAG_CRC_ERROR_IN_DATA_FIELD 0x0004 /*************************************************************************** Type definitions ***************************************************************************/ enum floperr_t { FLOPPY_ERROR_SUCCESS, /* no error */ FLOPPY_ERROR_INTERNAL, /* fatal internal error */ FLOPPY_ERROR_UNSUPPORTED, /* this operation is unsupported */ FLOPPY_ERROR_OUTOFMEMORY, /* ran out of memory */ FLOPPY_ERROR_SEEKERROR, /* attempted to seek to nonexistent location */ FLOPPY_ERROR_INVALIDIMAGE, /* this image in invalid */ FLOPPY_ERROR_READONLY, /* attempt to write to read-only image */ FLOPPY_ERROR_NOSPACE, FLOPPY_ERROR_PARAMOUTOFRANGE, FLOPPY_ERROR_PARAMNOTSPECIFIED }; struct floppy_image_legacy; struct FloppyCallbacks { 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); floperr_t (*read_indexed_sector)(floppy_image_legacy *floppy, int head, int track, int sector_index, void *buffer, size_t buflen); floperr_t (*write_indexed_sector)(floppy_image_legacy *floppy, int head, int track, int sector_index, const void *buffer, size_t buflen, int ddam); floperr_t (*read_track)(floppy_image_legacy *floppy, int head, int track, uint64_t offset, void *buffer, size_t buflen); floperr_t (*write_track)(floppy_image_legacy *floppy, int head, int track, uint64_t offset, const void *buffer, size_t buflen); floperr_t (*format_track)(floppy_image_legacy *floppy, int head, int track, util::option_resolution *params); floperr_t (*post_format)(floppy_image_legacy *floppy, util::option_resolution *params); int (*get_heads_per_disk)(floppy_image_legacy *floppy); int (*get_tracks_per_disk)(floppy_image_legacy *floppy); int (*get_sectors_per_track)(floppy_image_legacy *floppy, int head, int track); uint32_t (*get_track_size)(floppy_image_legacy *floppy, int head, int track); floperr_t (*get_sector_length)(floppy_image_legacy *floppy, int head, int track, int sector, uint32_t *sector_length); floperr_t (*get_indexed_sector_info)(floppy_image_legacy *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, uint32_t *sector_length, unsigned long *flags); floperr_t (*get_track_data_offset)(floppy_image_legacy *floppy, int head, int track, uint64_t *offset); }; struct FloppyFormat { const char *name; const char *extensions; const char *description; floperr_t (*identify)(floppy_image_legacy *floppy, const struct FloppyFormat *format, int *vote); floperr_t (*construct)(floppy_image_legacy *floppy, const struct FloppyFormat *format, util::option_resolution *params); floperr_t (*destruct)(floppy_image_legacy *floppy, const struct FloppyFormat *format); const char *param_guidelines; }; #define FLOPPY_IDENTIFY(name) floperr_t name(floppy_image_legacy *floppy, const struct FloppyFormat *format, int *vote) #define FLOPPY_CONSTRUCT(name) floperr_t name(floppy_image_legacy *floppy, const struct FloppyFormat *format, util::option_resolution *params) #define FLOPPY_DESTRUCT(name) floperr_t name(floppy_image_legacy *floppy, const struct FloppyFormat *format) FLOPPY_IDENTIFY(td0_dsk_identify); FLOPPY_CONSTRUCT(td0_dsk_construct); FLOPPY_DESTRUCT(td0_dsk_destruct); FLOPPY_IDENTIFY(imd_dsk_identify); FLOPPY_CONSTRUCT(imd_dsk_construct); FLOPPY_IDENTIFY(cqm_dsk_identify); FLOPPY_CONSTRUCT(cqm_dsk_construct); FLOPPY_IDENTIFY(dsk_dsk_identify); FLOPPY_CONSTRUCT(dsk_dsk_construct); FLOPPY_IDENTIFY(d88_dsk_identify); FLOPPY_CONSTRUCT(d88_dsk_construct); FLOPPY_IDENTIFY(fdi_dsk_identify); FLOPPY_CONSTRUCT(fdi_dsk_construct); #define LEGACY_FLOPPY_OPTIONS_NAME(name) floppyoptions_##name #define LEGACY_FLOPPY_OPTIONS_START(name) \ const struct FloppyFormat floppyoptions_##name[] = \ { #define LEGACY_FLOPPY_OPTIONS_END0 \ { nullptr } \ }; #define LEGACY_FLOPPY_OPTIONS_EXTERN(name) \ extern const struct FloppyFormat floppyoptions_##name[] #define LEGACY_FLOPPY_OPTION(name, extensions_, description_, identify_, construct_, destruct_, ranges_)\ { #name, extensions_, description_, identify_, construct_, destruct_, ranges_ }, #define LEGACY_FLOPPY_OPTIONS_END \ LEGACY_FLOPPY_OPTION( fdi, "fdi", "Formatted Disk Image", fdi_dsk_identify, fdi_dsk_construct, nullptr, nullptr) \ LEGACY_FLOPPY_OPTION( td0, "td0", "Teledisk floppy disk image", td0_dsk_identify, td0_dsk_construct, td0_dsk_destruct, nullptr) \ LEGACY_FLOPPY_OPTION( imd, "imd", "IMD floppy disk image", imd_dsk_identify, imd_dsk_construct, nullptr, nullptr) \ LEGACY_FLOPPY_OPTION( cqm, "cqm,dsk", "CopyQM floppy disk image", cqm_dsk_identify, cqm_dsk_construct, nullptr, nullptr) \ LEGACY_FLOPPY_OPTION( dsk, "dsk", "DSK floppy disk image", dsk_dsk_identify, dsk_dsk_construct, nullptr, nullptr) \ LEGACY_FLOPPY_OPTION( d88, "d77,d88,1dd", "D88 Floppy Disk image", d88_dsk_identify, d88_dsk_construct, nullptr, nullptr) \ LEGACY_FLOPPY_OPTIONS_END0 LEGACY_FLOPPY_OPTIONS_EXTERN(default); #define PARAM_END '\0' #define PARAM_HEADS 'H' #define PARAM_TRACKS 'T' #define PARAM_SECTORS 'S' #define PARAM_SECTOR_LENGTH 'L' #define PARAM_INTERLEAVE 'I' #define PARAM_FIRST_SECTOR_ID 'F' #define HEADS(range) "H" #range #define TRACKS(range) "T" #range #define SECTORS(range) "S" #range #define SECTOR_LENGTH(range) "L" #range #define INTERLEAVE(range) "I" #range #define FIRST_SECTOR_ID(range) "F" #range /*************************************************************************** Prototypes ***************************************************************************/ OPTION_GUIDE_EXTERN(floppy_option_guide); /* opening, closing and creating of floppy images */ floperr_t floppy_open(void *fp, const struct io_procs *procs, const std::string &extension, const struct FloppyFormat *format, int flags, floppy_image_legacy **outfloppy); floperr_t floppy_open_choices(void *fp, const struct io_procs *procs, const std::string &extension, const struct FloppyFormat *formats, int flags, floppy_image_legacy **outfloppy); floperr_t floppy_create(void *fp, const struct io_procs *procs, const struct FloppyFormat *format, util::option_resolution *parameters, floppy_image_legacy **outfloppy); void floppy_close(floppy_image_legacy *floppy); /* useful for identifying a floppy image */ floperr_t floppy_identify(void *fp, const struct io_procs *procs, const char *extension, const struct FloppyFormat *formats, int *identified_format); /* functions useful within format constructors */ void *floppy_tag(floppy_image_legacy *floppy); void *floppy_create_tag(floppy_image_legacy *floppy, size_t tagsize); struct FloppyCallbacks *floppy_callbacks(floppy_image_legacy *floppy); uint8_t floppy_get_filler(floppy_image_legacy *floppy); void floppy_set_filler(floppy_image_legacy *floppy, uint8_t filler); /* calls for accessing disk image data */ floperr_t floppy_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, int offset, void *buffer, size_t buffer_len); 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); 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); 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); floperr_t floppy_read_track(floppy_image_legacy *floppy, int head, int track, void *buffer, size_t buffer_len); floperr_t floppy_write_track(floppy_image_legacy *floppy, int head, int track, const void *buffer, size_t buffer_len); floperr_t floppy_read_track_data(floppy_image_legacy *floppy, int head, int track, void *buffer, size_t 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 floppy_format_track(floppy_image_legacy *floppy, int head, int track, util::option_resolution *params); int floppy_get_tracks_per_disk(floppy_image_legacy *floppy); int floppy_get_heads_per_disk(floppy_image_legacy *floppy); uint32_t floppy_get_track_size(floppy_image_legacy *floppy, int head, int track); floperr_t floppy_get_sector_length(floppy_image_legacy *floppy, int head, int track, int sector, uint32_t *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_t *sector_length, unsigned long *flags); floperr_t floppy_get_sector_count(floppy_image_legacy *floppy, int head, int track, int *sector_count); floperr_t floppy_load_track(floppy_image_legacy *floppy, int head, int track, int dirtify, void **track_data, size_t *track_length); int floppy_is_read_only(floppy_image_legacy *floppy); uint8_t floppy_random_byte(floppy_image_legacy *floppy); /* accessors for meta information about the image */ const char *floppy_format_description(floppy_image_legacy *floppy); /* calls for accessing the raw disk image */ void floppy_image_read(floppy_image_legacy *floppy, void *buffer, uint64_t offset, size_t length); void floppy_image_write(floppy_image_legacy *floppy, const void *buffer, uint64_t offset, size_t length); void floppy_image_write_filler(floppy_image_legacy *floppy, uint8_t filler, uint64_t offset, size_t length); uint64_t floppy_image_size(floppy_image_legacy *floppy); /* misc */ const char *floppy_error(floperr_t err); ////////////////////////////////////////////////////////// // New implementation ////////////////////////////////////////////////////////// class floppy_image; //! Class representing a floppy image format. class floppy_image_format_t { public: floppy_image_format_t(); virtual ~floppy_image_format_t(); /*! @brief Identify an image. The identify function tests if the image is valid for this particular format. @param io buffer containing the image data. @param form_factor Physical form factor of disk, from the enum in floppy_image @return 1 if image valid, 0 otherwise. */ virtual int identify(io_generic *io, uint32_t form_factor) = 0; /*! @brief Load an image. The load function opens an image file and converts it to the internal MESS floppy representation. @param io source buffer containing the image data. @param form_factor Physical form factor of disk, from the enum in floppy_image @param image output buffer for data in MESS internal format. @return true on success, false otherwise. */ virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) = 0; /*! @brief Save an image. The save function writes back an image from the MESS internal floppy representation to the appropriate format on disk. @param io output buffer for the data in the on-disk format. @param image source buffer containing data in MESS internal format. @return true on success, false otherwise. */ virtual bool save(io_generic *io, floppy_image *image); //! @returns string containing name of format. virtual const char *name() const = 0; //! @returns string containing description of format. virtual const char *description() const = 0; //! @returns string containing comma-separated list of file //! extensions the format may use. virtual const char *extensions() const = 0; //! @returns true if format supports saving. virtual bool supports_save() const = 0; //! Used if a linked list of formats is needed floppy_image_format_t *next; //! This appends a format to the linked list of formats, needed for floppy_image_device(). void append(floppy_image_format_t *_next); //! This checks if the file has the proper extension for this format. //! @param file_name //! @returns true if file matches the extension. bool extension_matches(const char *file_name) const; protected: //! Input for convert_to_edge enum { MG_SHIFT = 28, MG_0 = (4 << MG_SHIFT), MG_1 = (5 << MG_SHIFT), MG_W = (6 << MG_SHIFT) }; // **** Reader helpers **** //! Struct designed for easy track data description. Contains an opcode and two params. //! Optional, you can always do things by hand, but useful nevertheless. //! A vector of these structures describes one track. struct desc_e { int type, //!< An opcode p1, //!< first param p2; //!< second param }; //! Opcodes of the format description language used by generate_track() enum { END, //!< End of description FM, //!< One byte in p1 to be fm-encoded, msb first, repeated p2 times MFM, //!< One byte in p1 to be mfm-encoded, msb first, repeated p2 times MFMBITS, //!< A value of p2 bits in p1 to be mfm-encoded, msb first GCR5, //!< One byte in p1 to be gcr5-encoded, repeated p2 times _8N1, //!< One byte in p1 to be 8N1-encoded, repeated p2 times RAW, //!< One 16 bits word in p1 to be written raw, msb first, repeated p2 times RAWBYTE, //!< One 8 bit byte in p1 to be written raw, msb first, repeated p2 times RAWBITS, //!< A value of p2 bits in p1 to be copied as-is, msb first SYNC_GCR5, //!< gcr5 sync byte TRACK_ID, //!< Track id byte, mfm-encoded TRACK_ID_FM, //!< Track id byte, fm-encoded TRACK_ID_DOS2_GCR5, //!< Track id byte, gcr5-encoded TRACK_ID_DOS25_GCR5, //!< Track id byte, gcr5-encoded TRACK_ID_GCR6, //!< Track id low 6 bits, gcr6-encoded TRACK_ID_8N1, //!< Track id byte, 8N1-encoded TRACK_ID_VICTOR_GCR5, //!< Track id byte, gcr5-encoded HEAD_ID, //!< Head id byte, mfm-encoded HEAD_ID_FM, //!< Head id byte, fm-encoded HEAD_ID_SWAP, //!< Head id byte swapped (0->1, 1->0), mfm-encoded TRACK_HEAD_ID_GCR6, //!< Track id 7th bit + head, gc6-encoded SECTOR_ID, //!< Sector id byte, mfm-encoded SECTOR_ID_FM, //!< Sector id byte, fm-encoded SECTOR_ID_GCR5, //!< Sector id byte, gcr5-encoded SECTOR_ID_GCR6, //!< Sector id byte, gcr6-encoded SECTOR_ID_8N1, //!< Sector id byte, 8N1-encoded SIZE_ID, //!< Sector size code on one byte [log2(size/128)], mfm-encoded SIZE_ID_FM, //!< Sector size code on one byte [log2(size/128)], fm-encoded SECTOR_INFO_GCR6, //!< Sector info byte, gcr6-encoded OFFSET_ID_O, //!< Offset (track*2+head) byte, odd bits, mfm-encoded OFFSET_ID_E, //!< Offset (track*2+head) byte, even bits, mfm-encoded SECTOR_ID_O, //!< Sector id byte, odd bits, mfm-encoded SECTOR_ID_E, //!< Sector id byte, even bits, mfm-encoded REMAIN_O, //!< Remaining sector count, odd bits, mfm-encoded, total sector count in p1 REMAIN_E, //!< Remaining sector count, even bits, mfm-encoded, total sector count in p1 SECTOR_DATA, //!< Sector data to mfm-encode, which in p1, -1 for the current one per the sector id SECTOR_DATA_FM, //!< Sector data to fm-encode, which in p1, -1 for the current one per the sector id SECTOR_DATA_O, //!< Sector data to mfm-encode, odd bits only, which in p1, -1 for the current one per the sector id SECTOR_DATA_E, //!< Sector data to mfm-encode, even bits only, which in p1, -1 for the current one per the sector id SECTOR_DATA_GCR5, //!< Sector data to gcr5-encode, which in p1, -1 for the current one per the sector id SECTOR_DATA_MAC, //!< Transformed sector data + checksum, mac style, id in p1, -1 for the current one per the sector id SECTOR_DATA_8N1, //!< Sector data to 8N1-encode, which in p1, -1 for the current one per the sector id CRC_CCITT_START, //!< Start a CCITT CRC calculation, with the usual x^16 + x^12 + x^5 + 1 (11021) polynomial, p1 = crc id CRC_CCITT_FM_START, //!< Start a CCITT CRC calculation, with the usual x^16 + x^12 + x^5 + 1 (11021) polynomial, p1 = crc id CRC_AMIGA_START, //!< Start an amiga checksum calculation, p1 = crc id CRC_CBM_START, //!< Start a CBM checksum calculation (xor of original data values, gcr5-encoded), p1 = crc id CRC_MACHEAD_START, //!< Start of the mac gcr6 sector header checksum calculation (xor of pre-encode 6-bits values, gcr6-encoded) CRC_FCS_START, //!< Start a Compucolor File Control System checksum calculation, p1 = crc id CRC_VICTOR_HDR_START, //!< Start a Victor 9000 checksum calculation, p1 = crc id CRC_VICTOR_DATA_START, //!< Start a Victor 9000 checksum calculation, p1 = crc id CRC_END, //!< End the checksum, p1 = crc id CRC, //!< Write a checksum in the appropriate format, p1 = crc id SECTOR_LOOP_START, //!< Start of the per-sector loop, sector number goes from p1 to p2 inclusive SECTOR_LOOP_END, //!< End of the per-sector loop SECTOR_INTERLEAVE_SKEW //!< Defines interleave and skew for sector counting }; //! Sector data description struct desc_s { int size; //!< Sector size, int bytes const uint8_t *data; //!< Sector data uint8_t sector_id; //!< Sector ID uint8_t sector_info; //!< Sector free byte }; /*! @brief Generate one track according to the description vector. @param desc track data description @param track @param head @param sect a vector indexed by sector id. @param sect_count number of sectors. @param track_size in _cells_, i.e. 100000 for a usual 2us-per-cell track at 300rpm. @param image */ void generate_track(const desc_e *desc, int track, int head, const desc_s *sect, int sect_count, int track_size, floppy_image *image); /*! @brief Generate a track from cell binary values, MSB-first. @param track @param head @param trackbuf track input buffer. @param track_size in cells, not bytes. @param image @param subtrack subtrack index, 0-3 @param splice write splice position */ void generate_track_from_bitstream(int track, int head, const uint8_t *trackbuf, int track_size, floppy_image *image, int subtrack = 0, int splice = 0); //! @brief Generate a track from cell level values (0/1/W/D/N). /*! Note that this function needs to be able to split cells in two, so no time value should be less than 2, and even values are a good idea. */ /*! @param track @param head @param trackbuf track input buffer. @param track_size in cells, not bytes. @param splice_pos is the position of the track splice. For normal formats, use -1. For protected formats, you're supposed to know. trackbuf may be modified at that position or after. @param image */ void generate_track_from_levels(int track, int head, std::vector &trackbuf, int splice_pos, floppy_image *image); //! Normalize the times in a cell buffer to sum up to 200000000 void normalize_times(std::vector &buffer); // Some conversion tables for gcr static const uint8_t gcr5fw_tb[0x10], gcr5bw_tb[0x20]; static const uint8_t gcr6fw_tb[0x40], gcr6bw_tb[0x100]; // Some useful descriptions shared by multiple formats // Atari ST formats (100K cells) // Standard TOS 9 sectors-per-track format static const desc_e atari_st_9[]; // Usual 10 sectors-per-track format static const desc_e atari_st_10[]; // Fastcopy Pro optimized formats, with fake sector header for // faster verify and skew/interleave where appropriate static const desc_e atari_st_fcp_9[]; static const desc_e *const atari_st_fcp_10[10]; static const desc_e atari_st_fcp_11[]; static const desc_e atari_st_fcp_10_0[], atari_st_fcp_10_1[], atari_st_fcp_10_2[], atari_st_fcp_10_3[]; static const desc_e atari_st_fcp_10_4[], atari_st_fcp_10_5[], atari_st_fcp_10_6[], atari_st_fcp_10_7[]; static const desc_e atari_st_fcp_10_8[], atari_st_fcp_10_9[]; static const desc_e *atari_st_fcp_get_desc(int track, int head, int head_count, int sect_count); // Amiga formats (100K cells) // Standard 11 sectors per track format static const desc_e amiga_11[]; // Standard 22 sectors per track format (guessed, to check w.r.t the real thing) static const desc_e amiga_22[]; // **** Writer helpers **** /*! @brief Rebuild a cell bitstream for a track. Takes the cell standard angular size as a parameter, gives out a msb-first bitstream. Beware that fuzzy bits will always give out the same value. @param track @param head @param cell_size @param trackbuf Output buffer size should be 34% more than the nominal number of cells (the dpll tolerates a cell size down to 75% of the nominal one, with gives a cell count of 1/0.75=1.333... times the nominal one). @param track_size Output size is given in bits (cells). @param image */ /*! @verbatim Computing the standard angular size of a cell is simple. Noting: d = standard cell duration in microseconds r = motor rotational speed in rpm then: a = r * d * 10 / 3. Some values: Type Cell RPM Size C1541 tr 1-17 3.25 300 3250 C1541 tr 18-24 3.50 300 3500 C1541 tr 25-30 3.75 300 3750 C1541 tr 31+ 4.00 300 4000 8" DD 1 360 1200 5.25" SD 4 300 4000 5.25" DD 2 300 2000 5.25" HD 1 360 1200 3.5" SD 4 300 4000 3.5" DD 2 300 2000 3.5" HD 1 300 1000 3.5" ED 0.5 300 500 @endverbatim */ void generate_bitstream_from_track(int track, int head, int cell_size, uint8_t *trackbuf, int &track_size, floppy_image *image, int subtrack = 0); //! Defines a standard sector for extracting. struct desc_xs { int track, //!< Track for this sector head, //!< Head for this sector size; //!< Size of this sector const uint8_t *data; //!< Data within this sector }; struct desc_pc_sector { uint8_t track, head, sector, size; int actual_size; uint8_t *data; bool deleted; bool bad_crc; }; int calc_default_pc_gap3_size(uint32_t form_factor, int sector_size); void 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); void 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=22); void 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=40, int gap_1=26, int gap_2=11); void 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=80, int gap_1=50, int gap_2=22); //! @brief Extract standard sectors from a regenerated bitstream. //! Sectors must point to an array of 256 desc_xs. //! An existing sector is recognizable by having ->data non-null. //! Sector data is written in sectdata up to sectdata_size bytes. //! The ones implemented here are the ones used by multiple //! systems. //! PC-type sectors with MFM encoding, sector size can go from 128 bytes to 16K. void extract_sectors_from_bitstream_mfm_pc(const uint8_t *bitstream, int track_size, desc_xs *sectors, uint8_t *sectdata, int sectdata_size); //! PC-type sectors with FM encoding void extract_sectors_from_bitstream_fm_pc(const uint8_t *bitstream, int track_size, desc_xs *sectors, uint8_t *sectdata, int sectdata_size); //! Commodore type sectors with GCR5 encoding void extract_sectors_from_bitstream_gcr5(const uint8_t *bitstream, int track_size, desc_xs *sectors, uint8_t *sectdata, int sectdata_size, int head, int tracks); //! Victor 9000 type sectors with GCR5 encoding void extract_sectors_from_bitstream_victor_gcr5(const uint8_t *bitstream, int track_size, desc_xs *sectors, uint8_t *sectdata, int sectdata_size); //! @brief Get a geometry (including sectors) from an image. //! PC-type sectors with MFM encoding void get_geometry_mfm_pc(floppy_image *image, int cell_size, int &track_count, int &head_count, int §or_count); //! PC-type sectors with FM encoding void get_geometry_fm_pc(floppy_image *image, int cell_size, int &track_count, int &head_count, int §or_count); //! Regenerate the data for a full track. //! PC-type sectors with MFM encoding and fixed-size. void get_track_data_mfm_pc(int track, int head, floppy_image *image, int cell_size, int sector_size, int sector_count, uint8_t *sectdata); //! Regenerate the data for a full track. //! PC-type sectors with FM encoding and fixed-size. void get_track_data_fm_pc(int track, int head, floppy_image *image, int cell_size, int sector_size, int sector_count, uint8_t *sectdata); //! Look up a bit in a level-type stream. bool bit_r(const std::vector &buffer, int offset); //! Look up multiple bits uint32_t bitn_r(const std::vector &buffer, int offset, int count); //! Write a bit with a given size. void bit_w(std::vector &buffer, bool val, uint32_t size = 1000); void bit_w(std::vector &buffer, bool val, uint32_t size, int offset); //! Calculate a CCITT-type CRC. uint16_t calc_crc_ccitt(const std::vector &buffer, int start, int end); //! Write a series of (raw) bits void raw_w(std::vector &buffer, int n, uint32_t val, uint32_t size = 1000); void raw_w(std::vector &buffer, int n, uint32_t val, uint32_t size, int offset); //! FM-encode and write a series of bits void fm_w(std::vector &buffer, int n, uint32_t val, uint32_t size = 1000); void fm_w(std::vector &buffer, int n, uint32_t val, uint32_t size, int offset); //! MFM-encode and write a series of bits void mfm_w(std::vector &buffer, int n, uint32_t val, uint32_t size = 1000); void mfm_w(std::vector &buffer, int n, uint32_t val, uint32_t size, int offset); //! MFM-encode every two bits and write void mfm_half_w(std::vector &buffer, int start_bit, uint32_t val, uint32_t size = 1000); //! GCR5-encode and write a series of bits void gcr5_w(std::vector &buffer, uint8_t val, uint32_t size = 1000); void gcr5_w(std::vector &buffer, uint8_t val, uint32_t size, int offset); //! 8N1-encode and write a series of bits void _8n1_w(std::vector &buffer, int n, uint32_t val, uint32_t size = 1000); //! GCR4 encode (Apple II sector header) uint16_t gcr4_encode(uint8_t va); //! GCR4 decode uint8_t gcr4_decode(uint8_t e0, uint8_t e1); //! GCR6 encode (Apple II 16-sector and Mac-style GCR) uint32_t gcr6_encode(uint8_t va, uint8_t vb, uint8_t vc); //! GCR6 decode void gcr6_decode(uint8_t e0, uint8_t e1, uint8_t e2, uint8_t e3, uint8_t &va, uint8_t &vb, uint8_t &vc); uint8_t sbyte_mfm_r(const uint8_t *bitstream, int &pos, int track_size); uint8_t sbyte_gcr5_r(const uint8_t *bitstream, int &pos, int track_size); private: enum { CRC_NONE, CRC_AMIGA, CRC_CBM, CRC_CCITT, CRC_CCITT_FM, CRC_MACHEAD, CRC_FCS, CRC_VICTOR_HDR, CRC_VICTOR_DATA }; enum { MAX_CRC_COUNT = 64 }; //! Holds data used internally for generating CRCs. struct gen_crc_info { int type, //!< Type of CRC start, //!< Start position end, //!< End position write; //!< where to write the CRC bool fixup_mfm_clock; //!< would the MFM clock bit after the CRC need to be fixed? }; bool type_no_data(int type) const; bool type_data_mfm(int type, int p1, const gen_crc_info *crcs) const; int crc_cells_size(int type) const; void fixup_crc_amiga(std::vector &buffer, const gen_crc_info *crc); void fixup_crc_cbm(std::vector &buffer, const gen_crc_info *crc); void fixup_crc_ccitt(std::vector &buffer, const gen_crc_info *crc); void fixup_crc_ccitt_fm(std::vector &buffer, const gen_crc_info *crc); void fixup_crc_machead(std::vector &buffer, const gen_crc_info *crc); void fixup_crc_fcs(std::vector &buffer, const gen_crc_info *crc); void fixup_crc_victor_header(std::vector &buffer, const gen_crc_info *crc); void fixup_crc_victor_data(std::vector &buffer, const gen_crc_info *crc); void fixup_crcs(std::vector &buffer, gen_crc_info *crcs); void collect_crcs(const desc_e *desc, gen_crc_info *crcs) const; int sbit_r(const uint8_t *bitstream, int pos); int sbit_rp(const uint8_t *bitstream, int &pos, int track_size); int calc_sector_index(int num, int interleave, int skew, int total_sectors, int track_head); }; // a dce_type is simply a pointer to its alloc function typedef floppy_image_format_t *(*floppy_format_type)(); // this template function creates a stub which constructs a image format template floppy_image_format_t *floppy_image_format_creator() { return new _FormatClass(); } // ======================> floppy_image //! Class representing floppy image //! Internal format is close but not identical to the mfi format. //! //! //! Track data consists of a series of 32-bits lsb-first values //! representing magnetic cells. Bits 0-27 indicate the absolute //! position of the start of the cell (not the size), and bits //! 28-31 the type. Type can be: //! - 0, MG_A -> Magnetic orientation A //! - 1, MG_B -> Magnetic orientation B //! - 2, MG_N -> Non-magnetized zone (neutral) //! - 3, MG_D -> Damaged zone, reads as neutral but cannot be changed by writing //! //! The position is in angular units of 1/200,000,000th of a turn. //! The last cell implicit end position is of course 200,000,000. //! //! Unformatted tracks are encoded as zero-size. //! //! The "track splice" information indicates where to start writing //! if you try to rewrite a physical disk with the data. Some //! preservation formats encode that information, it is guessed for //! others. The write track function of fdcs should set it. The //! representation is the angular position relative to the index. //! //! The media type is divided in two parts. The first half //! indicates the physical form factor, i.e. all medias with that //! form factor can be physically inserted in a reader that handles //! it. The second half indicates the variants which are usually //! detectable by the reader, such as density and number of sides. //! //! Resolution is quarter-track. The optional subtrack parameter is //! 0-3: //! - 0 = Track itself //! - 1 = 1st quarter track //! - 2 = Half track //! - 3 = 2nd quarter track class floppy_image { public: //! Floppy format data enum { TIME_MASK = 0x0fffffff, MG_MASK = 0xf0000000, MG_SHIFT = 28, //!< Bitshift constant for magnetic orientation data MG_A = (0 << MG_SHIFT), //!< - 0, MG_A -> Magnetic orientation A MG_B = (1 << MG_SHIFT), //!< - 1, MG_B -> Magnetic orientation B MG_N = (2 << MG_SHIFT), //!< - 2, MG_N -> Non-magnetized zone (neutral) MG_D = (3 << MG_SHIFT) //!< - 3, MG_D -> Damaged zone, reads as neutral but cannot be changed by writing }; //! Form factors enum { FF_UNKNOWN = 0x00000000, //!< Unknown, useful when converting FF_3 = 0x20202033, //!< "3 " 3 inch disk FF_35 = 0x20203533, //!< "35 " 3.5 inch disk FF_525 = 0x20353235, //!< "525 " 5.25 inch disk FF_8 = 0x20202038 //!< "8 " 8 inch disk }; //! Variants enum { SSSD = 0x44535353, //!< "SSSD", Single-sided single-density SSDD = 0x44445353, //!< "SSDD", Single-sided double-density SSQD = 0x44515353, //!< "SSQD", Single-sided quad-density DSSD = 0x44535344, //!< "DSSD", Double-sided single-density DSDD = 0x44445344, //!< "DSDD", Double-sided double-density (720K in 3.5, 360K in 5.25) DSQD = 0x44515344, //!< "DSQD", Double-sided quad-density (720K in 5.25, means DD+80 tracks) DSHD = 0x44485344, //!< "DSHD", Double-sided high-density (1440K) DSED = 0x44455344 //!< "DSED", Double-sided extra-density (2880K) }; //! Encodings enum { FM = 0x2020464D, //!< " FM", frequency modulation MFM = 0x204D464D, //!< " MFM", modified frequency modulation M2FM = 0x4D32464D //!< "M2FM", modified modified frequency modulation }; // construction/destruction //! floppy_image constructor /*! @param _tracks number of tracks. @param _heads number of heads. @param _form_factor form factor of drive (from enum) */ floppy_image(int tracks, int heads, uint32_t form_factor); virtual ~floppy_image(); //! @return the form factor. uint32_t get_form_factor() const { return form_factor; } //! @return the variant. uint32_t get_variant() const { return variant; } //! @param v the variant. void set_variant(uint32_t v) { variant = v; } /*! @param track @param subtrack @param head head number @return a pointer to the data buffer for this track and head */ std::vector &get_buffer(int track, int head, int subtrack = 0) { return track_array[track*4+subtrack][head].cell_data; } //! Sets the write splice position. //! The "track splice" information indicates where to start writing //! if you try to rewrite a physical disk with the data. Some //! preservation formats encode that information, it is guessed for //! others. The write track function of fdcs should set it. The //! representation is the angular position relative to the index. /*! @param track @param subtrack @param head @param pos the position */ void set_write_splice_position(int track, int head, uint32_t pos, int subtrack = 0) { track_array[track*4+subtrack][head].write_splice = pos; } //! @return the current write splice position. uint32_t get_write_splice_position(int track, int head, int subtrack = 0) const { return track_array[track*4+subtrack][head].write_splice; } //! @return the maximal geometry supported by this format. void get_maximal_geometry(int &tracks, int &heads) const; //! @return the current geometry of the loaded image. void get_actual_geometry(int &tracks, int &heads); //! @return the track resolution (0=full track, 1 = half-track, 2 = quarter track) int get_resolution() const; //! Returns the variant name for the particular disk form factor/variant //! @param form_factor //! @param variant //! @param returns a string containing the variant name. static const char *get_variant_name(uint32_t form_factor, uint32_t variant); private: int tracks, heads; uint32_t form_factor, variant; struct track_info { std::vector cell_data; uint32_t write_splice; track_info() { write_splice = 0; } }; // track number multiplied by 4 then head // last array size may be bigger than actual track size std::vector > track_array; }; #endif /* FLOPIMG_H */