summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author holub <andrei.holub@gmail.com>2025-10-23 03:17:28 -0400
committer GitHub <noreply@github.com>2025-10-23 09:17:28 +0200
commit23261f6bf65ebdac974757ce23ae39b9d91e8bec (patch)
tree57e192c6da04768cb0be6168ecb30bf480b23b1d
parent7ee6b2207a42b477efb3e6a450752cd10a878fc9 (diff)
sinclair/specnext.cpp: Improved interrupts handling (#14399)
-rw-r--r--src/devices/machine/z80ctc.h1
-rw-r--r--src/mame/sinclair/specnext.cpp84
-rw-r--r--src/mame/sinclair/specnext_ctc.cpp29
-rw-r--r--src/mame/sinclair/specnext_ctc.h4
-rw-r--r--src/mame/sinclair/specnext_im2.cpp16
-rw-r--r--src/mame/sinclair/specnext_im2.h6
6 files changed, 106 insertions, 34 deletions
diff --git a/src/devices/machine/z80ctc.h b/src/devices/machine/z80ctc.h
index 0af27a0211c..5d5ee1e1f00 100644
--- a/src/devices/machine/z80ctc.h
+++ b/src/devices/machine/z80ctc.h
@@ -114,6 +114,7 @@ protected:
// internal helpers
u8 channel_int_state(int ch) const noexcept { return m_channel[ch]->m_int_state; }
+ u8 channel_mode(int ch) const noexcept { return m_channel[ch]->m_mode; }
void interrupt_check();
z80ctc_channel_device &channel_config(int ch) { return *m_channel[ch].lookup(); }
diff --git a/src/mame/sinclair/specnext.cpp b/src/mame/sinclair/specnext.cpp
index 918eddc2db5..6493d92ceb0 100644
--- a/src/mame/sinclair/specnext.cpp
+++ b/src/mame/sinclair/specnext.cpp
@@ -19,7 +19,6 @@
#include "emu.h"
-#include "beta_m.h"
#include "screen_ula.h"
#include "spec128.h"
#include "specnext_copper.h"
@@ -45,18 +44,29 @@
#define LOG_IO (1U << 1)
#define LOG_MEM (1U << 2)
#define LOG_COPPER (1U << 3)
+#define LOG_INT (1U << 4)
-//#define VERBOSE ( LOG_GENERAL | LOG_IO | /*LOG_MEM |*/ LOG_COPPER )
+//#define VERBOSE ( LOG_GENERAL /*| LOG_IO | LOG_MEM | LOG_COPPER*/ | LOG_INT )
#include "logmacro.h"
#define LOGIO(...) LOGMASKED(LOG_IO, __VA_ARGS__)
#define LOGMEM(...) LOGMASKED(LOG_MEM, __VA_ARGS__)
#define LOGCOPPER(...) LOGMASKED(LOG_COPPER, __VA_ARGS__)
+#define LOGINT(...) LOGMASKED(LOG_INT, __VA_ARGS__)
namespace {
#define TIMINGS_PERFECT 0
+constexpr u8 INT_PRIORITY_LINE = 0;
+constexpr u8 INT_PRIORITY_UART0_RX = 1;
+constexpr u8 INT_PRIORITY_UART1_RX = 2;
+constexpr u8 INT_PRIORITY_CTC = 3; // 3-10 reserved for 8 chanels, only 4 used atm
+constexpr u8 INT_PRIORITY_ULA = 11;
+constexpr u8 INT_PRIORITY_UART0_TX = 12;
+constexpr u8 INT_PRIORITY_UART1_TX = 13;
+
+
struct video_timings_info {
u16 min_hblank;
u16 int_h;
@@ -100,7 +110,8 @@ public:
, m_view5(*this, "mem_view5")
, m_view6(*this, "mem_view6")
, m_view7(*this, "mem_view7")
- , m_im2(*this, "im2")
+ , m_im2_line(*this, "im2_line")
+ , m_im2_ula(*this, "im2_ula")
, m_copper(*this, "copper")
, m_ctc(*this, "ctc")
, m_dma(*this, "dma")
@@ -185,6 +196,7 @@ private:
INTERRUPT_GEN_MEMBER(line_interrupt);
TIMER_CALLBACK_MEMBER(spi_clock);
void line_irq_adjust();
+ template <unsigned DeviceId> void int_w(int state);
u8 g_machine_id() { return m_io_issue->read() ? MACHINE_NEXT : MACHINE_TBBLUE; }
u8 g_board_issue() { return m_io_issue->read(); }
@@ -263,7 +275,7 @@ private:
bool nr_8f_mapping_mode_pentagon_1024() const { return m_nr_8f_mapping_mode == 0b11; }
bool nr_8f_mapping_mode_pentagon_1024_en() const { return nr_8f_mapping_mode_pentagon_1024() && BIT(~m_port_eff7_data, 2); }
- void nr_c0_im2_vector_w(u8 data) { m_nr_c0_im2_vector = data; m_ctc->write(0, m_nr_c0_im2_vector << 5); m_im2->vector_w(m_nr_c0_im2_vector << 5); }
+ void nr_c0_im2_vector_w(u8 data) { m_nr_c0_im2_vector = data; m_ctc->write(0, m_nr_c0_im2_vector << 5); }
u32 internal_port_enable() const;
bool port_ff_io_en() const { return BIT(internal_port_enable(), 0); }
@@ -319,7 +331,8 @@ private:
memory_bank_creator m_bank_boot_rom;
memory_bank_array_creator<8> m_bank_ram;
memory_view m_view0, m_view1, m_view2, m_view3, m_view4, m_view5, m_view6, m_view7;
- required_device<specnext_im2_device> m_im2;
+ required_device<specnext_im2_device> m_im2_line;
+ required_device<specnext_im2_device> m_im2_ula;
required_device<specnext_copper_device> m_copper;
required_device<specnext_ctc_device> m_ctc;
required_device<specnext_dma_device> m_dma;
@@ -541,6 +554,7 @@ private:
u8 m_nr_fa_xadc_d1;
bool m_pulse_int_n;
+ u16 m_im2_int_status; // u14
u8 m_nr_09_scanlines; // u2
u8 m_eff_nr_03_machine_timing; // u3
@@ -1457,7 +1471,7 @@ u8 specnext_state::reg_r(offs_t nr_register)
port_253b_dat = vpos_to_cvc(m_screen->vpos()) & 0xff;
break;
case 0x20:
- port_253b_dat = 0;//(BIT(im2_int_status, 0) <<) | (BIT(im2_int_status, 11) <<) | (0b00 <<) | BIT(im2_int_status, 3, 4);
+ port_253b_dat = (BIT(m_im2_int_status, INT_PRIORITY_LINE) << 7) | (BIT(m_im2_int_status, INT_PRIORITY_ULA) << 6) | (0b00 << 4) | BIT(m_im2_int_status, INT_PRIORITY_CTC, 4);
break;
case 0x22:
port_253b_dat = ((!m_pulse_int_n) << 7) | (0b0000 << 3) | (port_ff_interrupt_disable() << 2) | (m_nr_22_line_interrupt_en << 1) | BIT(m_nr_23_line_interrupt, 8);
@@ -1686,19 +1700,20 @@ u8 specnext_state::reg_r(offs_t nr_register)
}
break;
case 0xc5:
- port_253b_dat = 0;//ctc_int_en;
+ port_253b_dat = m_ctc->ctrl_int_r();
break;
case 0xc6:
port_253b_dat = (0 << 7) | (m_nr_c6_int_en_2_654 << 4) | (0 << 3) | m_nr_c6_int_en_2_210;
break;
case 0xc8:
- port_253b_dat = (0b000000 << 2);// | (im2_int_status(0) << 1) | im2_int_status(11);
+ port_253b_dat = (0b000000 << 2) | (BIT(m_im2_int_status, INT_PRIORITY_LINE) << 1) | BIT(m_im2_int_status, INT_PRIORITY_ULA);
break;
case 0xc9:
- port_253b_dat = 0;//im2_int_status(10 downto 3);
+ port_253b_dat = BIT(m_im2_int_status, INT_PRIORITY_CTC, 8);
break;
case 0xca:
- port_253b_dat = (0 << 7);// | (im2_int_status(13) << 6) | (im2_int_status(2) << 5) | (im2_int_status(2) << 4) | (0 << 3) | (im2_int_status(12) << 2) | (im2_int_status(1) << 1) | im2_int_status(1);
+ port_253b_dat = (0b0 << 7) | (BIT(m_im2_int_status, INT_PRIORITY_UART1_TX) << 6) | (BIT(m_im2_int_status, INT_PRIORITY_UART1_RX) << 5) | (BIT(m_im2_int_status, INT_PRIORITY_UART1_RX) << 4)
+ | (0b0 << 3) | (BIT(m_im2_int_status, INT_PRIORITY_UART0_TX) << 2) | (BIT(m_im2_int_status, INT_PRIORITY_UART0_RX) << 1) | BIT(m_im2_int_status, INT_PRIORITY_UART0_RX);
break;
case 0xcc:
port_253b_dat = (m_nr_cc_dma_int_en_0_7 << 7) | (0b00000 << 2) | m_nr_cc_dma_int_en_0_10;
@@ -1993,11 +2008,13 @@ void specnext_state::reg_w(offs_t nr_wr_reg, u8 nr_wr_dat)
m_nr_22_line_interrupt_en = BIT(nr_wr_dat, 1);
m_nr_23_line_interrupt = (m_nr_23_line_interrupt & ~0x0100) | (BIT(nr_wr_dat, 0) << 8);
line_irq_adjust();
- port_ff_w((m_port_ff_data & 0xbf) | (BIT(nr_wr_dat, 1) << 6));
+ port_ff_w((m_port_ff_data & 0xbf) | (BIT(nr_wr_dat, 2) << 6));
+ LOGINT("[Line Interrupt Control] ULA: %d; Line: %d; Line#: %03x\n", BIT(~nr_wr_dat, 2), m_nr_22_line_interrupt_en, m_nr_23_line_interrupt);
break;
case 0x23:
m_nr_23_line_interrupt = (m_nr_23_line_interrupt & ~0x00ff) | nr_wr_dat;
line_irq_adjust();
+ LOGINT("[Line Interrupt Value] %03x\n", m_nr_23_line_interrupt);
break;
case 0x26:
m_screen->update_now();
@@ -2307,6 +2324,7 @@ void specnext_state::reg_w(offs_t nr_wr_reg, u8 nr_wr_dat)
m_nr_c0_stackless_nmi = BIT(nr_wr_dat, 3);
m_maincpu->nmi_stackless_w(m_nr_c0_stackless_nmi);
m_nr_c0_int_mode_pulse_0_im2_1 = BIT(nr_wr_dat, 0);
+ LOGINT("[Interrupt Control] Vector: %02x; HW IM2: %d\n", m_nr_c0_im2_vector << 5, m_nr_c0_int_mode_pulse_0_im2_1);
break;
case 0xc2:
m_nr_c2_retn_address_lsb = nr_wr_dat;
@@ -2319,13 +2337,14 @@ void specnext_state::reg_w(offs_t nr_wr_reg, u8 nr_wr_dat)
m_nr_22_line_interrupt_en = BIT(nr_wr_dat, 1);
line_irq_adjust();
port_ff_w((m_port_ff_data & 0xbf) | (BIT(~nr_wr_dat, 0) << 6));
+ LOGINT("[INT EN 0] Line: %d; ULA: %d\n", m_nr_22_line_interrupt_en, BIT(nr_wr_dat, 0));
break;
case 0xc5:
{
u8 active = BIT(nr_wr_dat, 0, 4);
- for (auto ch = 0; ch < 4; ++ch, active >>= 1)
- m_ctc->write(ch, 1 | ((active & 1) ? 0 : 2)); // for inactive: CONTROL + RESET
+ m_ctc->ctrl_int_w(active);
}
+ LOGINT("[INT EN 1] CTC7..0: %02x\n", nr_wr_dat);
break;
case 0xc6:
m_nr_c6_int_en_2_654 = (BIT(nr_wr_dat, 6) << 2) | (BIT(nr_wr_dat, 5) << 1) | BIT(nr_wr_dat, 4);
@@ -2334,9 +2353,11 @@ void specnext_state::reg_w(offs_t nr_wr_reg, u8 nr_wr_dat)
case 0xcc:
m_nr_cc_dma_int_en_0_7 = BIT(nr_wr_dat, 7);
m_nr_cc_dma_int_en_0_10 = BIT(nr_wr_dat, 0, 2);
+ LOGINT("[DMA INT EN 0] NMI: %d; Line: %d; ULA: %d\n", m_nr_cc_dma_int_en_0_7, m_nr_cc_dma_int_en_0_10 >> 1, m_nr_cc_dma_int_en_0_10 & 1);
break;
case 0xcd:
m_nr_cd_dma_int_en_1 = nr_wr_dat;
+ LOGINT("[DMA INT EN 1] CTC7..0: %02x\n", m_nr_cd_dma_int_en_1);
break;
case 0xce:
m_nr_ce_dma_int_en_2_654 = BIT(nr_wr_dat, 4, 3);
@@ -2404,7 +2425,8 @@ void specnext_state::nr_07_cpu_speed_w(u8 data)
m_maincpu->set_clock_scale(1 << m_nr_07_cpu_speed);
m_ctc->set_clock_scale(1 << m_nr_07_cpu_speed);
m_dma->set_clock_scale(1 << m_nr_07_cpu_speed);
- m_im2->set_clock_scale(1 << m_nr_07_cpu_speed);
+ m_im2_line->set_clock_scale(1 << m_nr_07_cpu_speed);
+ m_im2_ula->set_clock_scale(1 << m_nr_07_cpu_speed);
}
void specnext_state::nr_14_global_transparent_rgb_w(u8 data)
@@ -2425,26 +2447,43 @@ void specnext_state::nr_1a_ula_clip_y2_w(u8 data)
static const z80_daisy_config z80_daisy_chain[] =
{
- { "im2" },
+ { "im2_line" },
{ "ctc" },
+ { "im2_ula" },
//{ "dma" },
{ nullptr }
};
TIMER_CALLBACK_MEMBER(specnext_state::irq_on)
{
- m_im2->int_w((m_nr_02_bus_reset || !m_nr_c0_int_mode_pulse_0_im2_1) ? 0xff : (11 << 1));
+ m_im2_ula->int_w((m_nr_02_bus_reset || !m_nr_c0_int_mode_pulse_0_im2_1) ? 0xff : ((m_nr_c0_im2_vector << 5) | (INT_PRIORITY_ULA << 1)));
}
TIMER_CALLBACK_MEMBER(specnext_state::line_irq_on)
{
m_screen->update_now();
- m_im2->int_w((m_nr_02_bus_reset || !m_nr_c0_int_mode_pulse_0_im2_1) ? 0xff : (1 << 1));
+ m_im2_line->int_w((m_nr_02_bus_reset || !m_nr_c0_int_mode_pulse_0_im2_1) ? 0xff : ((m_nr_c0_im2_vector << 5) | (INT_PRIORITY_LINE << 1)));
}
void specnext_state::ctc_irq_on(int state)
{
- m_maincpu->set_input_line(INPUT_LINE_IRQ0, state);
+ specnext_state::int_w<INT_PRIORITY_CTC>(state); // TODO + chanel
+}
+
+template <unsigned DeviceId> void specnext_state::int_w(int state)
+{
+ if (state)
+ {
+ if (!m_im2_int_status)
+ m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
+ m_im2_int_status |= (1 << DeviceId);
+ }
+ else
+ {
+ m_im2_int_status &= ~(1 << DeviceId);
+ if (!m_im2_int_status)
+ m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
+ }
}
INTERRUPT_GEN_MEMBER(specnext_state::specnext_interrupt)
@@ -3167,6 +3206,7 @@ void specnext_state::machine_start()
save_item(NAME(m_nr_f9_xadc_d0));
save_item(NAME(m_nr_fa_xadc_d1));
save_item(NAME(m_pulse_int_n));
+ save_item(NAME(m_im2_int_status));
save_item(NAME(m_nr_09_scanlines));
save_item(NAME(m_eff_nr_03_machine_timing));
save_item(NAME(m_eff_nr_05_5060));
@@ -3328,6 +3368,7 @@ void specnext_state::machine_reset()
//z80_retn_seen_28_d = 0;
//im2_dma_delay = 0;
m_pulse_int_n = 1;
+ m_im2_int_status = 0x00;
m_nr_c2_retn_address_lsb = 0x00;
m_nr_c3_retn_address_msb = 0x00;
//z80_stackless_retn_en = 0;
@@ -3620,8 +3661,11 @@ void specnext_state::tbblue(machine_config &config)
m_maincpu->out_retn_seen_cb().set(FUNC(specnext_state::leave_nmi));
m_maincpu->busack_cb().set(m_dma, FUNC(specnext_dma_device::bai_w));
- SPECNEXT_IM2(config, m_im2, 28_MHz_XTAL / 8);
- m_im2->intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
+ SPECNEXT_IM2(config, m_im2_line, 28_MHz_XTAL / 8);
+ m_im2_line->intr_callback().set(FUNC(specnext_state::int_w<INT_PRIORITY_LINE>));
+
+ SPECNEXT_IM2(config, m_im2_ula, 28_MHz_XTAL / 8);
+ m_im2_ula->intr_callback().set(FUNC(specnext_state::int_w<INT_PRIORITY_ULA>));
SPECNEXT_CTC(config, m_ctc, 28_MHz_XTAL / 8);
m_ctc->zc_callback<0>().set(m_ctc, FUNC(z80ctc_device::trg1));
diff --git a/src/mame/sinclair/specnext_ctc.cpp b/src/mame/sinclair/specnext_ctc.cpp
index 8ac86c0ba31..04f7a1f2019 100644
--- a/src/mame/sinclair/specnext_ctc.cpp
+++ b/src/mame/sinclair/specnext_ctc.cpp
@@ -7,6 +7,7 @@
#include "emu.h"
#include "specnext_ctc.h"
+constexpr u16 CONTROL_WORD = 0x01;
// device type definition
DEFINE_DEVICE_TYPE(SPECNEXT_CTC, specnext_ctc_device, "specnext_ctc", "Spectrum Next CTC")
@@ -22,5 +23,31 @@ int specnext_ctc_device::z80daisy_irq_ack()
int const channel = (z80ctc_device::z80daisy_irq_ack() - m_vector) / 2;
return ((channel > 0) || (channel_int_state(0) == Z80_DAISY_IEO))
? (m_vector + ((channel + 3) << 1))
- : m_vector;
+ : 0xff;
+}
+
+void specnext_ctc_device::ctrl_int_w(u8 ch_mask)
+{
+ for (int ch = 0; ch < 4; ch++)
+ {
+ u8 mode = channel_mode(ch);
+ if (ch_mask & (1 << ch))
+ mode |= 0x80;
+ else
+ mode &= ~0x80;
+
+ write(ch, CONTROL_WORD | mode);
+ }
+}
+
+u8 specnext_ctc_device::ctrl_int_r()
+{
+ u8 int_mode = 0;
+ for (int ch = 0; ch < 4; ch++)
+ {
+ if (channel_mode(ch) & 0x80)
+ int_mode |= (1 << ch);
+ }
+
+ return int_mode;
}
diff --git a/src/mame/sinclair/specnext_ctc.h b/src/mame/sinclair/specnext_ctc.h
index fa8f03f347f..f4c8ef4e5b7 100644
--- a/src/mame/sinclair/specnext_ctc.h
+++ b/src/mame/sinclair/specnext_ctc.h
@@ -13,9 +13,11 @@ class specnext_ctc_device : public z80ctc_device
public:
specnext_ctc_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+ void ctrl_int_w(u8 ch_mask);
+ u8 ctrl_int_r();
+
protected:
virtual int z80daisy_irq_ack() override;
-
};
DECLARE_DEVICE_TYPE(SPECNEXT_CTC, specnext_ctc_device)
diff --git a/src/mame/sinclair/specnext_im2.cpp b/src/mame/sinclair/specnext_im2.cpp
index eeed15077ff..923340d783d 100644
--- a/src/mame/sinclair/specnext_im2.cpp
+++ b/src/mame/sinclair/specnext_im2.cpp
@@ -27,22 +27,24 @@ int specnext_im2_device::z80daisy_irq_state()
int specnext_im2_device::z80daisy_irq_ack()
{
- return m_vector_base | m_vector_lo;
+ m_state |= Z80_DAISY_IEO;
+ return m_vector;
}
void specnext_im2_device::z80daisy_irq_reti()
{
+ m_state &= ~Z80_DAISY_IEO;
}
TIMER_CALLBACK_MEMBER(specnext_im2_device::irq_off)
{
- m_state = 0;
+ m_state &= ~Z80_DAISY_INT;
m_intr_cb(CLEAR_LINE);
}
-void specnext_im2_device::int_w(u8 vector_lo)
+void specnext_im2_device::int_w(u8 vector)
{
- m_vector_lo = vector_lo;
+ m_vector = vector;
m_state = Z80_DAISY_INT;
m_intr_cb(ASSERT_LINE);
m_irq_off_timer->adjust(clocks_to_attotime(32));
@@ -54,8 +56,7 @@ void specnext_im2_device::device_start()
m_irq_off_timer = timer_alloc(FUNC(specnext_im2_device::irq_off), this);
save_item(NAME(m_state));
- save_item(NAME(m_vector_base));
- save_item(NAME(m_vector_lo));
+ save_item(NAME(m_vector));
}
void specnext_im2_device::device_reset()
@@ -63,6 +64,5 @@ void specnext_im2_device::device_reset()
m_irq_off_timer->reset();
m_state = 0;
- m_vector_base = 0;
- m_vector_lo = 0xff;
+ m_vector = 0xff;
}
diff --git a/src/mame/sinclair/specnext_im2.h b/src/mame/sinclair/specnext_im2.h
index e788a6a8a2e..4dee077dfe4 100644
--- a/src/mame/sinclair/specnext_im2.h
+++ b/src/mame/sinclair/specnext_im2.h
@@ -16,8 +16,7 @@ public:
auto intr_callback() { return m_intr_cb.bind(); }
- void vector_w(u8 vector) { m_vector_base = vector; }
- void int_w(u8 vector_lo = 0xff);
+ void int_w(u8 vector = 0xff);
protected:
virtual void device_start() override ATTR_COLD;
@@ -34,8 +33,7 @@ protected:
private:
u8 m_state;
- u8 m_vector_base;
- u8 m_vector_lo;
+ u8 m_vector;
};