summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/formats/cp68_dsk.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/formats/cp68_dsk.cpp')
-rw-r--r--src/lib/formats/cp68_dsk.cpp296
1 files changed, 296 insertions, 0 deletions
diff --git a/src/lib/formats/cp68_dsk.cpp b/src/lib/formats/cp68_dsk.cpp
new file mode 100644
index 00000000000..5872da350b6
--- /dev/null
+++ b/src/lib/formats/cp68_dsk.cpp
@@ -0,0 +1,296 @@
+// license:BSD-3-Clause copyright-holders:Barry Rodewald, 68bit, Michael R. Furman
+/*
+ * cp68_dsk.cpp - CP68 compatible disk images
+ *
+ * Created on: 02/08/2022
+ *
+ * This CP68 floppy disk image support leverages the wd177x_format support with
+ * the few differences handled by format variations. This format is based off
+ * flex_dsk.cpp with the necessary changes for the CP/68 disk format.
+ *
+ * The CP68 Disk format is as follows:
+ *
+ * Geometry:: 18 Sectors * 35 Tracks * 128 Bytes * 1 Side
+ *
+ * Track 0:: Boot Sectors 0, 1, 2; Directory Sectors 4-18
+ * Tracks 1-34:: Sectors 1-18
+ *
+ * System Information Record:: Sector 2, Bytes $7A-$7F
+ * $7A - System Link Start Track
+ * $7B - System Link Start Sector
+ * $7C - System Link End Track
+ * $7D - System Link End Sector
+ * $7E - Free Chain Start Track
+ * $7F - Free Chain Start Sector
+ *
+ * The formats below include interleaved sectors to improve performance. The
+ * interleave has been chosen to perform well on slower hardware and software
+ * combinations while still offering some performance improvement. Tighter
+ * interleaving may well be possible but it depends on the systems and
+ * software.
+ *
+ * Note about Gap Bytes::
+ * The gap bytes used here for MAME are not standard. The reason is
+ * with 4,000 time quanta for each cell only allows for 50,000 Cells
+ * total on the disk. The original Gap Bytes as shown in the manual
+ * https://archive.org/details/CP-68_An_M6800_Operating_System_1979/page/80/mode/2up?q=GAP
+ * wind up with too many cells to fit on the disk. This has been
+ * adjusted to the minimum allowable according to the WD1771 data
+ * sheet of 8, 11, 11
+ *
+ * Note: This code is compatible with the Disk images from deramp.com (Mike
+ * Douglas) at the following location. It is _NOT_ compatible with the SimH
+ * disk images:
+ *
+ * https://deramp.com/downloads/swtpc/software/CP68/
+ */
+
+#include "cp68_dsk.h"
+
+#include "imageutl.h"
+
+#include "ioprocs.h"
+
+#include <tuple>
+
+
+namespace
+{
+ class cp68_formats : public wd177x_format
+ {
+ public:
+ struct sysinfo_sector_cp68
+ {
+ uint8_t unused1[122]{};
+ uint8_t link_start_track = 0;
+ uint8_t link_start_sector = 0;
+ uint8_t link_end_track = 0;
+ uint8_t link_end_sector = 0;
+ uint8_t fc_start_track = 0;
+ uint8_t fc_start_sector = 0;
+ };
+
+ static const format formats[];
+ static const format formats_head1[];
+ static const format formats_track0[];
+ static const format formats_head1_track0[];
+ };
+}
+
+cp68_format::cp68_format() : wd177x_format(cp68_formats::formats)
+{
+}
+
+const char *cp68_format::name() const noexcept
+{
+ return "cp68";
+}
+
+const char *cp68_format::description() const noexcept
+{
+ return "CP/68 compatible disk image";
+}
+
+const char *cp68_format::extensions() const noexcept
+{
+ return "dsk";
+}
+
+int cp68_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ int type = find_size(io, form_factor, variants);
+
+ if (type != -1)
+ return FIFID_SIZE;
+ return 0;
+}
+
+
+int cp68_format::find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ uint64_t size;
+ if (io.length(size))
+ return -1;
+
+ uint8_t boot0[128];
+ cp68_formats::sysinfo_sector_cp68 info;
+ size_t actual;
+ std::error_condition ec;
+
+ // Look at the boot sector.
+ std::tie(ec, actual) = read_at(io, 128 * 0, &boot0, sizeof(boot0));
+ if (ec || actual == 0) // FIXME: what's the actual minimum size boot sector to probe?
+ return -1;
+ uint8_t boot0_sector_id = 1;
+ // uint8_t boot1_sector_id = 2;
+
+ // This floppy format uses a strategy of looking for 6800 boot code to
+ // set the numbering of the first two sectors. If this is shown to not
+ // be practical in some common cases then a separate format variant
+ // might be needed.
+ if (boot0[0] == 0xbd && boot0[3] == 0x86) {
+ // Found a 6800 jsr and ldaa, looks like a CP68 6800 boot sector.
+ boot0_sector_id = 0;
+ }
+
+ for (int i=0; cp68_formats::formats[i].form_factor; i++) {
+ const format &f = cp68_formats::formats[i];
+
+ // Look at the system information sector.
+ std::tie(ec, actual) = read_at(io, f.sector_base_size * 2, &info, sizeof(struct cp68_formats::sysinfo_sector_cp68));
+ if (ec || actual == 0) // FIXME: what's the actual minimum sector size?
+ continue;
+
+ LOG_FORMATS("CP68 floppy dsk: size %d bytes, %d total sectors, %d remaining bytes, expected form factor %x\n", (uint32_t)size, (uint32_t)size / f.sector_base_size, (uint32_t)size % f.sector_base_size, form_factor);
+
+ // Consistency checks.
+ // Free Chain
+ if (info.fc_start_track < 1 || info.fc_start_track >= f.track_count)
+ continue;
+ else if (info.fc_start_sector < 1 || info.fc_start_sector > f.sector_count)
+ continue;
+
+ // Init Linkage
+ if (info.link_start_track < 1 || info.link_start_track >= f.track_count)
+ continue;
+ else if (info.link_end_track < 1 || info.link_end_track >= f.track_count)
+ continue;
+
+ if (info.link_start_sector < 1 || info.link_start_sector > f.sector_count)
+ continue;
+ else if (info.link_end_sector < 1 || info.link_end_sector > f.sector_count)
+ continue;
+
+
+ unsigned int format_size = 0;
+ for (int track=0; track < f.track_count; track++) {
+ for (int head=0; head < f.head_count; head++) {
+ const format &tf = get_track_format(f, head, track);
+ format_size += compute_track_size(tf);
+ }
+ }
+
+ if (format_size != size)
+ continue;
+
+ // Check that the boot sector ID matches.
+ const format &ft0 = cp68_formats::formats_track0[i];
+ if (ft0.form_factor) {
+ // There is a specialized track 0 format.
+ if (ft0.sector_base_id == -1) {
+ if (ft0.per_sector_id[0] != boot0_sector_id)
+ continue;
+ } else {
+ if (ft0.sector_base_id != boot0_sector_id)
+ continue;
+ }
+ } else {
+ // Otherwise check the default track format.
+ if (f.sector_base_id == -1) {
+ if (f.per_sector_id[0] != boot0_sector_id)
+ continue;
+ } else {
+ if (f.sector_base_id != boot0_sector_id)
+ continue;
+ }
+ }
+
+ LOG_FORMATS("CP68 matching format index %d\n", i);
+ return i;
+ }
+ return -1;
+}
+
+const wd177x_format::format &cp68_format::get_track_format(const format &f, int head, int track) const
+{
+ int n = -1;
+
+ for (int i = 0; cp68_formats::formats[i].form_factor; i++) {
+ if (&cp68_formats::formats[i] == &f) {
+ n = i;
+ break;
+ }
+ }
+
+ if (n < 0) {
+ LOG_FORMATS("Error format not found\n");
+ return f;
+ }
+
+ if (head >= f.head_count) {
+ LOG_FORMATS("Error invalid head %d\n", head);
+ return f;
+ }
+
+ if (track >= f.track_count) {
+ LOG_FORMATS("Error invalid track %d\n", track);
+ return f;
+ }
+
+ if (track > 0) {
+ if (head == 1) {
+ const format &fh1 = cp68_formats::formats_head1[n];
+ if (!fh1.form_factor) {
+ LOG_FORMATS("Error expected a head 1 format\n");
+ return f;
+ }
+ return fh1;
+ }
+ return f;
+ }
+
+ // Track 0
+
+ if (head == 1) {
+ const format &fh1t0 = cp68_formats::formats_head1_track0[n];
+ if (fh1t0.form_factor) {
+ return fh1t0;
+ }
+ const format &fh1 = cp68_formats::formats_head1[n];
+ if (fh1.form_factor) {
+ return fh1;
+ }
+ LOG_FORMATS("Error expected a head 1 format\n");
+ return f;
+ }
+
+ // Head 0
+
+ const format &ft0 = cp68_formats::formats_track0[n];
+ if (ft0.form_factor) {
+ return ft0;
+ }
+
+ return f;
+}
+
+
+const cp68_formats::format cp68_formats::formats[] = {
+ { // 0 87.5K 5 1/4 inch single density cp68 1.0 format three boot sectors
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 18, 35, 1, 128, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 8, 11, 11
+ },
+ {}
+};
+
+const cp68_formats::format cp68_formats::formats_head1[] = {
+ { // 0 87.5K 5 1/4 inch single density cp68 1.0 format three boot sectors
+ },
+ {}
+};
+
+const cp68_formats::format cp68_formats::formats_track0[] = {
+ { // 0 87.5K 5 1/4 inch single density cp68 1.0 format three boot sectors
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 18, 35, 1, 128, {}, -1, {0, 6, 11, 16, 2, 8, 13, 18, 5, 10, 15, 1, 7, 12, 17, 4, 9, 14}, 8, 11, 11
+ },
+ {}
+};
+
+const cp68_formats::format cp68_formats::formats_head1_track0[] = {
+ { // 0 87.5K 5 1/4 inch single density cp68 1.0 format three boot sectors
+ },
+ {}
+};
+
+const cp68_format FLOPPY_CP68_FORMAT;