diff --git a/src/lib/formats/as_dsk.cpp b/src/lib/formats/as_dsk.cpp index eef5bb6dc80..38f2544386c 100644 --- a/src/lib/formats/as_dsk.cpp +++ b/src/lib/formats/as_dsk.cpp @@ -248,58 +248,205 @@ bool woz_format::load(util::random_read &io, uint32_t form_factor, const std::ve return true; } -bool woz_format::save(util::random_read_write &io, const std::vector &variants, floppy_image *image) const +as_format::tdata as_format::analyze_for_save(floppy_image *image, int head, int track, int subtrack, int speed_zone) { - std::vector> tracks(160); - bool twosided = false; + // 200000000 / 60.0 * 1.979e-6 ~= 6.5967 + static const int cell_size_per_speed_zone[7] = { + 394 * 65967 / 10000, + 429 * 65967 / 10000, + 472 * 65967 / 10000, + 525 * 65967 / 10000, + 590 * 65967 / 10000, - if(image->get_form_factor() == floppy_image::FF_525) { - for(unsigned int i=0; i != 141; i++) - if(image->track_is_formatted(i >> 2, 0, i & 3)) - tracks[i] = generate_bitstream_from_track(i >> 2, 0, 3915, image, i & 3); + 3915, + 1000 + }; - } else if(image->get_variant() == floppy_image::DSHD) { - for(unsigned int i=0; i != 160; i++) - if(image->track_is_formatted(i >> 1, i & 1)) { - tracks[i] = generate_bitstream_from_track(i >> 1, i & 1, 1000, image); - if(i & 1) - twosided = true; - } + static const int ticks_per_speed_zone[7] = { + 60*8000000 / 394, + 60*8000000 / 429, + 60*8000000 / 472, + 60*8000000 / 525, + 60*8000000 / 590, - } else { - // 200000000 / 60.0 * 1.979e-6 ~= 6.5967 - static const int cell_size_per_speed_zone[5] = { - 394 * 65967 / 10000, - 429 * 65967 / 10000, - 472 * 65967 / 10000, - 525 * 65967 / 10000, - 590 * 65967 / 10000 - }; + 1333333, + 1600000 + }; - for(unsigned int i=0; i != 160; i++) - if(image->track_is_formatted(i >> 1, i & 1)) { - tracks[i] = generate_bitstream_from_track(i >> 1, i & 1, cell_size_per_speed_zone[i / (2*16)], image); - if(i & 1) - twosided = true; - } + tdata result; + + if(!image->track_is_formatted(track, head, subtrack)) + return result; + + // Generate a bitstream to get the data and whether the phase is clean + int cell_size = cell_size_per_speed_zone[speed_zone]; + int max_delta; + std::vector bitstream = generate_bitstream_from_track(track, head, cell_size, image, subtrack, &max_delta); + + // Bitstreams encodable as non-flux have a max_delta as 10% or less, otherwise it's 40% or more. Use 20% as the limit + if(max_delta <= cell_size/5) { + result.track_size = bitstream.size(); + result.data.resize((bitstream.size()+7)/8, 0); + for(unsigned j=0; j != bitstream.size(); j++) + if(bitstream[j]) + result.data[j >> 3] |= 0x80 >> (j & 7); + return result; } + result.flux = true; + + const std::vector &tbuf = image->get_buffer(track, head, subtrack); + uint32_t first_edge = 0, last_edge = 0; + for(uint32_t fp : tbuf) + if((fp & floppy_image::MG_MASK) == floppy_image::MG_F) { + first_edge = fp & floppy_image::TIME_MASK; + break; + } + for(auto i = tbuf.rbegin(); i != tbuf.rend(); ++i) + if((*i & floppy_image::MG_MASK) == floppy_image::MG_F) { + last_edge = *i & floppy_image::TIME_MASK; + break; + } + + int dt = last_edge - 200000000; + if((-dt) < first_edge) + dt = first_edge; + + if(dt < -10000 || dt > 10000) + dt = 0; + + uint32_t cur_tick = 0; + uint64_t ticks = ticks_per_speed_zone[speed_zone]; + for(uint32_t fp : tbuf) + if((fp & floppy_image::MG_MASK) == floppy_image::MG_F) { + uint32_t next_tick = ((fp & floppy_image::TIME_MASK) - dt) * ticks / 200000000; + uint32_t cdt = next_tick - cur_tick; + if(cdt) { + while(cdt >= 255) { + result.data.push_back(255); + cdt -= 255; + } + result.data.push_back(cdt); + } + cur_tick = next_tick; + } + + uint32_t cdt = ticks - cur_tick; + if(cdt) { + while(cdt >= 255) { + result.data.push_back(255); + cdt -= 255; + } + result.data.push_back(cdt); + } + + result.track_size = result.data.size(); + return result; +} + +std::pair as_format::count_blocks(const std::vector &tracks) +{ int max_blocks = 0; int total_blocks = 0; for(const auto &t : tracks) { - int blocks = (t.size() + 4095) / 4096; + int blocks = (t.data.size() + 511) / 512; total_blocks += blocks; if(max_blocks < blocks) max_blocks = blocks; } + return std::make_pair(total_blocks, max_blocks); +} - std::vector data(1536 + total_blocks*512, 0); +bool as_format::test_flux(const std::vector &tracks) +{ + for(const auto &t : tracks) + if(t.flux) + return true; + return false; +} + +void as_format::save_tracks(std::vector &data, const std::vector &tracks, uint32_t total_blocks, bool has_flux) +{ + w32(data, 80, 0x50414D54); // TMAP + w32(data, 84, 160); // size + + uint32_t fstart = 1536 + total_blocks*512; + if(has_flux) { + w32(data, fstart, 0x58554c46); + w32(data, fstart+4, 160); + fstart += 8; + } + + memset(data.data()+88, 0xff, 160); + if(has_flux) + memset(data.data()+fstart, 0xff, 160); + + uint8_t tcount = 0; + for(int i=0; i != 160 ; i++) { + if(!tracks[i].data.empty()) { + if(!tracks[i].flux) + data[88+i] = tcount; + else + data[fstart+i] = tcount; + tcount++; + } + } + + w32(data, 248, 0x534B5254); // TRKS + w32(data, 252, 1280 + total_blocks*512); // size + + uint8_t tid = 0; + uint16_t tb = 3; + for(int i=0; i != 160 ; i++) + if(!tracks[i].data.empty()) { + int size = tracks[i].data.size(); + int blocks = (size + 511) / 512; + memcpy(data.data() + tb*512, tracks[i].data.data(), size); + w16(data, 256 + tid*8, tb); + w16(data, 256 + tid*8 + 2, blocks); + w32(data, 256 + tid*8 + 4, tracks[i].track_size); + tb += blocks; + tid ++; + } + + w32(data, 8, crc32r(&data[12], data.size() - 12)); +} + + +bool woz_format::save(util::random_read_write &io, const std::vector &variants, floppy_image *image) const +{ + std::vector tracks(160); + bool twosided = false; + + if(image->get_form_factor() == floppy_image::FF_525) { + for(unsigned int i=0; i != 141; i++) + tracks[i] = analyze_for_save(image, 0, i >> 2, i & 3, 5); + + } else if(image->get_variant() == floppy_image::DSHD) { + for(unsigned int i=0; i != 160; i++) { + tracks[i] = analyze_for_save(image, i & 1, i >> 1, 0, 6); + if((i & 1) && tracks[i].track_size) + twosided = true; + } + + } else { + for(unsigned int i=0; i != 160; i++) { + tracks[i] = analyze_for_save(image, i & 1, i >> 1, 0, i / (2*16)); + if((i & 1) && tracks[i].track_size) + twosided = true; + } + } + + auto [total_blocks, max_blocks] = count_blocks(tracks); + bool has_flux = test_flux(tracks); + + std::vector data(1536 + total_blocks*512 + (has_flux ? 512 : 0), 0); memcpy(&data[0], signature2, 8); w32(data, 12, 0x4F464E49); // INFO w32(data, 16, 60); // size - data[20] = 2; // chunk version + data[20] = 3; // chunk version data[21] = image->get_form_factor() == floppy_image::FF_525 ? 1 : 2; data[22] = 0; // not write protected data[23] = 1; // synchronized, since our internal format is @@ -315,40 +462,10 @@ bool woz_format::save(util::random_read_write &io, const std::vector & w16(data, 60, 0); // compatibility unknown w16(data, 62, 0); // needed ram unknown w16(data, 64, max_blocks); - w32(data, 80, 0x50414D54); // TMAP - w32(data, 84, 160); // size + w16(data, 66, has_flux ? total_blocks+3 : 0); + w16(data, 68, max_blocks); - uint8_t tcount = 0; - for(int i=0; i != 160 ; i++) - data[88 + i] = tracks[i].empty() ? 0xff : tcount++; - - w32(data, 248, 0x534B5254); // TRKS - w32(data, 252, 1280 + total_blocks*512); // size - - uint8_t tid = 0; - uint16_t tb = 3; - for(int i=0; i != 160 ; i++) - if(!tracks[i].empty()) { - int blocks = (tracks[i].size() + 4095) / 4096; - w16(data, 256 + tid*8, tb); - w16(data, 256 + tid*8 + 2, blocks); - w32(data, 256 + tid*8 + 4, tracks[i].size()); - tb += blocks; - tid ++; - } - - tb = 3; - for(int i=0; i != 160 ; i++) - if(!tracks[i].empty()) { - int off = tb * 512; - int size = tracks[i].size(); - for(int j=0; j != size; j++) - if(tracks[i][j]) - data[off + (j >> 3)] |= 0x80 >> (j & 7); - tb += (size + 4095) / 4096; - } - - w32(data, 8, crc32r(&data[12], data.size() - 12)); + save_tracks(data, tracks, total_blocks, has_flux); size_t actual; io.write_at(0, data.data(), data.size(), actual); diff --git a/src/lib/formats/as_dsk.h b/src/lib/formats/as_dsk.h index b33252a4c77..82091f1d1d7 100644 --- a/src/lib/formats/as_dsk.h +++ b/src/lib/formats/as_dsk.h @@ -16,6 +16,12 @@ public: as_format(); protected: + struct tdata { + std::vector data; + int track_size = 0; + bool flux = false; + }; + static uint32_t r32(const std::vector &data, uint32_t offset); static uint16_t r16(const std::vector &data, uint32_t offset); static uint8_t r8(const std::vector &data, uint32_t offset); @@ -26,6 +32,11 @@ protected: static bool load_bitstream_track(const std::vector &img, floppy_image *image, int head, int track, int subtrack, uint8_t idx, uint32_t off_trks, bool may_be_short, bool set_variant); static void load_flux_track(const std::vector &img, floppy_image *image, int head, int track, int subtrack, uint8_t fidx, uint32_t off_trks); + + static tdata analyze_for_save(floppy_image *image, int head, int track, int subtrack, int speed_zone); + static std::pair count_blocks(const std::vector &tracks); + static bool test_flux(const std::vector &tracks); + static void save_tracks(std::vector &data, const std::vector &tracks, uint32_t total_blocks, bool has_flux); }; class woz_format : public as_format diff --git a/src/lib/formats/flopimg.cpp b/src/lib/formats/flopimg.cpp index 6d57e4f1b8d..67a380d3eaa 100644 --- a/src/lib/formats/flopimg.cpp +++ b/src/lib/formats/flopimg.cpp @@ -1298,7 +1298,7 @@ const floppy_image_format_t::desc_e floppy_image_format_t::amiga_22[] = { { END } }; -std::vector floppy_image_format_t::generate_bitstream_from_track(int track, int head, int cell_size, floppy_image *image, int subtrack) +std::vector floppy_image_format_t::generate_bitstream_from_track(int track, int head, int cell_size, floppy_image *image, int subtrack, int *max_delta) { std::vector trackbuf; std::vector &tbuf = image->get_buffer(track, head, subtrack); @@ -1330,6 +1330,8 @@ std::vector floppy_image_format_t::generate_bitstream_from_track(int track bool next_is_first; public: + int min_delta, max_delta; + pll(const std::vector &_tbuf, int cell_size) : tbuf(_tbuf) { period = cell_size; period_adjust_base = period * 0.05; @@ -1338,6 +1340,8 @@ std::vector floppy_image_format_t::generate_bitstream_from_track(int track max_period = int(cell_size*1.25); phase_adjust = 0; freq_hist = 0; + min_delta = 0; + max_delta = 0; // Try to go back 16 flux changes from the end of the track, or at most at the start int flux_to_step = 16; @@ -1379,6 +1383,10 @@ std::vector floppy_image_format_t::generate_bitstream_from_track(int track bit = true; int delta = edge - (next - period/2); + if(delta < min_delta) + min_delta = delta; + if(delta > max_delta) + max_delta = delta; phase_adjust = 0.65*delta; @@ -1452,6 +1460,12 @@ std::vector floppy_image_format_t::generate_bitstream_from_track(int track trackbuf.push_back(r.first); } + if(max_delta) { + *max_delta = -cpll.min_delta; + if(*max_delta < cpll.max_delta) + *max_delta = cpll.max_delta; + } + return trackbuf; } diff --git a/src/lib/formats/flopimg.h b/src/lib/formats/flopimg.h index 69567a4286e..5566a5eb5cc 100644 --- a/src/lib/formats/flopimg.h +++ b/src/lib/formats/flopimg.h @@ -321,7 +321,7 @@ protected: @endverbatim */ - static std::vector generate_bitstream_from_track(int track, int head, int cell_size, floppy_image *image, int subtrack = 0); + static std::vector generate_bitstream_from_track(int track, int head, int cell_size, floppy_image *image, int subtrack = 0, int *max_delta = nullptr); static std::vector generate_nibbles_from_bitstream(const std::vector &bitstream); struct desc_pc_sector