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.
This commit is contained in:
Vas Crabb 2022-12-18 22:21:33 +11:00
parent 696b16ccb9
commit a32b70c034
13 changed files with 737 additions and 367 deletions

View File

@ -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
@ -4053,6 +4055,28 @@ if (BUSES["PCE"]~=null) then
}
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
@ -5078,26 +5102,6 @@ if (BUSES["THOMSON"]~=null) then
}
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

View File

@ -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

View File

@ -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);
}

View File

@ -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)")

View File

@ -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

View File

@ -33,22 +33,17 @@
#include "emu.h"
#include "joypad2.h"
#include "machine/74157.h"
//**************************************************************************
// DEVICE DEFINITIONS
//**************************************************************************
namespace {
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")
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")

View File

@ -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

View File

@ -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)

View File

@ -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")

View File

@ -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

View File

@ -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
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;
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;
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;
};
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_xe1(*this, "xe1"),
m_axes(*this, "CH%u", 0U),
m_mode(*this, "MODE"),
m_output_timer(nullptr),
m_req(1),
m_out(0x3f)
m_mode(*this, "MODE")
{
}
u8 sms_xe1ap_device::in_r()
void sms_xe1ap_device::device_add_mconfig(machine_config &config)
{
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;
}
}
MICOM_XE_1A(config, m_xe1);
m_xe1->buttons_handler().set_ioport("BUTTONS");
m_xe1->analog_handler().set(FUNC(sms_xe1ap_device::read_channel));
}
void sms_xe1ap_device::out_w(u8 data, u8 mem_mask)
void sms_xe1ap_device::device_reset()
{
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;
}
}
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
{
return INPUT_PORTS_NAME(sms_xe1ap);
}
void sms_xe1ap_device::device_start()
{
m_output_timer = timer_alloc(FUNC(sms_xe1ap_device::step_output), this);
save_item(NAME(m_req));
save_item(NAME(m_out));
}
TIMER_CALLBACK_MEMBER(sms_xe1ap_device::step_output)
{
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)")

View File

@ -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);
}
}

View File

@ -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