Fixes for the (S)VGA common DAC and some card-specific DAT's (ATi 68860, BT48x family, and the Cirrus Logic DAC), fixes Star Control II among other things.

This commit is contained in:
OBattler
2018-10-04 01:19:43 +02:00
parent 6b938d63c3
commit ed92602dad
883 changed files with 444853 additions and 88 deletions

View File

@@ -0,0 +1,879 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Implementation of Bus Mouse devices.
*
* These devices were made by both Microsoft and Logitech. At
* first, Microsoft used the same protocol as Logitech, but did
* switch to their new protocol for their InPort interface. So,
* although alike enough to be handled in the same driver, they
* are not the same.
*
* This code is based on my Minix driver for the Logitech(-mode)
* interface. Although that driver blindly took IRQ5, the board
* seems to be able to tell the driver what IRQ it is set for.
* When testing on MS-DOS (6.22), the 'mouse.exe' driver did not
* want to start, and only after disassembling it and inspecting
* the code it was discovered that driver actually does use the
* IRQ reporting feature. In a really, really weird way, too: it
* sets up the board, and then reads the CTRL register which is
* supposed to return that IRQ value. Depending on whether or
* not the FREEZE bit is set, it has to return either the two's
* complemented (negated) value, or (if clear) just the value.
* The mouse.com driver reads both values 10,000 times, and
* then makes up its mind. Maybe an effort to 'debounce' the
* reading of the DIP switches? Oh-well.
*
* NOTES: Verified with:
* AMI WinBIOS 486 (5A, no IRQ detect, OK, IRQ5 only)
* Microsoft Mouse.com V2.00 (DOS V6.22, 5A, OK)
* Microsoft Mouse.exe V9.1 (DOS V6.22, A5, OK)
* Logitech LMouse.com V6.02 (DOS V6.22)
* Logitech LMouse.com V6.43 (DOS V6.22)
* Microsoft WfW V3.11 on DOS V6.22
* GEOS V1.0 (OK, IRQ5 only)
* GEOS V2.0 (OK, IRQ5 only)
* Microsoft Windows 95 OSR2
* Microsoft Windows 98 SE
* Microsoft Windows NT 3.1
* Microsoft Windows NT 3.51
*
* The polling frequency for InPort controllers has to
* be changed to programmable. Microsoft uses 30Hz,
* but ATIXL ports are programmable 30-200Hz.
*
* Based on an early driver for MINIX 1.5.
*
* Version: @(#)mouse_bus.c 1.0.38 2018/05/23
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
*
* Copyright 1989-2018 Fred N. van Kempen.
*/
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#define HAVE_STDARG_H
#include "86box.h"
#include "config.h"
#include "io.h"
#include "pic.h"
#include "timer.h"
#include "device.h"
#include "mouse.h"
#define MOUSE_PORT 0x023c /* default */
#define MOUSE_IRQ 5 /* default */
#define MOUSE_BUTTONS 2 /* default */
#define MOUSE_DEBUG 0
/* Our mouse device. */
typedef struct mouse {
const char *name; /* name of this device */
int8_t type; /* type of this device */
uint16_t base; /* I/O port base to use */
int8_t irq; /* IRQ channel to use */
uint16_t flags; /* device flags */
uint8_t r_magic, /* MAGIC register */
r_ctrl, /* CONTROL register (WR) */
r_conf, /* CONFIG register */
r_cmd; /* (MS) current command */
uint16_t seq; /* general counter */
uint8_t but, /* current mouse status */
but_last;
uint8_t cur_but;
int8_t x, y;
int x_delay,
y_delay;
uint8_t irq_num;
int64_t timer; /* mouse event timer */
uint8_t (*read)(struct mouse *, uint16_t);
void (*write)(struct mouse *, uint16_t, uint8_t);
} mouse_t;
#define FLAG_NEW 0x100 /* device is the newer variant */
#define FLAG_INPORT 0x80 /* device is MS InPort */
#define FLAG_3BTN 0x20 /* enable 3-button mode */
#define FLAG_SCALED 0x10 /* enable delta scaling */
#define FLAG_INTR 0x04 /* dev can send interrupts */
#define FLAG_FROZEN 0x02 /* do not update counters */
#define FLAG_ENABLED 0x01 /* dev is enabled for use */
/* Definitions for Logitech. */
#define LTMOUSE_DATA 0 /* DATA register */
#define LTMOUSE_MAGIC 1 /* signature magic register */
# define LTMAGIC_BYTE1 0xa5 /* most drivers use this */
# define LTMAGIC_BYTE2 0x5a /* some drivers use this */
#define LTMOUSE_CTRL 2 /* CTRL register */
# define LTCTRL_FREEZE 0x80 /* do not sample when set */
# define LTCTRL_RD_Y_HI 0x60
# define LTCTRL_RD_Y_LO 0x40
# define LTCTRL_RD_X_HI 0x20
# define LTCTRL_RD_X_LO 0x00
# define LTCTRL_RD_MASK 0x60
# define LTCTRL_IDIS 0x10
# define LTCTRL_IENB 0x00
#define LTMOUSE_CONFIG 3 /* CONFIG register */
/* Definitions for Microsoft. */
#define MSMOUSE_CTRL 0 /* CTRL register */
# define MSCTRL_RESET 0x80 /* reset controller */
# define MSCTRL_FREEZE 0x20 /* HOLD- freeze data */
# define MSCTRL_IENB_A 0x08 /* ATIXL intr enable */
# define MSCTRL_IENB_M 0x01 /* MS intr enable */
# define MSCTRL_COMMAND 0x07
# define MSCTRL_RD_Y 0x02
# define MSCTRL_RD_X 0x01
# define MSCTRL_RD_BUT 0x00
#define MSMOUSE_DATA 1 /* DATA register */
# define MSDATA_IRQ 0x16
# define MSDATA_BASE 0x10 /* MS InPort: 30Hz */
# define MSDATA_HZ30 0x01 /* ATIXL 30Hz */
# define MSDATA_HZ50 0x02 /* ATIXL 50Hz */
# define MSDATA_HZ100 0x03 /* ATIXL 100Hz */
# define MSDATA_HZ200 0x04 /* ATIXL 200Hz */
#define MSMOUSE_MAGIC 2 /* MAGIC register */
# define MAGIC_MSBYTE1 0xde /* indicates MS InPort */
# define MAGIC_MSBYTE2 0x12
#define MSMOUSE_CONFIG 3 /* CONFIG register */
#define ENABLE_MOUSE_BUS_LOG 1
#ifdef ENABLE_MOUSE_BUS_LOG
int mouse_bus_do_log = ENABLE_MOUSE_BUS_LOG;
#endif
static void
mouse_bus_log(const char *format, ...)
{
#ifdef ENABLE_MOUSE_BUS_LOG
va_list ap;
if (mouse_bus_do_log) {
va_start(ap, format);
pclog_ex(format, ap);
va_end(ap);
}
#endif
}
/* Reset the controller state. */
static void
ms_reset(mouse_t *dev)
{
dev->r_ctrl = 0x00;
dev->r_cmd = 0x00;
dev->seq = 0;
dev->x = dev->y = 0;
dev->but = 0x00;
dev->flags &= 0xf0;
dev->flags |= (FLAG_INTR | FLAG_ENABLED);
dev->x_delay = dev->y_delay = 0;
dev->cur_but = 0x00;
}
static void
ms_update_data(mouse_t *dev)
{
int delta_x, delta_y;
if (dev->x_delay > 127) {
delta_x = 127;
dev->x_delay -= 127;
} else if (dev->x_delay < -128) {
delta_x = -128;
dev->x_delay += 128;
} else {
delta_x = dev->x_delay;
dev->x_delay = 0;
}
if (dev->y_delay > 127) {
delta_y = 127;
dev->y_delay -= 127;
} else if (dev->y_delay < -128) {
delta_y = -128;
dev->x_delay += 128;
} else {
delta_y = dev->y_delay;
dev->y_delay = 0;
}
if (!(dev->flags & FLAG_FROZEN)) {
dev->x = (int8_t) delta_x;
dev->y = (int8_t) delta_y;
dev->cur_but = dev->but;
}
}
/* Handle a WRITE to an InPort register. */
static void
ms_write(mouse_t *dev, uint16_t port, uint8_t val)
{
uint8_t valxor;
switch (port) {
case MSMOUSE_CTRL:
/* Bit 7 is reset. */
if (val & MSCTRL_RESET)
ms_reset(dev);
/* Bits 0-2 are the internal register index. */
switch (val & 0x07) {
case MSCTRL_COMMAND:
case MSCTRL_RD_BUT:
case MSCTRL_RD_X:
case MSCTRL_RD_Y:
dev->r_cmd = val & 0x07;
break;
}
break;
case MSMOUSE_DATA:
picintc(1 << dev->irq);
if (val == MSDATA_IRQ)
picint(1 << dev->irq);
else {
switch (dev->r_cmd) {
case MSCTRL_COMMAND:
valxor = (dev->r_ctrl ^ val);
if (valxor & MSCTRL_FREEZE) {
if (val & MSCTRL_FREEZE) {
/* Hold the sampling while we do something. */
dev->flags |= FLAG_FROZEN;
} else {
/* Reset current state. */
dev->flags &= ~FLAG_FROZEN;
dev->x = dev->y = 0;
dev->but = 0;
}
}
if (val & (MSCTRL_IENB_M | MSCTRL_IENB_A))
dev->flags |= FLAG_INTR;
else
dev->flags &= ~FLAG_INTR;
dev->r_ctrl = val;
break;
default:
break;
}
}
break;
case MSMOUSE_MAGIC:
break;
case MSMOUSE_CONFIG:
break;
}
}
/* Handle a READ from an InPort register. */
static uint8_t
ms_read(mouse_t *dev, uint16_t port)
{
uint8_t ret = 0x00;
switch (port) {
case MSMOUSE_CTRL:
ret = dev->r_ctrl;
break;
case MSMOUSE_DATA:
switch (dev->r_cmd) {
case MSCTRL_RD_BUT:
ret = dev->cur_but;
if (dev->flags & FLAG_NEW)
ret |= 0x40; /* On new InPort, always have bit 6 set. */
break;
case MSCTRL_RD_X:
ret = dev->x;
break;
case MSCTRL_RD_Y:
ret = dev->y;
break;
case MSCTRL_COMMAND:
ret = dev->r_ctrl;
break;
}
break;
case MSMOUSE_MAGIC:
if (dev->seq & 0x01)
ret = MAGIC_MSBYTE2;
else
ret = MAGIC_MSBYTE1;
dev->seq ^= 1;
break;
case MSMOUSE_CONFIG:
/* Not really present in real hardware. */
break;
}
return(ret);
}
/* Reset the controller state. */
static void
lt_reset(mouse_t *dev)
{
dev->r_magic = 0x00;
dev->r_ctrl = (LTCTRL_IENB);
dev->r_conf = 0x00;
dev->seq = 0;
dev->x = dev->y = 0;
dev->but = 0x00;
dev->flags &= 0xf0;
dev->flags |= (FLAG_INTR | FLAG_ENABLED);
dev->irq_num = 0;
}
/* Called at 30hz */
static void
bm_timer(void *priv)
{
mouse_t *dev = (mouse_t *)priv;
if (dev->flags & FLAG_INPORT) {
dev->timer = ((1000000LL * TIMER_USEC) / 30LL);
ms_update_data(dev);
if (dev->flags & FLAG_INTR)
picint(1 << dev->irq);
} else {
picint(1 << dev->irq);
if (dev->irq_num == 5) {
mouse_bus_log("5th IRQ, enabling mouse...\n");
lt_reset(dev);
dev->flags |= FLAG_ENABLED;
}
if (dev->irq_num == 4) {
mouse_bus_log("4th IRQ, going for the 5th...\n");
dev->irq_num++;
dev->timer = ((1000000LL * TIMER_USEC) / 30LL);
} else {
mouse_bus_log("IRQ before the 4th, disabling timer...\n");
dev->timer = 0;
}
}
}
/* Handle a WRITE to a Logitech register. */
static void
lt_write(mouse_t *dev, uint16_t port, uint8_t val)
{
uint8_t b;
switch (port) {
case LTMOUSE_DATA: /* [00] data register */
break;
case LTMOUSE_MAGIC: /* [01] magic data register */
switch(val) {
case LTMAGIC_BYTE1:
case LTMAGIC_BYTE2:
lt_reset(dev);
dev->r_magic = val;
// dev->flags |= FLAG_ENABLED;
break;
}
break;
case LTMOUSE_CTRL: /* [02] control register */
#if 0
if (!(dev->flags & FLAG_ENABLED)) {
dev->irq_num++;
dev->timer = ((1000000LL * TIMER_USEC) / 30LL);
break;
}
#endif
b = (dev->r_ctrl ^ val);
if (b & LTCTRL_FREEZE) {
if (val & LTCTRL_FREEZE) {
/* Hold the sampling while we do something. */
dev->flags |= FLAG_FROZEN;
} else {
/* Reset current state. */
dev->flags &= ~FLAG_FROZEN;
dev->x = dev->y = 0;
if (dev->but)
dev->but |= 0x80;
}
}
if (b & LTCTRL_IDIS) {
/* Disable or enable interrupts. */
if (val & LTCTRL_IDIS)
dev->flags &= ~FLAG_INTR;
else
dev->flags |= FLAG_INTR;
}
/* Save new register value. */
dev->r_ctrl = val | 0x0F;
/* Clear any pending interrupts. */
if (val & LTCTRL_FREEZE) {
mouse_bus_log("Freeze: Raise IRQ %i\n", dev->irq);
picint(1 << dev->irq); /* Microsoft MOUSE.SYS v3.0 seems to expect every freeze to send an IRQ? */
} else {
mouse_bus_log("No freeze: Lower IRQ %i\n", dev->irq);
picintc(1 << dev->irq);
}
break;
case LTMOUSE_CONFIG: /* [03] config register */
/*
* The original Logitech design was based on using a
* 8255 parallel I/O chip. This chip has to be set up
* for proper operation, and this configuration data
* is what is programmed into this register.
*
* A snippet of code found in the FreeBSD kernel source
* explains the value:
*
* D7 = Mode set flag (1 = active)
* D6,D5 = Mode selection (port A)
* 00 = Mode 0 = Basic I/O
* 01 = Mode 1 = Strobed I/O
* 10 = Mode 2 = Bi-dir bus
* D4 = Port A direction (1 = input)
* D3 = Port C (upper 4 bits) direction. (1 = input)
* D2 = Mode selection (port B & C)
* 0 = Mode 0 = Basic I/O
* 1 = Mode 1 = Strobed I/O
* D1 = Port B direction (1 = input)
* D0 = Port C (lower 4 bits) direction. (1 = input)
*
* So 91 means Basic I/O on all 3 ports, Port A is an input
* port, B is an output port, C is split with upper 4 bits
* being an output port and lower 4 bits an input port, and
* enable the sucker. Courtesy Intel 8255 databook. Lars
*/
dev->r_conf = val;
break;
default:
break;
}
}
static int
lt_read_int(mouse_t *dev)
{
if (!(dev->flags & FLAG_NEW))
return 1; /* On old LogiBus, read the IRQ bits always. */
if (dev->flags & FLAG_INTR)
return 1; /* On new LogiBus, read the IRQ bits if interrupts are enabled. */
return 0; /* Otherwise, do not. */
}
/* Handle a READ from a Logitech register. */
static uint8_t
lt_read(mouse_t *dev, uint16_t port)
{
uint8_t ret = 0xff;
/* The GEOS drivers actually check this. */
if (! (dev->flags & FLAG_ENABLED)) return(ret);
switch (port) {
case LTMOUSE_DATA: /* [00] data register */
ret = 0x07;
if (dev->but & 0x01) /* LEFT */
ret &= ~0x04;
if (dev->but & 0x02) /* RIGHT */
ret &= ~0x01;
if (dev->flags & FLAG_3BTN)
if (dev->but & 0x04) /* MIDDLE */
ret &= ~0x02;
ret <<= 5;
switch(dev->r_ctrl & LTCTRL_RD_MASK) {
case LTCTRL_RD_X_LO: /* X, low bits */
ret |= (dev->x & 0x0f);
break;
case LTCTRL_RD_X_HI: /* X, high bits */
ret |= (dev->x >> 4) & 0x0f;
break;
case LTCTRL_RD_Y_LO: /* Y, low bits */
ret |= (dev->y & 0x0f);
break;
case LTCTRL_RD_Y_HI: /* Y, high bits */
ret |= (dev->y >> 4) & 0x0f;
break;
}
break;
case LTMOUSE_MAGIC: /* [01] magic data register */
/*
* Drivers write a magic byte to this register, usually
* this is either 5A (AMI WinBIOS, MS Mouse 2.0) or
* A5 (MS Mouse 9.1, Windows drivers, UNIX/Linux/Minix.)
*/
ret = dev->r_magic;
break;
case LTMOUSE_CTRL: /* [02] control register */
ret = dev->r_ctrl;
dev->r_ctrl |= 0x0F;
if ((dev->seq > 0x3FF) && lt_read_int(dev)) {
/* !IDIS, return DIP switch setting. */
switch(dev->irq) {
case 2:
dev->r_ctrl &= ~0x08;
break;
case 3:
dev->r_ctrl &= ~0x04;
break;
case 4:
dev->r_ctrl &= ~0x02;
break;
case 5:
dev->r_ctrl &= ~0x01;
break;
}
}
dev->seq = (dev->seq + 1) & 0x7ff;
break;
case LTMOUSE_CONFIG: /* [03] config register */
ret = dev->r_conf;
break;
default:
break;
}
return(ret);
}
/* Handle a WRITE operation to one of our registers. */
static void
bm_write(uint16_t port, uint8_t val, void *priv)
{
mouse_t *dev = (mouse_t *)priv;
mouse_bus_log("%s: write(%d,%02x)\n", dev->name, port & 0x03, val);
dev->write(dev, port & 0x03, val);
}
/* Handle a READ operation from one of our registers. */
static uint8_t
bm_read(uint16_t port, void *priv)
{
mouse_t *dev = (mouse_t *)priv;
uint8_t ret;
ret = dev->read(dev, port & 0x03);
mouse_bus_log("%s: read(%d): %02x\n", dev->name, port & 0x03, ret);
return(ret);
}
/* The emulator calls us with an update on the host mouse device. */
static int
bm_poll(int x, int y, int z, int b, void *priv)
{
uint8_t b_last;
mouse_t *dev = (mouse_t *)priv;
b_last = dev->but;
/* Return early if nothing to do. */
if (!x && !y && !z && (dev->but == b))
return(1);
/* If we are not enabled, return. */
if (! (dev->flags & FLAG_ENABLED))
mouse_bus_log("bm_poll(): Mouse not enabled\n");
if (dev->flags & FLAG_SCALED) {
/* Scale down the motion. */
if ((x < -1) || (x > 1)) x >>= 1;
if ((y < -1) || (y > 1)) y >>= 1;
}
if (dev->flags & FLAG_INPORT) {
if (x || y || z)
dev->but = 0x40; /* Mouse has moved. */
else
dev->but = 0x00;
if (x > 127) x = 127;
if (y > 127) y = 127;
if (x < -128) x = -128;
if (y < -128) y = -128;
dev->x_delay += x;
dev->y_delay += y;
dev->but |= (uint8_t) (((b & 1) << 2) | ((b & 2) >> 1));
if (dev->flags & FLAG_3BTN)
dev->but |= ((b & 4) >> 1);
if ((b_last ^ dev->but) & 0x04)
dev->but |= 0x20; /* Left button state has changed. */
if (((b_last ^ dev->but) & 0x02) && (dev->flags & FLAG_3BTN))
dev->but |= 0x10; /* Middle button state has changed. */
if ((b_last ^ dev->but) & 0x01)
dev->but |= 0x08; /* Right button state has changed. */
dev->but |= 0x80; /* Packet complete. */
} else {
/* If we are frozen, do not update the state. */
if (! (dev->flags & FLAG_FROZEN)) {
/* Add the delta to our state. */
x += dev->x;
if (x > 127)
x = 127;
if (x < -128)
x = -128;
dev->x = (int8_t)x;
y += dev->y;
if (y > 127)
y = 127;
if (y < -1287)
y = -1287;
dev->y = (int8_t)y;
dev->x_delay += x;
dev->y_delay += y;
dev->but = b;
}
/* Either way, generate an interrupt. */
if ((dev->flags & FLAG_INTR) && !(dev->flags & FLAG_INPORT))
picint(1 << dev->irq);
}
return(0);
}
/* Release all resources held by the device. */
static void
bm_close(void *priv)
{
mouse_t *dev = (mouse_t *)priv;
/* Release our I/O range. */
io_removehandler(dev->base, 4,
bm_read, NULL, NULL, bm_write, NULL, NULL, dev);
free(dev);
}
/* Initialize the device for use by the user. */
static void *
bm_init(const device_t *info)
{
mouse_t *dev;
int i, j;
dev = (mouse_t *)malloc(sizeof(mouse_t));
memset(dev, 0x00, sizeof(mouse_t));
dev->name = info->name;
dev->type = info->local;
dev->base = device_get_config_hex16("base");
dev->irq = device_get_config_int("irq");
i = device_get_config_int("buttons");
if (i > 2)
dev->flags |= FLAG_3BTN;
j = device_get_config_int("model");
if (j)
dev->flags |= FLAG_NEW;
switch(dev->type) {
case MOUSE_TYPE_LOGIBUS:
lt_reset(dev);
/* Initialize I/O handlers. */
dev->read = lt_read;
dev->write = lt_write;
// dev->timer = 0;
// timer_add(bm_timer, &dev->timer, &dev->timer, dev);
break;
case MOUSE_TYPE_INPORT:
dev->flags |= FLAG_INPORT;
ms_reset(dev);
/* Initialize I/O handlers. */
dev->read = ms_read;
dev->write = ms_write;
dev->timer = (33334LL * TIMER_USEC);
timer_add(bm_timer, &dev->timer, TIMER_ALWAYS_ENABLED, dev);
break;
}
/* Request an I/O range. */
io_sethandler(dev->base, 4,
bm_read, NULL, NULL, bm_write, NULL, NULL, dev);
mouse_bus_log("%s: I/O=%04x, IRQ=%d, buttons=%d, model=%s\n",
dev->name, dev->base, dev->irq, i, j ? "new" : "old");
/* Tell them how many buttons we have. */
mouse_set_buttons((dev->flags & FLAG_3BTN) ? 3 : 2);
/* Return our private data to the I/O layer. */
return(dev);
}
static const device_config_t bm_config[] = {
{
"base", "Address", CONFIG_HEX16, "", MOUSE_PORT,
{
{
"0x230", 0x230
},
{
"0x234", 0x234
},
{
"0x238", 0x238
},
{
"0x23C", 0x23c
},
{
""
}
}
},
{
"irq", "IRQ", CONFIG_SELECTION, "", MOUSE_IRQ, {
{
"IRQ 2", 2
},
{
"IRQ 3", 3
},
{
"IRQ 4", 4
},
{
"IRQ 5", 5
},
{
""
}
}
},
{
"buttons", "Buttons", CONFIG_SELECTION, "", MOUSE_BUTTONS, {
{
"Two", 2
},
{
"Three", 3
},
{
""
}
}
},
{
"model", "Model", CONFIG_SELECTION, "", 0, {
{
"Old", 0
},
{
"New", 1
},
{
""
}
}
},
{
"", "", -1
}
};
const device_t mouse_logibus_device = {
"Logitech Bus Mouse",
DEVICE_ISA,
MOUSE_TYPE_LOGIBUS,
bm_init, bm_close, NULL,
bm_poll, NULL, NULL,
bm_config
};
const device_t mouse_msinport_device = {
"Microsoft Bus Mouse (InPort)",
DEVICE_ISA,
MOUSE_TYPE_INPORT,
bm_init, bm_close, NULL,
bm_poll, NULL, NULL,
bm_config
};