// license:BSD-3-Clause // copyright-holders:Ernesto Corvi, Nicola Salmoria, David Haywood, Vas Crabb #include "emu.h" #include "machine/taito68705interface.h" #include "cpu/z80/z80.h" /* Most Taito 68705s share a similar (often identical) hookup. This file encapsulates that. used by: 40love.cpp - 40love bigevglf.cpp - bigevglf bking.cpp - bking3 bublbobl.cpp - tokio buggychl.cpp - buggychl flstory.cpp - flstory lkage.cpp - lkage lsasquad.cpp - lsasquad, daikaiju matmania.cpp - maniach nycaptor.cpp - nycaptor renegade.cpp - renegade retofinv.cpp - retofinv slapfght.cpp - slapfght xain.cpp - xsleena and the following with slight changes: slapfght.cpp - tigerh (inverted status bits read on port C) arkanoid.cpp - arkanoid (latch control on port C, inputs on port B) taito_l.cpp - puzznic (latch control on port C) not hooked up here, but possible (needs investigating) pitnrun.cpp - have more functionality on portB, currently using 'instant timers' for latches taitosj.cpp - ^^ 68705 sets in Taito drivers that are NOT suitable for hookup here? bublbobl.cpp - bub68705 - this is a bootleg, not an official Taito hookup changela.cpp - changela - looks like an ancestor of arkanoid without automatic semaphores mexico86.cpp - knightb, mexico86 - bootleg 68705s sqix.cpp - hotsmash - kaneko hookup, different from Taito ones. there are other drivers (and games in existing drivers) that could hookup here, but currently lack MCU dumps. */ DEFINE_DEVICE_TYPE(TAITO68705_MCU, taito68705_mcu_device, "taito68705", "Taito MC68705 MCU Interface") DEFINE_DEVICE_TYPE(TAITO68705_MCU_TIGER, taito68705_mcu_tiger_device, "taito68705tiger", "Taito MC68705 MCU Interface (Tiger Heli)") DEFINE_DEVICE_TYPE(ARKANOID_68705P3, arkanoid_68705p3_device, "arkanoid68705p3", "Arkanoid MC68705P3 Interface") DEFINE_DEVICE_TYPE(ARKANOID_68705P5, arkanoid_68705p5_device, "arkanoid68705p5", "Arkanoid MC68705P5 Interface") READ8_MEMBER(taito68705_mcu_device_base::data_r) { // clear MCU semaphore flag and return data u8 const result(m_mcu_latch); if (!machine().side_effects_disabled()) { m_mcu_flag = false; m_semaphore_cb(CLEAR_LINE); } return result; } WRITE8_MEMBER(taito68705_mcu_device_base::data_w) { // set host semaphore flag and latch data if (!m_reset_input) m_host_flag = true; m_host_latch = data; if (m_latch_driven) m_mcu->pa_w(space, 0, data); m_mcu->set_input_line(M68705_IRQ_LINE, m_host_flag ? ASSERT_LINE : CLEAR_LINE); } WRITE_LINE_MEMBER(taito68705_mcu_device_base::reset_w) { m_reset_input = ASSERT_LINE == state; if (CLEAR_LINE != state) { m_host_flag = false; m_mcu_flag = false; m_mcu->set_input_line(M68705_IRQ_LINE, CLEAR_LINE); } m_mcu->set_input_line(INPUT_LINE_RESET, state); } taito68705_mcu_device_base::taito68705_mcu_device_base( machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock) : device_t(mconfig, type, tag, owner, clock) , m_mcu(*this, "mcu") , m_semaphore_cb(*this) , m_latch_driven(false) , m_reset_input(false) , m_host_flag(false) , m_mcu_flag(false) , m_host_latch(0xff) , m_mcu_latch(0xff) , m_pa_output(0xff) { } WRITE8_MEMBER(taito68705_mcu_device_base::mcu_pa_w) { m_pa_output = data; } void taito68705_mcu_device_base::device_start() { m_semaphore_cb.resolve_safe(); save_item(NAME(m_latch_driven)); save_item(NAME(m_reset_input)); save_item(NAME(m_host_flag)); save_item(NAME(m_mcu_flag)); save_item(NAME(m_host_latch)); save_item(NAME(m_mcu_latch)); save_item(NAME(m_pa_output)); m_latch_driven = false; m_reset_input = false; m_host_latch = 0xff; m_mcu_latch = 0xff; m_pa_output = 0xff; } void taito68705_mcu_device_base::device_reset() { m_host_flag = false; m_mcu_flag = false; m_mcu->set_input_line(M68705_IRQ_LINE, CLEAR_LINE); m_semaphore_cb(CLEAR_LINE); } void taito68705_mcu_device_base::latch_control(u8 data, u8 &value, unsigned host_bit, unsigned mcu_bit) { // save this here to simulate latch propagation delays u8 const old_pa_value(pa_value()); // rising edge clears the host semaphore flag if (BIT(data, host_bit)) { m_latch_driven = false; m_mcu->pa_w(m_mcu->space(AS_PROGRAM), 0, 0xff); if (!BIT(value, host_bit)) { m_host_flag = false; m_mcu->set_input_line(M68705_IRQ_LINE, CLEAR_LINE); } } else { m_latch_driven = true; m_mcu->pa_w(m_mcu->space(AS_PROGRAM), 0, m_host_latch); } // PB2 sets the MCU semaphore when low if (!BIT(data, mcu_bit)) { if (!m_reset_input) m_mcu_flag = true; // data is latched on falling edge if (BIT(value, mcu_bit)) m_mcu_latch = old_pa_value; } value = data; if (!BIT(data, mcu_bit) && !m_reset_input) m_semaphore_cb(ASSERT_LINE); } taito68705_mcu_device::taito68705_mcu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : taito68705_mcu_device(mconfig, TAITO68705_MCU, tag, owner, clock) { } taito68705_mcu_device::taito68705_mcu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) : taito68705_mcu_device_base(mconfig, type, tag, owner, clock) , m_aux_strobe_cb(*this) , m_pb_output(0xff) { } MACHINE_CONFIG_START(taito68705_mcu_device::device_add_mconfig) MCFG_DEVICE_ADD("mcu", M68705P5, DERIVED_CLOCK(1, 1)) MCFG_M68705_PORTC_R_CB(READ8(*this, taito68705_mcu_device, mcu_portc_r)) MCFG_M68705_PORTA_W_CB(WRITE8(*this, taito68705_mcu_device, mcu_pa_w)) MCFG_M68705_PORTB_W_CB(WRITE8(*this, taito68705_mcu_device, mcu_portb_w)) MACHINE_CONFIG_END void taito68705_mcu_device::device_start() { taito68705_mcu_device_base::device_start(); m_aux_strobe_cb.resolve_safe(); save_item(NAME(m_pb_output)); m_pb_output = 0xff; } /* * Port B connections: * parts in [ ] are optional (not used by buggychl) * * all bits are logical 1 when read (+5V pullup) * * 0 n.c. * 1 W IRQ ack and enable latch which holds data from main Z80 memory * 2 W loads latch to Z80 * 3 W to Z80 BUSRQ (put it on hold?) * 4 W n.c. * 5 W [selects Z80 memory access direction (0 = write 1 = read)] * 6 W [loads the latch which holds the low 8 bits of the address of * the main Z80 memory location to access] * 7 W [loads the latch which holds the high 8 bits of the address of * the main Z80 memory location to access] */ READ8_MEMBER(taito68705_mcu_device::mcu_portc_r) { // PC0 is the host semaphore flag (active high) // PC1 is the MCU semaphore flag (active low) return (host_flag() ? 0x01 : 0x00) | (mcu_flag() ? 0x00 : 0x02) | 0xfc; } WRITE8_MEMBER(taito68705_mcu_device::mcu_portb_w) { // some games have additional peripherals strobed on falling edge u8 const old_pa_value(pa_value()); u8 const aux_strobes((mem_mask & ~data & m_pb_output) >> 2); // rising edge on PB1 clears the host semaphore flag // PB2 sets the MCU semaphore when low latch_control(data, m_pb_output, 1, 2); // callbacks for other peripherals for (unsigned i = 0; i < 6; ++i) { if (BIT(aux_strobes, i)) m_aux_strobe_cb(i, old_pa_value, 0xff); } } /* The Tiger Heli interface has some extensions, handle them here */ taito68705_mcu_tiger_device::taito68705_mcu_tiger_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : taito68705_mcu_device(mconfig, TAITO68705_MCU_TIGER, tag, owner, clock) { } READ8_MEMBER(taito68705_mcu_tiger_device::mcu_portc_r) { // Tiger Heli has these status bits inverted MCU-side return taito68705_mcu_device::mcu_portc_r(space, offset, mem_mask) ^ 0x03; } // Arkanoid/Puzznic (latch control on PC2 and PC3 instead of PB1 and PB2) arkanoid_mcu_device_base::arkanoid_mcu_device_base( machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock) : taito68705_mcu_device_base(mconfig, type, tag, owner, clock) , m_portb_r_cb(*this) , m_pc_output(0xff) { } READ8_MEMBER(arkanoid_mcu_device_base::mcu_pb_r) { return m_portb_r_cb(space, offset, mem_mask); } READ8_MEMBER(arkanoid_mcu_device_base::mcu_pc_r) { // PC0 is the host semaphore flag (active high) // PC1 is the MCU semaphore flag (active low) return (host_flag() ? 0x01 : 0x00) | (mcu_flag() ? 0x00 : 0x02) | 0xfc; } WRITE8_MEMBER(arkanoid_mcu_device_base::mcu_pc_w) { // rising edge on PC2 clears the host semaphore flag // PC3 sets the MCU semaphore when low latch_control(data, m_pc_output, 2, 3); } void arkanoid_mcu_device_base::device_start() { taito68705_mcu_device_base::device_start(); m_portb_r_cb.resolve_safe(0xff); save_item(NAME(m_pc_output)); m_pc_output = 0xff; } arkanoid_68705p3_device::arkanoid_68705p3_device( machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) : arkanoid_mcu_device_base(mconfig, ARKANOID_68705P3, tag, owner, clock) { } MACHINE_CONFIG_START(arkanoid_68705p3_device::device_add_mconfig) MCFG_DEVICE_ADD("mcu", M68705P3, DERIVED_CLOCK(1, 1)) MCFG_M68705_PORTB_R_CB(READ8(*this, arkanoid_68705p3_device, mcu_pb_r)) MCFG_M68705_PORTC_R_CB(READ8(*this, arkanoid_68705p3_device, mcu_pc_r)) MCFG_M68705_PORTA_W_CB(WRITE8(*this, arkanoid_68705p3_device, mcu_pa_w)) MCFG_M68705_PORTC_W_CB(WRITE8(*this, arkanoid_68705p3_device, mcu_pc_w)) MACHINE_CONFIG_END arkanoid_68705p5_device::arkanoid_68705p5_device( machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) : arkanoid_mcu_device_base(mconfig, ARKANOID_68705P5, tag, owner, clock) { } MACHINE_CONFIG_START(arkanoid_68705p5_device::device_add_mconfig) MCFG_DEVICE_ADD("mcu", M68705P5, DERIVED_CLOCK(1, 1)) MCFG_M68705_PORTB_R_CB(READ8(*this, arkanoid_68705p5_device, mcu_pb_r)) MCFG_M68705_PORTC_R_CB(READ8(*this, arkanoid_68705p5_device, mcu_pc_r)) MCFG_M68705_PORTA_W_CB(WRITE8(*this, arkanoid_68705p5_device, mcu_pa_w)) MCFG_M68705_PORTC_W_CB(WRITE8(*this, arkanoid_68705p5_device, mcu_pc_w)) MACHINE_CONFIG_END