// license:BSD-3-Clause // copyright-holders:Vas Crabb, R. Belmont #include "emu.h" #include "uniprint.h" //#define VERBOSE 1 //#define LOG_OUTPUT_FUNC osd_printf_info #include "logmacro.h" namespace { ROM_START(uniprint) ROM_REGION(0x1000, "rom", 0) ROM_LOAD("videx_uniprint_2732a.bin", 0x000000, 0x001000, CRC(e8423ef6) SHA1(0a2789ec2cb28f10a2836a91feabc24ceca8f142)) ROM_END INPUT_PORTS_START(uniprint) PORT_START("S1") PORT_DIPNAME(0x70, 0x00, "Printer Type") PORT_DIPLOCATION("S1:1,2,3") PORT_DIPSETTING( 0x00, "Apple DMP/C. Itoh 8510/NEC 8023A/Leading Edge Prowriter") PORT_DIPSETTING( 0x10, "Epson MX, FX, RX/Mannesman Tally 160L, Sprit-80") PORT_DIPSETTING( 0x20, "Star Gemini 10, 15") PORT_DIPSETTING( 0x30, "Data Products (IDS) 440, 460, Prism, uPrism/Okidata 82A, 83A, 93A") PORT_DIPSETTING( 0x40, "Anadex 9000, 9000A, 9001, 9001A, 9500A, 9501, 9501A") PORT_DIPSETTING( 0x50, "Leading Edge Gorilla Banana/Commodore VIC Printer/Radio Shack DMP-100/Sekiosha GP-100A") PORT_DIPSETTING( 0x60, "Centronics 739-1") PORT_DIPSETTING( 0x70, "Reserved") PORT_DIPNAME(0x0c, 0x00, "Default Page Width") PORT_DIPLOCATION("S1:4,5") PORT_DIPSETTING( 0x00, "No Width") PORT_DIPSETTING( 0x04, "80 Columns") PORT_DIPSETTING( 0x08, "96 Columns") PORT_DIPSETTING( 0x0c, "132 Columns") PORT_DIPNAME(0x03, 0x00, "Default Page Length") PORT_DIPLOCATION("S1:6,7") PORT_DIPSETTING( 0x00, "No Length") PORT_DIPSETTING( 0x01, "54 Lines") PORT_DIPSETTING( 0x02, "57 Lines") PORT_DIPSETTING( 0x03, "60 Lines") PORT_START("S2") PORT_DIPNAME(0x01, 0x01, "Strobe Polarity") PORT_DIPLOCATION("S1:8") PORT_DIPSETTING( 0x00, "Positive") PORT_DIPSETTING( 0x01, "Negative") PORT_DIPNAME(0x02, 0x02, "Acknowledge Polarity") PORT_DIPLOCATION("S1:9") PORT_DIPSETTING( 0x00, "Positive") PORT_DIPSETTING( 0x02, "Negative") INPUT_PORTS_END } // anonymous namespace DEFINE_DEVICE_TYPE(A2BUS_UNIPRINT, a2bus_uniprint_device, "a2uniprint", "Videx Uniprint Printer Interface") a2bus_uniprint_device::a2bus_uniprint_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) : device_t(mconfig, A2BUS_UNIPRINT, 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_s2(*this, "S2"), m_rom(*this, "rom"), m_strobe_timer(nullptr), m_data_latch(0xffU), m_ack_latch(0x01U), m_next_strobe(1U), m_ack_in(1U) { } //---------------------------------------------- // DIP switch handlers //---------------------------------------------- INPUT_CHANGED_MEMBER(a2bus_uniprint_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 //---------------------------------------------- // address lines are connected only to the EPROM, so all C0NX accesses are the same u8 a2bus_uniprint_device::read_c0nx(u8 offset) { // inverted /ACK? if (m_s2->read() & 0x02U) { return (m_s1->read() & 0x07FU) | ((m_ack_latch ^ 1) << 7); } return (m_s1->read() & 0x07FU) | (m_ack_latch << 7); } void a2bus_uniprint_device::write_c0nx(u8 offset, u8 data) { m_data_latch = data; m_printer_out->write(data); // clearing the ACK latch will acknowledge an interrupt if (m_ack_in) { m_ack_latch = 0x00U; } 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())); } } u8 a2bus_uniprint_device::read_cnxx(u8 offset) { return m_rom[offset + (slotno() * 0x100)]; } u8 a2bus_uniprint_device::read_c800(u16 offset) { return m_rom[offset + 0x800]; } //---------------------------------------------- // device_t implementation //---------------------------------------------- tiny_rom_entry const *a2bus_uniprint_device::device_rom_region() const { return ROM_NAME(uniprint); } void a2bus_uniprint_device::device_add_mconfig(machine_config &config) { CENTRONICS(config, m_printer_conn, centronics_devices, "printer"); m_printer_conn->ack_handler().set(FUNC(a2bus_uniprint_device::ack_w)); OUTPUT_LATCH(config, m_printer_out); m_printer_conn->set_output_latch(*m_printer_out); } ioport_constructor a2bus_uniprint_device::device_input_ports() const { return INPUT_PORTS_NAME(uniprint); } void a2bus_uniprint_device::device_start() { m_strobe_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(a2bus_uniprint_device::update_strobe), this)); m_next_strobe = 1U; save_item(NAME(m_data_latch)); save_item(NAME(m_ack_latch)); save_item(NAME(m_next_strobe)); save_item(NAME(m_ack_in)); m_strobe_timer->adjust(attotime::from_ticks(1, clock())); } void a2bus_uniprint_device::device_reset() { m_ack_latch = 0x01U; } //---------------------------------------------- // printer status inputs //---------------------------------------------- WRITE_LINE_MEMBER(a2bus_uniprint_device::ack_w) { if (bool(state) != bool(m_ack_in)) { 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; } } } //---------------------------------------------- // timer handlers //---------------------------------------------- TIMER_CALLBACK_MEMBER(a2bus_uniprint_device::update_strobe) { LOG("Output /STROBE=%u\n", m_next_strobe); // invert strobe if DIP set to positive polarity m_printer_conn->write_strobe(m_next_strobe ^ (m_s2->read() & 0x01) ? 0 : 1); if (!m_next_strobe) { LOG("Start strobe timer\n"); m_next_strobe = 1U; m_strobe_timer->adjust(attotime::from_ticks(1, clock())); } }