sinclair/next/specnext.cpp: Implemented DMA delay (allows to stop DMA in Continious mode for ISR)

This commit is contained in:
Andrei Holub 2026-04-17 08:42:15 -04:00 committed by Andrei I. Holub
parent c65da9b69d
commit fdce3c40fa
6 changed files with 87 additions and 22 deletions

View File

@ -184,7 +184,7 @@ void z80dma_device::device_reset()
m_timer->reset(); m_timer->reset();
m_status = 0; m_status = 0;
m_dma_seq = ~0; m_dma_seq = SEQ_IDLE;
m_rdy = 0; m_rdy = 0;
m_force_ready = 0; m_force_ready = 0;
m_wait = 0; m_wait = 0;
@ -298,6 +298,8 @@ void z80dma_device::disable()
{ {
set_busrq(CLEAR_LINE); set_busrq(CLEAR_LINE);
} }
m_dma_seq = SEQ_IDLE;
LOGDMA("IDLE\n");
} }
void z80dma_device::update_bao() void z80dma_device::update_bao()
@ -607,6 +609,7 @@ TIMER_CALLBACK_MEMBER(z80dma_device::clock_w)
} }
break; break;
case SEQ_IDLE:
default: default:
break; break;
} }
@ -919,6 +922,9 @@ void z80dma_device::bai_w(int state)
{ {
m_busrq_ack = state; m_busrq_ack = state;
update_bao(); update_bao();
if (m_busrq_ack && m_dma_seq == SEQ_IDLE)
set_busrq(CLEAR_LINE);
} }

View File

@ -93,7 +93,8 @@ protected:
enum enum
{ {
SEQ_WAIT_READY = 0, SEQ_IDLE = 0,
SEQ_WAIT_READY,
SEQ_REQUEST_BUS, SEQ_REQUEST_BUS,
SEQ_WAITING_ACK, SEQ_WAITING_ACK,
SEQ_TRANS1_INC_DEC_SOURCE_ADDRESS, SEQ_TRANS1_INC_DEC_SOURCE_ADDRESS,

View File

@ -1523,6 +1523,8 @@ void specnext_state::update_dma_delay()
const u16 dma_int_mask = (m_nr_cc_dma_int_en_0_7 << INT_PRIORITY_NMI) | ((m_nr_cc_dma_int_en_0_10 & 1) << INT_PRIORITY_ULA) const u16 dma_int_mask = (m_nr_cc_dma_int_en_0_7 << INT_PRIORITY_NMI) | ((m_nr_cc_dma_int_en_0_10 & 1) << INT_PRIORITY_ULA)
| (m_nr_cd_dma_int_en_1 << INT_PRIORITY_CTC) | ((m_nr_cc_dma_int_en_0_10 >> 1) << INT_PRIORITY_LINE); | (m_nr_cd_dma_int_en_1 << INT_PRIORITY_CTC) | ((m_nr_cc_dma_int_en_0_10 >> 1) << INT_PRIORITY_LINE);
m_dma->dma_delay_w((m_nr_c0_int_mode_pulse_0_im2_1 && (dma_int_mask & m_im2_int_status)) ? 1 : 0); m_dma->dma_delay_w((m_nr_c0_int_mode_pulse_0_im2_1 && (dma_int_mask & m_im2_int_status)) ? 1 : 0);
if (m_nr_c0_int_mode_pulse_0_im2_1)
LOGINTVVV("DMA delay %d (int_status=%04x, int_mask=%04x)\n", (dma_int_mask & m_im2_int_status) ? 1 : 0, m_im2_int_status, dma_int_mask);
} }
u8 specnext_state::reg_r(offs_t nr_register) u8 specnext_state::reg_r(offs_t nr_register)
@ -2714,19 +2716,44 @@ void specnext_state::irq_w(int state)
{ {
m_maincpu->set_input_line(INPUT_LINE_IRQ0, state); m_maincpu->set_input_line(INPUT_LINE_IRQ0, state);
const std::array<int, 10> states =
{
m_im2_line->z80daisy_irq_state(),
m_im2_uart0_rx->z80daisy_irq_state(),
m_im2_uart1_rx->z80daisy_irq_state(),
m_ctc->z80daisy_chanel_irq_state(0),
m_ctc->z80daisy_chanel_irq_state(1),
m_ctc->z80daisy_chanel_irq_state(2),
m_ctc->z80daisy_chanel_irq_state(3),
m_im2_ula->z80daisy_irq_state(),
m_im2_uart0_tx->z80daisy_irq_state(),
m_im2_uart1_tx->z80daisy_irq_state()
};
const std::array<u16, 10> masks =
{
1 << INT_PRIORITY_LINE,
1 << INT_PRIORITY_UART0_RX,
1 << INT_PRIORITY_UART1_RX,
1 << (INT_PRIORITY_CTC + 0),
1 << (INT_PRIORITY_CTC + 1),
1 << (INT_PRIORITY_CTC + 2),
1 << (INT_PRIORITY_CTC + 3),
1 << INT_PRIORITY_ULA,
1 << INT_PRIORITY_UART0_TX,
1 << INT_PRIORITY_UART1_TX
};
const int tmp = m_im2_int_status; const int tmp = m_im2_int_status;
m_im2_int_status &= 1 << INT_PRIORITY_NMI; m_im2_int_status = 0;
m_im2_int_status |= ((m_im2_uart0_tx->z80daisy_irq_state() & Z80_DAISY_IEO) != 0) << INT_PRIORITY_UART0_TX; for(int i = 0; i < states.size(); ++i)
m_im2_int_status |= ((m_im2_uart1_tx->z80daisy_irq_state() & Z80_DAISY_IEO) != 0) << INT_PRIORITY_UART1_TX; {
m_im2_int_status |= ((m_im2_ula->z80daisy_irq_state() & Z80_DAISY_IEO) != 0) << INT_PRIORITY_ULA; m_im2_int_status |= (states[i] & Z80_DAISY_IEO) ? masks[i] : 0;
m_im2_int_status |= ((m_ctc->z80daisy_chanel_irq_state(3) & Z80_DAISY_IEO) != 0) << (INT_PRIORITY_CTC + 3); if ((states[i] & Z80_DAISY_INT) && !m_im2_int_status) // only highest priority IRQ
m_im2_int_status |= ((m_ctc->z80daisy_chanel_irq_state(2) & Z80_DAISY_IEO) != 0) << (INT_PRIORITY_CTC + 2); m_im2_int_status |= masks[i];
m_im2_int_status |= ((m_ctc->z80daisy_chanel_irq_state(1) & Z80_DAISY_IEO) != 0) << (INT_PRIORITY_CTC + 1); }
m_im2_int_status |= ((m_ctc->z80daisy_chanel_irq_state(0) & Z80_DAISY_IEO) != 0) << (INT_PRIORITY_CTC + 0); m_im2_int_status |= tmp & (1 << INT_PRIORITY_NMI);
m_im2_int_status |= ((m_im2_uart0_rx->z80daisy_irq_state() & Z80_DAISY_IEO) != 0) << INT_PRIORITY_UART0_RX; LOGINTVVV("IRQs: %s %04x -> %04x\n", state ? "+" : "-", tmp, m_im2_int_status);
m_im2_int_status |= ((m_im2_uart1_rx->z80daisy_irq_state() & Z80_DAISY_IEO) != 0) << INT_PRIORITY_UART1_RX;
m_im2_int_status |= ((m_im2_line->z80daisy_irq_state() & Z80_DAISY_IEO) != 0) << INT_PRIORITY_LINE;
LOGINTVVV("IRQ%s: %04x -> %04x\n", state ? "+" : "-", tmp, m_im2_int_status);
update_dma_delay(); update_dma_delay();
} }

