// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/**********************************************************************
Logitech bus mouse interface emulation
References:
- ec1841 technical manual
- https://github.com/OBattler/86Box/blob/master/src/mouse_bus.c
- https://communities.intel.com/docs/DOC-22714
- http://toastytech.com/guis/msmouse.html
To do:
- selectable IRQ level
- Microsoft protocol
- ec1841.0003 clone: diag mode
- ec1841.0003 clone: fix detection by m86v32 driver
**********************************************************************/
#include "emu.h"
#include "busmouse.h"
#include "machine/i8255.h"
#define LOG_GENERAL (1U << 0)
//#define VERBOSE (LOG_GENERAL)
//#define LOG_OUTPUT_FUNC printf
#include "logmacro.h"
//**************************************************************************
// DEVICE DEFINITIONS
//**************************************************************************
DEFINE_DEVICE_TYPE(BUS_MOUSE, bus_mouse_device, "bus_mouse", "Bus Mouse Interface")
void bus_mouse_device::map(address_map &map)
{
map(0x0, 0x3).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write));
}
//-------------------------------------------------
// INPUT_CHANGED_MEMBER( mouse_x_changed )
//-------------------------------------------------
INPUT_CHANGED_MEMBER(bus_mouse_device::mouse_x_changed)
{
m_x += newval - oldval;
LOG("m_x_c: irqdis %d; %d = %d - %d\n", irq_disabled, m_x, newval, oldval);
}
//-------------------------------------------------
// INPUT_CHANGED_MEMBER( mouse_y_changed )
//-------------------------------------------------
INPUT_CHANGED_MEMBER(bus_mouse_device::mouse_y_changed)
{
m_y += newval - oldval;
LOG("m_y_c: irqdis %d; %d = %d - %d\n", irq_disabled, m_y, newval, oldval);
}
//-------------------------------------------------
// INPUT_PORTS( bus_mouse )
//-------------------------------------------------
INPUT_PORTS_START( bus_mouse )
PORT_START("mouse_x")
PORT_BIT( 0xff, 0x00, IPT_MOUSE_X ) PORT_SENSITIVITY(50) PORT_KEYDELTA(5) PORT_MINMAX(0, 255) PORT_CHANGED_MEMBER(DEVICE_SELF, bus_mouse_device, mouse_x_changed, 0)
PORT_START("mouse_y")
PORT_BIT( 0xff, 0x00, IPT_MOUSE_Y ) PORT_SENSITIVITY(50) PORT_KEYDELTA(5) PORT_MINMAX(0, 255) PORT_CHANGED_MEMBER(DEVICE_SELF, bus_mouse_device, mouse_y_changed, 0)
PORT_START("mouse_buttons")
PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Left Mouse Button") PORT_CODE(MOUSECODE_BUTTON1)
PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Right Mouse Button") PORT_CODE(MOUSECODE_BUTTON3)
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Middle Mouse Button") PORT_CODE(MOUSECODE_BUTTON2)
PORT_BIT( 0x1f, IP_ACTIVE_HIGH, IPT_UNUSED )
PORT_START("irq_rate")
PORT_DIPNAME(0x0f, 0x02, "IRQ rate")
PORT_DIPSETTING(0x01, "15 hz")
PORT_DIPSETTING(0x02, "30 hz")
PORT_DIPSETTING(0x04, "60 hz")
PORT_DIPSETTING(0x08, "120 hz")
PORT_START("irq_line")
PORT_DIPNAME(0x0f, 0x02, "IRQ line")
PORT_DIPSETTING(0x02, "IRQ4")
#if 0
PORT_DIPSETTING(0x08, "IRQ2")
PORT_DIPSETTING(0x04, "IRQ3")
PORT_DIPSETTING(0x01, "IRQ5")
#endif
INPUT_PORTS_END
//-------------------------------------------------
// input_ports - device-specific input ports
//-------------------------------------------------
ioport_constructor bus_mouse_device::device_input_ports() const
{
return INPUT_PORTS_NAME(bus_mouse);
}
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// bus_mouse_device - constructor
//-------------------------------------------------
bus_mouse_device::bus_mouse_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, BUS_MOUSE, tag, owner, clock)
, m_write_extint(*this)
, m_buttons(*this, "mouse_buttons")
{
}
MACHINE_CONFIG_START(bus_mouse_device::device_add_mconfig)
MCFG_DEVICE_ADD("ppi", I8255, 0)
MCFG_I8255_IN_PORTA_CB(READ8(*this, bus_mouse_device, ppi_a_r))
MCFG_I8255_OUT_PORTC_CB(WRITE8(*this, bus_mouse_device, ppi_c_w))
MCFG_I8255_IN_PORTC_CB(READ8(*this, bus_mouse_device, ppi_c_r))
MACHINE_CONFIG_END
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void bus_mouse_device::device_start()
{
// resolve callbacks
m_write_extint.resolve_safe();
m_irq_timer = timer_alloc(0);
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void bus_mouse_device::device_reset()
{
int hz = 2 * 15 * ioport("irq_rate")->read();
m_irq_timer->adjust(attotime::from_hz(hz), 0, attotime::from_hz(hz));
irq = 0;
irq_disabled = 1;
irq_line = ioport("irq_line")->read();
LOG("irq rate: %d Hz\n", hz);
}
void bus_mouse_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
irq = !irq;
if (!irq_disabled && irq)
{
m_write_extint(irq);
}
}
READ8_MEMBER(bus_mouse_device::ppi_a_r)
{
return m_pa;
}
READ8_MEMBER(bus_mouse_device::ppi_c_r)
{
return irq ? irq_line : 0;
}
WRITE8_MEMBER(bus_mouse_device::ppi_c_w)
{
irq_disabled = BIT(data, 4);
switch (data & 0xe0)
{
case 0:
m_write_extint(CLEAR_LINE);
m_pa = 0;
break;
case 0x80: // LSB X
m_pa = m_x & 0xf;
break;
case 0xa0: // MSB X
m_pa = (m_x >> 4) & 0xf;
if (m_x < 0) m_pa |= 8;
m_pa |= m_buttons->read() & 0xe0;
m_x = 0;
break;
case 0xc0: // LSB Y
m_pa = m_y & 0xf;
break;
case 0xe0: // MSB Y
m_pa = (m_y >> 4) & 0xf;
if (m_y < 0) m_pa |= 8;
m_pa |= m_buttons->read() & 0xe0;
m_y = 0;
break;
}
LOG("c_w: data %02x m_pa %02x\n", data, m_pa);
}