summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/apple/adbmodem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/apple/adbmodem.cpp')
-rw-r--r--src/mame/apple/adbmodem.cpp218
1 files changed, 218 insertions, 0 deletions
diff --git a/src/mame/apple/adbmodem.cpp b/src/mame/apple/adbmodem.cpp
new file mode 100644
index 00000000000..d90192d5e3a
--- /dev/null
+++ b/src/mame/apple/adbmodem.cpp
@@ -0,0 +1,218 @@
+// license:BSD-3-Clause
+// copyright-holders:R. Belmont
+/*
+ Apple ADB Modem 342S0440-B
+ Emulation by R. Belmont
+
+ This is a PIC1654S that converts ADB bit serial to and from bit serial
+ for the VIA shifter.
+
+ Connections:
+ Pin 1 - RA2 - ADB out (inverted)
+ Pin 2 - RA3 - ADB in
+ Pin 3 - RTCC - ADB in
+ Pin 13 - RB2 - to VIA CB1 (shift clock)
+ Pin 14 - RB3 - to VIA CB2 (shift data)
+ Pin 16 - RB4 - to VIA PB3 (IRQ)
+ Pin 27 - RA0 - to VIA PB4 (newaction bit 0)
+ Pin 28 - RA1 - to VIA PB5 (newaction bit 1)
+*/
+
+#include "emu.h"
+#include "adbmodem.h"
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+DEFINE_DEVICE_TYPE(ADBMODEM, adbmodem_device, "adbmodem", "Apple ADB Modem")
+
+ROM_START( adbmodem )
+ ROM_REGION(0x400, "adbmodem", 0)
+ ROM_LOAD( "342s0440-b.bin", 0x0000, 0x0400, CRC(cffb33eb) SHA1(4a35a44605073ae6076a0292e2056ee4d938d1bd) )
+ROM_END
+
+//-------------------------------------------------
+// ADDRESS_MAP
+//-------------------------------------------------
+
+void adbmodem_device::adbmodem_map(address_map &map)
+{
+}
+
+
+//-------------------------------------------------
+// device_add_mconfig - add device configuration
+//-------------------------------------------------
+
+void adbmodem_device::device_add_mconfig(machine_config &config)
+{
+ PIC1654S(config, m_maincpu, 3.6864_MHz_XTAL);
+ m_maincpu->set_addrmap(AS_PROGRAM, &adbmodem_device::adbmodem_map);
+ m_maincpu->read_a().set(FUNC(adbmodem_device::porta_r));
+ m_maincpu->write_a().set(FUNC(adbmodem_device::porta_w));
+ m_maincpu->read_b().set(FUNC(adbmodem_device::portb_r));
+ m_maincpu->write_b().set(FUNC(adbmodem_device::portb_w));
+
+#if USE_BUS_ADB
+ ADB_CONNECTOR(config, "adb1", adb_device::default_devices, "a9m0330", false);
+#endif
+}
+
+const tiny_rom_entry *adbmodem_device::device_rom_region() const
+{
+ return ROM_NAME( adbmodem );
+}
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+#if USE_BUS_ADB
+void adbmodem_device::adb_w(int id, int state)
+{
+ m_adb_device_out[id] = state;
+ adb_change();
+}
+
+void adbmodem_device::adb_poweron_w(int id, int state)
+{
+ m_adb_device_poweron[id] = state;
+}
+
+void adbmodem_device::adb_change()
+{
+ bool adb = m_adb_out & m_adb_device_out[0] & m_adb_device_out[1];
+ logerror("adb c:%d 1:%d 2:%d -> %d (%02x %02x)\n", m_adb_out, m_adb_device_out[0], m_adb_device_out[1], adb, ddrs[0], ports[0]);
+ for (int i = 0; i != 2; i++)
+ if (m_adb_device[i])
+ {
+ m_adb_device[i]->adb_w(adb);
+ }
+}
+#endif
+
+//-------------------------------------------------
+// adbmodem_device - constructor
+//-------------------------------------------------
+
+adbmodem_device::adbmodem_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, ADBMODEM, tag, owner, clock),
+ write_reset(*this),
+ write_linechange(*this),
+ write_via_clock(*this),
+ write_via_data(*this),
+ write_irq(*this),
+ m_maincpu(*this, "adbmodem"),
+ m_via_data(0),
+ m_via_clock(0),
+ m_last_adb(0),
+ m_via_state(0),
+ m_last_adb_time(0),
+ m_adb_in(false),
+ m_reset_line(0),
+ m_adb_dtime(0),
+ m_last_via_clock(1)
+#if USE_BUS_ADB
+ , m_adb_connector{{*this, "adb1"}, {*this, finder_base::DUMMY_TAG}}
+#endif
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void adbmodem_device::device_start()
+{
+#if USE_BUS_ADB
+ for (int i = 0; i < 2; i++)
+ {
+ m_adb_device[i] = m_adb_connector[i] ? m_adb_connector[i]->get_device() : nullptr;
+ if (m_adb_device[i])
+ {
+ m_adb_device[i]->adb_r().set([this, i](int state) { adb_w(i, state); });
+ m_adb_device[i]->poweron_r().set([this, i](int state) { adb_poweron_w(i, state); });
+ }
+ }
+#endif
+
+ save_item(NAME(m_via_data));
+ save_item(NAME(m_via_clock));
+ save_item(NAME(m_adb_in));
+ save_item(NAME(m_reset_line));
+ save_item(NAME(m_adb_dtime));
+
+#if USE_BUS_ADB
+ save_item(NAME(m_adb_out));
+ save_item(NAME(m_adb_device_out));
+ save_item(NAME(m_adb_device_poweron));
+#endif
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void adbmodem_device::device_reset()
+{
+ #if USE_BUS_ADB
+ m_adb_device_out[0] = m_adb_device_out[1] = true;
+ m_adb_device_poweron[0] = m_adb_device_poweron[1] = true;
+ #endif
+
+ m_adb_in = true; // line is pulled up to +5v, so nothing plugged in would read as "1"
+ m_reset_line = 0;
+ m_via_data = 0;
+ m_via_clock = 0;
+ m_last_adb_time = m_maincpu->total_cycles();
+ m_last_adb = 0;
+ m_last_via_clock = 1;
+}
+
+u8 adbmodem_device::porta_r()
+{
+ return (m_via_state & 3) | (m_adb_in << 3);
+}
+
+void adbmodem_device::porta_w(u8 data)
+{
+ write_linechange(BIT(data, 2) ^ 1);
+}
+
+u8 adbmodem_device::portb_r()
+{
+ return (m_via_data << 3);
+}
+
+void adbmodem_device::portb_w(u8 data)
+{
+ m_last_via_clock = BIT(data, 2);
+ write_via_data(BIT(data, 3));
+ write_via_clock(BIT(data, 2));
+ write_irq(BIT(data, 4) ^ 1);
+}
+
+void adbmodem_device::set_via_data(u8 dat)
+{
+ /*
+ There is a race condition we sometimes lose on fast CPUs like the IIci where
+ the final bit shifts out of the VIA and the 68k writes the VIA ACR (causing the
+ data line to go high) before the PIC has sampled it to get the final bit state.
+ This manifested as ADB TALK mouse commands going to register 1 instead of 0, which
+ due to implementation details of macadb returned 00 00 for no mouse data available
+ instead of 80 80, and therefore the mouse button state in MacOS went nuts.
+
+ We solve this by only accepting ADB data line state changes when the last VIA clock
+ we wrote was 0; the VIA is configured with this device to transmit on the falling
+ edge of the clock.
+
+ Thanks to Tashtari for his extensively commmented disassembly and port of this PIC
+ program to the modern PIC16F87 and PIC16F88, which are pin-compatible with the
+ PIC1654S originally used. https://github.com/lampmerchant/macseadb88
+ */
+ if (!m_last_via_clock)
+ {
+ m_via_data = dat;
+ }
+}