// 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") u8 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; } void taito68705_mcu_device_base::data_w(u8 data) { // 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(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) { } void taito68705_mcu_device_base::mcu_pa_w(u8 data) { 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(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_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) { } void taito68705_mcu_device::device_add_mconfig(machine_config &config) { M68705P5(config, m_mcu, DERIVED_CLOCK(1, 1)); m_mcu->porta_w().set(FUNC(taito68705_mcu_device::mcu_pa_w)); m_mcu->portb_w().set(FUNC(taito68705_mcu_device::mcu_portb_w)); m_mcu->portc_r().set(FUNC(taito68705_mcu_device::mcu_portc_r)); } 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] */ u8 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; } void taito68705_mcu_device::mcu_portb_w(offs_t offset, u8 data, u8 mem_mask) { // 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) { } u8 taito68705_mcu_tiger_device::mcu_portc_r() { // Tiger-Heli has these status bits inverted MCU-side return taito68705_mcu_device::mcu_portc_r() ^ 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) { } u8 arkanoid_mcu_device_base::mcu_pb_r() { return m_portb_r_cb(); } u8 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; } void arkanoid_mcu_device_base::mcu_pc_w(u8 data) { // 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) { } void arkanoid_68705p3_device::device_add_mconfig(machine_config &config) { M68705P3(config, m_mcu, DERIVED_CLOCK(1, 1)); m_mcu->portb_r().set(FUNC(arkanoid_68705p3_device::mcu_pb_r)); m_mcu->portc_r().set(FUNC(arkanoid_68705p3_device::mcu_pc_r)); m_mcu->porta_w().set(FUNC(arkanoid_68705p3_device::mcu_pa_w)); m_mcu->portc_w().set(FUNC(arkanoid_68705p3_device::mcu_pc_w)); } 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) { } void arkanoid_68705p5_device::device_add_mconfig(machine_config &config) { M68705P5(config, m_mcu, DERIVED_CLOCK(1, 1)); m_mcu->portb_r().set(FUNC(arkanoid_68705p5_device::mcu_pb_r)); m_mcu->portc_r().set(FUNC(arkanoid_68705p5_device::mcu_pc_r)); m_mcu->porta_w().set(FUNC(arkanoid_68705p5_device::mcu_pa_w)); m_mcu->portc_w().set(FUNC(arkanoid_68705p5_device::mcu_pc_w)); }