From 0a687da2aed8287a543b1cffd7c3a318a65be674 Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Sun, 4 Apr 2021 01:18:11 +1100 Subject: bus/a2bus updates: [Vas Crabb, Golden Child] * Added Orange Micro Buffered Grappler+ printer interface card. * Synchronise all I/O for Apple Parallel Interface Card. --- src/devices/bus/a2bus/a2pic.cpp | 77 ++-- src/devices/bus/a2bus/a2pic.h | 6 + src/devices/bus/a2bus/grapplerplus.cpp | 685 ++++++++++++++++++++++++++------- src/devices/bus/a2bus/grapplerplus.h | 151 +++++++- src/mame/drivers/apple2.cpp | 3 +- src/mame/drivers/apple2e.cpp | 3 +- src/mame/drivers/apple2gs.cpp | 3 +- 7 files changed, 742 insertions(+), 186 deletions(-) diff --git a/src/devices/bus/a2bus/a2pic.cpp b/src/devices/bus/a2bus/a2pic.cpp index 4912cf53c0d..aaf0503be82 100644 --- a/src/devices/bus/a2bus/a2pic.cpp +++ b/src/devices/bus/a2bus/a2pic.cpp @@ -323,9 +323,51 @@ void a2bus_pic_device::device_reset() WRITE_LINE_MEMBER(a2bus_pic_device::ack_w) { - if (bool(state) != bool(m_ack_in)) + machine().scheduler().synchronize(timer_expired_delegate(FUNC(a2bus_pic_device::set_ack_in), this), state ? 1 : 0); +} + + +WRITE_LINE_MEMBER(a2bus_pic_device::perror_w) +{ + machine().scheduler().synchronize(timer_expired_delegate(FUNC(a2bus_pic_device::set_perror_in), this), state ? 1 : 0); +} + + +WRITE_LINE_MEMBER(a2bus_pic_device::select_w) +{ + machine().scheduler().synchronize(timer_expired_delegate(FUNC(a2bus_pic_device::set_select_in), this), state ? 1 : 0); +} + + +WRITE_LINE_MEMBER(a2bus_pic_device::fault_w) +{ + machine().scheduler().synchronize(timer_expired_delegate(FUNC(a2bus_pic_device::set_fault_in), this), state ? 1 : 0); +} + + + +//---------------------------------------------- +// timer handlers +//---------------------------------------------- + +TIMER_CALLBACK_MEMBER(a2bus_pic_device::release_strobe) +{ + int const state(BIT(~m_input_sw1->read(), 3)); + LOG("Output /STROBE=%d\n", state); + m_printer_conn->write_strobe(state); +} + + + +//---------------------------------------------- +// synchronised inputs +//---------------------------------------------- + +void a2bus_pic_device::set_ack_in(void *ptr, s32 param) +{ + if (u32(param) != m_ack_in) { - m_ack_in = state ? 1U : 0U; + m_ack_in = u8(u32(param)); LOG("/ACK=%u\n", m_ack_in); if (started() && (m_ack_in != BIT(m_input_sw1->read(), 4))) { @@ -336,50 +378,37 @@ WRITE_LINE_MEMBER(a2bus_pic_device::ack_w) } -WRITE_LINE_MEMBER(a2bus_pic_device::perror_w) +void a2bus_pic_device::set_perror_in(void *ptr, s32 param) { - if (bool(state) != bool(m_perror_in)) + if (u32(param) != m_perror_in) { - m_perror_in = state ? 1U : 0U; + m_perror_in = u8(u32(param)); LOG("PAPER EMPTY=%u\n", m_perror_in); } } -WRITE_LINE_MEMBER(a2bus_pic_device::select_w) +void a2bus_pic_device::set_select_in(void *ptr, s32 param) { - if (bool(state) != bool(m_select_in)) + if (u32(param) != m_select_in) { - m_select_in = state ? 1U : 0U; + m_select_in = u8(u32(param)); LOG("SELECT=%u\n", m_select_in); } } -WRITE_LINE_MEMBER(a2bus_pic_device::fault_w) +void a2bus_pic_device::set_fault_in(void *ptr, s32 param) { - if (bool(state) != bool(m_fault_in)) + if (u32(param) != m_fault_in) { - m_fault_in = state ? 1U : 0U; + m_fault_in = u8(u32(param)); LOG("/FAULT=%u\n", m_fault_in); } } -//---------------------------------------------- -// timer handlers -//---------------------------------------------- - -TIMER_CALLBACK_MEMBER(a2bus_pic_device::release_strobe) -{ - int const state(BIT(~m_input_sw1->read(), 3)); - LOG("Output /STROBE=%d\n", state); - m_printer_conn->write_strobe(state); -} - - - //---------------------------------------------- // helpers //---------------------------------------------- diff --git a/src/devices/bus/a2bus/a2pic.h b/src/devices/bus/a2bus/a2pic.h index 027c18611f0..048260c1b60 100644 --- a/src/devices/bus/a2bus/a2pic.h +++ b/src/devices/bus/a2bus/a2pic.h @@ -91,6 +91,12 @@ private: // timer handlers TIMER_CALLBACK_MEMBER(release_strobe); + // synchronised inputs + void set_ack_in(void *ptr, s32 param); + void set_perror_in(void *ptr, s32 param); + void set_select_in(void *ptr, s32 param); + void set_fault_in(void *ptr, s32 param); + // helpers void reset_mode(); void start_strobe(); diff --git a/src/devices/bus/a2bus/grapplerplus.cpp b/src/devices/bus/a2bus/grapplerplus.cpp index 480cfa1c8f9..8b49bdb3ba5 100644 --- a/src/devices/bus/a2bus/grapplerplus.cpp +++ b/src/devices/bus/a2bus/grapplerplus.cpp @@ -1,5 +1,10 @@ // license:BSD-3-Clause // copyright-holders:Vas Crabb +/* + * TODO for Buffered Grappler+: + * - /RST does not reset the MCU, it’s connected to P27 + * - On the schematic, P22 is pulled up, but the program checks it - perhaps a test mode? + */ #include "emu.h" #include "grapplerplus.h" @@ -22,6 +27,18 @@ ROM_START(grapplerplus) ROM_END +ROM_START(bufgrapplerplus) + // TODO: get proper ROM labels and confirm contents + // despite having the same version string, this differs from the Grapper+ 3.2.u9 ROM + + ROM_REGION(0x1000, "rom", 0) + ROM_LOAD( "3.2.u18", 0x0000, 0x1000, CRC(cd07c7ef) SHA1(6c2f1375b5df6bb65dfca1444c064661242fef1a) ) + + ROM_REGION(0x0400, "mcu", 0) + ROM_LOAD( "8048.u10", 0x0000, 0x0400, CRC(f60c14a9) SHA1(e7d87dd2f7af42f0eed776b839dea0a351ab67bc) ) +ROM_END + + INPUT_PORTS_START(grapplerplus) PORT_START("S1") PORT_DIPNAME(0x07, 0x00, "Printer Type") PORT_DIPLOCATION("S1:4,3,2") @@ -33,7 +50,7 @@ INPUT_PORTS_START(grapplerplus) PORT_DIPSETTING( 0x06, "Okidata 84 w/o Step II Graphics") PORT_DIPSETTING( 0x05, "Apple Dot Matrix") PORT_DIPSETTING( 0x07, "invalid") - PORT_DIPNAME(0x08, 0x08, "Most Significant Bit") PORT_DIPLOCATION("S1:1") PORT_CHANGED_MEMBER(DEVICE_SELF, a2bus_grapplerplus_device, sw_msb, 0) + PORT_DIPNAME(0x08, 0x08, "Most Significant Bit") PORT_DIPLOCATION("S1:1") PORT_CHANGED_MEMBER(DEVICE_SELF, a2bus_grapplerplus_device_base, sw_msb, 0) PORT_DIPSETTING( 0x08, "Software Control") PORT_DIPSETTING( 0x00, "Not Transmitted") INPUT_PORTS_END @@ -43,70 +60,85 @@ INPUT_PORTS_END DEFINE_DEVICE_TYPE(A2BUS_GRAPPLERPLUS, a2bus_grapplerplus_device, "a2grapplerplus", "Orange Micro Grappler+ Printer Interface") +DEFINE_DEVICE_TYPE(A2BUS_BUFGRAPPLERPLUS, a2bus_buf_grapplerplus_device, "a2bufgrapplerplus", "Orange Micro Buffered Grappler+ Printer Interface") -a2bus_grapplerplus_device::a2bus_grapplerplus_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) : - device_t(mconfig, A2BUS_GRAPPLERPLUS, tag, owner, clock), +//============================================================== +// Grappler+ base +//============================================================== + +a2bus_grapplerplus_device_base::a2bus_grapplerplus_device_base(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock) : + device_t(mconfig, type, tag, owner, clock), device_a2bus_card_interface(mconfig, *this), m_printer_conn(*this, "prn"), m_printer_out(*this, "prn_out"), m_s1(*this, "S1"), m_rom(*this, "rom"), - m_strobe_timer(nullptr), m_rom_bank(0x0000U), - m_data_latch(0xffU), - m_ack_latch(0x01U), - m_irq_disable(1U), - m_irq(0x00U), - m_next_strobe(1U), + m_ack_latch(1U), m_ack_in(1U), - m_busy_in(0x8U), - m_pe_in(0x04U), - m_slct_in(0x02U) + m_busy_in(1U), + m_pe_in(1U), + m_slct_in(1U) { } -//---------------------------------------------- -// DIP switch handlers -//---------------------------------------------- +//-------------------------------------------------- +// device_t implementation +//-------------------------------------------------- -INPUT_CHANGED_MEMBER(a2bus_grapplerplus_device::sw_msb) +void a2bus_grapplerplus_device_base::device_add_mconfig(machine_config &config) { - if (BIT(m_data_latch, 7)) - m_printer_out->write(m_data_latch & (BIT(m_s1->read(), 3) ? 0xffU : 0x7fU)); + CENTRONICS(config, m_printer_conn, centronics_devices, "printer"); + m_printer_conn->busy_handler().set(FUNC(a2bus_grapplerplus_device_base::busy_w)); + m_printer_conn->perror_handler().set(FUNC(a2bus_grapplerplus_device_base::pe_w)); + m_printer_conn->select_handler().set(FUNC(a2bus_grapplerplus_device_base::slct_w)); + + OUTPUT_LATCH(config, m_printer_out); + m_printer_conn->set_output_latch(*m_printer_out); +} + + +ioport_constructor a2bus_grapplerplus_device_base::device_input_ports() const +{ + return INPUT_PORTS_NAME(grapplerplus); } +void a2bus_grapplerplus_device_base::device_start() +{ + save_item(NAME(m_rom_bank)); + save_item(NAME(m_ack_latch)); + save_item(NAME(m_ack_in)); + save_item(NAME(m_busy_in)); + save_item(NAME(m_pe_in)); + save_item(NAME(m_slct_in)); +} -//---------------------------------------------- -// device_a2bus_card_interface implementation -//---------------------------------------------- -u8 a2bus_grapplerplus_device::read_c0nx(u8 offset) +void a2bus_grapplerplus_device_base::device_reset() { - return - m_irq | - ((m_s1->read() & 0x07U) << 4) | - m_busy_in | - m_pe_in | - m_slct_in | - m_ack_latch; + m_ack_latch = 1U; } -void a2bus_grapplerplus_device::write_c0nx(u8 offset, u8 data) + +//-------------------------------------------------- +// device_a2bus_card_interface implementation +//-------------------------------------------------- + +void a2bus_grapplerplus_device_base::write_c0nx(u8 offset, u8 data) { LOG("Write C0n%01X=%02X\n", offset, data); if (!(offset & 0x03U)) // !A0 && !A1 - write data { - // latch output data - remember MSB can be forced low by DIP switch + // latch output data LOG("Latch data %02X\n", data); - m_data_latch = data; - m_printer_out->write(data & (BIT(m_s1->read(), 3) ? 0xffU : 0x7fU)); + data_latched(data); // clearing the ACK latch will acknowledge an interrupt if (m_ack_in) @@ -115,27 +147,13 @@ void a2bus_grapplerplus_device::write_c0nx(u8 offset, u8 data) LOG("Clearing acknowledge latch\n"); else LOG("Previous data not acknowledged\n"); - m_ack_latch = 0x00U; - if (m_irq) - { - assert(!m_irq_disable); - LOG("Releasing slot IRQ\n"); - m_irq = 0x00U; - lower_slot_irq(); - } + m_ack_latch = 0U; + ack_latch_cleared(); } else { LOG("/ACK asserted, not clearing acknowledge latch\n"); } - - // generate strobe pulse after one clock cycle - m_next_strobe = 0U; - if (!m_strobe_timer->enabled()) - { - LOG("Start strobe timer\n"); - m_strobe_timer->adjust(attotime::from_ticks(1, clock())); - } } if (BIT(offset, 0)) // A0 - select high ROM bank @@ -146,6 +164,168 @@ void a2bus_grapplerplus_device::write_c0nx(u8 offset, u8 data) LOG("High ROM bank already selected\n"); m_rom_bank = 0x0800U; } +} + + +u8 a2bus_grapplerplus_device_base::read_cnxx(u8 offset) +{ + if (!machine().side_effects_disabled()) + { + if (m_rom_bank) + LOG("Select low ROM bank\n"); + m_rom_bank = 0x0000U; + } + return m_rom[(!m_ack_latch && BIT(offset, 7)) ? (offset & 0xbfU) : offset]; +} + + +void a2bus_grapplerplus_device_base::write_cnxx(u8 offset, u8 data) +{ + LOG("Write Cn%02X=%02X (bus conflict)\n", offset, data); + + if (m_rom_bank) + LOG("Select low ROM bank\n"); + m_rom_bank = 0x0000U; +} + + +u8 a2bus_grapplerplus_device_base::read_c800(u16 offset) +{ + return m_rom[(offset & 0x07ffU) | m_rom_bank]; +} + + + +//-------------------------------------------------- +// printer status inputs +//-------------------------------------------------- + +WRITE_LINE_MEMBER(a2bus_grapplerplus_device_base::ack_w) +{ + machine().scheduler().synchronize(timer_expired_delegate(FUNC(a2bus_grapplerplus_device_base::set_ack_in), this), state ? 1 : 0); +} + + +WRITE_LINE_MEMBER(a2bus_grapplerplus_device_base::busy_w) +{ + machine().scheduler().synchronize(timer_expired_delegate(FUNC(a2bus_grapplerplus_device_base::set_busy_in), this), state ? 1 : 0); +} + + +WRITE_LINE_MEMBER(a2bus_grapplerplus_device_base::pe_w) +{ + machine().scheduler().synchronize(timer_expired_delegate(FUNC(a2bus_grapplerplus_device_base::set_pe_in), this), state ? 1 : 0); +} + + +WRITE_LINE_MEMBER(a2bus_grapplerplus_device_base::slct_w) +{ + machine().scheduler().synchronize(timer_expired_delegate(FUNC(a2bus_grapplerplus_device_base::set_slct_in), this), state ? 1 : 0); +} + + + +//-------------------------------------------------- +// synchronised printer status inputs +//-------------------------------------------------- + +void a2bus_grapplerplus_device_base::set_ack_in(void *ptr, s32 param) +{ + if (u32(param) != m_ack_in) + { + LOG("/ACK=%d\n", param); + m_ack_in = u8(u32(param)); + if (!param) + { + if (!m_ack_latch) + LOG("Set acknowledge latch\n"); + else + LOG("No data written since previous acknowledge\n"); + m_ack_latch = 1U; + ack_latch_set(); + } + } +} + + +void a2bus_grapplerplus_device_base::set_busy_in(void *ptr, s32 param) +{ + if (u32(param) != m_busy_in) + { + LOG("BUSY=%d\n", param); + m_busy_in = u8(u32(param)); + } +} + + +void a2bus_grapplerplus_device_base::set_pe_in(void *ptr, s32 param) +{ + if (u32(param) != m_pe_in) + { + LOG("PAPER EMPTY=%d\n", param); + m_pe_in = u8(u32(param)); + } +} + + +void a2bus_grapplerplus_device_base::set_slct_in(void *ptr, s32 param) +{ + if (u32(param) != m_slct_in) + { + LOG("SELECT=%d\n", state); + m_slct_in = u8(u32(param)); + } +} + + + +//============================================================== +// Grappler+ implementation +//============================================================== + +a2bus_grapplerplus_device::a2bus_grapplerplus_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) : + a2bus_grapplerplus_device_base(mconfig, A2BUS_GRAPPLERPLUS, tag, owner, clock), + m_strobe_timer(nullptr), + m_data_latch(0xffU), + m_irq_disable(1U), + m_irq(0x00U), + m_next_strobe(1U) +{ +} + + + +//-------------------------------------------------- +// DIP switch handlers +//-------------------------------------------------- + +INPUT_CHANGED_MEMBER(a2bus_grapplerplus_device::sw_msb) +{ + if (BIT(m_data_latch, 7)) + m_printer_out->write(m_data_latch & (BIT(m_s1->read(), 3) ? 0xffU : 0x7fU)); +} + + + +//-------------------------------------------------- +// device_a2bus_card_interface implementation +//-------------------------------------------------- + +u8 a2bus_grapplerplus_device::read_c0nx(u8 offset) +{ + return + m_irq | + ((m_s1->read() & 0x07U) << 4) | + (busy_in() << 3) | + (pe_in() << 2) | + (slct_in() << 1) | + ack_latch(); +} + + +void a2bus_grapplerplus_device::write_c0nx(u8 offset, u8 data) +{ + a2bus_grapplerplus_device_base::write_c0nx(offset, data); if (BIT(offset, 1)) // A1 - disable interrupt { @@ -156,7 +336,7 @@ void a2bus_grapplerplus_device::write_c0nx(u8 offset, u8 data) m_irq_disable = 1U; if (m_irq) { - assert(m_ack_latch); + assert(ack_latch()); LOG("Releasing slot IRQ\n"); m_irq = 0x00U; lower_slot_irq(); @@ -169,7 +349,7 @@ void a2bus_grapplerplus_device::write_c0nx(u8 offset, u8 data) else LOG("Interrupt request already enabled\n"); m_irq_disable = 0U; - if (m_ack_latch && !m_irq) + if (ack_latch() && !m_irq) { LOG("Asserting slot IRQ\n"); m_irq = 0x80U; @@ -179,169 +359,394 @@ void a2bus_grapplerplus_device::write_c0nx(u8 offset, u8 data) } -u8 a2bus_grapplerplus_device::read_cnxx(u8 offset) + +//-------------------------------------------------- +// device_t implementation +//-------------------------------------------------- + +tiny_rom_entry const *a2bus_grapplerplus_device::device_rom_region() const +{ + return ROM_NAME(grapplerplus); +} + + +void a2bus_grapplerplus_device::device_add_mconfig(machine_config &config) { - if (!machine().side_effects_disabled()) + a2bus_grapplerplus_device_base::device_add_mconfig(config); + + m_printer_conn->ack_handler().set(FUNC(a2bus_grapplerplus_device::ack_w)); +} + + +void a2bus_grapplerplus_device::device_start() +{ + a2bus_grapplerplus_device_base::device_start(); + + m_strobe_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(a2bus_grapplerplus_device::update_strobe), this)); + + m_next_strobe = 1U; + + save_item(NAME(m_data_latch)); + save_item(NAME(m_irq_disable)); + save_item(NAME(m_irq)); + save_item(NAME(m_next_strobe)); + + m_strobe_timer->adjust(attotime::from_ticks(7, clock())); +} + + +void a2bus_grapplerplus_device::device_reset() +{ + a2bus_grapplerplus_device_base::device_reset(); + + m_irq_disable = 1U; + if (m_irq) { - if (m_rom_bank) - LOG("Select low ROM bank\n"); - m_rom_bank = 0x0000U; + m_irq = 0x00U; + lower_slot_irq(); } - return m_rom[(!m_ack_latch && BIT(offset, 7)) ? (offset & 0xbfU) : offset]; } -void a2bus_grapplerplus_device::write_cnxx(u8 offset, u8 data) + +//-------------------------------------------------- +// a2bus_grapplerplus_device_base implementation +//-------------------------------------------------- + +void a2bus_grapplerplus_device::data_latched(u8 data) { - LOG("Write Cn%02X=%02X (bus conflict)\n", offset, data); + // remember MSB can be forced low by DIP switch + m_data_latch = data; + m_printer_out->write(m_data_latch & (BIT(m_s1->read(), 3) ? 0xffU : 0x7fU)); - if (m_rom_bank) - LOG("Select low ROM bank\n"); - m_rom_bank = 0x0000U; + // generate strobe pulse after one clock cycle + m_next_strobe = 0U; + if (!m_strobe_timer->enabled()) + { + LOG("Start strobe timer\n"); + m_strobe_timer->adjust(attotime::from_ticks(7, clock())); + } } -u8 a2bus_grapplerplus_device::read_c800(u16 offset) +void a2bus_grapplerplus_device::ack_latch_set() { - return m_rom[(offset & 0x07ffU) | m_rom_bank]; + if (!m_irq_disable && !m_irq) + { + LOG("Asserting slot IRQ\n"); + m_irq = 0x80U; + raise_slot_irq(); + } +} + + +void a2bus_grapplerplus_device::ack_latch_cleared() +{ + if (m_irq) + { + assert(!m_irq_disable); + LOG("Releasing slot IRQ\n"); + m_irq = 0x00U; + lower_slot_irq(); + } } -//---------------------------------------------- -// device_t implementation -//---------------------------------------------- +//-------------------------------------------------- +// timer handlers +//-------------------------------------------------- -tiny_rom_entry const *a2bus_grapplerplus_device::device_rom_region() const +TIMER_CALLBACK_MEMBER(a2bus_grapplerplus_device::update_strobe) { - return ROM_NAME(grapplerplus); + LOG("Output /STROBE=%u\n", m_next_strobe); + m_printer_conn->write_strobe(m_next_strobe); + if (!m_next_strobe) + { + LOG("Start strobe timer\n"); + m_next_strobe = 1U; + m_strobe_timer->adjust(attotime::from_ticks(7, clock())); + } } -void a2bus_grapplerplus_device::device_add_mconfig(machine_config &config) + +//============================================================== +// Buffered Grappler+ implementation +//============================================================== + +a2bus_buf_grapplerplus_device::a2bus_buf_grapplerplus_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) : + a2bus_grapplerplus_device_base(mconfig, A2BUS_BUFGRAPPLERPLUS, tag, owner, clock), + m_mcu(*this, "mcu"), + m_ram(), + m_ram_row(0xff00), + m_mcu_p2(0xffU), + m_data_latch(0xffU), + m_ibusy(1U), + m_buf_ack_latch(1U), + m_buf_ack_in(1U) { - CENTRONICS(config, m_printer_conn, centronics_devices, "printer"); - m_printer_conn->ack_handler().set(FUNC(a2bus_grapplerplus_device::ack_w)); - m_printer_conn->busy_handler().set(FUNC(a2bus_grapplerplus_device::busy_w)); - m_printer_conn->perror_handler().set(FUNC(a2bus_grapplerplus_device::pe_w)); - m_printer_conn->select_handler().set(FUNC(a2bus_grapplerplus_device::slct_w)); +} - OUTPUT_LATCH(config, m_printer_out); - m_printer_conn->set_output_latch(*m_printer_out); + + +//-------------------------------------------------- +// device_a2bus_card_interface implementation +//-------------------------------------------------- + +u8 a2bus_buf_grapplerplus_device::read_c0nx(u8 offset) +{ + return + ((m_s1->read() & 0x0fU) << 4) | + (m_ibusy << 3) | + (pe_in() << 2) | + (slct_in() << 1) | + ack_latch(); } -ioport_constructor a2bus_grapplerplus_device::device_input_ports() const + +//-------------------------------------------------- +// device_t implementation +//-------------------------------------------------- + +tiny_rom_entry const *a2bus_buf_grapplerplus_device::device_rom_region() const { - return INPUT_PORTS_NAME(grapplerplus); + return ROM_NAME(bufgrapplerplus); } -void a2bus_grapplerplus_device::device_start() +void a2bus_buf_grapplerplus_device::device_add_mconfig(machine_config &config) { - m_strobe_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(a2bus_grapplerplus_device::update_strobe), this)); + a2bus_grapplerplus_device_base::device_add_mconfig(config); - m_next_strobe = 1U; + m_printer_conn->ack_handler().set(FUNC(a2bus_buf_grapplerplus_device::buf_ack_w)); - save_item(NAME(m_rom_bank)); - save_item(NAME(m_data_latch)); - save_item(NAME(m_ack_latch)); - save_item(NAME(m_irq_disable)); - save_item(NAME(m_irq)); - save_item(NAME(m_next_strobe)); - save_item(NAME(m_ack_in)); - save_item(NAME(m_busy_in)); - save_item(NAME(m_pe_in)); - save_item(NAME(m_slct_in)); + I8048(config, m_mcu, DERIVED_CLOCK(1, 1)); + m_mcu->set_addrmap(AS_IO, &a2bus_buf_grapplerplus_device::mcu_io); + m_mcu->p2_out_cb().set(FUNC(a2bus_buf_grapplerplus_device::mcu_p2_w)); + m_mcu->t0_in_cb().set([this] () { return busy_in(); }); + m_mcu->t1_in_cb().set([this] () { return m_buf_ack_latch; }); + m_mcu->bus_in_cb().set(FUNC(a2bus_buf_grapplerplus_device::mcu_bus_r)); + m_mcu->bus_out_cb().set(FUNC(a2bus_buf_grapplerplus_device::mcu_bus_w)); +} - m_strobe_timer->adjust(attotime::from_ticks(1, clock())); + +void a2bus_buf_grapplerplus_device::device_start() +{ + a2bus_grapplerplus_device_base::device_start(); + + m_ram = std::make_unique(0x10000); + + save_pointer(NAME(m_ram), 0x10000); + save_item(NAME(m_ram_row)); + save_item(NAME(m_mcu_p2)); + save_item(NAME(m_data_latch)); + save_item(NAME(m_ibusy)); + save_item(NAME(m_buf_ack_latch)); + save_item(NAME(m_buf_ack_in)); } -void a2bus_grapplerplus_device::device_reset() + +//-------------------------------------------------- +// a2bus_grapplerplus_device_base implementation +//-------------------------------------------------- + +void a2bus_buf_grapplerplus_device::data_latched(u8 data) { - m_ack_latch = 0x01U; - m_irq_disable = 1U; - if (m_irq) - { - m_irq = 0x00U; - lower_slot_irq(); - } + // IBUSY is exposed on C0nX + if (!m_ibusy) + LOG("Setting IBUSY\n"); + else + LOG("IBUSY already set\n"); + m_ibusy = 1U; + + // these signals cross executable device domains + machine().scheduler().synchronize(timer_expired_delegate(FUNC(a2bus_buf_grapplerplus_device::set_buf_data), this), int(unsigned(data))); + m_mcu->set_input_line(MCS48_INPUT_IRQ, ASSERT_LINE); } -//---------------------------------------------- +//-------------------------------------------------- // printer status inputs -//---------------------------------------------- +//-------------------------------------------------- + +DECLARE_WRITE_LINE_MEMBER(a2bus_buf_grapplerplus_device::buf_ack_w) +{ + machine().scheduler().synchronize(timer_expired_delegate(FUNC(a2bus_buf_grapplerplus_device::set_buf_ack_in), this), state ? 1 : 0); +} + + -WRITE_LINE_MEMBER(a2bus_grapplerplus_device::ack_w) +//-------------------------------------------------- +// MCU I/O handlers +//-------------------------------------------------- + +void a2bus_buf_grapplerplus_device::mcu_io(address_map &map) { - if (bool(state) != bool(m_ack_in)) + map(0x00, 0xff).nopr(); // read to put the BUS lines in high-impedance state before a real read using INS +} + + +void a2bus_buf_grapplerplus_device::mcu_p2_w(u8 data) +{ + //P22 = pulled up + //P27 = /RST + + // check for changed bits + u8 const diff(data ^ m_mcu_p2); + m_mcu_p2 = data; + + // P20 enables /CAS on /RD or /WR + if (BIT(diff, 0)) + LOG("RAM EN=%u\n", BIT(data, 0)); + + // row address strobe + if (BIT(diff, 1)) { - LOG("/ACK=%d\n", state); - m_ack_in = state ? 1U : 0U; - if (!state) + if (!BIT(data, 1)) { - if (!m_ack_latch) - LOG("Set acknowledge latch\n"); + LOG("Row address %02X\n", m_mcu->p1_r()); + m_ram_row = u16(m_mcu->p1_r()) << 8; + } + else + { + LOG("Released /RAS\n"); + } + } + + // P23 is the /IOEN signal + if (BIT(diff, 3)) + LOG("/IOEN=%u\n", BIT(data, 3)); + + // P24 allows fast DRAM refresh using ALE for /RAS + if (BIT(diff, 4)) + LOG(BIT(data, 4) ? "Start DRAM refresh\n" : "End DRAM refresh\n"); + + // P25 is the strobe output and clears the acknowledge latch + if (BIT(diff, 5)) + { + if (BIT(data, 5)) + { + if (m_buf_ack_in) + { + if (m_buf_ack_latch) + LOG("T1 already set\n"); + else + LOG("Setting T1\n"); + m_buf_ack_latch = 1U; + } else - LOG("No data written since previous acknowledge\n"); - m_ack_latch = 0x01U; - if (!m_irq_disable && !m_irq) { - LOG("Asserting slot IRQ\n"); - m_irq = 0x80U; - raise_slot_irq(); + LOG("/ACK asserted, not setting T1\n"); } } + LOG("Output /STROBE=%u\n", BIT(data, 5)); + m_printer_conn->write_strobe(BIT(data, 5)); } } -WRITE_LINE_MEMBER(a2bus_grapplerplus_device::busy_w) +u8 a2bus_buf_grapplerplus_device::mcu_bus_r() { - if (bool(state) != bool(m_busy_in)) + u8 result(0xffU); + + // RAM EN enables RAM + if (BIT(m_mcu_p2, 0)) + { + if (!BIT(m_mcu_p2, 1)) + { + u16 const addr(m_ram_row | u16(m_mcu->p1_r())); + LOG("Read RAM @%04X=%02X\n", addr, m_ram[addr]); + result &= m_ram[addr]; + } + else + { + LOG("/RD asserted /CAS while /RAS not asserted\n"); + } + } + + // /IOEN enables the data latch, pulses /IACK and clears IBUSY on /RD + if (!BIT(m_mcu_p2, 3)) { - LOG("BUSY=%d\n", state); - m_busy_in = state ? 0x08U : 0x00U; + LOG("Read data latch %02X\n", m_data_latch); + result &= m_data_latch; + machine().scheduler().synchronize(timer_expired_delegate(FUNC(a2bus_buf_grapplerplus_device::clear_ibusy), this)); + m_mcu->set_input_line(MCS48_INPUT_IRQ, CLEAR_LINE); + ack_w(0); + ack_w(1); } + + return result; } -WRITE_LINE_MEMBER(a2bus_grapplerplus_device::pe_w) +void a2bus_buf_grapplerplus_device::mcu_bus_w(u8 data) { - if (bool(state) != bool(m_pe_in)) + // RAM EN enables RAM + if (BIT(m_mcu_p2, 0)) + { + if (!BIT(m_mcu_p2, 1)) + { + u16 const addr(m_ram_row | u16(m_mcu->p1_r())); + LOG("Wrote RAM @%04X=%02X\n", addr, data); + m_ram[addr] = data; + } + else + { + LOG("/WR asserted /CAS while /RAS not asserted\n"); + } + } + + // /IOEN enables output latch + if (!BIT(m_mcu_p2, 3)) { - LOG("PAPER EMPTY=%d\n", state); - m_pe_in = state ? 0x04U : 0x00U; + LOG("Output data %02X\n", data); + m_printer_out->write(data); } } -WRITE_LINE_MEMBER(a2bus_grapplerplus_device::slct_w) + +//-------------------------------------------------- +// synchronised signals +//-------------------------------------------------- + +void a2bus_buf_grapplerplus_device::set_buf_data(void *ptr, s32 param) { - if (bool(state) != bool(m_slct_in)) - { - LOG("SELECT=%d\n", state); - m_slct_in = state ? 0x02U : 0x00U; - } + m_data_latch = u8(u32(param)); } +void a2bus_buf_grapplerplus_device::set_buf_ack_in(void *ptr, s32 param) +{ + if (u32(param) != m_buf_ack_in) + { + LOG("/ACK=%d\n", param); + m_buf_ack_in = u8(u32(param)); + if (!param) + { + if (m_buf_ack_latch) + LOG("Clearing T1\n"); + else + LOG("No data output since previous acknowledge\n"); + m_buf_ack_latch = 0U; + } + } +} -//---------------------------------------------- -// timer handlers -//---------------------------------------------- -TIMER_CALLBACK_MEMBER(a2bus_grapplerplus_device::update_strobe) +void a2bus_buf_grapplerplus_device::clear_ibusy(void *ptr, s32 param) { - LOG("Output /STROBE=%u\n", m_next_strobe); - m_printer_conn->write_strobe(m_next_strobe); - if (!m_next_strobe) + if (m_ibusy) { - LOG("Start strobe timer\n"); - m_next_strobe = 1U; - m_strobe_timer->adjust(attotime::from_ticks(1, clock())); + LOG("Clearing IBUSY\n"); + m_ibusy = 0U; + } + else + { + LOG("IBUSY already clear\n"); } } diff --git a/src/devices/bus/a2bus/grapplerplus.h b/src/devices/bus/a2bus/grapplerplus.h index 1060dc11c4f..3c881a2babc 100644 --- a/src/devices/bus/a2bus/grapplerplus.h +++ b/src/devices/bus/a2bus/grapplerplus.h @@ -26,6 +26,24 @@ P.E. 23 24 GND SLCT 25 26 GND + Orange Micro Buffered Grappler+ Printer Interface + + 26-pin two-row header to printer: + + STB 1 2 GND + D0 3 4 GND + D1 5 6 GND + D2 7 8 GND + D3 9 10 GND + D4 11 12 GND + D5 23 14 GND + D6 15 16 GND + D7 17 18 GND + ACK 19 20 GND + BUSY 21 22 GND + P.E. 23 24 GND + SLCT 25 26 N/C + ***********************************************************************/ #ifndef MAME_BUS_A2BUS_GRAPPLERPLUS_H #define MAME_BUS_A2BUS_GRAPPLERPLUS_H @@ -33,60 +51,155 @@ #pragma once #include "a2bus.h" + #include "bus/centronics/ctronics.h" +#include "cpu/mcs48/mcs48.h" + -class a2bus_grapplerplus_device : public device_t, public device_a2bus_card_interface +class a2bus_grapplerplus_device_base : public device_t, public device_a2bus_card_interface { public: - a2bus_grapplerplus_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock); - // DIP switch handlers - DECLARE_INPUT_CHANGED_MEMBER(sw_msb); + virtual DECLARE_INPUT_CHANGED_MEMBER(sw_msb) { } // device_a2bus_card_interface implementation - virtual u8 read_c0nx(u8 offset) override; virtual void write_c0nx(u8 offset, u8 data) override; virtual u8 read_cnxx(u8 offset) override; virtual void write_cnxx(u8 offset, u8 data) override; virtual u8 read_c800(u16 offset) override; protected: + a2bus_grapplerplus_device_base(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock); + // device_t implementation - virtual tiny_rom_entry const *device_rom_region() const override; virtual void device_add_mconfig(machine_config &config) override; virtual ioport_constructor device_input_ports() const override; virtual void device_start() override; virtual void device_reset() override; - // timer handlers - TIMER_CALLBACK_MEMBER(update_strobe); + // ACK latch set input + DECLARE_WRITE_LINE_MEMBER(ack_w); + + // signal state + u8 ack_latch() const { return m_ack_latch; } + u8 busy_in() const { return m_busy_in; } + u8 pe_in() const { return m_pe_in; } + u8 slct_in() const { return m_slct_in; } + + required_device m_printer_conn; + required_device m_printer_out; + required_ioport m_s1; private: // printer status inputs - DECLARE_WRITE_LINE_MEMBER(ack_w); DECLARE_WRITE_LINE_MEMBER(busy_w); DECLARE_WRITE_LINE_MEMBER(pe_w); DECLARE_WRITE_LINE_MEMBER(slct_w); - required_device m_printer_conn; - required_device m_printer_out; - required_ioport m_s1; - required_region_ptr m_rom; - emu_timer * m_strobe_timer; + // synchronised printer status inputs + void set_ack_in(void *ptr, s32 param); + void set_busy_in(void *ptr, s32 param); + void set_pe_in(void *ptr, s32 param); + void set_slct_in(void *ptr, s32 param); + + // for derived devices to implement + virtual void data_latched(u8 data) = 0; + virtual void ack_latch_set() { } + virtual void ack_latch_cleared() { } + + required_region_ptr m_rom; u16 m_rom_bank; // U2D (pin 13) - u8 m_data_latch; // U10 u8 m_ack_latch; // U2C (pin 9) + u8 m_ack_in; // printer connector pin 19 (synchronised) + u8 m_busy_in; // printer connector pin 21 (synchronised) + u8 m_pe_in; // printer connector pin 23 (synchronised) + u8 m_slct_in; // printer connector pin 25 (synchronised) +}; + + +class a2bus_grapplerplus_device : public a2bus_grapplerplus_device_base +{ +public: + a2bus_grapplerplus_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock); + + // DIP switch handlers + virtual DECLARE_INPUT_CHANGED_MEMBER(sw_msb) override; + + // device_a2bus_card_interface implementation + virtual u8 read_c0nx(u8 offset) override; + virtual void write_c0nx(u8 offset, u8 data) override; + +protected: + // device_t implementation + virtual tiny_rom_entry const *device_rom_region() const override; + virtual void device_add_mconfig(machine_config &config) override; + virtual void device_start() override; + virtual void device_reset() override; + +private: + // a2bus_grapplerplus_device_base implementation + virtual void data_latched(u8 data) override; + virtual void ack_latch_set() override; + virtual void ack_latch_cleared() override; + + // timer handlers + TIMER_CALLBACK_MEMBER(update_strobe); + + emu_timer * m_strobe_timer; + + u8 m_data_latch; // U10 u8 m_irq_disable; // U2A (pin 4) u8 m_irq; // U3D (pin 13) u8 m_next_strobe; // U5A (pin 5) - u8 m_ack_in; // printer connector pin 19 - u8 m_busy_in; // printer connector pin 21 - u8 m_pe_in; // printer connector pin 23 - u8 m_slct_in; // printer connector pin 25 +}; + + +class a2bus_buf_grapplerplus_device : public a2bus_grapplerplus_device_base +{ +public: + a2bus_buf_grapplerplus_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock); + + // device_a2bus_card_interface implementation + virtual u8 read_c0nx(u8 offset) override; + +protected: + // device_t implementation + virtual tiny_rom_entry const *device_rom_region() const override; + virtual void device_add_mconfig(machine_config &config) override; + virtual void device_start() override; + +private: + // a2bus_grapplerplus_device_base implementation + virtual void data_latched(u8 data) override; + + // printer status inputs + DECLARE_WRITE_LINE_MEMBER(buf_ack_w); + + // MCU I/O handlers + void mcu_io(address_map &map); + void mcu_p2_w(u8 data); + u8 mcu_bus_r(); + void mcu_bus_w(u8 data); + + // synchronised signals + void set_buf_data(void *ptr, s32 param); + void set_buf_ack_in(void *ptr, s32 param); + void clear_ibusy(void *ptr, s32 param); + + required_device m_mcu; + std::unique_ptr m_ram; + + u16 m_ram_row; // U1-U8 + u8 m_mcu_p2; // U10 + u8 m_data_latch; // U14 (synchronised) + u8 m_ibusy; // U12 + u8 m_buf_ack_latch; // U12 + u8 m_buf_ack_in; // printer connector pin 19 (synchronised) }; DECLARE_DEVICE_TYPE(A2BUS_GRAPPLERPLUS, a2bus_grapplerplus_device) +DECLARE_DEVICE_TYPE(A2BUS_BUFGRAPPLERPLUS, a2bus_buf_grapplerplus_device) #endif // MAME_BUS_A2BUS_GRAPPLERPLUS_H diff --git a/src/mame/drivers/apple2.cpp b/src/mame/drivers/apple2.cpp index 1e5e3d14579..39d2328f3ed 100644 --- a/src/mame/drivers/apple2.cpp +++ b/src/mame/drivers/apple2.cpp @@ -1314,7 +1314,8 @@ static void apple2_cards(device_slot_interface &device) device.option_add("aevm80", A2BUS_AEVIEWMASTER80); /* Applied Engineering ViewMaster 80 */ device.option_add("parprn", A2BUS_PARPRN); /* Apple II Parallel Printer Interface Card */ device.option_add("parallel", A2BUS_PIC); /* Apple II Parallel Interface Card */ - device.option_add("grapplerplus", A2BUS_GRAPPLERPLUS); /* Orange Micro Grappler+ Printer Interface card */ + device.option_add("grapplus", A2BUS_GRAPPLERPLUS); /* Orange Micro Grappler+ Printer Interface card */ + device.option_add("bufgrapplus", A2BUS_BUFGRAPPLERPLUS); /* Orange Micro Buffered Grappler+ Printer Interface card */ device.option_add("corvus", A2BUS_CORVUS); /* Corvus flat-cable HDD interface (see notes in a2corvus.c) */ device.option_add("mcms1", A2BUS_MCMS1); /* Mountain Computer Music System, card 1 of 2 */ device.option_add("mcms2", A2BUS_MCMS2); /* Mountain Computer Music System, card 2 of 2. must be in card 1's slot + 1! */ diff --git a/src/mame/drivers/apple2e.cpp b/src/mame/drivers/apple2e.cpp index f5019069272..1344605e892 100644 --- a/src/mame/drivers/apple2e.cpp +++ b/src/mame/drivers/apple2e.cpp @@ -4665,7 +4665,8 @@ static void apple2_cards(device_slot_interface &device) device.option_add("aevm80", A2BUS_AEVIEWMASTER80); /* Applied Engineering ViewMaster 80 */ device.option_add("parprn", A2BUS_PARPRN); /* Apple II Parallel Printer Interface Card */ device.option_add("parallel", A2BUS_PIC); /* Apple II Parallel Interface Card */ - device.option_add("grapplerplus", A2BUS_GRAPPLERPLUS); /* Orange Micro Grappler+ Printer Interface card */ + device.option_add("grapplus", A2BUS_GRAPPLERPLUS); /* Orange Micro Grappler+ Printer Interface card */ + device.option_add("bufgrapplus", A2BUS_BUFGRAPPLERPLUS); /* Orange Micro Buffered Grappler+ Printer Interface card */ device.option_add("corvus", A2BUS_CORVUS); /* Corvus flat-cable HDD interface (see notes in a2corvus.c) */ device.option_add("mcms1", A2BUS_MCMS1); /* Mountain Computer Music System, card 1 of 2 */ device.option_add("mcms2", A2BUS_MCMS2); /* Mountain Computer Music System, card 2 of 2. must be in card 1's slot + 1! */ diff --git a/src/mame/drivers/apple2gs.cpp b/src/mame/drivers/apple2gs.cpp index b1c42bec41d..a69007226bb 100644 --- a/src/mame/drivers/apple2gs.cpp +++ b/src/mame/drivers/apple2gs.cpp @@ -4763,7 +4763,8 @@ static void apple2_cards(device_slot_interface &device) device.option_add("aevm80", A2BUS_AEVIEWMASTER80); /* Applied Engineering ViewMaster 80 */ device.option_add("parprn", A2BUS_PARPRN); /* Apple II Parallel Printer Interface Card */ device.option_add("parallel", A2BUS_PIC); /* Apple Parallel Interface Card */ - device.option_add("grapplerplus", A2BUS_GRAPPLERPLUS); /* Orange Micro Grappler+ Printer Interface card */ + device.option_add("grapplus", A2BUS_GRAPPLERPLUS); /* Orange Micro Grappler+ Printer Interface card */ + device.option_add("bufgrapplus", A2BUS_BUFGRAPPLERPLUS); /* Orange Micro Buffered Grappler+ Printer Interface card */ device.option_add("corvus", A2BUS_CORVUS); /* Corvus flat-cable HDD interface (see notes in a2corvus.c) */ device.option_add("mcms1", A2BUS_MCMS1); /* Mountain Computer Music System, card 1 of 2 */ device.option_add("mcms2", A2BUS_MCMS2); /* Mountain Computer Music System, card 2 of 2. must be in card 1's slot + 1! */ -- cgit v1.2.3