bus/a2bus updates: [Vas Crabb, Golden Child]

* Added Orange Micro Buffered Grappler+ printer interface card.
* Synchronise all I/O for Apple Parallel Interface Card.
This commit is contained in:
Vas Crabb 2021-04-04 01:18:11 +11:00
parent d7199ad66f
commit 0a687da2ae
7 changed files with 753 additions and 197 deletions

View File

@ -323,46 +323,25 @@ void a2bus_pic_device::device_reset()
WRITE_LINE_MEMBER(a2bus_pic_device::ack_w)
{
if (bool(state) != bool(m_ack_in))
{
m_ack_in = state ? 1U : 0U;
LOG("/ACK=%u\n", m_ack_in);
if (started() && (m_ack_in != BIT(m_input_sw1->read(), 4)))
{
LOG("Active /ACK edge\n");
set_ack_latch();
}
}
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)
{
if (bool(state) != bool(m_perror_in))
{
m_perror_in = state ? 1U : 0U;
LOG("PAPER EMPTY=%u\n", m_perror_in);
}
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)
{
if (bool(state) != bool(m_select_in))
{
m_select_in = state ? 1U : 0U;
LOG("SELECT=%u\n", m_select_in);
}
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)
{
if (bool(state) != bool(m_fault_in))
{
m_fault_in = state ? 1U : 0U;
LOG("/FAULT=%u\n", m_fault_in);
}
machine().scheduler().synchronize(timer_expired_delegate(FUNC(a2bus_pic_device::set_fault_in), this), state ? 1 : 0);
}
@ -380,6 +359,56 @@ TIMER_CALLBACK_MEMBER(a2bus_pic_device::release_strobe)
//----------------------------------------------
// synchronised inputs
//----------------------------------------------
void a2bus_pic_device::set_ack_in(void *ptr, s32 param)
{
if (u32(param) != m_ack_in)
{
m_ack_in = u8(u32(param));
LOG("/ACK=%u\n", m_ack_in);
if (started() && (m_ack_in != BIT(m_input_sw1->read(), 4)))
{
LOG("Active /ACK edge\n");
set_ack_latch();
}
}
}
void a2bus_pic_device::set_perror_in(void *ptr, s32 param)
{
if (u32(param) != m_perror_in)
{
m_perror_in = u8(u32(param));
LOG("PAPER EMPTY=%u\n", m_perror_in);
}
}
void a2bus_pic_device::set_select_in(void *ptr, s32 param)
{
if (u32(param) != m_select_in)
{
m_select_in = u8(u32(param));
LOG("SELECT=%u\n", m_select_in);
}
}
void a2bus_pic_device::set_fault_in(void *ptr, s32 param)
{
if (u32(param) != m_fault_in)
{
m_fault_in = u8(u32(param));
LOG("/FAULT=%u\n", m_fault_in);
}
}
//----------------------------------------------
// helpers
//----------------------------------------------

View File

@ -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();

View File

