From 905470b6537ad1b41b42d5445f464daa17296d78 Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Fri, 16 Dec 2022 17:52:06 +1100 Subject: bus/sms_ctrl: Added support for Sega mouse and four-player adaptor. --- hash/megadriv.xml | 71 +---- scripts/src/bus.lua | 4 + src/devices/bus/sms_ctrl/controllers.cpp | 56 ++-- src/devices/bus/sms_ctrl/controllers.h | 3 + src/devices/bus/sms_ctrl/mouse.cpp | 265 +++++++++++++++++ src/devices/bus/sms_ctrl/mouse.h | 19 ++ src/devices/bus/sms_ctrl/smsctrl.cpp | 4 +- src/devices/bus/sms_ctrl/teamplayer.cpp | 487 +++++++++++++++++++++++++++++++ src/devices/bus/sms_ctrl/teamplayer.h | 18 ++ src/mame/sega/mdconsole.cpp | 4 - src/mame/sega/megadriv.cpp | 10 +- src/mame/sega/megadriv.h | 6 +- 12 files changed, 852 insertions(+), 95 deletions(-) create mode 100644 src/devices/bus/sms_ctrl/mouse.cpp create mode 100644 src/devices/bus/sms_ctrl/mouse.h create mode 100644 src/devices/bus/sms_ctrl/teamplayer.cpp create mode 100644 src/devices/bus/sms_ctrl/teamplayer.h diff --git a/hash/megadriv.xml b/hash/megadriv.xml index 27129ebdc6f..439aedd9b19 100644 --- a/hash/megadriv.xml +++ b/hash/megadriv.xml @@ -1363,13 +1363,10 @@ The three available regions are PAL, NTSC-U and NTSC-J or a combination such as - + Cannon Fodder (Europe) 1994 Virgin Interactive - @@ -3188,13 +3185,10 @@ Crashes after EA logo, requires better [VDP] irq handling - + Fun 'N Games (Europe) 1993 Sony Imagesoft - @@ -4172,13 +4166,10 @@ Moans about [Sega Mega Modem] not hooked up, punts to a red screen - + Jimmy White's Whirlwind Snooker (Europe) 1991 Virgin Games - @@ -4657,13 +4648,10 @@ Unsupported [Sega Mouse] peripheral - + Lemmings 2 - The Tribes (Europe) 1994 Psygnosis - @@ -7263,13 +7251,10 @@ Unsupported [Menacer] peripheral - + Shanghai II - Dragon's Eye (USA) 1994 Activision - @@ -12285,7 +12270,6 @@ https://bootleggames.fandom.com/wiki/Barver_Battle_Saga:_Tai_Kong_Zhan_Shi Sega @@ -18123,13 +18107,10 @@ Unemulated [Sega Mega Modem] features - + Janou Touryuumon (Japan) 1993 Sega - @@ -19019,13 +19000,10 @@ Black screen - + Lemmings 2 - The Tribes (USA) 1994 Psygnosis - @@ -19236,13 +19214,10 @@ Unsupported [Justifier] peripheral (joypad works) - + Lord Monarch - Tokoton Sentou Densetsu (Japan) 1994 Sega - @@ -19623,13 +19598,10 @@ https://segaretro.org/Ma_Qiao_E_Mo_Ta:_Devilish_Mahjong_Tower - + Marble Madness (Japan) 1993 Tengen - @@ -22322,13 +22294,10 @@ Unemulated [Sega Mega Modem] features - + Nobunaga no Yabou - Haouden (Japan) 1994 Koei - @@ -22342,13 +22311,10 @@ Unsupported [Sega Mouse] peripheral - + Nobunaga's Ambition (USA) 1993 Koei - @@ -22359,13 +22325,10 @@ Unsupported [Sega Mouse] peripheral - + Nobunaga no Yabou - Zenkokuban (Japan) 1993 Koei - @@ -24536,13 +24499,10 @@ https://segaretro.org/Rastan_Saga_II - + Richard Scarry's BusyTown (USA) 1994 Sega - @@ -30480,13 +30440,10 @@ No BGMs - + Wacky Worlds (USA) 1994 Sega - diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua index 347b1edd6f3..610e15de431 100644 --- a/scripts/src/bus.lua +++ b/scripts/src/bus.lua @@ -3557,6 +3557,8 @@ if (BUSES["SMS_CTRL"]~=null) then MAME_DIR .. "src/devices/bus/sms_ctrl/md6bt.h", MAME_DIR .. "src/devices/bus/sms_ctrl/mdpad.cpp", MAME_DIR .. "src/devices/bus/sms_ctrl/mdpad.h", + MAME_DIR .. "src/devices/bus/sms_ctrl/mouse.cpp", + MAME_DIR .. "src/devices/bus/sms_ctrl/mouse.h", MAME_DIR .. "src/devices/bus/sms_ctrl/multitap.cpp", MAME_DIR .. "src/devices/bus/sms_ctrl/multitap.h", MAME_DIR .. "src/devices/bus/sms_ctrl/paddle.cpp", @@ -3571,6 +3573,8 @@ if (BUSES["SMS_CTRL"]~=null) then MAME_DIR .. "src/devices/bus/sms_ctrl/sports.h", MAME_DIR .. "src/devices/bus/sms_ctrl/sportsjp.cpp", MAME_DIR .. "src/devices/bus/sms_ctrl/sportsjp.h", + MAME_DIR .. "src/devices/bus/sms_ctrl/teamplayer.cpp", + MAME_DIR .. "src/devices/bus/sms_ctrl/teamplayer.h", } end diff --git a/src/devices/bus/sms_ctrl/controllers.cpp b/src/devices/bus/sms_ctrl/controllers.cpp index a4679c7aeb1..4323b3a2bb9 100644 --- a/src/devices/bus/sms_ctrl/controllers.cpp +++ b/src/devices/bus/sms_ctrl/controllers.cpp @@ -15,43 +15,51 @@ #include "lphaser.h" #include "md6bt.h" #include "mdpad.h" +#include "mouse.h" #include "multitap.h" #include "paddle.h" #include "rfu.h" #include "rs232adapt.h" #include "sports.h" #include "sportsjp.h" +#include "teamplayer.h" -char const *const SMS_CTRL_OPTION_DIY_PADDLE = "diypaddle"; -char const *const SMS_CTRL_OPTION_GRAPHIC = "graphic"; -char const *const SMS_CTRL_OPTION_LPHASER = "lphaser"; -char const *const SMS_CTRL_OPTION_MD_6BUTTON = "md6button"; -char const *const SMS_CTRL_OPTION_MD_PAD = "mdpad"; -char const *const SMS_CTRL_OPTION_MULTITAP = "multitap"; -char const *const SMS_CTRL_OPTION_JOYPAD = "mspad"; -char const *const SMS_CTRL_OPTION_PADDLE = "paddle"; -char const *const SMS_CTRL_OPTION_RAPID_FIRE = "rapidfire"; -char const *const SMS_CTRL_OPTION_RS232 = "rs232"; -char const *const SMS_CTRL_OPTION_SPORTS = "sports"; -char const *const SMS_CTRL_OPTION_SPORTS_JP = "sportsjp"; +char const *const SMS_CTRL_OPTION_DIY_PADDLE = "diypaddle"; +char const *const SMS_CTRL_OPTION_GRAPHIC = "graphic"; +char const *const SMS_CTRL_OPTION_LPHASER = "lphaser"; +char const *const SMS_CTRL_OPTION_MD_6BUTTON = "md6button"; +char const *const SMS_CTRL_OPTION_MD_PAD = "mdpad"; +char const *const SMS_CTRL_OPTION_MEGA_MOUSE = "mouseus"; +char const *const SMS_CTRL_OPTION_MULTITAP = "multitap"; +char const *const SMS_CTRL_OPTION_JOYPAD = "mspad"; +char const *const SMS_CTRL_OPTION_PADDLE = "paddle"; +char const *const SMS_CTRL_OPTION_RAPID_FIRE = "rapidfire"; +char const *const SMS_CTRL_OPTION_RS232 = "rs232"; +char const *const SMS_CTRL_OPTION_SEGA_MOUSE = "mouse"; +char const *const SMS_CTRL_OPTION_SPORTS = "sports"; +char const *const SMS_CTRL_OPTION_SPORTS_JP = "sportsjp"; +char const *const SMS_CTRL_OPTION_TEAM_PLAYER = "teamplay"; void sms_control_port_devices(device_slot_interface &device) { - device.option_add(SMS_CTRL_OPTION_DIY_PADDLE, SMS_DIY_PADDLE); - device.option_add(SMS_CTRL_OPTION_GRAPHIC, SMS_GRAPHIC); - device.option_add(SMS_CTRL_OPTION_LPHASER, SMS_LIGHT_PHASER); - device.option_add(SMS_CTRL_OPTION_MD_6BUTTON, SMS_MD6BUTTON); - device.option_add(SMS_CTRL_OPTION_MD_PAD, SMS_MDPAD); - device.option_add(SMS_CTRL_OPTION_MULTITAP, SMS_MULTITAP); - device.option_add(SMS_CTRL_OPTION_JOYPAD, SMS_JOYPAD); - device.option_add(SMS_CTRL_OPTION_PADDLE, SMS_PADDLE); - device.option_add(SMS_CTRL_OPTION_RAPID_FIRE, SMS_RAPID_FIRE); - device.option_add(SMS_CTRL_OPTION_RS232, SMS_RS232); - device.option_add(SMS_CTRL_OPTION_SPORTS, SMS_SPORTS_PAD); - device.option_add(SMS_CTRL_OPTION_SPORTS_JP, SMS_SPORTS_PAD_JP); + device.option_add(SMS_CTRL_OPTION_DIY_PADDLE, SMS_DIY_PADDLE); + device.option_add(SMS_CTRL_OPTION_GRAPHIC, SMS_GRAPHIC); + device.option_add(SMS_CTRL_OPTION_LPHASER, SMS_LIGHT_PHASER); + device.option_add(SMS_CTRL_OPTION_MD_6BUTTON, SMS_MD6BUTTON); + device.option_add(SMS_CTRL_OPTION_MD_PAD, SMS_MDPAD); + device.option_add(SMS_CTRL_OPTION_MEGA_MOUSE, SMS_MEGAMOUSE); + device.option_add(SMS_CTRL_OPTION_MULTITAP, SMS_MULTITAP); + device.option_add(SMS_CTRL_OPTION_JOYPAD, SMS_JOYPAD); + device.option_add(SMS_CTRL_OPTION_PADDLE, SMS_PADDLE); + device.option_add(SMS_CTRL_OPTION_RAPID_FIRE, SMS_RAPID_FIRE); + device.option_add(SMS_CTRL_OPTION_RS232, SMS_RS232); + device.option_add(SMS_CTRL_OPTION_SEGA_MOUSE, SMS_SEGAMOUSE); + device.option_add(SMS_CTRL_OPTION_SPORTS, SMS_SPORTS_PAD); + device.option_add(SMS_CTRL_OPTION_SPORTS_JP, SMS_SPORTS_PAD_JP); + device.option_add(SMS_CTRL_OPTION_TEAM_PLAYER, SMS_TEAM_PLAYER); } diff --git a/src/devices/bus/sms_ctrl/controllers.h b/src/devices/bus/sms_ctrl/controllers.h index 0e128a2404c..fb3cd5c0dda 100644 --- a/src/devices/bus/sms_ctrl/controllers.h +++ b/src/devices/bus/sms_ctrl/controllers.h @@ -16,13 +16,16 @@ extern char const *const SMS_CTRL_OPTION_GRAPHIC; extern char const *const SMS_CTRL_OPTION_LPHASER; extern char const *const SMS_CTRL_OPTION_MD_6BUTTON; extern char const *const SMS_CTRL_OPTION_MD_PAD; +extern char const *const SMS_CTRL_OPTION_MEGA_MOUSE; extern char const *const SMS_CTRL_OPTION_MULTITAP; extern char const *const SMS_CTRL_OPTION_JOYPAD; extern char const *const SMS_CTRL_OPTION_PADDLE; extern char const *const SMS_CTRL_OPTION_RAPID_FIRE; extern char const *const SMS_CTRL_OPTION_RS232; +extern char const *const SMS_CTRL_OPTION_SEGA_MOUSE; extern char const *const SMS_CTRL_OPTION_SPORTS; extern char const *const SMS_CTRL_OPTION_SPORTS_JP; +extern char const *const SMS_CTRL_OPTION_TEAM_PLAYER; void sms_control_port_devices(device_slot_interface &device); void sms_control_port_passive_devices(device_slot_interface &device); diff --git a/src/devices/bus/sms_ctrl/mouse.cpp b/src/devices/bus/sms_ctrl/mouse.cpp new file mode 100644 index 00000000000..88618dd8c83 --- /dev/null +++ b/src/devices/bus/sms_ctrl/mouse.cpp @@ -0,0 +1,265 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/********************************************************************** + + Sega Mega Mouse emulation + + The Sega Mouse sold in Japan and Europe has only two buttons but + is otherwise compatible. + +**********************************************************************/ + +#include "emu.h" +#include "mouse.h" + +#include + +//#define VERBOSE 1 +//#define LOG_OUTPUT_FUNC osd_printf_info +#include "logmacro.h" + + +namespace { + +INPUT_PORTS_START( sms_megamouse ) + PORT_START("BUTTONS") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("%p A (Left Button)") + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("%p C (Right Button)") + PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_NAME("%p B (Middle Button)") + PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_START) + + PORT_START("X") + PORT_BIT( 0x1fff, 0x0000, IPT_MOUSE_X ) PORT_SENSITIVITY(50) PORT_KEYDELTA(1) + + PORT_START("Y") + PORT_BIT( 0x1fff, 0x0000, IPT_MOUSE_Y ) PORT_SENSITIVITY(50) PORT_KEYDELTA(1) PORT_REVERSE +INPUT_PORTS_END + + +INPUT_PORTS_START( sms_segamouse ) + PORT_INCLUDE(sms_megamouse) + + PORT_MODIFY("BUTTONS") + PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("%p Left Button") + PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("%p Right Button") + PORT_BIT(0x0c, IP_ACTIVE_HIGH, IPT_UNUSED) +INPUT_PORTS_END + + + +class sms_megamouse_device : public device_t, public device_sms_control_interface +{ +public: + sms_megamouse_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock); + + virtual u8 in_r() override { return m_out; } + virtual void out_w(u8 data, u8 mem_mask) override; + +protected: + sms_megamouse_device(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock); + + virtual ioport_constructor device_input_ports() const override { return INPUT_PORTS_NAME(sms_megamouse); } + virtual void device_start() override; + +private: + TIMER_CALLBACK_MEMBER(next_step); + required_ioport m_buttons; + required_ioport_array<2> m_axes; + + emu_timer *m_busy_timer; + + u16 m_base[2]; + s16 m_count[2]; + u8 m_th_in; + u8 m_tr_in; + u8 m_out; + u8 m_phase; +}; + + +sms_megamouse_device::sms_megamouse_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) : + sms_megamouse_device(mconfig, SMS_MEGAMOUSE, tag, owner, clock) +{ +} + + +sms_megamouse_device::sms_megamouse_device(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock) : + device_t(mconfig, type, tag, owner, clock), + device_sms_control_interface(mconfig, *this), + m_buttons(*this, "BUTTONS"), + m_axes(*this, "%c", 'X'), + m_th_in(1), + m_tr_in(1), + m_out(0x30), + m_phase(0) +{ +} + + +void sms_megamouse_device::out_w(u8 data, u8 mem_mask) +{ + u8 const th = BIT(data, 6); + u8 const tr = BIT(data, 5); + if (th != m_th_in) + { + // seems to respond to TH pretty quickly + if (th) + { + LOG("%s: TH rising, deselected\n", machine().describe_context()); + m_busy_timer->enable(false); + m_out = 0x30; + m_phase = 0; + } + else + { + LOG("%s: TH falling, selected\n", machine().describe_context()); + m_out = 0x3b; + + for (unsigned i = 0; m_axes.size() > i; ++i) + { + u16 const current = m_axes[i]->read(); + m_count[i] = s16(current) - s16(m_base[i]); + m_base[i] = current; + if (0x1000 < m_count[i]) + m_count[i] -= 0x2000; + else if (-0x1000 > m_count[i]) + m_count[i] += 0x2000; + } + LOG( + "%s: movement (%d %d)\n", + machine().describe_context(), + m_count[0], + m_count[1]); + + if (!tr) + { + LOG("%s: TR low, wait for data update\n", machine().describe_context()); + m_busy_timer->adjust(attotime::from_nsec(7200), 0); + } + } + m_th_in = th; + m_tr_in = tr; + } + else if (!m_th_in && (tr != m_tr_in)) + { + if (m_busy_timer->enabled()) + { + LOG( + "%s: busy, deferring response to TH %s\n", + machine().describe_context(), + th ? "rising" : "falling"); + } + else + { + LOG( + "%s: TR %s, wait for data update\n", + machine().describe_context(), + tr ? "rising" : "falling"); + m_busy_timer->adjust(attotime::from_nsec(7200), tr); + } + m_tr_in = tr; + } +} + + +void sms_megamouse_device::device_start() +{ + m_busy_timer = timer_alloc(FUNC(sms_megamouse_device::next_step), this); + + std::fill(std::begin(m_base), std::end(m_base), 0); + std::fill(std::begin(m_count), std::end(m_count), 0); + m_out = 0x30; + m_phase = 0; + + save_item(NAME(m_base)); + save_item(NAME(m_count)); + save_item(NAME(m_th_in)); + save_item(NAME(m_tr_in)); + save_item(NAME(m_out)); + save_item(NAME(m_phase)); +} + + +TIMER_CALLBACK_MEMBER(sms_megamouse_device::next_step) + +{ + if (BIT(param, 1)) + { + u8 const tr = BIT(param, 0); + LOG("update TL = %u\n", tr); + if (tr) + m_out |= 0x10; + else + m_out &= ~0x10; + + assert(!m_th_in); + if (tr != m_tr_in) + { + LOG("TR %s, wait for data update\n", tr ? "high" : "low"); + m_busy_timer->adjust(attotime::from_nsec(7200), tr); + } + } + else + { + switch (m_phase) + { + case 0: + m_out = (m_out & 0x30) | 0x0f; + LOG("update data unused = 0x%X\n", m_out & 0x0f); + break; + case 1: + if (BIT(param, 0)) + { + m_out = (m_out & 0x30) | (m_buttons->read() & 0x0f); + LOG("update data buttons = 0x%X\n", m_out & 0x0f); + } + else + { + m_out = + (m_out & 0x30) | + ((util::sext(m_count[1], 9) != m_count[1]) ? 0x80 : 0x00) | + ((util::sext(m_count[0], 9) != m_count[0]) ? 0x40 : 0x00) | + (BIT(m_count[1], 8) << 1) | + (BIT(m_count[0], 8) << 0); + LOG("update data flags = 0x%X\n", m_out & 0x0f); + } + break; + case 2: + case 3: + m_out = (m_out & 0x30) | BIT(m_count[BIT(m_phase, 0)], BIT(param, 0) ? 0 : 4, 4); + LOG( + "update data %c %s = 0x%X\n", + 'X' + BIT(m_phase, 0), + BIT(param, 0) ? "low" : "high", + m_out & 0x0f); + break; + } + if (BIT(param, 0)) + m_phase = (m_phase + 1) & 0x03; + m_busy_timer->adjust(attotime::from_nsec(4800), param | 0x02); + } +} + + + +class sms_segamouse_device : public sms_megamouse_device +{ +public: + sms_segamouse_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock); + +protected: + virtual ioport_constructor device_input_ports() const override { return INPUT_PORTS_NAME(sms_segamouse); } +}; + + +sms_segamouse_device::sms_segamouse_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) : + sms_megamouse_device(mconfig, SMS_SEGAMOUSE, tag, owner, clock) +{ +} + +} // anonymous namespace + + + +DEFINE_DEVICE_TYPE_PRIVATE(SMS_MEGAMOUSE, device_sms_control_interface, sms_megamouse_device, "sms_megamouse", "Sega Mega Mouse (US)") +DEFINE_DEVICE_TYPE_PRIVATE(SMS_SEGAMOUSE, device_sms_control_interface, sms_segamouse_device, "sms_segamouse", "Sega Mega Drive Mouse (World)") diff --git a/src/devices/bus/sms_ctrl/mouse.h b/src/devices/bus/sms_ctrl/mouse.h new file mode 100644 index 00000000000..1d316204e6e --- /dev/null +++ b/src/devices/bus/sms_ctrl/mouse.h @@ -0,0 +1,19 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/********************************************************************** + + Sega Mega Mouse emulation + +**********************************************************************/ +#ifndef MAME_BUS_SMS_CTRL_MOUSE_H +#define MAME_BUS_SMS_CTRL_MOUSE_H + +#pragma once + +#include "smsctrl.h" + + +DECLARE_DEVICE_TYPE(SMS_MEGAMOUSE, device_sms_control_interface) +DECLARE_DEVICE_TYPE(SMS_SEGAMOUSE, device_sms_control_interface) + +#endif // MAME_BUS_SMS_CTRL_MOUSE_H diff --git a/src/devices/bus/sms_ctrl/smsctrl.cpp b/src/devices/bus/sms_ctrl/smsctrl.cpp index 8db72026c28..1df60acf304 100644 --- a/src/devices/bus/sms_ctrl/smsctrl.cpp +++ b/src/devices/bus/sms_ctrl/smsctrl.cpp @@ -2,7 +2,7 @@ // copyright-holders:Vas Crabb /********************************************************************** - Sega DE-9 controller port emulation + Sega 7-bit I/O port emulation **********************************************************************/ @@ -15,7 +15,7 @@ // GLOBAL VARIABLES //************************************************************************** -DEFINE_DEVICE_TYPE(SMS_CONTROL_PORT, sms_control_port_device, "sms_control_port", "Sega DE-9 controller port") +DEFINE_DEVICE_TYPE(SMS_CONTROL_PORT, sms_control_port_device, "sms_control_port", "Sega 9-pin I/O port") diff --git a/src/devices/bus/sms_ctrl/teamplayer.cpp b/src/devices/bus/sms_ctrl/teamplayer.cpp new file mode 100644 index 00000000000..eb6a6a59389 --- /dev/null +++ b/src/devices/bus/sms_ctrl/teamplayer.cpp @@ -0,0 +1,487 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/********************************************************************** + + Sega Mega Drive 4-Player Adaptor emulation + + Known as Team Player Multi-Player Adaptor in the US + Known as Sega Tap (セガタップ) in Japan + + Everything here is guessed based on behaviour expected by + software. It isn't clear exactly how downstream devices are + clocked, and how controllers are identified. + +**********************************************************************/ + +#include "emu.h" +#include "teamplayer.h" + +#include "controllers.h" + +#include + +//#define VERBOSE 1 +//#define LOG_OUTPUT_FUNC osd_printf_info +#include "logmacro.h" + + +namespace { + +class sms_teamplayer_device : public device_t, public device_sms_control_interface +{ +public: + sms_teamplayer_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(reselect); + +protected: + virtual void device_add_mconfig(machine_config &config) override; + virtual ioport_constructor device_input_ports() const override; + virtual void device_resolve_objects() override; + virtual void device_start() override; + +private: + enum : u8 + { + PHASE_START = 0, + PHASE_UNUSED, + PHASE_IDENT1, + PHASE_IDENT2, + PHASE_PORT_A, + PHASE_PORT_B, + PHASE_PORT_C, + PHASE_PORT_D, + PHASE_FINAL + }; + + template DECLARE_WRITE_LINE_MEMBER(th_up_w); + + TIMER_CALLBACK_MEMBER(probe_step); + + void selected(); + void deselected(); + void th_falling(); + void th_rising(); + void probe(unsigned step); + void next_port(u8 tl); + void next_nybble(u8 tl); + + static u8 identify(u8 const (&response)[8]); + + required_device_array m_ports; + required_ioport m_select; + + emu_timer *m_probe_timer; + + u8 m_data_down; + u8 m_mask_down; + u8 m_th_up[4]; + u8 m_out; + + u8 m_phase; + u8 m_nybble; + + u8 m_probe[4][8]; + u8 m_ident[4]; +}; + + + +INPUT_PORTS_START( sms_teamplayer ) + PORT_START("SELECT") + PORT_CONFNAME(0x07, 0x04, "Switch") PORT_CHANGED_MEMBER(DEVICE_SELF, sms_teamplayer_device, reselect, 0) + PORT_CONFSETTING( 0x00, "A") + PORT_CONFSETTING( 0x01, "B") + PORT_CONFSETTING( 0x02, "C") + PORT_CONFSETTING( 0x03, "D") + PORT_CONFSETTING( 0x04, "Multi") +INPUT_PORTS_END + + + +sms_teamplayer_device::sms_teamplayer_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) : + device_t(mconfig, SMS_TEAM_PLAYER, tag, owner, clock), + device_sms_control_interface(mconfig, *this), + m_ports(*this, "%c", 'a'), + m_select(*this, "SELECT"), + m_probe_timer(nullptr), + m_data_down(0x3f), + m_mask_down(0x00), + m_out(0x33), + m_phase(PHASE_START), + m_nybble(0) +{ +} + + +INPUT_CHANGED_MEMBER(sms_teamplayer_device::reselect) +{ + if (!BIT(m_data_down, 6)) + { + if (m_ports.size() <= oldval) + { + deselected(); + } + else if (m_ports.size() <= newval) + { + selected(); + if (!BIT(m_data_down, 5)) + th_falling(); + } + } + + if ((m_ports.size() > newval) || BIT(m_data_down, 6)) + { + for (unsigned i = 0; m_ports.size() > i; ++i) + m_ports[i]->out_w((newval == i) ? m_data_down : 0x7f, (newval == i) ? m_mask_down : 0x00); + } + th_w((m_ports.size() > newval) ? m_th_up[newval] : 1); +} + + +u8 sms_teamplayer_device::in_r() +{ + u8 const sel = m_select->read(); + return (m_ports.size() > sel) ? m_ports[sel]->in_r() : m_out; +} + + +void sms_teamplayer_device::out_w(u8 data, u8 mem_mask) +{ + u8 const sel = m_select->read(); + if (m_ports.size() > sel) + { + m_ports[sel]->out_w(data, mem_mask); + } + else + { + u8 const changed = data ^ m_data_down; + if (BIT(changed, 6)) + { + if (BIT(data, 6)) + { + deselected(); + } + else + { + selected(); + if (!BIT(data, 5)) + th_falling(); + } + } + else if (!BIT(m_data_down, 6) && BIT(changed, 5)) + { + if (BIT(data, 5)) + th_rising(); + else + th_falling(); + } + } + m_data_down = data; + m_mask_down = mem_mask; +} + + +void sms_teamplayer_device::device_add_mconfig(machine_config &config) +{ + SMS_CONTROL_PORT(config, m_ports[0], sms_control_port_devices, SMS_CTRL_OPTION_MD_PAD); + m_ports[0]->th_handler().set(FUNC(sms_teamplayer_device::th_up_w<0>)); + + SMS_CONTROL_PORT(config, m_ports[1], sms_control_port_devices, SMS_CTRL_OPTION_MD_PAD); + m_ports[1]->th_handler().set(FUNC(sms_teamplayer_device::th_up_w<1>)); + + SMS_CONTROL_PORT(config, m_ports[2], sms_control_port_devices, SMS_CTRL_OPTION_MD_PAD); + m_ports[2]->th_handler().set(FUNC(sms_teamplayer_device::th_up_w<2>)); + + SMS_CONTROL_PORT(config, m_ports[3], sms_control_port_devices, SMS_CTRL_OPTION_MD_PAD); + m_ports[3]->th_handler().set(FUNC(sms_teamplayer_device::th_up_w<3>)); +} + + +ioport_constructor sms_teamplayer_device::device_input_ports() const +{ + return INPUT_PORTS_NAME(sms_teamplayer); +} + + +void sms_teamplayer_device::device_resolve_objects() +{ + m_data_down = 0x3f; + m_mask_down = 0x00; + std::fill(std::begin(m_th_up), std::end(m_th_up), 1); +} + + +void sms_teamplayer_device::device_start() +{ + m_probe_timer = timer_alloc(FUNC(sms_teamplayer_device::probe_step), this); + + m_out = 0x30; + m_phase = PHASE_START; + m_nybble = 0; + + for (auto &response : m_probe) + std::fill(std::begin(response), std::end(response), 0x3f); + std::fill(std::begin(m_ident), std::end(m_ident), 0x0f); + + save_item(NAME(m_data_down)); + save_item(NAME(m_mask_down)); + save_item(NAME(m_th_up)); + save_item(NAME(m_out)); + save_item(NAME(m_phase)); + save_item(NAME(m_nybble)); + save_item(NAME(m_probe)); + save_item(NAME(m_ident)); +} + + +template +DECLARE_WRITE_LINE_MEMBER(sms_teamplayer_device::th_up_w) +{ + if (bool(state) != bool(m_th_up[N])) + { + m_th_up[N] = state; + if (m_select->read() == N) + th_w(state); + } +} + + +TIMER_CALLBACK_MEMBER(sms_teamplayer_device::probe_step) +{ + probe(param + 1); + + if (6 > param) + { + for (auto &port : m_ports) + port->out_w(BIT(param, 0) ? 0x3f : 0x7f, BIT(param, 0) ? 0x40 : 0x00); + m_probe_timer->adjust(attotime::from_usec(1), param + 1); + } + else + { + for (unsigned i = 0; m_ports.size() > i; ++i) + { + m_ident[i] = identify(m_probe[i]); + LOG( + "port %c probe response %02X %02X %02X %02X %02X %02X %02X %02X ident %02X\n", + 'A' + i, + m_probe[i][0], + m_probe[i][1], + m_probe[i][2], + m_probe[i][3], + m_probe[i][4], + m_probe[i][5], + m_probe[i][6], + m_probe[i][7], + m_ident[i]); + } + + if (PHASE_IDENT1 == m_phase) + { + LOG("report port A ident 0x%X\n", m_ident[0]); + m_out = 0x20 | m_ident[0]; + } + } +} + + +void sms_teamplayer_device::selected() +{ + LOG("%s: selected\n", machine().describe_context()); + probe(0); + for (auto &port : m_ports) + port->out_w(0x3f, 0x40); + m_probe_timer->adjust(attotime::from_usec(1), 0); + m_out = 0x3f; + m_phase = PHASE_START; + m_nybble = 0; +} + + +void sms_teamplayer_device::deselected() +{ + LOG("%s: deselected\n", machine().describe_context()); + m_probe_timer->enable(false); + for (auto &port : m_ports) + port->out_w(0x7f, 0x00); + m_out = 0x33; + m_phase = PHASE_START; + m_nybble = 0; +} + + +void sms_teamplayer_device::th_falling() +{ + LOG("%s: TR falling\n", machine().describe_context()); + switch (m_phase) + { + case PHASE_START: + m_out = 0x20; + ++m_phase; + break; + + case PHASE_UNUSED: + case PHASE_IDENT1: + ++m_phase; + if (!m_probe_timer->enabled()) + { + u8 const val = m_ident[(m_phase - PHASE_IDENT1) * 2]; + LOG( + "%s: report port %c ident 0x%X\n", + machine().describe_context(), + 'A' + ((m_phase - PHASE_IDENT1) * 2), + val); + m_out = 0x20 | val; + } + break; + + case PHASE_IDENT2: + next_port(0x00); + break; + + case PHASE_PORT_A: + case PHASE_PORT_B: + case PHASE_PORT_C: + case PHASE_PORT_D: + next_nybble(0x00); + break; + + default: + m_out = 0x20; + } +} + + +void sms_teamplayer_device::th_rising() +{ + LOG("%s: TR rising\n", machine().describe_context()); + switch (m_phase) + { + case PHASE_UNUSED: + m_out = 0x3f; + break; + + case PHASE_IDENT1: + case PHASE_IDENT2: + { + assert(!m_probe_timer->enabled()); + u8 const val = m_ident[((m_phase - PHASE_IDENT1) * 2) + 1]; + LOG( + "%s: report port %c ident 0x%X\n", + machine().describe_context(), + 'A' + ((m_phase - PHASE_IDENT1) * 2) + 1, + val); + m_out = 0x30 | val; + } + break; + + case PHASE_PORT_A: + case PHASE_PORT_B: + case PHASE_PORT_C: + case PHASE_PORT_D: + next_nybble(0x10); + break; + + default: + m_out = 0x30; + } +} + + +void sms_teamplayer_device::probe(unsigned step) +{ + for (unsigned i = 0; m_ports.size() > i; ++i) + m_probe[i][step] = m_ports[i]->in_r(); +} + + +void sms_teamplayer_device::next_port(u8 tl) +{ + ++m_phase; + m_nybble = 0; + while ((PHASE_PORT_D >= m_phase) && (0x0f == m_ident[m_phase - PHASE_PORT_A])) + ++m_phase; + + if (PHASE_PORT_D >= m_phase) + { + u8 const port = m_phase - PHASE_PORT_A; + LOG( + "%s: report port %c data\n", + machine().describe_context(), + 'A' + port); + switch (m_ident[port]) + { + case 0: + case 1: + m_out = 0x20 | tl | (m_probe[port][0] & 0x0f); + break; + case 2: + m_out = 0x20 | tl; // FIXME: mouse support + break; + } + } + else + { + LOG("%s: no more ports with supported controllers\n", machine().describe_context()); + m_out = 0x20 | tl; + } +} + + +void sms_teamplayer_device::next_nybble(u8 tl) +{ + u8 const port = m_phase - PHASE_PORT_A; + switch (m_ident[port]) + { + case 0: + if (!m_nybble++) + m_out = 0x20 | tl | (BIT(m_probe[port][1], 4, 2) << 2) | BIT(m_probe[port][1], 4, 2); + else + next_port(tl); + break; + case 1: + switch (m_nybble++) + { + case 0: + m_out = 0x20 | tl | (BIT(m_probe[port][1], 4, 2) << 2) | BIT(m_probe[port][1], 4, 2); + break; + case 1: + m_out = 0x20 | tl | (m_probe[port][1] & 0x0f); + break; + default: + next_port(tl); + } + break; + case 2: + // FIXME: mouse support + if (5 > m_nybble++) + m_out = 0x20 | tl; + else + next_port(tl); + break; + default: + next_port(tl); + } +} + + +u8 sms_teamplayer_device::identify(u8 const (&response)[8]) +{ + if (!(response[0] & 0x0f) && (0x0b == (response[1] & 0x0f)) && !(response[2] & 0x0f) && (0x0b == (response[3] & 0x0f))) + return 0x02; // mouse + else if ((response[1] & 0x0c) || (response[3] & 0x0c) || (response[5] & 0x0c)) + return 0x0f; // Mega Drive pads pull these bits down for detection + else if (!(response[5] & 0x03)) + return 0x01; // 6-button pad + else + return 0x00; // 3-button pad +} + +} // anonymous namespace + + + +DEFINE_DEVICE_TYPE_PRIVATE(SMS_TEAM_PLAYER, device_sms_control_interface, sms_teamplayer_device, "sms_teamplayer", "Sega Mega Drive 4-Player Adaptor") diff --git a/src/devices/bus/sms_ctrl/teamplayer.h b/src/devices/bus/sms_ctrl/teamplayer.h new file mode 100644 index 00000000000..4eb9a2a01ce --- /dev/null +++ b/src/devices/bus/sms_ctrl/teamplayer.h @@ -0,0 +1,18 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/********************************************************************** + + Sega Mega Drive 4-Player Adaptor emulation + +**********************************************************************/ +#ifndef MAME_BUS_SMS_CTRL_TEAMPLAYER_H +#define MAME_BUS_SMS_CTRL_TEAMPLAYER_H + +#pragma once + +#include "smsctrl.h" + + +DECLARE_DEVICE_TYPE(SMS_TEAM_PLAYER, device_sms_control_interface) + +#endif // MAME_BUS_SMS_CTRL_TEAMPLAYER_H diff --git a/src/mame/sega/mdconsole.cpp b/src/mame/sega/mdconsole.cpp index 9214a942bb2..e17aea2e3ed 100644 --- a/src/mame/sega/mdconsole.cpp +++ b/src/mame/sega/mdconsole.cpp @@ -58,13 +58,9 @@ void md_cons_state::md_exp_port(machine_config &config) static INPUT_PORTS_START( md ) // Unemulated controllers: - // Sega Mouse // Sega Menacer // Konami Justifier - // Sega Team Player // EA 4-Play -/* there exists both a 2 buttons version of the Mouse (Jpn ver, to be used with RPGs, it - can aslo be used as trackball) and a 3 buttons version (US ver, no trackball feats.) */ PORT_START("RESET") // Buttons on Mega Drive console PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_NAME("Reset Button") PORT_IMPULSE(1) // reset, resets 68k (and..?) diff --git a/src/mame/sega/megadriv.cpp b/src/mame/sega/megadriv.cpp index 89c5012ca83..43307db952b 100644 --- a/src/mame/sega/megadriv.cpp +++ b/src/mame/sega/megadriv.cpp @@ -793,8 +793,8 @@ void md_core_state::md_core_ntsc(machine_config &config) m_vdp->set_screen("megadriv"); SCREEN(config, m_screen, SCREEN_TYPE_RASTER); - m_screen->set_refresh_hz(double(MASTER_CLOCK_NTSC) / 10.0 / 262.0 / 342.0); // same as SMS? -// m_screen->set_refresh_hz(double(MASTER_CLOCK_NTSC) / 8.0 / 262.0 / 427.0); // or 427 Htotal? + m_screen->set_refresh_hz(MASTER_CLOCK_NTSC / 10 / 262 / 342); // same as SMS? +// m_screen->set_refresh_hz(double(MASTER_CLOCK_NTSC) / 8 / 262 / 427); // or 427 Htotal? m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(0)); // Vblank handled manually. m_screen->set_size(64*8, 620); m_screen->set_visarea(0, 32*8-1, 0, 28*8-1); @@ -817,8 +817,8 @@ void md_core_state::md_core_pal(machine_config &config) m_vdp->set_screen("megadriv"); SCREEN(config, m_screen, SCREEN_TYPE_RASTER); - m_screen->set_refresh_hz(double(MASTER_CLOCK_PAL) / 10.0 / 313.0 / 342.0); // same as SMS? -// m_screen->set_refresh_hz(double(MASTER_CLOCK_PAL) / 8.0 / 313.0 / 423.0); // or 423 Htotal? + m_screen->set_refresh_hz(MASTER_CLOCK_PAL / 10 / 313 / 342); // same as SMS? +// m_screen->set_refresh_hz(MASTER_CLOCK_PAL / 8 / 313 / 423); // or 423 Htotal? m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(0)); // Vblank handled manually. m_screen->set_size(64*8, 620); m_screen->set_visarea(0, 32*8-1, 0, 28*8-1); @@ -901,7 +901,7 @@ void md_base_state::md2_ntsc(machine_config &config) md_ntsc(config); // Internalized YM3438 in VDP ASIC - YM3438(config.replace(), m_ymsnd, MASTER_CLOCK_NTSC/7); // 7.67 MHz + YM3438(config.replace(), m_ymsnd, MASTER_CLOCK_NTSC / 7); // 7.67 MHz m_ymsnd->add_route(0, "lspeaker", 0.50); m_ymsnd->add_route(1, "rspeaker", 0.50); } diff --git a/src/mame/sega/megadriv.h b/src/mame/sega/megadriv.h index df64819ae6d..84f2cccd0ff 100644 --- a/src/mame/sega/megadriv.h +++ b/src/mame/sega/megadriv.h @@ -13,9 +13,6 @@ #include "sound/ymopn.h" #include "video/315_5313.h" -#define MASTER_CLOCK_NTSC 53693175 -#define MASTER_CLOCK_PAL 53203424 - #define MD_CPU_REGION_SIZE 0x800000 @@ -26,6 +23,9 @@ INPUT_PORTS_EXTERN( megadriv ); class md_core_state : public driver_device { protected: + static inline constexpr XTAL MASTER_CLOCK_NTSC = 53.693175_MHz_XTAL; + static inline constexpr XTAL MASTER_CLOCK_PAL = 53.203424_MHz_XTAL; + md_core_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag), m_maincpu(*this,"maincpu"), -- cgit v1.2.3