View File

@ -42,6 +42,16 @@ specnext_dma_device::specnext_dma_device(const machine_config &mconfig, const ch
{ {
} }
void specnext_dma_device::dma_delay_w(bool dma_delay)
{
if (m_dma_delay && !dma_delay && (m_dma_seq == SEQ_WAIT_READY))
{
set_busrq(ASSERT_LINE);
m_dma_seq = SEQ_WAITING_ACK;
}
m_dma_delay = dma_delay;
}
void specnext_dma_device::reset_byte_counter() void specnext_dma_device::reset_byte_counter()
{ {
m_byte_counter = m_dma_mode ? 0 : 1; m_byte_counter = m_dma_mode ? 0 : 1;
@ -97,8 +107,8 @@ TIMER_CALLBACK_MEMBER(specnext_dma_device::clock_w)
{ {
set_busrq(CLEAR_LINE); set_busrq(CLEAR_LINE);
m_dma_seq = SEQ_WAIT_READY; m_dma_seq = SEQ_WAIT_READY;
return;
} }
return;
} }
} }

View File

@ -13,7 +13,7 @@ public:
specnext_dma_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); specnext_dma_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
void dma_mode_w(bool dma_mode) { m_dma_mode = dma_mode; } void dma_mode_w(bool dma_mode) { m_dma_mode = dma_mode; }
void dma_delay_w(bool dma_delay) { m_dma_delay = dma_delay; } void dma_delay_w(bool dma_delay);
virtual void write(u8 data) override; virtual void write(u8 data) override;

View File

@ -8,6 +8,10 @@
#include "specnext_im2.h" #include "specnext_im2.h"
#define VERBOSE 0
#include "logmacro.h"
// device type definition // device type definition
DEFINE_DEVICE_TYPE(SPECNEXT_IM2, specnext_im2_device, "specnext_im2", "Spectrum Next IM2") DEFINE_DEVICE_TYPE(SPECNEXT_IM2, specnext_im2_device, "specnext_im2", "Spectrum Next IM2")
@ -35,17 +39,34 @@ int specnext_im2_device::z80daisy_irq_ack()
void specnext_im2_device::z80daisy_irq_reti() void specnext_im2_device::z80daisy_irq_reti()
{ {
m_state = 0; if (m_state & Z80_DAISY_IEO)
m_irq_cb(CLEAR_LINE); {
m_state &= ~Z80_DAISY_IEO;
m_irq_cb((m_state & Z80_DAISY_INT) ? ASSERT_LINE : CLEAR_LINE);
}
} }
void specnext_im2_device::irq_w(int state) void specnext_im2_device::irq_w(int state)
{ {
if (state != CLEAR_LINE) if (state == CLEAR_LINE)
m_state = Z80_DAISY_INT; {
m_state = 0;
m_irq_cb(CLEAR_LINE);
}
else if (m_state & Z80_DAISY_IEO)
{
LOG("IM2: Ignoring IRQ while in IEO\n");
}
else else
m_state &= ~Z80_DAISY_INT; {
m_irq_cb(state); // FPGA im2_device in S_ISR cannot transition to S_REQ;
// don't assert INT while being serviced (IEO set), or
// the daisy chain would ACK this device, consuming the
// new INT, and a subsequent RETI would clear IEO while
// the original ISR still runs.
m_state |= Z80_DAISY_INT;
m_irq_cb(ASSERT_LINE);
}
} }