Namco custom chip improvements (#9141)

* namco06: synchronize data writes
* namco06: Improve NMI delay.
* namco5x: use chip select lines, synchronize writes.
* mb88xx: clarify interrupt line is 'logical', not voltage-based
* mb88xx: clear the appropriate interrupt enable flag when taking the int
* galaga: use correct callback for bosco 06xx rw.
* galaga: re-order namco06 callbacks, for consistency.
* galaga: use correct clock for the second 06xx in bosco.
This commit is contained in:
Mike 2022-01-14 18:13:21 -08:00 committed by GitHub
parent 25dce8aa6d
commit a6addb11da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 99 additions and 80 deletions

View File

@ -368,8 +368,10 @@ int mb88_cpu_device::pla( int inA, int inB )
void mb88_cpu_device::execute_set_input(int inputnum, int state)
{
/* on rising edge trigger interrupt */
if ( (m_pio & 0x04) && !m_nf && state != CLEAR_LINE )
/* On rising edge trigger interrupt.
* Note this is a logical level, the actual pin is high-to-low voltage
* triggered. */
if ( (m_pio & INT_CAUSE_EXTERNAL) && !m_nf && state != CLEAR_LINE )
{
m_pending_interrupt |= INT_CAUSE_EXTERNAL;
}
@ -437,6 +439,9 @@ void mb88_cpu_device::update_pio( int cycles )
{
/* if we have a live external source, call the irqcallback */
standard_irq_callback( 0 );
/* The datasheet doesn't mention if the interrupt flag
* is cleared, but it seems to be only for this case. */
m_pio &= ~INT_CAUSE_EXTERNAL;
m_PC = 0x02;
}
else if (m_pending_interrupt & m_pio & INT_CAUSE_TIMER)

View File

@ -56,11 +56,6 @@ WRITE_LINE_MEMBER( namco_52xx_device::reset )
m_cpu->set_input_line(INPUT_LINE_RESET, !state);
}
TIMER_CALLBACK_MEMBER( namco_52xx_device::latch_callback )
{
m_latched_cmd = param;
}
uint8_t namco_52xx_device::K_r()
{
return m_latched_cmd & 0x0f;
@ -108,16 +103,17 @@ void namco_52xx_device::O_w(uint8_t data)
void namco_52xx_device::write(uint8_t data)
{
machine().scheduler().synchronize(timer_expired_delegate(FUNC(namco_52xx_device::latch_callback),this), data);
machine().scheduler().synchronize(timer_expired_delegate(FUNC(namco_52xx_device::write_sync),this), data);
}
// TODO: should use chip_select line for this
m_cpu->pulse_input_line(0, m_irq_duration);
TIMER_CALLBACK_MEMBER( namco_52xx_device::write_sync )
{
m_latched_cmd = param;
}
WRITE_LINE_MEMBER( namco_52xx_device::chip_select )
{
// TODO: broken sound when using this
//m_cpu->set_input_line(0, state);
m_cpu->set_input_line(0, state);
}
TIMER_CALLBACK_MEMBER( namco_52xx_device::external_clock_pulse )
@ -143,7 +139,6 @@ namco_52xx_device::namco_52xx_device(const machine_config &mconfig, const char *
: device_t(mconfig, NAMCO_52XX, tag, owner, clock),
m_cpu(*this, "mcu"),
m_discrete(*this, finder_base::DUMMY_TAG),
m_irq_duration(attotime::from_usec(100)),
m_basenode(0),
m_extclock(0),
m_romread(*this),

View File

@ -16,7 +16,6 @@ public:
void set_extclock(attoseconds_t clk) { m_extclock = clk; }
auto romread_callback() { return m_romread.bind(); }
auto si_callback() { return m_si.bind(); }
namco_52xx_device &set_irq_duration(attotime t) { m_irq_duration = t; return *this; }
DECLARE_WRITE_LINE_MEMBER( reset );
WRITE_LINE_MEMBER( chip_select );
@ -28,7 +27,7 @@ protected:
virtual const tiny_rom_entry *device_rom_region() const override;
virtual void device_add_mconfig(machine_config &config) override;
TIMER_CALLBACK_MEMBER( latch_callback );
TIMER_CALLBACK_MEMBER( write_sync );
TIMER_CALLBACK_MEMBER( external_clock_pulse );
private:
@ -36,7 +35,6 @@ private:
required_device<mb88_cpu_device> m_cpu;
required_device<discrete_device> m_discrete;
attotime m_irq_duration;
int m_basenode;
attoseconds_t m_extclock;
emu_timer *m_extclock_pulse_timer;

View File

@ -52,10 +52,6 @@
#include "emu.h"
#include "namco54.h"
TIMER_CALLBACK_MEMBER( namco_54xx_device::latch_callback )
{
m_latched_cmd = param;
}
WRITE_LINE_MEMBER( namco_54xx_device::reset )
{
@ -92,16 +88,18 @@ void namco_54xx_device::R1_w(uint8_t data)
void namco_54xx_device::write(uint8_t data)
{
machine().scheduler().synchronize(timer_expired_delegate(FUNC(namco_54xx_device::latch_callback),this), data);
// TODO: should use chip_select line for this
m_cpu->pulse_input_line(0, m_irq_duration);
machine().scheduler().synchronize(timer_expired_delegate(FUNC(namco_54xx_device::write_sync),this), data);
}
TIMER_CALLBACK_MEMBER( namco_54xx_device::write_sync )
{
m_latched_cmd = param;
}
WRITE_LINE_MEMBER( namco_54xx_device::chip_select )
{
// TODO: broken sound when using this
//m_cpu->set_input_line(0, state);
m_cpu->set_input_line(0, state);
}
@ -120,7 +118,6 @@ namco_54xx_device::namco_54xx_device(const machine_config &mconfig, const char *
: device_t(mconfig, NAMCO_54XX, tag, owner, clock),
m_cpu(*this, "mcu"),
m_discrete(*this, finder_base::DUMMY_TAG),
m_irq_duration(attotime::from_usec(100)),
m_basenode(0),
m_latched_cmd(0)
{

View File

@ -14,7 +14,6 @@ public:
template <typename T> void set_discrete(T &&tag) { m_discrete.set_tag(std::forward<T>(tag)); }
void set_basenote(int node) { m_basenode = node; }
namco_54xx_device &set_irq_duration(attotime t) { m_irq_duration = t; return *this; }
DECLARE_WRITE_LINE_MEMBER( reset );
WRITE_LINE_MEMBER( chip_select );
@ -26,14 +25,11 @@ protected:
virtual const tiny_rom_entry *device_rom_region() const override;
virtual void device_add_mconfig(machine_config &config) override;
TIMER_CALLBACK_MEMBER( latch_callback );
private:
// internal state
required_device<mb88_cpu_device> m_cpu;
required_device<discrete_device> m_discrete;
attotime m_irq_duration;
int m_basenode;
uint8_t m_latched_cmd;
@ -41,6 +37,7 @@ private:
uint8_t R0_r();
void O_w(uint8_t data);
void R1_w(uint8_t data);
TIMER_CALLBACK_MEMBER( write_sync );
};
DECLARE_DEVICE_TYPE(NAMCO_54XX, namco_54xx_device)

View File

@ -1607,7 +1607,6 @@ void bosco_state::bosco(machine_config &config)
namco_54xx_device &n54xx(NAMCO_54XX(config, "54xx", MASTER_CLOCK/6/2)); /* 1.536 MHz */
n54xx.set_discrete("discrete");
n54xx.set_basenote(NODE_01);
n54xx.set_irq_duration(attotime::from_usec(200));
namco_06xx_device &n06xx_0(NAMCO_06XX(config, "06xx_0", MASTER_CLOCK/6/64));
n06xx_0.set_maincpu(m_maincpu);
@ -1615,18 +1614,19 @@ void bosco_state::bosco(machine_config &config)
n06xx_0.rw_callback<0>().set("51xx", FUNC(namco_51xx_device::rw));
n06xx_0.read_callback<0>().set("51xx", FUNC(namco_51xx_device::read));
n06xx_0.write_callback<0>().set("51xx", FUNC(namco_51xx_device::write));
n06xx_0.read_callback<2>().set("50xx_1", FUNC(namco_50xx_device::read));
n06xx_0.chip_select_callback<2>().set("50xx_1", FUNC(namco_50xx_device::chip_select));
n06xx_0.rw_callback<2>().set("50xx_1", FUNC(namco_50xx_device::rw));
n06xx_0.read_callback<2>().set("50xx_1", FUNC(namco_50xx_device::read));
n06xx_0.write_callback<2>().set("50xx_1", FUNC(namco_50xx_device::write));
n06xx_0.write_callback<3>().set("54xx", FUNC(namco_54xx_device::write));
n06xx_0.chip_select_callback<3>().set("54xx", FUNC(namco_54xx_device::chip_select));
n06xx_0.write_callback<3>().set("54xx", FUNC(namco_54xx_device::write));
namco_06xx_device &n06xx_1(NAMCO_06XX(config, "06xx_1", MASTER_CLOCK/6/64));
// The clock should be hblank, but approx with 512.
namco_06xx_device &n06xx_1(NAMCO_06XX(config, "06xx_1", MASTER_CLOCK/6/512));
n06xx_1.set_maincpu(m_subcpu);
n06xx_1.read_callback<0>().set("50xx_2", FUNC(namco_50xx_device::read));
n06xx_1.chip_select_callback<0>().set("50xx_2", FUNC(namco_50xx_device::chip_select));
n06xx_1.rw_callback<2>().set("50xx_2", FUNC(namco_50xx_device::rw));
n06xx_1.rw_callback<0>().set("50xx_2", FUNC(namco_50xx_device::rw));
n06xx_1.write_callback<0>().set("50xx_2", FUNC(namco_50xx_device::write));
n06xx_1.write_callback<1>().set("52xx", FUNC(namco_52xx_device::write));
n06xx_1.chip_select_callback<1>().set("52xx", FUNC(namco_52xx_device::chip_select));

View File

@ -106,23 +106,16 @@ TIMER_CALLBACK_MEMBER( namco_06xx_device::nmi_generate )
//
// During reads, the first NMI pulse is supressed to give the chip a
// cycle to write.
//
// If the control register is written while CS is asserted, RW won't be
// changed until the next rising edge.
if (m_rw_change && m_next_timer_state)
if (m_next_timer_state)
{
if (!m_rw_stretch)
{
m_rw[0](0, BIT(m_control, 4));
m_rw[1](0, BIT(m_control, 4));
m_rw[2](0, BIT(m_control, 4));
m_rw[3](0, BIT(m_control, 4));
m_rw_change = false;
}
m_rw[0](0, BIT(m_control, 4));
m_rw[1](0, BIT(m_control, 4));
m_rw[2](0, BIT(m_control, 4));
m_rw[3](0, BIT(m_control, 4));
}
if (m_next_timer_state && !m_nmi_stretch )
if (m_next_timer_state && !m_read_stretch)
{
set_nmi(ASSERT_LINE);
}
@ -130,6 +123,7 @@ TIMER_CALLBACK_MEMBER( namco_06xx_device::nmi_generate )
{
set_nmi(CLEAR_LINE);
}
m_read_stretch = false;
m_chipsel[0](0, BIT(m_control, 0) && m_next_timer_state);
m_chipsel[1](0, BIT(m_control, 1) && m_next_timer_state);
@ -137,8 +131,6 @@ TIMER_CALLBACK_MEMBER( namco_06xx_device::nmi_generate )
m_chipsel[3](0, BIT(m_control, 3) && m_next_timer_state);
m_next_timer_state = !m_next_timer_state;
m_nmi_stretch = false;
m_rw_stretch = false;
}
uint8_t namco_06xx_device::data_r(offs_t offset)
@ -161,16 +153,21 @@ uint8_t namco_06xx_device::data_r(offs_t offset)
void namco_06xx_device::data_w(offs_t offset, uint8_t data)
{
machine().scheduler().synchronize(timer_expired_delegate(FUNC(namco_06xx_device::write_sync),this), data);
}
TIMER_CALLBACK_MEMBER( namco_06xx_device::write_sync )
{
if (BIT(m_control, 4))
{
logerror("%s: 06XX '%s' write in read mode %02x\n",machine().describe_context(),tag(),m_control);
return;
}
if (BIT(m_control, 0)) m_write[0](0, data);
if (BIT(m_control, 1)) m_write[1](0, data);
if (BIT(m_control, 2)) m_write[2](0, data);
if (BIT(m_control, 3)) m_write[3](0, data);
if (BIT(m_control, 0)) m_write[0](0, param);
if (BIT(m_control, 1)) m_write[1](0, param);
if (BIT(m_control, 2)) m_write[2](0, param);
if (BIT(m_control, 3)) m_write[3](0, param);
}
@ -181,34 +178,56 @@ uint8_t namco_06xx_device::ctrl_r()
void namco_06xx_device::ctrl_w(uint8_t data)
{
m_control = data;
machine().scheduler().synchronize(timer_expired_delegate(FUNC(namco_06xx_device::ctrl_w_sync),this), data);
}
TIMER_CALLBACK_MEMBER( namco_06xx_device::ctrl_w_sync )
{
m_control = param;
// The upper 3 control bits are the clock divider.
if ((m_control & 0xe0) == 0)
{
m_nmi_timer->adjust(attotime::never);
m_next_timer_state = true;
set_nmi(CLEAR_LINE);
m_chipsel[0](0, CLEAR_LINE);
m_chipsel[1](0, CLEAR_LINE);
m_chipsel[2](0, CLEAR_LINE);
m_chipsel[3](0, CLEAR_LINE);
// Setting this to true makes the next RW change not stretch.
m_next_timer_state = true;
// RW is left as-is
}
else
{
m_rw_stretch = !m_next_timer_state;
m_rw_change = true;
m_next_timer_state = true;
m_nmi_stretch = BIT(m_control, 4);
// NMI is cleared immediately if its to be stretched.
if (m_nmi_stretch) set_nmi(CLEAR_LINE);
// NMI is cleared immediately if this is a read
// It will be supressed the next clock cycle.
if (BIT(m_control, 4))
{
set_nmi(CLEAR_LINE);
m_read_stretch = true;
} else {
m_read_stretch = false;
}
uint8_t num_shifts = (m_control & 0xe0) >> 5;
uint8_t divisor = 1 << num_shifts;
// The next change should happen on the next clock falling edge.
// Xevious' race causes this to bootloopsif it isn't 0.
m_nmi_timer->adjust(attotime::zero, 0, attotime::from_hz(clock() / divisor) / 2);
attotime period = attotime::from_hz(clock() / divisor) / 2;
// This delay should be the next falling clock.
// That's complicated to get, as it's derived from the master
// clock. The CPU uses this same clock, so writes will come at
// a specific pace.
// Instead, just approximate a quarter cycle.
// Xevious is very sensitive to this. It will bootloop if it
// isn't correct.
attotime delay = attotime::from_hz(clock()) / 4; // average of one clock
if (!m_next_timer_state)
{
// NMI is asserted, wait one additional clock to start
m_nmi_timer->adjust(delay + attotime::from_hz(clock() / divisor), 0, period);
} else {
m_nmi_timer->adjust(delay, 0, period);
}
}
}
@ -228,9 +247,7 @@ namco_06xx_device::namco_06xx_device(const machine_config &mconfig, const char *
: device_t(mconfig, NAMCO_06XX, tag, owner, clock)
, m_control(0)
, m_next_timer_state(false)
, m_nmi_stretch(false)
, m_rw_stretch(false)
, m_rw_change(false)
, m_read_stretch(false)
, m_nmicpu(*this, finder_base::DUMMY_TAG)
, m_chipsel(*this)
, m_rw(*this)
@ -255,9 +272,7 @@ void namco_06xx_device::device_start()
save_item(NAME(m_control));
save_item(NAME(m_next_timer_state));
save_item(NAME(m_nmi_stretch));
save_item(NAME(m_rw_stretch));
save_item(NAME(m_rw_change));
save_item(NAME(m_read_stretch));
}
//-------------------------------------------------

View File

@ -33,14 +33,14 @@ private:
void set_nmi(int state);
TIMER_CALLBACK_MEMBER( nmi_generate );
TIMER_CALLBACK_MEMBER( write_sync );
TIMER_CALLBACK_MEMBER( ctrl_w_sync );
// internal state
emu_timer *m_nmi_timer;
uint8_t m_control;
bool m_next_timer_state;
bool m_nmi_stretch;
bool m_rw_stretch;
bool m_rw_change;
bool m_read_stretch;
required_device<cpu_device> m_nmicpu;

View File

@ -159,8 +159,13 @@ uint8_t namco_50xx_device::R2_r()
void namco_50xx_device::O_w(uint8_t data)
{
uint8_t out = (data & 0x0f);
if (data & 0x10)
machine().scheduler().synchronize(timer_expired_delegate(FUNC(namco_50xx_device::O_w_sync),this), data);
}
TIMER_CALLBACK_MEMBER( namco_50xx_device::O_w_sync )
{
uint8_t out = (param & 0x0f);
if (param & 0x10)
m_portO = (m_portO & 0x0f) | (out << 4);
else
m_portO = (m_portO & 0xf0) | (out);

View File

@ -32,6 +32,7 @@ private:
uint8_t m_cmd;
uint8_t m_portO;
TIMER_CALLBACK_MEMBER( O_w_sync );
TIMER_CALLBACK_MEMBER( rw_sync );
TIMER_CALLBACK_MEMBER( write_sync );

View File

@ -130,8 +130,13 @@ uint8_t namco_51xx_device::R3_r()
void namco_51xx_device::O_w(uint8_t data)
{
uint8_t out = (data & 0x0f);
if (data & 0x10)
machine().scheduler().synchronize(timer_expired_delegate(FUNC(namco_51xx_device::O_w_sync),this), data);
}
TIMER_CALLBACK_MEMBER( namco_51xx_device::O_w_sync )
{
uint8_t out = (param & 0x0f);
if (param & 0x10)
m_portO = (m_portO & 0x0f) | (out << 4);
else
m_portO = (m_portO & 0xf0) | (out);

View File

@ -49,6 +49,7 @@ private:
TIMER_CALLBACK_MEMBER( rw_sync );
TIMER_CALLBACK_MEMBER( write_sync );
TIMER_CALLBACK_MEMBER( O_w_sync );
};
DECLARE_DEVICE_TYPE(NAMCO_51XX, namco_51xx_device)