From ab6b6fc9e7971b367ec54d565cf10f04e71109ec Mon Sep 17 00:00:00 2001
From: Patrick Mackinlay <pmackinlay@hotmail.com>
Date: Tue, 14 Jan 2025 13:53:33 +0700
Subject: [PATCH] x68k: add serial mouse

---
 src/mame/sharp/x68k.cpp       | 169 ++++------------------------------
 src/mame/sharp/x68k.h         |  24 +----
 src/mame/sharp/x68k_mouse.cpp | 103 +++++++++++++++++++++
 src/mame/sharp/x68k_mouse.h   |  36 ++++++++
 4 files changed, 158 insertions(+), 174 deletions(-)
 create mode 100644 src/mame/sharp/x68k_mouse.cpp
 create mode 100644 src/mame/sharp/x68k_mouse.h

diff --git a/src/mame/sharp/x68k.cpp b/src/mame/sharp/x68k.cpp
index 8e5def0a501a6..c6787eeef393d 100644
--- a/src/mame/sharp/x68k.cpp
+++ b/src/mame/sharp/x68k.cpp
@@ -119,6 +119,7 @@
 #include "x68k.h"
 #include "x68k_hdc.h"
 #include "x68k_kbd.h"
+#include "x68k_mouse.h"
 
 #include "machine/nvram.h"
 
@@ -174,127 +175,6 @@ TIMER_CALLBACK_MEMBER(x68k_state::led_callback)
 
 }
 
-
-// mouse input
-// port B of the Z8530 SCC
-// typically read from the SCC data port on receive buffer full interrupt per byte
-int x68k_state::read_mouse()
-{
-	char val = 0;
-	char ipt = 0;
-
-	if(!(m_scc->get_reg_b(5) & 0x02))
-		return 0xff;
-
-	switch(m_mouse.inputtype)
-	{
-	case 0:
-		ipt = m_mouse1->read();
-		break;
-	case 1:
-		val = m_mouse2->read();
-		ipt = val - m_mouse.last_mouse_x;
-		m_mouse.last_mouse_x = val;
-		break;
-	case 2:
-		val = m_mouse3->read();
-		ipt = val - m_mouse.last_mouse_y;
-		m_mouse.last_mouse_y = val;
-		break;
-	}
-	m_mouse.inputtype++;
-	if(m_mouse.inputtype > 2)
-	{
-		int i_val = m_scc->get_reg_b(0);
-		m_mouse.inputtype = 0;
-		m_mouse.bufferempty = 1;
-		i_val &= ~0x01;
-		m_scc->set_reg_b(0, i_val);
-		LOGMASKED(LOG_SYS, "SCC: mouse buffer empty\n");
-	}
-
-	return ipt;
-}
-
-/*
-    0xe98001 - Z8530 command port B
-    0xe98003 - Z8530 data port B  (mouse input)
-    0xe98005 - Z8530 command port A
-    0xe98007 - Z8530 data port A  (RS232)
-*/
-uint16_t x68k_state::scc_r(offs_t offset)
-{
-	offset %= 4;
-	switch(offset)
-	{
-	case 0:
-		return m_scc->reg_r(0);
-	case 1:
-		return read_mouse();
-	case 2:
-		return m_scc->reg_r(1);
-	case 3:
-		return m_scc->reg_r(3);
-	default:
-		return 0xff;
-	}
-}
-
-void x68k_state::scc_w(offs_t offset, uint16_t data)
-{
-	offset %= 4;
-
-	switch(offset)
-	{
-	case 0:
-		m_scc->reg_w(0,(uint8_t)data);
-		if((m_scc->get_reg_b(5) & 0x02) != m_scc_prev)
-		{
-			if(m_scc->get_reg_b(5) & 0x02)  // Request to Send
-			{
-				int val = m_scc->get_reg_b(0);
-				m_mouse.bufferempty = 0;
-				val |= 0x01;
-				m_scc->set_reg_b(0,val);
-			}
-		}
-		break;
-	case 1:
-		m_scc->reg_w(2,(uint8_t)data);
-		break;
-	case 2:
-		m_scc->reg_w(1,(uint8_t)data);
-		break;
-	case 3:
-		m_scc->reg_w(3,(uint8_t)data);
-		break;
-	}
-	m_scc_prev = m_scc->get_reg_b(5) & 0x02;
-}
-
-TIMER_CALLBACK_MEMBER(x68k_state::scc_ack)
-{
-	if(m_mouse.bufferempty != 0)  // nothing to do if the mouse data buffer is empty
-		return;
-
-//  if((m_ioc.irqstatus & 0xc0) != 0)
-//      return;
-
-	// hard-code the IRQ vector for now, until the SCC code is more complete
-	if((m_scc->get_reg_a(9) & 0x08) || (m_scc->get_reg_b(9) & 0x08))  // SCC reg WR9 is the same for both channels
-	{
-		if((m_scc->get_reg_b(1) & 0x18) != 0)  // if bits 3 and 4 of WR1 are 0, then Rx IRQs are disabled on this channel
-		{
-			if(m_scc->get_reg_b(5) & 0x02)  // RTS signal
-			{
-				m_mouse.irqactive = true;
-				m_mouse.irqvector = 0x54;
-				update_ipl();
-			}
-		}
-	}
-}
-
 void x68k_state::set_adpcm()
 {
 	uint32_t rate = adpcm_div[m_adpcm.rate];
@@ -833,7 +713,7 @@ void x68k_state::update_ipl()
 		new_ipl = 7;
 	else if (m_mfp_int)
 		new_ipl = 6;
-	else if (m_mouse.irqactive)
+	else if (m_scc_int)
 		new_ipl = 5;
 	else if (m_exp_irq4[0] || m_exp_irq4[1])
 		new_ipl = 4;
@@ -946,18 +826,6 @@ uint8_t x68k_state::iack4()
 		return 0x18; // spurious interrupt
 }
 
