summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author fulivi <fulivi@users.noreply.github.com>2020-01-04 22:11:48 +0100
committer R. Belmont <rb6502@users.noreply.github.com>2020-01-04 16:11:48 -0500
commit2ec25ca6d5cf0dd68d5556f8a58af0dc5b1e2260 (patch)
tree8256049c139f4ad9bacbaed240276c4b05249a5d
parentc59bf24ff119dc271f2754e926b69624f5e9b004 (diff)
Emulation of isbc202 floppy disk controller (#6119)
* imds2: isbc202 floppy disk controller added * imds2: attempt to fix problems with CI checks
-rw-r--r--scripts/src/bus.lua14
-rw-r--r--scripts/src/formats.lua12
-rw-r--r--scripts/src/machine.lua24
-rw-r--r--scripts/target/mame/mess.lua4
-rw-r--r--src/devices/bus/multibus/isbc202.cpp1195
-rw-r--r--src/devices/bus/multibus/isbc202.h160
-rw-r--r--src/devices/bus/multibus/multibus.cpp59
-rw-r--r--src/devices/bus/multibus/multibus.h54
-rw-r--r--src/devices/machine/i3001.cpp137
-rw-r--r--src/devices/machine/i3001.h106
-rw-r--r--src/devices/machine/i3002.cpp428
-rw-r--r--src/devices/machine/i3002.h130
-rw-r--r--src/lib/formats/img_dsk.cpp214
-rw-r--r--src/lib/formats/img_dsk.h54
-rw-r--r--src/mame/drivers/imds2.cpp8
-rw-r--r--src/tools/floptool.cpp2
16 files changed, 2601 insertions, 0 deletions
diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua
index d917a846642..17261a3addb 100644
--- a/scripts/src/bus.lua
+++ b/scripts/src/bus.lua
@@ -4085,3 +4085,17 @@ if (BUSES["TMC600"]~=null) then
MAME_DIR .. "src/devices/bus/tmc600/euro.h",
}
end
+
+---------------------------------------------------
+--
+--@src/devices/bus/multibus/multibus.h,BUSES["MULTIBUS"] = true
+---------------------------------------------------
+
+if (BUSES["MULTIBUS"]~=null) then
+ files {
+ MAME_DIR .. "src/devices/bus/multibus/multibus.cpp",
+ MAME_DIR .. "src/devices/bus/multibus/multibus.h",
+ MAME_DIR .. "src/devices/bus/multibus/isbc202.cpp",
+ MAME_DIR .. "src/devices/bus/multibus/isbc202.h",
+ }
+end
diff --git a/scripts/src/formats.lua b/scripts/src/formats.lua
index 6bb724e3e40..e09d34c2e33 100644
--- a/scripts/src/formats.lua
+++ b/scripts/src/formats.lua
@@ -1003,6 +1003,18 @@ end
--------------------------------------------------
--
+--@src/lib/formats/img_dsk.h,FORMATS["IMG_DSK"] = true
+--------------------------------------------------
+
+if (FORMATS["IMG_DSK"]~=null or _OPTIONS["with-tools"]) then
+ files {
+ MAME_DIR.. "src/lib/formats/img_dsk.cpp",
+ MAME_DIR.. "src/lib/formats/img_dsk.h",
+ }
+end
+
+--------------------------------------------------
+--
--@src/lib/formats/iq151_dsk.h,FORMATS["IQ151_DSK"] = true
--------------------------------------------------
diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua
index 7d1eaef0e7b..f04cb88e031 100644
--- a/scripts/src/machine.lua
+++ b/scripts/src/machine.lua
@@ -4196,3 +4196,27 @@ if (MACHINES["VRENDER0"]~=null) then
MAME_DIR .. "src/devices/machine/vrender0.h",
}
end
+
+---------------------------------------------------
+--
+--@src/devices/machine/i3001.h,MACHINES["I3001"] = true
+---------------------------------------------------
+
+if (MACHINES["I3001"]~=null) then
+ files {
+ MAME_DIR .. "src/devices/machine/i3001.cpp",
+ MAME_DIR .. "src/devices/machine/i3001.h",
+ }
+end
+
+---------------------------------------------------
+--
+--@src/devices/machine/i3002.h,MACHINES["I3002"] = true
+---------------------------------------------------
+
+if (MACHINES["I3002"]~=null) then
+ files {
+ MAME_DIR .. "src/devices/machine/i3002.cpp",
+ MAME_DIR .. "src/devices/machine/i3002.h",
+ }
+end
diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua
index 8bc8627ae2b..1c89fe13140 100644
--- a/scripts/target/mame/mess.lua
+++ b/scripts/target/mame/mess.lua
@@ -486,6 +486,8 @@ MACHINES["HP_TACO"] = true
MACHINES["1MA6"] = true
MACHINES["1MB5"] = true
MACHINES["I2CMEM"] = true
+MACHINES["I3001"] = true
+MACHINES["I3002"] = true
MACHINES["I7220"] = true
MACHINES["I80130"] = true
MACHINES["I8087"] = true
@@ -825,6 +827,7 @@ BUSES["MIDI"] = true
BUSES["MEGADRIVE"] = true
BUSES["MSX_SLOT"] = true
BUSES["MTX"] = true
+BUSES["MULTIBUS"] = true
BUSES["NASBUS"] = true
BUSES["NEOGEO"] = true
BUSES["NEOGEO_CTRL"] = true
@@ -975,6 +978,7 @@ FORMATS["HECT_TAP"] = true
FORMATS["HTI_TAP"] = true
FORMATS["HPI_DSK"] = true
FORMATS["HP_IPC_DSK"] = true
+FORMATS["IMG_DSK"] = true
FORMATS["IQ151_DSK"] = true
FORMATS["ITT3030_DSK"] = true
FORMATS["JFD_DSK"] = true
diff --git a/src/devices/bus/multibus/isbc202.cpp b/src/devices/bus/multibus/isbc202.cpp
new file mode 100644
index 00000000000..c8fe127c277
--- /dev/null
+++ b/src/devices/bus/multibus/isbc202.cpp
@@ -0,0 +1,1195 @@
+// license:BSD-3-Clause
+// copyright-holders: F. Ulivi
+/*********************************************************************
+
+ isbc202.cpp
+
+ Intel iSBC-202 SSDD 8" floppy disk controller
+
+ This controller interfaces a standard 8/16-bit Multibus system
+ with up to 4 8" SSDD floppy drives. It was typically used to
+ expand a MDS-II system with double-density drives.
+ The ISIS-II OS identifies the drives as :F0: :F1: :F2: :F3:
+ The iSBC-202 controller is based on two boards: a "channel"
+ board with the actual controller and an "interface" board.
+ The latter handles the low-level aspects of the disks, especially
+ the encoding and decoding of MMFM modulated bits. This board
+ interfaces directly with standard Shugart SA-800-1 drives.
+ The drives are housed, two at time, in a (big) external box
+ that also holds their power supply.
+ The channel board is implemented with a 3000-series bit slice
+ processor. Microcode is stored on 4 512x8 bipolar PROMs.
+ The channel board is bus mastering, i.e. it can issue memory
+ read/write cycles to the main processor RAM.
+ Format of data on disk is entirely Intel proprietary.
+ Intel also designed a similar controller (iSBC-201) for FM
+ IBM-standard disks. AFAIK, this board shares the channel board
+ with iSBC-202 (but not the microcode) and has a different
+ interface board.
+ This table summarizes the main characteristics of the disks.
+
+ | Bit cell size | 2 µs |
+ | Modulation | MMFM |
+ | Bit order | MS first |
+ | Sides | 1 |
+ | Tracks | 77 |
+ | Sectors/track | 52 |
+ | Sector size | 128 bytes|
+ | Formatted size | 500.5 kB |
+ | Rotation speed | 360 RPM |
+
+ Special thanks to Eric Smith for dumping the microcode PROMs.
+ Without his work this driver wouldn't exist at all.
+
+ Reference manuals.
+ - Intellec series II MDS double-density diskette subsystem
+ (schematic drawings) - Intel 1980 - 9800425-02 Rev. B
+ - Intellec double-density diskette operating system hardware
+ reference manual - Intel 1977 - 9800422A
+ - SBC 202 double-density diskette controller hardware reference
+ manual - Intel 1977 - 9800420A
+
+ What follows is a list of things that I left out. They could be
+ implemented at a later time just for completeness sake as ISIS-II
+ doesn't rely on them at all.
+ - The STOP signal (it seems to be used in iSBC-201 only)
+ - Interrupt to CPU
+ - Head load/unload commands (MAME doesn't emulate head loading)
+
+*********************************************************************/
+
+#include "emu.h"
+#include "isbc202.h"
+#include "formats/img_dsk.h"
+
+// Debugging
+#include "logmacro.h"
+#define LOG_BUS_MASK (LOG_GENERAL << 1)
+#define LOG_BUS(...) LOGMASKED(LOG_BUS_MASK, __VA_ARGS__)
+#define LOG_RD_MASK (LOG_BUS_MASK << 1)
+#define LOG_RD(...) LOGMASKED(LOG_RD_MASK, __VA_ARGS__)
+#define LOG_WR_MASK (LOG_RD_MASK << 1)
+#define LOG_WR(...) LOGMASKED(LOG_WR_MASK, __VA_ARGS__)
+#define LOG_DR_MASK (LOG_WR_MASK << 1)
+#define LOG_DR(...) LOGMASKED(LOG_DR_MASK, __VA_ARGS__)
+#undef VERBOSE
+//#define VERBOSE (LOG_GENERAL|LOG_BUS_MASK|LOG_RD_MASK|LOG_WR_MASK|LOG_DR_MASK)
+#define VERBOSE LOG_GENERAL
+
+// Bit manipulation
+namespace {
+ template<typename T> constexpr T BIT_MASK(unsigned n)
+ {
+ return (T)1U << n;
+ }
+
+ template<typename T> void BIT_CLR(T& w , unsigned n)
+ {
+ w &= ~BIT_MASK<T>(n);
+ }
+
+ template<typename T> void BIT_SET(T& w , unsigned n)
+ {
+ w |= BIT_MASK<T>(n);
+ }
+}
+
+// Constants
+constexpr unsigned TIMEOUT_MS = 10; // "timeout" timer: 10 ms
+constexpr unsigned HALF_BIT_CELL_US = 1;// Half bit cell duration in µs
+constexpr unsigned BIT_FREQUENCY = 500000; // Frequency of bit cells in Hz
+constexpr uint16_t CRC_POLY = 0x1021; // CRC-CCITT
+
+// Timers
+enum {
+ TIMEOUT_TMR_ID,
+ BYTE_TMR_ID,
+ F_TMR_ID
+};
+
+// device type definition
+DEFINE_DEVICE_TYPE(ISBC202, isbc202_device, "iSBC202", "iSBC-202 floppy controller")
+
+// Microcode disassembler
+class isbc202_disassembler : public util::disasm_interface
+{
+public:
+ isbc202_disassembler();
+ virtual ~isbc202_disassembler() = default;
+
+ virtual u32 opcode_alignment() const override;
+ virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params) override;
+};
+
+isbc202_disassembler::isbc202_disassembler()
+{
+}
+
+u32 isbc202_disassembler::opcode_alignment() const
+{
+ return 1;
+}
+
+offs_t isbc202_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params)
+{
+ uint32_t microcode = opcodes.r32(pc);
+
+ // Decode address control instruction
+ uint8_t ac = (uint8_t)(microcode >> 25);
+
+ if ((ac & 0b1100000) == 0b0000000) {
+ // JCC
+ util::stream_format(stream , "JCC $%03x" , (((uint16_t)ac & 0b11111) << 4) | (pc & 0xf));
+ } else if ((ac & 0b1110000) == 0b0100000) {
+ // JZR
+ util::stream_format(stream , "JZR $00%x" , ac & 0b1111);
+ } else if ((ac & 0b1110000) == 0b0110000) {
+ // JCR
+ util::stream_format(stream , "JCR $%03x" , (pc & 0b111110000) | (ac & 0b1111));
+ } else if ((ac & 0b1111000) == 0b1110000) {
+ // JCE
+ util::stream_format(stream , "JCE $%03x" , (pc & 0b110001111) | ((ac & 0b111) << 4));
+ } else if ((ac & 0b1110000) == 0b1000000) {
+ // JFL
+ util::stream_format(stream , "JFL $%03x" , (pc & 0b100001000) | ((ac & 0b1111) << 4) | 0b10);
+ } else if ((ac & 0b1111000) == 0b1010000) {
+ // JCF
+ util::stream_format(stream , "JCF $%03x" , (pc & 0b110001000) | ((ac & 0b111) << 4) | 0b10);
+ } else if ((ac & 0b1111000) == 0b1011000) {
+ // JZF
+ util::stream_format(stream , "JZF $%03x" , (pc & 0b110001000) | ((ac & 0b111) << 4) | 0b10);
+ } else if ((ac & 0b1111000) == 0b1100000) {
+ // JPR
+ util::stream_format(stream , "JPR $%02xx" , ((pc >> 4) & 0b11000) | (ac & 0b111));
+ } else if ((ac & 0b1111000) == 0b1101000) {
+ // JLL
+ util::stream_format(stream , "JLL $%03x" , (pc & 0b110000000) | ((ac & 0b111) << 4) | 0b100);
+ } else if ((ac & 0b1111100) == 0b1111100) {
+ // JRL
+ util::stream_format(stream , "JRL $%03x" , (pc & 0b110000000) | ((ac & 0b11) << 4) | 0b001001100);
+ } else {
+ // JPX
+ util::stream_format(stream , "JPX $%02xx" , ((pc >> 4) & 0b11100) | (ac & 0b11));
+ }
+
+ // Decode input multiplexer
+ uint8_t in = (uint8_t)((microcode >> 13) & 7);
+ util::stream_format(stream , " I=%u" , in);
+
+ // Decode function code
+ uint8_t fc = (uint8_t)((microcode >> 18) & 0x7f);
+ uint8_t fg;
+ uint8_t rg;
+ unsigned reg;
+ i3002_device::decode_fc(fc , fg , rg , reg);
+ util::stream_format(stream , " CPE=%u%u/%2s" , fg , rg + 1 , i3002_device::reg_name(reg));
+
+ // Decode flag control
+ stream << (BIT(microcode , 17) ? " FF1" : " FF0");
+ stream << (BIT(microcode , 16) ? " HCZ" : " SCZ");
+
+ // Decode K
+ uint8_t slk = (uint8_t)((microcode >> 8) & 3);
+ uint8_t mask = (uint8_t)microcode;
+ uint8_t kbus;
+ if (!BIT(slk , 0)) {
+ kbus = mask;
+ } else if (slk == 1) {
+ kbus = 0xff;
+ } else {
+ kbus = 0;
+ }
+ util::stream_format(stream , " K=$%02x" , kbus);
+
+ // Decode OUT
+ util::stream_format(stream , " S=%u M=$%02x" , slk , mask);
+ if (BIT(slk , 0) && !BIT(mask , 7)) {
+ uint8_t out = (uint8_t)((microcode >> 10) & 7);
+ util::stream_format(stream , " O=%u" , out);
+ }
+
+ return 1 | SUPPORTED;
+}
+
+// isbc202_device
+isbc202_device::isbc202_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : cpu_device(mconfig , ISBC202 , tag , owner , multibus_slot_device::BUS_CLOCK / 4)
+ , device_multibus_interface(mconfig , *this)
+ , m_mcu(*this , "mcu")
+ , m_cpes(*this , "cpe%u" , 0)
+ , m_drives(*this , "floppy%u" , 0)
+ , m_program_config("microprogram" , ENDIANNESS_BIG , 32 , 9 , -2)
+ , m_cache(nullptr)
+ , m_mem_space(nullptr)
+{
+}
+
+isbc202_device::~isbc202_device()
+{
+}
+
+void isbc202_device::install_io_rw(address_space& space)
+{
+ space.install_readwrite_handler(0x78 , 0x7f , read8_delegate(*this , FUNC(isbc202_device::io_r)) , write8_delegate(*this , FUNC(isbc202_device::io_w)));
+}
+
+void isbc202_device::install_mem_rw(address_space& space)
+{
+ m_mem_space = &space;
+}
+
+READ8_MEMBER(isbc202_device::io_r)
+{
+ uint8_t res = 0;
+
+ switch (offset) {
+ case 0:
+ // Read drive status & INT (auto XACK)
+ // Bit What
+ // 7 0
+ // 6 Drive 3 ready
+ // 5 Drive 2 ready
+ // 4 1
+ // 3 1
+ // 2 Int pending
+ // 1 Drive 1 ready
+ // 0 Drive 0 ready
+ {
+ uint8_t ready = m_ready_in & m_ready_ff;
+ if (BIT(ready , 3)) {
+ BIT_SET(res , 6);
+ }
+ if (BIT(ready , 2)) {
+ BIT_SET(res , 5);
+ }
+ BIT_SET(res , 4);
+ BIT_SET(res , 3);
+ if (m_irq) {
+ BIT_SET(res , 2);
+ }
+ if (BIT(ready , 1)) {
+ BIT_SET(res , 1);
+ }
+ if (BIT(ready , 0)) {
+ BIT_SET(res , 0);
+ }
+ }
+ break;
+
+ case 1:
+ // Read result type (auto XACK)
+ m_irq = false;
+ res = m_data_low_out;
+ break;
+
+ case 3:
+ // Read result byte (no auto XACK)
+ if (m_cpu == nullptr) {
+ m_cpu = dynamic_cast<cpu_device*>(&space.device());
+ set_start(3 , true);
+ } else {
+ m_cpu = nullptr;
+ res = m_data_low_out;
+ }
+ break;
+
+ default:
+ LOG("RD from unknown reg!\n");
+ break;
+ }
+
+ LOG_BUS("IO R @%u=%02x\n" , offset , res);
+ return res;
+}
+
+WRITE8_MEMBER(isbc202_device::io_w)
+{
+ LOG_BUS("IO W @%u=%02x\n" , offset , data);
+
+ switch (offset) {
+ case 0:
+ case 1:
+ // Write LSB address
+ case 2:
+ // Write MSB address & start op
+ case 4:
+ case 5:
+ case 6:
+ if (m_cpu != nullptr) {
+ LOG("CPU != NULL!\n");
+ }
+ m_cpu = dynamic_cast<cpu_device*>(&space.device());
+ m_cpu_data = data;
+ set_start(offset , false);
+ break;
+
+ case 7:
+ // Reset
+ pulse_input_line(INPUT_LINE_RESET , attotime::zero);
+ break;
+
+ default:
+ LOG("WR to unknown reg!\n");
+ break;
+ }
+}
+
+WRITE_LINE_MEMBER(isbc202_device::co_w)
+{
+ m_inputs[ IN_SEL_CO ] = state;
+ m_mcu->fi_w(state);
+ m_cpes[ 3 ]->li_w(state);
+}
+
+READ8_MEMBER(isbc202_device::px_r)
+{
+ if (BIT(m_px_s1s0 , 0)) {
+ return m_cmd & 7;
+ } else if (BIT(m_px_s1s0 , 1)) {
+ return (m_op_us & 7) | 8;
+ } else {
+ return 0;
+ }
+}
+
+void isbc202_device::device_start()
+{
+ state_add(STATE_GENPC , "GENPC" , m_microcode_addr).noshow();
+ state_add(STATE_GENPCBASE , "CURPC" , m_microcode_addr).noshow();
+ state_add(STATE_GENFLAGS , "FLAGS" , m_flags).noshow().callimport().callexport().formatstr("%9s");
+
+ for (int i = 0; i < i3002_device::REG_COUNT; ++i) {
+ state_add(i , i3002_device::reg_name(i) , m_regs[ i ]).callimport().callexport();
+ }
+
+ save_item(NAME(m_flags));
+ save_item(NAME(m_regs));
+ save_item(NAME(m_microcode_addr));
+ save_item(NAME(m_code_word));
+ save_item(NAME(m_ac));
+ save_item(NAME(m_fc));
+ save_item(NAME(m_fc32));
+ save_item(NAME(m_fc10));
+ save_item(NAME(m_in_sel));
+ save_item(NAME(m_out_sel));
+ save_item(NAME(m_slk));
+ save_item(NAME(m_mask));
+ save_item(NAME(m_kbus));
+ save_item(NAME(m_inputs));
+ save_item(NAME(m_op_us));
+ save_item(NAME(m_px_s1s0));
+ save_item(NAME(m_cmd));
+ save_item(NAME(m_cpu_rd));
+ save_item(NAME(m_ready_in));
+ save_item(NAME(m_ready_ff));
+ save_item(NAME(m_gate_lower));
+ save_item(NAME(m_irq));
+ save_item(NAME(m_data_low_out));
+ save_item(NAME(m_data_low_in));
+ save_item(NAME(m_cpu_data));
+ save_item(NAME(m_addr_low_out));
+ save_item(NAME(m_mem_wrt));
+ save_item(NAME(m_wrt_inh));
+ save_item(NAME(m_direction));
+ save_item(NAME(m_ibus_cached));
+ save_item(NAME(m_ibus));
+ save_item(NAME(m_crc));
+ save_item(NAME(m_crc_enabled));
+ save_item(NAME(m_crc_out));
+ save_item(NAME(m_reading));
+ save_item(NAME(m_writing));
+ save_item(NAME(m_data_sr));
+ save_item(NAME(m_last_data_bit));
+ save_item(NAME(m_clock_sr));
+ save_item(NAME(m_last_f_time));
+ save_item(NAME(m_clock_gate));
+ save_item(NAME(m_amwrt));
+ save_item(NAME(m_dlyd_amwrt));
+
+ m_cache = space(AS_PROGRAM).cache<2 , -2 , ENDIANNESS_BIG>();
+ set_icountptr(m_icount);
+ space(AS_PROGRAM).install_rom(0 , 0x1ff , memregion("microcode")->base());
+
+ for (auto& d : m_drives) {
+ d->get_device()->setup_ready_cb(floppy_image_device::ready_cb(&isbc202_device::floppy_ready_cb , this));
+ d->get_device()->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&isbc202_device::floppy_index_cb , this));
+ }
+
+ m_timeout_timer = timer_alloc(TIMEOUT_TMR_ID);
+ m_byte_timer = timer_alloc(BYTE_TMR_ID);
+ m_f_timer = timer_alloc(F_TMR_ID);
+}
+
+void isbc202_device::device_reset()
+{
+ // Set start address
+ m_mcu->addr_w(0);
+ // Select drive #0
+ m_op_us = 0;
+ m_current_drive = m_drives[ 0 ]->get_device();
+
+ // XFERREQ is always 1 because R/W in CPU memory is instantaneous
+ m_inputs[ IN_SEL_XFERQ ] = true;
+
+ m_inputs[ IN_SEL_TIMEOUT ] = true;
+ m_inputs[ IN_SEL_F ] = false;
+
+ m_cpu = nullptr;
+
+ m_irq = false;
+
+ m_reading = false;
+ m_writing = false;
+
+ m_timeout_timer->reset();
+ m_byte_timer->reset();
+ m_f_timer->reset();
+}
+
+void isbc202_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id) {
+ case TIMEOUT_TMR_ID:
+ m_inputs[ IN_SEL_TIMEOUT ] = true;
+ break;
+
+ case BYTE_TMR_ID:
+ m_inputs[ IN_SEL_F ] = true;
+ m_f_timer->adjust(attotime::from_usec(HALF_BIT_CELL_US * 2));
+ m_dlyd_amwrt = m_amwrt;
+ if (m_reading) {
+ m_last_f_time = machine().time();
+ rd_bits(8);
+ m_byte_timer->adjust(m_pll.ctime - machine().time());
+ // Updating of AZ flag actually happens when F goes low
+ m_inputs[ IN_SEL_AZ ] = m_crc == 0;
+ }
+ break;
+
+ case F_TMR_ID:
+ m_inputs[ IN_SEL_F ] = false;
+ if (m_writing) {
+ write_byte();
+ m_data_sr = dbus_r();
+ m_byte_timer->adjust(attotime::from_usec(HALF_BIT_CELL_US * 14));
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+ROM_START(isbc202)
+ ROM_REGION(0x800 , "microcode" , ROMREGION_32BIT | ROMREGION_BE)
+ ROM_LOAD32_BYTE("sbc202-a10-0230.bin" , 0x000 , 0x200 , CRC(e8fa3893) SHA1(88fab74b0466e8aac36eee46cd7536ed1b32a2c9))
+ ROM_LOAD32_BYTE("sbc202-a11-0261.bin" , 0x001 , 0x200 , CRC(3ad01769) SHA1(4c22b8fc3ea599dd49684ff4dcafc29ec3425c4c))
+ ROM_LOAD32_BYTE("sbc202-a12-0233.bin" , 0x002 , 0x200 , CRC(61496232) SHA1(b0473217944b2f6e966d97e97cf5ad8d883a09e4))
+ ROM_LOAD32_BYTE("sbc202-a13-0232.bin" , 0x003 , 0x200 , CRC(c369ab86) SHA1(fc3b7f9c3e71ea1442827c51247a9944c6d40b37))
+ROM_END
+
+const tiny_rom_entry *isbc202_device::device_rom_region() const
+{
+ return ROM_NAME(isbc202);
+}
+
+static void isbc202_floppies(device_slot_interface &device)
+{
+ device.option_add("8ssdd" , FLOPPY_8_SSDD);
+}
+
+static const floppy_format_type isbc202_floppy_formats[] = {
+ FLOPPY_MFI_FORMAT,
+ FLOPPY_IMG_FORMAT,
+ nullptr
+};
+
+void isbc202_device::device_add_mconfig(machine_config &config)
+{
+ I3001(config , m_mcu , 0);
+
+ // Allocation of the bit-slices:
+ // m_cpes[ 0 ] Bits 0..1
+ // m_cpes[ 1 ] Bits 2..3
+ // m_cpes[ 2 ] Bits 4..5
+ // m_cpes[ 3 ] Bits 6..7
+ for (auto& finder : m_cpes) {
+ I3002(config , finder , 0);
+ }
+
+ // Connect CO/CI signals
+ m_mcu->fo_w().set(m_cpes[ 0 ] , FUNC(i3002_device::ci_w));
+ m_cpes[ 0 ]->co_w().set(m_cpes[ 1 ] , FUNC(i3002_device::ci_w));
+ m_cpes[ 1 ]->co_w().set(m_cpes[ 2 ] , FUNC(i3002_device::ci_w));
+ m_cpes[ 2 ]->co_w().set(m_cpes[ 3 ] , FUNC(i3002_device::ci_w));
+ m_cpes[ 3 ]->co_w().set(FUNC(isbc202_device::co_w));
+
+ // Connect RO/LI signals
+ m_cpes[ 0 ]->ro_w().set(FUNC(isbc202_device::co_w));
+ m_cpes[ 1 ]->ro_w().set(m_cpes[ 0 ] , FUNC(i3002_device::li_w));
+ m_cpes[ 2 ]->ro_w().set(m_cpes[ 1 ] , FUNC(i3002_device::li_w));
+ m_cpes[ 3 ]->ro_w().set(m_cpes[ 2 ] , FUNC(i3002_device::li_w));
+
+ // Connect M-bus
+ m_cpes[ 0 ]->mbus_r().set([this]() { return mbus_r(); });
+ m_cpes[ 1 ]->mbus_r().set([this]() { return mbus_r() >> 2; });
+ m_cpes[ 2 ]->mbus_r().set([this]() { return mbus_r() >> 4; });
+ m_cpes[ 3 ]->mbus_r().set([this]() { return mbus_r() >> 6; });
+
+ // Connect I-bus
+ m_cpes[ 0 ]->ibus_r().set([this]() { return ibus_r(); });
+ m_cpes[ 1 ]->ibus_r().set([this]() { return ibus_r() >> 2; });
+ m_cpes[ 2 ]->ibus_r().set([this]() { return ibus_r() >> 4; });
+ m_cpes[ 3 ]->ibus_r().set([this]() { return ibus_r() >> 6; });
+
+ // Connect SX input
+ m_mcu->sx_r().set([this]() { return m_microcode_addr & 0xf; });
+ // Connect PX input
+ m_mcu->px_r().set(FUNC(isbc202_device::px_r));
+
+ // Drives
+ for (auto& finder : m_drives) {
+ FLOPPY_CONNECTOR(config , finder , isbc202_floppies , "8ssdd" , isbc202_floppy_formats).set_fixed(true);
+ }
+}
+
+void isbc202_device::execute_run()
+{
+ do {
+ m_microcode_addr = m_mcu->addr_r();
+ debugger_instruction_hook(m_microcode_addr);
+ m_code_word = m_cache->read_dword(m_microcode_addr);
+
+ // Unpack microcode into fields
+ // Bits Field
+ //================
+ // 31..25 Address Control
+ // 24..18 Function Code
+ // 17 Flag Control bits 3 & 2
+ // 16 Flag Control bits 1 & 0
+ // 15..13 Input multiplexer selection
+ // 12..10 Output control
+ // 9..8 SLK field
+ // 7..0 Mask field
+ m_ac = (uint8_t)(m_code_word >> 25);
+ m_fc = (uint8_t)((m_code_word >> 18) & 0x7f);
+ m_fc32 = BIT(m_code_word , 17);
+ m_fc10 = BIT(m_code_word , 16);
+ m_in_sel = (uint8_t)((m_code_word >> 13) & 7);
+ m_out_sel = (uint8_t)((m_code_word >> 10) & 7);
+ m_slk = (uint8_t)((m_code_word >> 8) & 3);
+ m_mask = (uint8_t)m_code_word;
+
+ m_mcu->fc_w((m_fc32 ? 0b1100 : 0b0000) | (m_fc10 ? 0b0011 : 0b0000));
+
+ // Set outputs
+ if (BIT(m_slk , 0) && !BIT(m_mask , 7)) {
+ set_output();
+ }
+
+ // Compute K-bus
+ if (!BIT(m_slk , 0)) {
+ m_kbus = m_mask;
+ } else if (m_slk == 1) {
+ m_kbus = 0xff;
+ } else {
+ m_kbus = 0;
+ }
+
+ m_ibus_cached = false;
+
+ // Update CPEs
+ m_cpes[ 0 ]->fc_kbus_w(m_fc , m_kbus);
+ m_cpes[ 1 ]->fc_kbus_w(m_fc , m_kbus >> 2);
+ m_cpes[ 2 ]->fc_kbus_w(m_fc , m_kbus >> 4);
+ m_cpes[ 3 ]->fc_kbus_w(m_fc , m_kbus >> 6);
+ if (m_cpes[ 0 ]->update_ro()) {
+ // Data propagate to right (right-shift op)
+ m_cpes[ 3 ]->clk_w(1);
+ m_cpes[ 2 ]->clk_w(1);
+ m_cpes[ 1 ]->clk_w(1);
+ m_cpes[ 0 ]->clk_w(1);
+ } else {
+ // Data propagate to left (every op but right-shift)
+ m_cpes[ 0 ]->clk_w(1);
+ m_cpes[ 1 ]->clk_w(1);
+ m_cpes[ 2 ]->clk_w(1);
+ m_cpes[ 3 ]->clk_w(1);
+ }
+
+ // Update MCU
+ if (m_in_sel == IN_SEL_AC0) {
+ m_mcu->ac_w(m_ac);
+ } else {
+ m_mcu->ac_w((m_ac & 0b1111110) | m_inputs[ m_in_sel ]);
+ }
+ m_mcu->clk_w(1);
+
+ m_icount--;
+ } while (m_icount > 0);
+}
+
+device_memory_interface::space_config_vector isbc202_device::memory_space_config() const
+{
+ return space_config_vector{
+ std::make_pair(AS_PROGRAM , &m_program_config)
+ };
+}
+
+std::unique_ptr<util::disasm_interface> isbc202_device::create_disassembler()
+{
+ return std::make_unique<isbc202_disassembler>();
+}
+
+void isbc202_device::state_import(const device_state_entry &entry)
+{
+ switch (entry.index()) {
+ case i3002_device::REG_R0:
+ case i3002_device::REG_R1:
+ case i3002_device::REG_R2:
+ case i3002_device::REG_R3:
+ case i3002_device::REG_R4:
+ case i3002_device::REG_R5:
+ case i3002_device::REG_R6:
+ case i3002_device::REG_R7:
+ case i3002_device::REG_R8:
+ case i3002_device::REG_R9:
+ case i3002_device::REG_T:
+ case i3002_device::REG_AC:
+ case i3002_device::REG_MAR:
+ m_cpes[ 0 ]->get_reg(entry.index()) = m_regs[ entry.index() ] & i3002_device::WORD_MASK;
+ m_cpes[ 1 ]->get_reg(entry.index()) = (m_regs[ entry.index() ] >> 2) & i3002_device::WORD_MASK;
+ m_cpes[ 2 ]->get_reg(entry.index()) = (m_regs[ entry.index() ] >> 4) & i3002_device::WORD_MASK;
+ m_cpes[ 3 ]->get_reg(entry.index()) = (m_regs[ entry.index() ] >> 6) & i3002_device::WORD_MASK;
+ break;
+
+ default:
+ break;
+ }
+}
+
+void isbc202_device::state_export(const device_state_entry &entry)
+{
+ switch (entry.index()) {
+ case STATE_GENFLAGS:
+ m_flags = 0;
+ if (m_inputs[ IN_SEL_CO ]) {
+ BIT_SET(m_flags , 0);
+ }
+ if (m_mcu->fo_r()) {
+ BIT_SET(m_flags , 1);
+ }
+ if (m_mcu->carry_r()) {
+ BIT_SET(m_flags , 2);
+ }
+ if (m_mcu->zero_r()) {
+ BIT_SET(m_flags , 3);
+ }
+ break;
+
+ case i3002_device::REG_R0:
+ case i3002_device::REG_R1:
+ case i3002_device::REG_R2:
+ case i3002_device::REG_R3:
+ case i3002_device::REG_R4:
+ case i3002_device::REG_R5:
+ case i3002_device::REG_R6:
+ case i3002_device::REG_R7:
+ case i3002_device::REG_R8:
+ case i3002_device::REG_R9:
+ case i3002_device::REG_T:
+ case i3002_device::REG_AC:
+ case i3002_device::REG_MAR:
+ m_regs[ entry.index() ] = m_cpes[ 3 ]->get_reg(entry.index());
+ m_regs[ entry.index() ] <<= 2;
+ m_regs[ entry.index() ] |= m_cpes[ 2 ]->get_reg(entry.index());
+ m_regs[ entry.index() ] <<= 2;
+ m_regs[ entry.index() ] |= m_cpes[ 1 ]->get_reg(entry.index());
+ m_regs[ entry.index() ] <<= 2;
+ m_regs[ entry.index() ] |= m_cpes[ 0 ]->get_reg(entry.index());
+ break;
+
+ default:
+ break;
+ }
+}
+
+void isbc202_device::state_string_export(const device_state_entry &entry, std::string &str) const
+{
+ switch (entry.index()) {
+ case STATE_GENFLAGS:
+ str = string_format("%c %c %s %s" ,
+ BIT(m_flags , 3) ? 'Z' : '-' ,
+ BIT(m_flags , 2) ? 'C' : '-' ,
+ BIT(m_flags , 1) ? "FO" : "--" ,
+ BIT(m_flags , 0) ? "FI" : "--");
+ break;
+
+ default:
+ break;
+ }
+}
+
+void isbc202_device::set_output()
+{
+ switch (m_out_sel) {
+ case 0:
+ // Bit What
+ // 6..3 -
+ // 2 Head load
+ // 1 Start timeout
+ // 0 Step pulse
+ if (BIT(m_mask , 1)) {
+ m_inputs[ IN_SEL_TIMEOUT ] = false;
+ m_timeout_timer->adjust(attotime::from_msec(TIMEOUT_MS));
+ }
+ if (BIT(m_mask , 0) && (!m_direction || m_current_drive->trk00_r())) {
+ LOG_DR("Step %s\n" , m_direction ? "OUT" : "IN");
+ m_current_drive->dir_w(m_direction);
+ m_current_drive->stp_w(0);
+ m_current_drive->stp_w(1);
+ }
+ break;
+
+ case 1:
+ // Bit What
+ // 6 Reset data overrun
+ // 5 Set XACK
+ // 4 Set write inhibit
+ // 3 Clear write inhibit
+ // 2 -
+ // 1 Reset RDY latches (0)
+ // 0 -
+ if (BIT(m_mask , 5)) {
+ if (m_cpu != nullptr) {
+ // Release CPU from wait state
+ LOG_BUS("CPU out of wait state\n");
+ m_cpu->trigger(1);
+ if (!m_cpu_rd) {
+ m_cpu = nullptr;
+ }
+ // Ensure the MCU executes a few instruction before the CPU
+ machine().scheduler().boost_interleave(attotime::from_usec(1) , attotime::from_usec(5));
+ } else {
+ LOG("No CPU to wake up?\n");
+ }
+ m_inputs[ IN_SEL_START ] = false;
+ }
+ if (BIT(m_mask , 4)) {
+ m_wrt_inh = true;
+ }
+ if (BIT(m_mask , 3)) {
+ m_wrt_inh = false;
+ }
+ if (!BIT(m_mask , 1)) {
+ m_ready_ff = 0xf;
+ }
+ break;
+
+ case 2:
+ // Bit What
+ // 6..2 -
+ // 1..0 s1:s0 for PX input selection
+ m_px_s1s0 = m_mask & 3;
+ break;
+
+ case 3:
+ // Bit What
+ // 6 Z2
+ // 5 SR OUT (0 = DATA, 1 = CRC)
+ // 4 Write gate (0)
+ // 3 -
+ // 2 Z1
+ // 1..0 -
+ m_crc_out = BIT(m_mask , 5);
+ set_rd_wr(m_reading, !BIT(m_mask , 4));
+ break;
+
+ case 4:
+ // Bit What
+ // 6 Stepping direction (1 = out, 0 = in)
+ // 5 CRC enable (0)
+ // 4..0 -
+ m_direction = BIT(m_mask , 6);
+ m_crc_enabled = !BIT(m_mask , 5);
+ break;
+
+ case 5:
+ // Bit What
+ // 6..5 -
+ // 4 Mem write (0)
+ // 3 GATE LOWER (0)
+ // 2 -
+ // 1 INOP RESET (0)
+ // 0 AMWRT
+ m_mem_wrt = !BIT(m_mask , 4);
+ m_gate_lower = BIT(m_mask , 3);
+ m_amwrt = BIT(m_mask , 0);
+ break;
+
+ case 6:
+ // Bit What
+ // 6 Latch data bus into A24/A25
+ // 5 Set INT FF
+ // 4 Set Track > 43
+ // 3 Latch D into A[7..0]
+ // 2 Latch D into D[7..0]
+ // 1 Latch D into D[F..8]
+ // 0 Set XFER REQ
+ if (BIT(m_mask , 6)) {
+ m_data_low_in = m_cpu_data;
+ }
+ if (BIT(m_mask , 5)) {
+ m_irq = true;
+ }
+ if (BIT(m_mask , 3)) {
+ m_addr_low_out = dbus_r();
+ }
+ if (BIT(m_mask , 2)) {
+ m_data_low_out = dbus_r();
+ }
+ if (BIT(m_mask , 0)) {
+ if (m_mem_wrt) {
+ if (!m_wrt_inh) {
+ // CPU memory write
+ uint16_t addr = m_addr_low_out | ((uint16_t)abus_r() << 8);
+ if (m_mem_space) {
+ LOG_BUS("MEM W %04x=%02x\n" , addr , m_data_low_out);
+ m_mem_space->write_byte(addr , m_data_low_out);
+ } else {
+ LOG("CPU AS not set!\n");
+ }
+ }
+ } else {
+ // CPU memory read
+ uint16_t addr = m_addr_low_out | ((uint16_t)abus_r() << 8);
+ if (m_mem_space) {
+ m_data_low_in = m_mem_space->read_byte(addr);
+ LOG_BUS("MEM R %04x=%02x\n" , addr , m_data_low_in);
+ } else {
+ LOG("CPU AS not set!\n");
+ }
+ }
+ }
+ break;
+
+ case 7:
+ // Bit What
+ // 6 Latch OP & US
+ // 5 Reset INDEX
+ // 4 Set Track <= 43
+ // 3 Clear START/STOP
+ // 2 Unload head
+ // 1 Set RESET READ
+ // 0 Clear RESET READ
+ if (BIT(m_mask , 6)) {
+ m_op_us = dbus_r() & 0x3f;
+ m_current_drive = m_drives[ selected_drive() ]->get_device();
+ }
+ if (BIT(m_mask , 5)) {
+ m_inputs[ IN_SEL_INDEX ] = false;
+ }
+ if (BIT(m_mask , 3)) {
+ m_cmd = 0;
+ LOG_BUS("CLR ST\n");
+ // TODO: more
+ }
+ if (BIT(m_mask , 1)) {
+ set_rd_wr(false, m_writing);
+ }
+ if (BIT(m_mask , 0)) {
+ set_rd_wr(true, m_writing);
+ }
+ break;
+ }
+}
+
+unsigned isbc202_device::selected_drive() const
+{
+ return (m_op_us >> 4) & 3;
+}
+
+unsigned isbc202_device::drive_idx(floppy_image_device *drive)
+{
+ for (unsigned i = 0; i < 4; ++i) {
+ if (drive == m_drives[ i ]->get_device()) {
+ return i;
+ }
+ }
+ LOG("Unknown drive!\n");
+ return 0;
+}
+
+void isbc202_device::floppy_ready_cb(floppy_image_device *floppy , int state)
+{
+ unsigned idx = drive_idx(floppy);
+
+ uint8_t old_state = m_ready_in;
+
+ if (!state) {
+ BIT_SET(m_ready_in , idx);
+ } else {
+ BIT_CLR(m_ready_in , idx);
+ }
+
+ uint8_t gone_not_ready = ~m_ready_in & old_state;
+
+ BIT_CLR(gone_not_ready, selected_drive());
+
+ m_ready_ff &= ~gone_not_ready;
+}
+
+void isbc202_device::floppy_index_cb(floppy_image_device *floppy , int state)
+{
+ if (state && floppy == m_current_drive) {
+ LOG_DR("Index @%.6f\n" , machine().time().as_double());
+ m_inputs[ IN_SEL_INDEX ] = true;
+ }
+}
+
+uint8_t isbc202_device::dbus_r() const
+{
+ return m_cpes[ 0 ]->dbus_r() |
+ (m_cpes[ 1 ]->dbus_r() << 2) |
+ (m_cpes[ 2 ]->dbus_r() << 4) |
+ (m_cpes[ 3 ]->dbus_r() << 6);
+}
+
+uint8_t isbc202_device::mbus_r() const
+{
+ return m_gate_lower ? 0 : m_data_low_in;
+}
+
+uint8_t isbc202_device::ibus_r()
+{
+ if (!m_ibus_cached) {
+ m_ibus_cached = true;
+ m_ibus = 0xff;
+ if (BIT(m_slk , 0) && BIT(m_mask , 7)) {
+ if (BIT(m_mask , 5)) {
+ // Bit What
+ // 7 Drive 3/1 ready
+ // 6 Drive 2/0 ready
+ // 5 Track 0
+ // 4 STOP
+ // 3..0 1
+ m_ibus = 0x0f;
+ uint8_t ready = m_ready_in & m_ready_ff;
+ if (m_gate_lower) {
+ ready >>= 2;
+ }
+ if (BIT(ready , 1)) {
+ BIT_SET(m_ibus, 7);
+ }
+ if (BIT(ready , 0)) {
+ BIT_SET(m_ibus, 6);
+ }
+ if (!m_current_drive->trk00_r()) {
+ BIT_SET(m_ibus, 5);
+ }
+ // TODO: STOP bit
+ } else if (BIT(m_mask , 6)) {
+ // Bit What
+ // 7 Selected drive not ready
+ // 6 Write fault
+ // 5 Write protection
+ // 4 Data overrun
+ // 3..0 1
+ m_ibus = 0x0f;
+ if (!BIT(m_ready_in , selected_drive())) {
+ BIT_SET(m_ibus, 7);
+ }
+ if (m_current_drive->wpt_r()) {
+ BIT_SET(m_ibus, 5);
+ }
+ } else if (BIT(m_mask , 3)) {
+ // Read clock SR
+ m_ibus = aligned_rd_data(m_clock_sr);
+ } else if (BIT(m_mask , 4)) {
+ // Read data SR
+ m_ibus = aligned_rd_data(m_data_sr);
+ }
+ }
+ }
+
+ return m_ibus;
+}
+
+uint8_t isbc202_device::abus_r() const
+{
+ return m_cpes[ 0 ]->abus_r() |
+ (m_cpes[ 1 ]->abus_r() << 2) |
+ (m_cpes[ 2 ]->abus_r() << 4) |
+ (m_cpes[ 3 ]->abus_r() << 6);
+}
+
+void isbc202_device::set_start(uint8_t off , bool read)
+{
+ m_cmd = off;
+ m_inputs[ IN_SEL_START ] = true;
+ // Put CPU in wait state
+ m_cpu->spin_until_trigger(1);
+ m_cpu_rd = read;
+ LOG_BUS("CPU in wait state (rd=%d)\n" , read);
+ if (read) {
+ // If CPU is suspended when reading, rewind PC so that the
+ // "IN" instruction is repeated when CPU is released
+ m_cpu->set_pc(m_cpu->pc() - 2);
+ }
+}
+
+void isbc202_device::set_rd_wr(bool new_rd , bool new_wr)
+{
+ if (!m_reading && new_rd) {
+ // Start reading
+ LOG_RD("Start RD @%.6f\n" , machine().time().as_double());
+ m_pll.set_clock(attotime::from_usec(HALF_BIT_CELL_US));
+ m_pll.read_reset(machine().time());
+
+ // Search for next SYNC (16x 1 and a 0)
+ m_byte_timer->reset();
+ m_f_timer->reset();
+ unsigned cnt_trans = 0;
+ attotime rot_period = attotime::from_hz(6);
+ while ((m_pll.ctime - machine().time()) < rot_period) {
+ attotime edge = m_current_drive->get_next_transition(m_pll.ctime);
+ if (edge.is_never()) {
+ break;
+ }
+ attotime tm;
+ bool bit = m_pll.feed_read_data(tm , edge , attotime::never);
+ if (cnt_trans < 32) {
+ if (!(BIT(cnt_trans , 0) ^ bit)) {
+ cnt_trans++;
+ } else {
+ cnt_trans = 0;
+ }
+ } else if (cnt_trans == 32) {
+ if (!bit) {
+ cnt_trans++;
+ } else {
+ cnt_trans = 0;
+ }
+ } else {
+ if (!bit) {
+ LOG_RD("SYNC found @%.6f\n" , m_pll.ctime.as_double());
+ // GOT SYNC!
+ if (m_crc_enabled) {
+ // CRC shouldn't be enabled here or register won't get cleared
+ LOG("Huh? CRC enabled during SYNC scan?\n");
+ }
+ m_crc = 0;
+ // Load the "0" bit into data/clock SR
+ m_data_sr = 0;
+ m_clock_sr = 0;
+ // Read 7 more bits to make a full byte
+ rd_bits(7);
+ // Timer to go off at end of 8th bit of AM byte (when F signal goes high)
+ m_byte_timer->adjust(m_pll.ctime - machine().time());
+ break;
+ } else {
+ cnt_trans = 32;
+ }
+ }
+ }
+ } else if (m_reading && !new_rd) {
+ // Stop reading
+ LOG_RD("Stop RD\n");
+ m_byte_timer->reset();
+ m_f_timer->reset();
+ m_inputs[ IN_SEL_F ] = false;
+ }
+ m_reading = new_rd;
+
+ if (!m_writing && new_wr) {
+ // Start writing
+ LOG_WR("Start WR\n");
+ m_pll.set_clock(attotime::from_usec(HALF_BIT_CELL_US));
+ m_pll.start_writing(machine().time());
+ m_pll.ctime = machine().time();
+ m_last_data_bit = false;
+ m_byte_timer->adjust(attotime::from_usec(HALF_BIT_CELL_US * 14));
+ } else if (m_writing && !new_wr) {
+ // Stop writing
+ LOG_WR("Stop WR\n");
+ m_pll.stop_writing(m_current_drive , machine().time());
+ m_byte_timer->reset();
+ m_f_timer->reset();
+ m_inputs[ IN_SEL_F ] = false;
+ }
+ m_writing = new_wr;
+}
+
+uint8_t isbc202_device::aligned_rd_data(uint16_t sr)
+{
+ attotime tmp{ machine().time() - m_last_f_time };
+
+ // Compute how many bit cells have gone by since the last time F went high
+ unsigned bits = tmp.as_ticks(BIT_FREQUENCY);
+ if (bits) {
+ LOG_RD("Aligning by %u bits\n" , bits);
+ sr <<= bits;
+ }
+ return (uint8_t)(sr >> 8);
+}
+
+void isbc202_device::rd_bits(unsigned n)
+{
+ while (n--) {
+ attotime edge = m_current_drive->get_next_transition(m_pll.ctime);
+ if (edge.is_never()) {
+ break;
+ }
+ attotime tm;
+ bool clock_bit = m_pll.feed_read_data(tm , edge , attotime::never);
+ edge = m_current_drive->get_next_transition(m_pll.ctime);
+ if (edge.is_never()) {
+ break;
+ }
+ bool data_bit = m_pll.feed_read_data(tm , edge , attotime::never);
+
+ m_clock_sr = (m_clock_sr << 1) | clock_bit;
+ bool crc_bit = BIT(m_data_sr , 15);
+ m_data_sr = (m_data_sr << 1) | data_bit;
+ update_crc(crc_bit);
+ }
+ LOG_RD("CLK %04x DT %04x CRC %04x\n" , m_clock_sr , m_data_sr , m_crc);
+}
+
+void isbc202_device::write_byte()
+{
+ LOG_WR("WR DT %02x CRC %04x CE %d CO %d AW %d @%.6f\n" , m_data_sr & 0xff , m_crc , m_crc_enabled , m_crc_out , m_dlyd_amwrt , machine().time().as_double());
+
+ for (unsigned i = 0; i < 8; i++) {
+ bool sr_bit = BIT(m_data_sr , 7);
+ bool crc_bit = update_crc(sr_bit);
+ bool data_bit = m_crc_out ? crc_bit : sr_bit;
+ bool clock_bit = m_clock_gate && !data_bit && !m_last_data_bit;
+ if (i <= 3 && m_dlyd_amwrt) {
+ m_clock_gate = true;
+ } else {
+ m_clock_gate = !data_bit && !clock_bit;
+ }
+ attotime dummy;
+
+ m_pll.write_next_bit(clock_bit , dummy , nullptr , attotime::never);
+ m_pll.write_next_bit(data_bit , dummy , nullptr , attotime::never);
+ m_data_sr <<= 1;
+ m_last_data_bit = data_bit;
+ }
+ m_pll.commit(m_current_drive , machine().time());
+ m_pll.ctime = machine().time();
+}
+
+bool isbc202_device::update_crc(bool bit)
+{
+ bool out = BIT(m_crc , 15);
+
+ if (m_crc_enabled && (out ^ bit)) {
+ m_crc = (m_crc << 1) ^ CRC_POLY;
+ } else {
+ m_crc <<= 1;
+ }
+
+ return out;
+}
diff --git a/src/devices/bus/multibus/isbc202.h b/src/devices/bus/multibus/isbc202.h
new file mode 100644
index 00000000000..b72e860b9fc
--- /dev/null
+++ b/src/devices/bus/multibus/isbc202.h
@@ -0,0 +1,160 @@
+// license:BSD-3-Clause
+// copyright-holders: F. Ulivi
+/*********************************************************************
+
+ isbc202.h
+
+ Intel iSBC-202 SSDD 8" floppy disk controller
+
+*********************************************************************/
+
+#ifndef MAME_BUS_MULTIBUS_ISBC202_H
+#define MAME_BUS_MULTIBUS_ISBC202_H
+
+#pragma once
+
+#include "multibus.h"
+#include "machine/i3001.h"
+#include "machine/i3002.h"
+#include "machine/fdc_pll.h"
+#include "imagedev/floppy.h"
+
+class isbc202_device : public cpu_device,
+ public device_multibus_interface
+{
+public:
+ // Construction/destruction
+ isbc202_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+ virtual ~isbc202_device();
+
+ // device_multibus_interface overrides
+ virtual void install_io_rw(address_space& space) override;
+ virtual void install_mem_rw(address_space& space) override;
+
+ // Access to I/O space by CPU
+ DECLARE_READ8_MEMBER(io_r);
+ DECLARE_WRITE8_MEMBER(io_w);
+
+ DECLARE_WRITE_LINE_MEMBER(co_w);
+
+ DECLARE_READ8_MEMBER(px_r);
+
+protected:
+ // device_t overrides
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
+ virtual const tiny_rom_entry *device_rom_region() const override;
+ virtual void device_add_mconfig(machine_config &config) override;
+ // device_execute_interface overrides
+ virtual uint32_t execute_min_cycles() const noexcept override { return 1; }
+ virtual void execute_run() override;
+ // device_memory_interface overrides
+ virtual space_config_vector memory_space_config() const override;
+ // device_disasm_interface overrides
+ virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;
+ // device_state_interface overrides
+ virtual void state_import(const device_state_entry &entry) override;
+ virtual void state_export(const device_state_entry &entry) override;
+ virtual void state_string_export(const device_state_entry &entry, std::string &str) const override;
+
+private:
+ required_device<i3001_device> m_mcu;
+ required_device_array<i3002_device , 4> m_cpes;
+ required_device_array<floppy_connector , 4> m_drives;
+
+ address_space_config m_program_config;
+ memory_access_cache<2 , -2 , ENDIANNESS_BIG> *m_cache;
+
+ address_space *m_mem_space;
+
+ int m_icount;
+ uint8_t m_flags;
+ uint8_t m_regs[ i3002_device::REG_COUNT ];
+ uint16_t m_microcode_addr;
+ uint32_t m_code_word;
+ uint8_t m_ac;
+ uint8_t m_fc;
+ bool m_fc32;
+ bool m_fc10;
+ enum { // m_in_sel Input on AC[0]
+ IN_SEL_AC0, // 0 AC[0]
+ IN_SEL_CO, // 1 CO
+ IN_SEL_START, // 2 START
+ IN_SEL_F, // 3 F
+ IN_SEL_AZ, // 4 AZ
+ IN_SEL_INDEX, // 5 INDEX
+ IN_SEL_XFERQ, // 6 XFERREQ
+ IN_SEL_TIMEOUT,//7 TIMEOUT
+ IN_SEL_COUNT
+ };
+ uint8_t m_in_sel;
+ uint8_t m_out_sel;
+ uint8_t m_slk;
+ uint8_t m_mask;
+ uint8_t m_kbus;
+ bool m_inputs[ IN_SEL_COUNT ];
+ // IC identifiers have this form: x-Ayy
+ // where x is either C (for channel board) or I (for interface board)
+ // and yy is the numeric IC ID.
+ uint8_t m_op_us; // C-A44
+ floppy_image_device *m_current_drive;
+ uint8_t m_px_s1s0; // C-A16-11 & C-A16-13
+ uint8_t m_cmd; // C-A8
+ cpu_device *m_cpu; // When != nullptr: CPU is suspended in wait state
+ bool m_cpu_rd;
+ uint8_t m_ready_in;
+ uint8_t m_ready_ff; // I-A44 & I-A43
+ bool m_gate_lower; // I-A58-9
+ bool m_irq; // C-A37-9
+ uint8_t m_data_low_out; // C-A43
+ uint8_t m_data_low_in; // C-A25
+ uint8_t m_cpu_data;
+ uint8_t m_addr_low_out; // C-A41
+ bool m_mem_wrt; // I-A58-4
+ bool m_wrt_inh; // I-A46-13
+ bool m_direction; // I-A58-11
+ bool m_ibus_cached;
+ uint8_t m_ibus;
+ uint16_t m_crc; // I-A62
+ bool m_crc_enabled; // I-A58-13
+ bool m_crc_out; // C-A16-2
+ bool m_reading; // I-A46-9
+ bool m_writing; // C-A16-4
+ uint16_t m_data_sr; // C-A36 & C-A29 (MSB), next byte in LSB
+ bool m_last_data_bit; // I-A48-6
+ uint16_t m_clock_sr; // C-A34 & C-A27 (MSB), next byte in LSB
+ attotime m_last_f_time;
+ bool m_clock_gate; // I-A10-8
+ bool m_amwrt; // I-A58-6
+ bool m_dlyd_amwrt;
+
+ // PLL
+ fdc_pll_t m_pll;
+
+ // Timers
+ emu_timer *m_timeout_timer;
+ emu_timer *m_byte_timer;
+ emu_timer *m_f_timer;
+
+ void set_output();
+ unsigned selected_drive() const;
+ unsigned drive_idx(floppy_image_device *drive);
+ void floppy_ready_cb(floppy_image_device *floppy , int state);
+ void floppy_index_cb(floppy_image_device *floppy , int state);
+ uint8_t dbus_r() const;
+ uint8_t mbus_r() const;
+ uint8_t abus_r() const;
+ uint8_t ibus_r();
+ void set_start(uint8_t off , bool read);
+ void set_rd_wr(bool new_rd , bool new_wr);
+ uint8_t aligned_rd_data(uint16_t sr);
+ void rd_bits(unsigned n);
+ void write_byte();
+ bool update_crc(bool bit);
+};
+
+// device type declaration
+DECLARE_DEVICE_TYPE(ISBC202, isbc202_device)
+
+#endif /* MAME_BUS_MULTIBUS_ISBC202_H */
diff --git a/src/devices/bus/multibus/multibus.cpp b/src/devices/bus/multibus/multibus.cpp
new file mode 100644
index 00000000000..d962e01070b
--- /dev/null
+++ b/src/devices/bus/multibus/multibus.cpp
@@ -0,0 +1,59 @@
+// license:BSD-3-Clause
+// copyright-holders: F. Ulivi
+/*********************************************************************
+
+ multibus.cpp
+
+ Intel Multibus
+
+*********************************************************************/
+
+#include "emu.h"
+#include "multibus.h"
+#include "isbc202.h"
+
+// device type definition
+DEFINE_DEVICE_TYPE(MULTIBUS_SLOT, multibus_slot_device, "Multibus slot", "Intel Multibus slot")
+
+multibus_slot_device::multibus_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig , MULTIBUS_SLOT , tag , owner , clock)
+ , device_single_card_slot_interface<device_multibus_interface>(mconfig , *this)
+{
+ option_reset();
+ option_add("isbc202" , ISBC202);
+ set_default_option(nullptr);
+ set_fixed(false);
+}
+
+multibus_slot_device::~multibus_slot_device()
+{
+}
+
+void multibus_slot_device::install_io_rw(address_space& space)
+{
+ device_multibus_interface *card = get_card_device();
+ if (card) {
+ card->install_io_rw(space);
+ }
+}
+
+void multibus_slot_device::install_mem_rw(address_space& space)
+{
+ device_multibus_interface *card = get_card_device();
+ if (card) {
+ card->install_mem_rw(space);
+ }
+}
+
+void multibus_slot_device::device_start()
+{
+}
+
+device_multibus_interface::device_multibus_interface(const machine_config &mconfig , device_t &device)
+ : device_interface(device , "multibus")
+{
+}
+
+device_multibus_interface::~device_multibus_interface()
+{
+}
diff --git a/src/devices/bus/multibus/multibus.h b/src/devices/bus/multibus/multibus.h
new file mode 100644
index 00000000000..f45a486a516
--- /dev/null
+++ b/src/devices/bus/multibus/multibus.h
@@ -0,0 +1,54 @@
+// license:BSD-3-Clause
+// copyright-holders: F. Ulivi
+/*********************************************************************
+
+ multibus.h
+
+ Intel Multibus
+
+*********************************************************************/
+
+#ifndef MAME_BUS_MULTIBUS_MULTIBUS_H
+#define MAME_BUS_MULTIBUS_MULTIBUS_H
+
+#pragma once
+
+class device_multibus_interface;
+
+class multibus_slot_device : public device_t,
+ public device_single_card_slot_interface<device_multibus_interface>
+{
+public:
+ multibus_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+ virtual ~multibus_slot_device();
+
+ static constexpr unsigned BUS_CLOCK = 9830400;
+
+ // Install r/w functions in I/O space
+ void install_io_rw(address_space& space);
+
+ // Set memory space
+ void install_mem_rw(address_space& space);
+
+protected:
+ virtual void device_start() override;
+};
+
+class device_multibus_interface : public device_interface
+{
+public:
+ // Install r/w functions in I/O space
+ virtual void install_io_rw(address_space& space) = 0;
+
+ // Set CPU memory space
+ virtual void install_mem_rw(address_space& space) = 0;
+
+protected:
+ device_multibus_interface(const machine_config &mconfig , device_t &device);
+ virtual ~device_multibus_interface();
+};
+
+// device type declaration
+DECLARE_DEVICE_TYPE(MULTIBUS_SLOT, multibus_slot_device)
+
+#endif /* MAME_BUS_MULTIBUS_MULTIBUS_H */
diff --git a/src/devices/machine/i3001.cpp b/src/devices/machine/i3001.cpp
new file mode 100644
index 00000000000..decca559d56
--- /dev/null
+++ b/src/devices/machine/i3001.cpp
@@ -0,0 +1,137 @@
+// license:BSD-3-Clause
+// copyright-holders:F. Ulivi
+/**********************************************************************
+
+ i3001.cpp
+
+ Intel 3001 Microprogram Control Unit
+
+**********************************************************************/
+
+#include "emu.h"
+#include "i3001.h"
+
+// Device type definition
+DEFINE_DEVICE_TYPE(I3001, i3001_device, "i3001", "Intel i3001 MCU")
+
+i3001_device::i3001_device(const machine_config &mconfig , const char *tag , device_t *owner , uint32_t clock)
+ : device_t(mconfig , I3001 , tag , owner , clock)
+ , m_fo_handler(*this)
+ , m_px_handler(*this)
+ , m_sx_handler(*this)
+{
+}
+
+void i3001_device::fc_w(uint8_t fc)
+{
+ m_fc = fc;
+
+ switch (fc & 0b1100) {
+ case 0:
+ // FF0
+ m_fo = false;
+ break;
+
+ case 0b0100:
+ // FFC
+ m_fo = m_carry;
+ break;
+
+ case 0b1000:
+ // FFZ
+ m_fo = m_zero;
+ break;
+
+ case 0b1100:
+ // FF1
+ m_fo = true;
+ break;
+ }
+ m_fo_handler(m_fo);
+}
+
+WRITE_LINE_MEMBER(i3001_device::clk_w)
+{
+ update();
+}
+
+void i3001_device::device_start()
+{
+ m_fo_handler.resolve_safe();
+ m_px_handler.resolve_safe(0);
+ m_sx_handler.resolve_safe(0);
+
+ save_item(NAME(m_addr));
+ save_item(NAME(m_pr));
+ save_item(NAME(m_ac));
+ save_item(NAME(m_fc));
+ save_item(NAME(m_fi));
+ save_item(NAME(m_carry));
+ save_item(NAME(m_zero));
+ save_item(NAME(m_flag));
+}
+
+void i3001_device::update()
+{
+ m_flag = m_fi;
+
+ // Decode FC10
+ switch (m_fc & 0b11) {
+ case 0:
+ // SCZ
+ m_carry = m_zero = m_flag;
+ break;
+
+ case 0b01:
+ // STZ
+ m_zero = m_flag;
+ break;
+
+ case 0b10:
+ // STC
+ m_carry = m_flag;
+ break;
+
+ default:
+ // HCZ
+ break;
+ }
+
+ // Decode AC
+ if ((m_ac & 0b1100000) == 0b0000000) {
+ // JCC
+ m_addr = pack_row_col(m_ac & 0b11111 , get_col(m_addr));
+ } else if ((m_ac & 0b1110000) == 0b0100000) {
+ // JZR
+ m_addr = pack_row_col(0 , m_ac & 0b1111);
+ } else if ((m_ac & 0b1110000) == 0b0110000) {
+ // JCR
+ m_addr = pack_row_col(get_row(m_addr) , m_ac & 0b1111);
+ } else if ((m_ac & 0b1111000) == 0b1110000) {
+ // JCE
+ m_addr = pack_row_col((get_row(m_addr) & 0b11000) | (m_ac & 0b00111) , get_col(m_addr));
+ } else if ((m_ac & 0b1110000) == 0b1000000) {
+ // JFL
+ m_addr = pack_row_col((get_row(m_addr) & 0b10000) | (m_ac & 0b01111) , (get_col(m_addr) & 0b1000) | 0b0010 | m_flag);
+ } else if ((m_ac & 0b1111000) == 0b1010000) {
+ // JCF
+ m_addr = pack_row_col((get_row(m_addr) & 0b11000) | (m_ac & 0b00111) , (get_col(m_addr) & 0b1000) | 0b0010 | m_carry);
+ } else if ((m_ac & 0b1111000) == 0b1011000) {
+ // JZF
+ m_addr = pack_row_col((get_row(m_addr) & 0b11000) | (m_ac & 0b00111) , (get_col(m_addr) & 0b1000) | 0b0010 | m_zero);
+ } else if ((m_ac & 0b1111000) == 0b1100000) {
+ // JPR
+ m_addr = pack_row_col((get_row(m_addr) & 0b11000) | (m_ac & 0b00111) , m_pr);
+ } else if ((m_ac & 0b1111000) == 0b1101000) {
+ // JLL
+ m_addr = pack_row_col((get_row(m_addr) & 0b11000) | (m_ac & 0b00111) , 0b0100 | (m_pr >> 2));
+ } else if ((m_ac & 0b1111100) == 0b1111100) {
+ // JRL
+ m_addr = pack_row_col((get_row(m_addr) & 0b11000) | 0b00100 | (m_ac & 0b00011) , 0b1100 | (m_pr & 0b0011));
+ } else {
+ // JPX
+ uint8_t px = m_px_handler() & 0b1111;
+ m_pr = m_sx_handler() & 0b1111;
+ m_addr = pack_row_col((get_row(m_addr) & 0b11100) | (m_ac & 0b00011) , px);
+ }
+}
diff --git a/src/devices/machine/i3001.h b/src/devices/machine/i3001.h
new file mode 100644
index 00000000000..11231456f5b
--- /dev/null
+++ b/src/devices/machine/i3001.h
@@ -0,0 +1,106 @@
+// license:BSD-3-Clause
+// copyright-holders:F. Ulivi
+/**********************************************************************
+
+ i3001.h
+
+ Intel 3001 Microprogram Control Unit
+
+**********************************************************************
+ _____ _____
+ PX4/ 1|* \_/ |40 VCC
+ PX7/ 2| |39 AC0
+ PX6/ 3| |38 AC1
+ PX5/ 4| |37 AC5
+ SX3/ 5| |36 LD
+ SX2/ 6| |35 ERA
+ PR2 7| |34 MA8
+ SX1/ 8| |33 MA7
+ PR1 9| DIP40 |32 MA6
+ SX0/ 10| i3001 |31 MA5
+ PR0 11| |30 MA4
+ FC3 12| |29 MA0
+ FC2 13| |28 MA3
+ FO/ 14| |27 MA2
+ FC0 15| |26 MA1
+ FC1 16| |25 EN
+ FI/ 17| |24 AC6
+ ISE 18| |23 AC4
+ CLK 19| |22 AC3
+ GND 20|_____________|21 AC2
+
+**********************************************************************/
+
+#ifndef MAME_MACHINE_I3001_H
+#define MAME_MACHINE_I3001_H
+
+#pragma once
+
+class i3001_device : public device_t
+{
+public:
+ i3001_device(const machine_config &mconfig , const char *tag , device_t *owner , uint32_t clock = 0);
+
+ // Mask of valid bits in address
+ static constexpr uint16_t ADDR_MASK = ((1U << 9) - 1);
+
+ // Read current microprogram address (9 bits)
+ uint16_t addr_r() const { return m_addr; }
+
+ // Set Address Control code (7 bits)
+ void ac_w(uint16_t ac) { m_ac = ac; }
+
+ // Set Flag Control code (4 bits)
+ void fc_w(uint8_t fc);
+
+ // Write Flag Input
+ DECLARE_WRITE_LINE_MEMBER(fi_w) { m_fi = state; }
+
+ // Read Flag Output
+ DECLARE_READ_LINE_MEMBER(fo_r) { return m_fo; }
+
+ // Read carry/zero flags
+ DECLARE_READ_LINE_MEMBER(carry_r) { return m_carry; }
+ DECLARE_READ_LINE_MEMBER(zero_r) { return m_zero; }
+
+ // Output callbacks
+ auto fo_w() { return m_fo_handler.bind(); }
+
+ // Input callbacks
+ auto px_r() { return m_px_handler.bind(); }
+ auto sx_r() { return m_sx_handler.bind(); }
+
+ // Load address (in real hw address is loaded through PX/SX buses)
+ void addr_w(uint16_t addr) { m_addr = addr & ADDR_MASK; }
+
+ // Clock pulse
+ DECLARE_WRITE_LINE_MEMBER(clk_w);
+
+protected:
+ virtual void device_start() override;
+
+private:
+ devcb_write_line m_fo_handler;
+ devcb_read8 m_px_handler;
+ devcb_read8 m_sx_handler;
+
+ uint16_t m_addr;
+ uint8_t m_pr;
+ uint8_t m_ac;
+ uint8_t m_fc;
+ bool m_fi;
+ bool m_fo;
+ bool m_carry;
+ bool m_zero;
+ bool m_flag;
+
+ void update();
+ static uint8_t get_row(uint16_t addr) { return (addr >> 4); }
+ static uint8_t get_col(uint16_t addr) { return addr & 0b1111; }
+ static uint16_t pack_row_col(uint8_t row , uint8_t col) { return ((uint16_t)row << 4) | col; }
+};
+
+// device type definition
+DECLARE_DEVICE_TYPE(I3001, i3001_device)
+
+#endif /* MAME_MACHINE_I3001_H */
diff --git a/src/devices/machine/i3002.cpp b/src/devices/machine/i3002.cpp
new file mode 100644
index 00000000000..0abfff3eb45
--- /dev/null
+++ b/src/devices/machine/i3002.cpp
@@ -0,0 +1,428 @@
+// license:BSD-3-Clause
+// copyright-holders:F. Ulivi
+/**********************************************************************
+
+ i3002.cpp
+
+ Intel 3002 Central Processing Element
+
+**********************************************************************/
+
+#include "emu.h"
+#include "i3002.h"
+
+// Device type definition
+DEFINE_DEVICE_TYPE(I3002, i3002_device, "i3002", "Intel i3002 CPE")
+
+// Bit manipulation
+namespace {
+ template<typename T> constexpr T BIT_MASK(unsigned n)
+ {
+ return (T)1U << n;
+ }
+
+ template<typename T> void BIT_CLR(T& w , unsigned n)
+ {
+ w &= ~BIT_MASK<T>(n);
+ }
+
+ template<typename T> void BIT_SET(T& w , unsigned n)
+ {
+ w |= BIT_MASK<T>(n);
+ }
+}
+
+// Table to decode bits 0..3 of FC into RG and register index
+struct fc_fields {
+ uint8_t rg; // Register group [0..2] ("I" , "II" , "III" in Intel docs)
+ unsigned reg; // Register index
+};
+
+constexpr std::array<struct fc_fields , 16> fc_field_tab =
+ {{
+ { 0 , i3002_device::REG_R0 }, // 0000
+ { 0 , i3002_device::REG_R1 }, // 0001
+ { 0 , i3002_device::REG_R2 }, // 0010
+ { 0 , i3002_device::REG_R3 }, // 0011
+ { 0 , i3002_device::REG_R4 }, // 0100
+ { 0 , i3002_device::REG_R5 }, // 0101
+ { 0 , i3002_device::REG_R6 }, // 0110
+ { 0 , i3002_device::REG_R7 }, // 0111
+ { 0 , i3002_device::REG_R8 }, // 1000
+ { 0 , i3002_device::REG_R9 }, // 1001
+ { 1 , i3002_device::REG_T }, // 1010
+ { 1 , i3002_device::REG_AC }, // 1011
+ { 0 , i3002_device::REG_T }, // 1100
+ { 0 , i3002_device::REG_AC }, // 1101
+ { 2 , i3002_device::REG_T }, // 1110
+ { 2 , i3002_device::REG_AC } // 1111
+ }};
+
+// Register names
+static const std::array<const char* , i3002_device::REG_COUNT> reg_names =
+ {{
+ "R0",
+ "R1",
+ "R2",
+ "R3",
+ "R4",
+ "R5",
+ "R6",
+ "R7",
+ "R8",
+ "R9",
+ "T",
+ "AC",
+ "MAR"
+ }};
+
+i3002_device::i3002_device(const machine_config &mconfig , const char *tag , device_t *owner , uint32_t clock)
+ : device_t(mconfig , I3002 , tag , owner , clock)
+ , m_co_handler(*this)
+ , m_ro_handler(*this)
+ , m_ibus_handler(*this)
+ , m_mbus_handler(*this)
+{
+}
+
+const char *i3002_device::reg_name(unsigned reg_idx)
+{
+ return reg_names[ reg_idx ];
+}
+
+void i3002_device::decode_fc(uint8_t fc , uint8_t& fg , uint8_t& rg , unsigned& reg)
+{
+ // Function group
+ fg = (fc >> 4) & 7;
+
+ uint8_t lsn = fc & 0xf;
+
+ // Register & register group
+ rg = fc_field_tab[ lsn ].rg;
+ reg = fc_field_tab[ lsn ].reg;
+}
+
+void i3002_device::fc_kbus_w(uint8_t fc , uint8_t k)
+{
+ m_fc = fc & 0x7f;
+ m_kbus = k & WORD_MASK;
+}
+
+WRITE_LINE_MEMBER(i3002_device::clk_w)
+{
+ if (state) {
+ update();
+ }
+}
+
+bool i3002_device::update_ro()
+{
+ // Update RO output if doing a right-shift only
+ uint8_t fg;
+ uint8_t rg;
+ unsigned reg;
+
+ decode_fc(m_fc , fg , rg , reg);
+
+ if (fg == 0 && rg == 2) {
+ uint8_t ik = m_ibus_handler() & m_kbus;
+ uint8_t at = m_reg[ reg ];
+ set_ro(BIT(at , 0) && !BIT(ik , 0));
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void i3002_device::device_start()
+{
+ m_co_handler.resolve_safe();
+ m_ro_handler.resolve_safe();
+ m_ibus_handler.resolve_safe(0);
+ m_mbus_handler.resolve_safe(0);
+
+ save_item(NAME(m_reg));
+ save_item(NAME(m_fc));
+ save_item(NAME(m_kbus));
+ save_item(NAME(m_ci));
+ save_item(NAME(m_li));
+ save_item(NAME(m_co));
+ save_item(NAME(m_ro));
+}
+
+void i3002_device::update()
+{
+ uint8_t fg;
+ uint8_t rg;
+ unsigned reg;
+
+ decode_fc(m_fc , fg , rg , reg);
+
+ switch (fg) {
+ case 0:
+ switch (rg) {
+ case 0:
+ // FC 0 RG I
+ {
+ uint8_t tmp = (m_reg[ REG_AC ] & m_kbus) + m_reg[ reg ] + m_ci;
+ m_reg[ REG_AC ] = m_reg[ reg ] = tmp & WORD_MASK;
+ set_co(BIT(tmp , SLICE_SIZE));
+ }
+ break;
+
+ case 1:
+ // FC 0 RG II
+ {
+ uint8_t m = m_mbus_handler() & WORD_MASK;
+ uint8_t tmp = (m_reg[ REG_AC ] & m_kbus) + m + m_ci;
+ m_reg[ reg ] = tmp & WORD_MASK;
+ set_co(BIT(tmp , SLICE_SIZE));
+ }
+ break;
+
+ case 2:
+ // FC 0 RG III
+ {
+ // Who designed this mess???
+ uint8_t ik = m_ibus_handler() & WORD_MASK & m_kbus;
+ uint8_t at = m_reg[ reg ];
+ m_reg[ reg ] = 0;
+ set_ro(BIT(at , 0) && !BIT(ik , 0));
+ if (m_li || (BIT(ik , 1) && BIT(at , 1))) {
+ BIT_SET(m_reg[ reg ] , 1);
+ }
+ if ((BIT(at , 0) && BIT(ik , 0)) || (BIT(at , 1) || BIT(ik , 1))) {
+ BIT_SET(m_reg[ reg ] , 0);
+ }
+ }
+ break;
+ }
+ break;
+
+ case 1:
+ switch (rg) {
+ case 0:
+ // FC 1 RG I
+ {
+ m_reg[ REG_MAR ] = m_kbus | m_reg[ reg ];
+ uint8_t tmp = m_reg[ reg ] + m_kbus + m_ci;
+ m_reg[ reg ] = tmp & WORD_MASK;
+ set_co(BIT(tmp , SLICE_SIZE));
+ }
+ break;
+
+ case 1:
+ // FC 1 RG II
+ {
+ uint8_t m = m_mbus_handler() & WORD_MASK;
+ m_reg[ REG_MAR ] = m_kbus | m;
+ uint8_t tmp = m + m_kbus + m_ci;
+ m_reg[ reg ] = tmp & WORD_MASK;
+ set_co(BIT(tmp , SLICE_SIZE));
+ }
+ break;
+
+ case 2:
+ // FC 1 RG III
+ {
+ uint8_t tmp = ((m_reg[ reg ] ^ WORD_MASK) | m_kbus) + (m_reg[ reg ] & m_kbus) + m_ci;
+ m_reg[ reg ] = tmp & WORD_MASK;
+ set_co(BIT(tmp , SLICE_SIZE));
+ }
+ break;
+ }
+ break;
+
+ case 2:
+ switch (rg) {
+ case 0:
+ case 1:
+ // FC 2 RG I
+ // FC 2 RG II
+ {
+ uint8_t tmp = (m_reg[ REG_AC ] & m_kbus) + WORD_MASK + m_ci;
+ m_reg[ reg ] = tmp & WORD_MASK;
+ set_co(BIT(tmp , SLICE_SIZE));
+ }
+ break;
+
+ case 2:
+ // FC 2 RG III
+ {
+ uint8_t i = m_ibus_handler() & WORD_MASK;
+ uint8_t tmp = (i & m_kbus) + WORD_MASK + m_ci;
+ m_reg[ reg ] = tmp & WORD_MASK;
+ set_co(BIT(tmp , SLICE_SIZE));
+ }
+ break;
+ }
+ break;
+
+ case 3:
+ switch (rg) {
+ case 0:
+ // FC 3 RG I
+ {
+ uint8_t tmp = (m_reg[ REG_AC ] & m_kbus) + m_reg[ reg ] + m_ci;
+ m_reg[ reg ] = tmp & WORD_MASK;
+ set_co(BIT(tmp , SLICE_SIZE));
+ }
+ break;
+
+ case 1:
+ // FC 3 RG II (== FC 0 RG II)
+ {
+ uint8_t m = m_mbus_handler() & WORD_MASK;
+ uint8_t tmp = (m_reg[ REG_AC ] & m_kbus) + m + m_ci;
+ m_reg[ reg ] = tmp & WORD_MASK;
+ set_co(BIT(tmp , SLICE_SIZE));
+ }
+ break;
+
+ case 2:
+ // FC 3 RG III
+ {
+ uint8_t i = m_ibus_handler() & WORD_MASK;
+ uint8_t tmp = (i & m_kbus) + m_reg[ reg ] + m_ci;
+ m_reg[ reg ] = tmp & WORD_MASK;
+ set_co(BIT(tmp , SLICE_SIZE));
+ }
+ break;
+ }
+ break;
+
+ case 4:
+ switch (rg) {
+ case 0:
+ // FC 4 RG I
+ {
+ uint8_t tmp = m_reg[ reg ] & m_reg[ REG_AC ] & m_kbus;
+ m_reg[ reg ] = tmp;
+ set_co(m_ci || tmp != 0);
+ }
+ break;
+
+ case 1:
+ // FC 4 RG II
+ {
+ uint8_t m = m_mbus_handler() & WORD_MASK;
+ uint8_t tmp = m & m_reg[ REG_AC ] & m_kbus;
+ m_reg[ reg ] = tmp;
+ set_co(m_ci || tmp != 0);
+ }
+ break;
+
+ case 2:
+ // FC 4 RG III
+ {
+ uint8_t i = m_ibus_handler() & WORD_MASK;
+ uint8_t tmp = i & m_reg[ reg ] & m_kbus;
+ m_reg[ reg ] = tmp;
+ set_co(m_ci || tmp != 0);
+ }
+ break;
+ }
+ break;
+
+ case 5:
+ switch (rg) {
+ case 0:
+ case 2:
+ // FC 5 RG I
+ // FC 5 RG III
+ {
+ uint8_t tmp = m_reg[ reg ] & m_kbus;
+ m_reg[ reg ] = tmp;
+ set_co(m_ci || tmp != 0);
+ }
+ break;
+
+ case 1:
+ // FC 5 RG II
+ {
+ uint8_t m = m_mbus_handler() & WORD_MASK;
+ uint8_t tmp = m & m_kbus;
+ m_reg[ reg ] = tmp;
+ set_co(m_ci || tmp != 0);
+ }
+ break;
+ }
+ break;
+
+ case 6:
+ switch (rg) {
+ case 0:
+ // FC 6 RG I
+ {
+ uint8_t tmp = m_reg[ REG_AC ] & m_kbus;
+ m_reg[ reg ] |= tmp;
+ set_co(m_ci || tmp != 0);
+ }
+ break;
+
+ case 1:
+ // FC 6 RG II
+ {
+ uint8_t m = m_mbus_handler() & WORD_MASK;
+ uint8_t tmp = m_reg[ REG_AC ] & m_kbus;
+ m_reg[ reg ] = m | tmp;
+ set_co(m_ci || tmp != 0);
+ }
+ break;
+
+ case 2:
+ // FC 6 RG III
+ {
+ uint8_t ik = m_ibus_handler() & WORD_MASK & m_kbus;
+ m_reg[ reg ] |= ik;
+ set_co(m_ci || ik != 0);
+ }
+ break;
+ }
+ break;
+
+ case 7:
+ switch (rg) {
+ case 0:
+ // FC 7 RG I
+ {
+ uint8_t tmp = m_reg[ REG_AC ] & m_kbus;
+ set_co(m_ci || (m_reg[ reg ] & tmp) != 0);
+ m_reg[ reg ] = m_reg[ reg ] ^ tmp ^ WORD_MASK;
+ }
+ break;
+
+ case 1:
+ // FC 7 RG II
+ {
+ uint8_t m = m_mbus_handler() & WORD_MASK;
+ uint8_t tmp = m_reg[ REG_AC ] & m_kbus;
+ set_co(m_ci || (m & tmp) != 0);
+ m_reg[ reg ] = m ^ tmp ^ WORD_MASK;
+ }
+ break;
+
+ case 2:
+ // FC 7 RG III
+ {
+ uint8_t ik = m_ibus_handler() & WORD_MASK & m_kbus;
+ set_co(m_ci || (m_reg[ reg ] & ik) != 0);
+ m_reg[ reg ] = m_reg[ reg ] ^ ik ^ WORD_MASK;
+ }
+ break;
+ }
+ break;
+ }
+}
+
+void i3002_device::set_co(bool co)
+{
+ m_co = co;
+ m_co_handler(m_co);
+}
+
+void i3002_device::set_ro(bool ro)
+{
+ m_ro = ro;
+ m_ro_handler(m_ro);
+}
diff --git a/src/devices/machine/i3002.h b/src/devices/machine/i3002.h
new file mode 100644
index 00000000000..70932afe7a2
--- /dev/null
+++ b/src/devices/machine/i3002.h
@@ -0,0 +1,130 @@
+// license:BSD-3-Clause
+// copyright-holders:F. Ulivi
+/**********************************************************************
+
+ i3002.h
+
+ Intel 3002 Central Processing Element
+
+**********************************************************************
+ _____ _____
+ I0/ 1|* \_/ |28 VCC
+ I1/ 2| |27 F2
+ K0/ 3| |26 F1
+ K1/ 4| |25 F0
+ X 5| |24 F3
+ Y 6| |23 ED/
+ CO/ 7| |22 M0/
+ RO/ 8| |21 M1/
+ LI/ 9| DIP28 |20 D1/
+ CI/ 10| i3002 |19 D0/
+ EA/ 11| |18 CLK
+ A1/ 12| |17 F4
+ A0/ 13| |16 F5
+ GND 14|_____________|15 F6
+
+**********************************************************************/
+
+#ifndef MAME_MACHINE_I3002_H
+#define MAME_MACHINE_I3002_H
+
+#pragma once
+
+class i3002_device : public device_t
+{
+public:
+ i3002_device(const machine_config &mconfig , const char *tag , device_t *owner , uint32_t clock = 0);
+
+ // Width of bit-slice
+ static constexpr unsigned SLICE_SIZE = 2;
+
+ // Mask of significant bits
+ static constexpr uint8_t WORD_MASK = ((1U << SLICE_SIZE) - 1);
+
+ // Registers
+ enum : unsigned {
+ REG_R0,
+ REG_R1,
+ REG_R2,
+ REG_R3,
+ REG_R4,
+ REG_R5,
+ REG_R6,
+ REG_R7,
+ REG_R8,
+ REG_R9,
+ REG_T,
+ REG_AC,
+ REG_MAR,
+ REG_COUNT
+ };
+
+ static const char *reg_name(unsigned reg_idx);
+
+ // Decode function code into function group, register group and register index
+ static void decode_fc(uint8_t fc , uint8_t& fg , uint8_t& rg , unsigned& reg);
+
+ // Set function code (7 bits) and K-bus (2 bits)
+ void fc_kbus_w(uint8_t fc , uint8_t k);
+
+ // Write Carry Input
+ DECLARE_WRITE_LINE_MEMBER(ci_w) { m_ci = state != 0; }
+
+ // Write Left Input
+ DECLARE_WRITE_LINE_MEMBER(li_w) { m_li = state != 0; }
+
+ // Read Carry Output
+ DECLARE_READ_LINE_MEMBER(co_r) const { return m_co; }
+
+ // Read Right Output
+ DECLARE_READ_LINE_MEMBER(ro_r) const { return m_ro; }
+
+ // Output callbacks
+ auto co_w() { return m_co_handler.bind(); }
+ auto ro_w() { return m_ro_handler.bind(); }
+
+ // Input bus callbacks
+ auto ibus_r() { return m_ibus_handler.bind(); }
+ auto mbus_r() { return m_mbus_handler.bind(); }
+
+ // Read output buses
+ uint8_t abus_r() const { return m_reg[ REG_MAR ]; }
+ uint8_t dbus_r() const { return m_reg[ REG_AC ]; }
+
+ // Clock pulse
+ DECLARE_WRITE_LINE_MEMBER(clk_w);
+
+ // Compute RO if FC is the code of right-shift op (and return true)
+ // Return false in all other cases
+ bool update_ro();
+
+ // Access to registers (debugging only: registers are not directly accessible in the real hw)
+ const uint8_t& get_reg(unsigned idx) const { return m_reg[ idx ]; }
+ uint8_t& get_reg(unsigned idx) { return m_reg[ idx ]; }
+
+protected:
+ virtual void device_start() override;
+
+private:
+ devcb_write_line m_co_handler;
+ devcb_write_line m_ro_handler;
+ devcb_read8 m_ibus_handler;
+ devcb_read8 m_mbus_handler;
+
+ uint8_t m_reg[ REG_COUNT ];
+ uint8_t m_fc;
+ uint8_t m_kbus;
+ bool m_ci;
+ bool m_li;
+ bool m_co;
+ bool m_ro;
+
+ void update();
+ void set_co(bool co);
+ void set_ro(bool ro);
+};
+
+// device type definition
+DECLARE_DEVICE_TYPE(I3002, i3002_device)
+
+#endif /* MAME_MACHINE_I3002_H */
diff --git a/src/lib/formats/img_dsk.cpp b/src/lib/formats/img_dsk.cpp
new file mode 100644
index 00000000000..4145d74a665
--- /dev/null
+++ b/src/lib/formats/img_dsk.cpp
@@ -0,0 +1,214 @@
+// license:BSD-3-Clause
+// copyright-holders: F. Ulivi
+/*********************************************************************
+
+ img_dsk.cpp
+
+ "IMG" disk format
+
+ This format is just a raw image of every sector on SSDD 8" floppy
+ disks as used in Intel MDS-II systems.
+ Files with this format have no header/trailer and are exactly
+ 512512 bytes in size (52 sectors, 1 head, 77 tracks,
+ 128 bytes per sector).
+
+*********************************************************************/
+
+#include "emu.h"
+#include "img_dsk.h"
+
+// Debugging
+#define VERBOSE 0
+#define LOG(...) do { if (VERBOSE) printf(__VA_ARGS__); } while (false)
+
+constexpr unsigned CELL_SIZE = 1200; // Bit cell size (1 µs)
+constexpr uint8_t INDEX_AM = 0x0c; // Index address mark
+constexpr uint8_t ID_AM = 0x0e; // ID address mark
+constexpr uint8_t DATA_AM = 0x0b; // Data address mark
+constexpr uint8_t AM_CLOCK = 0x70; // Clock pattern of AM
+constexpr unsigned PREIDX_GAP = 45; // Size of pre-index gap
+//constexpr unsigned GAP1_SIZE = 28; // Size of gap 1
+//constexpr unsigned GAP2_SIZE = 28; // Size of gap 2
+//constexpr int ID_DATA_OFFSET = 35 * 16; // Nominal distance (in cells) between ID & DATA AM
+// Size of image file
+constexpr unsigned IMG_IMAGE_SIZE = IMG_TRACKS * IMG_HEADS * IMG_SECTORS * IMG_SECTOR_SIZE;
+constexpr uint16_t CRC_POLY = 0x1021; // CRC-CCITT
+
+img_format::img_format()
+{
+}
+
+int img_format::identify(io_generic *io, uint32_t form_factor)
+{
+ uint64_t size = io_generic_size(io);
+
+ if (((form_factor == floppy_image::FF_8) || (form_factor == floppy_image::FF_UNKNOWN)) &&
+ size == IMG_IMAGE_SIZE) {
+ return 50;
+ } else {
+ return 0;
+ }
+}
+
+bool img_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+{
+ uint64_t size = io_generic_size(io);
+ if (size != IMG_IMAGE_SIZE) {
+ return false;
+ }
+ image->set_variant(floppy_image::SSDD);
+
+ // Suck in the whole image
+ std::vector<uint8_t> image_data(size);
+ io_generic_read(io, (void *)image_data.data(), 0, size);
+
+ for (unsigned cyl = 0; cyl < IMG_TRACKS; cyl++) {
+ std::vector<uint32_t> track_data;
+
+ write_gap(track_data, 0 , PREIDX_GAP);
+ write_mmfm_byte(track_data , INDEX_AM , AM_CLOCK);
+
+ for (unsigned sector = 0; sector < IMG_SECTORS; sector++) {
+ unsigned offset_in_image = (sector + cyl * IMG_SECTORS) * IMG_SECTOR_SIZE;
+ write_sector(track_data , cyl , sector + 1 , &image_data[ offset_in_image ]);
+ }
+ fill_with_gap4(track_data);
+ generate_track_from_levels(cyl , 0 , track_data , 0 , image);
+ }
+ return true;
+}
+
+bool img_format::save(io_generic *io, floppy_image *image)
+{
+ // TODO:
+ return false;
+}
+
+const char *img_format::name() const
+{
+ return "img";
+}
+
+const char *img_format::description() const
+{
+ return "MDS-II floppy disk image";
+}
+
+const char *img_format::extensions() const
+{
+ return "img";
+}
+
+bool img_format::supports_save() const
+{
+ // TODO:
+ return false;
+}
+
+void img_format::write_mmfm_bit(std::vector<uint32_t> &buffer , bool data_bit , bool clock_bit)
+{
+ bool had_transition = buffer.size() < 2 ? false : bit_r(buffer, buffer.size() - 1) || bit_r(buffer , buffer.size() - 2);
+ clock_bit = !data_bit && (clock_bit || !had_transition);
+ bit_w(buffer , clock_bit , CELL_SIZE);
+ bit_w(buffer , data_bit , CELL_SIZE);
+
+ if (BIT(m_crc , 15) ^ data_bit) {
+ m_crc = (m_crc << 1) ^ CRC_POLY;
+ } else {
+ m_crc <<= 1;
+ }
+}
+
+void img_format::write_mmfm_byte(std::vector<uint32_t> &buffer , uint8_t data , uint8_t clock)
+{
+ for (int i = 7; i >= 0; i--) {
+ write_mmfm_bit(buffer , BIT(data , i) , BIT(clock , i));
+ }
+}
+
+void img_format::write_sync(std::vector<uint32_t> &buffer)
+{
+ write_gap(buffer , 18 , 10);
+}
+
+void img_format::write_crc(std::vector<uint32_t> &buffer , uint16_t crc)
+{
+ // Note that CRC is stored with MSB (x^15) first
+ for (unsigned i = 0; i < 16; i++) {
+ write_mmfm_bit(buffer , BIT(crc , 15 - i) , 0);
+ }
+}
+
+void img_format::write_gap(std::vector<uint32_t> &buffer , unsigned size_00 , unsigned size_ff)
+{
+ for (unsigned i = 0; i < size_00; ++i) {
+ write_mmfm_byte(buffer, 0);
+ }
+ for (unsigned i = 0; i < size_ff; ++i) {
+ write_mmfm_byte(buffer, 0xff);
+ }
+}
+
+void img_format::write_sector(std::vector<uint32_t> &buffer , uint8_t track_no , uint8_t sect_no , const uint8_t *sect_data)
+{
+ // **** On-disk format of a sector ****
+ //
+ // | Offset | Size | Value | Content |
+ // |--------+------+-------+----------------------|
+ // | 0 | 18 | 00 | Gap 1/3 |
+ // | 18 | 10 | ff | Gap 1/3 |
+ // | 28 | 1 | 0e | ID AM (clock = 70) |
+ // | 29 | 1 | xx | Track no. |
+ // | 30 | 1 | 00 | N/U |
+ // | 31 | 1 | xx | Sector no. |
+ // | 32 | 1 | 00 | N/U |
+ // | 33 | 2 | xx | ID CRC |
+ // | 35 | 18 | 00 | Gap 2 |
+ // | 53 | 10 | ff | Gap 2 |
+ // | 63 | 1 | 0b | Data AM (clock = 70) |
+ // | 64 | 128 | xx | Sector data |
+ // | 192 | 2 | xx | Data CRC |
+ // | 194 | | | |
+
+ // Gap1
+ write_sync(buffer);
+ // ID AM
+ m_crc = 0;
+ write_mmfm_byte(buffer , ID_AM , AM_CLOCK);
+ // Track #
+ write_mmfm_byte(buffer , track_no);
+ write_mmfm_byte(buffer , 0);
+ // Sector #
+ write_mmfm_byte(buffer , sect_no);
+ write_mmfm_byte(buffer , 0);
+ // ID CRC
+ write_crc(buffer , m_crc);
+ // Gap 2
+ write_sync(buffer);
+ // Data AM
+ m_crc = 0;
+ write_mmfm_byte(buffer , DATA_AM , AM_CLOCK);
+ for (unsigned i = 0; i < IMG_SECTOR_SIZE; i++) {
+ // Data
+ write_mmfm_byte(buffer , sect_data[ i ]);
+ }
+ // Data CRC
+ write_crc(buffer , m_crc);
+}
+
+void img_format::fill_with_gap4(std::vector<uint32_t> &buffer)
+{
+ // Cell count in a track (1 µs cells in a 1/6 s track)
+ unsigned cell_count = (500000 * 120) / 360;
+ unsigned cells_in_buffer = buffer.size();
+ // Size of gap 4
+ unsigned gap_4 = (cell_count - cells_in_buffer) / 16;
+ // Gap 4
+ write_gap(buffer , gap_4 , 0);
+ // Last cell to round everything up to 2E+8
+ if (buffer.size() * CELL_SIZE < 200000000) {
+ bit_w(buffer , false , 200000000 - buffer.size() * CELL_SIZE);
+ }
+}
+
+const floppy_format_type FLOPPY_IMG_FORMAT = &floppy_image_format_creator<img_format>;
diff --git a/src/lib/formats/img_dsk.h b/src/lib/formats/img_dsk.h
new file mode 100644
index 00000000000..86f08b6919b
--- /dev/null
+++ b/src/lib/formats/img_dsk.h
@@ -0,0 +1,54 @@
+// license:BSD-3-Clause
+// copyright-holders: F. Ulivi
+/*********************************************************************
+
+ img_dsk.h
+
+ "IMG" disk format for SSDD Intel MDS-II 8" disks
+
+*********************************************************************/
+#ifndef MAME_FORMATS_IMG_DSK_H
+#define MAME_FORMATS_IMG_DSK_H
+
+#pragma once
+
+#include "flopimg.h"
+
+#include <vector>
+
+// Geometry constants
+constexpr unsigned IMG_TRACKS = 77;
+constexpr unsigned IMG_HEADS = 1;
+constexpr unsigned IMG_SECTORS = 52;
+constexpr unsigned IMG_SECTOR_SIZE = 128;
+
+class img_format : public floppy_image_format_t
+{
+public:
+ img_format();
+
+ virtual int identify(io_generic *io, uint32_t form_factor) override;
+ virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
+ virtual bool save(io_generic *io, floppy_image *image) override;
+ virtual const char *name() const override;
+ virtual const char *description() const override;
+ virtual const char *extensions() const override;
+ virtual bool supports_save() const override;
+
+private:
+ uint16_t m_crc;
+
+ void write_mmfm_bit(std::vector<uint32_t> &buffer , bool data_bit , bool clock_bit);
+ void write_mmfm_byte(std::vector<uint32_t> &buffer , uint8_t data , uint8_t clock = 0);
+ void write_sync(std::vector<uint32_t> &buffer);
+ void write_crc(std::vector<uint32_t> &buffer , uint16_t crc);
+ void write_gap(std::vector<uint32_t> &buffer , unsigned size_00 , unsigned size_ff);
+ void write_sector(std::vector<uint32_t> &buffer , uint8_t track_no , uint8_t sect_no , const uint8_t *sect_data);
+ void fill_with_gap4(std::vector<uint32_t> &buffer);
+ std::vector<uint8_t> get_next_id_n_block(const uint8_t *bitstream , int bitstream_size , int& pos , int& start_pos);
+ bool get_next_sector(const uint8_t *bitstream , int bitstream_size , int& pos , unsigned& track , unsigned& head , unsigned& sector , uint8_t *sector_data);
+};
+
+extern const floppy_format_type FLOPPY_IMG_FORMAT;
+
+#endif // MAME_FORMATS_IMG_DSK_H
diff --git a/src/mame/drivers/imds2.cpp b/src/mame/drivers/imds2.cpp
index be7837d6272..1d059f11269 100644
--- a/src/mame/drivers/imds2.cpp
+++ b/src/mame/drivers/imds2.cpp
@@ -87,6 +87,7 @@
#include "machine/pit8253.h"
#include "machine/pic8259.h"
#include "bus/rs232/rs232.h"
+#include "bus/multibus/multibus.h"
// CPU oscillator of IPC board: 8 MHz
#define IPC_XTAL_Y2 8_MHz_XTAL
@@ -124,6 +125,7 @@ private:
required_device<ls259_device> m_ipcctrl;
required_device_array<rs232_port_device, 2> m_serial;
required_device<imds2ioc_device> m_ioc;
+ required_device<multibus_slot_device> m_slot;
std::vector<uint8_t> m_ipc_ram;
@@ -161,6 +163,7 @@ imds2_state::imds2_state(const machine_config &mconfig, device_type type, const
m_ipcctrl(*this, "ipcctrl"),
m_serial(*this, "serial%u", 0U),
m_ioc(*this, "ioc"),
+ m_slot(*this, "slot"),
m_ipc_rom(*this, "ipcrom")
{
}
@@ -218,6 +221,9 @@ void imds2_state::driver_start()
{
// Allocate 64k for IPC RAM
m_ipc_ram.resize(0x10000);
+
+ m_slot->install_io_rw(m_ipccpu->space(AS_IO));
+ m_slot->install_mem_rw(m_ipccpu->space(AS_PROGRAM));
}
bool imds2_state::in_ipc_rom(offs_t offset) const
@@ -300,6 +306,8 @@ void imds2_state::imds2(machine_config &config)
IMDS2IOC(config, m_ioc);
m_ioc->master_intr_cb().set(m_ipclocpic, FUNC(pic8259_device::ir6_w));
m_ioc->parallel_int_cb().set(m_ipclocpic, FUNC(pic8259_device::ir5_w));
+
+ MULTIBUS_SLOT(config , m_slot , 0);
}
ROM_START(imds2)
diff --git a/src/tools/floptool.cpp b/src/tools/floptool.cpp
index 1953e7b0538..591fd5b91ad 100644
--- a/src/tools/floptool.cpp
+++ b/src/tools/floptool.cpp
@@ -50,6 +50,7 @@
#include "formats/applix_dsk.h"
#include "formats/hpi_dsk.h"
+#include "formats/img_dsk.h"
#include "formats/dvk_mx_dsk.h"
#include "formats/aim_dsk.h"
@@ -98,6 +99,7 @@ static floppy_format_type floppy_formats[] = {
FLOPPY_APPLIX_FORMAT,
FLOPPY_HPI_FORMAT,
+ FLOPPY_IMG_FORMAT,
FLOPPY_DVK_MX_FORMAT,
FLOPPY_AIM_FORMAT,