Bochs/bochs/iodev/busmouse.cc
Volker Ruppert 59faa4d3ae Rewrite of the MS busmouse support to make it work with DOS and Win95 drivers.
The device exists for some years now, but it was never functional and it is not
yet mentioned in the docs. After studying Linux kernel sources and a lot of
tests it is now usable. A configure option doesn't exist, so it must be enabled
in config.h. At runtime it is only loaded if the mouse type is set to 'bus'.
2013-12-27 17:47:44 +00:00

309 lines
9.1 KiB
C++

/////////////////////////////////////////////////////////////////////////
// $Id$
/////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2004-2013 The Bochs Project
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
// Initial code by Ben Lunt 'fys frontiernet net'
// Define BX_PLUGGABLE in files that can be compiled into plugins. For
// platforms that require a special tag on exported symbols, BX_PLUGGABLE
// is used to know when we are exporting symbols and when we are importing.
#define BX_PLUGGABLE
#include "iodev.h"
#include "busmouse.h"
#if BX_SUPPORT_BUSMOUSE
#define LOG_THIS theBusMouse->
bx_busm_c *theBusMouse = NULL;
#define BUS_MOUSE_IRQ 5
#define PORT_CONTROL 0x023C
#define PORT_DATA 0x023D
#define PORT_SIGNATURE 0x023E
#define PORT_CONFIG 0x023F
#define BUSM_CTRL_READ_BUTTONS 0x00
#define BUSM_CTRL_READ_X 0x01
#define BUSM_CTRL_READ_Y 0x02
#define BUSM_CTRL_COMMAND 0x07
int libbusmouse_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
{
// Create one instance of the busmouse device object.
theBusMouse = new bx_busm_c();
// Register this device.
BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theBusMouse, BX_PLUGIN_BUSMOUSE);
return 0; // Success
}
void libbusmouse_LTX_plugin_fini(void)
{
delete theBusMouse;
}
bx_busm_c::bx_busm_c()
{
put("busmouse", "BUSM");
}
bx_busm_c::~bx_busm_c()
{
SIM->get_bochs_root()->remove("busmouse");
BX_DEBUG(("Exit"));
}
void bx_busm_c::init(void)
{
BX_DEBUG(("Init $Id$"));
DEV_register_irq(BUS_MOUSE_IRQ, "Bus Mouse");
// Call our timer routine at 30hz
BX_BUSM_THIS timer_index =
bx_pc_system.register_timer(this, timer_handler, 33334, 1, 1, "bus mouse timer");
for (int i=0x23C; i<=0x23F; i++) {
DEV_register_ioread_handler(this, read_handler, i, "Bus Mouse", 1);
DEV_register_iowrite_handler(this, write_handler, i, "Bus Mouse", 1);
}
DEV_register_default_mouse(this, mouse_enq_static, NULL);
BX_BUSM_THIS mouse_delayed_dx = 0;
BX_BUSM_THIS mouse_delayed_dy = 0;
BX_BUSM_THIS mouse_buttons = 0;
BX_BUSM_THIS current_x =
BX_BUSM_THIS current_y =
BX_BUSM_THIS current_b = 0;
BX_BUSM_THIS control_val = 0; // the control port value
BX_BUSM_THIS command_val = 0; // command byte
BX_BUSM_THIS sig_port_sequ = 0; // signature byte toggle
BX_BUSM_THIS interrupts = 0; // interrupts off
BX_BUSM_THIS needs_update = 0;
BX_INFO(("BusMouse initialized"));
}
void bx_busm_c::register_state(void)
{
bx_list_c *list = new bx_list_c(SIM->get_bochs_root(), "busmouse", "Busmouse State");
BXRS_DEC_PARAM_FIELD(list, mouse_delayed_dx, BX_BUSM_THIS mouse_delayed_dx);
BXRS_DEC_PARAM_FIELD(list, mouse_delayed_dy, BX_BUSM_THIS mouse_delayed_dy);
BXRS_HEX_PARAM_FIELD(list, mouse_buttons, BX_BUSM_THIS mouse_buttons);
BXRS_HEX_PARAM_FIELD(list, current_x, BX_BUSM_THIS current_x);
BXRS_HEX_PARAM_FIELD(list, current_y, BX_BUSM_THIS current_y);
BXRS_HEX_PARAM_FIELD(list, current_b, BX_BUSM_THIS current_b);
BXRS_HEX_PARAM_FIELD(list, control_val, BX_BUSM_THIS control_val);
BXRS_HEX_PARAM_FIELD(list, command_val, BX_BUSM_THIS command_val);
BXRS_PARAM_BOOL(list, sig_port_sequ, BX_BUSM_THIS sig_port_sequ);
BXRS_PARAM_BOOL(list, interrupts, BX_BUSM_THIS interrupts);
BXRS_PARAM_BOOL(list, needs_update, BX_BUSM_THIS needs_update);
}
// static IO port read callback handler
// redirects to non-static class handler to avoid virtual functions
Bit32u bx_busm_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
{
#if !BX_USE_BUSM_SMF
bx_busm_c *class_ptr = (bx_busm_c *) this_ptr;
return class_ptr->read(address, io_len);
}
Bit32u bx_busm_c::read(Bit32u address, unsigned io_len)
{
#else
UNUSED(this_ptr);
#endif // !BX_USE_BUSM_SMF
Bit8u value = 0;
switch (address) {
case PORT_CONTROL:
value = BX_BUSM_THIS control_val;
break;
case PORT_DATA:
switch (BX_BUSM_THIS control_val) {
case BUSM_CTRL_READ_BUTTONS:
value = BX_BUSM_THIS current_b;
break;
case BUSM_CTRL_READ_X:
value = BX_BUSM_THIS current_x;
break;
case BUSM_CTRL_READ_Y:
value = BX_BUSM_THIS current_y;
break;
case BUSM_CTRL_COMMAND:
value = BX_BUSM_THIS command_val;
break;
default:
BX_ERROR(("Reading data port in unsupported mode 0x%02x", BX_BUSM_THIS control_val));
}
break;
case PORT_SIGNATURE:
if (!BX_BUSM_THIS sig_port_sequ) {
value = 0xDE;
} else {
value = 0x22; // Manufacturer id ?
}
BX_BUSM_THIS sig_port_sequ ^= 1;
break;
case PORT_CONFIG:
BX_ERROR(("Unsupported read from port 0x%04x", address));
break;
}
BX_DEBUG(("read from address 0x%04x, value = 0x%02x ", address, value));
return value;
}
// static IO port write callback handler
// redirects to non-static class handler to avoid virtual functions
void bx_busm_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
{
#if !BX_USE_BUSM_SMF
bx_busm_c *class_ptr = (bx_busm_c *) this_ptr;
class_ptr->write(address, value, io_len);
}
void bx_busm_c::write(Bit32u address, Bit32u value, unsigned io_len)
{
#else
UNUSED(this_ptr);
#endif // !BX_USE_BUSM_SMF
BX_DEBUG(("write to address 0x%04x, value = 0x%02x ", address, value));
switch (address) {
case PORT_CONTROL:
BX_BUSM_THIS control_val = value & 0x07;
break;
case PORT_DATA:
if (BX_BUSM_THIS control_val == BUSM_CTRL_COMMAND) {
if ((value & 0x20) == 0x20) {
BX_BUSM_THIS update_mouse_data();
} else {
DEV_pic_lower_irq(BUS_MOUSE_IRQ);
}
switch (value) {
case 0x10: // interrupts off
BX_BUSM_THIS interrupts = 0;
break;
case 0x09:
case 0x11: // interrupts on
BX_BUSM_THIS interrupts = 1;
break;
case 0x16: // test irq
DEV_pic_raise_irq(BUS_MOUSE_IRQ);
break;
}
BX_BUSM_THIS command_val = value;
} else {
BX_ERROR(("Value 0x%02x written in supported mode 0x%02x", value, BX_BUSM_THIS control_val));
}
break;
case PORT_SIGNATURE:
case PORT_CONFIG:
BX_ERROR(("Unsupported write to port 0x%04x (value = 0x%02x)", address, value));
break;
}
}
void bx_busm_c::mouse_enq_static(void *dev, int delta_x, int delta_y, int delta_z, unsigned button_state, bx_bool absxy)
{
((bx_busm_c*)dev)->mouse_enq(delta_x, delta_y, delta_z, button_state);
}
void bx_busm_c::mouse_enq(int delta_x, int delta_y, int delta_z, unsigned button_state)
{
// scale down the motion
if ((delta_x < -1) || (delta_x > 1))
delta_x /= 2;
if ((delta_y < -1) || (delta_y > 1))
delta_y /= 2;
if (delta_x > 127) delta_x =127;
if (delta_y > 127) delta_y =127;
if (delta_x < -128) delta_x = -128;
if (delta_y < -128) delta_y = -128;
BX_BUSM_THIS mouse_delayed_dx += delta_x;
BX_BUSM_THIS mouse_delayed_dy -= delta_y;
BX_BUSM_THIS mouse_buttons = (Bit8u)(0x40 | ((button_state & 1) << 2) |
((button_state & 2) >> 1));
BX_BUSM_THIS needs_update = 1;
}
void bx_busm_c::update_mouse_data()
{
int delta_x, delta_y;
if (BX_BUSM_THIS mouse_delayed_dx > 127) {
delta_x = 127;
BX_BUSM_THIS mouse_delayed_dx -= 127;
} else if (BX_BUSM_THIS mouse_delayed_dx < -128) {
delta_x = -128;
BX_BUSM_THIS mouse_delayed_dx += 128;
} else {
delta_x = BX_BUSM_THIS mouse_delayed_dx;
BX_BUSM_THIS mouse_delayed_dx = 0;
}
if (BX_BUSM_THIS mouse_delayed_dy > 127) {
delta_y = 127;
BX_BUSM_THIS mouse_delayed_dy -= 127;
} else if (BX_BUSM_THIS mouse_delayed_dy < -128) {
delta_y = -128;
BX_BUSM_THIS mouse_delayed_dy += 128;
} else {
delta_y = BX_BUSM_THIS mouse_delayed_dy;
BX_BUSM_THIS mouse_delayed_dy = 0;
}
if ((BX_BUSM_THIS mouse_delayed_dx == 0) &&
(BX_BUSM_THIS mouse_delayed_dy == 0)) {
BX_BUSM_THIS needs_update = 0;
}
BX_BUSM_THIS current_x = (Bit8u) delta_x;
BX_BUSM_THIS current_y = (Bit8u) delta_y;
BX_BUSM_THIS current_b = mouse_buttons;
}
void bx_busm_c::timer_handler(void *this_ptr)
{
bx_busm_c *class_ptr = (bx_busm_c *) this_ptr;
class_ptr->busm_timer();
}
// Called at 30hz
void bx_busm_c::busm_timer(void)
{
// if interrupts are on, fire the interrupt
if (BX_BUSM_THIS interrupts && BX_BUSM_THIS needs_update) {
DEV_pic_raise_irq(BUS_MOUSE_IRQ);
}
}
#endif // BX_SUPPORT_BUSMOUSE