-uint8_t x68k_state::iack5()
-{
-	if (!machine().side_effects_disabled())
-	{
-		m_mouse.irqactive = false;
-		update_ipl();
-	}
-
-	// TODO: use vector from SCC
-	return m_mouse.irqvector;
-}
-
 void x68k_state::cpu_space_map(address_map &map)
 {
 	map.global_mask(0xffffff);
@@ -965,7 +833,7 @@ void x68k_state::cpu_space_map(address_map &map)
 	map(0xfffff5, 0xfffff5).r(FUNC(x68k_state::iack2));
 	map(0xfffff7, 0xfffff7).r(m_hd63450, FUNC(hd63450_device::iack));
 	map(0xfffff9, 0xfffff9).r(FUNC(x68k_state::iack4));
-	map(0xfffffb, 0xfffffb).r(FUNC(x68k_state::iack5));
+	map(0xfffffb, 0xfffffb).lr8(NAME([this]() { return m_scc->m1_r(); }));
 	map(0xfffffd, 0xfffffd).r(m_mfpdev, FUNC(mc68901_device::get_vector));
 	map(0xffffff, 0xffffff).lr8(NAME([] () { return m68000_base_device::autovector(7); }));
 }
@@ -1005,7 +873,7 @@ void x68k_state::x68k_base_map(address_map &map)
 	map(0xe90000, 0xe91fff).rw(m_ym2151, FUNC(ym2151_device::read), FUNC(ym2151_device::write)).umask16(0x00ff);
 	map(0xe94000, 0xe94003).m(m_upd72065, FUNC(upd72065_device::map)).umask16(0x00ff);
 	map(0xe94004, 0xe94007).rw(FUNC(x68k_state::fdc_r), FUNC(x68k_state::fdc_w));
-	map(0xe98000, 0xe99fff).rw(FUNC(x68k_state::scc_r), FUNC(x68k_state::scc_w));
+	map(0xe98000, 0xe99fff).rw(m_scc, FUNC(scc8530_device::ab_dc_r), FUNC(scc8530_device::ab_dc_w)).umask16(0x00ff);
 	map(0xe9a000, 0xe9bfff).rw(FUNC(x68k_state::ppi_r), FUNC(x68k_state::ppi_w));
 	map(0xe9c000, 0xe9dfff).rw(FUNC(x68k_state::ioc_r), FUNC(x68k_state::ioc_w));
 	map(0xe9e000, 0xe9e3ff).rw(FUNC(x68k_state::exp_r), FUNC(x68k_state::exp_w));  // FPU (Optional)
@@ -1068,16 +936,6 @@ static INPUT_PORTS_START( x68000 )
 	PORT_CONFNAME( 0x02, 0x02, "Enable fake bus errors")
 	PORT_CONFSETTING(   0x00, DEF_STR( Off ))
 	PORT_CONFSETTING(   0x02, DEF_STR( On ))
