summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/formats/dmk_dsk.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/formats/dmk_dsk.cpp')
-rw-r--r--src/lib/formats/dmk_dsk.cpp328
1 files changed, 226 insertions, 102 deletions
diff --git a/src/lib/formats/dmk_dsk.cpp b/src/lib/formats/dmk_dsk.cpp
index 6a9e1dc0f0b..d02401b56cf 100644
--- a/src/lib/formats/dmk_dsk.cpp
+++ b/src/lib/formats/dmk_dsk.cpp
@@ -2,20 +2,49 @@
// copyright-holders:Wilbert Pol
/*********************************************************************
- formats/dmk_dsk.h
+ formats/dmk_dsk.cpp
DMK disk images
TODO:
- Add write/format support.
-- Add single density support.
-- Check support on other drivers besides msx.
+- Check support on other drivers besides MSX.
*********************************************************************/
#include "dmk_dsk.h"
+#include "coretmpl.h"
#include "ioprocs.h"
+#include "multibyte.h"
+
+#include <tuple>
+
+
+namespace {
+
+constexpr int HEADER_SIZE = 16;
+
+uint32_t wide_fm(uint16_t val)
+{
+ uint32_t res = 0;
+ for (int i = 15; i >= 0; i--) {
+ res |= (util::BIT(val, i) << (i * 2 + 1));
+ }
+ return res;
+}
+
+uint32_t data_to_wide_fm(uint8_t val)
+{
+ uint16_t res = 0xaaaa; // clock
+ for (int i = 7; i >= 0; i--) {
+ res |= (util::BIT(val, i) << i * 2); // data
+ }
+ return wide_fm(res);
+}
+
+} // anonymous namespace
+
dmk_format::dmk_format()
@@ -23,38 +52,41 @@ dmk_format::dmk_format()
}
-const char *dmk_format::name() const
+const char *dmk_format::name() const noexcept
{
return "dmk";
}
-const char *dmk_format::description() const
+const char *dmk_format::description() const noexcept
{
return "DMK disk image";
}
-const char *dmk_format::extensions() const
+const char *dmk_format::extensions() const noexcept
{
return "dmk";
}
-int dmk_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants)
+int dmk_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint64_t size;
if (io.length(size))
return 0;
- const int header_size = 16;
- uint8_t header[header_size];
+ std::error_condition err;
size_t actual;
- io.read_at(0, header, header_size, actual);
- int tracks = header[1];
- int track_size = ( header[3] << 8 ) | header[2];
- int heads = (header[4] & 0x10) ? 1 : 2;
+ uint8_t header[HEADER_SIZE];
+ std::tie(err, actual) = read_at(io, 0, header, HEADER_SIZE);
+ if (err || (HEADER_SIZE != actual))
+ return 0;
+
+ const int tracks_from_header = header[1];
+ const int track_size = get_u16le(&header[2]);
+ const int heads = util::BIT(header[4], 4) ? 1 : 2;
// The first header byte must be 00 or FF
if (header[0] != 0x00 && header[0] != 0xff)
@@ -62,150 +94,249 @@ int dmk_format::identify(util::random_read &io, uint32_t form_factor, const std:
return 0;
}
- // Bytes C-F must be zero
- if (header[0x0c] != 0 || header[0xd] != 0 || header[0xe] != 0 || header[0xf] != 0)
+ // Verify reserved/unsupported header bytes
+ for (int i = 5; i < 0x10; i++)
{
- return 0;
+ if (header[i] != 0x00)
+ return 0;
}
// Check track size within limits
- if (track_size < 0x80 || track_size > 0x3FFF )
- {
+ if (track_size < 0x80 || track_size > 0x3fff)
return 0;
- }
- if (size == header_size + heads * tracks * track_size)
+ const int tracks_in_file = (size - HEADER_SIZE) / (heads * track_size);
+ for (int track = 0; track < tracks_in_file; track++)
{
- return 70;
- }
+ for (int head = 0; head < heads; head++)
+ {
+ // Read track
+ std::vector<uint8_t> track_data(track_size);
+ std::tie(err, actual) = read_at(io, HEADER_SIZE + (heads * track + head) * track_size, &track_data[0], track_size);
+ if (err || track_size != actual)
+ return 0;
- return 0;
+ // Verify idam entries
+ for (int idam_index = 0; idam_index < 64; idam_index++)
+ {
+ const uint16_t idam_entry = get_u16le(&track_data[2 * idam_index]);
+ if (idam_entry == 0x0000)
+ continue;
+ const uint16_t idam_offset = idam_entry & 0x3fff;
+ if (idam_offset >= track_size)
+ return 0;
+ if (track_data[idam_offset] != 0xfe)
+ return 0;
+ }
+ }
+ }
+ if (size == HEADER_SIZE + heads * tracks_from_header * track_size)
+ return FIFID_HINT|FIFID_SIZE;
+ else
+ return FIFID_HINT;
}
-bool dmk_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image *image)
+bool dmk_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
+ std::error_condition err;
size_t actual;
- const int header_size = 16;
- uint8_t header[header_size];
- io.read_at(0, header, header_size, actual);
+ uint64_t size;
+ if (io.length(size))
+ return false;
- const int tracks = header[1];
- const int track_size = ( header[3] << 8 ) | header[2];
- const int heads = (header[4] & 0x10) ? 1 : 2;
- const bool is_sd = (header[4] & 0x40) ? true : false;
- //const int raw_track_size = 2 * 8 * ( track_size - 0x80 );
+ uint8_t header[HEADER_SIZE];
+ std::tie(err, actual) = read_at(io, 0, header, HEADER_SIZE);
+ if (err || (HEADER_SIZE != actual))
+ return false;
- if (is_sd)
- {
- if (heads == 2)
- {
- image->set_variant(floppy_image::DSSD);
- }
- else
- {
- image->set_variant(floppy_image::SSSD);
- }
- }
- else
- {
- if (heads == 2)
- {
- image->set_variant(floppy_image::DSDD);
- }
- else
- {
- image->set_variant(floppy_image::SSDD);
- }
- }
+ const int track_size = get_u16le(&header[2]);
+ const int heads = util::BIT(header[4], 4) ? 1 : 2;
+ const bool is_sd = util::BIT(header[4], 6);
+ const int tracks = (size - HEADER_SIZE) / (heads * track_size);
+
+ const auto variant = is_sd ? (heads == 2 ? floppy_image::DSSD : floppy_image::SSSD)
+ : (heads == 2 ? floppy_image::DSDD : floppy_image::SSDD);
+ image.set_variant(variant);
+
+ const int fm_stride = is_sd ? 1 : 2;
for (int track = 0; track < tracks; track++)
{
for (int head = 0; head < heads; head++)
{
+ int fm_loss = 0;
std::vector<uint8_t> track_data(track_size);
std::vector<uint32_t> raw_track_data;
+ int mark_location[64 * 2 + 1];
+ uint8_t mark_value[64 * 2 + 1];
+ bool mark_is_mfm[64 * 2 + 1];
int iam_location = -1;
- int idam_location[64];
- int dam_location[64];
// Read track
- io.read_at(header_size + (heads * track + head) * track_size, &track_data[0], track_size, actual);
+ std::tie(err, actual) = read_at(io, HEADER_SIZE + (heads * track + head) * track_size, &track_data[0], track_size);
+ if (err || track_size != actual)
+ return false;
- for (int i = 0; i < 64; i++)
+ for (int i = 0; i < 64 * 2 + 1; i++)
{
- idam_location[i] = -1;
- dam_location[i] = -1;
+ mark_location[i] = -1;
+ mark_value[i] = 0xfe;
+ mark_is_mfm[i] = !is_sd; // Use default encoding
}
+ int mark_count = 0;
- // Find IDAM locations
+ // Find IDAM/DAM locations
uint16_t track_header_offset = 0;
- uint16_t track_offset = ( ( track_data[track_header_offset + 1] << 8 ) | track_data[track_header_offset] ) & 0x3fff;
+ uint16_t track_offset = get_u16le(&track_data[track_header_offset]) & 0x3fff;
+ bool idam_is_mfm = util::BIT(track_data[track_header_offset + 1], 7);
track_header_offset += 2;
- while ( track_offset != 0 && track_offset >= 0x83 && track_offset < track_size && track_header_offset < 0x80 )
+ while (track_offset != 0 && track_offset >= 0x83 && track_offset < track_size && track_header_offset < 0x80)
{
// Assume 3 bytes before IDAM pointers are the start of IDAM indicators
- idam_location[(track_header_offset/2) - 1] = track_offset - 3;
+ int mark_offset = idam_is_mfm ? 3 : 0;
+ mark_location[mark_count] = track_offset - mark_offset;
+ mark_value[mark_count] = 0xfe;
+ mark_is_mfm[mark_count] = idam_is_mfm;
+ mark_count++;
+ int stride = idam_is_mfm ? 1 : fm_stride;
// Scan for DAM location
- for (int i = track_offset + 53; i > track_offset + 10; i--)
+ for (int i = track_offset + 10 * stride; i < track_offset + 53 * stride; i++)
{
- if ((track_data[i] == 0xfb || track_data[i] == 0xf8) && track_data[i-1] == 0xa1 && track_data[i-2] == 0xa1)
+ if ((track_data[i] >= 0xf8 && track_data[i] <= 0xfb))
{
- dam_location[(track_header_offset/2) - 1] = i - 3;
- break;
+ if (!idam_is_mfm || get_u16le(&track_data[i - 2]) == 0xa1a1)
+ {
+ mark_location[mark_count] = i - mark_offset;
+ mark_value[mark_count] = track_data[i];
+ mark_is_mfm[mark_count] = idam_is_mfm;
+ mark_count++;
+
+ break;
+ }
}
}
- track_offset = ( ( track_data[track_header_offset + 1] << 8 ) | track_data[track_header_offset] ) & 0x3fff;
+ idam_is_mfm = util::BIT(track_data[track_header_offset + 1], 7);
+ track_offset = get_u16le(&track_data[track_header_offset]) & 0x3fff;
track_header_offset += 2;
- };
+ }
+
+ // Prevent encoding from switching after last sector
+ if (mark_count > 0)
+ {
+ mark_is_mfm[mark_count] = mark_is_mfm[mark_count - 1];
+ }
// Find IAM location
- for(int i = idam_location[0] - 1; i >= 3; i--)
+ for (int i = mark_location[0] - 1; i >= 3; i--)
{
// It's usually 3 bytes but several dumped tracks seem to contain only 2 bytes
- if (track_data[i] == 0xfc && track_data[i-1] == 0xc2 && track_data[i-2] == 0xc2)
+ if (track_data[i] == 0xfc && (is_sd || get_u16le(&track_data[i - 2]) == 0xc2c2))
{
- iam_location = i - 3;
+ iam_location = i - (is_sd ? 0 : 3);
break;
}
}
- int idam_index = 0;
- int dam_index = 0;
+ int curr_mark = 0;
+ bool enc_mfm = mark_is_mfm[curr_mark];
for (int offset = 0x80; offset < track_size; offset++)
{
if (offset == iam_location)
{
- // Write IAM
- raw_w(raw_track_data, 16, 0x5224);
- raw_w(raw_track_data, 16, 0x5224);
- raw_w(raw_track_data, 16, 0x5224);
- offset += 3;
+ if (!is_sd)
+ {
+ // Write IAM
+ raw_w(raw_track_data, 16, 0x5224);
+ raw_w(raw_track_data, 16, 0x5224);
+ raw_w(raw_track_data, 16, 0x5224);
+ offset += 3;
+ }
+ else
+ {
+ raw_w(raw_track_data, 32, wide_fm(0xf77a)); // FC clocked with D7
+ offset += fm_stride;
+ }
}
- if (offset == idam_location[idam_index])
+ // If close to mark, switch encoding
+ if (offset + 8 >= mark_location[curr_mark])
+ {
+ bool new_enc = mark_is_mfm[curr_mark];
+ if (new_enc != enc_mfm)
+ {
+ enc_mfm = new_enc;
+ }
+ }
+ if (offset == mark_location[curr_mark]
+ || (!enc_mfm && offset - fm_stride + 1 == mark_location[curr_mark])
+ )
{
- raw_w(raw_track_data, 16, 0x4489);
- raw_w(raw_track_data, 16, 0x4489);
- raw_w(raw_track_data, 16, 0x4489);
- idam_index += 1;
- offset += 3;
+ if (enc_mfm)
+ {
+ raw_w(raw_track_data, 16, 0x4489);
+ raw_w(raw_track_data, 16, 0x4489);
+ raw_w(raw_track_data, 16, 0x4489);
+ offset += 3;
+ if (fm_stride == 1)
+ {
+ fm_loss += 3;
+ }
+ }
+ else
+ {
+ uint16_t mark;
+ switch (mark_value[curr_mark])
+ {
+ default:
+ case 0xfb: mark = 0xf56f; break;
+ case 0xfa: mark = 0xf56e; break;
+ case 0xf9: mark = 0xf56b; break;
+ case 0xf8: mark = 0xf56a; break;
+ case 0xfe: mark = 0xf57e; break;
+ }
+ raw_w(raw_track_data, 32, wide_fm(mark));
+ offset += fm_stride;
+ }
+ curr_mark++;
}
- if (offset == dam_location[dam_index])
+ if (enc_mfm)
{
- raw_w(raw_track_data, 16, 0x4489);
- raw_w(raw_track_data, 16, 0x4489);
- raw_w(raw_track_data, 16, 0x4489);
- dam_index += 1;
- offset += 3;
+ if (fm_stride == 1)
+ {
+ fm_loss++;
+ }
+ mfm_w(raw_track_data, 8, track_data[offset]);
}
+ else
+ raw_w(raw_track_data, 32, data_to_wide_fm(track_data[offset]));
+
+
+ if (!enc_mfm)
+ offset += fm_stride - 1;
+ }
- mfm_w(raw_track_data, 8, track_data[offset]);
+ if (fm_loss != 0)
+ {
+ if (enc_mfm)
+ {
+ for (int jj = 0; jj < fm_loss; jj++)
+ {
+ mfm_w(raw_track_data, 8, 0x4e);
+ }
+ }
+ else
+ {
+ for (int jj = 0; jj < fm_loss/2; jj++)
+ {
+ raw_w(raw_track_data, 32, data_to_wide_fm(0xff));
+ }
+ }
}
generate_track_from_levels(track, head, raw_track_data, 0, image);
@@ -215,17 +346,10 @@ bool dmk_format::load(util::random_read &io, uint32_t form_factor, const std::ve
return true;
}
-
-bool dmk_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, floppy_image *image)
-{
- return false;
-}
-
-
-bool dmk_format::supports_save() const
+bool dmk_format::supports_save() const noexcept
{
return false;
}
-const floppy_format_type FLOPPY_DMK_FORMAT = &floppy_image_format_creator<dmk_format>;
+const dmk_format FLOPPY_DMK_FORMAT;