@ -1,5 +1,10 @@
// license:BSD-3-Clause
// copyright-holders:Vas Crabb
/*
* TODO for Buffered Grappler+:
* - /RST does not reset the MCU, its 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));
}
void a2bus_grapplerplus_device_base::device_reset()
{
m_ack_latch = 1U;
}
//----------------------------------------------
//--------------------------------------------------
// device_a2bus_card_interface implementation
//----------------------------------------------
//--------------------------------------------------
u8 a2bus_grapplerplus_device::read_c0nx(u8 offset)
{
return
m_irq |
((m_s1->read() & 0x07U) << 4) |
m_busy_in |
m_pe_in |
m_slct_in |
m_ack_latch;
}
void a2bus_grapplerplus_device::write_c0nx(u8 offset, u8 data)
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,38 +359,10 @@ void a2bus_grapplerplus_device::write_c0nx(u8 offset, u8 data)
}
u8 a2bus_grapplerplus_device::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::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::read_c800(u16 offset)
{
return m_rom[(offset & 0x07ffU) | m_rom_bank];
}
//----------------------------------------------
//--------------------------------------------------
// device_t implementation
//----------------------------------------------
//--------------------------------------------------
tiny_rom_entry const *a2bus_grapplerplus_device::device_rom_region() const
{
@ -220,47 +372,33 @@ tiny_rom_entry const *a2bus_grapplerplus_device::device_rom_region() const
void a2bus_grapplerplus_device::device_add_mconfig(machine_config &config)
{
CENTRONICS(config, m_printer_conn, centronics_devices, "printer");
a2bus_grapplerplus_device_base::device_add_mconfig(config);
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);
}
ioport_constructor a2bus_grapplerplus_device::device_input_ports() const
{
return INPUT_PORTS_NAME(grapplerplus);
}
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_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));
m_strobe_timer->adjust(attotime::from_ticks(1, clock()));
m_strobe_timer->adjust(attotime::from_ticks(7, clock()));
}
void a2bus_grapplerplus_device::device_reset()
{
m_ack_latch = 0x01U;
a2bus_grapplerplus_device_base::device_reset();
m_irq_disable = 1U;
if (m_irq)
{
@ -271,68 +409,53 @@ void a2bus_grapplerplus_device::device_reset()
//----------------------------------------------
// printer status inputs
//----------------------------------------------
//--------------------------------------------------
// a2bus_grapplerplus_device_base implementation
//--------------------------------------------------
WRITE_LINE_MEMBER(a2bus_grapplerplus_device::ack_w)
void a2bus_grapplerplus_device::data_latched(u8 data)
{
if (bool(state) != bool(m_ack_in))
// 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));
// generate strobe pulse after one clock cycle
m_next_strobe = 0U;
if (!m_strobe_timer->enabled())
{
LOG("/ACK=%d\n", state);
m_ack_in = state ? 1U : 0U;
if (!state)
{
if (!m_ack_latch)
LOG("Set acknowledge latch\n");
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("Start strobe timer\n");
m_strobe_timer->adjust(attotime::from_ticks(7, clock()));
}
}
WRITE_LINE_MEMBER(a2bus_grapplerplus_device::busy_w)
void a2bus_grapplerplus_device::ack_latch_set()
{
if (bool(state) != bool(m_busy_in))
if (!m_irq_disable && !m_irq)
{
LOG("BUSY=%d\n", state);
m_busy_in = state ? 0x08U : 0x00U;
LOG("Asserting slot IRQ\n");
m_irq = 0x80U;
raise_slot_irq();
}
}
WRITE_LINE_MEMBER(a2bus_grapplerplus_device::pe_w)
void a2bus_grapplerplus_device::ack_latch_cleared()
{
if (bool(state) != bool(m_pe_in))
if (m_irq)
{
LOG("PAPER EMPTY=%d\n", state);
m_pe_in = state ? 0x04U : 0x00U;
}
}
WRITE_LINE_MEMBER(a2bus_grapplerplus_device::slct_w)
{
if (bool(state) != bool(m_slct_in))
{
LOG("SELECT=%d\n", state);
m_slct_in = state ? 0x02U : 0x00U;
assert(!m_irq_disable);
LOG("Releasing slot IRQ\n");
m_irq = 0x00U;
lower_slot_irq();
}
}
//----------------------------------------------
//--------------------------------------------------
// timer handlers
//----------------------------------------------
//--------------------------------------------------
TIMER_CALLBACK_MEMBER(a2bus_grapplerplus_device::update_strobe)
{
@ -342,6 +465,288 @@ TIMER_CALLBACK_MEMBER(a2bus_grapplerplus_device::update_strobe)
{
LOG("Start strobe timer\n");
m_next_strobe = 1U;
m_strobe_timer->adjust(attotime::from_ticks(1, clock()));
m_strobe_timer->adjust(attotime::from_ticks(7, clock()));
}
}
//==============================================================
// 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)
{
}
//--------------------------------------------------
// 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();
}
//--------------------------------------------------
// device_t implementation
//--------------------------------------------------
tiny_rom_entry const *a2bus_buf_grapplerplus_device::device_rom_region() const
{
return ROM_NAME(bufgrapplerplus);
}
void a2bus_buf_grapplerplus_device::device_add_mconfig(machine_config &config)
{
a2bus_grapplerplus_device_base::device_add_mconfig(config);
m_printer_conn->ack_handler().set(FUNC(a2bus_buf_grapplerplus_device::buf_ack_w));
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));
}
void a2bus_buf_grapplerplus_device::device_start()
{
a2bus_grapplerplus_device_base::device_start();
m_ram = std::make_unique<u8 []>(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));
}
//--------------------------------------------------
// a2bus_grapplerplus_device_base implementation
//--------------------------------------------------
void a2bus_buf_grapplerplus_device::data_latched(u8 data)
{
// 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);
}
//--------------------------------------------------
// MCU I/O handlers
//--------------------------------------------------
void a2bus_buf_grapplerplus_device::mcu_io(address_map &map)
{
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))
{
if (!BIT(data, 1))
{
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("/ACK asserted, not setting T1\n");
}
}
LOG("Output /STROBE=%u\n", BIT(data, 5));
m_printer_conn->write_strobe(BIT(data, 5));
}
}
u8 a2bus_buf_grapplerplus_device::mcu_bus_r()
{
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("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;
}
void a2bus_buf_grapplerplus_device::mcu_bus_w(u8 data)
{
// 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("Output data %02X\n", data);
m_printer_out->write(data);
}
}
//--------------------------------------------------
// synchronised signals
//--------------------------------------------------
void a2bus_buf_grapplerplus_device::set_buf_data(void *ptr, s32 param)
{
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;
}
}
}
void a2bus_buf_grapplerplus_device::clear_ibusy(void *ptr, s32 param)
{
if (m_ibusy)
{
LOG("Clearing IBUSY\n");
m_ibusy = 0U;
}
else
{
LOG("IBUSY already clear\n");
}
}

View File

@ -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"
class a2bus_grapplerplus_device : public device_t, public device_a2bus_card_interface
#include "bus/centronics/ctronics.h"
#include "cpu/mcs48/mcs48.h"
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);
private:
// printer status inputs
// ACK latch set input
DECLARE_WRITE_LINE_MEMBER(ack_w);
DECLARE_WRITE_LINE_MEMBER(busy_w);
DECLARE_WRITE_LINE_MEMBER(pe_w);
DECLARE_WRITE_LINE_MEMBER(slct_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<centronics_device> m_printer_conn;
required_device<output_latch_device> m_printer_out;
required_ioport m_s1;
required_region_ptr<u8> m_rom;
emu_timer * m_strobe_timer;
private:
// printer status inputs
DECLARE_WRITE_LINE_MEMBER(busy_w);
DECLARE_WRITE_LINE_MEMBER(pe_w);
DECLARE_WRITE_LINE_MEMBER(slct_w);
// 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<u8> 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<mcs48_cpu_device> m_mcu;
std::unique_ptr<u8 []> 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

View File

@ -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! */

View File

@ -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! */

View File

@ -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! */