-
-	PORT_START("mouse1")  // mouse buttons
-	PORT_BIT( 0x00000001, IP_ACTIVE_HIGH, IPT_BUTTON9) PORT_NAME("Left mouse button") PORT_CODE(MOUSECODE_BUTTON1)
-	PORT_BIT( 0x00000002, IP_ACTIVE_HIGH, IPT_BUTTON10) PORT_NAME("Right mouse button") PORT_CODE(MOUSECODE_BUTTON2)
-
-	PORT_START("mouse2")  // X-axis
-	PORT_BIT( 0xff, 0x00, IPT_MOUSE_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(1)
-
-	PORT_START("mouse3")  // Y-axis
-	PORT_BIT( 0xff, 0x00, IPT_MOUSE_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(0) PORT_PLAYER(1)
 INPUT_PORTS_END
 
 void x68k_state::floppy_load_unload(bool load, floppy_image_device *dev)
@@ -1151,10 +1009,6 @@ void x68k_state::machine_start()
 	m_spriteram = (uint16_t*)(memregion("user1")->base());
 	space.install_ram(0x000000,m_ram->size()-1,m_ram->pointer());
 
-	// start mouse timer
-	m_mouse_timer->adjust(attotime::zero, 0, attotime::from_msec(1));  // a guess for now
-	m_mouse.inputtype = 0;
-
 	// start LED timer
 	m_led_timer->adjust(attotime::zero, 0, attotime::from_msec(400));
 
@@ -1173,11 +1027,11 @@ void x68k_state::machine_start()
 
 	m_dmac_int = false;
 	m_mfp_int = false;
+	m_scc_int = false;
 	m_exp_irq2[0] = m_exp_irq2[1] = false;
 	m_exp_irq4[0] = m_exp_irq4[1] = false;
 	m_exp_nmi[0] = m_exp_nmi[1] = false;
 	m_ioc.irqstatus = 0;
-	m_mouse.irqactive = false;
 	m_current_ipl = 0;
 	m_adpcm.rate = 0;
 	m_adpcm.clock = 0;
@@ -1204,7 +1058,6 @@ void x68k_state::driver_start()
 	// copy last half of BIOS to a user region, to use for initial startup
 	memcpy(user2,(rom+0xff0000),0x10000);
 
-	m_mouse_timer = timer_alloc(FUNC(x68ksupr_state::scc_ack), this);
 	m_led_timer = timer_alloc(FUNC(x68ksupr_state::led_callback), this);
 	m_fdc_tc = timer_alloc(FUNC(x68ksupr_state::floppy_tc_tick), this);
 	m_adpcm_timer = timer_alloc(FUNC(x68ksupr_state::adpcm_drq_tick), this);
@@ -1249,6 +1102,11 @@ static void keyboard_devices(device_slot_interface &device)
 	device.option_add("x68k", X68K_KEYBOARD);
 }
 
+static void mouse_devices(device_slot_interface &device)
+{
+	device.option_add("x68k", X68K_MOUSE);
+}
+
 void x68k_state::x68000_base(machine_config &config)
 {
 	config.set_maximum_quantum(attotime::from_hz(60));
@@ -1281,7 +1139,12 @@ void x68k_state::x68000_base(machine_config &config)
 	m_hd63450->dma_read<0>().set("upd72065", FUNC(upd72065_device::dma_r));
 	m_hd63450->dma_write<0>().set("upd72065", FUNC(upd72065_device::dma_w));
 
-	SCC8530(config, m_scc, 40_MHz_XTAL / 8);
+	SCC8530N(config, m_scc, 40_MHz_XTAL / 8);
+	m_scc->out_int_callback().set([this](int state) { m_scc_int = state; update_ipl(); });
+
+	rs232_port_device &mouse(RS232_PORT(config, "mouse_port", mouse_devices, "x68k"));
+	mouse.rxd_handler().set(m_scc, FUNC(scc8530_device::rxb_w));
+	m_scc->out_rtsb_callback().set(mouse, FUNC(rs232_port_device::write_rts));
 
 	RP5C15(config, m_rtc, 32.768_kHz_XTAL);
 	m_rtc->alarm().set(m_mfpdev, FUNC(mc68901_device::i0_w));
diff --git a/src/mame/sharp/x68k.h b/src/mame/sharp/x68k.h
index 2c1dc8552af67..b5c40b213b6bb 100644
--- a/src/mame/sharp/x68k.h
+++ b/src/mame/sharp/x68k.h
@@ -20,7 +20,7 @@
 #include "cpu/m68000/m68000.h"
 #include "cpu/m68000/m68030.h"
 #include "imagedev/floppy.h"
-#include "machine/8530scc.h"
+#include "machine/z80scc.h"
 #include "machine/hd63450.h"
 #include "machine/i8255.h"
 #include "machine/mb87030.h"
@@ -64,9 +64,6 @@ class x68k_state : public driver_device
 		, m_expansion(*this, "exp%u", 1U)
 		, m_adpcm_out(*this, {"adpcm_outl", "adpcm_outr"})
 		, m_options(*this, "options")
-		, m_mouse1(*this, "mouse1")
-		, m_mouse2(*this, "mouse2")
-		, m_mouse3(*this, "mouse3")
 		, m_eject_drv_out(*this, "eject_drv%u", 0U)
 		, m_ctrl_drv_out(*this, "ctrl_drv%u", 0U)
 		, m_access_drv_out(*this, "access_drv%u", 0U)
@@ -100,7 +97,7 @@ class x68k_state : public driver_device
 	required_device<palette_device> m_pcgpalette;
 	required_device<mc68901_device> m_mfpdev;
 	required_device<rp5c15_device> m_rtc;
-	required_device<scc8530_legacy_device> m_scc;
+	required_device<scc8530_device> m_scc;
 	required_device<ym2151_device> m_ym2151;
 	required_device<i8255_device> m_ppi;
 	required_device<screen_device> m_screen;
@@ -111,9 +108,6 @@ class x68k_state : public driver_device
 	required_device_array<filter_volume_device, 2> m_adpcm_out;
 
 	required_ioport m_options;
-	required_ioport m_mouse1;
-	required_ioport m_mouse2;
-	required_ioport m_mouse3;
 
 	output_finder<4> m_eject_drv_out;
 	output_finder<4> m_ctrl_drv_out;
@@ -178,24 +172,15 @@ class x68k_state : public driver_device
 		uint8_t hdcvector = 0;
 		uint8_t prnvector = 0;
 	} m_ioc;
-	struct
-	{
-		int inputtype = 0;  // determines which input is to be received
-		bool irqactive = false;  // true if IRQ is being serviced
-		uint8_t irqvector = 0;
-		char last_mouse_x = 0;  // previous mouse x-axis value
-		char last_mouse_y = 0;  // previous mouse y-axis value
-		int bufferempty = 0;  // non-zero if buffer is empty
-	} m_mouse;
 	uint8_t m_ppi_portc = 0;
 	bool m_dmac_int = false;
 	bool m_mfp_int = false;
+	bool m_scc_int = false;
 	bool m_exp_irq2[2]{};
 	bool m_exp_irq4[2]{};
 	bool m_exp_nmi[2]{};
 	uint8_t m_current_ipl = 0;
 	int m_led_state = 0;
-	emu_timer* m_mouse_timer = nullptr;
 	emu_timer* m_led_timer = nullptr;
 	unsigned char m_scc_prev = 0;
 	emu_timer* m_fdc_tc = nullptr;
@@ -233,7 +218,6 @@ class x68k_state : public driver_device
 	void dma_irq(int state);
 	void dma_end(offs_t offset, uint8_t data);
 
-	int read_mouse();
 	void set_adpcm();
 
 	void fm_irq(int state);
@@ -241,8 +225,6 @@ class x68k_state : public driver_device
 	template <int N> void irq4_line(int state);
 	template <int N> void nmi_line(int state);
 
-	void scc_w(offs_t offset, uint16_t data);
-	uint16_t scc_r(offs_t offset);
 	void fdc_w(offs_t offset, uint16_t data);
 	uint16_t fdc_r(offs_t offset);
 	void ioc_w(offs_t offset, uint16_t data);
diff --git a/src/mame/sharp/x68k_mouse.cpp b/src/mame/sharp/x68k_mouse.cpp
new file mode 100644
index 0000000000000..f30cfc7d25d54
--- /dev/null
+++ b/src/mame/sharp/x68k_mouse.cpp
@@ -0,0 +1,103 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#include "emu.h"
+#include "x68k_mouse.h"
+
+#include <algorithm>
+
+//#define VERBOSE (LOG_GENERAL)
+#include "logmacro.h"
+
+enum status_mask : u8
+{
+	STS_RB = 0x01, // right button
+	STS_LB = 0x02, // left button
+	STS_OX = 0x10, // x overflow
+	STS_UX = 0x20, // x underflow
+	STS_OY = 0x40, // y overflow
+	STS_UY = 0x80, // y underflow
+};
+
+DEFINE_DEVICE_TYPE(X68K_MOUSE, x68k_mouse_device, "x68k_mouse", "Sharp X68000 Mouse")
+
+x68k_mouse_device::x68k_mouse_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
+	: buffered_rs232_device<3>(mconfig, X68K_MOUSE, tag, owner, clock)
+	, m_buttons(*this, "BTN")
+	, m_x_axis(*this, "X")
+	, m_y_axis(*this, "Y")
+{
+}
+
+void x68k_mouse_device::device_start()
+{
+	buffered_rs232_device<3>::device_start();
+
+	save_item(NAME(m_b));
+	save_item(NAME(m_x));
+	save_item(NAME(m_y));
+
+	set_data_frame(1, 8, PARITY_NONE, STOP_BITS_2);
+	set_tra_rate(4'800);
+
+	transmit_register_reset();
+
+	m_b = 0;
+	m_x = 0;
+	m_y = 0;
+}
+
+s16 read_axis(ioport_port &port, u16 &old_val)
+{
+	u16 const new_val = port.read();
+	s16 const delta = new_val - old_val;
+
+	old_val = new_val;
+
+	return delta;
+}
+
+void x68k_mouse_device::input_rts(int state)
+{
+	if (!state && fifo_empty())
+	{
+		u8 status = m_buttons->read();
+		s16 const dx = read_axis(*m_x_axis, m_x);
+		s16 const dy = read_axis(*m_y_axis, m_y);
+
+		if (dx || dy || m_b != status)
+		{
+			if (dy < -128)
+				status |= STS_UY;
+			if (dy > 127)
+				status |= STS_OY;
+			if (dx < -128)
+				status |= STS_UX;
+			if (dx > 127)
+				status |= STS_OX;
+
+			transmit_byte(status);
+			transmit_byte(s8(std::clamp<s16>(dx, -128, 127)));
+			transmit_byte(s8(std::clamp<s16>(dy, -128, 127)));
+
+			m_b = status & (STS_LB | STS_RB);
+		}
+	}
+}
+
+INPUT_PORTS_START(x68k)
+	PORT_START("BTN")
+	PORT_BIT(0x0002, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_CODE(MOUSECODE_BUTTON1)
+	PORT_BIT(0x0001, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_CODE(MOUSECODE_BUTTON2)
+
+	PORT_START("X")
+	PORT_BIT(0xfff, 0x000, IPT_MOUSE_X) PORT_SENSITIVITY(100) PORT_KEYDELTA(0)
+
+	PORT_START("Y")
+	PORT_BIT(0xfff, 0x000, IPT_MOUSE_Y) PORT_SENSITIVITY(100) PORT_KEYDELTA(0)
+INPUT_PORTS_END
+
+ioport_constructor x68k_mouse_device::device_input_ports() const
+{
+	return INPUT_PORTS_NAME(x68k);
+}
diff --git a/src/mame/sharp/x68k_mouse.h b/src/mame/sharp/x68k_mouse.h
new file mode 100644
index 0000000000000..9028c831fe47e
--- /dev/null
+++ b/src/mame/sharp/x68k_mouse.h
@@ -0,0 +1,36 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+#ifndef MAME_SHARP_X68K_MOUSE_H
+#define MAME_SHARP_X68K_MOUSE_H
+
+#pragma once
+
+#include "bus/rs232/rs232.h"
+
+class x68k_mouse_device : public buffered_rs232_device<3>
+{
+public:
+	x68k_mouse_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock = 0);
+
+	virtual void input_rts(int state) override; // MSCTRL (active low)
+
+protected:
+	virtual ioport_constructor device_input_ports() const override ATTR_COLD;
+	virtual void device_start() override ATTR_COLD;
+
+	virtual void received_byte(u8 byte) override {}
+
+private:
+	required_ioport m_buttons;
+	required_ioport m_x_axis;
+	required_ioport m_y_axis;
+
+	u8 m_b;
+	u16 m_x;
+	u16 m_y;
+};
+
+DECLARE_DEVICE_TYPE(X68K_MOUSE, x68k_mouse_device)
+
+#endif // MAME_SHARP_X68K_MOUSE_H
