summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author AJR <ariedlmayer@gmail.com>2025-10-26 13:56:39 -0400
committer AJR <ariedlmayer@gmail.com>2025-10-26 14:06:05 -0400
commitf69ba6ec9fbb2cfbd3693e1ad084d2f9acf98d17 (patch)
tree5133ee227bd4f80210d995d406c9aaf9a13279b9
parent6e0e442a2d06596c1585e2c68392ec3fec0df392 (diff)
Coleco Adam floppy disk emulation improvements
- Completely reimplement Adam .dsk floppy format, permuting sectors of 720KB and 1.44MB 3.5" formats into (and out of) the correct native interleave - Split adam_fdc device into several sets, some now properly supporting 720KB and/or 1.44MB formats - Remove the "Doug Slopsema" BIOS for the 320KB FDC variant upon determining it to be a copy of the 320ta BIOS with slight bitrot
-rw-r--r--src/devices/bus/adamnet/adamnet.cpp4
-rw-r--r--src/devices/bus/adamnet/fdc.cpp240
-rw-r--r--src/devices/bus/adamnet/fdc.h86
-rw-r--r--src/lib/formats/adam_dsk.cpp377
-rw-r--r--src/lib/formats/adam_dsk.h20
-rw-r--r--src/mame/coleco/adam.cpp7
6 files changed, 639 insertions, 95 deletions
diff --git a/src/devices/bus/adamnet/adamnet.cpp b/src/devices/bus/adamnet/adamnet.cpp
index c2886c61248..220c13662c0 100644
--- a/src/devices/bus/adamnet/adamnet.cpp
+++ b/src/devices/bus/adamnet/adamnet.cpp
@@ -261,6 +261,10 @@ void adamnet_devices(device_slot_interface &device)
{
device.option_add("ddp", ADAM_DDP);
device.option_add("fdc", ADAM_FDC);
+ device.option_add("fdc_320kb", ADAM_FDC_320KB);
+ device.option_add("fdc_a720dipi", ADAM_FDC_A720DIPI);
+ device.option_add("fdc_fp720at", ADAM_FDC_FP720AT);
+ device.option_add("fdc_mihddd", ADAM_FDC_MIHDDD);
device.option_add("kb", ADAM_KB);
device.option_add("prn", ADAM_PRN);
device.option_add("spi", ADAM_SPI);
diff --git a/src/devices/bus/adamnet/fdc.cpp b/src/devices/bus/adamnet/fdc.cpp
index b53e655b485..d79aecf849e 100644
--- a/src/devices/bus/adamnet/fdc.cpp
+++ b/src/devices/bus/adamnet/fdc.cpp
@@ -8,17 +8,21 @@
/*
- TODO:
+ Note: the 3.5" controllers are not compatible with 5.25" drives,
+ and vice versa, due to differences in disk geometry and sector
+ interleaving.
- - 320KB DSDD 5.25"
- - 720KB DSDD 3.5"
- - 1.44MB DSHD 3.5"
+ 640KB DSQD 5.25" disks are rumored to have also been used on the
+ Adam, but no images of these are confirmed to exist and they are
+ not supported by any extant FDC variant.
*/
#include "emu.h"
#include "fdc.h"
+#include "machine/clock.h"
+
#include "formats/adam_dsk.h"
@@ -37,6 +41,10 @@
//**************************************************************************
DEFINE_DEVICE_TYPE(ADAM_FDC, adam_fdc_device, "adam_fdc", "Adam FDC")
+DEFINE_DEVICE_TYPE(ADAM_FDC_320KB, adam_fdc_320kb_device, "adam_fdc_320kb", "Adam FDC (320KB DSDD modification)")
+DEFINE_DEVICE_TYPE(ADAM_FDC_A720DIPI, adam_fdc_a720dipi_device, "adam_fdc_a720dipi", "Adam FDC (720KB 3.5\" A720DIPI)")
+DEFINE_DEVICE_TYPE(ADAM_FDC_FP720AT, adam_fdc_fp720at_device, "adam_fdc_fp720at", "Adam FDC (720KB 3.5\" FastPack 720A(T))")
+DEFINE_DEVICE_TYPE(ADAM_FDC_MIHDDD, adam_fdc_mihddd_device, "adam_fdc_mihddd", "Adam FDC (1.44MB 3.5\" Micro Innovations HD-DD)")
//-------------------------------------------------
@@ -45,23 +53,56 @@ DEFINE_DEVICE_TYPE(ADAM_FDC, adam_fdc_device, "adam_fdc", "Adam FDC")
ROM_START( adam_fdc )
ROM_REGION( 0x1000, M6801_TAG, 0 )
- ROM_DEFAULT_BIOS("ssdd")
- ROM_SYSTEM_BIOS( 0, "ssdd", "Coleco 160KB SSDD" )
- ROMX_LOAD( "adam disk u10 ad 31 rev a 09-27-84.u10", 0x0000, 0x1000, CRC(4b0b7143) SHA1(1cb68891c3af80e99efad7e309136ca37244f060), ROM_BIOS(0) )
- ROM_SYSTEM_BIOS( 1, "320ta", "320KB DSDD (Minh Ta?)" )
- ROMX_LOAD( "320ta.u10", 0x0000, 0x1000, CRC(dcd865b3) SHA1(dde583e0d18ce4406e9ea44ab34d083e73ee30e2), ROM_BIOS(1) )
- ROM_SYSTEM_BIOS( 2, "dbl24", "320KB DSDD (DBL)" )
- ROMX_LOAD( "dbl2-4.u10", 0x0000, 0x1000, CRC(5df49f15) SHA1(43d5710e4fb05f520e813869a049585b41ada86b), ROM_BIOS(2) )
- ROM_SYSTEM_BIOS( 3, "320doug", "320KB DSDD (Doug Slopsema)" )
- ROMX_LOAD( "doug.u10", 0x0000, 0x1000, CRC(2b2a9c6d) SHA1(e40304cbb6b9f174d9f5762d920983c79c899b3e), ROM_BIOS(3) )
- ROM_SYSTEM_BIOS( 4, "a720dipi", "720KB 3.5\" A720DIPI 7607 MMSG" )
- ROMX_LOAD( "a720dipi 7607 mmsg =c= 1988.u10", 0x0000, 0x1000, CRC(5f248557) SHA1(15b3aaebba38af84f6a1a6ccdf840ca3d58635da), ROM_BIOS(4) )
- ROM_SYSTEM_BIOS( 5, "fp720at", "720KB 3.5\" FastPack 720A(T)" )
- ROMX_LOAD( "fastpack 720a,t.u10", 0x0000, 0x1000, CRC(8f952c88) SHA1(e593a89d7c6e7ea99e7ce376ffa2732d7b646d49), ROM_BIOS(5) )
- ROM_SYSTEM_BIOS( 6, "mihddd", "1.44MB 3.5\" Micro Innovations HD-DD" )
- ROMX_LOAD( "1440k micro innovations hd-dd.u10", 0x0000, 0x1000, CRC(2efec8c0) SHA1(f6df22339c93dca938b65d0cbe23abcad89ec230), ROM_BIOS(6) )
- ROM_SYSTEM_BIOS( 7, "pmhd", "1.44MB 3.5\" Powermate High Density" )
- ROMX_LOAD( "pmhdfdc.u10", 0x0000, 0x1000, CRC(fed4006c) SHA1(bc8dd00dd5cde9500a4cd7dc1e4d74330184472a), ROM_BIOS(7) )
+ //ROM_SYSTEM_BIOS( n, "ssdd", "Coleco 160KB SSDD" )
+ ROM_LOAD( "adam disk u10 ad 31 rev a 09-27-84.u10", 0x0000, 0x1000, CRC(4b0b7143) SHA1(1cb68891c3af80e99efad7e309136ca37244f060) )
+ROM_END
+
+
+//-------------------------------------------------
+// ROM( adam_fdc_320kb )
+//-------------------------------------------------
+
+ROM_START( adam_fdc_320kb )
+ ROM_REGION( 0x1000, M6801_TAG, 0 )
+ ROM_DEFAULT_BIOS( "320ta" )
+ ROM_SYSTEM_BIOS( 0, "320ta", "Minh Ta?" )
+ ROMX_LOAD( "320ta.u10", 0x0000, 0x1000, CRC(dcd865b3) SHA1(dde583e0d18ce4406e9ea44ab34d083e73ee30e2), ROM_BIOS(0) )
+ ROM_SYSTEM_BIOS( 1, "dbl24", "DBL" )
+ ROMX_LOAD( "dbl2-4.u10", 0x0000, 0x1000, CRC(5df49f15) SHA1(43d5710e4fb05f520e813869a049585b41ada86b), ROM_BIOS(1) )
+ ROM_SYSTEM_BIOS( 2, "pmhd", "PMHD" ) // formerly listed as 1.44MB 3.5" Powermate High Density but actually a bugfixed version of "dbl24"
+ ROMX_LOAD( "pmhdfdc.u10", 0x0000, 0x1000, CRC(fed4006c) SHA1(bc8dd00dd5cde9500a4cd7dc1e4d74330184472a), ROM_BIOS(2) )
+ROM_END
+
+
+//-------------------------------------------------
+// ROM( adam_fdc_a720dipi )
+//-------------------------------------------------
+
+ROM_START( adam_fdc_a720dipi )
+ ROM_REGION( 0x1000, M6801_TAG, 0 )
+ //ROM_SYSTEM_BIOS( n, "a720dipi", "720KB 3.5\" A720DIPI 7607 MMSG" )
+ ROM_LOAD( "a720dipi 7607 mmsg =c= 1988.u10", 0x0000, 0x1000, CRC(5f248557) SHA1(15b3aaebba38af84f6a1a6ccdf840ca3d58635da) )
+ROM_END
+
+
+//-------------------------------------------------
+// ROM( adam_fdc_fp720at )
+//-------------------------------------------------
+
+ROM_START( adam_fdc_fp720at )
+ ROM_REGION( 0x1000, M6801_TAG, 0 )
+ //ROM_SYSTEM_BIOS( n, "fp720at", "720KB 3.5\" FastPack 720A(T)" )
+ ROM_LOAD( "fastpack 720a,t.u10", 0x0000, 0x1000, CRC(8f952c88) SHA1(e593a89d7c6e7ea99e7ce376ffa2732d7b646d49) )
+ROM_END
+
+
+//-------------------------------------------------
+// ROM( adam_fdc_mihddd )
+//-------------------------------------------------
+
+ROM_START( adam_fdc_mihddd )
+ ROM_REGION( 0x1000, M6801_TAG, 0 )
+ ROM_LOAD( "1440k micro innovations hd-dd.u10", 0x0000, 0x1000, CRC(2efec8c0) SHA1(f6df22339c93dca938b65d0cbe23abcad89ec230) )
ROM_END
@@ -74,6 +115,26 @@ const tiny_rom_entry *adam_fdc_device::device_rom_region() const
return ROM_NAME( adam_fdc );
}
+const tiny_rom_entry *adam_fdc_320kb_device::device_rom_region() const
+{
+ return ROM_NAME( adam_fdc_320kb );
+}
+
+const tiny_rom_entry *adam_fdc_a720dipi_device::device_rom_region() const
+{
+ return ROM_NAME( adam_fdc_a720dipi );
+}
+
+const tiny_rom_entry *adam_fdc_fp720at_device::device_rom_region() const
+{
+ return ROM_NAME( adam_fdc_fp720at );
+}
+
+const tiny_rom_entry *adam_fdc_mihddd_device::device_rom_region() const
+{
+ return ROM_NAME( adam_fdc_mihddd );
+}
+
//-------------------------------------------------
// ADDRESS_MAP( fdc6801_mem )
@@ -107,12 +168,28 @@ void adam_fdc_device::floppy_formats(format_registration &fr)
fr.add(FLOPPY_ADAM_FORMAT);
}
-static void adam_fdc_floppies(device_slot_interface &device)
+static void adam_160kb_floppies(device_slot_interface &device)
+{
+ device.option_add("525ssdd", FLOPPY_525_SSDD);
+}
+
+static void adam_320kb_floppies(device_slot_interface &device)
{
device.option_add("525ssdd", FLOPPY_525_SSDD);
device.option_add("525dsdd", FLOPPY_525_DD);
}
+static void adam_720kb_floppies(device_slot_interface &device)
+{
+ device.option_add("35dd", FLOPPY_35_DD);
+}
+
+static void adam_1440kb_floppies(device_slot_interface &device)
+{
+ device.option_add("35dd", FLOPPY_35_DD);
+ device.option_add("35hd", FLOPPY_35_HD);
+}
+
//-------------------------------------------------
// device_add_mconfig - add device configuration
@@ -120,7 +197,7 @@ static void adam_fdc_floppies(device_slot_interface &device)
void adam_fdc_device::device_add_mconfig(machine_config &config)
{
- M6803(config, m_maincpu, 4_MHz_XTAL),
+ M6803(config, m_maincpu, 4_MHz_XTAL);
m_maincpu->set_addrmap(AS_PROGRAM, &adam_fdc_device::adam_fdc_mem);
m_maincpu->in_p1_cb().set(FUNC(adam_fdc_device::p1_r));
m_maincpu->out_p1_cb().set(FUNC(adam_fdc_device::p1_w));
@@ -130,7 +207,42 @@ void adam_fdc_device::device_add_mconfig(machine_config &config)
WD2793(config, m_fdc, 4_MHz_XTAL / 4);
m_fdc->intrq_wr_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
- FLOPPY_CONNECTOR(config, m_connector, adam_fdc_floppies, "525ssdd", adam_fdc_device::floppy_formats).enable_sound(true);
+ FLOPPY_CONNECTOR(config, m_connector, adam_160kb_floppies, "525ssdd", adam_fdc_device::floppy_formats).enable_sound(true);
+}
+
+void adam_fdc_320kb_device::device_add_mconfig(machine_config &config)
+{
+ adam_fdc_device::device_add_mconfig(config);
+
+ FLOPPY_CONNECTOR(config.replace(), m_connector, adam_320kb_floppies, "525dsdd", adam_fdc_320kb_device::floppy_formats).enable_sound(true);
+}
+
+void adam_fdc_a720dipi_device::device_add_mconfig(machine_config &config)
+{
+ adam_fdc_device::device_add_mconfig(config);
+
+ FLOPPY_CONNECTOR(config.replace(), m_connector, adam_720kb_floppies, "35dd", adam_fdc_a720dipi_device::floppy_formats).enable_sound(true);
+}
+
+void adam_fdc_fp720at_device::device_add_mconfig(machine_config &config)
+{
+ adam_fdc_device::device_add_mconfig(config);
+
+ FLOPPY_CONNECTOR(config.replace(), m_connector, adam_720kb_floppies, "35dd", adam_fdc_fp720at_device::floppy_formats).enable_sound(true);
+}
+
+void adam_fdc_mihddd_device::device_add_mconfig(machine_config &config)
+{
+ adam_fdc_device::device_add_mconfig(config);
+
+ // clocks are increased to keep up with 500 Kbps data rate of HD floppy disks
+ m_maincpu->set_clock(8000000);
+ m_fdc->set_clock(2000000);
+
+ // external serial clock used to maintain ADAMnet rate
+ CLOCK(config, "clock", 500000).signal_handler().set([this] (int state) { if (state) { m_maincpu->clock_serial(); } });
+
+ FLOPPY_CONNECTOR(config.replace(), m_connector, adam_1440kb_floppies, "35hd", adam_fdc_mihddd_device::floppy_formats).enable_sound(true);
}
@@ -165,8 +277,8 @@ ioport_constructor adam_fdc_device::device_input_ports() const
// adam_fdc_device - constructor
//-------------------------------------------------
-adam_fdc_device::adam_fdc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, ADAM_FDC, tag, owner, clock),
+adam_fdc_device::adam_fdc_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, type, tag, owner, clock),
device_adamnet_card_interface(mconfig, *this),
m_maincpu(*this, M6801_TAG),
m_fdc(*this, WD2793_TAG),
@@ -177,6 +289,56 @@ adam_fdc_device::adam_fdc_device(const machine_config &mconfig, const char *tag,
{
}
+adam_fdc_device::adam_fdc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : adam_fdc_device(mconfig, ADAM_FDC, tag, owner, clock)
+{
+}
+
+
+//-------------------------------------------------
+// adam_fdc_320kb_device - constructor
+//-------------------------------------------------
+
+adam_fdc_320kb_device::adam_fdc_320kb_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
+ : adam_fdc_device(mconfig, type, tag, owner, clock)
+{
+}
+
+adam_fdc_320kb_device::adam_fdc_320kb_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : adam_fdc_320kb_device(mconfig, ADAM_FDC_320KB, tag, owner, clock)
+{
+}
+
+
+//-------------------------------------------------
+// adam_fdc_a720dipi_device - constructor
+//-------------------------------------------------
+
+adam_fdc_a720dipi_device::adam_fdc_a720dipi_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : adam_fdc_320kb_device(mconfig, ADAM_FDC_A720DIPI, tag, owner, clock)
+{
+}
+
+
+//-------------------------------------------------
+// adam_fdc_fp720at_device - constructor
+//-------------------------------------------------
+
+adam_fdc_fp720at_device::adam_fdc_fp720at_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : adam_fdc_320kb_device(mconfig, ADAM_FDC_FP720AT, tag, owner, clock)
+{
+}
+
+
+//-------------------------------------------------
+// adam_fdc_mihddd_device - constructor
+//-------------------------------------------------
+
+adam_fdc_mihddd_device::adam_fdc_mihddd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : adam_fdc_320kb_device(mconfig, ADAM_FDC_MIHDDD, tag, owner, clock)
+{
+}
+
//-------------------------------------------------
// device_start - device-specific startup
@@ -265,6 +427,12 @@ uint8_t adam_fdc_device::p1_r()
return data;
}
+uint8_t adam_fdc_a720dipi_device::p1_r()
+{
+ // signal inverted here because drive outputs it that way?
+ return adam_fdc_device::p1_r() ^ 0x01;
+}
+
//-------------------------------------------------
// p1_w -
@@ -303,13 +471,27 @@ void adam_fdc_device::p1_w(uint8_t data)
m_fdc->set_floppy(m_floppy);
- // side select (320KB)
- if (m_floppy) m_floppy->ss_w(!BIT(data, 4));
-
// motor enable
if (m_floppy) m_floppy->mon_w(!BIT(data, 6));
}
+void adam_fdc_320kb_device::p1_w(uint8_t data)
+{
+ adam_fdc_device::p1_w(data);
+
+ // side select
+ if (m_floppy) m_floppy->ss_w(!BIT(data, 4));
+}
+
+void adam_fdc_mihddd_device::p1_w(uint8_t data)
+{
+ // keep DDEN low here
+ adam_fdc_320kb_device::p1_w(data & 0xf7);
+
+ // DD/HD select
+ m_fdc->enmf_w(BIT(data, 3));
+}
+
//-------------------------------------------------
// p2_r -
diff --git a/src/devices/bus/adamnet/fdc.h b/src/devices/bus/adamnet/fdc.h
index 36aced31f74..1035ac6d841 100644
--- a/src/devices/bus/adamnet/fdc.h
+++ b/src/devices/bus/adamnet/fdc.h
@@ -32,10 +32,10 @@ public:
adam_fdc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
protected:
- // device-level overrides
- virtual void device_start() override ATTR_COLD;
+ adam_fdc_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
- // optional information overrides
+ // device_t overrides
+ virtual void device_start() override ATTR_COLD;
virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD;
virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
virtual ioport_constructor device_input_ports() const override ATTR_COLD;
@@ -43,7 +43,6 @@ protected:
// device_adamnet_card_interface overrides
virtual void adamnet_reset_w(int state) override;
-private:
required_device<m6801_cpu_device> m_maincpu;
required_device<wd2793_device> m_fdc;
required_device<floppy_connector> m_connector;
@@ -55,17 +54,90 @@ private:
uint8_t read_data_r(offs_t offset);
uint8_t write_data_r(offs_t offset);
- uint8_t p1_r();
- void p1_w(uint8_t data);
+ virtual uint8_t p1_r();
+ virtual void p1_w(uint8_t data);
uint8_t p2_r();
void p2_w(uint8_t data);
+private:
void adam_fdc_mem(address_map &map) ATTR_COLD;
};
-// device type definition
+// ======================> adam_fdc_320kb_device
+
+class adam_fdc_320kb_device : public adam_fdc_device
+{
+public:
+ // construction/destruction
+ adam_fdc_320kb_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+protected:
+ adam_fdc_320kb_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
+
+ // device_t overrides
+ virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD;
+ virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
+
+ virtual void p1_w(uint8_t data) override;
+};
+
+
+// ======================> adam_fdc_a720dipi_device
+
+class adam_fdc_a720dipi_device : public adam_fdc_320kb_device
+{
+public:
+ // construction/destruction
+ adam_fdc_a720dipi_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+protected:
+ // device_t overrides
+ virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD;
+ virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
+
+ virtual uint8_t p1_r() override;
+};
+
+
+// ======================> adam_fdc_fp720at_device
+
+class adam_fdc_fp720at_device : public adam_fdc_320kb_device
+{
+public:
+ // construction/destruction
+ adam_fdc_fp720at_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+protected:
+ // device_t overrides
+ virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD;
+ virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
+};
+
+
+// ======================> adam_fdc_mihddd_device
+
+class adam_fdc_mihddd_device : public adam_fdc_320kb_device
+{
+public:
+ // construction/destruction
+ adam_fdc_mihddd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+protected:
+ // device_t overrides
+ virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD;
+ virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
+
+ virtual void p1_w(uint8_t data) override;
+};
+
+
+// device type declarations
DECLARE_DEVICE_TYPE(ADAM_FDC, adam_fdc_device)
+DECLARE_DEVICE_TYPE(ADAM_FDC_320KB, adam_fdc_320kb_device)
+DECLARE_DEVICE_TYPE(ADAM_FDC_A720DIPI, adam_fdc_a720dipi_device)
+DECLARE_DEVICE_TYPE(ADAM_FDC_FP720AT, adam_fdc_fp720at_device)
+DECLARE_DEVICE_TYPE(ADAM_FDC_MIHDDD, adam_fdc_mihddd_device)
#endif // MAME_BUS_ADAMNET_FDC_H
diff --git a/src/lib/formats/adam_dsk.cpp b/src/lib/formats/adam_dsk.cpp
index 0f8dd81556c..fce713249d5 100644
--- a/src/lib/formats/adam_dsk.cpp
+++ b/src/lib/formats/adam_dsk.cpp
@@ -1,16 +1,99 @@
// license:BSD-3-Clause
-// copyright-holders:Curt Coder
-/*********************************************************************
+// copyright-holders:AJR
+/***************************************************************************
- formats/adam_dsk.cpp
+ Coleco Adam disk image format (new implementation)
- Coleco Adam disk image format
+ The Adam's Elementary Operating System (EOS) sees all mass storage
+ devices as sequences of 1024-byte blocks addressed linearly through the
+ ADAMnet bus. However, these blocks are actually stored on 40-track or
+ 80-track floppy disks in two logically consecutive 512-byte sectors.
+ Furthermore, logically consecutive sectors are physically non-adjacent
+ due to interleaving implemented at the level of the FDC MCU (the
+ geometry is transparent to the operating system), and different native
+ interleaving schemes were devised depending on the number of sectors
+ per track. However, .dsk images are stored using the original 8-sector
+ interleave (1,6,3,8,5,2,7,4) regardless of their capacity. The 3.5-inch
+ formats, which use 9 or 18 sectors per track, thus require a convoluted
+ permutation of the sectors to transform their native interleave to or
+ from the non-native interleave.
-*********************************************************************/
+ Track description for 160KB SSDD format:
+ 100x4e 12x00 3xf5 fe 2x00 01 02 f7 22x4e 12x00 3xf5 fb 512xe5 f7
+ 100x4e 12x00 3xf5 fe 2x00 02 02 f7 22x4e 12x00 3xf5 fb 512xe5 f7
+ 100x4e 12x00 3xf5 fe 2x00 03 02 f7 22x4e 12x00 3xf5 fb 512xe5 f7
+ 100x4e 12x00 3xf5 fe 2x00 04 02 f7 22x4e 12x00 3xf5 fb 512xe5 f7
+ 100x4e 12x00 3xf5 fe 2x00 05 02 f7 22x4e 12x00 3xf5 fb 512xe5 f7
+ 100x4e 12x00 3xf5 fe 2x00 06 02 f7 22x4e 12x00 3xf5 fb 512xe5 f7
+ 100x4e 12x00 3xf5 fe 2x00 07 02 f7 22x4e 12x00 3xf5 fb 512xe5 f7
+ 100x4e 12x00 3xf5 fe 2x00 08 02 f7 22x4e 12x00 3xf5 fb 512xe5 f7
+ 859x4e
-#include "formats/adam_dsk.h"
+***************************************************************************/
-adam_format::adam_format() : wd177x_format(formats)
+#include "adam_dsk.h"
+
+#include "ioprocs.h"
+
+#include <cstring>
+#include <memory>
+
+
+namespace {
+
+struct format_desc
+{
+ uint32_t size;
+ uint32_t form_factor;
+ uint32_t variant;
+ uint32_t cell_size;
+ uint8_t track_count;
+ uint8_t sector_count;
+ uint8_t head_count;
+};
+
+const format_desc s_formats[] =
+{
+ { 163840, floppy_image::FF_525, floppy_image::SSDD, 2000, 40, 8, 1 },
+ { 327680, floppy_image::FF_525, floppy_image::DSDD, 2000, 40, 8, 2 },
+ { 737280, floppy_image::FF_35, floppy_image::DSDD, 2000, 80, 9, 2 },
+ { 1474560, floppy_image::FF_35, floppy_image::DSHD, 1000, 80, 18, 2 },
+ { 0, floppy_image::FF_UNKNOWN, 0, 0, 0, 0, 0 }
+};
+
+const uint8_t s_interleave_9s[9] =
+{
+ 0, 4, 8, 3, 7, 2, 6, 1, 5
+};
+
+const uint8_t s_interleave_18s[18] =
+{
+ 0, 4, 8, 12, 16, 2, 6, 10, 14,
+ 1, 5, 9, 13, 17, 3, 7, 11, 15
+};
+
+const uint8_t s_cpm_boot[8] =
+{
+ 0x18, 0x01, 0xe5, 0x3e, 0xc9, 0x32, 0x66, 0x00
+};
+
+constexpr uint8_t ZZZZ = 0x40; // a "don't care" value
+
+const uint8_t s_eos_directory[0x38] =
+{
+ 0x55, 0xaa, 0x00, 0xff, ZZZZ, ZZZZ, ZZZZ, ZZZZ, ZZZZ, ZZZZ, ZZZZ, ZZZZ, ZZZZ, // magic numbers required by EOS
+ 'B', 'O', 'O', 'T', 0x03, ZZZZ, ZZZZ, ZZZZ, ZZZZ, ZZZZ, ZZZZ, ZZZZ,
+ ZZZZ, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, ZZZZ, ZZZZ, ZZZZ, ZZZZ, ZZZZ, // covers block 0 and only block 0
+ 'D', 'I', 'R', 'E', 'C', 'T', 'O', 'R', 'Y', 0x03, ZZZZ, ZZZZ,
+ ZZZZ, 0x01, 0x00, 0x00, 0x00 // begins with block 1
+};
+
+} // anonymous namespace
+
+
+const adam_format FLOPPY_ADAM_FORMAT;
+
+adam_format::adam_format()
{
}
@@ -29,45 +112,249 @@ const char *adam_format::extensions() const noexcept
return "dsk";
}
-const adam_format::format adam_format::formats[] = {
- // track description
- // 100x4e 12x00 3xf5 fe 2x00 01 02 f7 22x4e 12x00 3xf5 fb 512xe5 f7
- // 100x4e 12x00 3xf5 fe 2x00 02 02 f7 22x4e 12x00 3xf5 fb 512xe5 f7
- // 100x4e 12x00 3xf5 fe 2x00 03 02 f7 22x4e 12x00 3xf5 fb 512xe5 f7
- // 100x4e 12x00 3xf5 fe 2x00 04 02 f7 22x4e 12x00 3xf5 fb 512xe5 f7
- // 100x4e 12x00 3xf5 fe 2x00 05 02 f7 22x4e 12x00 3xf5 fb 512xe5 f7
- // 100x4e 12x00 3xf5 fe 2x00 06 02 f7 22x4e 12x00 3xf5 fb 512xe5 f7
- // 100x4e 12x00 3xf5 fe 2x00 07 02 f7 22x4e 12x00 3xf5 fb 512xe5 f7
- // 100x4e 12x00 3xf5 fe 2x00 08 02 f7 22x4e 12x00 3xf5 fb 512xe5 f7
- // 859x4e
- { /* 160K 5 1/4 inch double density single sided */
- floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
- 2000, 8, 40, 1, 512, {}, 1, {}, 100, 22, 100
- },
-
- // Unverified gap sizes -->
- { /* 320K 5 1/4 inch double density */
- floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
- 2000, 8, 40, 2, 512, {}, 1, {}, 100, 22, 84
- },
- { /* 640K 5 1/4 inch quad density */
- floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
- 2000, 8, 80, 2, 512, {}, 1, {}, 100, 22, 84
- },
- { /* 720K 3 1/2 inch double density */
- floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM,
- 2000, 9, 80, 2, 512, {}, 1, {}, 100, 22, 84
- },
- { /* 1440K 3 1/2 inch high density */
- floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
- 1000, 18, 80, 2, 512, {}, 1, {}, 100, 22, 84
- },
- {}
-};
+bool adam_format::supports_save() const noexcept
+{
+ return true;
+}
+
+int adam_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;
+
+ // Size must match exactly
+ const format_desc *f = &s_formats[0];
+ while (f->size != size)
+ {
+ if (!(++f)->size)
+ return 0;
+ }
-int adam_format::get_image_offset(const format &f, int head, int track) const
+ // Form factor and variant must agree if provided
+ if (form_factor != floppy_image::FF_UNKNOWN && form_factor != f->form_factor)
+ return 0;
+ if (!variants.empty() && !has_variant(variants, f->variant))
+ return 0;
+
+ // Size confirmed; now do a little extra work to confirm presence of Adam data
+ // (since .dsk extension is shared by a lot of formats for other systems)
+
+ // First, check for the beginning of a standalone bootloader commonly used on CP/M disks
+ std::uint8_t buffer[0x38];
+ auto const [err, actual] = read_at(io, 0, &buffer[0], 8);
+ if (!err && actual == 8 && !std::memcmp(&buffer[0], s_cpm_boot, 8))
+ return FIFID_SIZE | FIFID_HINT;
+
+ // EOS bootloaders are varied enough that checking for their system calls is annoying inefficient
+ // Instead, check in block 1 for the magic signature and the special BOOT and DIRECTORY entries
+ // (which some CP/M disks also have as part of a fake directory structure)
+ auto const [err2, actual2] = read_at(io, 0x40d, &buffer[0], 0x38);
+ if (!err2 && actual2 == 0x38)
+ {
+ int i = 0;
+ while (i < 0x38 && (s_eos_directory[i] == ZZZZ || s_eos_directory[i] == buffer[i]))
+ i++;
+ if (i == 0x38)
+ return FIFID_SIZE | FIFID_HINT;
+ }
+
+ return FIFID_SIZE;
+}
+
+bool adam_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- return (track + (head ? f.track_count : 0)) * compute_track_size(f);
+ uint64_t size;
+ if (io.length(size))
+ return false;
+
+ // Find the size that matches
+ const format_desc *f = &s_formats[0];
+ while (f->size != size)
+ {
+ if (!(++f)->size)
+ return false;
+ }
+
+ // Verify that the geometry will fit
+ int max_tracks, max_heads;
+ image.get_maximal_geometry(max_tracks, max_heads);
+ if (f->track_count > max_tracks || f->head_count > max_heads)
+ return false;
+
+ const int track_size = f->sector_count * 512;
+ const auto track_data = std::make_unique<uint8_t []>(track_size);
+ desc_pc_sector sector_desc[18];
+
+ const uint32_t cell_count = 200000000 / f->cell_size;
+ unsigned sector_offset = 0;
+ switch (f->form_factor)
+ {
+ case floppy_image::FF_525:
+ // Sectors are stored in native order
+ for (int sector = 0; sector < f->sector_count; sector++)
+ {
+ sector_desc[sector].sector = sector + 1;
+ sector_desc[sector].size = 2;
+ sector_desc[sector].actual_size = 512;
+ sector_desc[sector].data = &track_data[uint32_t(sector) * 512];
+ sector_desc[sector].deleted = false;
+ sector_desc[sector].bad_crc = false;
+ }
+
+ // For double-sided 5.25" images, read one side after the other
+ for (int head = 0; head < f->head_count; head++)
+ {
+ for (int track = 0; track < f->track_count; track++)
+ {
+ // Read in one entire track
+ auto const [err, actual] = read_at(io, uint32_t(sector_offset) * 512, track_data.get(), track_size);
+ if (err || (actual != track_size))
+ return false;
+
+ // Finish up the sector descriptions
+ for (int sector = 0; sector < f->sector_count; sector++)
+ {
+ sector_desc[sector].track = track;
+ sector_desc[sector].head = head;
+ }
+
+ // Create the track
+ build_wd_track_mfm(track, head, image, cell_count, f->sector_count, sector_desc, 100, 100, 22);
+ sector_offset += f->sector_count;
+ }
+ }
+ break;
+
+ case floppy_image::FF_35:
+ // Set up the native interleave
+ for (int i = 0; i < f->sector_count; i++)
+ {
+ const int sector = f->sector_count == 18 ? s_interleave_18s[i] : s_interleave_9s[i];
+ sector_desc[sector].sector = sector + 1;
+ sector_desc[sector].size = 2;
+ sector_desc[sector].actual_size = 512;
+ sector_desc[sector].data = &track_data[uint32_t(i) * 512];
+ sector_desc[sector].deleted = false;
+ sector_desc[sector].bad_crc = false;
+ }
+
+ // For 3.5" images, tracks are interleaved between sides
+ for (int track = 0; track < f->track_count; track++)
+ {
+ for (int head = 0; head < f->head_count; head++)
+ {
+ for (int i = 0; i < f->sector_count; i++, sector_offset++)
+ {
+ // Apply the non-native sector interleave (5 modulo 8)
+ const unsigned mangled_offset = sector_offset ^ ((sector_offset & 1) << 2);
+
+ // Read in one sector at a time
+ auto const [err, actual] = read_at(io, uint32_t(mangled_offset) * 512, &track_data[uint32_t(i) * 512], 512);
+ if (err || (actual != 512))
+ return false;
+
+ // Also finish up the sector descriptions
+ sector_desc[i].track = track;
+ sector_desc[i].head = head;
+ }
+
+ // Create the track (TODO: verify gap sizes)
+ build_wd_track_mfm(track, head, image, cell_count, f->sector_count, sector_desc, 84, 100, 22);
+ }
+ }
+ break;
+
+ default:
+ // Never happens
+ return false;
+ }
+
+ // Assign the form factor and variant
+ image.set_form_variant(f->form_factor, f->variant);
+
+ // Success
+ return true;
}
-const adam_format FLOPPY_ADAM_FORMAT;
+bool adam_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
+{
+ int tracks, heads;
+ image.get_actual_geometry(tracks, heads);
+
+ // Find an appropriate format
+ const format_desc *f = &s_formats[0];
+ while (f->form_factor != image.get_form_factor() || f->variant != image.get_variant()
+ || f->track_count > tracks || f->head_count > heads)
+ {
+ if (!(++f)->size)
+ return false;
+ }
+
+ unsigned sector_offset = 0;
+ switch (f->form_factor)
+ {
+ case floppy_image::FF_525:
+ for (int head = 0; head < f->head_count; head++)
+ {
+ for (int track = 0; track < f->track_count; track++)
+ {
+ // Extract the sectors for each track
+ const auto bitstream = generate_bitstream_from_track(track, head, f->cell_size, image);
+ const auto sectors = extract_sectors_from_bitstream_mfm_pc(bitstream);
+ if (sectors.size() > f->sector_count + 1)
+ return false;
+
+ for (int sector = 1; sector <= f->sector_count; sector++, sector_offset++)
+ {
+ const auto &s = sectors[sector];
+ if (s.size() != 512)
+ return false;
+
+ // Write each sector in sequence
+ auto const [err, actual] = write_at(io, uint32_t(sector_offset) * 512, s.data(), 512);
+ if (err || (actual != 512))
+ return false;
+ }
+ }
+ }
+ break;
+
+ case floppy_image::FF_35:
+ for (int track = 0; track < f->track_count; track++)
+ {
+ for (int head = 0; head < f->head_count; head++)
+ {
+ // Extract the sectors for each track
+ const auto bitstream = generate_bitstream_from_track(track, head, f->cell_size, image);
+ const auto sectors = extract_sectors_from_bitstream_mfm_pc(bitstream);
+ if (sectors.size() > f->sector_count + 1)
+ return false;
+
+ for (int i = 0; i < f->sector_count; i++, sector_offset++)
+ {
+ // Resolve the native interleave
+ const auto &s = sectors[(f->sector_count == 18 ? s_interleave_18s[i] : s_interleave_9s[i]) + 1];
+ if (s.size() != 512)
+ return false;
+
+ // Apply the non-native sector interleave (5 modulo 8)
+ const unsigned mangled_offset = sector_offset ^ ((sector_offset & 1) << 2);
+
+ // Write each sector
+ auto const [err, actual] = write_at(io, uint32_t(mangled_offset) * 512, s.data(), 512);
+ if (err || (actual != 512))
+ return false;
+ }
+ }
+ }
+ break;
+
+ default:
+ // Never happens
+ return false;
+ }
+
+ // Success at last
+ return true;
+}
diff --git a/src/lib/formats/adam_dsk.h b/src/lib/formats/adam_dsk.h
index 36982ac02bd..f5bb73af65a 100644
--- a/src/lib/formats/adam_dsk.h
+++ b/src/lib/formats/adam_dsk.h
@@ -1,20 +1,14 @@
// license:BSD-3-Clause
-// copyright-holders:Curt Coder
-/*********************************************************************
+// copyright-holders:AJR
- formats/adam_dsk.h
-
- Coleco Adam disk image format
-
-*********************************************************************/
#ifndef MAME_FORMATS_ADAM_DSK_H
#define MAME_FORMATS_ADAM_DSK_H
#pragma once
-#include "wd177x_dsk.h"
+#include "flopimg.h"
-class adam_format : public wd177x_format
+class adam_format : public floppy_image_format_t
{
public:
adam_format();
@@ -22,11 +16,11 @@ public:
virtual const char *name() const noexcept override;
virtual const char *description() const noexcept override;
virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
- virtual int get_image_offset(const format &f, int head, int track) const override;
-
-private:
- static const format formats[];
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
};
extern const adam_format FLOPPY_ADAM_FORMAT;
diff --git a/src/mame/coleco/adam.cpp b/src/mame/coleco/adam.cpp
index f21effdfd67..cabfe98527c 100644
--- a/src/mame/coleco/adam.cpp
+++ b/src/mame/coleco/adam.cpp
@@ -1036,7 +1036,12 @@ void adam_state::adam(machine_config &config)
ADAMNET_SLOT(config, "net2", m_adamnet, adamnet_devices, "prn");
ADAMNET_SLOT(config, "net3", m_adamnet, adamnet_devices, "ddp");
ADAMNET_SLOT(config, "net4", m_adamnet, adamnet_devices, "fdc");
- ADAMNET_SLOT(config, "net5", m_adamnet, adamnet_devices, "fdc").set_option_device_input_defaults("fdc", device_iptdef_drive2);
+ adamnet_slot_device &net5(ADAMNET_SLOT(config, "net5", m_adamnet, adamnet_devices, "fdc"));
+ net.set_option_device_input_defaults("fdc", device_iptdef_drive2);
+ net.set_option_device_input_defaults("fdc_320kb", device_iptdef_drive2);
+ net.set_option_device_input_defaults("fdc_a720dipi", device_iptdef_drive2);
+ net.set_option_device_input_defaults("fdc_fp720at", device_iptdef_drive2);
+ net.set_option_device_input_defaults("fdc_mihddd", device_iptdef_drive2);
ADAMNET_SLOT(config, "net6", m_adamnet, adamnet_devices, nullptr);
ADAMNET_SLOT(config, "net7", m_adamnet, adamnet_devices, nullptr);
ADAMNET_SLOT(config, "net8", m_adamnet, adamnet_devices, nullptr);