summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2022-12-18 22:21:33 +1100
committer Vas Crabb <vas@vastheman.com>2022-12-18 22:21:33 +1100
commita32b70c03472c0bddbc280ebd69fe9e75593c0e2 (patch)
treee4b3e5bcc4b60ca3ce99e5dd4fda2e3501aa7d29
parent696b16ccb957eae6dfe94a21286fccaaf5877177 (diff)
More Micom Soft controller wrangling:
* bus/msx/ctrl: Added XE-1AP pad with defaults for personal computers. * bus/pce_ctrl: Added XHE-3 PC joystick adapter.
-rw-r--r--scripts/src/bus.lua44
-rw-r--r--scripts/src/machine.lua12
-rw-r--r--src/devices/bus/msx/ctrl/ctrl.cpp2
-rw-r--r--src/devices/bus/msx/ctrl/xe1ap.cpp101
-rw-r--r--src/devices/bus/msx/ctrl/xe1ap.h19
-rw-r--r--src/devices/bus/pce_ctrl/joypad2.cpp93
-rw-r--r--src/devices/bus/pce_ctrl/joypad2.h75
-rw-r--r--src/devices/bus/pce_ctrl/pcectrl.cpp6
-rw-r--r--src/devices/bus/pce_ctrl/xhe3.cpp92
-rw-r--r--src/devices/bus/pce_ctrl/xhe3.h18
-rw-r--r--src/devices/bus/sms_ctrl/xe1ap.cpp307
-rw-r--r--src/devices/machine/micomxe1a.cpp278
-rw-r--r--src/devices/machine/micomxe1a.h49
13 files changed, 733 insertions, 363 deletions
diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua
index d25711e9585..e954b19dc20 100644
--- a/scripts/src/bus.lua
+++ b/scripts/src/bus.lua
@@ -1839,6 +1839,8 @@ if (BUSES["MSX_CTRL"]~=null) then
MAME_DIR .. "src/devices/bus/msx/ctrl/towns6b.h",
MAME_DIR .. "src/devices/bus/msx/ctrl/townspad.cpp",
MAME_DIR .. "src/devices/bus/msx/ctrl/townspad.h",
+ MAME_DIR .. "src/devices/bus/msx/ctrl/xe1ap.cpp",
+ MAME_DIR .. "src/devices/bus/msx/ctrl/xe1ap.h",
}
end
@@ -4055,6 +4057,28 @@ end
---------------------------------------------------
--
+--@src/devices/bus/pce_ctrl/pcectrl.h,BUSES["PCE_CTRL"] = true
+---------------------------------------------------
+
+if (BUSES["PCE_CTRL"]~=null) then
+ files {
+ MAME_DIR .. "src/devices/bus/pce_ctrl/pcectrl.cpp",
+ MAME_DIR .. "src/devices/bus/pce_ctrl/pcectrl.h",
+ MAME_DIR .. "src/devices/bus/pce_ctrl/joypad2.cpp",
+ MAME_DIR .. "src/devices/bus/pce_ctrl/joypad2.h",
+ MAME_DIR .. "src/devices/bus/pce_ctrl/joypad6.cpp",
+ MAME_DIR .. "src/devices/bus/pce_ctrl/joypad6.h",
+ MAME_DIR .. "src/devices/bus/pce_ctrl/multitap.cpp",
+ MAME_DIR .. "src/devices/bus/pce_ctrl/multitap.h",
+ MAME_DIR .. "src/devices/bus/pce_ctrl/pachinko.cpp",
+ MAME_DIR .. "src/devices/bus/pce_ctrl/pachinko.h",
+ MAME_DIR .. "src/devices/bus/pce_ctrl/xhe3.cpp",
+ MAME_DIR .. "src/devices/bus/pce_ctrl/xhe3.h",
+ }
+end
+
+---------------------------------------------------
+--
--@src/devices/bus/scv/slot.h,BUSES["SCV"] = true
---------------------------------------------------
if (BUSES["SCV"]~=null) then
@@ -5080,26 +5104,6 @@ end
---------------------------------------------------
--
---@src/devices/bus/pce_ctrl/pcectrl.h,BUSES["PCE_CTRL"] = true
----------------------------------------------------
-
-if (BUSES["PCE_CTRL"]~=null) then
- files {
- MAME_DIR .. "src/devices/bus/pce_ctrl/pcectrl.cpp",
- MAME_DIR .. "src/devices/bus/pce_ctrl/pcectrl.h",
- MAME_DIR .. "src/devices/bus/pce_ctrl/joypad2.cpp",
- MAME_DIR .. "src/devices/bus/pce_ctrl/joypad2.h",
- MAME_DIR .. "src/devices/bus/pce_ctrl/joypad6.cpp",
- MAME_DIR .. "src/devices/bus/pce_ctrl/joypad6.h",
- MAME_DIR .. "src/devices/bus/pce_ctrl/multitap.cpp",
- MAME_DIR .. "src/devices/bus/pce_ctrl/multitap.h",
- MAME_DIR .. "src/devices/bus/pce_ctrl/pachinko.cpp",
- MAME_DIR .. "src/devices/bus/pce_ctrl/pachinko.h",
- }
-end
-
----------------------------------------------------
---
--@src/devices/bus/pc8801/pc8801_31.h,BUSES["PC8801"] = true
---------------------------------------------------
diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua
index d4a23022e4c..4439cfd339f 100644
--- a/scripts/src/machine.lua
+++ b/scripts/src/machine.lua
@@ -5101,3 +5101,15 @@ if (MACHINES["AM9516"]~=null) then
MAME_DIR .. "src/devices/machine/am9516.h",
}
end
+
+---------------------------------------------------
+--
+--@src/devices/machine/micomxe1a.h,MACHINES["MICOMXE1A"] = true
+---------------------------------------------------
+
+if (MACHINES["MICOMXE1A"]~=null) then
+ files {
+ MAME_DIR .. "src/devices/machine/micomxe1a.cpp",
+ MAME_DIR .. "src/devices/machine/micomxe1a.h",
+ }
+end
diff --git a/src/devices/bus/msx/ctrl/ctrl.cpp b/src/devices/bus/msx/ctrl/ctrl.cpp
index e2981e1adc0..68139c0f625 100644
--- a/src/devices/bus/msx/ctrl/ctrl.cpp
+++ b/src/devices/bus/msx/ctrl/ctrl.cpp
@@ -16,6 +16,7 @@
#include "sgadapt.h"
#include "towns6b.h"
#include "townspad.h"
+#include "xe1ap.h"
DEFINE_DEVICE_TYPE(MSX_GENERAL_PURPOSE_PORT, msx_general_purpose_port_device, "msx_general_purpose_port", "MSX General Purpose port")
@@ -52,4 +53,5 @@ void msx_general_purpose_port_devices(device_slot_interface &device)
device.option_add("sega", MSX_SEGACTRL);
device.option_add("towns6b", MSX_TOWNS6B);
device.option_add("townspad", MSX_TOWNSPAD);
+ device.option_add("xe1ap", MSX_XE1AP);
}
diff --git a/src/devices/bus/msx/ctrl/xe1ap.cpp b/src/devices/bus/msx/ctrl/xe1ap.cpp
new file mode 100644
index 00000000000..8badb0cb997
--- /dev/null
+++ b/src/devices/bus/msx/ctrl/xe1ap.cpp
@@ -0,0 +1,101 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/**********************************************************************
+
+ Dempa Micom Soft Analog/Digital Controller XE-1AP emulation
+
+**********************************************************************/
+
+#include "emu.h"
+#include "xe1ap.h"
+
+#include "machine/micomxe1a.h"
+
+
+namespace {
+
+INPUT_PORTS_START(xe1ap)
+ PORT_START("BUTTONS")
+ PORT_BIT(0x0001, IP_ACTIVE_LOW, IPT_BUTTON4) PORT_NAME("%p D") // left shoulder lower
+ PORT_BIT(0x0002, IP_ACTIVE_LOW, IPT_BUTTON3) PORT_NAME("%p C") // left shoulder upper
+ PORT_BIT(0x0004, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("%p B") // right shoulder lower
+ PORT_BIT(0x0008, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("%p A") // right shoulder upper
+ PORT_BIT(0x0010, IP_ACTIVE_LOW, IPT_SELECT) // left face bottom
+ PORT_BIT(0x0020, IP_ACTIVE_LOW, IPT_START) // right face bottom
+ PORT_BIT(0x0040, IP_ACTIVE_LOW, IPT_BUTTON6) PORT_NAME("%p E2") // left face top inner
+ PORT_BIT(0x0080, IP_ACTIVE_LOW, IPT_BUTTON5) PORT_NAME("%p E1") // left face top outer
+ PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_BUTTON8) PORT_NAME("%p B'") // right face top inner
+ PORT_BIT(0x0200, IP_ACTIVE_LOW, IPT_BUTTON7) PORT_NAME("%p A'") // right face top outer
+ PORT_BIT(0xfc00, IP_ACTIVE_LOW, IPT_UNUSED)
+
+ PORT_START("CH0")
+ PORT_BIT(0xff, 0x80, IPT_AD_STICK_Y) PORT_SENSITIVITY(50) PORT_KEYDELTA(50)
+
+ PORT_START("CH1")
+ PORT_BIT(0xff, 0x80, IPT_AD_STICK_X) PORT_SENSITIVITY(50) PORT_KEYDELTA(50)
+
+ PORT_START("CH2")
+ PORT_BIT(0xff, 0x80, IPT_PADDLE_V) PORT_REVERSE PORT_SENSITIVITY(50) PORT_KEYDELTA(50)
+
+ PORT_START("CH3")
+ PORT_BIT(0xff, 0x00, IPT_UNUSED)
+
+ PORT_START("MODE")
+ PORT_CONFNAME(0x01, 0x01, "Mode") PORT_WRITE_LINE_DEVICE_MEMBER("xe1", micom_xe_1a_device, mode_w)
+ PORT_CONFSETTING( 0x00, "Digital")
+ PORT_CONFSETTING( 0x01, "Analog")
+ PORT_CONFNAME(0x02, 0x00, "Interface") PORT_WRITE_LINE_DEVICE_MEMBER("xe1", micom_xe_1a_device, interface_w)
+ PORT_CONFSETTING( 0x00, "Personal Computer")
+ PORT_CONFSETTING( 0x02, "MD")
+INPUT_PORTS_END
+
+
+class xe1ap_device : public device_t, public device_msx_general_purpose_port_interface
+{
+public:
+ xe1ap_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) ATTR_COLD;
+
+ virtual u8 read() override { return m_xe1->out_r() | 0xc0; }
+ virtual void pin_8_w(int state) override { m_xe1->req_w(state); }
+
+protected:
+ virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
+ virtual ioport_constructor device_input_ports() const override ATTR_COLD { return INPUT_PORTS_NAME(xe1ap); }
+ virtual void device_start() override ATTR_COLD { }
+ virtual void device_reset() override ATTR_COLD;
+
+private:
+ u8 read_channel(offs_t offset) { return m_axes[offset]->read(); }
+
+ required_device<micom_xe_1a_device> m_xe1;
+ required_ioport_array<4> m_axes;
+ required_ioport m_mode;
+};
+
+xe1ap_device::xe1ap_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) :
+ device_t(mconfig, MSX_XE1AP, tag, owner, clock),
+ device_msx_general_purpose_port_interface(mconfig, *this),
+ m_xe1(*this, "xe1"),
+ m_axes(*this, "CH%u", 0U),
+ m_mode(*this, "MODE")
+{
+}
+
+void xe1ap_device::device_add_mconfig(machine_config &config)
+{
+ MICOM_XE_1A(config, m_xe1);
+ m_xe1->buttons_handler().set_ioport("BUTTONS");
+ m_xe1->analog_handler().set(FUNC(xe1ap_device::read_channel));
+}
+
+void xe1ap_device::device_reset()
+{
+ auto const mode = m_mode->read();
+ m_xe1->mode_w(BIT(mode, 0));
+ m_xe1->interface_w(BIT(mode, 1));
+}
+
+} // anonymous namespace
+
+
+DEFINE_DEVICE_TYPE_PRIVATE(MSX_XE1AP, device_msx_general_purpose_port_interface, xe1ap_device, "msx_xe1ap", "Dempa Micom Soft Analog Controller (XE-1AP, PC)")
diff --git a/src/devices/bus/msx/ctrl/xe1ap.h b/src/devices/bus/msx/ctrl/xe1ap.h
new file mode 100644
index 00000000000..515394b71fa
--- /dev/null
+++ b/src/devices/bus/msx/ctrl/xe1ap.h
@@ -0,0 +1,19 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/**********************************************************************
+
+ Dempa Micom Soft Analog/Digital Controller XE-1AP emulation
+
+**********************************************************************/
+
+#ifndef MAME_BUS_MSX_CTRL_XE1AP_H
+#define MAME_BUS_MSX_CTRL_XE1AP_H
+
+#pragma once
+
+#include "ctrl.h"
+
+
+DECLARE_DEVICE_TYPE(MSX_XE1AP, device_msx_general_purpose_port_interface)
+
+#endif // MAME_BUS_MSX_CTRL_XE1AP_H
diff --git a/src/devices/bus/pce_ctrl/joypad2.cpp b/src/devices/bus/pce_ctrl/joypad2.cpp
index 2da64ed49ce..7e07f827b56 100644
--- a/src/devices/bus/pce_ctrl/joypad2.cpp
+++ b/src/devices/bus/pce_ctrl/joypad2.cpp
@@ -33,22 +33,17 @@
#include "emu.h"
#include "joypad2.h"
+#include "machine/74157.h"
-//**************************************************************************
-// DEVICE DEFINITIONS
-//**************************************************************************
-
-DEFINE_DEVICE_TYPE(PCE_JOYPAD2, pce_joypad2_device, "pce_joypad2", "NEC PC Engine Pad")
-DEFINE_DEVICE_TYPE(PCE_JOYPAD2_TURBO, pce_joypad2_turbo_device, "pce_joypad2_turbo", "NEC PC Engine/TurboGrafx-16 2 Button Joypad")
-
+namespace {
-static INPUT_PORTS_START( pce_joypad2 )
+INPUT_PORTS_START( pce_joypad2 )
// II is left of I on the original pad so we map them in reverse order
PORT_START("BUTTONS")
PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("%p Button I")
PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("%p Button II")
- PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_SELECT ) PORT_NAME("%p Select")
+ PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_SELECT )
PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START ) PORT_NAME("%p Run")
PORT_START("DIRECTION")
@@ -59,7 +54,7 @@ static INPUT_PORTS_START( pce_joypad2 )
INPUT_PORTS_END
-static INPUT_PORTS_START( pce_joypad2_turbo )
+INPUT_PORTS_START( pce_joypad2_turbo )
PORT_INCLUDE( pce_joypad2 )
PORT_START("TURBO")
@@ -74,6 +69,73 @@ static INPUT_PORTS_START( pce_joypad2_turbo )
INPUT_PORTS_END
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> pce_joypad2_device
+
+class pce_joypad2_device : public device_t, public device_pce_control_port_interface
+{
+public:
+ // construction/destruction
+ pce_joypad2_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+
+protected:
+ // construction/destruction
+ pce_joypad2_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock);
+
+ // optional information overrides
+ virtual ioport_constructor device_input_ports() const override;
+
+ // device-level overrides
+ virtual void device_add_mconfig(machine_config &config) override;
+ virtual void device_start() override;
+
+ // device_pce_control_port_interface overrides
+ virtual u8 peripheral_r() override;
+ virtual void sel_w(int state) override;
+ virtual void clr_w(int state) override;
+
+ // devices
+ required_device<ls157_device> m_muxer;
+};
+
+
+// ======================> pce_joypad2_turbo_device
+
+class pce_joypad2_turbo_device : public pce_joypad2_device
+{
+public:
+ // construction/destruction
+ pce_joypad2_turbo_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+
+protected:
+ // optional information overrides
+ virtual ioport_constructor device_input_ports() const override;
+
+ // device-level overrides
+ virtual void device_add_mconfig(machine_config &config) override;
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ // device_pce_control_port_interface overrides
+ virtual void clr_w(int state) override;
+
+private:
+ u8 buttons_r();
+
+ // IO ports
+ required_ioport m_buttons_io;
+ required_ioport m_turbo_io;
+
+ // internal states
+ u8 m_counter; // Turbo rate counter, connected on 74xx163 QB and QC.
+ bool m_prev_clr; // previous CLR pin state
+};
+
+
+
//-------------------------------------------------
// input_ports - device-specific input ports
//-------------------------------------------------
@@ -242,3 +304,14 @@ u8 pce_joypad2_turbo_device::buttons_r()
}
return ret;
}
+
+} // anonymous namespace
+
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+DEFINE_DEVICE_TYPE_PRIVATE(PCE_JOYPAD2, device_pce_control_port_interface, pce_joypad2_device, "pce_joypad2", "NEC PC Engine Pad")
+DEFINE_DEVICE_TYPE_PRIVATE(PCE_JOYPAD2_TURBO, device_pce_control_port_interface, pce_joypad2_turbo_device, "pce_joypad2_turbo", "NEC PC Engine/TurboGrafx-16 2 Button Joypad")
diff --git a/src/devices/bus/pce_ctrl/joypad2.h b/src/devices/bus/pce_ctrl/joypad2.h
index 13fc0cbe7de..2ad0e2727ec 100644
--- a/src/devices/bus/pce_ctrl/joypad2.h
+++ b/src/devices/bus/pce_ctrl/joypad2.h
@@ -5,87 +5,16 @@
NEC PC Engine/TurboGrafx-16 2 Button Joypad emulation
**********************************************************************/
-
#ifndef MAME_BUS_PCE_CTRL_JOYPAD2_H
#define MAME_BUS_PCE_CTRL_JOYPAD2_H
#pragma once
-
-#include "machine/74157.h"
#include "pcectrl.h"
-
-//**************************************************************************
-// TYPE DEFINITIONS
-//**************************************************************************
-
-// ======================> pce_joypad2_device
-
-class pce_joypad2_device : public device_t,
- public device_pce_control_port_interface
-{
-public:
- // construction/destruction
- pce_joypad2_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
-
-protected:
- // construction/destruction
- pce_joypad2_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock);
-
- // optional information overrides
- virtual ioport_constructor device_input_ports() const override;
-
- // device-level overrides
- virtual void device_add_mconfig(machine_config &config) override;
- virtual void device_start() override;
-
- // device_pce_control_port_interface overrides
- virtual u8 peripheral_r() override;
- virtual void sel_w(int state) override;
- virtual void clr_w(int state) override;
-
- // devices
- required_device<ls157_device> m_muxer;
-};
-
-// ======================> pce_joypad2_turbo_device
-
-class pce_joypad2_turbo_device : public pce_joypad2_device
-{
-public:
- // construction/destruction
- pce_joypad2_turbo_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
-
-protected:
- // optional information overrides
- virtual ioport_constructor device_input_ports() const override;
-
- // device-level overrides
- virtual void device_add_mconfig(machine_config &config) override;
- virtual void device_start() override;
- virtual void device_reset() override;
-
- // device_pce_control_port_interface overrides
- virtual void clr_w(int state) override;
-
-private:
- u8 buttons_r();
-
- // IO ports
- required_ioport m_buttons_io;
- required_ioport m_turbo_io;
-
- // internal states
- u8 m_counter; // Turbo rate counter, connected on 74xx163 QB and QC.
- bool m_prev_clr; // previous CLR pin state
-};
-
-
// device type definition
-DECLARE_DEVICE_TYPE(PCE_JOYPAD2, pce_joypad2_device)
-DECLARE_DEVICE_TYPE(PCE_JOYPAD2_TURBO, pce_joypad2_turbo_device)
-
+DECLARE_DEVICE_TYPE(PCE_JOYPAD2, device_pce_control_port_interface)
+DECLARE_DEVICE_TYPE(PCE_JOYPAD2_TURBO, device_pce_control_port_interface)
#endif // MAME_BUS_PCE_CTRL_JOYPAD2_H
diff --git a/src/devices/bus/pce_ctrl/pcectrl.cpp b/src/devices/bus/pce_ctrl/pcectrl.cpp
index 71e2d9db525..6ed8f3c5acd 100644
--- a/src/devices/bus/pce_ctrl/pcectrl.cpp
+++ b/src/devices/bus/pce_ctrl/pcectrl.cpp
@@ -51,14 +51,16 @@
**********************************************************************/
#include "emu.h"
-#include "screen.h"
#include "pcectrl.h"
+#include "screen.h"
+
// slot devices
#include "joypad2.h"
#include "joypad6.h"
#include "multitap.h"
#include "pachinko.h"
+#include "xhe3.h"
@@ -176,6 +178,8 @@ void pce_control_port_devices(device_slot_interface &device)
device.option_add("multitap", PCE_MULTITAP);
device.option_add("pachinko", PCE_PACHINKO);
+
+ device.option_add("pcjoy", PCE_XHE3);
// 3 Button Joypad/Joysticks (ex: Avenue Pad 3)
// PC Engine Mouse (PI-PD10)
// Memory Base 128 (PI-AD19)
diff --git a/src/devices/bus/pce_ctrl/xhe3.cpp b/src/devices/bus/pce_ctrl/xhe3.cpp
new file mode 100644
index 00000000000..db73882e0fc
--- /dev/null
+++ b/src/devices/bus/pce_ctrl/xhe3.cpp
@@ -0,0 +1,92 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***********************************************************************
+
+ Micom Soft XHE-3 PC Joystick Adapter for PC Engine emuation
+
+***********************************************************************/
+
+#include "emu.h"
+#include "xhe3.h"
+
+#include "bus/msx/ctrl/ctrl.h"
+
+
+namespace {
+
+INPUT_PORTS_START( pce_xhe3 )
+ PORT_START("BUTTONS") // using IPT_START/IPT_SELECT would steal a player index
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Select")
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_OTHER) PORT_NAME("Run")
+INPUT_PORTS_END
+
+
+
+class pce_xhe3_device : public device_t, public device_pce_control_port_interface
+{
+public:
+ pce_xhe3_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
+
+ virtual u8 peripheral_r() override;
+ virtual void sel_w(int state) override;
+ virtual void clr_w(int state) override;
+
+protected:
+ virtual void device_add_mconfig(machine_config &config) override;
+ virtual ioport_constructor device_input_ports() const override { return INPUT_PORTS_NAME(pce_xhe3); }
+ virtual void device_start() override;
+
+private:
+ required_device<msx_general_purpose_port_device> m_port;
+ required_ioport m_buttons;
+
+ u8 m_sel_in;
+};
+
+
+pce_xhe3_device::pce_xhe3_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) :
+ device_t(mconfig, PCE_XHE3, tag, owner, clock),
+ device_pce_control_port_interface(mconfig, *this),
+ m_port(*this, "joy"),
+ m_buttons(*this, "BUTTONS"),
+ m_sel_in(1)
+{
+}
+
+
+u8 pce_xhe3_device::peripheral_r()
+{
+ if (m_sel_in)
+ return bitswap<4>(m_port->read(), 2, 1, 3, 0);
+ else
+ return bitswap<2>(m_port->read(), 5, 4) | (m_buttons->read() << 2);
+}
+
+
+void pce_xhe3_device::sel_w(int state)
+{
+ m_sel_in = state ? 1 : 0;
+}
+
+
+void pce_xhe3_device::clr_w(int state)
+{
+ m_port->pin_8_w(state);
+}
+
+
+void pce_xhe3_device::device_add_mconfig(machine_config &config)
+{
+ MSX_GENERAL_PURPOSE_PORT(config, m_port, msx_general_purpose_port_devices, "xe1ap");
+}
+
+
+void pce_xhe3_device::device_start()
+{
+ save_item(NAME(m_sel_in));
+}
+
+} // anonymous namespace
+
+
+DEFINE_DEVICE_TYPE_PRIVATE(PCE_XHE3, device_pce_control_port_interface, pce_xhe3_device, "pce_xhe3", "Micom Soft XHE-3 PC Joystick Adapter for PC Engine")
diff --git a/src/devices/bus/pce_ctrl/xhe3.h b/src/devices/bus/pce_ctrl/xhe3.h
new file mode 100644
index 00000000000..86323dd08a9
--- /dev/null
+++ b/src/devices/bus/pce_ctrl/xhe3.h
@@ -0,0 +1,18 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***********************************************************************
+
+ Micom Soft XHE-3 PC Joystick Adapter for PC Engine emuation
+
+***********************************************************************/
+#ifndef MAME_BUS_PCE_CTRL_XHE3_H
+#define MAME_BUS_PCE_CTRL_XHE3_H
+
+#pragma once
+
+#include "pcectrl.h"
+
+
+DECLARE_DEVICE_TYPE(PCE_XHE3, device_pce_control_port_interface)
+
+#endif // MAME_BUS_PCE_CTRL_XHE3_H
diff --git a/src/devices/bus/sms_ctrl/xe1ap.cpp b/src/devices/bus/sms_ctrl/xe1ap.cpp
index 88fb3589025..f29d6a53e24 100644
--- a/src/devices/bus/sms_ctrl/xe1ap.cpp
+++ b/src/devices/bus/sms_ctrl/xe1ap.cpp
@@ -4,129 +4,29 @@
Dempa Micom Soft Analog/Digital Controller XE-1AP emulation
- PC pin Name MD pin Name Dir Signal
- 1 Up 1 Up In D0
- 2 Down 2 Down In D1
- 3 Left 3 Left In D2
- 4 Right 4 Right In D3
- 6 TRIG1 6 TL In L/H
- 7 TRIG2 9 TR In Ack
- 8 STROBE 7 TH Out Req
-
- In analog mode, data is shifted out as eleven nybbles:
-
- _ ____________________________________________
- Req \_________/
- ____ __ __ __ __ __ __ __ __
- Ack \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/
- _____ _____ _____ _____
- L/H XX\____/ \_____/ \_____/ \_____/ \_____/
- ____ _____ _____ _____ _____ _____ _____ _____ _____
- D XXX____X_____X_____X_____X_____X_____X_____X_____X_____X
-
- The falling edge on Req causes data output to start. The host
- can't control the speed, it just polls the L/H and Ack lines to
- know when the data is ready to read.
-
- Step D0 D1 D2 D3
- 1 Select Start B' A'
- 2 D C B A
- 2 X4 X5 X6 X7
- 3 Y4 Y5 Y6 Y7
- 5 RZ4 RZ5 RZ6 RZ7
- 6 Z4 Z5 Z6 Z7
- 7 X0 X1 X2 X3
- 8 Y0 Y1 Y2 Y3
- 9 RZ0 RZ1 RZ2 RZ3
- 10 Z0 Z1 Z2 Z3
- 11 E2 E1 - -
-
- In digital mode, Req is a simple multiplexer input:
-
- Req 0 1
- D0 Up Throttle Up
- D1 Down Throttle Down
- D2 Left C
- D3 Right D
- L/H A E1
- Ack B E2
-
- Start appears as simultaneous Left/Right
- Select appears as simultaneous Up/Down
-
- This mode is almost compatible with a 6-button Towns Pad (on a
- real 6-button Towns Pad, buttons A and B can be read in either
- state, they bypass the multiplexer).
-
- TODO:
- * Measure timings.
- * Confirm reported value for unused analog channel 4.
- * Confirm A', B', E1 and E2 bit mappings in analog mode.
- * Confirm buttons mapping in digital mode.
- - Is the mapping different in PC vs MD mode?
- * Implement trigger A/B rapid fire switches.
- * Implement channel shift switch (Y->X, X->Z, Z->X).
- * Implement special modes (holding buttons on power-on):
- - Double displacement modes:
- + X/Y (hold SELECT + A')
- + Z (hold SELECT + B')
- + X/Y/Z (hold SELECT + A' + B')
- - Up/down reverse mode (hold C)
-
**********************************************************************/
#include "emu.h"
#include "xe1ap.h"
-//#define VERBOSE 1
-//#define LOG_OUTPUT_FUNC osd_printf_info
-#include "logmacro.h"
+#include "machine/micomxe1a.h"
namespace {
-class sms_xe1ap_device : public device_t, public device_sms_control_interface
-{
-public:
- sms_xe1ap_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
-
- virtual u8 in_r() override;
- virtual void out_w(u8 data, u8 mem_mask) override;
-
- DECLARE_INPUT_CHANGED_MEMBER(mode_changed);
-
-protected:
- virtual ioport_constructor device_input_ports() const override;
- virtual void device_start() override;
-
-private:
- TIMER_CALLBACK_MEMBER(step_output);
-
- required_ioport m_buttons;
- required_ioport_array<4> m_axes;
- required_ioport m_mode;
-
- emu_timer *m_output_timer;
-
- u8 m_req;
- u8 m_out;
-};
-
-
-
INPUT_PORTS_START( sms_xe1ap )
PORT_START("BUTTONS")
- PORT_BIT(0x0001, IP_ACTIVE_LOW, IPT_SELECT) // left face bottom
- PORT_BIT(0x0002, IP_ACTIVE_LOW, IPT_START) // right face bottom
- PORT_BIT(0x0004, IP_ACTIVE_LOW, IPT_BUTTON6) PORT_NAME("%p B'") // right face top inner
- PORT_BIT(0x0008, IP_ACTIVE_LOW, IPT_BUTTON5) PORT_NAME("%p A'") // right face top outer
- PORT_BIT(0x0010, IP_ACTIVE_LOW, IPT_BUTTON4) PORT_NAME("%p D") // left shoulder lower
- PORT_BIT(0x0020, IP_ACTIVE_LOW, IPT_BUTTON3) PORT_NAME("%p C") // left shoulder upper
- PORT_BIT(0x0040, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("%p B") // right shoulder lower
- PORT_BIT(0x0080, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("%p A") // right shoulder upper
- PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_BUTTON8) PORT_NAME("%p E2") // left face top inner
- PORT_BIT(0x0200, IP_ACTIVE_LOW, IPT_BUTTON7) PORT_NAME("%p E1") // left face top outer
- PORT_BIT(0x0c00, IP_ACTIVE_LOW, IPT_UNUSED)
+ PORT_BIT(0x0001, IP_ACTIVE_LOW, IPT_BUTTON4) PORT_NAME("%p D") // left shoulder lower
+ PORT_BIT(0x0002, IP_ACTIVE_LOW, IPT_BUTTON3) PORT_NAME("%p C") // left shoulder upper
+ PORT_BIT(0x0004, IP_ACTIVE_LOW, IPT_BUTTON2) PORT_NAME("%p B") // right shoulder lower
+ PORT_BIT(0x0008, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("%p A") // right shoulder upper
+ PORT_BIT(0x0010, IP_ACTIVE_LOW, IPT_SELECT) // left face bottom
+ PORT_BIT(0x0020, IP_ACTIVE_LOW, IPT_START) // right face bottom
+ PORT_BIT(0x0040, IP_ACTIVE_LOW, IPT_BUTTON6) PORT_NAME("%p E2") // left face top inner
+ PORT_BIT(0x0080, IP_ACTIVE_LOW, IPT_BUTTON5) PORT_NAME("%p E1") // left face top outer
+ PORT_BIT(0x0100, IP_ACTIVE_LOW, IPT_BUTTON8) PORT_NAME("%p B'") // right face top inner
+ PORT_BIT(0x0200, IP_ACTIVE_LOW, IPT_BUTTON7) PORT_NAME("%p A'") // right face top outer
+ PORT_BIT(0xfc00, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("CH0")
PORT_BIT(0xff, 0x80, IPT_AD_STICK_Y) PORT_SENSITIVITY(50) PORT_KEYDELTA(50)
@@ -138,181 +38,70 @@ INPUT_PORTS_START( sms_xe1ap )
PORT_BIT(0xff, 0x80, IPT_PADDLE_V) PORT_REVERSE PORT_SENSITIVITY(50) PORT_KEYDELTA(50)
PORT_START("CH3")
- PORT_BIT(0xff, 0x80, IPT_UNUSED)
+ PORT_BIT(0xff, 0x00, IPT_UNUSED)
PORT_START("MODE")
- PORT_CONFNAME(0x01, 0x01, "Mode") PORT_CHANGED_MEMBER(DEVICE_SELF, sms_xe1ap_device, mode_changed, 0)
+ PORT_CONFNAME(0x01, 0x01, "Mode") PORT_WRITE_LINE_DEVICE_MEMBER("xe1", micom_xe_1a_device, mode_w)
PORT_CONFSETTING( 0x00, "Digital")
PORT_CONFSETTING( 0x01, "Analog")
+ PORT_CONFNAME(0x02, 0x02, "Interface") PORT_WRITE_LINE_DEVICE_MEMBER("xe1", micom_xe_1a_device, interface_w)
+ PORT_CONFSETTING( 0x00, "Personal Computer")
+ PORT_CONFSETTING( 0x02, "MD")
INPUT_PORTS_END
-sms_xe1ap_device::sms_xe1ap_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) :
- device_t(mconfig, SMS_XE1AP, tag, owner, clock),
- device_sms_control_interface(mconfig, *this),
- m_buttons(*this, "BUTTONS"),
- m_axes(*this, "CH%u", 0U),
- m_mode(*this, "MODE"),
- m_output_timer(nullptr),
- m_req(1),
- m_out(0x3f)
+class sms_xe1ap_device : public device_t, public device_sms_control_interface
{
-}
-
+public:
+ sms_xe1ap_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) ATTR_COLD;
-u8 sms_xe1ap_device::in_r()
-{
- if (BIT(m_mode->read(), 0))
- {
- LOG(
- "%s: analog mode read data = %02X\n",
- machine().describe_context(),
- m_out);
- return m_out;
- }
- else
- {
- u16 const buttons = m_buttons->read();
- if (m_req)
- {
- u8 const x = m_axes[0]->read();
- u8 const y = m_axes[1]->read();
- u8 const result =
- ((BIT(buttons, 0) && (0x40 <= y)) ? 0x01 : 0x00) | // Select/Up
- ((BIT(buttons, 0) && (0xc0 > y)) ? 0x02 : 0x00) | // Select/Down
- ((BIT(buttons, 1) && (0x40 <= x)) ? 0x04 : 0x00) | // Start/Left
- ((BIT(buttons, 1) && (0xc0 > x)) ? 0x08 : 0x00) | // Start/Right
- (BIT(buttons, 7) << 4) | // A
- (BIT(buttons, 6) << 5); // B
- LOG(
- "%s: digital mode basic read = 0x%02X\n",
- machine().describe_context(),
- result);
- return result;
- }
- else
- {
- u8 const z = m_axes[3]->read();
- u8 const result =
- ((0xc0 > z) ? 0x01 : 0x00) | // Throttle Up
- ((0x40 <= z) ? 0x02 : 0x00) | // Throttle Down
- (BIT(buttons, 5) << 2) | // C
- (BIT(buttons, 4) << 3) | // D
- (BIT(buttons, 9) << 4) | // E1
- (BIT(buttons, 8) << 5); // E2
- LOG(
- "%s: digital mode extended read = 0x%02X\n",
- machine().describe_context(),
- result);
- return result;
- }
- }
-}
+ virtual u8 in_r() override { return m_xe1->out_r(); }
+ virtual void out_w(u8 data, u8 mem_mask) override { m_xe1->req_w(BIT(data, 6)); }
+protected:
+ virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
+ virtual ioport_constructor device_input_ports() const override ATTR_COLD { return INPUT_PORTS_NAME(sms_xe1ap); }
+ virtual void device_start() override ATTR_COLD { }
+ virtual void device_reset() override ATTR_COLD;
-void sms_xe1ap_device::out_w(u8 data, u8 mem_mask)
-{
- u8 const req = BIT(data, 6);
- if (req != m_req)
- {
- if (BIT(m_mode->read(), 0))
- {
- LOG("%s: /Req = %u\n", machine().describe_context(), req);
- if (!req)
- m_output_timer->adjust(attotime::from_usec(4), 0);
- }
- else
- {
- LOG("%s: /Req = %u ignored in digital mode\n", machine().describe_context(), req);
- }
- m_req = req;
- }
-}
+private:
+ u8 read_channel(offs_t offset) { return m_axes[offset]->read(); }
+ required_device<micom_xe_1a_device> m_xe1;
+ required_ioport_array<4> m_axes;
+ required_ioport m_mode;
+};
-INPUT_CHANGED_MEMBER(sms_xe1ap_device::mode_changed)
-{
- if (newval)
- {
- LOG("Analog mode selected\n");
- }
- else
- {
- LOG("Digital mode selected\n");
- m_output_timer->enable(false);
- m_out = 0x3f;
- }
-}
-ioport_constructor sms_xe1ap_device::device_input_ports() const
+sms_xe1ap_device::sms_xe1ap_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) :
+ device_t(mconfig, SMS_XE1AP, tag, owner, clock),
+ device_sms_control_interface(mconfig, *this),
+ m_xe1(*this, "xe1"),
+ m_axes(*this, "CH%u", 0U),
+ m_mode(*this, "MODE")
{
- return INPUT_PORTS_NAME(sms_xe1ap);
}
-void sms_xe1ap_device::device_start()
+void sms_xe1ap_device::device_add_mconfig(machine_config &config)
{
- m_output_timer = timer_alloc(FUNC(sms_xe1ap_device::step_output), this);
-
- save_item(NAME(m_req));
- save_item(NAME(m_out));
+ MICOM_XE_1A(config, m_xe1);
+ m_xe1->buttons_handler().set_ioport("BUTTONS");
+ m_xe1->analog_handler().set(FUNC(sms_xe1ap_device::read_channel));
}
-TIMER_CALLBACK_MEMBER(sms_xe1ap_device::step_output)
+void sms_xe1ap_device::device_reset()
{
- if (!BIT(param, 0))
- {
- auto const step = param >> 1;
- if (12 > step)
- {
- switch (step)
- {
- case 0:
- case 1:
- m_out = BIT(m_buttons->read(), step ? 4 : 0, 4);
- break;
- case 2:
- case 3:
- case 4:
- case 5:
- case 6:
- case 7:
- case 8:
- case 9:
- m_out = BIT(m_axes[((step - 2) & 0x03) ^ 0x01]->read(), (step < 6) ? 4 : 0, 4);
- break;
- case 10:
- m_out = BIT(m_buttons->read(), 8, 4);
- break;
- default:
- m_out = 0x0f;
- }
- m_out |= BIT(param, 1) ? 0x30 : 0x20;
- LOG(
- "Set nybble %u data = 0x%X, L/H = %d, /Ack = 1\n",
- step,
- BIT(m_out, 0, 4),
- BIT(m_out, 4));
- m_output_timer->adjust(attotime::from_usec(4), param + 1);
- }
- else
- {
- m_out = 0x3f;
- }
- }
- else
- {
- m_out &= 0x1f;
- LOG("Set nybble %u /Ack = 0\n", param >> 1);
- m_output_timer->adjust(attotime::from_usec(20), param + 1);
- }
+ auto const mode = m_mode->read();
+ m_xe1->mode_w(BIT(mode, 0));
+ m_xe1->interface_w(BIT(mode, 1));
}
} // anonymous namespace
-DEFINE_DEVICE_TYPE_PRIVATE(SMS_XE1AP, device_sms_control_interface, sms_xe1ap_device, "sms_xe1ap", "Dempa Micom Soft Analog Controller (XE-1AP)")
+DEFINE_DEVICE_TYPE_PRIVATE(SMS_XE1AP, device_sms_control_interface, sms_xe1ap_device, "sms_xe1ap", "Dempa Micom Soft Analog Controller (XE-1AP, Sega)")
diff --git a/src/devices/machine/micomxe1a.cpp b/src/devices/machine/micomxe1a.cpp
new file mode 100644
index 00000000000..1b94f9f0418
--- /dev/null
+++ b/src/devices/machine/micomxe1a.cpp
@@ -0,0 +1,278 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/**********************************************************************
+
+ Dempa Micom Soft Analog/Digital Controller emulation
+
+ PC pin Name MD pin Name Dir Signal
+ 1 Up 1 Up In D0
+ 2 Down 2 Down In D1
+ 3 Left 3 Left In D2
+ 4 Right 4 Right In D3
+ 6 TRIG1 6 TL In L/H
+ 7 TRIG2 9 TR In Ack
+ 8 STROBE 7 TH Out Req
+
+ In analog mode, data is shifted out as eleven nybbles:
+
+ _ ____________________________________________
+ Req \_________/
+ ____ __ __ __ __ __ __ __ __
+ Ack \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/
+ _____ _____ _____ _____
+ L/H XX\____/ \_____/ \_____/ \_____/ \_____/
+ ____ _____ _____ _____ _____ _____ _____ _____ _____
+ D XXX____X_____X_____X_____X_____X_____X_____X_____X_____X
+
+ The falling edge on Req causes data output to start. The host
+ can't control the speed, it just polls the L/H and Ack lines to
+ know when the data is ready to read.
+
+ Step D0 D1 D2 D3
+ 1 D C B/B' A/A'
+ 2 Select Start E2 E1
+ 3 Y4 Y5 Y6 Y7
+ 4 X4 X5 X6 X7
+ 5 Z4 Z5 Z6 Z7
+ 6 RZ4 RZ5 RZ6 RZ7
+ 7 Y0 Y1 Y2 Y3
+ 8 X0 X1 X2 X3
+ 9 Z0 Z1 Z2 Z3
+ 10 RZ0 RZ1 RZ2 RZ3
+ 11 B' A' B A
+ 12 - - - -
+
+ In MD mode, each pair of nybbles is transmitted in reverse
+ order.
+
+ In digital mode, Req is a simple multiplexer input:
+
+ Req 0 1
+ D0 Up Throttle Up
+ D1 Down Throttle Down
+ D2 Left C
+ D3 Right D
+ L/H A/A' E1
+ Ack B/B' E2
+
+ Start appears as simultaneous Left/Right
+ Select appears as simultaneous Up/Down
+
+ This mode is almost compatible with a 6-button Towns Pad (on a
+ real 6-button Towns Pad, buttons A and B can be read in either
+ state, they bypass the multiplexer).
+
+ TODO:
+ * Dump MB88513 microcontroller from original controller.
+ * Measure timings.
+ * Latch data at beginning of packet.
+ * Confirm A', B', E1 and E2 bit mappings in analog mode.
+ * Confirm buttons mapping in digital mode.
+ - Is the mapping different in PC vs MD mode?
+ * Implement trigger A/B rapid fire switches.
+ * Implement channel shift switch (Y->X, X->Z, Z->X).
+ * Implement special modes (holding buttons on power-on):
+ - Double displacement modes:
+ + X/Y (hold SELECT + A')
+ + Z (hold SELECT + B')
+ + X/Y/Z (hold SELECT + A' + B')
+ - Up/down reverse mode (hold C)
+ * Implement desktop (XE-1AJ/CZ-8NJ2) version:
+ - Four analog channels
+ - E1/E2 on a rocker switch (can't press simultaneously)
+ - Hold mode for A and B triggers
+ - Variable rapid fire rate for A and B triggers
+ - Reset button
+ - Different special modes
+ - No Mega Drive mode
+ - Start and Select not reported in digital mode
+
+**********************************************************************/
+
+#include "emu.h"
+#include "micomxe1a.h"
+
+//#define VERBOSE 1
+//#define LOG_OUTPUT_FUNC osd_printf_info
+#include "logmacro.h"
+
+
+DEFINE_DEVICE_TYPE(MICOM_XE_1A, micom_xe_1a_device, "micom_xe_1a", "Dempa Micom Soft Analog/Digital Intelligent Controller")
+
+
+
+micom_xe_1a_device::micom_xe_1a_device(
+ machine_config const &mconfig,
+ char const *tag,
+ device_t *owner,
+ u32 clock):
+ device_t(mconfig, MICOM_XE_1A, tag, owner, clock),
+ m_buttons_callback(*this),
+ m_analog_callback(*this),
+ m_output_timer(nullptr),
+ m_req(1),
+ m_mode(1),
+ m_interface(0),
+ m_out(0x2e)
+{
+}
+
+
+u8 micom_xe_1a_device::out_r()
+{
+ if (m_mode)
+ {
+ LOG("%s: analog mode read data = %02X\n", machine().describe_context(), m_out);
+ return m_out;
+ }
+ else
+ {
+ u16 const buttons = m_buttons_callback();
+ if (m_req)
+ {
+ u8 const z = m_analog_callback(2);
+ u8 const result =
+ ((0xc0 > z) ? 0x01 : 0x00) | // Throttle Up
+ ((0x40 <= z) ? 0x02 : 0x00) | // Throttle Down
+ (BIT(buttons, 1) << 2) | // C
+ (BIT(buttons, 0) << 3) | // D
+ (BIT(buttons, 7) << 4) | // E1
+ (BIT(buttons, 6) << 5); // E2
+ LOG(
+ "%s: digital mode extended read = 0x%02X\n",
+ machine().describe_context(),
+ result);
+ return result;
+ }
+ else
+ {
+ u8 const y = m_analog_callback(0);
+ u8 const x = m_analog_callback(1);
+ u8 const result =
+ ((BIT(buttons, 4) && (0x40 <= y)) ? 0x01 : 0x00) | // Select/Up
+ ((BIT(buttons, 4) && (0xc0 > y)) ? 0x02 : 0x00) | // Select/Down
+ ((BIT(buttons, 5) && (0x40 <= x)) ? 0x04 : 0x00) | // Start/Left
+ ((BIT(buttons, 5) && (0xc0 > x)) ? 0x08 : 0x00) | // Start/Right
+ ((BIT(buttons, 3) & BIT(buttons, 9)) << 4) | // A/A'
+ ((BIT(buttons, 2) & BIT(buttons, 8)) << 5); // B/B'
+ LOG(
+ "%s: digital mode basic read = 0x%02X\n",
+ machine().describe_context(),
+ result);
+ return result;
+ }
+ }
+}
+
+
+WRITE_LINE_MEMBER(micom_xe_1a_device::req_w)
+{
+ u8 const req = state ? 1 : 0;
+ if (req != m_req)
+ {
+ if (m_mode)
+ {
+ LOG("%s: /Req = %u\n", machine().describe_context(), req);
+ if (!req)
+ {
+ // acquire data
+ u16 const buttons = m_buttons_callback();
+ u8 analog[4];
+ for (unsigned i = 0; std::size(analog) > i; ++i)
+ analog[i] = m_analog_callback(i);
+
+ // pack data
+ m_data[0] = BIT(buttons, 0, 8) & ((BIT(buttons, 8, 2) << 2) | 0xf3);
+ m_data[1] = BIT(analog[0], 4, 4) | (BIT(analog[1], 4, 4) << 4);
+ m_data[2] = BIT(analog[2], 4, 4) | (BIT(analog[3], 4, 4) << 4);
+ m_data[3] = BIT(analog[0], 0, 4) | (BIT(analog[1], 0, 4) << 4);
+ m_data[4] = BIT(analog[2], 0, 4) | (BIT(analog[3], 0, 4) << 4);
+ m_data[5] = BIT(buttons, 8, 8) & ((BIT(buttons, 6, 2) << 2) | 0xf3);
+
+ // takes a while to respond
+ m_output_timer->adjust(attotime::from_nsec(46'600), 0);
+ }
+ }
+ else
+ {
+ LOG("%s: /Req = %u ignored in digital mode\n", machine().describe_context(), req);
+ }
+ m_req = req;
+ }
+}
+
+
+WRITE_LINE_MEMBER(micom_xe_1a_device::mode_w)
+{
+ u8 const mode = state ? 1 : 0;
+ if (mode != m_mode)
+ {
+ if (mode)
+ {
+ LOG("Analog mode selected\n");
+ }
+ else
+ {
+ LOG("Digital mode selected\n");
+ m_output_timer->enable(false);
+ m_out = 0x2e;
+ }
+ m_mode = mode;
+ }
+}
+
+
+WRITE_LINE_MEMBER(micom_xe_1a_device::interface_w)
+{
+ m_interface = state ? 1 : 0;
+}
+
+
+void micom_xe_1a_device::device_start()
+{
+ m_buttons_callback.resolve_safe(0xffff);
+ m_analog_callback.resolve_safe(0x00);
+
+ m_output_timer = timer_alloc(FUNC(micom_xe_1a_device::step_output), this);
+
+ std::fill(std::begin(m_data), std::end(m_data), 0x00);
+ m_out = 0x2e;
+
+ save_item(NAME(m_req));
+ save_item(NAME(m_mode));
+ save_item(NAME(m_interface));
+ save_item(NAME(m_data));
+ save_item(NAME(m_out));
+}
+
+
+TIMER_CALLBACK_MEMBER(micom_xe_1a_device::step_output)
+{
+ auto const step = param >> 1;
+ if (!BIT(param, 0))
+ {
+ auto const nybble = step ^ m_interface;
+ if ((std::size(m_data) * 2) > step)
+ m_out = BIT(m_data[nybble >> 1], BIT(nybble, 0) ? 4 : 0, 4);
+ else
+ m_out = 0xe;
+ m_out |= BIT(step, 0) ? 0x30 : 0x20;
+ LOG(
+ "Set nybble %u data = 0x%X, L/H = %d, /Ack = 1\n",
+ step,
+ BIT(m_out, 0, 4),
+ BIT(m_out, 4));
+ if ((std::size(m_data) * 2) > step)
+ {
+ m_output_timer->adjust(
+ attotime::from_nsec(BIT(step, 0) ? 3'800 : 21'800),
+ param + 1);
+ }
+ }
+ else if ((std::size(m_data) * 2) > step)
+ {
+ m_out &= 0x1f;
+ LOG("Set nybble %u /Ack = 0\n", step);
+ m_output_timer->adjust(attotime::from_nsec(12'100), param + 1);
+ }
+}
diff --git a/src/devices/machine/micomxe1a.h b/src/devices/machine/micomxe1a.h
new file mode 100644
index 00000000000..e8b55e7f488
--- /dev/null
+++ b/src/devices/machine/micomxe1a.h
@@ -0,0 +1,49 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/**********************************************************************
+
+ Dempa Micom Soft Analog/Digital Controller emulation
+
+**********************************************************************/
+#ifndef MAME_MACHINE_MICOMXE1A_H
+#define MAME_MACHINE_MICOMXE1A_H
+
+#pragma once
+
+
+class micom_xe_1a_device : public device_t
+{
+public:
+ micom_xe_1a_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock = 0) ATTR_COLD;
+
+ auto buttons_handler() { return m_buttons_callback.bind(); }
+ auto analog_handler() { return m_analog_callback.bind(); }
+
+ u8 out_r();
+
+ DECLARE_WRITE_LINE_MEMBER(req_w);
+ DECLARE_WRITE_LINE_MEMBER(mode_w); // 0 = digital, 1 = analog
+ DECLARE_WRITE_LINE_MEMBER(interface_w); // 0 = PC, 1 = MD
+
+protected:
+ virtual void device_start() override ATTR_COLD;
+
+private:
+ TIMER_CALLBACK_MEMBER(step_output);
+
+ devcb_read16 m_buttons_callback;
+ devcb_read8 m_analog_callback;
+
+ emu_timer *m_output_timer;
+
+ u8 m_req;
+ u8 m_mode;
+ u8 m_interface;
+ u8 m_data[6];
+ u8 m_out;
+};
+
+
+DECLARE_DEVICE_TYPE(MICOM_XE_1A, micom_xe_1a_device)
+
+#endif // MAME_MACHINE_MICOMXE1A_H