From ca7632dc295b9982f59ea9ecd1db8f3a54fb29b7 Mon Sep 17 00:00:00 2001 From: Andrei Holub Date: Mon, 10 Nov 2025 17:03:25 -0500 Subject: [PATCH 1/2] sinclair/specnext.cpp: Added Kempston/MD Pad inputs --- src/mame/sinclair/specnext.cpp | 135 +++++++++++++++++++++++---------- 1 file changed, 97 insertions(+), 38 deletions(-) diff --git a/src/mame/sinclair/specnext.cpp b/src/mame/sinclair/specnext.cpp index 9a2811cb952eb..9400c9e1ed69d 100644 --- a/src/mame/sinclair/specnext.cpp +++ b/src/mame/sinclair/specnext.cpp @@ -132,6 +132,8 @@ class specnext_state : public spectrum_128_state , m_io_video(*this, "VIDEO") , m_io_layers(*this, "LYRS") , m_io_mouse(*this, "mouse_input%u", 1U) + , m_io_joy_left(*this, "JOY_LEFT") + , m_io_joy_right(*this, "JOY_RIGHT") {} void tbblue(machine_config &config); @@ -169,8 +171,9 @@ class specnext_state : public spectrum_128_state u8 port_ff_r(); void port_ff_w(u8 data); void turbosound_address_w(u8 data); - u8 mf_port_r(offs_t addr); - void mf_port_w(offs_t addr, u8 data); + template u8 mf_port_r(offs_t addr); + template void mf_port_w(offs_t addr, u8 data); + template u8 kempston_md_r(offs_t addr); attotime copper_until_pos_r(u16 pos); void bank_update(u8 bank, u8 count); @@ -354,6 +357,8 @@ class specnext_state : public spectrum_128_state optional_ioport m_io_video; optional_ioport m_io_layers; required_ioport_array<3> m_io_mouse; + required_ioport m_io_joy_left; + required_ioport m_io_joy_right; video_timings_info m_video_timings; rectangle m_clip256x192; @@ -386,8 +391,8 @@ class specnext_state : public spectrum_128_state u8 m_nr_03_machine_timing; // u3 bool m_nr_03_config_mode; u8 m_nr_04_romram_bank; // u7 - u8 m_nr_05_joy1; // u2 - u8 m_nr_05_joy0; // u2 + u8 m_nr_05_joy0; // u3 + u8 m_nr_05_joy1; // u3 bool m_nr_05_5060; u8 m_nr_06_psg_mode; // u2 bool m_nr_06_ps2_mode; @@ -1155,11 +1160,10 @@ void specnext_state::turbosound_address_w(u8 data) m_ay[m_nr_08_psg_turbosound_en ? m_ay_select : 0]->address_w(data); } -u8 specnext_state::mf_port_r(offs_t addr) +template u8 specnext_state::mf_port_r(offs_t addr) { if (!machine().side_effects_disabled()) { - const u8 port = addr & 0xff; u8 port_mf_enable_io_a = 0x3f; u8 port_mf_disable_io_a = 0xbf; if (m_nr_0a_mf_type & 2) @@ -1173,8 +1177,8 @@ u8 specnext_state::mf_port_r(offs_t addr) port_mf_disable_io_a = 0x3f; } - m_mf->port_mf_enable_rd_w(port_multiface_io_en() && (port == port_mf_enable_io_a)); - m_mf->port_mf_disable_rd_w(port_multiface_io_en() && (port == port_mf_disable_io_a)); + m_mf->port_mf_enable_rd_w(port_multiface_io_en() && (Lsb == port_mf_enable_io_a)); + m_mf->port_mf_disable_rd_w(port_multiface_io_en() && (Lsb == port_mf_disable_io_a)); m_mf->port_mf_enable_wr_w(0); m_mf->port_mf_disable_wr_w(0); m_mf->clock_w(); @@ -1184,7 +1188,7 @@ u8 specnext_state::mf_port_r(offs_t addr) u8 data; if (!m_mf->mf_port_en_r()) - data = 0x00; + data = Lsb == 0x1f ? kempston_md_r<0>(addr) : 0x00; else if (m_nr_0a_mf_type != 0b00) data = (BIT(m_port_7ffd_data, 3) << 7) | 0x7f; else @@ -1207,9 +1211,50 @@ u8 specnext_state::mf_port_r(offs_t addr) return data; } -void specnext_state::mf_port_w(offs_t addr, u8 data) +template u8 specnext_state::kempston_md_r(offs_t addr) +{ + const bool mdL_1f_en = m_nr_05_joy0 == 0b101; + const bool mdL_37_en = m_nr_05_joy0 == 0b110; + const bool joyL_1f_en = m_nr_05_joy0 == 0b001 || mdL_1f_en; + const bool joyL_37_en = m_nr_05_joy0 == 0b100 || mdL_37_en; + + const bool mdR_1f_en = m_nr_05_joy1 == 0b101; + const bool mdR_37_en = m_nr_05_joy1 == 0b110; + const bool joyR_1f_en = m_nr_05_joy1 == 0b001 || mdR_1f_en; + const bool joyR_37_en = m_nr_05_joy1 == 0b100 || mdR_37_en; + + if (Joy == 0 && port_1f_io_en() && (joyL_1f_en || joyR_1f_en)) + { + u8 joyL_1f = m_io_joy_left->read(); + if (!mdL_1f_en) joyL_1f &= 0x3f; + if (!joyL_1f_en) joyL_1f &= 0xc0; + + u8 joyR_1f = m_io_joy_right->read(); + if (!mdR_1f_en) joyR_1f &= 0x3f; + if (!joyR_1f_en) joyR_1f &= 0xc0; + + return joyL_1f | joyR_1f; + } + else if (Joy == 1 && port_37_io_en() && (joyL_37_en || joyR_37_en)) + { + u8 joyL_37 = m_io_joy_left->read(); + if (!mdL_37_en) joyL_37 &= 0x3f; + if (!joyL_37_en) joyL_37 &= 0xc0; + + u8 joyR_37 = m_io_joy_right->read(); + if (!mdR_37_en) joyR_37 &= 0x3f; + if (!joyR_37_en) joyR_37 &= 0xc0; + + return joyL_37 | joyR_37; + } + else + { + return 0x00; + } +} + +template void specnext_state::mf_port_w(offs_t addr, u8 data) { - const u8 port = addr & 0xff; u8 port_mf_enable_io_a = 0x3f; u8 port_mf_disable_io_a = 0xbf; if (m_nr_0a_mf_type & 2) @@ -1225,8 +1270,8 @@ void specnext_state::mf_port_w(offs_t addr, u8 data) m_mf->port_mf_enable_rd_w(0); m_mf->port_mf_disable_rd_w(0); - m_mf->port_mf_enable_wr_w(port_multiface_io_en() && (port == port_mf_enable_io_a)); - m_mf->port_mf_disable_wr_w(port_multiface_io_en() && (port == port_mf_disable_io_a)); + m_mf->port_mf_enable_wr_w(port_multiface_io_en() && (Lsb == port_mf_enable_io_a)); + m_mf->port_mf_disable_wr_w(port_multiface_io_en() && (Lsb == port_mf_disable_io_a)); m_mf->clock_w(); bank_update(0, 2); m_mf->port_mf_enable_rd_w(0); @@ -1675,7 +1720,11 @@ u8 specnext_state::reg_r(offs_t nr_register) port_253b_dat = 0;//(i_KBD_EXTENDED_KEYS(12) <<) | (i_KBD_EXTENDED_KEYS(7 downto 2) <<) | i_KBD_EXTENDED_KEYS(0); break; case 0xb2: - port_253b_dat = 0;//(i_JOY_RIGHT(10 downto 8) <<) | (i_JOY_RIGHT(11) <<) | (i_JOY_LEFT(10 downto 8) <<) | i_JOY_LEFT(11); + { + u16 i_joy_left = m_io_joy_left->read(); + u16 i_joy_right = m_io_joy_right->read(); + port_253b_dat = (BIT(i_joy_right, 8, 3) << 5) | (BIT(i_joy_right, 11) << 4) | (BIT(i_joy_left, 8, 3) << 1) | BIT(i_joy_left, 11); + } break; case 0xb8: port_253b_dat = m_nr_b8_divmmc_ep_0; @@ -2733,30 +2782,10 @@ void specnext_state::map_io(address_map &map) map(0x0000, 0x0000).select(0xfffe).rw(FUNC(specnext_state::spectrum_ula_r), FUNC(specnext_state::spectrum_ula_w)); map(0x00ff, 0x00ff).mirror(0xff00).rw(FUNC(specnext_state::port_ff_r), FUNC(specnext_state::port_ff_w)); - map(0x001f, 0x001f).select(0xff00).lrw8(NAME([this](offs_t offset) - { - return mf_port_r(offset | 0x1f); - }), NAME([this](offs_t offset, u8 data) { - mf_port_w(offset | 0x1f, data); - })); - map(0x003f, 0x003f).select(0xff00).lrw8(NAME([this](offs_t offset) - { - return mf_port_r(offset | 0x3f); - }), NAME([this](offs_t offset, u8 data) { - mf_port_w(offset | 0x3f, data); - })); - map(0x009f, 0x009f).select(0xff00).lrw8(NAME([this](offs_t offset) - { - return mf_port_r(offset | 0x8f); - }), NAME([this](offs_t offset, u8 data) { - mf_port_w(offset | 0x8f, data); - })); - map(0x00bf, 0x00bf).select(0xff00).lrw8(NAME([this](offs_t offset) - { - return mf_port_r(offset | 0xbf); - }), NAME([this](offs_t offset, u8 data) { - mf_port_w(offset | 0xbf, data); - })); + map(0x001f, 0x001f).select(0xff00).rw(FUNC(specnext_state::mf_port_r<0x1f>), FUNC(specnext_state::mf_port_w<0x1f>)); + map(0x003f, 0x003f).select(0xff00).rw(FUNC(specnext_state::mf_port_r<0x3f>), FUNC(specnext_state::mf_port_w<0x3f>)); + map(0x009f, 0x009f).select(0xff00).rw(FUNC(specnext_state::mf_port_r<0x9f>), FUNC(specnext_state::mf_port_w<0x9f>)); + map(0x00bf, 0x00bf).select(0xff00).rw(FUNC(specnext_state::mf_port_r<0xbf>), FUNC(specnext_state::mf_port_w<0xbf>)); map(0x0001, 0x0001).mirror(0xfff4).lr8(NAME([this]() { // #bff5 return m_nr_08_psg_turbosound_en ? m_ay_select : 0; @@ -2897,6 +2926,8 @@ void specnext_state::map_io(address_map &map) map(0x0fdf, 0x0fdf).mirror(0xf000).lr8(NAME([this]() -> u8 { return ~m_io_mouse[1]->read(); })); // #ffdf map(0x0adf, 0x0adf).mirror(0xf000).lr8(NAME([this]() -> u8 { return 0x80 | (m_io_mouse[2]->read() & 0x07); })); // #fadf + map(0x0037, 0x0037).mirror(0xff00).r(FUNC(specnext_state::kempston_md_r<1>)); + // TODO resolve conflicts mf+joy+DAC: 1f, 3f //map(0x001f, 0x001f).mirror(0xff00).lr8(NAME([]() -> u8 { return 0x00; /* Joy1,2*/ })).lw8(NAME([this](u8 data) { // if (m_nr_08_dac_en) @@ -2989,6 +3020,34 @@ INPUT_PORTS_START(specnext) PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_BUTTON5) PORT_NAME("Right mouse button") PORT_CODE(MOUSECODE_BUTTON1) PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_BUTTON6) PORT_NAME("Middle mouse button") PORT_CODE(MOUSECODE_BUTTON3) + PORT_START("JOY_LEFT") + PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_PLAYER(1) PORT_CODE(JOYCODE_HAT1RIGHT) PORT_NAME("Joystick (L) Right") PORT_CODE(JOYCODE_X_RIGHT_SWITCH) PORT_8WAY + PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT) PORT_PLAYER(1) PORT_CODE(JOYCODE_HAT1LEFT) PORT_NAME("Joystick (L) Left") PORT_CODE(JOYCODE_X_LEFT_SWITCH) PORT_8WAY + PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN) PORT_PLAYER(1) PORT_CODE(JOYCODE_HAT1DOWN) PORT_NAME("Joystick (L) Down") PORT_CODE(JOYCODE_Y_DOWN_SWITCH) PORT_8WAY + PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP) PORT_PLAYER(1) PORT_CODE(JOYCODE_HAT1UP) PORT_NAME("Joystick (L) Up") PORT_CODE(JOYCODE_Y_UP_SWITCH) PORT_8WAY + PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_PLAYER(1) PORT_CODE(JOYCODE_BUTTON2) PORT_NAME("Joystick (L) B") + PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_BUTTON5) PORT_PLAYER(1) PORT_CODE(JOYCODE_BUTTON5) PORT_NAME("Joystick (L) C") + PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_PLAYER(1) PORT_CODE(JOYCODE_BUTTON1) PORT_NAME("Joystick (L) A") + PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_BUTTON8) PORT_PLAYER(1) PORT_CODE(JOYCODE_BUTTON8) PORT_NAME("Joystick (L) Start") + PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_BUTTON4) PORT_PLAYER(1) PORT_CODE(JOYCODE_BUTTON4) PORT_NAME("Joystick (L) Y") + PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_BUTTON6) PORT_PLAYER(1) PORT_CODE(JOYCODE_BUTTON6) PORT_NAME("Joystick (L) Z") + PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_PLAYER(1) PORT_CODE(JOYCODE_BUTTON3) PORT_NAME("Joystick (L) X") + PORT_BIT(0x800, IP_ACTIVE_HIGH, IPT_BUTTON7) PORT_PLAYER(1) PORT_CODE(JOYCODE_BUTTON7) PORT_NAME("Joystick (L) Mode") + + PORT_START("JOY_RIGHT") + PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_PLAYER(2) PORT_CODE(JOYCODE_HAT1RIGHT) PORT_NAME("Joystick (R) Right") PORT_CODE(JOYCODE_X_RIGHT_SWITCH) PORT_8WAY + PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT) PORT_PLAYER(2) PORT_CODE(JOYCODE_HAT1LEFT) PORT_NAME("Joystick (R) Left") PORT_CODE(JOYCODE_X_LEFT_SWITCH) PORT_8WAY + PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN) PORT_PLAYER(2) PORT_CODE(JOYCODE_HAT1DOWN) PORT_NAME("Joystick (R) Down") PORT_CODE(JOYCODE_Y_DOWN_SWITCH) PORT_8WAY + PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP) PORT_PLAYER(2) PORT_CODE(JOYCODE_HAT1UP) PORT_NAME("Joystick (R) Up") PORT_CODE(JOYCODE_Y_UP_SWITCH) PORT_8WAY + PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_PLAYER(2) PORT_CODE(JOYCODE_BUTTON2) PORT_NAME("Joystick (R) B") + PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_BUTTON5) PORT_PLAYER(2) PORT_CODE(JOYCODE_BUTTON5) PORT_NAME("Joystick (R) C") + PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_PLAYER(2) PORT_CODE(JOYCODE_BUTTON1) PORT_NAME("Joystick (R) A") + PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_BUTTON8) PORT_PLAYER(2) PORT_CODE(JOYCODE_BUTTON8) PORT_NAME("Joystick (R) Start") + PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_BUTTON4) PORT_PLAYER(1) PORT_CODE(JOYCODE_BUTTON4) PORT_NAME("Joystick (R) Y") + PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_BUTTON6) PORT_PLAYER(1) PORT_CODE(JOYCODE_BUTTON6) PORT_NAME("Joystick (R) Z") + PORT_BIT(0x400, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_PLAYER(1) PORT_CODE(JOYCODE_BUTTON3) PORT_NAME("Joystick (R) X") + PORT_BIT(0x800, IP_ACTIVE_HIGH, IPT_BUTTON7) PORT_PLAYER(1) PORT_CODE(JOYCODE_BUTTON7) PORT_NAME("Joystick (R) Mode") + PORT_MODIFY("NMI") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("NMI MF") PORT_CODE(KEYCODE_F12) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(specnext_state::on_mf_nmi), 0) PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("NMI DivMMC") PORT_CODE(KEYCODE_F11) PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(specnext_state::on_divmmc_nmi), 0) From 1c5cb95201180a50427a3d022203675500f5b512 Mon Sep 17 00:00:00 2001 From: Andrei Holub Date: Wed, 12 Nov 2025 19:06:36 -0500 Subject: [PATCH 2/2] more DAC decoding --- src/mame/sinclair/specnext.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/mame/sinclair/specnext.cpp b/src/mame/sinclair/specnext.cpp index 9400c9e1ed69d..471819d0b372c 100644 --- a/src/mame/sinclair/specnext.cpp +++ b/src/mame/sinclair/specnext.cpp @@ -1274,6 +1274,20 @@ template void specnext_state::mf_port_w(offs_t addr, u8 data) m_mf->port_mf_disable_wr_w(port_multiface_io_en() && (Lsb == port_mf_disable_io_a)); m_mf->clock_w(); bank_update(0, 2); + + if (Lsb == 0x1f && m_nr_08_dac_en && port_dac_sd1_ABCD_1f0f4f5f_io_en()) + { + m_dac[0]->data_w(data); + m_dac[1]->data_w(data); + m_dac[2]->data_w(data); + m_dac[3]->data_w(data); + } + else if (Lsb == 0x3f && m_nr_08_dac_en && port_dac_stereo_AD_3f5f_io_en()) + { + m_dac[0]->data_w(data); + m_dac[3]->data_w(data); + } + m_mf->port_mf_enable_rd_w(0); m_mf->port_mf_disable_rd_w(0); } @@ -2928,19 +2942,10 @@ void specnext_state::map_io(address_map &map) map(0x0037, 0x0037).mirror(0xff00).r(FUNC(specnext_state::kempston_md_r<1>)); - // TODO resolve conflicts mf+joy+DAC: 1f, 3f - //map(0x001f, 0x001f).mirror(0xff00).lr8(NAME([]() -> u8 { return 0x00; /* Joy1,2*/ })).lw8(NAME([this](u8 data) { - // if (m_nr_08_dac_en) - // m_dac[0]->data_w(data); - //})); map(0x00f1, 0x00f1).mirror(0xff00).lw8(NAME([this](u8 data) { if (m_nr_08_dac_en) m_dac[0]->data_w(data); })); - //map(0x003f, 0x003f).mirror(0xff00).lw8(NAME([this](u8 data) { - // if (m_nr_08_dac_en) - // m_dac[0]->data_w(data); - //})); map(0x000f, 0x000f).mirror(0xff00).lw8(NAME([this](u8 data) { if (m_nr_08_dac_en) m_dac[1]->data_w(data);