More reorganization and finally merged the two makefiles.
This commit is contained in:
365
src/device/bugger.c
Normal file
365
src/device/bugger.c
Normal file
@@ -0,0 +1,365 @@
|
||||
/*
|
||||
* 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 the ISA Bus (de)Bugger expansion card
|
||||
* sold as a DIY kit in the late 1980's in The Netherlands.
|
||||
* This card was a assemble-yourself 8bit ISA addon card for
|
||||
* PC and AT systems that had several tools to aid in low-
|
||||
* level debugging (mostly for faulty BIOSes, bootloaders
|
||||
* and system kernels...)
|
||||
*
|
||||
* The standard version had a total of 16 LEDs (8 RED, plus
|
||||
* 8 GREEN), two 7-segment displays and one 8-position DIP
|
||||
* switch block on board for use as debugging tools.
|
||||
*
|
||||
* The "Plus" version, added an extra 2 7-segment displays,
|
||||
* as well as a very simple RS-232 serial interface that
|
||||
* could be used as a mini-console terminal.
|
||||
*
|
||||
* Two I/O ports were used; one for control, at offset 0 in
|
||||
* I/O space, and one for data, at offset 1 in I/O space.
|
||||
* Both registers could be read from and written to. Although
|
||||
* the author has a vague memory of a DIP switch to set the
|
||||
* board's I/O address, comments in old software seems to
|
||||
* indicate that it was actually fixed to 0x7A (and 0x7B.)
|
||||
*
|
||||
* A READ on the data register always returned the actual
|
||||
* state of the DIP switch. Writing data to the LEDs was done
|
||||
* in two steps.. first, the block number (RED or GREEN) was
|
||||
* written to the CTRL register, and then the actual LED data
|
||||
* was written to the DATA register. Likewise, data for the
|
||||
* 7-segment displays was written.
|
||||
*
|
||||
* The serial port was a bit different, and its operation is
|
||||
* not verified, but two extra bits in the control register
|
||||
* were used to set up parameters, and also the actual data
|
||||
* input and output.
|
||||
*
|
||||
* TODO: Still have to implement the RS232 Serial Port Parameters
|
||||
* configuration register (CTRL_SPCFG bit set) but have to
|
||||
* remember that stuff first...
|
||||
*
|
||||
*
|
||||
*
|
||||
* Author: Fred N. van Kempen, <decwiz@yahoo.com>
|
||||
* Copyright 1989-2018 Fred N. van Kempen.
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#define HAVE_STDARG_H
|
||||
#include <86box/86box.h>
|
||||
#include <86box/io.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/plat.h>
|
||||
#include <86box/ui.h>
|
||||
#include <86box/bugger.h>
|
||||
|
||||
|
||||
/* BugBugger registers. */
|
||||
#define BUG_CTRL 0
|
||||
# define CTRL_RLED 0x00 /* write to the RED LED block */
|
||||
# define CTRL_GLED 0x01 /* write to the GREEN LED block */
|
||||
# define CTRL_SEG1 0x02 /* write to the RIGHT 7SEG displays */
|
||||
# define CTRL_SEG2 0x04 /* write to the LEFT 7SEG displays */
|
||||
# define CTRL_SPORT 0x20 /* enable the serial port */
|
||||
# define CTRL_SPCFG 0x40 /* set up the serial port */
|
||||
# define CTRL_INIT 0x80 /* enable and reset the card */
|
||||
# define CTRL_RESET 0xff /* this resets the board */
|
||||
#define BUG_DATA 1
|
||||
|
||||
|
||||
static uint8_t bug_ctrl, /* control register */
|
||||
bug_data, /* data register */
|
||||
bug_ledr, bug_ledg, /* RED and GREEN LEDs */
|
||||
bug_seg1, bug_seg2, /* LEFT and RIGHT 7SEG displays */
|
||||
bug_spcfg; /* serial port configuration */
|
||||
# define FIFO_LEN 256
|
||||
static uint8_t bug_buff[FIFO_LEN], /* serial port data buffer */
|
||||
*bug_bptr;
|
||||
# define UISTR_LEN 24
|
||||
static char bug_str[UISTR_LEN]; /* UI output string */
|
||||
|
||||
|
||||
extern void ui_sb_bugui(char *__str);
|
||||
|
||||
|
||||
#ifdef ENABLE_BUGGER_LOG
|
||||
int bugger_do_log = ENABLE_BUGGER_LOG;
|
||||
|
||||
|
||||
static void
|
||||
bugger_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (bugger_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define bugger_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
|
||||
/* Update the system's UI with the actual Bugger status. */
|
||||
static void
|
||||
bug_setui(void)
|
||||
{
|
||||
/* Format all current info in a string. */
|
||||
sprintf(bug_str, "%02X:%02X %c%c%c%c%c%c%c%c-%c%c%c%c%c%c%c%c",
|
||||
bug_seg2, bug_seg1,
|
||||
(bug_ledg&0x80)?'G':'g', (bug_ledg&0x40)?'G':'g',
|
||||
(bug_ledg&0x20)?'G':'g', (bug_ledg&0x10)?'G':'g',
|
||||
(bug_ledg&0x08)?'G':'g', (bug_ledg&0x04)?'G':'g',
|
||||
(bug_ledg&0x02)?'G':'g', (bug_ledg&0x01)?'G':'g',
|
||||
(bug_ledr&0x80)?'R':'r', (bug_ledr&0x40)?'R':'r',
|
||||
(bug_ledr&0x20)?'R':'r', (bug_ledr&0x10)?'R':'r',
|
||||
(bug_ledr&0x08)?'R':'r', (bug_ledr&0x04)?'R':'r',
|
||||
(bug_ledr&0x02)?'R':'r', (bug_ledr&0x01)?'R':'r');
|
||||
|
||||
/* Send formatted string to the UI. */
|
||||
ui_sb_bugui(bug_str);
|
||||
}
|
||||
|
||||
|
||||
/* Flush the serial port. */
|
||||
static void
|
||||
bug_spflsh(void)
|
||||
{
|
||||
*bug_bptr = '\0';
|
||||
bugger_log("BUGGER- serial port [%s]\n", bug_buff);
|
||||
bug_bptr = bug_buff;
|
||||
}
|
||||
|
||||
|
||||
/* Handle a write to the Serial Port Data register. */
|
||||
static void
|
||||
bug_wsport(uint8_t val)
|
||||
{
|
||||
uint8_t old = bug_ctrl;
|
||||
|
||||
/* Clear the SPORT bit to indicate we are busy. */
|
||||
bug_ctrl &= ~CTRL_SPORT;
|
||||
|
||||
/* Delay while processing byte.. */
|
||||
if (bug_bptr == &bug_buff[FIFO_LEN-1]) {
|
||||
/* Buffer full, gotta flush. */
|
||||
bug_spflsh();
|
||||
}
|
||||
|
||||
/* Write (store) the byte. */
|
||||
*bug_bptr++ = val;
|
||||
|
||||
/* Restore the SPORT bit. */
|
||||
bug_ctrl |= (old & CTRL_SPORT);
|
||||
|
||||
bugger_log("BUGGER- sport %02x\n", val);
|
||||
}
|
||||
|
||||
|
||||
/* Handle a write to the Serial Port Configuration register. */
|
||||
static void
|
||||
bug_wspcfg(uint8_t val)
|
||||
{
|
||||
bug_spcfg = val;
|
||||
|
||||
bugger_log("BUGGER- spcfg %02x\n", bug_spcfg);
|
||||
}
|
||||
|
||||
|
||||
/* Handle a write to the control register. */
|
||||
static void
|
||||
bug_wctrl(uint8_t val)
|
||||
{
|
||||
if (val == CTRL_RESET) {
|
||||
/* User wants us to reset. */
|
||||
bug_ctrl = CTRL_INIT;
|
||||
bug_spcfg = 0x00;
|
||||
bug_bptr = NULL;
|
||||
} else {
|
||||
/* If turning off the serial port, flush it. */
|
||||
if ((bug_ctrl & CTRL_SPORT) && !(val & CTRL_SPORT))
|
||||
bug_spflsh();
|
||||
|
||||
/* FIXME: did they do this using an XOR of operation bits? --FvK */
|
||||
|
||||
if (val & CTRL_SPCFG) {
|
||||
/* User wants to configure the serial port. */
|
||||
bug_ctrl &= ~(CTRL_SPORT|CTRL_SEG2|CTRL_SEG1|CTRL_GLED);
|
||||
bug_ctrl |= CTRL_SPCFG;
|
||||
} else if (val & CTRL_SPORT) {
|
||||
/* User wants to talk to the serial port. */
|
||||
bug_ctrl &= ~(CTRL_SPCFG|CTRL_SEG2|CTRL_SEG1|CTRL_GLED);
|
||||
bug_ctrl |= CTRL_SPORT;
|
||||
if (bug_bptr == NULL)
|
||||
bug_bptr = bug_buff;
|
||||
} else if (val & CTRL_SEG2) {
|
||||
/* User selected SEG2 (LEFT, Plus only) for output. */
|
||||
bug_ctrl &= ~(CTRL_SPCFG|CTRL_SPORT|CTRL_SEG1|CTRL_GLED);
|
||||
bug_ctrl |= CTRL_SEG2;
|
||||
} else if (val & CTRL_SEG1) {
|
||||
/* User selected SEG1 (RIGHT) for output. */
|
||||
bug_ctrl &= ~(CTRL_SPCFG|CTRL_SPORT|CTRL_SEG2|CTRL_GLED);
|
||||
bug_ctrl |= CTRL_SEG1;
|
||||
} else if (val & CTRL_GLED) {
|
||||
/* User selected the GREEN LEDs for output. */
|
||||
bug_ctrl &= ~(CTRL_SPCFG|CTRL_SPORT|CTRL_SEG2|CTRL_SEG1);
|
||||
bug_ctrl |= CTRL_GLED;
|
||||
} else {
|
||||
/* User selected the RED LEDs for output. */
|
||||
bug_ctrl &=
|
||||
~(CTRL_SPCFG|CTRL_SPORT|CTRL_SEG2|CTRL_SEG1|CTRL_GLED);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the UI with active settings. */
|
||||
bugger_log("BUGGER- ctrl %02x\n", bug_ctrl);
|
||||
bug_setui();
|
||||
}
|
||||
|
||||
|
||||
/* Handle a write to the data register. */
|
||||
static void
|
||||
bug_wdata(uint8_t val)
|
||||
{
|
||||
bug_data = val;
|
||||
|
||||
if (bug_ctrl & CTRL_SPCFG)
|
||||
bug_wspcfg(val);
|
||||
else if (bug_ctrl & CTRL_SPORT)
|
||||
bug_wsport(val);
|
||||
else {
|
||||
if (bug_ctrl & CTRL_SEG2)
|
||||
bug_seg2 = val;
|
||||
else if (bug_ctrl & CTRL_SEG1)
|
||||
bug_seg1 = val;
|
||||
else if (bug_ctrl & CTRL_GLED)
|
||||
bug_ledg = val;
|
||||
else
|
||||
bug_ledr = val;
|
||||
|
||||
bugger_log("BUGGER- data %02x\n", bug_data);
|
||||
}
|
||||
|
||||
/* Update the UI with active settings. */
|
||||
bug_setui();
|
||||
}
|
||||
|
||||
|
||||
/* Reset the ISA BusBugger controller. */
|
||||
static void
|
||||
bug_reset(void)
|
||||
{
|
||||
/* Clear the data register. */
|
||||
bug_data = 0x00;
|
||||
|
||||
/* Clear the RED and GREEN LEDs. */
|
||||
bug_ledr = 0x00; bug_ledg = 0x00;
|
||||
|
||||
/* Clear both 7SEG displays. */
|
||||
bug_seg1 = 0x00; bug_seg2 = 0x00;
|
||||
|
||||
/* Reset the control register (updates UI.) */
|
||||
bug_wctrl(CTRL_RESET);
|
||||
}
|
||||
|
||||
|
||||
/* Handle a WRITE operation to one of our registers. */
|
||||
static void
|
||||
bug_write(uint16_t port, uint8_t val, void *priv)
|
||||
{
|
||||
switch (port-BUGGER_ADDR) {
|
||||
case BUG_CTRL: /* control register */
|
||||
if (val == CTRL_RESET) {
|
||||
/* Perform a full reset. */
|
||||
bug_reset();
|
||||
} else if (bug_ctrl & CTRL_INIT) {
|
||||
/* Only allow writes if initialized. */
|
||||
bug_wctrl(val);
|
||||
}
|
||||
break;
|
||||
|
||||
case BUG_DATA: /* data register */
|
||||
if (bug_ctrl & CTRL_INIT) {
|
||||
bug_wdata(val);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Handle a READ operation from one of our registers. */
|
||||
static uint8_t
|
||||
bug_read(uint16_t port, void *priv)
|
||||
{
|
||||
uint8_t ret = 0xff;
|
||||
|
||||
if (bug_ctrl & CTRL_INIT) switch (port-BUGGER_ADDR) {
|
||||
case BUG_CTRL: /* control register */
|
||||
ret = bug_ctrl;
|
||||
break;
|
||||
|
||||
case BUG_DATA: /* data register */
|
||||
if (bug_ctrl & CTRL_SPCFG) {
|
||||
ret = bug_spcfg;
|
||||
} else if (bug_ctrl & CTRL_SPORT) {
|
||||
ret = 0x00; /* input not supported */
|
||||
} else {
|
||||
/* Just read the DIP switch. */
|
||||
ret = bug_data;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
/* Initialize the ISA BusBugger emulator. */
|
||||
static void *
|
||||
bug_init(const device_t *info)
|
||||
{
|
||||
bugger_log("%s, I/O=%04x\n", info->name, BUGGER_ADDR);
|
||||
|
||||
/* Initialize local registers. */
|
||||
bug_reset();
|
||||
|
||||
io_sethandler(BUGGER_ADDR, BUGGER_ADDRLEN,
|
||||
bug_read, NULL, NULL, bug_write, NULL, NULL, NULL);
|
||||
|
||||
/* Just so its not NULL. */
|
||||
return(&bug_ctrl);
|
||||
}
|
||||
|
||||
|
||||
/* Remove the ISA BusBugger emulator from the system. */
|
||||
static void
|
||||
bug_close(UNUSED(void *priv))
|
||||
{
|
||||
io_removehandler(BUGGER_ADDR, BUGGER_ADDRLEN,
|
||||
bug_read, NULL, NULL, bug_write, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
const device_t bugger_device = {
|
||||
"ISA/PCI Bus Bugger",
|
||||
DEVICE_ISA | DEVICE_AT,
|
||||
0,
|
||||
bug_init, bug_close, NULL,
|
||||
NULL, NULL, NULL,
|
||||
NULL
|
||||
};
|
||||
37
src/device/hwm.c
Normal file
37
src/device/hwm.c
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Common functions for hardware monitoring chips.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Author: RichardG, <richardg867@gmail.com>
|
||||
*
|
||||
* Copyright 2020 RichardG.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/hwm.h>
|
||||
|
||||
|
||||
hwm_values_t hwm_values;
|
||||
|
||||
|
||||
void
|
||||
hwm_set_values(hwm_values_t new_values)
|
||||
{
|
||||
hwm_values = new_values;
|
||||
}
|
||||
|
||||
|
||||
hwm_values_t*
|
||||
hwm_get_values()
|
||||
{
|
||||
return &hwm_values;
|
||||
}
|
||||
269
src/device/hwm_lm75.c
Normal file
269
src/device/hwm_lm75.c
Normal file
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Emulation of the National Semiconductor LM75 temperature sensor chip.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Author: RichardG, <richardg867@gmail.com>
|
||||
*
|
||||
* Copyright 2020 RichardG.
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#define HAVE_STDARG_H
|
||||
#include <wchar.h>
|
||||
#include <86box/86box.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/smbus.h>
|
||||
#include <86box/hwm.h>
|
||||
|
||||
|
||||
#define LM75_TEMP_TO_REG(t) ((t) << 8)
|
||||
|
||||
|
||||
static uint8_t lm75_smbus_read_byte(uint8_t addr, void *priv);
|
||||
static uint8_t lm75_smbus_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv);
|
||||
static uint16_t lm75_smbus_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv);
|
||||
static void lm75_smbus_write_byte(uint8_t addr, uint8_t val, void *priv);
|
||||
static void lm75_smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val, void *priv);
|
||||
static void lm75_smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val, void *priv);
|
||||
static void lm75_reset(lm75_t *dev);
|
||||
|
||||
|
||||
#ifdef ENABLE_LM75_LOG
|
||||
int lm75_do_log = ENABLE_LM75_LOG;
|
||||
|
||||
|
||||
static void
|
||||
lm75_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (lm75_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define lm75_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
|
||||
void
|
||||
lm75_remap(lm75_t *dev)
|
||||
{
|
||||
lm75_log("LM75: remapping to SMBus %02Xh\n", dev->smbus_addr);
|
||||
|
||||
smbus_removehandler(dev->smbus_addr, 1,
|
||||
lm75_smbus_read_byte, lm75_smbus_read_byte_cmd, lm75_smbus_read_word_cmd, NULL,
|
||||
lm75_smbus_write_byte, lm75_smbus_write_byte_cmd, lm75_smbus_write_word_cmd, NULL,
|
||||
dev);
|
||||
|
||||
if (dev->smbus_addr) smbus_sethandler(dev->smbus_addr, 1,
|
||||
lm75_smbus_read_byte, lm75_smbus_read_byte_cmd, lm75_smbus_read_word_cmd, NULL,
|
||||
lm75_smbus_write_byte, lm75_smbus_write_byte_cmd, lm75_smbus_write_word_cmd, NULL,
|
||||
dev);
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
lm75_smbus_read_byte(uint8_t addr, void *priv)
|
||||
{
|
||||
lm75_t *dev = (lm75_t *) priv;
|
||||
return lm75_read(dev, dev->addr_register);
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
lm75_smbus_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv)
|
||||
{
|
||||
lm75_t *dev = (lm75_t *) priv;
|
||||
return lm75_read(dev, cmd);
|
||||
}
|
||||
|
||||
static uint16_t
|
||||
lm75_smbus_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv)
|
||||
{
|
||||
lm75_t *dev = (lm75_t *) priv;
|
||||
uint8_t rethi = 0;
|
||||
uint8_t retlo = 0;
|
||||
|
||||
switch (cmd & 0x3) {
|
||||
case 0x0: /* temperature */
|
||||
rethi = lm75_read(dev, 0x0);
|
||||
retlo = lm75_read(dev, 0x1);
|
||||
break;
|
||||
case 0x1: /* configuration */
|
||||
rethi = retlo = lm75_read(dev, 0x2);
|
||||
break;
|
||||
case 0x2: /* Thyst */
|
||||
rethi = lm75_read(dev, 0x3);
|
||||
retlo = lm75_read(dev, 0x4);
|
||||
break;
|
||||
case 0x3: /* Tos */
|
||||
rethi = lm75_read(dev, 0x5);
|
||||
retlo = lm75_read(dev, 0x6);
|
||||
break;
|
||||
}
|
||||
|
||||
return (retlo << 8) | rethi; /* byte-swapped for some reason */
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
lm75_read(lm75_t *dev, uint8_t reg)
|
||||
{
|
||||
uint8_t ret;
|
||||
|
||||
/* The AS99127F hardware monitor uses the addresses of its LM75 devices
|
||||
to access some of its proprietary registers. Pass this operation on to
|
||||
the main monitor address through an internal SMBus call, if necessary. */
|
||||
if ((reg > 0x7) && ((reg & 0xf8) != 0x50) && (dev->as99127f_smbus_addr))
|
||||
ret = smbus_read_byte_cmd(dev->as99127f_smbus_addr, reg);
|
||||
else
|
||||
ret = dev->regs[reg & 0x7];
|
||||
|
||||
lm75_log("LM75: read(%02X) = %02X\n", reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
lm75_smbus_write_byte(uint8_t addr, uint8_t val, void *priv)
|
||||
{
|
||||
lm75_t *dev = (lm75_t *) priv;
|
||||
dev->addr_register = val;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
lm75_smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val, void *priv)
|
||||
{
|
||||
lm75_t *dev = (lm75_t *) priv;
|
||||
lm75_write(dev, cmd, val);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
lm75_smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val, void *priv)
|
||||
{
|
||||
lm75_t *dev = (lm75_t *) priv;
|
||||
uint8_t valhi = (val >> 8);
|
||||
uint8_t vallo = (val & 0xff);
|
||||
|
||||
switch (cmd & 0x3) {
|
||||
case 0x0: /* temperature */
|
||||
lm75_write(dev, 0x0, valhi);
|
||||
lm75_write(dev, 0x1, vallo);
|
||||
break;
|
||||
case 0x1: /* configuration */
|
||||
lm75_write(dev, 0x2, vallo);
|
||||
break;
|
||||
case 0x2: /* Thyst */
|
||||
lm75_write(dev, 0x3, valhi);
|
||||
lm75_write(dev, 0x4, vallo);
|
||||
break;
|
||||
case 0x3: /* Tos */
|
||||
lm75_write(dev, 0x5, valhi);
|
||||
lm75_write(dev, 0x6, vallo);
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
lm75_write(lm75_t *dev, uint8_t reg, uint8_t val)
|
||||
{
|
||||
lm75_log("LM75: write(%02X, %02X)\n", reg, val);
|
||||
|
||||
/* The AS99127F hardware monitor uses the addresses of its LM75 devices
|
||||
to access some of its proprietary registers. Pass this operation on to
|
||||
the main monitor address through an internal SMBus call, if necessary. */
|
||||
if ((reg > 0x7) && ((reg & 0xf8) != 0x50) && (dev->as99127f_smbus_addr)) {
|
||||
smbus_write_byte_cmd(dev->as99127f_smbus_addr, reg, val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t reg_idx = (reg & 0x7);
|
||||
|
||||
if ((reg_idx <= 0x1) || (reg_idx == 0x7))
|
||||
return 0; /* read-only registers */
|
||||
|
||||
dev->regs[reg_idx] = val;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
lm75_reset(lm75_t *dev)
|
||||
{
|
||||
uint16_t temp = LM75_TEMP_TO_REG(dev->values->temperatures[dev->local >> 8]);
|
||||
dev->regs[0x0] = (temp >> 8);
|
||||
dev->regs[0x1] = temp;
|
||||
dev->regs[0x3] = 0x4b;
|
||||
dev->regs[0x5] = 0x50;
|
||||
|
||||
lm75_remap(dev);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
lm75_close(void *priv)
|
||||
{
|
||||
lm75_t *dev = (lm75_t *) priv;
|
||||
free(dev);
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
lm75_init(const device_t *info)
|
||||
{
|
||||
lm75_t *dev = (lm75_t *) malloc(sizeof(lm75_t));
|
||||
memset(dev, 0, sizeof(lm75_t));
|
||||
|
||||
dev->local = info->local;
|
||||
dev->values = hwm_get_values();
|
||||
|
||||
dev->smbus_addr = dev->local;
|
||||
dev->as99127f_smbus_addr = 0x00;
|
||||
|
||||
lm75_reset(dev);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
|
||||
/* LM75 on SMBus address 4Ah, reporting temperatures[1]. */
|
||||
const device_t lm75_1_4a_device = {
|
||||
"National Semiconductor LM75 Temperature Sensor",
|
||||
DEVICE_AT,
|
||||
0x14a,
|
||||
lm75_init, lm75_close, NULL,
|
||||
NULL, NULL, NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
/* LM75 secondary/tertiary temperature sensors built into
|
||||
the Winbond W83781D family. Not to be used stand-alone. */
|
||||
const device_t lm75_w83781d_device = {
|
||||
"Winbond W83781D Secondary Temperature Sensor",
|
||||
DEVICE_AT,
|
||||
0,
|
||||
lm75_init, lm75_close, NULL,
|
||||
NULL, NULL, NULL,
|
||||
NULL
|
||||
};
|
||||
523
src/device/hwm_lm78.c
Normal file
523
src/device/hwm_lm78.c
Normal file
@@ -0,0 +1,523 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Emulation of the National Semiconductor LM78 hardware monitoring chip.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Author: RichardG, <richardg867@gmail.com>
|
||||
*
|
||||
* Copyright 2020 RichardG.
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#define HAVE_STDARG_H
|
||||
#include <wchar.h>
|
||||
#include <86box/86box.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/io.h>
|
||||
#include "cpu.h"
|
||||
#include <86box/smbus.h>
|
||||
#include <86box/hwm.h>
|
||||
|
||||
|
||||
#define LM78_SMBUS 0x10000
|
||||
#define LM78_W83781D 0x20000
|
||||
#define LM78_AS99127F_REV1 0x40000
|
||||
#define LM78_AS99127F_REV2 0x80000
|
||||
#define LM78_AS99127F (LM78_AS99127F_REV1 | LM78_AS99127F_REV2) /* special mask covering both _REV1 and _REV2 */
|
||||
#define LM78_WINBOND (LM78_W83781D | LM78_AS99127F) /* special mask covering all Winbond variants */
|
||||
#define LM78_WINBOND_VENDOR_ID ((dev->local & LM78_AS99127F_REV1) ? 0x12c3 : 0x5ca3)
|
||||
|
||||
#define CLAMP(a, min, max) (((a) < (min)) ? (min) : (((a) > (max)) ? (max) : (a)))
|
||||
#define LM78_RPM_TO_REG(r, d) ((r) ? CLAMP(1350000 / (r * d), 1, 255) : 0)
|
||||
#define LM78_VOLTAGE_TO_REG(v) ((v) >> 4)
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint32_t local;
|
||||
hwm_values_t *values;
|
||||
device_t *lm75[2];
|
||||
|
||||
uint8_t regs[256];
|
||||
uint8_t addr_register;
|
||||
uint8_t data_register;
|
||||
|
||||
uint8_t smbus_addr;
|
||||
uint8_t hbacs;
|
||||
uint8_t active_bank;
|
||||
} lm78_t;
|
||||
|
||||
|
||||
static uint8_t lm78_isa_read(uint16_t port, void *priv);
|
||||
static uint8_t lm78_smbus_read_byte(uint8_t addr, void *priv);
|
||||
static uint8_t lm78_smbus_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv);
|
||||
static uint16_t lm78_smbus_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv);
|
||||
static uint8_t lm78_read(lm78_t *dev, uint8_t reg, uint8_t bank);
|
||||
static void lm78_isa_write(uint16_t port, uint8_t val, void *priv);
|
||||
static void lm78_smbus_write_byte(uint8_t addr, uint8_t val, void *priv);
|
||||
static void lm78_smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val, void *priv);
|
||||
static void lm78_smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val, void *priv);
|
||||
static uint8_t lm78_write(lm78_t *dev, uint8_t reg, uint8_t val, uint8_t bank);
|
||||
static void lm78_reset(lm78_t *dev, uint8_t initialization);
|
||||
|
||||
|
||||
#ifdef ENABLE_LM78_LOG
|
||||
int lm78_do_log = ENABLE_LM78_LOG;
|
||||
|
||||
|
||||
static void
|
||||
lm78_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (lm78_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define lm78_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
|
||||
static void
|
||||
lm78_remap(lm78_t *dev)
|
||||
{
|
||||
lm75_t *lm75;
|
||||
|
||||
if (!(dev->local & LM78_SMBUS)) return;
|
||||
|
||||
lm78_log("LM78: remapping to SMBus %02Xh\n", dev->smbus_addr);
|
||||
|
||||
smbus_removehandler(dev->smbus_addr, 1,
|
||||
lm78_smbus_read_byte, lm78_smbus_read_byte_cmd, lm78_smbus_read_word_cmd, NULL,
|
||||
lm78_smbus_write_byte, lm78_smbus_write_byte_cmd, lm78_smbus_write_word_cmd, NULL,
|
||||
dev);
|
||||
|
||||
if (dev->smbus_addr) smbus_sethandler(dev->smbus_addr, 1,
|
||||
lm78_smbus_read_byte, lm78_smbus_read_byte_cmd, lm78_smbus_read_word_cmd, NULL,
|
||||
lm78_smbus_write_byte, lm78_smbus_write_byte_cmd, lm78_smbus_write_word_cmd, NULL,
|
||||
dev);
|
||||
|
||||
if (dev->local & LM78_AS99127F) {
|
||||
/* Store the main SMBus address on the LM75 devices to ensure reads/writes
|
||||
to the AS99127F's proprietary registers are passed through to this side. */
|
||||
for (uint8_t i = 0; i <= 1; i++) {
|
||||
lm75 = device_get_priv(dev->lm75[i]);
|
||||
if (lm75)
|
||||
lm75->as99127f_smbus_addr = dev->smbus_addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
lm78_isa_read(uint16_t port, void *priv)
|
||||
{
|
||||
lm78_t *dev = (lm78_t *) priv;
|
||||
uint8_t ret = 0xff;
|
||||
|
||||
switch (port & 0x7) {
|
||||
case 0x5:
|
||||
ret = (dev->addr_register & 0x7f);
|
||||
break;
|
||||
case 0x6:
|
||||
ret = lm78_read(dev, dev->addr_register, dev->active_bank);
|
||||
|
||||
if ((dev->active_bank == 0) &&
|
||||
((dev->addr_register == 0x41) || (dev->addr_register == 0x43) || (dev->addr_register == 0x45) || (dev->addr_register == 0x56) ||
|
||||
((dev->addr_register >= 0x60) && (dev->addr_register < 0x7f)))) {
|
||||
/* auto-increment registers */
|
||||
dev->addr_register++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
lm78_log("LM78: Read from unknown ISA port %d\n", port & 0x7);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
lm78_smbus_read_byte(uint8_t addr, void *priv)
|
||||
{
|
||||
lm78_t *dev = (lm78_t *) priv;
|
||||
return lm78_read(dev, dev->addr_register, dev->active_bank);
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
lm78_smbus_read_byte_cmd(uint8_t addr, uint8_t cmd, void *priv)
|
||||
{
|
||||
lm78_t *dev = (lm78_t *) priv;
|
||||
return lm78_read(dev, cmd, dev->active_bank);
|
||||
}
|
||||
|
||||
|
||||
static uint16_t
|
||||
lm78_smbus_read_word_cmd(uint8_t addr, uint8_t cmd, void *priv)
|
||||
{
|
||||
lm78_t *dev = (lm78_t *) priv;
|
||||
return (lm78_read(dev, cmd, dev->active_bank) << 8) | lm78_read(dev, cmd, dev->active_bank);
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
lm78_read(lm78_t *dev, uint8_t reg, uint8_t bank)
|
||||
{
|
||||
uint8_t ret = 0;
|
||||
lm75_t *lm75;
|
||||
|
||||
if (((reg & 0xf8) == 0x50) && (bank != 0)) {
|
||||
/* LM75 registers */
|
||||
lm75 = device_get_priv(dev->lm75[bank - 1]);
|
||||
if (lm75)
|
||||
ret = lm75_read(lm75, reg);
|
||||
} else {
|
||||
/* regular registers */
|
||||
if ((reg == 0x4f) && (dev->local & LM78_WINBOND)) /* special case for two-byte vendor ID register */
|
||||
ret = (dev->hbacs ? (LM78_WINBOND_VENDOR_ID >> 8) : LM78_WINBOND_VENDOR_ID);
|
||||
else if ((reg >= 0x60) && (reg <= 0x7f)) /* read auto-increment value RAM registers from their non-auto-increment locations */
|
||||
ret = dev->regs[reg & 0x3f];
|
||||
else if ((reg >= 0x80) && (reg <= 0x92)) /* AS99127F mirrors [0x00:0x12] to [0x80:0x92] */
|
||||
ret = dev->regs[reg & 0x7f];
|
||||
else
|
||||
ret = dev->regs[reg];
|
||||
}
|
||||
|
||||
lm78_log("LM78: read(%02X, %d) = %02X\n", reg, bank, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
lm78_isa_write(uint16_t port, uint8_t val, void *priv)
|
||||
{
|
||||
lm78_t *dev = (lm78_t *) priv;
|
||||
|
||||
switch (port & 0x7) {
|
||||
case 0x5:
|
||||
dev->addr_register = (val & 0x7f);
|
||||
break;
|
||||
case 0x6:
|
||||
lm78_write(dev, dev->addr_register, val, dev->active_bank);
|
||||
|
||||
if ((dev->active_bank == 0) &&
|
||||
((dev->addr_register == 0x41) || (dev->addr_register == 0x43) || (dev->addr_register == 0x45) || (dev->addr_register == 0x56) ||
|
||||
((dev->addr_register >= 0x60) && (dev->addr_register < 0x7f)))) {
|
||||
/* auto-increment registers */
|
||||
dev->addr_register++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
lm78_log("LM78: Write %02X to unknown ISA port %d\n", val, port & 0x7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
lm78_smbus_write_byte(uint8_t addr, uint8_t val, void *priv)
|
||||
{
|
||||
lm78_t *dev = (lm78_t *) priv;
|
||||
dev->addr_register = val;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
lm78_smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val, void *priv)
|
||||
{
|
||||
lm78_t *dev = (lm78_t *) priv;
|
||||
lm78_write(dev, cmd, val, dev->active_bank);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
lm78_smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val, void *priv)
|
||||
{
|
||||
lm78_t *dev = (lm78_t *) priv;
|
||||
lm78_write(dev, cmd, val, dev->active_bank);
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
lm78_write(lm78_t *dev, uint8_t reg, uint8_t val, uint8_t bank)
|
||||
{
|
||||
lm75_t *lm75;
|
||||
|
||||
lm78_log("LM78: write(%02X, %d, %02X)\n", reg, bank, val);
|
||||
|
||||
if (((reg & 0xf8) == 0x50) && (bank != 0)) {
|
||||
/* LM75 registers */
|
||||
lm75 = device_get_priv(dev->lm75[bank - 1]);
|
||||
if (lm75)
|
||||
lm75_write(lm75, reg, val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* regular registers */
|
||||
switch (reg) {
|
||||
case 0x41: case 0x42: case 0x4f: case 0x58:
|
||||
case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29: case 0x2a:
|
||||
case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: case 0x68: case 0x69: case 0x6a:
|
||||
/* read-only registers */
|
||||
return 0;
|
||||
case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e:
|
||||
/* Winbond-only registers */
|
||||
if (!(dev->local & LM78_WINBOND))
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((reg >= 0x60) && (reg <= 0x7f)) /* write auto-increment value RAM registers to their non-auto-increment locations */
|
||||
dev->regs[reg & 0x3f] = val;
|
||||
else if ((reg >= 0x80) && (reg <= 0x92)) /* AS99127F mirrors [0x00:0x12] to [0x80:0x92] */
|
||||
dev->regs[reg & 0x7f] = val;
|
||||
else
|
||||
dev->regs[reg] = val;
|
||||
|
||||
switch (reg) {
|
||||
case 0x40:
|
||||
if (val & 0x80) {
|
||||
/* INITIALIZATION bit resets all registers except main SMBus address */
|
||||
lm78_reset(dev, 1);
|
||||
}
|
||||
break;
|
||||
case 0x47:
|
||||
/* update FAN1/FAN2 values to match the new divisor */
|
||||
dev->regs[0x28] = LM78_RPM_TO_REG(dev->values->fans[0], 1 << ((dev->regs[0x47] >> 4) & 0x3));
|
||||
dev->regs[0x29] = LM78_RPM_TO_REG(dev->values->fans[1], 1 << ((dev->regs[0x47] >> 6) & 0x3));
|
||||
break;
|
||||
case 0x48:
|
||||
/* set main SMBus address */
|
||||
if (dev->local & LM78_SMBUS) {
|
||||
dev->smbus_addr = (dev->regs[0x48] & 0x7f);
|
||||
lm78_remap(dev);
|
||||
}
|
||||
break;
|
||||
case 0x49:
|
||||
if (!(dev->local & LM78_WINBOND)) {
|
||||
if (val & 0x20) {
|
||||
/* Chip Reset bit (LM78 only) resets all registers */
|
||||
lm78_reset(dev, 0);
|
||||
} else {
|
||||
dev->regs[0x49] = 0x40;
|
||||
}
|
||||
} else {
|
||||
dev->regs[0x49] &= 0x01;
|
||||
}
|
||||
break;
|
||||
case 0x4a:
|
||||
/* set LM75 SMBus addresses (Winbond only) */
|
||||
if (dev->local & LM78_SMBUS) {
|
||||
for (uint8_t i = 0; i <= 1; i++) {
|
||||
lm75 = device_get_priv(dev->lm75[i]);
|
||||
if (!lm75)
|
||||
continue;
|
||||
if (dev->regs[0x4a] & (0x08 * (0x10 * i))) /* DIS_T2 and DIS_T3 bit disable those interfaces */
|
||||
lm75->smbus_addr = 0x00;
|
||||
else
|
||||
lm75->smbus_addr = (0x48 + ((dev->regs[0x4a] >> (i * 4)) & 0x7));
|
||||
lm75_remap(lm75);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x4b:
|
||||
/* update FAN3 value to match the new divisor */
|
||||
dev->regs[0x2a] = LM78_RPM_TO_REG(dev->values->fans[2], 1 << ((dev->regs[0x4b] >> 6) & 0x3));
|
||||
break;
|
||||
case 0x4e:
|
||||
dev->hbacs = (dev->regs[0x4e] & 0x80);
|
||||
/* BANKSEL[0:2] is a bitfield according to the datasheet, but not in reality */
|
||||
dev->active_bank = (dev->regs[0x4e] & 0x07);
|
||||
break;
|
||||
case 0x87:
|
||||
/* fixes AS99127F boards hanging after BIOS save & exit, probably a reset register */
|
||||
if ((dev->local & LM78_AS99127F) && (val == 0x01)) {
|
||||
lm78_log("LM78: Reset requested through AS99127F\n");
|
||||
resetx86();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
lm78_reset(lm78_t *dev, uint8_t initialization)
|
||||
{
|
||||
memset(dev->regs, 0, 256);
|
||||
memset(dev->regs + 0xc0, 0xff, 32); /* C0-DF are 0xFF at least on the AS99127F */
|
||||
|
||||
uint8_t i;
|
||||
for (i = 0; i <= 6; i++)
|
||||
dev->regs[0x20 + i] = LM78_VOLTAGE_TO_REG(dev->values->voltages[i]);
|
||||
dev->regs[0x27] = dev->values->temperatures[0];
|
||||
for (i = 0; i <= 2; i++)
|
||||
dev->regs[0x28 + i] = LM78_RPM_TO_REG(dev->values->fans[i], 2);
|
||||
dev->regs[0x40] = 0x08;
|
||||
dev->regs[0x46] = 0x40;
|
||||
dev->regs[0x47] = 0x50;
|
||||
if (dev->local & LM78_SMBUS) {
|
||||
if (!initialization) /* don't reset main SMBus address if the reset was triggered by the INITIALIZATION bit */
|
||||
dev->smbus_addr = 0x2d;
|
||||
dev->regs[0x48] = dev->smbus_addr;
|
||||
if (dev->local & LM78_WINBOND)
|
||||
dev->regs[0x4a] = 0x01;
|
||||
} else {
|
||||
dev->regs[0x48] = 0x00;
|
||||
if (dev->local & LM78_WINBOND)
|
||||
dev->regs[0x4a] = 0x88;
|
||||
}
|
||||
if (dev->local & LM78_WINBOND) {
|
||||
dev->regs[0x49] = 0x02;
|
||||
dev->regs[0x4b] = 0x44;
|
||||
dev->regs[0x4c] = 0x01;
|
||||
dev->regs[0x4d] = 0x15;
|
||||
dev->regs[0x4e] = 0x80;
|
||||
dev->hbacs = (dev->regs[0x4e] & 0x80);
|
||||
dev->regs[0x4f] = (LM78_WINBOND_VENDOR_ID >> 8);
|
||||
dev->regs[0x57] = 0x80;
|
||||
|
||||
/* Initialize proprietary registers on the AS99127F. The BIOS accesses some
|
||||
of these on boot through read_byte_cmd on the TEMP2 address, hanging on
|
||||
POST code C1 if they're defaulted to 0. There's no documentation on what
|
||||
these are for. The following values were dumped from a live, initialized
|
||||
AS99127F Rev. 2 on a P4B motherboard, and they seem to work well enough. */
|
||||
if (dev->local & LM78_AS99127F) {
|
||||
/* 0x00 appears to mirror IN2 Low Limit */
|
||||
dev->regs[0x01] = dev->regs[0x23]; /* appears to mirror IN3 */
|
||||
dev->regs[0x02] = LM78_VOLTAGE_TO_REG(2800); /* appears to be a "maximum VCORE" of some kind; must read 2.8V on P3 boards */
|
||||
dev->regs[0x03] = 0x60;
|
||||
dev->regs[0x04] = dev->regs[0x23]; /* appears to mirror IN3 */
|
||||
dev->regs[0x05] = dev->regs[0x22]; /* appears to mirror IN2 */
|
||||
dev->regs[0x07] = 0xcd;
|
||||
/* 0x08 appears to mirror IN3 Low Limit */
|
||||
dev->regs[0x09] = dev->regs[0x0f] = dev->regs[0x11] = 0xf8; /* three instances of */
|
||||
dev->regs[0x0a] = dev->regs[0x10] = dev->regs[0x12] = 0xa5; /* the same word */
|
||||
dev->regs[0x0b] = 0xac;
|
||||
dev->regs[0x0c] = 0x8c;
|
||||
dev->regs[0x0d] = 0x68;
|
||||
dev->regs[0x0e] = 0x54;
|
||||
|
||||
dev->regs[0x53] = dev->regs[0x54] = dev->regs[0x55] = 0xff;
|
||||
dev->regs[0x58] = 0x31;
|
||||
dev->regs[0x59] = dev->regs[0x5a] = 0x8f;
|
||||
dev->regs[0x5c] = 0xe0;
|
||||
dev->regs[0x5d] = 0x48;
|
||||
dev->regs[0x5e] = 0xe2;
|
||||
dev->regs[0x5f] = 0x3f;
|
||||
} else {
|
||||
dev->regs[0x58] = 0x10;
|
||||
}
|
||||
} else {
|
||||
dev->regs[0x49] = 0x40;
|
||||
}
|
||||
|
||||
lm78_remap(dev);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
lm78_close(void *priv)
|
||||
{
|
||||
lm78_t *dev = (lm78_t *) priv;
|
||||
|
||||
uint16_t isa_io = (dev->local & 0xffff);
|
||||
if (isa_io)
|
||||
io_removehandler(isa_io, 8, lm78_isa_read, NULL, NULL, lm78_isa_write, NULL, NULL, dev);
|
||||
|
||||
free(dev);
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
lm78_init(const device_t *info)
|
||||
{
|
||||
lm78_t *dev = (lm78_t *) malloc(sizeof(lm78_t));
|
||||
memset(dev, 0, sizeof(lm78_t));
|
||||
|
||||
dev->local = info->local;
|
||||
dev->values = hwm_get_values();
|
||||
|
||||
/* initialize secondary/tertiary LM75 sensors on Winbond */
|
||||
for (uint8_t i = 0; i <= 1; i++) {
|
||||
if (dev->local & LM78_WINBOND) {
|
||||
dev->lm75[i] = (device_t *) malloc(sizeof(device_t));
|
||||
memcpy(dev->lm75[i], &lm75_w83781d_device, sizeof(device_t));
|
||||
dev->lm75[i]->local = ((i + 1) << 8);
|
||||
if (dev->local & LM78_SMBUS)
|
||||
dev->lm75[i]->local |= (0x48 + i);
|
||||
device_add(dev->lm75[i]);
|
||||
} else {
|
||||
dev->lm75[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
lm78_reset(dev, 0);
|
||||
|
||||
uint16_t isa_io = (dev->local & 0xffff);
|
||||
if (isa_io)
|
||||
io_sethandler(isa_io, 8, lm78_isa_read, NULL, NULL, lm78_isa_write, NULL, NULL, dev);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
|
||||
/* National Semiconductor LM78 on ISA and SMBus. */
|
||||
const device_t lm78_device = {
|
||||
"National Semiconductor LM78 Hardware Monitor",
|
||||
DEVICE_ISA,
|
||||
0x290 | LM78_SMBUS,
|
||||
lm78_init, lm78_close, NULL,
|
||||
NULL, NULL, NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
/* Winbond W83781D (or ASUS AS97127F) on ISA and SMBus. */
|
||||
const device_t w83781d_device = {
|
||||
"Winbond W83781D Hardware Monitor",
|
||||
DEVICE_ISA,
|
||||
0x290 | LM78_SMBUS | LM78_W83781D,
|
||||
lm78_init, lm78_close, NULL,
|
||||
NULL, NULL, NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
/* The ASUS AS99127F is a customized W83781D with no ISA interface (SMBus only),
|
||||
added proprietary registers and different chip/vendor IDs. */
|
||||
const device_t as99127f_device = {
|
||||
"ASUS AS99127F Rev. 1 Hardware Monitor",
|
||||
DEVICE_ISA,
|
||||
LM78_SMBUS | LM78_AS99127F_REV1,
|
||||
lm78_init, lm78_close, NULL,
|
||||
NULL, NULL, NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
/* Rev. 2 changes the vendor ID back to Winbond's and brings some other changes. */
|
||||
const device_t as99127f_rev2_device = {
|
||||
"ASUS AS99127F Rev. 2 Hardware Monitor",
|
||||
DEVICE_AT,
|
||||
LM78_SMBUS | LM78_AS99127F_REV2,
|
||||
lm78_init, lm78_close, NULL,
|
||||
NULL, NULL, NULL,
|
||||
NULL
|
||||
};
|
||||
113
src/device/ibm_5161.c
Normal file
113
src/device/ibm_5161.c
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Emulation of the IBM Expansion Unit (5161).
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Miran Grca, <mgrca8@gmail.com>
|
||||
*
|
||||
* Copyright 2016-2018 Miran Grca.
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#include <86box/86box.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/io.h>
|
||||
#include <86box/apm.h>
|
||||
#include <86box/dma.h>
|
||||
#include <86box/mem.h>
|
||||
#include <86box/pci.h>
|
||||
#include <86box/timer.h>
|
||||
#include <86box/pit.h>
|
||||
#include <86box/port_92.h>
|
||||
#include <86box/machine.h>
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t regs[8];
|
||||
} ibm_5161_t;
|
||||
|
||||
|
||||
static void
|
||||
ibm_5161_out(uint16_t port, uint8_t val, void *priv)
|
||||
{
|
||||
ibm_5161_t *dev = (ibm_5161_t *) priv;
|
||||
|
||||
dev->regs[port & 0x0007] = val;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
ibm_5161_in(uint16_t port, void *priv)
|
||||
{
|
||||
ibm_5161_t *dev = (ibm_5161_t *) priv;
|
||||
uint8_t ret = 0xff;
|
||||
|
||||
ret = dev->regs[port & 0x0007];
|
||||
|
||||
switch (port) {
|
||||
case 0x211:
|
||||
case 0x215:
|
||||
ret = (get_last_addr() >> 8) & 0xff;
|
||||
break;
|
||||
case 0x212:
|
||||
case 0x216:
|
||||
ret = get_last_addr() & 0xff;
|
||||
break;
|
||||
case 0x213:
|
||||
ret = dev->regs[3] & 0x01;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ibm_5161_close(void *p)
|
||||
{
|
||||
ibm_5161_t *dev = (ibm_5161_t *) p;
|
||||
|
||||
free(dev);
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ibm_5161_init(const device_t *info)
|
||||
{
|
||||
ibm_5161_t *dev = (ibm_5161_t *) malloc(sizeof(ibm_5161_t));
|
||||
memset(dev, 0, sizeof(ibm_5161_t));
|
||||
|
||||
/* Extender Card Registers */
|
||||
io_sethandler(0x0210, 0x0004,
|
||||
ibm_5161_in, NULL, NULL, ibm_5161_out, NULL, NULL, dev);
|
||||
|
||||
/* Receiver Card Registers */
|
||||
io_sethandler(0x0214, 0x0003,
|
||||
ibm_5161_in, NULL, NULL, ibm_5161_out, NULL, NULL, dev);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
|
||||
const device_t ibm_5161_device =
|
||||
{
|
||||
"IBM Expansion Unit (5161)",
|
||||
0,
|
||||
0,
|
||||
ibm_5161_init,
|
||||
ibm_5161_close,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
1084
src/device/isamem.c
Normal file
1084
src/device/isamem.c
Normal file
File diff suppressed because it is too large
Load Diff
760
src/device/isartc.c
Normal file
760
src/device/isartc.c
Normal file
@@ -0,0 +1,760 @@
|
||||
/*
|
||||
* VARCem Virtual ARchaeological Computer EMulator.
|
||||
* An emulator of (mostly) x86-based PC systems and devices,
|
||||
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
|
||||
* spanning the era between 1981 and 1995.
|
||||
*
|
||||
* This file is part of the VARCem Project.
|
||||
*
|
||||
* Implementation of a Clock/RTC Card for the ISA PC/XT.
|
||||
*
|
||||
* Systems starting with the PC/XT had, by default, a realtime
|
||||
* clock and NVR chip on the mainboard. The BIOS stored config
|
||||
* data in the NVR, and the system could maintain time and date
|
||||
* using the RTC.
|
||||
*
|
||||
* Originally, PC systems did not have this, and they first did
|
||||
* show up in non-IBM clone systems. Shortly after, expansion
|
||||
* cards with this function became available for the PC's (ISA)
|
||||
* bus, and they came in many forms and designs.
|
||||
*
|
||||
* This implementation offers some of those boards:
|
||||
*
|
||||
* Everex EV-170 (using NatSemi MM58167 chip)
|
||||
* DTK PII-147 Hexa I/O Plus (using UMC 82C8167 chip)
|
||||
*
|
||||
* and more will follow as time permits.
|
||||
*
|
||||
* NOTE: The IRQ functionalities have been implemented, but not yet
|
||||
* tested, as I need to write test software for them first :)
|
||||
*
|
||||
*
|
||||
*
|
||||
* Author: Fred N. van Kempen, <decwiz@yahoo.com>
|
||||
*
|
||||
* Copyright 2018 Fred N. van Kempen.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with
|
||||
* or without modification, are permitted provided that the
|
||||
* following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the entire
|
||||
* above notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the
|
||||
* following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names
|
||||
* of its contributors may be used to endorse or promote
|
||||
* products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <wchar.h>
|
||||
#include <86box/86box.h>
|
||||
#include "cpu.h"
|
||||
#include <86box/timer.h>
|
||||
#include <86box/machine.h>
|
||||
#include <86box/io.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/nvr.h>
|
||||
#include <86box/ui.h>
|
||||
#include <86box/plat.h>
|
||||
#include <86box/pic.h>
|
||||
#include <86box/isartc.h>
|
||||
|
||||
|
||||
#define ISARTC_DEBUG 0
|
||||
|
||||
|
||||
typedef struct {
|
||||
const char *name; /* board name */
|
||||
uint8_t board; /* board type */
|
||||
|
||||
uint8_t flags; /* various flags */
|
||||
#define FLAG_YEAR80 0x01 /* YEAR byte is base-80 */
|
||||
#define FLAG_YEARBCD 0x02 /* YEAR byte is in BCD */
|
||||
|
||||
int8_t irq; /* configured IRQ channel */
|
||||
int8_t base_addrsz;
|
||||
uint32_t base_addr; /* configured I/O address */
|
||||
|
||||
/* Fields for the specific driver. */
|
||||
void (*f_wr)(uint16_t, uint8_t, void *);
|
||||
uint8_t (*f_rd)(uint16_t, void *);
|
||||
int8_t year; /* register for YEAR value */
|
||||
char pad[3];
|
||||
|
||||
nvr_t nvr; /* RTC/NVR */
|
||||
} rtcdev_t;
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* *
|
||||
* Driver for the NatSemi MM58167 chip. *
|
||||
* *
|
||||
************************************************************************/
|
||||
#define MM67_REGS 32
|
||||
|
||||
/* Define the RTC chip registers - see datasheet, pg4. */
|
||||
#define MM67_MSEC 0 /* milliseconds */
|
||||
#define MM67_HUNTEN 1 /* hundredths/tenths of seconds */
|
||||
#define MM67_SEC 2 /* seconds */
|
||||
#define MM67_MIN 3 /* minutes */
|
||||
#define MM67_HOUR 4 /* hours */
|
||||
#define MM67_DOW 5 /* day of the week */
|
||||
#define MM67_DOM 6 /* day of the month */
|
||||
#define MM67_MON 7 /* month */
|
||||
#define MM67_AL_MSEC 8 /* milliseconds */
|
||||
#define MM67_AL_HUNTEN 9 /* hundredths/tenths of seconds */
|
||||
#define MM67_AL_SEC 10 /* seconds */
|
||||
#define MM67_AL_MIN 11 /* minutes */
|
||||
#define MM67_AL_HOUR 12 /* hours */
|
||||
#define MM67_AL_DOW 13 /* day of the week */
|
||||
#define MM67_AL_DOM 14 /* day of the month */
|
||||
#define MM67_AL_MON 15 /* month */
|
||||
# define MM67_AL_DONTCARE 0xc0 /* always match in compare */
|
||||
#define MM67_ISTAT 16 /* IRQ status */
|
||||
#define MM67_ICTRL 17 /* IRQ control */
|
||||
# define MM67INT_COMPARE 0x01 /* Compare */
|
||||
# define MM67INT_TENTH 0x02 /* Tenth */
|
||||
# define MM67INT_SEC 0x04 /* Second */
|
||||
# define MM67INT_MIN 0x08 /* Minute */
|
||||
# define MM67INT_HOUR 0x10 /* Hour */
|
||||
# define MM67INT_DAY 0x20 /* Day */
|
||||
# define MM67INT_WEEK 0x40 /* Week */
|
||||
# define MM67INT_MON 0x80 /* Month */
|
||||
#define MM67_RSTCTR 18 /* reset counters */
|
||||
#define MM67_RSTRAM 19 /* reset RAM */
|
||||
#define MM67_STATUS 20 /* status bit */
|
||||
#define MM67_GOCMD 21 /* GO Command */
|
||||
#define MM67_STBYIRQ 22 /* standby IRQ */
|
||||
#define MM67_TEST 31 /* test mode */
|
||||
|
||||
#ifdef ENABLE_ISARTC_LOG
|
||||
int isartc_do_log = ENABLE_ISARTC_LOG;
|
||||
|
||||
|
||||
static void
|
||||
isartc_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (isartc_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define isartc_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
|
||||
/* Check if the current time matches a set alarm time. */
|
||||
static int8_t
|
||||
mm67_chkalrm(nvr_t *nvr, int8_t addr)
|
||||
{
|
||||
return((nvr->regs[addr-MM67_AL_SEC+MM67_SEC] == nvr->regs[addr]) ||
|
||||
((nvr->regs[addr] & MM67_AL_DONTCARE) == MM67_AL_DONTCARE));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This is called every second through the NVR/RTC hook.
|
||||
*
|
||||
* We fake a 'running' RTC by updating its registers on
|
||||
* each passing second. Not exactly accurate, but good
|
||||
* enough.
|
||||
*
|
||||
* Note that this code looks nasty because of all the
|
||||
* BCD to decimal vv going on.
|
||||
*/
|
||||
static void
|
||||
mm67_tick(nvr_t *nvr)
|
||||
{
|
||||
rtcdev_t *dev = (rtcdev_t *)nvr->data;
|
||||
uint8_t *regs = nvr->regs;
|
||||
int mon, year, f = 0;
|
||||
|
||||
/* Update and set interrupt if needed. */
|
||||
regs[MM67_SEC] = RTC_BCDINC(nvr->regs[MM67_SEC], 1);
|
||||
if (regs[MM67_ICTRL] & MM67INT_SEC) f = MM67INT_SEC;
|
||||
|
||||
/* Roll over? */
|
||||
if (regs[MM67_SEC] >= RTC_BCD(60)) {
|
||||
/* Update and set interrupt if needed. */
|
||||
regs[MM67_SEC] = RTC_BCD(0);
|
||||
regs[MM67_MIN] = RTC_BCDINC(regs[MM67_MIN], 1);
|
||||
if (regs[MM67_ICTRL] & MM67INT_MIN) f = MM67INT_MIN;
|
||||
|
||||
/* Roll over? */
|
||||
if (regs[MM67_MIN] >= RTC_BCD(60)) {
|
||||
/* Update and set interrupt if needed. */
|
||||
regs[MM67_MIN] = RTC_BCD(0);
|
||||
regs[MM67_HOUR] = RTC_BCDINC(regs[MM67_HOUR], 1);
|
||||
if (regs[MM67_ICTRL] & MM67INT_HOUR) f = MM67INT_HOUR;
|
||||
|
||||
/* Roll over? */
|
||||
if (regs[MM67_HOUR] >= RTC_BCD(24)) {
|
||||
/* Update and set interrupt if needed. */
|
||||
regs[MM67_HOUR] = RTC_BCD(0);
|
||||
regs[MM67_DOW] = RTC_BCDINC(regs[MM67_DOW], 1);
|
||||
if (regs[MM67_ICTRL] & MM67INT_DAY) f = MM67INT_DAY;
|
||||
|
||||
/* Roll over? */
|
||||
if (regs[MM67_DOW] > RTC_BCD(7)) {
|
||||
/* Update and set interrupt if needed. */
|
||||
regs[MM67_DOW] = RTC_BCD(1);
|
||||
if (regs[MM67_ICTRL] & MM67INT_WEEK) f = MM67INT_WEEK;
|
||||
}
|
||||
|
||||
/* Roll over? */
|
||||
regs[MM67_DOM] = RTC_BCDINC(regs[MM67_DOM], 1);
|
||||
mon = RTC_DCB(regs[MM67_MON]);
|
||||
if (dev->year != -1) {
|
||||
year = RTC_DCB(regs[dev->year]);
|
||||
if (dev->flags & FLAG_YEAR80)
|
||||
year += 80;
|
||||
} else
|
||||
year = 80;
|
||||
year += 1900;
|
||||
if (RTC_DCB(regs[MM67_DOM]) > nvr_get_days(mon, year)) {
|
||||
/* Update and set interrupt if needed. */
|
||||
regs[MM67_DOM] = RTC_BCD(1);
|
||||
regs[MM67_MON] = RTC_BCDINC(regs[MM67_MON], 1);
|
||||
if (regs[MM67_ICTRL] & MM67INT_MON) f = MM67INT_MON;
|
||||
|
||||
/* Roll over? */
|
||||
if (regs[MM67_MON] > RTC_BCD(12)) {
|
||||
/* Update. */
|
||||
regs[MM67_MON] = RTC_BCD(1);
|
||||
if (dev->year != -1) {
|
||||
year++;
|
||||
if (dev->flags & FLAG_YEAR80)
|
||||
year -= 80;
|
||||
|
||||
if (dev->flags & FLAG_YEARBCD)
|
||||
regs[dev->year] = RTC_BCD(year % 100);
|
||||
else
|
||||
regs[dev->year] = year % 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for programmed alarm interrupt. */
|
||||
if (regs[MM67_ICTRL] & MM67INT_COMPARE) {
|
||||
year = 1;
|
||||
for (mon = MM67_AL_SEC; mon <= MM67_AL_MON; mon++)
|
||||
if (mon != dev->year)
|
||||
year &= mm67_chkalrm(nvr, mon);
|
||||
f = year ? MM67INT_COMPARE : 0x00;
|
||||
}
|
||||
|
||||
/* Raise the IRQ if needed (and if we have one..) */
|
||||
if (f != 0) {
|
||||
regs[MM67_ISTAT] = f;
|
||||
if (nvr->irq != -1)
|
||||
picint(1 << nvr->irq);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Get the current NVR time. */
|
||||
static void
|
||||
mm67_time_get(nvr_t *nvr, struct tm *tm)
|
||||
{
|
||||
rtcdev_t *dev = (rtcdev_t *)nvr->data;
|
||||
uint8_t *regs = nvr->regs;
|
||||
|
||||
/* NVR is in BCD data mode. */
|
||||
tm->tm_sec = RTC_DCB(regs[MM67_SEC]);
|
||||
tm->tm_min = RTC_DCB(regs[MM67_MIN]);
|
||||
tm->tm_hour = RTC_DCB(regs[MM67_HOUR]);
|
||||
tm->tm_wday = (RTC_DCB(regs[MM67_DOW]) - 1);
|
||||
tm->tm_mday = RTC_DCB(regs[MM67_DOM]);
|
||||
tm->tm_mon = (RTC_DCB(regs[MM67_MON]) - 1);
|
||||
if (dev->year != -1) {
|
||||
if (dev->flags & FLAG_YEARBCD)
|
||||
tm->tm_year = RTC_DCB(regs[dev->year]);
|
||||
else
|
||||
tm->tm_year = regs[dev->year];
|
||||
if (dev->flags & FLAG_YEAR80)
|
||||
tm->tm_year += 80;
|
||||
#ifdef MM67_CENTURY
|
||||
tm->tm_year += (regs[MM67_CENTURY] * 100) - 1900;
|
||||
#endif
|
||||
#if ISARTC_DEBUG > 1
|
||||
isartc_log("ISARTC: get_time: year=%i [%02x]\n", tm->tm_year, regs[dev->year]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Set the current NVR time. */
|
||||
static void
|
||||
mm67_time_set(nvr_t *nvr, struct tm *tm)
|
||||
{
|
||||
rtcdev_t *dev = (rtcdev_t *)nvr->data;
|
||||
uint8_t *regs = nvr->regs;
|
||||
int year;
|
||||
|
||||
/* NVR is in BCD data mode. */
|
||||
regs[MM67_SEC] = RTC_BCD(tm->tm_sec);
|
||||
regs[MM67_MIN] = RTC_BCD(tm->tm_min);
|
||||
regs[MM67_HOUR] = RTC_BCD(tm->tm_hour);
|
||||
regs[MM67_DOW] = RTC_BCD(tm->tm_wday + 1);
|
||||
regs[MM67_DOM] = RTC_BCD(tm->tm_mday);
|
||||
regs[MM67_MON] = RTC_BCD(tm->tm_mon + 1);
|
||||
if (dev->year != -1) {
|
||||
year = tm->tm_year;
|
||||
if (dev->flags & FLAG_YEAR80)
|
||||
year -= 80;
|
||||
if (dev->flags & FLAG_YEARBCD)
|
||||
regs[dev->year] = RTC_BCD(year % 100);
|
||||
else
|
||||
regs[dev->year] = year % 100;
|
||||
#ifdef MM67_CENTURY
|
||||
regs[MM67_CENTURY] = (year + 1900) / 100;
|
||||
#endif
|
||||
#if ISARTC_DEBUG > 1
|
||||
isartc_log("ISARTC: set_time: [%02x] year=%i (%i)\n", regs[dev->year], year, tm->tm_year);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
mm67_start(nvr_t *nvr)
|
||||
{
|
||||
struct tm tm;
|
||||
|
||||
/* Initialize the internal and chip times. */
|
||||
if (time_sync) {
|
||||
/* Use the internal clock's time. */
|
||||
nvr_time_get(&tm);
|
||||
mm67_time_set(nvr, &tm);
|
||||
} else {
|
||||
/* Set the internal clock from the chip time. */
|
||||
mm67_time_get(nvr, &tm);
|
||||
nvr_time_set(&tm);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Reset the RTC counters to a sane state. */
|
||||
static void
|
||||
mm67_reset(nvr_t *nvr)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Initialize the RTC to a known state. */
|
||||
for (i = MM67_MSEC; i <= MM67_MON; i++)
|
||||
nvr->regs[i] = RTC_BCD(0);
|
||||
nvr->regs[MM67_DOW] = RTC_BCD(1);
|
||||
nvr->regs[MM67_DOM] = RTC_BCD(1);
|
||||
nvr->regs[MM67_MON] = RTC_BCD(1);
|
||||
}
|
||||
|
||||
|
||||
/* Handle a READ operation from one of our registers. */
|
||||
static uint8_t
|
||||
mm67_read(uint16_t port, void *priv)
|
||||
{
|
||||
rtcdev_t *dev = (rtcdev_t *)priv;
|
||||
int reg = port - dev->base_addr;
|
||||
uint8_t ret = 0xff;
|
||||
|
||||
/* This chip is directly mapped on I/O. */
|
||||
sub_cycles(ISA_CYCLES(4));
|
||||
|
||||
switch(reg) {
|
||||
case MM67_ISTAT: /* IRQ status (RO) */
|
||||
ret = dev->nvr.regs[reg];
|
||||
dev->nvr.regs[reg] = 0x00;
|
||||
if (dev->irq != -1)
|
||||
picintc(1 << dev->irq);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = dev->nvr.regs[reg];
|
||||
break;
|
||||
}
|
||||
|
||||
#if ISARTC_DEBUG
|
||||
isartc_log("ISARTC: read(%04x) = %02x\n", port-dev->base_addr, ret);
|
||||
#endif
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
/* Handle a WRITE operation to one of our registers. */
|
||||
static void
|
||||
mm67_write(uint16_t port, uint8_t val, void *priv)
|
||||
{
|
||||
rtcdev_t *dev = (rtcdev_t *)priv;
|
||||
int reg = port - dev->base_addr;
|
||||
int i;
|
||||
|
||||
#if ISARTC_DEBUG
|
||||
isartc_log("ISARTC: write(%04x, %02x)\n", port-dev->base_addr, val);
|
||||
#endif
|
||||
|
||||
/* This chip is directly mapped on I/O. */
|
||||
sub_cycles(ISA_CYCLES(4));
|
||||
|
||||
switch(reg) {
|
||||
case MM67_ISTAT: /* intr status (RO) */
|
||||
break;
|
||||
|
||||
case MM67_ICTRL: /* intr control */
|
||||
dev->nvr.regs[MM67_ISTAT] = 0x00;
|
||||
dev->nvr.regs[reg] = val;
|
||||
break;
|
||||
|
||||
case MM67_RSTCTR:
|
||||
if (val == 0xff)
|
||||
mm67_reset(&dev->nvr);
|
||||
break;
|
||||
|
||||
case MM67_RSTRAM:
|
||||
if (val == 0xff) {
|
||||
for (i = MM67_AL_MSEC; i <= MM67_AL_MON; i++)
|
||||
dev->nvr.regs[i] = RTC_BCD(0);
|
||||
dev->nvr.regs[MM67_DOW] = RTC_BCD(1);
|
||||
dev->nvr.regs[MM67_DOM] = RTC_BCD(1);
|
||||
dev->nvr.regs[MM67_MON] = RTC_BCD(1);
|
||||
if (dev->year != -1) {
|
||||
val = (dev->flags & FLAG_YEAR80) ? 0 : 80;
|
||||
if (dev->flags & FLAG_YEARBCD)
|
||||
dev->nvr.regs[dev->year] = RTC_BCD(val);
|
||||
else
|
||||
dev->nvr.regs[dev->year] = val;
|
||||
#ifdef MM67_CENTURY
|
||||
dev->nvr.regs[MM67_CENTURY] = 19;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MM67_STATUS: /* STATUS (RO) */
|
||||
break;
|
||||
|
||||
case MM67_GOCMD:
|
||||
isartc_log("RTC: write gocmd=%02x\n", val);
|
||||
break;
|
||||
|
||||
case MM67_STBYIRQ:
|
||||
isartc_log("RTC: write stby=%02x\n", val);
|
||||
break;
|
||||
|
||||
case MM67_TEST:
|
||||
isartc_log("RTC: write test=%02x\n", val);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev->nvr.regs[reg] = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* *
|
||||
* Generic code for all supported chips. *
|
||||
* *
|
||||
************************************************************************/
|
||||
|
||||
/* Initialize the device for use. */
|
||||
static void *
|
||||
isartc_init(const device_t *info)
|
||||
{
|
||||
rtcdev_t *dev;
|
||||
|
||||
/* Create a device instance. */
|
||||
dev = (rtcdev_t *)malloc(sizeof(rtcdev_t));
|
||||
memset(dev, 0x00, sizeof(rtcdev_t));
|
||||
dev->name = info->name;
|
||||
dev->board = info->local;
|
||||
dev->irq = -1;
|
||||
dev->year = -1;
|
||||
dev->nvr.data = dev;
|
||||
dev->nvr.size = 16;
|
||||
|
||||
/* Do per-board initialization. */
|
||||
switch(dev->board) {
|
||||
case 0: /* Everex EV-170 Magic I/O */
|
||||
dev->flags |= FLAG_YEAR80;
|
||||
dev->base_addr = device_get_config_hex16("base");
|
||||
dev->base_addrsz = 32;
|
||||
dev->irq = device_get_config_int("irq");
|
||||
dev->f_rd = mm67_read;
|
||||
dev->f_wr = mm67_write;
|
||||
dev->nvr.reset = mm67_reset;
|
||||
dev->nvr.start = mm67_start;
|
||||
dev->nvr.tick = mm67_tick;
|
||||
dev->year = MM67_AL_DOM; /* year, NON STANDARD */
|
||||
break;
|
||||
|
||||
case 1: /* DTK PII-147 Hexa I/O Plus */
|
||||
dev->flags |= FLAG_YEARBCD;
|
||||
dev->base_addr = device_get_config_hex16("base");
|
||||
dev->base_addrsz = 32;
|
||||
dev->f_rd = mm67_read;
|
||||
dev->f_wr = mm67_write;
|
||||
dev->nvr.reset = mm67_reset;
|
||||
dev->nvr.start = mm67_start;
|
||||
dev->nvr.tick = mm67_tick;
|
||||
dev->year = MM67_AL_HUNTEN; /* year, NON STANDARD */
|
||||
break;
|
||||
|
||||
case 2: /* Paradise Systems 5PAK */
|
||||
dev->flags |= FLAG_YEAR80;
|
||||
dev->base_addr = 0x02c0;
|
||||
dev->base_addrsz = 32;
|
||||
dev->irq = device_get_config_int("irq");
|
||||
dev->f_rd = mm67_read;
|
||||
dev->f_wr = mm67_write;
|
||||
dev->nvr.reset = mm67_reset;
|
||||
dev->nvr.start = mm67_start;
|
||||
dev->nvr.tick = mm67_tick;
|
||||
dev->year = MM67_AL_DOM; /* year, NON STANDARD */
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Say hello! */
|
||||
isartc_log("ISARTC: %s (I/O=%04XH", info->name, dev->base_addr);
|
||||
if (dev->irq != -1)
|
||||
isartc_log(", IRQ%i", dev->irq);
|
||||
isartc_log(")\n");
|
||||
|
||||
/* Set up an I/O port handler. */
|
||||
io_sethandler(dev->base_addr, dev->base_addrsz,
|
||||
dev->f_rd,NULL,NULL, dev->f_wr,NULL,NULL, dev);
|
||||
|
||||
/* Hook into the NVR backend. */
|
||||
dev->nvr.fn = (wchar_t *)isartc_get_internal_name(isartc_type);
|
||||
dev->nvr.irq = dev->irq;
|
||||
nvr_init(&dev->nvr);
|
||||
|
||||
/* Let them know our device instance. */
|
||||
return((void *)dev);
|
||||
}
|
||||
|
||||
|
||||
/* Remove the device from the system. */
|
||||
static void
|
||||
isartc_close(void *priv)
|
||||
{
|
||||
rtcdev_t *dev = (rtcdev_t *)priv;
|
||||
|
||||
io_removehandler(dev->base_addr, dev->base_addrsz,
|
||||
dev->f_rd,NULL,NULL, dev->f_wr,NULL,NULL, dev);
|
||||
|
||||
if (dev->nvr.fn != NULL)
|
||||
free((wchar_t *)dev->nvr.fn);
|
||||
|
||||
free(dev);
|
||||
}
|
||||
|
||||
|
||||
static const device_config_t ev170_config[] = {
|
||||
{
|
||||
"base", "Address", CONFIG_HEX16, "", 0x02C0,
|
||||
{
|
||||
{
|
||||
"240H", 0x0240
|
||||
},
|
||||
{
|
||||
"2C0H", 0x02c0
|
||||
},
|
||||
{
|
||||
""
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
"irq", "IRQ", CONFIG_SELECTION, "", -1,
|
||||
{
|
||||
{
|
||||
"Disabled", -1
|
||||
},
|
||||
{
|
||||
"IRQ2", 2
|
||||
},
|
||||
{
|
||||
"IRQ5", 5
|
||||
},
|
||||
{
|
||||
"IRQ7", 7
|
||||
},
|
||||
{
|
||||
""
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
"", "", -1
|
||||
}
|
||||
};
|
||||
|
||||
static const device_t ev170_device = {
|
||||
"Everex EV-170 Magic I/O",
|
||||
DEVICE_ISA,
|
||||
0,
|
||||
isartc_init, isartc_close, NULL,
|
||||
NULL, NULL, NULL,
|
||||
ev170_config
|
||||
};
|
||||
|
||||
|
||||
static const device_config_t pii147_config[] = {
|
||||
{
|
||||
"base", "Address", CONFIG_HEX16, "", 0x0240,
|
||||
{
|
||||
{
|
||||
"Clock 1", 0x0240
|
||||
},
|
||||
{
|
||||
"Clock 2", 0x0340
|
||||
},
|
||||
{
|
||||
""
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
"", "", -1
|
||||
}
|
||||
};
|
||||
|
||||
static const device_t pii147_device = {
|
||||
"DTK PII-147 Hexa I/O Plus",
|
||||
DEVICE_ISA,
|
||||
1,
|
||||
isartc_init, isartc_close, NULL,
|
||||
NULL, NULL, NULL,
|
||||
pii147_config
|
||||
};
|
||||
|
||||
|
||||
static const device_config_t p5pak_config[] = {
|
||||
{
|
||||
"irq", "IRQ", CONFIG_SELECTION, "", -1,
|
||||
{
|
||||
{
|
||||
"Disabled", -1
|
||||
},
|
||||
{
|
||||
"IRQ2", 2
|
||||
},
|
||||
{
|
||||
"IRQ3", 3
|
||||
},
|
||||
{
|
||||
"IRQ5", 5
|
||||
},
|
||||
{
|
||||
""
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
"", "", -1
|
||||
}
|
||||
};
|
||||
|
||||
static const device_t p5pak_device = {
|
||||
"Paradise Systems 5-PAK",
|
||||
DEVICE_ISA,
|
||||
2,
|
||||
isartc_init, isartc_close, NULL,
|
||||
NULL, NULL, NULL,
|
||||
p5pak_config
|
||||
};
|
||||
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
const char *internal_name;
|
||||
const device_t *dev;
|
||||
} boards[] = {
|
||||
{ "None", "none", NULL, },
|
||||
{ "Everex EV-170 Magic I/O", "ev170", &ev170_device, },
|
||||
{ "DTK PII-147 Hexa I/O Plus", "pii147", &pii147_device, },
|
||||
{ "Paradise Systems 5-PAK", "p5pak", &p5pak_device, },
|
||||
{ "", "", NULL, },
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
isartc_reset(void)
|
||||
{
|
||||
if (isartc_type == 0) return;
|
||||
|
||||
/* Add the device to the system. */
|
||||
device_add(boards[isartc_type].dev);
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
isartc_get_name(int board)
|
||||
{
|
||||
return((char *)boards[board].name);
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
isartc_get_internal_name(int board)
|
||||
{
|
||||
return((char *)boards[board].internal_name);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
isartc_get_from_internal_name(char *s)
|
||||
{
|
||||
int c = 0;
|
||||
|
||||
while (strlen((char *) boards[c].internal_name)) {
|
||||
if (! strcmp(boards[c].internal_name, s))
|
||||
return(c);
|
||||
c++;
|
||||
}
|
||||
|
||||
/* Not found. */
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
const device_t *
|
||||
isartc_get_device(int board)
|
||||
{
|
||||
return(boards[board].dev);
|
||||
}
|
||||
330
src/device/keyboard.c
Normal file
330
src/device/keyboard.c
Normal file
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* General keyboard driver interface.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
|
||||
* Miran Grca, <mgrca8@gmail.com>
|
||||
* Fred N. van Kempen, <decwiz@yahoo.com>
|
||||
*
|
||||
* Copyright 2008-2019 Sarah Walker.
|
||||
* Copyright 2015-2019 Miran Grca.
|
||||
* Copyright 2017-2019 Fred N. van Kempen.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#include <86box/86box.h>
|
||||
#include <86box/machine.h>
|
||||
#include <86box/keyboard.h>
|
||||
|
||||
|
||||
int keyboard_scan;
|
||||
void (*keyboard_send)(uint16_t val);
|
||||
|
||||
|
||||
static int recv_key[512]; /* keyboard input buffer */
|
||||
static int oldkey[512];
|
||||
#if 0
|
||||
static int keydelay[512];
|
||||
#endif
|
||||
static scancode *scan_table; /* scancode table for keyboard */
|
||||
|
||||
static uint8_t caps_lock = 0;
|
||||
static uint8_t num_lock = 0;
|
||||
static uint8_t scroll_lock = 0;
|
||||
static uint8_t shift = 0;
|
||||
|
||||
|
||||
void
|
||||
keyboard_init(void)
|
||||
{
|
||||
memset(recv_key, 0x00, sizeof(recv_key));
|
||||
|
||||
keyboard_scan = 1;
|
||||
scan_table = NULL;
|
||||
|
||||
memset(keyboard_set3_flags, 0x00, sizeof(keyboard_set3_flags));
|
||||
keyboard_set3_all_repeat = 0;
|
||||
keyboard_set3_all_break = 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
keyboard_set_table(const scancode *ptr)
|
||||
{
|
||||
scan_table = (scancode *) ptr;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
fake_shift_needed(uint16_t scan)
|
||||
{
|
||||
switch(scan) {
|
||||
case 0x147:
|
||||
case 0x148:
|
||||
case 0x149:
|
||||
case 0x14a:
|
||||
case 0x14b:
|
||||
case 0x14d:
|
||||
case 0x14f:
|
||||
case 0x150:
|
||||
case 0x151:
|
||||
case 0x152:
|
||||
case 0x153:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
key_process(uint16_t scan, int down)
|
||||
{
|
||||
scancode *codes = scan_table;
|
||||
int c;
|
||||
|
||||
if (! keyboard_scan) return;
|
||||
|
||||
oldkey[scan] = down;
|
||||
if (down && codes[scan].mk[0] == 0)
|
||||
return;
|
||||
|
||||
if (!down && codes[scan].brk[0] == 0)
|
||||
return;
|
||||
|
||||
if (AT && ((keyboard_mode & 3) == 3)) {
|
||||
if (!keyboard_set3_all_break && !down && !(keyboard_set3_flags[codes[scan].mk[0]] & 2))
|
||||
return;
|
||||
}
|
||||
|
||||
c = 0;
|
||||
if (down) {
|
||||
/* Send the special code indicating an opening fake shift might be needed. */
|
||||
if (fake_shift_needed(scan))
|
||||
keyboard_send(0x100);
|
||||
while (codes[scan].mk[c] != 0)
|
||||
keyboard_send(codes[scan].mk[c++]);
|
||||
} else {
|
||||
while (codes[scan].brk[c] != 0)
|
||||
keyboard_send(codes[scan].brk[c++]);
|
||||
/* Send the special code indicating a closing fake shift might be needed. */
|
||||
if (fake_shift_needed(scan))
|
||||
keyboard_send(0x101);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Handle a keystroke event from the UI layer. */
|
||||
void
|
||||
keyboard_input(int down, uint16_t scan)
|
||||
{
|
||||
/* Translate E0 xx scan codes to 01xx because we use 512-byte arrays for states
|
||||
and scan code sets. */
|
||||
if ((scan >> 8) == 0xe0) {
|
||||
scan &= 0x00ff;
|
||||
scan |= 0x0100; /* extended key code */
|
||||
} else if ((scan >> 8) != 0x01)
|
||||
scan &= 0x00ff; /* we can receive a scan code whose upper byte is 0x01,
|
||||
this means we're the Win32 version running on windows
|
||||
that already sends us preprocessed scan codes, which
|
||||
means we then use the scan code as is, and need to
|
||||
make sure we do not accidentally strip that upper byte */
|
||||
|
||||
if (recv_key[scan & 0x1ff] ^ down) {
|
||||
if (down) {
|
||||
switch(scan & 0x1ff) {
|
||||
case 0x01c: /* Left Ctrl */
|
||||
shift |= 0x01;
|
||||
break;
|
||||
case 0x11c: /* Right Ctrl */
|
||||
shift |= 0x10;
|
||||
break;
|
||||
case 0x02a: /* Left Shift */
|
||||
shift |= 0x02;
|
||||
break;
|
||||
case 0x036: /* Right Shift */
|
||||
shift |= 0x20;
|
||||
break;
|
||||
case 0x038: /* Left Alt */
|
||||
shift |= 0x04;
|
||||
break;
|
||||
case 0x138: /* Right Alt */
|
||||
shift |= 0x40;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch(scan & 0x1ff) {
|
||||
case 0x01c: /* Left Ctrl */
|
||||
shift &= ~0x01;
|
||||
break;
|
||||
case 0x11c: /* Right Ctrl */
|
||||
shift &= ~0x10;
|
||||
break;
|
||||
case 0x02a: /* Left Shift */
|
||||
shift &= ~0x02;
|
||||
break;
|
||||
case 0x036: /* Right Shift */
|
||||
shift &= ~0x20;
|
||||
break;
|
||||
case 0x038: /* Left Alt */
|
||||
shift &= ~0x04;
|
||||
break;
|
||||
case 0x138: /* Right Alt */
|
||||
shift &= ~0x40;
|
||||
break;
|
||||
case 0x03a: /* Caps Lock */
|
||||
caps_lock ^= 1;
|
||||
break;
|
||||
case 0x045:
|
||||
num_lock ^= 1;
|
||||
break;
|
||||
case 0x046:
|
||||
scroll_lock ^= 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: Shouldn't this be some sort of bit shift? An array of 8 unsigned 64-bit integers
|
||||
should be enough. */
|
||||
/* recv_key[scan >> 6] |= ((uint64_t) down << ((uint64_t) scan & 0x3fLL)); */
|
||||
|
||||
/* pclog("Received scan code: %03X (%s)\n", scan & 0x1ff, down ? "down" : "up"); */
|
||||
recv_key[scan & 0x1ff] = down;
|
||||
|
||||
key_process(scan & 0x1ff, down);
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
keyboard_do_break(uint16_t scan)
|
||||
{
|
||||
scancode *codes = scan_table;
|
||||
|
||||
if (AT && ((keyboard_mode & 3) == 3)) {
|
||||
if (!keyboard_set3_all_break &&
|
||||
!recv_key[scan] &&
|
||||
!(keyboard_set3_flags[codes[scan].mk[0]] & 2))
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
} else
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Also called by the emulated keyboard controller to update the states of
|
||||
Caps Lock, Num Lock, and Scroll Lock when receving the "Set keyboard LEDs"
|
||||
command. */
|
||||
void
|
||||
keyboard_update_states(uint8_t cl, uint8_t nl, uint8_t sl)
|
||||
{
|
||||
caps_lock = cl;
|
||||
num_lock = nl;
|
||||
scroll_lock = sl;
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
keyboard_get_shift(void)
|
||||
{
|
||||
return shift;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
keyboard_get_states(uint8_t *cl, uint8_t *nl, uint8_t *sl)
|
||||
{
|
||||
if (cl)
|
||||
*cl = caps_lock;
|
||||
if (nl)
|
||||
*nl = num_lock;
|
||||
if (sl)
|
||||
*sl = scroll_lock;
|
||||
}
|
||||
|
||||
|
||||
/* Called by the UI to update the states of Caps Lock, Num Lock, and Scroll Lock. */
|
||||
void
|
||||
keyboard_set_states(uint8_t cl, uint8_t nl, uint8_t sl)
|
||||
{
|
||||
scancode *codes = scan_table;
|
||||
|
||||
int i;
|
||||
|
||||
if (caps_lock != cl) {
|
||||
i = 0;
|
||||
while (codes[0x03a].mk[i] != 0)
|
||||
keyboard_send(codes[0x03a].mk[i++]);
|
||||
if (keyboard_do_break(0x03a)) {
|
||||
i = 0;
|
||||
while (codes[0x03a].brk[i] != 0)
|
||||
keyboard_send(codes[0x03a].brk[i++]);
|
||||
}
|
||||
}
|
||||
|
||||
if (num_lock != nl) {
|
||||
i = 0;
|
||||
while (codes[0x045].mk[i] != 0)
|
||||
keyboard_send(codes[0x045].mk[i++]);
|
||||
if (keyboard_do_break(0x045)) {
|
||||
i = 0;
|
||||
while (codes[0x045].brk[i] != 0)
|
||||
keyboard_send(codes[0x045].brk[i++]);
|
||||
}
|
||||
}
|
||||
|
||||
if (scroll_lock != sl) {
|
||||
i = 0;
|
||||
while (codes[0x046].mk[i] != 0)
|
||||
keyboard_send(codes[0x046].mk[i++]);
|
||||
if (keyboard_do_break(0x046)) {
|
||||
i = 0;
|
||||
while (codes[0x046].brk[i] != 0)
|
||||
keyboard_send(codes[0x046].brk[i++]);
|
||||
}
|
||||
}
|
||||
|
||||
keyboard_update_states(cl, nl, sl);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
keyboard_recv(uint16_t key)
|
||||
{
|
||||
return recv_key[key];
|
||||
}
|
||||
|
||||
|
||||
/* Do we have Control-Alt-PgDn in the keyboard buffer? */
|
||||
int
|
||||
keyboard_isfsexit(void)
|
||||
{
|
||||
return( (recv_key[0x01D] || recv_key[0x11D]) &&
|
||||
(recv_key[0x038] || recv_key[0x138]) &&
|
||||
(recv_key[0x051] || recv_key[0x151]) );
|
||||
}
|
||||
|
||||
|
||||
/* Do we have F8-F12 in the keyboard buffer? */
|
||||
int
|
||||
keyboard_ismsexit(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
/* Windows: F8+F12 */
|
||||
return( recv_key[0x042] && recv_key[0x058] );
|
||||
#else
|
||||
/* WxWidgets cannot do two regular keys.. CTRL+END */
|
||||
return( (recv_key[0x01D] || recv_key[0x11D]) && (recv_key[0x04F] || recv_key[0x14F]) );
|
||||
#endif
|
||||
}
|
||||
2677
src/device/keyboard_at.c
Normal file
2677
src/device/keyboard_at.c
Normal file
File diff suppressed because it is too large
Load Diff
814
src/device/keyboard_xt.c
Normal file
814
src/device/keyboard_xt.c
Normal file
@@ -0,0 +1,814 @@
|
||||
/*
|
||||
* 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 the XT-style keyboard.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
|
||||
* Miran Grca, <mgrca8@gmail.com>
|
||||
* Fred N. van Kempen, <decwiz@yahoo.com>
|
||||
*
|
||||
* Copyright 2008-2019 Sarah Walker.
|
||||
* Copyright 2016-2019 Miran Grca.
|
||||
* Copyright 2017-2019 Fred N. van kempen.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#include <86box/86box.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/timer.h>
|
||||
#include <86box/fdd.h>
|
||||
#include <86box/machine.h>
|
||||
#include <86box/m_xt_t1000.h>
|
||||
#include <86box/io.h>
|
||||
#include <86box/pic.h>
|
||||
#include <86box/pit.h>
|
||||
#include <86box/ppi.h>
|
||||
#include <86box/mem.h>
|
||||
#include <86box/rom.h>
|
||||
#include <86box/sound.h>
|
||||
#include <86box/snd_speaker.h>
|
||||
#include <86box/video.h>
|
||||
#include <86box/keyboard.h>
|
||||
|
||||
|
||||
#define STAT_PARITY 0x80
|
||||
#define STAT_RTIMEOUT 0x40
|
||||
#define STAT_TTIMEOUT 0x20
|
||||
#define STAT_LOCK 0x10
|
||||
#define STAT_CD 0x08
|
||||
#define STAT_SYSFLAG 0x04
|
||||
#define STAT_IFULL 0x02
|
||||
#define STAT_OFULL 0x01
|
||||
|
||||
|
||||
typedef struct {
|
||||
int want_irq;
|
||||
int blocked;
|
||||
int tandy;
|
||||
|
||||
uint8_t pa, pb, pd;
|
||||
uint8_t key_waiting;
|
||||
uint8_t type;
|
||||
|
||||
pc_timer_t send_delay_timer;
|
||||
} xtkbd_t;
|
||||
|
||||
|
||||
/*XT keyboard has no escape scancodes, and no scancodes beyond 53*/
|
||||
const scancode scancode_xt[512] = {
|
||||
{ {0}, {0} }, { {0x01, 0}, {0x81, 0} },
|
||||
{ {0x02, 0}, {0x82, 0} }, { {0x03, 0}, {0x83, 0} },
|
||||
{ {0x04, 0}, {0x84, 0} }, { {0x05, 0}, {0x85, 0} },
|
||||
{ {0x06, 0}, {0x86, 0} }, { {0x07, 0}, {0x87, 0} },
|
||||
{ {0x08, 0}, {0x88, 0} }, { {0x09, 0}, {0x89, 0} },
|
||||
{ {0x0a, 0}, {0x8a, 0} }, { {0x0b, 0}, {0x8b, 0} },
|
||||
{ {0x0c, 0}, {0x8c, 0} }, { {0x0d, 0}, {0x8d, 0} },
|
||||
{ {0x0e, 0}, {0x8e, 0} }, { {0x0f, 0}, {0x8f, 0} },
|
||||
{ {0x10, 0}, {0x90, 0} }, { {0x11, 0}, {0x91, 0} },
|
||||
{ {0x12, 0}, {0x92, 0} }, { {0x13, 0}, {0x93, 0} },
|
||||
{ {0x14, 0}, {0x94, 0} }, { {0x15, 0}, {0x95, 0} },
|
||||
{ {0x16, 0}, {0x96, 0} }, { {0x17, 0}, {0x97, 0} },
|
||||
{ {0x18, 0}, {0x98, 0} }, { {0x19, 0}, {0x99, 0} },
|
||||
{ {0x1a, 0}, {0x9a, 0} }, { {0x1b, 0}, {0x9b, 0} },
|
||||
{ {0x1c, 0}, {0x9c, 0} }, { {0x1d, 0}, {0x9d, 0} },
|
||||
{ {0x1e, 0}, {0x9e, 0} }, { {0x1f, 0}, {0x9f, 0} },
|
||||
{ {0x20, 0}, {0xa0, 0} }, { {0x21, 0}, {0xa1, 0} },
|
||||
{ {0x22, 0}, {0xa2, 0} }, { {0x23, 0}, {0xa3, 0} },
|
||||
{ {0x24, 0}, {0xa4, 0} }, { {0x25, 0}, {0xa5, 0} },
|
||||
{ {0x26, 0}, {0xa6, 0} }, { {0x27, 0}, {0xa7, 0} },
|
||||
{ {0x28, 0}, {0xa8, 0} }, { {0x29, 0}, {0xa9, 0} },
|
||||
{ {0x2a, 0}, {0xaa, 0} }, { {0x2b, 0}, {0xab, 0} },
|
||||
{ {0x2c, 0}, {0xac, 0} }, { {0x2d, 0}, {0xad, 0} },
|
||||
{ {0x2e, 0}, {0xae, 0} }, { {0x2f, 0}, {0xaf, 0} },
|
||||
{ {0x30, 0}, {0xb0, 0} }, { {0x31, 0}, {0xb1, 0} },
|
||||
{ {0x32, 0}, {0xb2, 0} }, { {0x33, 0}, {0xb3, 0} },
|
||||
{ {0x34, 0}, {0xb4, 0} }, { {0x35, 0}, {0xb5, 0} },
|
||||
{ {0x36, 0}, {0xb6, 0} }, { {0x37, 0}, {0xb7, 0} },
|
||||
{ {0x38, 0}, {0xb8, 0} }, { {0x39, 0}, {0xb9, 0} },
|
||||
{ {0x3a, 0}, {0xba, 0} }, { {0x3b, 0}, {0xbb, 0} },
|
||||
{ {0x3c, 0}, {0xbc, 0} }, { {0x3d, 0}, {0xbd, 0} },
|
||||
{ {0x3e, 0}, {0xbe, 0} }, { {0x3f, 0}, {0xbf, 0} },
|
||||
{ {0x40, 0}, {0xc0, 0} }, { {0x41, 0}, {0xc1, 0} },
|
||||
{ {0x42, 0}, {0xc2, 0} }, { {0x43, 0}, {0xc3, 0} },
|
||||
{ {0x44, 0}, {0xc4, 0} }, { {0x45, 0}, {0xc5, 0} },
|
||||
{ {0x46, 0}, {0xc6, 0} }, { {0x47, 0}, {0xc7, 0} },
|
||||
{ {0x48, 0}, {0xc8, 0} }, { {0x49, 0}, {0xc9, 0} },
|
||||
{ {0x4a, 0}, {0xca, 0} }, { {0x4b, 0}, {0xcb, 0} },
|
||||
{ {0x4c, 0}, {0xcc, 0} }, { {0x4d, 0}, {0xcd, 0} },
|
||||
{ {0x4e, 0}, {0xce, 0} }, { {0x4f, 0}, {0xcf, 0} },
|
||||
{ {0x50, 0}, {0xd0, 0} }, { {0x51, 0}, {0xd1, 0} },
|
||||
{ {0x52, 0}, {0xd2, 0} }, { {0x53, 0}, {0xd3, 0} },
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*054*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*058*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*05c*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*060*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*064*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*068*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*06c*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*070*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*074*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*078*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*07c*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*080*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*084*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*088*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*08c*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*090*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*094*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*098*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*09c*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0a0*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0a4*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0a8*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0ac*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0b0*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0b4*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0b8*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0bc*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0c0*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0c4*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0c8*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0cc*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0d0*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0d4*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0d8*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0dc*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0e0*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0e4*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0e8*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0ec*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0f0*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0f4*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0f8*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*0fc*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*100*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*104*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*108*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*10c*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*110*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*114*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*118*/
|
||||
{ {0x1c, 0}, {0x9c, 0} }, { {0x1d, 0}, {0x9d, 0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*11c*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*120*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*124*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*128*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*12c*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*130*/
|
||||
{ {0}, {0} }, { {0x35, 0}, {0xb5, 0} },
|
||||
{ {0}, {0} }, { {0x37, 0}, {0xb7, 0} }, /*134*/
|
||||
{ {0x38, 0}, {0xb8, 0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*138*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*13c*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*140*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0x46, 0}, {0xc6, 0} }, { {0x47, 0}, {0xc7, 0} }, /*144*/
|
||||
{ {0x48, 0}, {0xc8, 0} }, { {0x49, 0}, {0xc9, 0} },
|
||||
{ {0}, {0} }, { {0x4b, 0}, {0xcb, 0} }, /*148*/
|
||||
{ {0}, {0} }, { {0x4d, 0}, {0xcd, 0} },
|
||||
{ {0}, {0} }, { {0x4f, 0}, {0xcf, 0} }, /*14c*/
|
||||
{ {0x50, 0}, {0xd0, 0} }, { {0x51, 0}, {0xd1, 0} },
|
||||
{ {0x52, 0}, {0xd2, 0} }, { {0x53, 0}, {0xd3, 0} }, /*150*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*154*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*158*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*15c*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*160*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*164*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*168*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*16c*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*170*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*174*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*148*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*17c*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*180*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*184*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*88*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*18c*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*190*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*194*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*198*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*19c*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1a0*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1a4*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1a8*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1ac*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1b0*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1b4*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1b8*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1bc*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1c0*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1c4*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1c8*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1cc*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1d0*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1d4*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1d8*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1dc*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1e0*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1e4*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1e8*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1ec*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1f0*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1f4*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} }, /*1f8*/
|
||||
{ {0}, {0} }, { {0}, {0} },
|
||||
{ {0}, {0} }, { {0}, {0} } /*1fc*/
|
||||
};
|
||||
|
||||
|
||||
static uint8_t key_queue[16];
|
||||
static int key_queue_start = 0,
|
||||
key_queue_end = 0;
|
||||
static int is_t1x00 = 0;
|
||||
|
||||
|
||||
#ifdef ENABLE_KEYBOARD_XT_LOG
|
||||
int keyboard_xt_do_log = ENABLE_KEYBOARD_XT_LOG;
|
||||
|
||||
|
||||
static void
|
||||
kbd_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (keyboard_xt_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define kbd_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
|
||||
static void
|
||||
kbd_poll(void *priv)
|
||||
{
|
||||
xtkbd_t *kbd = (xtkbd_t *)priv;
|
||||
|
||||
timer_advance_u64(&kbd->send_delay_timer, 1000 * TIMER_USEC);
|
||||
|
||||
if (!(kbd->pb & 0x40) && (kbd->type != 5))
|
||||
return;
|
||||
|
||||
if (kbd->want_irq) {
|
||||
kbd->want_irq = 0;
|
||||
kbd->pa = kbd->key_waiting;
|
||||
kbd->blocked = 1;
|
||||
picint(2);
|
||||
#ifdef ENABLE_KEYBOARD_XT_LOG
|
||||
kbd_log("keyboard_xt : take IRQ\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (key_queue_start != key_queue_end && !kbd->blocked) {
|
||||
kbd->key_waiting = key_queue[key_queue_start];
|
||||
kbd_log("XTkbd: reading %02X from the key queue at %i\n",
|
||||
kbd->key_waiting, key_queue_start);
|
||||
key_queue_start = (key_queue_start + 1) & 0x0f;
|
||||
kbd->want_irq = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
kbd_adddata(uint16_t val)
|
||||
{
|
||||
/* Test for T1000 'Fn' key (Right Alt / Right Ctrl) */
|
||||
if (is_t1x00) {
|
||||
if (keyboard_recv(0xb8) || keyboard_recv(0x9d)) { /* 'Fn' pressed */
|
||||
t1000_syskey(0x00, 0x04, 0x00); /* Set 'Fn' indicator */
|
||||
switch (val) {
|
||||
case 0x45: /* Num Lock => toggle numpad */
|
||||
t1000_syskey(0x00, 0x00, 0x10); break;
|
||||
case 0x47: /* Home => internal display */
|
||||
t1000_syskey(0x40, 0x00, 0x00); break;
|
||||
case 0x49: /* PgDn => turbo on */
|
||||
t1000_syskey(0x80, 0x00, 0x00); break;
|
||||
case 0x4D: /* Right => toggle LCD font */
|
||||
t1000_syskey(0x00, 0x00, 0x20); break;
|
||||
case 0x4F: /* End => external display */
|
||||
t1000_syskey(0x00, 0x40, 0x00); break;
|
||||
case 0x51: /* PgDn => turbo off */
|
||||
t1000_syskey(0x00, 0x80, 0x00); break;
|
||||
case 0x54: /* SysRQ => toggle window */
|
||||
t1000_syskey(0x00, 0x00, 0x08); break;
|
||||
}
|
||||
} else
|
||||
t1000_syskey(0x04, 0x00, 0x00); /* Reset 'Fn' indicator */
|
||||
}
|
||||
|
||||
key_queue[key_queue_end] = val;
|
||||
kbd_log("XTkbd: %02X added to key queue at %i\n",
|
||||
val, key_queue_end);
|
||||
key_queue_end = (key_queue_end + 1) & 0x0f;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
kbd_adddata_process(uint16_t val, void (*adddata)(uint16_t val))
|
||||
{
|
||||
uint8_t num_lock = 0, shift_states = 0;
|
||||
|
||||
if (!adddata)
|
||||
return;
|
||||
|
||||
keyboard_get_states(NULL, &num_lock, NULL);
|
||||
shift_states = keyboard_get_shift() & STATE_SHIFT_MASK;
|
||||
|
||||
switch(val) {
|
||||
case FAKE_LSHIFT_ON:
|
||||
if (num_lock) {
|
||||
if (!shift_states) {
|
||||
/* Num lock on and no shifts are pressed, send non-inverted fake shift. */
|
||||
adddata(0x2a);
|
||||
}
|
||||
} else {
|
||||
if (shift_states & STATE_LSHIFT) {
|
||||
/* Num lock off and left shift pressed. */
|
||||
adddata(0xaa);
|
||||
}
|
||||
if (shift_states & STATE_RSHIFT) {
|
||||
/* Num lock off and right shift pressed. */
|
||||
adddata(0xb6);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FAKE_LSHIFT_OFF:
|
||||
if (num_lock) {
|
||||
if (!shift_states) {
|
||||
/* Num lock on and no shifts are pressed, send non-inverted fake shift. */
|
||||
adddata(0xaa);
|
||||
}
|
||||
} else {
|
||||
if (shift_states & STATE_LSHIFT) {
|
||||
/* Num lock off and left shift pressed. */
|
||||
adddata(0x2a);
|
||||
}
|
||||
if (shift_states & STATE_RSHIFT) {
|
||||
/* Num lock off and right shift pressed. */
|
||||
adddata(0x36);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
adddata(val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
kbd_adddata_ex(uint16_t val)
|
||||
{
|
||||
kbd_adddata_process(val, kbd_adddata);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
kbd_write(uint16_t port, uint8_t val, void *priv)
|
||||
{
|
||||
xtkbd_t *kbd = (xtkbd_t *)priv;
|
||||
|
||||
switch (port) {
|
||||
case 0x61:
|
||||
if (!(kbd->pb & 0x40) && (val & 0x40)) {
|
||||
key_queue_start = key_queue_end = 0;
|
||||
kbd->want_irq = 0;
|
||||
kbd->blocked = 0;
|
||||
kbd_adddata(0xaa);
|
||||
}
|
||||
kbd->pb = val;
|
||||
ppi.pb = val;
|
||||
|
||||
speaker_update();
|
||||
if ((kbd->type <= 1) && !(kbd->pb & 0x08))
|
||||
speaker_gated = speaker_enable = 1;
|
||||
else {
|
||||
speaker_gated = val & 1;
|
||||
speaker_enable = val & 2;
|
||||
}
|
||||
if (speaker_enable)
|
||||
was_speaker_enable = 1;
|
||||
pit_ctr_set_gate(&pit->counters[2], val & 1);
|
||||
|
||||
if (val & 0x80) {
|
||||
kbd->pa = 0;
|
||||
kbd->blocked = 0;
|
||||
picintc(2);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_KEYBOARD_XT_LOG
|
||||
if (kbd->type <= 1)
|
||||
kbd_log("Casette motor is %s\n", !(val & 0x08) ? "ON" : "OFF");
|
||||
#endif
|
||||
break;
|
||||
#ifdef ENABLE_KEYBOARD_XT_LOG
|
||||
case 0x62:
|
||||
if (kbd->type <= 1)
|
||||
kbd_log("Casette IN is %i\n", !!(val & 0x10));
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
kbd_read(uint16_t port, void *priv)
|
||||
{
|
||||
xtkbd_t *kbd = (xtkbd_t *)priv;
|
||||
uint8_t ret = 0xff;
|
||||
|
||||
switch (port) {
|
||||
case 0x60:
|
||||
if ((kbd->type <= 1) && (kbd->pb & 0x80))
|
||||
ret = (kbd->pd & ~0x02) | (hasfpu ? 0x02 : 0x00);
|
||||
else if (((kbd->type == 2) || (kbd->type == 3)) && (kbd->pb & 0x80))
|
||||
ret = 0xff; /* According to Ruud on the PCem forum, this is supposed to return 0xFF on the XT. */
|
||||
else
|
||||
ret = kbd->pa;
|
||||
break;
|
||||
|
||||
case 0x61:
|
||||
ret = kbd->pb;
|
||||
break;
|
||||
|
||||
case 0x62:
|
||||
if (kbd->type == 0)
|
||||
ret = 0x00;
|
||||
else if (kbd->type == 1) {
|
||||
if (kbd->pb & 0x04)
|
||||
ret = ((mem_size-64) / 32) & 0x0f;
|
||||
else
|
||||
ret = ((mem_size-64) / 32) >> 4;
|
||||
} else {
|
||||
if (kbd->pb & 0x08)
|
||||
ret = kbd->pd >> 4;
|
||||
else {
|
||||
/* LaserXT = Always 512k RAM;
|
||||
LaserXT/3 = Bit 0: set = 512k, clear = 256k. */
|
||||
#if defined(DEV_BRANCH) && defined(USE_LASERXT)
|
||||
if (kbd->type == 6)
|
||||
ret = ((mem_size == 512) ? 0x0d : 0x0c) | (hasfpu ? 0x02 : 0x00);
|
||||
else
|
||||
#endif
|
||||
ret = (kbd->pd & 0x0d) | (hasfpu ? 0x02 : 0x00);
|
||||
}
|
||||
}
|
||||
ret |= (ppispeakon ? 0x20 : 0);
|
||||
|
||||
/* This is needed to avoid error 131 (cassette error).
|
||||
This is serial read: bit 5 = clock, bit 4 = data, cassette header is 256 x 0xff. */
|
||||
if (kbd->type <= 1)
|
||||
ret |= (ppispeakon ? 0x10 : 0);
|
||||
|
||||
if (kbd->type == 5)
|
||||
ret |= (tandy1k_eeprom_read() ? 0x10 : 0);
|
||||
break;
|
||||
|
||||
case 0x63:
|
||||
if ((kbd->type == 2) || (kbd->type == 3) || (kbd->type == 4) || (kbd->type == 6))
|
||||
ret = kbd->pd;
|
||||
break;
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
kbd_reset(void *priv)
|
||||
{
|
||||
xtkbd_t *kbd = (xtkbd_t *)priv;
|
||||
|
||||
kbd->want_irq = 0;
|
||||
kbd->blocked = 0;
|
||||
kbd->pa = 0x00;
|
||||
kbd->pb = 0x00;
|
||||
|
||||
keyboard_scan = 1;
|
||||
|
||||
key_queue_start = 0,
|
||||
key_queue_end = 0;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
kbd_init(const device_t *info)
|
||||
{
|
||||
int i, fdd_count = 0;
|
||||
|
||||
xtkbd_t *kbd;
|
||||
|
||||
kbd = (xtkbd_t *)malloc(sizeof(xtkbd_t));
|
||||
memset(kbd, 0x00, sizeof(xtkbd_t));
|
||||
|
||||
io_sethandler(0x0060, 4,
|
||||
kbd_read, NULL, NULL, kbd_write, NULL, NULL, kbd);
|
||||
keyboard_send = kbd_adddata_ex;
|
||||
kbd_reset(kbd);
|
||||
kbd->type = info->local;
|
||||
|
||||
key_queue_start = key_queue_end = 0;
|
||||
|
||||
video_reset(gfxcard);
|
||||
|
||||
if (kbd->type <= 3) {
|
||||
for (i = 0; i < FDD_NUM; i++) {
|
||||
if (fdd_get_flags(i))
|
||||
fdd_count++;
|
||||
}
|
||||
|
||||
/* DIP switch readout: bit set = OFF, clear = ON. */
|
||||
/* Switches 7, 8 - floppy drives. */
|
||||
if (!fdd_count)
|
||||
kbd->pd = 0x00;
|
||||
else
|
||||
kbd->pd = ((fdd_count - 1) << 6) | 0x01;
|
||||
/* Switches 5, 6 - video. */
|
||||
if (video_is_mda())
|
||||
kbd->pd |= 0x30;
|
||||
else if (video_is_cga())
|
||||
kbd->pd |= 0x20; /* 0x10 would be 40x25 */
|
||||
else
|
||||
kbd->pd |= 0x00;
|
||||
/* Switches 3, 4 - memory size. */
|
||||
if ((kbd->type == 3) || (kbd->type == 4) || (kbd->type == 6)) {
|
||||
switch (mem_size) {
|
||||
case 256:
|
||||
kbd->pd |= 0x00;
|
||||
break;
|
||||
case 512:
|
||||
kbd->pd |= 0x04;
|
||||
break;
|
||||
case 576:
|
||||
kbd->pd |= 0x08;
|
||||
break;
|
||||
case 640:
|
||||
default:
|
||||
kbd->pd |= 0x0c;
|
||||
break;
|
||||
}
|
||||
} else if (kbd->type >= 1) {
|
||||
switch (mem_size) {
|
||||
case 64:
|
||||
kbd->pd |= 0x00;
|
||||
break;
|
||||
case 128:
|
||||
kbd->pd |= 0x04;
|
||||
break;
|
||||
case 192:
|
||||
kbd->pd |= 0x08;
|
||||
break;
|
||||
case 256:
|
||||
default:
|
||||
kbd->pd |= 0x0c;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (mem_size) {
|
||||
case 16:
|
||||
kbd->pd |= 0x00;
|
||||
break;
|
||||
case 32:
|
||||
kbd->pd |= 0x04;
|
||||
break;
|
||||
case 48:
|
||||
kbd->pd |= 0x08;
|
||||
break;
|
||||
case 64:
|
||||
default:
|
||||
kbd->pd |= 0x0c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Switch 2 - 8087 FPU. */
|
||||
if (hasfpu)
|
||||
kbd->pd |= 0x02;
|
||||
|
||||
/* Switch 1 - always off. */
|
||||
kbd->pd |= 0x01;
|
||||
}
|
||||
|
||||
timer_add(&kbd->send_delay_timer, kbd_poll, kbd, 1);
|
||||
|
||||
keyboard_set_table(scancode_xt);
|
||||
|
||||
is_t1x00 = (kbd->type == 6);
|
||||
|
||||
return(kbd);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
kbd_close(void *priv)
|
||||
{
|
||||
xtkbd_t *kbd = (xtkbd_t *)priv;
|
||||
|
||||
/* Stop the timer. */
|
||||
timer_disable(&kbd->send_delay_timer);
|
||||
|
||||
/* Disable scanning. */
|
||||
keyboard_scan = 0;
|
||||
|
||||
keyboard_send = NULL;
|
||||
|
||||
io_removehandler(0x0060, 4,
|
||||
kbd_read, NULL, NULL, kbd_write, NULL, NULL, kbd);
|
||||
|
||||
free(kbd);
|
||||
}
|
||||
|
||||
|
||||
const device_t keyboard_pc_device = {
|
||||
"IBM PC Keyboard (1981)",
|
||||
0,
|
||||
0,
|
||||
kbd_init,
|
||||
kbd_close,
|
||||
kbd_reset,
|
||||
NULL, NULL, NULL
|
||||
};
|
||||
|
||||
const device_t keyboard_pc82_device = {
|
||||
"IBM PC Keyboard (1982)",
|
||||
0,
|
||||
1,
|
||||
kbd_init,
|
||||
kbd_close,
|
||||
kbd_reset,
|
||||
NULL, NULL, NULL
|
||||
};
|
||||
|
||||
const device_t keyboard_xt_device = {
|
||||
"XT (1982) Keyboard",
|
||||
0,
|
||||
2,
|
||||
kbd_init,
|
||||
kbd_close,
|
||||
kbd_reset,
|
||||
NULL, NULL, NULL
|
||||
};
|
||||
|
||||
const device_t keyboard_xt86_device = {
|
||||
"XT (1986) Keyboard",
|
||||
0,
|
||||
3,
|
||||
kbd_init,
|
||||
kbd_close,
|
||||
kbd_reset,
|
||||
NULL, NULL, NULL
|
||||
};
|
||||
|
||||
const device_t keyboard_xt_compaq_device = {
|
||||
"Compaq Portable Keyboard",
|
||||
0,
|
||||
4,
|
||||
kbd_init,
|
||||
kbd_close,
|
||||
kbd_reset,
|
||||
NULL, NULL, NULL
|
||||
};
|
||||
|
||||
const device_t keyboard_tandy_device = {
|
||||
"Tandy 1000 Keyboard",
|
||||
0,
|
||||
5,
|
||||
kbd_init,
|
||||
kbd_close,
|
||||
kbd_reset,
|
||||
NULL, NULL, NULL
|
||||
};
|
||||
|
||||
const device_t keyboard_xt_t1x00_device = {
|
||||
"Toshiba T1x00 Keyboard",
|
||||
0,
|
||||
6,
|
||||
kbd_init,
|
||||
kbd_close,
|
||||
kbd_reset,
|
||||
NULL, NULL, NULL
|
||||
};
|
||||
|
||||
#if defined(DEV_BRANCH) && defined(USE_LASERXT)
|
||||
const device_t keyboard_xt_lxt3_device = {
|
||||
"VTech Laser XT3 Keyboard",
|
||||
0,
|
||||
7,
|
||||
kbd_init,
|
||||
kbd_close,
|
||||
kbd_reset,
|
||||
NULL, NULL, NULL
|
||||
};
|
||||
#endif
|
||||
256
src/device/mouse.c
Normal file
256
src/device/mouse.c
Normal file
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Common driver module for MOUSE devices.
|
||||
*
|
||||
* TODO: Add the Genius bus- and serial mouse.
|
||||
* Remove the '3-button' flag from mouse types.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Miran Grca, <mgrca8@gmail.com>
|
||||
* Fred N. van Kempen, <decwiz@yahoo.com>
|
||||
*
|
||||
* Copyright 2016-2018 Miran Grca.
|
||||
* Copyright 2017,2018 Fred N. van Kempen.
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#define HAVE_STDARG_H
|
||||
#include <86box/86box.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/mouse.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
const char *internal_name;
|
||||
const device_t *device;
|
||||
} mouse_t;
|
||||
|
||||
|
||||
int mouse_type = 0;
|
||||
int mouse_x,
|
||||
mouse_y,
|
||||
mouse_z,
|
||||
mouse_buttons;
|
||||
|
||||
|
||||
static const device_t mouse_none_device = {
|
||||
"None",
|
||||
0, MOUSE_TYPE_NONE,
|
||||
NULL, NULL, NULL,
|
||||
NULL, NULL, NULL,
|
||||
NULL
|
||||
};
|
||||
static const device_t mouse_internal_device = {
|
||||
"Internal Mouse",
|
||||
0, MOUSE_TYPE_INTERNAL,
|
||||
NULL, NULL, NULL,
|
||||
NULL, NULL, NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
static mouse_t mouse_devices[] = {
|
||||
{ "none", &mouse_none_device },
|
||||
{ "internal", &mouse_internal_device },
|
||||
{ "logibus", &mouse_logibus_device },
|
||||
{ "msbus", &mouse_msinport_device },
|
||||
#if 0
|
||||
{ "genibus", &mouse_genibus_device },
|
||||
#endif
|
||||
{ "mssystems", &mouse_mssystems_device },
|
||||
{ "msserial", &mouse_msserial_device },
|
||||
{ "ltserial", &mouse_ltserial_device },
|
||||
{ "ps2", &mouse_ps2_device },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
static const device_t *mouse_curr;
|
||||
static void *mouse_priv;
|
||||
static int mouse_nbut;
|
||||
static int (*mouse_dev_poll)();
|
||||
|
||||
|
||||
#ifdef ENABLE_MOUSE_LOG
|
||||
int mouse_do_log = ENABLE_MOUSE_LOG;
|
||||
|
||||
|
||||
static void
|
||||
mouse_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (mouse_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define mouse_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
|
||||
/* Initialize the mouse module. */
|
||||
void
|
||||
mouse_init(void)
|
||||
{
|
||||
/* Initialize local data. */
|
||||
mouse_x = mouse_y = mouse_z = 0;
|
||||
mouse_buttons = 0x00;
|
||||
|
||||
mouse_type = MOUSE_TYPE_NONE;
|
||||
mouse_curr = NULL;
|
||||
mouse_priv = NULL;
|
||||
mouse_nbut = 0;
|
||||
mouse_dev_poll = NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mouse_close(void)
|
||||
{
|
||||
if (mouse_curr == NULL) return;
|
||||
|
||||
mouse_curr = NULL;
|
||||
mouse_priv = NULL;
|
||||
mouse_nbut = 0;
|
||||
mouse_dev_poll = NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mouse_reset(void)
|
||||
{
|
||||
if (mouse_curr != NULL)
|
||||
return; /* Mouse already initialized. */
|
||||
|
||||
mouse_log("MOUSE: reset(type=%d, '%s')\n",
|
||||
mouse_type, mouse_devices[mouse_type].device->name);
|
||||
|
||||
/* Clear local data. */
|
||||
mouse_x = mouse_y = mouse_z = 0;
|
||||
mouse_buttons = 0x00;
|
||||
|
||||
/* If no mouse configured, we're done. */
|
||||
if (mouse_type == 0) return;
|
||||
|
||||
mouse_curr = mouse_devices[mouse_type].device;
|
||||
|
||||
if (mouse_curr != NULL)
|
||||
mouse_priv = device_add(mouse_curr);
|
||||
}
|
||||
|
||||
|
||||
/* Callback from the hardware driver. */
|
||||
void
|
||||
mouse_set_buttons(int buttons)
|
||||
{
|
||||
mouse_nbut = buttons;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mouse_process(void)
|
||||
{
|
||||
static int poll_delay = 2;
|
||||
|
||||
if (mouse_curr == NULL)
|
||||
return;
|
||||
|
||||
if (--poll_delay) return;
|
||||
|
||||
mouse_poll();
|
||||
|
||||
if ((mouse_dev_poll != NULL) || (mouse_curr->available != NULL)) {
|
||||
if (mouse_curr->available != NULL)
|
||||
mouse_curr->available(mouse_x,mouse_y,mouse_z,mouse_buttons, mouse_priv);
|
||||
else
|
||||
mouse_dev_poll(mouse_x,mouse_y,mouse_z,mouse_buttons, mouse_priv);
|
||||
|
||||
/* Reset mouse deltas. */
|
||||
mouse_x = mouse_y = mouse_z = 0;
|
||||
}
|
||||
|
||||
poll_delay = 2;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mouse_set_poll(int (*func)(int,int,int,int,void *), void *arg)
|
||||
{
|
||||
if (mouse_type != MOUSE_TYPE_INTERNAL) return;
|
||||
|
||||
mouse_dev_poll = func;
|
||||
mouse_priv = arg;
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
mouse_get_name(int mouse)
|
||||
{
|
||||
return((char *)mouse_devices[mouse].device->name);
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
mouse_get_internal_name(int mouse)
|
||||
{
|
||||
return((char *)mouse_devices[mouse].internal_name);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
mouse_get_from_internal_name(char *s)
|
||||
{
|
||||
int c = 0;
|
||||
|
||||
while (mouse_devices[c].internal_name != NULL) {
|
||||
if (! strcmp((char *)mouse_devices[c].internal_name, s))
|
||||
return(c);
|
||||
c++;
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
mouse_has_config(int mouse)
|
||||
{
|
||||
if (mouse_devices[mouse].device == NULL) return(0);
|
||||
|
||||
return(mouse_devices[mouse].device->config ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
const device_t *
|
||||
mouse_get_device(int mouse)
|
||||
{
|
||||
return(mouse_devices[mouse].device);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
mouse_get_buttons(void)
|
||||
{
|
||||
return(mouse_nbut);
|
||||
}
|
||||
|
||||
|
||||
/* Return number of MOUSE types we know about. */
|
||||
int
|
||||
mouse_get_ndev(void)
|
||||
{
|
||||
return((sizeof(mouse_devices)/sizeof(mouse_t)) - 1);
|
||||
}
|
||||
858
src/device/mouse_bus.c
Normal file
858
src/device/mouse_bus.c
Normal file
@@ -0,0 +1,858 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* NOTES: Ported from Bochs with extensive modifications per testing
|
||||
* of the real hardware, testing of drivers, and the old code.
|
||||
*
|
||||
* Logitech Bus Mouse verified with:
|
||||
* Linux Slackware 3.0
|
||||
* Logitech LMouse.com 3.12
|
||||
* Logitech LMouse.com 3.30
|
||||
* Logitech LMouse.com 3.41
|
||||
* Logitech LMouse.com 3.42
|
||||
* Logitech LMouse.com 4.00
|
||||
* Logitech LMouse.com 5.00
|
||||
* Logitech LMouse.com 6.00
|
||||
* Logitech LMouse.com 6.02 Beta
|
||||
* Logitech LMouse.com 6.02
|
||||
* Logitech LMouse.com 6.12
|
||||
* Logitech LMouse.com 6.20
|
||||
* Logitech LMouse.com 6.23
|
||||
* Logitech LMouse.com 6.30
|
||||
* Logitech LMouse.com 6.31E
|
||||
* Logitech LMouse.com 6.34
|
||||
* Logitech Mouse.exe 6.40
|
||||
* Logitech Mouse.exe 6.41
|
||||
* Logitech Mouse.exe 6.44
|
||||
* Logitech Mouse.exe 6.46
|
||||
* Logitech Mouse.exe 6.50
|
||||
* Microsoft Mouse.com 2.00
|
||||
* Microsoft Mouse.sys 3.00
|
||||
* Microsoft Mouse.com 7.04
|
||||
* Microsoft Mouse.com 8.21J
|
||||
* Microsoft Windows 1.00 DR5
|
||||
* Microsoft Windows 3.10.026
|
||||
* Microsoft Windows 3.10.068 both MOUSE.DRV and LMOUSE.DRV
|
||||
* Microsoft Windows NT 3.1
|
||||
* Microsoft Windows 95
|
||||
*
|
||||
* InPort verified with:
|
||||
* Linux Slackware 3.0
|
||||
* Logitech LMouse.com 6.12
|
||||
* Logitech LMouse.com 6.41
|
||||
* Microsoft Windows 3.10.068 both MOUSE.DRV and LMOUSE.DRV
|
||||
* Microsoft Windows NT 3.1
|
||||
* Microsoft Windows 98 SE
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Miran Grca, <mgrca8@gmail.com>
|
||||
* Fred N. van Kempen, <decwiz@yahoo.com>
|
||||
*
|
||||
* Copyright 200?-2019 Bochs.
|
||||
* Copyright 2017-2019 Miran Grca.
|
||||
* Copyright 1989-2019 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/86box.h>
|
||||
#include <86box/io.h>
|
||||
#include <86box/pic.h>
|
||||
#include <86box/timer.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/mouse.h>
|
||||
#include <86box/random.h>
|
||||
|
||||
#define IRQ_MASK ((1 << 5) >> dev->irq)
|
||||
|
||||
/* MS Inport Bus Mouse Adapter */
|
||||
#define INP_PORT_CONTROL 0x0000
|
||||
#define INP_PORT_DATA 0x0001
|
||||
#define INP_PORT_SIGNATURE 0x0002
|
||||
#define INP_PORT_CONFIG 0x0003
|
||||
|
||||
#define INP_CTRL_READ_BUTTONS 0x00
|
||||
#define INP_CTRL_READ_X 0x01
|
||||
#define INP_CTRL_READ_Y 0x02
|
||||
#define INP_CTRL_COMMAND 0x07
|
||||
#define INP_CTRL_RAISE_IRQ 0x16
|
||||
#define INP_CTRL_RESET 0x80
|
||||
|
||||
#define INP_HOLD_COUNTER (1 << 5)
|
||||
#define INP_ENABLE_TIMER_IRQ (1 << 4)
|
||||
#define INP_ENABLE_DATA_IRQ (1 << 3)
|
||||
#define INP_PERIOD_MASK 0x07
|
||||
|
||||
/* MS/Logictech Standard Bus Mouse Adapter */
|
||||
#define BUSM_PORT_DATA 0x0000
|
||||
#define BUSM_PORT_SIGNATURE 0x0001
|
||||
#define BUSM_PORT_CONTROL 0x0002
|
||||
#define BUSM_PORT_CONFIG 0x0003
|
||||
|
||||
#define HOLD_COUNTER (1 << 7)
|
||||
#define READ_X (0 << 6)
|
||||
#define READ_Y (1 << 6)
|
||||
#define READ_LOW (0 << 5)
|
||||
#define READ_HIGH (1 << 5)
|
||||
#define DISABLE_IRQ (1 << 4)
|
||||
|
||||
#define DEVICE_ACTIVE (1 << 7)
|
||||
|
||||
#define READ_X_LOW (READ_X | READ_LOW)
|
||||
#define READ_X_HIGH (READ_X | READ_HIGH)
|
||||
#define READ_Y_LOW (READ_Y | READ_LOW)
|
||||
#define READ_Y_HIGH (READ_Y | READ_HIGH)
|
||||
|
||||
#define FLAG_INPORT (1 << 0)
|
||||
#define FLAG_ENABLED (1 << 1)
|
||||
#define FLAG_HOLD (1 << 2)
|
||||
#define FLAG_TIMER_INT (1 << 3)
|
||||
#define FLAG_DATA_INT (1 << 4)
|
||||
|
||||
static const uint8_t periods[4] = { 30, 50, 100, 200 };
|
||||
|
||||
|
||||
/* Our mouse device. */
|
||||
typedef struct mouse {
|
||||
uint8_t current_b, control_val,
|
||||
config_val, sig_val,
|
||||
command_val, pad;
|
||||
|
||||
int8_t current_x, current_y;
|
||||
|
||||
int base, irq, bn, flags,
|
||||
mouse_delayed_dx, mouse_delayed_dy,
|
||||
mouse_buttons, mouse_buttons_last,
|
||||
toggle_counter, timer_enabled;
|
||||
|
||||
double period;
|
||||
pc_timer_t timer; /* mouse event timer */
|
||||
} mouse_t;
|
||||
|
||||
|
||||
#ifdef ENABLE_MOUSE_BUS_LOG
|
||||
int bm_do_log = ENABLE_MOUSE_BUS_LOG;
|
||||
|
||||
|
||||
static void
|
||||
bm_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (bm_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define bm_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
|
||||
/* Handle a READ operation from one of our registers. */
|
||||
static uint8_t
|
||||
lt_read(uint16_t port, void *priv)
|
||||
{
|
||||
mouse_t *dev = (mouse_t *)priv;
|
||||
uint8_t value = 0xff;
|
||||
|
||||
switch (port & 0x03) {
|
||||
case BUSM_PORT_DATA:
|
||||
/* Testing and another source confirm that the buttons are
|
||||
*ALWAYS* present, so I'm going to change this a bit. */
|
||||
switch (dev->control_val & 0x60) {
|
||||
case READ_X_LOW:
|
||||
value = dev->current_x & 0x0F;
|
||||
dev->current_x &= ~0x0F;
|
||||
break;
|
||||
case READ_X_HIGH:
|
||||
value = (dev->current_x >> 4) & 0x0F;
|
||||
dev->current_x &= ~0xF0;
|
||||
break;
|
||||
case READ_Y_LOW:
|
||||
value = dev->current_y & 0x0F;
|
||||
dev->current_y &= ~0x0F;
|
||||
break;
|
||||
case READ_Y_HIGH:
|
||||
value = (dev->current_y >> 4) & 0x0F;
|
||||
dev->current_y &= ~0xF0;
|
||||
break;
|
||||
default:
|
||||
bm_log("ERROR: Reading data port in unsupported mode 0x%02x\n", dev->control_val);
|
||||
}
|
||||
value |= ((dev->current_b ^ 7) << 5);
|
||||
break;
|
||||
case BUSM_PORT_SIGNATURE:
|
||||
value = dev->sig_val;
|
||||
break;
|
||||
case BUSM_PORT_CONTROL:
|
||||
value = dev->control_val;
|
||||
dev->control_val |= 0x0F;
|
||||
/* If the conditions are right, simulate the flakiness of the correct IRQ bit. */
|
||||
if (dev->flags & FLAG_TIMER_INT)
|
||||
dev->control_val = (dev->control_val & ~IRQ_MASK) | (random_generate() & IRQ_MASK);
|
||||
break;
|
||||
case BUSM_PORT_CONFIG:
|
||||
/* Read from config port returns control_val in the upper 4 bits when enabled,
|
||||
possibly solid interrupt readout in the lower 4 bits, 0xff when not (at power-up). */
|
||||
if (dev->flags & FLAG_ENABLED)
|
||||
return (dev->control_val | 0x0F) & ~IRQ_MASK;
|
||||
else
|
||||
return 0xff;
|
||||
break;
|
||||
}
|
||||
|
||||
bm_log("DEBUG: read from address 0x%04x, value = 0x%02x\n", port, value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
ms_read(uint16_t port, void *priv)
|
||||
{
|
||||
mouse_t *dev = (mouse_t *)priv;
|
||||
uint8_t value = 0xff;
|
||||
|
||||
switch (port & 0x03) {
|
||||
case INP_PORT_CONTROL:
|
||||
value = dev->control_val;
|
||||
break;
|
||||
case INP_PORT_DATA:
|
||||
switch (dev->command_val) {
|
||||
case INP_CTRL_READ_BUTTONS:
|
||||
value = dev->current_b;
|
||||
break;
|
||||
case INP_CTRL_READ_X:
|
||||
value = dev->current_x;
|
||||
dev->current_x = 0;
|
||||
break;
|
||||
case INP_CTRL_READ_Y:
|
||||
value = dev->current_y;
|
||||
dev->current_y = 0;
|
||||
break;
|
||||
case INP_CTRL_COMMAND:
|
||||
value = dev->control_val;
|
||||
break;
|
||||
default:
|
||||
bm_log("ERROR: Reading data port in unsupported mode 0x%02x\n", dev->control_val);
|
||||
}
|
||||
break;
|
||||
case INP_PORT_SIGNATURE:
|
||||
if (dev->toggle_counter)
|
||||
value = 0x12;
|
||||
else
|
||||
value = 0xDE;
|
||||
dev->toggle_counter ^= 1;
|
||||
break;
|
||||
case INP_PORT_CONFIG:
|
||||
bm_log("ERROR: Unsupported read from port 0x%04x\n", port);
|
||||
break;
|
||||
}
|
||||
|
||||
bm_log("DEBUG: read from address 0x%04x, value = 0x%02x\n", port, value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/* Handle a WRITE operation to one of our registers. */
|
||||
static void
|
||||
lt_write(uint16_t port, uint8_t val, void *priv)
|
||||
{
|
||||
mouse_t *dev = (mouse_t *)priv;
|
||||
uint8_t bit;
|
||||
|
||||
bm_log("DEBUG: write to address 0x%04x, value = 0x%02x\n", port, val);
|
||||
|
||||
switch (port & 0x03) {
|
||||
case BUSM_PORT_DATA:
|
||||
bm_log("ERROR: Unsupported write to port 0x%04x (value = 0x%02x)\n", port, val);
|
||||
break;
|
||||
case BUSM_PORT_SIGNATURE:
|
||||
dev->sig_val = val;
|
||||
break;
|
||||
case BUSM_PORT_CONTROL:
|
||||
dev->control_val = val | 0x0F;
|
||||
|
||||
if (!(val & DISABLE_IRQ))
|
||||
dev->flags |= FLAG_TIMER_INT;
|
||||
else
|
||||
dev->flags &= ~FLAG_TIMER_INT;
|
||||
|
||||
if (val & HOLD_COUNTER)
|
||||
dev->flags |= FLAG_HOLD;
|
||||
else
|
||||
dev->flags &= ~FLAG_HOLD;
|
||||
|
||||
if (dev->irq != -1)
|
||||
picintc(1 << dev->irq);
|
||||
|
||||
break;
|
||||
case BUSM_PORT_CONFIG:
|
||||
/*
|
||||
* 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)
|
||||
* This indicates the mode of operation of D7:
|
||||
* 1 = Mode set, 0 = Bit set/reset
|
||||
* 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
|
||||
*
|
||||
* 1001 1011 9B 1111 Default state
|
||||
* 1001 0001 91 1001 Driver-initialized state
|
||||
* The only difference is - port C upper and port B go from
|
||||
* input to output.
|
||||
*/
|
||||
if (val & DEVICE_ACTIVE) {
|
||||
/* Mode set/reset - enable this */
|
||||
dev->config_val = val;
|
||||
if (dev->timer_enabled)
|
||||
dev->flags |= (FLAG_ENABLED | FLAG_TIMER_INT);
|
||||
else
|
||||
dev->flags |= FLAG_ENABLED;
|
||||
dev->control_val = 0x0F & ~IRQ_MASK;
|
||||
} else {
|
||||
/* Single bit set/reset */
|
||||
bit = 1 << ((val >> 1) & 0x07); /* Bits 3-1 specify the target bit */
|
||||
if (val & 1)
|
||||
dev->control_val |= bit; /* Set */
|
||||
else
|
||||
dev->control_val &= ~bit; /* Reset */
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Handle a WRITE operation to one of our registers. */
|
||||
static void
|
||||
ms_write(uint16_t port, uint8_t val, void *priv)
|
||||
{
|
||||
mouse_t *dev = (mouse_t *)priv;
|
||||
|
||||
bm_log("DEBUG: write to address 0x%04x, value = 0x%02x\n", port, val);
|
||||
|
||||
switch (port & 0x03) {
|
||||
case INP_PORT_CONTROL:
|
||||
/* Bit 7 is reset. */
|
||||
if (val & INP_CTRL_RESET)
|
||||
dev->control_val = 0;
|
||||
|
||||
/* Bits 0-2 are the internal register index. */
|
||||
switch(val & 0x07) {
|
||||
case INP_CTRL_COMMAND:
|
||||
case INP_CTRL_READ_BUTTONS:
|
||||
case INP_CTRL_READ_X:
|
||||
case INP_CTRL_READ_Y:
|
||||
dev->command_val = val & 0x07;
|
||||
break;
|
||||
default:
|
||||
bm_log("ERROR: Unsupported command written to port 0x%04x (value = 0x%02x)\n", port, val);
|
||||
}
|
||||
break;
|
||||
case INP_PORT_DATA:
|
||||
if (dev->irq != -1)
|
||||
picintc(1 << dev->irq);
|
||||
switch(dev->command_val) {
|
||||
case INP_CTRL_COMMAND:
|
||||
if (val & INP_HOLD_COUNTER)
|
||||
dev->flags |= FLAG_HOLD;
|
||||
else
|
||||
dev->flags &= ~FLAG_HOLD;
|
||||
|
||||
if (val & INP_ENABLE_TIMER_IRQ)
|
||||
dev->flags |= FLAG_TIMER_INT;
|
||||
else
|
||||
dev->flags &= ~FLAG_TIMER_INT;
|
||||
|
||||
if (val & INP_ENABLE_DATA_IRQ)
|
||||
dev->flags |= FLAG_DATA_INT;
|
||||
else
|
||||
dev->flags &= ~FLAG_DATA_INT;
|
||||
|
||||
switch(val & INP_PERIOD_MASK) {
|
||||
case 0:
|
||||
dev->period = 0.0;
|
||||
timer_disable(&dev->timer);
|
||||
dev->timer_enabled = 0;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
dev->period = (1000000.0 / (double)periods[(val & INP_PERIOD_MASK) - 1]);
|
||||
dev->timer_enabled = (val & INP_ENABLE_TIMER_IRQ) ? 1 : 0;
|
||||
timer_disable(&dev->timer);
|
||||
if (dev->timer_enabled)
|
||||
timer_set_delay_u64(&dev->timer, (uint64_t) (dev->period * (double)TIMER_USEC));
|
||||
bm_log("DEBUG: Timer is now %sabled at period %i\n", (val & INP_ENABLE_TIMER_IRQ) ? "en" : "dis", (int32_t) dev->period);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
if ((val & INP_ENABLE_TIMER_IRQ) && (dev->irq != -1))
|
||||
picint(1 << dev->irq);
|
||||
dev->control_val &= INP_PERIOD_MASK;
|
||||
dev->control_val |= (val & ~INP_PERIOD_MASK);
|
||||
return;
|
||||
default:
|
||||
bm_log("ERROR: Unsupported period written to port 0x%04x (value = 0x%02x)\n", port, val);
|
||||
}
|
||||
|
||||
dev->control_val = val;
|
||||
|
||||
break;
|
||||
default:
|
||||
bm_log("ERROR: Unsupported write to port 0x%04x (value = 0x%02x)\n", port, val);
|
||||
}
|
||||
break;
|
||||
case INP_PORT_SIGNATURE:
|
||||
case INP_PORT_CONFIG:
|
||||
bm_log("ERROR: Unsupported write to port 0x%04x (value = 0x%02x)\n", port, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 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)
|
||||
{
|
||||
mouse_t *dev = (mouse_t *)priv;
|
||||
int xor;
|
||||
|
||||
if (!(dev->flags & FLAG_ENABLED))
|
||||
return(1); /* Mouse is disabled, do nothing. */
|
||||
|
||||
if (!x && !y && !((b ^ dev->mouse_buttons_last) & 0x07)) {
|
||||
dev->mouse_buttons_last = b;
|
||||
return(1); /* State has not changed, do nothing. */
|
||||
}
|
||||
|
||||
/* Converts button states from MRL to LMR. */
|
||||
dev->mouse_buttons = (uint8_t) (((b & 1) << 2) | ((b & 2) >> 1));
|
||||
if (dev->bn == 3)
|
||||
dev->mouse_buttons |= ((b & 4) >> 1);
|
||||
|
||||
if ((dev->flags & FLAG_INPORT) && !dev->timer_enabled) {
|
||||
/* This is an InPort mouse in data interrupt mode,
|
||||
so update bits 6-3 here. */
|
||||
|
||||
/* If the mouse has moved, set bit 6. */
|
||||
if (x || y)
|
||||
dev->mouse_buttons |= 0x40;
|
||||
|
||||
/* Set bits 3-5 according to button state changes. */
|
||||
xor = ((dev->current_b ^ dev->mouse_buttons) & 0x07) << 3;
|
||||
dev->mouse_buttons |= xor;
|
||||
}
|
||||
|
||||
dev->mouse_buttons_last = b;
|
||||
|
||||
/* Clamp x and y to between -128 and 127 (int8_t range). */
|
||||
if (x > 127) x = 127;
|
||||
if (x < -128) x = -128;
|
||||
|
||||
if (y > 127) y = 127;
|
||||
if (y < -128) y = -128;
|
||||
|
||||
if (dev->timer_enabled) {
|
||||
/* Update delayed coordinates. */
|
||||
dev->mouse_delayed_dx += x;
|
||||
dev->mouse_delayed_dy += y;
|
||||
} else {
|
||||
/* If the counters are not frozen, update them. */
|
||||
if (!(dev->flags & FLAG_HOLD)) {
|
||||
dev->current_x = (int8_t) x;
|
||||
dev->current_y = (int8_t) y;
|
||||
|
||||
dev->current_b = dev->mouse_buttons;
|
||||
}
|
||||
|
||||
/* Send interrupt. */
|
||||
if ((dev->flags & FLAG_DATA_INT) && (dev->irq != -1)) {
|
||||
picint(1 << dev->irq);
|
||||
bm_log("DEBUG: Data Interrupt Fired...\n");
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/* The timer calls us on every tick if the mouse is in timer mode
|
||||
(InPort mouse is so configured, MS/Logitech Bus mouse always). */
|
||||
static void
|
||||
bm_update_data(mouse_t *dev)
|
||||
{
|
||||
int delta_x, delta_y;
|
||||
int xor;
|
||||
|
||||
/* If the counters are not frozen, update them. */
|
||||
if (!(dev->flags & FLAG_HOLD)) {
|
||||
/* Update the deltas and the delays. */
|
||||
if (dev->mouse_delayed_dx > 127) {
|
||||
delta_x = 127;
|
||||
dev->mouse_delayed_dx -= 127;
|
||||
} else if (dev->mouse_delayed_dx < -128) {
|
||||
delta_x = -128;
|
||||
dev->mouse_delayed_dx += 128;
|
||||
} else {
|
||||
delta_x = dev->mouse_delayed_dx;
|
||||
dev->mouse_delayed_dx = 0;
|
||||
}
|
||||
|
||||
if (dev->mouse_delayed_dy > 127) {
|
||||
delta_y = 127;
|
||||
dev->mouse_delayed_dy -= 127;
|
||||
} else if (dev->mouse_delayed_dy < -128) {
|
||||
delta_y = -128;
|
||||
dev->mouse_delayed_dy += 128;
|
||||
} else {
|
||||
delta_y = dev->mouse_delayed_dy;
|
||||
dev->mouse_delayed_dy = 0;
|
||||
}
|
||||
|
||||
dev->current_x = (int8_t) delta_x;
|
||||
dev->current_y = (int8_t) delta_y;
|
||||
} else
|
||||
delta_x = delta_y = 0;
|
||||
|
||||
if (dev->flags & FLAG_INPORT) {
|
||||
/* This is an InPort mouse in timer mode, so update current_b always,
|
||||
and update bits 6-3 (mouse moved and button state changed) here. */
|
||||
xor = ((dev->current_b ^ dev->mouse_buttons) & 0x07) << 3;
|
||||
dev->current_b = (dev->mouse_buttons & 0x87) | xor;
|
||||
if (delta_x || delta_y)
|
||||
dev->current_b |= 0x40;
|
||||
} else if (!(dev->flags & FLAG_HOLD)) {
|
||||
/* This is a MS/Logitech Bus Mouse, so only update current_b if the
|
||||
counters are frozen. */
|
||||
dev->current_b = dev->mouse_buttons;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Called at the configured period (InPort mouse) or 45 times per second (MS/Logitech Bus mouse). */
|
||||
static void
|
||||
bm_timer(void *priv)
|
||||
{
|
||||
mouse_t *dev = (mouse_t *)priv;
|
||||
|
||||
bm_log("DEBUG: Timer Tick (flags=%08X)...\n", dev->flags);
|
||||
|
||||
/* The period is configured either via emulator settings (for MS/Logitech Bus mouse)
|
||||
or via software (for InPort mouse). */
|
||||
timer_advance_u64(&dev->timer, (uint64_t) (dev->period * (double)TIMER_USEC));
|
||||
|
||||
if ((dev->flags & FLAG_TIMER_INT) && (dev->irq != -1)) {
|
||||
picint(1 << dev->irq);
|
||||
bm_log("DEBUG: Timer Interrupt Fired...\n");
|
||||
}
|
||||
|
||||
bm_update_data(dev);
|
||||
}
|
||||
|
||||
|
||||
/* Release all resources held by the device. */
|
||||
static void
|
||||
bm_close(void *priv)
|
||||
{
|
||||
mouse_t *dev = (mouse_t *)priv;
|
||||
|
||||
if (dev)
|
||||
free(dev);
|
||||
}
|
||||
|
||||
|
||||
/* Set the mouse's IRQ. */
|
||||
void
|
||||
mouse_bus_set_irq(void *priv, int irq)
|
||||
{
|
||||
mouse_t *dev = (mouse_t *)priv;
|
||||
|
||||
dev->irq = irq;
|
||||
}
|
||||
|
||||
|
||||
/* Initialize the device for use by the user. */
|
||||
static void *
|
||||
bm_init(const device_t *info)
|
||||
{
|
||||
mouse_t *dev;
|
||||
int hz;
|
||||
|
||||
dev = (mouse_t *)malloc(sizeof(mouse_t));
|
||||
memset(dev, 0x00, sizeof(mouse_t));
|
||||
|
||||
if ((info->local & ~MOUSE_TYPE_ONBOARD) == MOUSE_TYPE_INPORT)
|
||||
dev->flags = FLAG_INPORT;
|
||||
else
|
||||
dev->flags = 0;
|
||||
|
||||
if (info->local & MOUSE_TYPE_ONBOARD) {
|
||||
dev->base = 0x023c;
|
||||
dev->irq = -1;
|
||||
dev->bn = 2;
|
||||
} else {
|
||||
dev->base = device_get_config_hex16("base");
|
||||
dev->irq = device_get_config_int("irq");
|
||||
dev->bn = device_get_config_int("buttons");
|
||||
}
|
||||
mouse_set_buttons(dev->bn);
|
||||
|
||||
dev->mouse_delayed_dx = 0;
|
||||
dev->mouse_delayed_dy = 0;
|
||||
dev->mouse_buttons = 0;
|
||||
dev->mouse_buttons_last = 0;
|
||||
dev->sig_val = 0; /* the signature port value */
|
||||
dev->current_x =
|
||||
dev->current_y = 0;
|
||||
dev->current_b = 0;
|
||||
dev->command_val = 0; /* command byte */
|
||||
dev->toggle_counter = 0; /* signature byte / IRQ bit toggle */
|
||||
dev->period = 0.0;
|
||||
|
||||
timer_add(&dev->timer, bm_timer, dev, 0);
|
||||
|
||||
if (dev->flags & FLAG_INPORT) {
|
||||
dev->control_val = 0; /* the control port value */
|
||||
dev->flags |= FLAG_ENABLED;
|
||||
|
||||
io_sethandler(dev->base, 4,
|
||||
ms_read, NULL, NULL, ms_write, NULL, NULL, dev);
|
||||
|
||||
dev->timer_enabled = 0;
|
||||
} else {
|
||||
dev->control_val = 0x0f; /* the control port value */
|
||||
dev->config_val = 0x9b; /* the config port value - 0x9b is the
|
||||
default state of the 8255: all ports
|
||||
are set to input */
|
||||
|
||||
hz = device_get_config_int("hz");
|
||||
if (hz > 0)
|
||||
dev->period = (1000000.0 / (double)hz);
|
||||
|
||||
io_sethandler(dev->base, 4,
|
||||
lt_read, NULL, NULL, lt_write, NULL, NULL, dev);
|
||||
|
||||
if (hz > 0) {
|
||||
timer_set_delay_u64(&dev->timer, (uint64_t) (dev->period * (double)TIMER_USEC));
|
||||
dev->timer_enabled = 1;
|
||||
} else {
|
||||
dev->flags |= FLAG_DATA_INT;
|
||||
dev->timer_enabled = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->flags & FLAG_INPORT)
|
||||
bm_log("MS Inport BusMouse initialized\n");
|
||||
else
|
||||
bm_log("Standard MS/Logitech BusMouse initialized\n");
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
|
||||
static const device_config_t lt_config[] = {
|
||||
{
|
||||
"base", "Address", CONFIG_HEX16, "", 0x23c,
|
||||
{
|
||||
{
|
||||
"0x230", 0x230
|
||||
},
|
||||
{
|
||||
"0x234", 0x234
|
||||
},
|
||||
{
|
||||
"0x238", 0x238
|
||||
},
|
||||
{
|
||||
"0x23C", 0x23c
|
||||
},
|
||||
{
|
||||
""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"irq", "IRQ", CONFIG_SELECTION, "", 5, {
|
||||
{
|
||||
"IRQ 2", 2
|
||||
},
|
||||
{
|
||||
"IRQ 3", 3
|
||||
},
|
||||
{
|
||||
"IRQ 4", 4
|
||||
},
|
||||
{
|
||||
"IRQ 5", 5
|
||||
},
|
||||
{
|
||||
""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"hz", "Hz", CONFIG_SELECTION, "", 45, {
|
||||
{
|
||||
"Non-timed (original)", 0
|
||||
},
|
||||
{
|
||||
"30 Hz (JMP2 = 1)", 30
|
||||
},
|
||||
{
|
||||
"45 Hz (JMP2 not populated)", 45
|
||||
},
|
||||
{
|
||||
"60 Hz (JMP 2 = 2)", 60
|
||||
},
|
||||
{
|
||||
""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"buttons", "Buttons", CONFIG_SELECTION, "", 2, {
|
||||
{
|
||||
"Two", 2
|
||||
},
|
||||
{
|
||||
"Three", 3
|
||||
},
|
||||
{
|
||||
""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"", "", -1
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static const device_config_t ms_config[] = {
|
||||
{
|
||||
"base", "Address", CONFIG_HEX16, "", 0x23c,
|
||||
{
|
||||
{
|
||||
"0x230", 0x230
|
||||
},
|
||||
{
|
||||
"0x234", 0x234
|
||||
},
|
||||
{
|
||||
"0x238", 0x238
|
||||
},
|
||||
{
|
||||
"0x23C", 0x23c
|
||||
},
|
||||
{
|
||||
""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"irq", "IRQ", CONFIG_SELECTION, "", 5, {
|
||||
{
|
||||
"IRQ 2", 2
|
||||
},
|
||||
{
|
||||
"IRQ 3", 3
|
||||
},
|
||||
{
|
||||
"IRQ 4", 4
|
||||
},
|
||||
{
|
||||
"IRQ 5", 5
|
||||
},
|
||||
{
|
||||
""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"buttons", "Buttons", CONFIG_SELECTION, "", 2, {
|
||||
{
|
||||
"Two", 2
|
||||
},
|
||||
{
|
||||
"Three", 3
|
||||
},
|
||||
{
|
||||
""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"", "", -1
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const device_t mouse_logibus_device = {
|
||||
"Logitech/Microsoft Bus Mouse",
|
||||
DEVICE_ISA,
|
||||
MOUSE_TYPE_LOGIBUS,
|
||||
bm_init, bm_close, NULL,
|
||||
bm_poll, NULL, NULL,
|
||||
lt_config
|
||||
};
|
||||
|
||||
const device_t mouse_logibus_onboard_device = {
|
||||
"Logitech Bus Mouse (On-Board)",
|
||||
DEVICE_ISA,
|
||||
MOUSE_TYPE_LOGIBUS | MOUSE_TYPE_ONBOARD,
|
||||
bm_init, bm_close, NULL,
|
||||
bm_poll, NULL, NULL
|
||||
};
|
||||
|
||||
const device_t mouse_msinport_device = {
|
||||
"Microsoft Bus Mouse (InPort)",
|
||||
DEVICE_ISA,
|
||||
MOUSE_TYPE_INPORT,
|
||||
bm_init, bm_close, NULL,
|
||||
bm_poll, NULL, NULL,
|
||||
ms_config
|
||||
};
|
||||
353
src/device/mouse_ps2.c
Normal file
353
src/device/mouse_ps2.c
Normal file
@@ -0,0 +1,353 @@
|
||||
/*
|
||||
* 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 PS/2 series Mouse devices.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <wchar.h>
|
||||
#define HAVE_STDARG_H
|
||||
#include <86box/86box.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/keyboard.h>
|
||||
#include <86box/mouse.h>
|
||||
|
||||
|
||||
enum {
|
||||
MODE_STREAM,
|
||||
MODE_REMOTE,
|
||||
MODE_ECHO
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
const char *name; /* name of this device */
|
||||
int8_t type; /* type of this device */
|
||||
|
||||
int mode;
|
||||
|
||||
uint8_t flags;
|
||||
uint8_t resolution;
|
||||
uint8_t sample_rate;
|
||||
|
||||
uint8_t command;
|
||||
|
||||
int x, y, z, b;
|
||||
|
||||
uint8_t last_data[6];
|
||||
} mouse_t;
|
||||
#define FLAG_INTELLI 0x80 /* device is IntelliMouse */
|
||||
#define FLAG_INTMODE 0x40 /* using Intellimouse mode */
|
||||
#define FLAG_SCALED 0x20 /* enable delta scaling */
|
||||
#define FLAG_ENABLED 0x10 /* dev is enabled for use */
|
||||
#define FLAG_CTRLDAT 0x08 /* ctrl or data mode */
|
||||
|
||||
|
||||
int mouse_scan = 0;
|
||||
|
||||
|
||||
#ifdef ENABLE_MOUSE_PS2_LOG
|
||||
int mouse_ps2_do_log = ENABLE_MOUSE_PS2_LOG;
|
||||
|
||||
|
||||
static void
|
||||
mouse_ps2_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (mouse_ps2_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define mouse_ps2_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
|
||||
void
|
||||
mouse_clear_data(void *priv)
|
||||
{
|
||||
mouse_t *dev = (mouse_t *)priv;
|
||||
|
||||
dev->flags &= ~FLAG_CTRLDAT;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ps2_write(uint8_t val, void *priv)
|
||||
{
|
||||
mouse_t *dev = (mouse_t *)priv;
|
||||
uint8_t temp;
|
||||
|
||||
if (dev->flags & FLAG_CTRLDAT) {
|
||||
dev->flags &= ~FLAG_CTRLDAT;
|
||||
|
||||
switch (dev->command) {
|
||||
case 0xe8: /* set mouse resolution */
|
||||
dev->resolution = val;
|
||||
keyboard_at_adddata_mouse(0xfa);
|
||||
break;
|
||||
|
||||
case 0xf3: /* set sample rate */
|
||||
dev->sample_rate = val;
|
||||
keyboard_at_adddata_mouse(0xfa); /* Command response */
|
||||
break;
|
||||
|
||||
default:
|
||||
keyboard_at_adddata_mouse(0xfc);
|
||||
}
|
||||
} else {
|
||||
dev->command = val;
|
||||
|
||||
switch (dev->command) {
|
||||
case 0xe6: /* set scaling to 1:1 */
|
||||
dev->flags &= ~FLAG_SCALED;
|
||||
keyboard_at_adddata_mouse(0xfa);
|
||||
break;
|
||||
|
||||
case 0xe7: /* set scaling to 2:1 */
|
||||
dev->flags |= FLAG_SCALED;
|
||||
keyboard_at_adddata_mouse(0xfa);
|
||||
break;
|
||||
|
||||
case 0xe8: /* set mouse resolution */
|
||||
dev->flags |= FLAG_CTRLDAT;
|
||||
keyboard_at_adddata_mouse(0xfa);
|
||||
break;
|
||||
|
||||
case 0xe9: /* status request */
|
||||
keyboard_at_adddata_mouse(0xfa);
|
||||
temp = (dev->flags & 0x30);
|
||||
if (mouse_buttons & 0x01)
|
||||
temp |= 0x01;
|
||||
if (mouse_buttons & 0x02)
|
||||
temp |= 0x02;
|
||||
if (mouse_buttons & 0x04)
|
||||
temp |= 0x03;
|
||||
keyboard_at_adddata_mouse(temp);
|
||||
keyboard_at_adddata_mouse(dev->resolution);
|
||||
keyboard_at_adddata_mouse(dev->sample_rate);
|
||||
break;
|
||||
|
||||
case 0xeb: /* Get mouse data */
|
||||
keyboard_at_adddata_mouse(0xfa);
|
||||
|
||||
temp = 0;
|
||||
if (dev->x < 0)
|
||||
temp |= 0x10;
|
||||
if (dev->y < 0)
|
||||
temp |= 0x20;
|
||||
if (mouse_buttons & 1)
|
||||
temp |= 1;
|
||||
if (mouse_buttons & 2)
|
||||
temp |= 2;
|
||||
if ((mouse_buttons & 4) && (dev->flags & FLAG_INTELLI))
|
||||
temp |= 4;
|
||||
keyboard_at_adddata_mouse(temp);
|
||||
keyboard_at_adddata_mouse(dev->x & 0xff);
|
||||
keyboard_at_adddata_mouse(dev->y & 0xff);
|
||||
if (dev->flags & FLAG_INTMODE)
|
||||
keyboard_at_adddata_mouse(dev->z);
|
||||
break;
|
||||
|
||||
case 0xf2: /* read ID */
|
||||
keyboard_at_adddata_mouse(0xfa);
|
||||
if (dev->flags & FLAG_INTMODE)
|
||||
keyboard_at_adddata_mouse(0x03);
|
||||
else
|
||||
keyboard_at_adddata_mouse(0x00);
|
||||
break;
|
||||
|
||||
case 0xf3: /* set command mode */
|
||||
dev->flags |= FLAG_CTRLDAT;
|
||||
keyboard_at_adddata_mouse(0xfa); /* ACK for command byte */
|
||||
break;
|
||||
|
||||
case 0xf4: /* enable */
|
||||
dev->flags |= FLAG_ENABLED;
|
||||
keyboard_at_adddata_mouse(0xfa);
|
||||
break;
|
||||
|
||||
case 0xf5: /* disable */
|
||||
dev->flags &= ~FLAG_ENABLED;
|
||||
keyboard_at_adddata_mouse(0xfa);
|
||||
break;
|
||||
|
||||
case 0xff: /* reset */
|
||||
dev->mode = MODE_STREAM;
|
||||
dev->flags &= 0x88;
|
||||
mouse_queue_start = mouse_queue_end = 0;
|
||||
keyboard_at_adddata_mouse(0xfa);
|
||||
keyboard_at_adddata_mouse(0xaa);
|
||||
keyboard_at_adddata_mouse(0x00);
|
||||
break;
|
||||
|
||||
default:
|
||||
keyboard_at_adddata_mouse(0xfe);
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->flags & FLAG_INTELLI) {
|
||||
for (temp = 0; temp < 5; temp++)
|
||||
dev->last_data[temp] = dev->last_data[temp + 1];
|
||||
|
||||
dev->last_data[5] = val;
|
||||
|
||||
if (dev->last_data[0] == 0xf3 && dev->last_data[1] == 0xc8 &&
|
||||
dev->last_data[2] == 0xf3 && dev->last_data[3] == 0x64 &&
|
||||
dev->last_data[4] == 0xf3 && dev->last_data[5] == 0x50)
|
||||
dev->flags |= FLAG_INTMODE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ps2_poll(int x, int y, int z, int b, void *priv)
|
||||
{
|
||||
mouse_t *dev = (mouse_t *)priv;
|
||||
uint8_t buff[3] = { 0x08, 0x00, 0x00 };
|
||||
|
||||
if (!x && !y && !z && (b == dev->b))
|
||||
return(0xff);
|
||||
|
||||
#if 0
|
||||
if (!(dev->flags & FLAG_ENABLED))
|
||||
return(0xff);
|
||||
#endif
|
||||
|
||||
if (!mouse_scan)
|
||||
return(0xff);
|
||||
|
||||
dev->x += x;
|
||||
dev->y -= y;
|
||||
dev->z -= z;
|
||||
if ((dev->mode == MODE_STREAM) && (dev->flags & FLAG_ENABLED) &&
|
||||
(((mouse_queue_end - mouse_queue_start) & 0x0f) < 13)) {
|
||||
dev->b = b;
|
||||
|
||||
if (dev->x > 255) dev->x = 255;
|
||||
if (dev->x < -256) dev->x = -256;
|
||||
if (dev->y > 255) dev->y = 255;
|
||||
if (dev->y < -256) dev->y = -256;
|
||||
if (dev->z < -8) dev->z = -8;
|
||||
if (dev->z > 7) dev->z = 7;
|
||||
|
||||
if (dev->x < 0)
|
||||
buff[0] |= 0x10;
|
||||
if (dev->y < 0)
|
||||
buff[0] |= 0x20;
|
||||
if (mouse_buttons & 0x01)
|
||||
buff[0] |= 0x01;
|
||||
if (mouse_buttons & 0x02)
|
||||
buff[0] |= 0x02;
|
||||
if (dev->flags & FLAG_INTELLI) {
|
||||
if (mouse_buttons & 0x04)
|
||||
buff[0] |= 0x04;
|
||||
}
|
||||
buff[1] = (dev->x & 0xff);
|
||||
buff[2] = (dev->y & 0xff);
|
||||
|
||||
keyboard_at_adddata_mouse(buff[0]);
|
||||
keyboard_at_adddata_mouse(buff[1]);
|
||||
keyboard_at_adddata_mouse(buff[2]);
|
||||
if (dev->flags & FLAG_INTMODE)
|
||||
keyboard_at_adddata_mouse(dev->z);
|
||||
|
||||
dev->x = dev->y = dev->z = 0;
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialize the device for use by the user.
|
||||
*
|
||||
* We also get called from the various machines.
|
||||
*/
|
||||
void *
|
||||
mouse_ps2_init(const device_t *info)
|
||||
{
|
||||
mouse_t *dev;
|
||||
int i;
|
||||
|
||||
dev = (mouse_t *)malloc(sizeof(mouse_t));
|
||||
memset(dev, 0x00, sizeof(mouse_t));
|
||||
dev->name = info->name;
|
||||
dev->type = info->local;
|
||||
|
||||
dev->mode = MODE_STREAM;
|
||||
i = device_get_config_int("buttons");
|
||||
if (i > 2)
|
||||
dev->flags |= FLAG_INTELLI;
|
||||
|
||||
/* Hook into the general AT Keyboard driver. */
|
||||
keyboard_at_set_mouse(ps2_write, dev);
|
||||
|
||||
mouse_ps2_log("%s: buttons=%d\n", dev->name, (dev->flags & FLAG_INTELLI) ? 3 : 2);
|
||||
|
||||
/* Tell them how many buttons we have. */
|
||||
mouse_set_buttons((dev->flags & FLAG_INTELLI) ? 3 : 2);
|
||||
|
||||
/* Return our private data to the I/O layer. */
|
||||
return(dev);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ps2_close(void *priv)
|
||||
{
|
||||
mouse_t *dev = (mouse_t *)priv;
|
||||
|
||||
/* Unhook from the general AT Keyboard driver. */
|
||||
keyboard_at_set_mouse(NULL, NULL);
|
||||
|
||||
free(dev);
|
||||
}
|
||||
|
||||
|
||||
static const device_config_t ps2_config[] = {
|
||||
{
|
||||
"buttons", "Buttons", CONFIG_SELECTION, "", 2, {
|
||||
{
|
||||
"Two", 2
|
||||
},
|
||||
{
|
||||
"Three", 3
|
||||
},
|
||||
{
|
||||
"Wheel", 4
|
||||
},
|
||||
{
|
||||
""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"", "", -1
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const device_t mouse_ps2_device = {
|
||||
"Standard PS/2 Mouse",
|
||||
DEVICE_PS2,
|
||||
MOUSE_TYPE_PS2,
|
||||
mouse_ps2_init, ps2_close, NULL,
|
||||
ps2_poll, NULL, NULL,
|
||||
ps2_config
|
||||
};
|
||||
927
src/device/mouse_serial.c
Normal file
927
src/device/mouse_serial.c
Normal file
@@ -0,0 +1,927 @@
|
||||
/*
|
||||
* 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 Serial Mouse devices.
|
||||
*
|
||||
* TODO: Add the Genius Serial Mouse.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Author: Fred N. van Kempen, <decwiz@yahoo.com>
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <wchar.h>
|
||||
#define HAVE_STDARG_H
|
||||
#include <86box/86box.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/timer.h>
|
||||
#include <86box/serial.h>
|
||||
#include <86box/mouse.h>
|
||||
|
||||
|
||||
#define SERMOUSE_PORT 0 /* attach to Serial0 */
|
||||
|
||||
enum {
|
||||
PHASE_IDLE,
|
||||
PHASE_ID,
|
||||
PHASE_DATA,
|
||||
PHASE_STATUS,
|
||||
PHASE_DIAGNOSTIC,
|
||||
PHASE_FORMAT_AND_REVISION,
|
||||
PHASE_COPYRIGHT_STRING,
|
||||
PHASE_BUTTONS
|
||||
};
|
||||
|
||||
enum {
|
||||
REPORT_PHASE_PREPARE,
|
||||
REPORT_PHASE_TRANSMIT
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
const char *name; /* name of this device */
|
||||
int8_t type, /* type of this device */
|
||||
port;
|
||||
uint8_t flags, but, /* device flags */
|
||||
want_data,
|
||||
status, format,
|
||||
prompt, on_change,
|
||||
id_len, id[255],
|
||||
data_len, data[5];
|
||||
int abs_x, abs_y,
|
||||
rel_x, rel_y,
|
||||
rel_z,
|
||||
oldb, lastb;
|
||||
|
||||
int command_pos, command_phase,
|
||||
report_pos, report_phase,
|
||||
command_enabled, report_enabled;
|
||||
double transmit_period, report_period;
|
||||
pc_timer_t command_timer, report_timer;
|
||||
|
||||
serial_t *serial;
|
||||
} mouse_t;
|
||||
#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 */
|
||||
|
||||
|
||||
#ifdef ENABLE_MOUSE_SERIAL_LOG
|
||||
int mouse_serial_do_log = ENABLE_MOUSE_SERIAL_LOG;
|
||||
|
||||
|
||||
static void
|
||||
mouse_serial_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (mouse_serial_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define mouse_serial_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
|
||||
static void
|
||||
sermouse_timer_on(mouse_t *dev, double period, int report)
|
||||
{
|
||||
pc_timer_t *timer;
|
||||
int *enabled;
|
||||
|
||||
if (report) {
|
||||
timer = &dev->report_timer;
|
||||
enabled = &dev->report_enabled;
|
||||
} else {
|
||||
timer = &dev->command_timer;
|
||||
enabled = &dev->command_enabled;
|
||||
}
|
||||
|
||||
timer_on_auto(timer, period);
|
||||
|
||||
*enabled = 1;
|
||||
}
|
||||
|
||||
|
||||
static double
|
||||
sermouse_transmit_period(mouse_t *dev, int bps, int rps)
|
||||
{
|
||||
double dbps = (double) bps;
|
||||
double temp = 0.0;
|
||||
int word_len;
|
||||
|
||||
switch (dev->format) {
|
||||
case 0:
|
||||
case 1: /* Mouse Systems and Three Byte Packed formats: 8 data, no parity, 2 stop, 1 start */
|
||||
word_len = 11;
|
||||
break;
|
||||
case 2: /* Hexadecimal format - 8 data, no parity, 1 stop, 1 start - number of stop bits is a guess because
|
||||
it is not documented anywhere. */
|
||||
word_len = 10;
|
||||
break;
|
||||
case 3:
|
||||
case 6: /* Bit Pad One formats: 7 data, even parity, 2 stop, 1 start */
|
||||
word_len = 11;
|
||||
break;
|
||||
case 5: /* MM Series format: 8 data, odd parity, 1 stop, 1 start */
|
||||
word_len = 11;
|
||||
break;
|
||||
default:
|
||||
case 7: /* Microsoft-compatible format: 7 data, no parity, 1 stop, 1 start */
|
||||
word_len = 9;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rps == -1)
|
||||
temp = (double) word_len;
|
||||
else {
|
||||
temp = (double) rps;
|
||||
temp = (9600.0 - (temp * 33.0));
|
||||
temp /= rps;
|
||||
}
|
||||
temp = (1000000.0 / dbps) * temp;
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
|
||||
/* Callback from serial driver: RTS was toggled. */
|
||||
static void
|
||||
sermouse_callback(struct serial_s *serial, void *priv)
|
||||
{
|
||||
mouse_t *dev = (mouse_t *)priv;
|
||||
|
||||
/* Start a timer to wake us up in a little while. */
|
||||
dev->command_pos = 0;
|
||||
dev->command_phase = PHASE_ID;
|
||||
if (dev->id[0] != 'H')
|
||||
dev->format = 7;
|
||||
dev->transmit_period = sermouse_transmit_period(dev, 1200, -1);
|
||||
timer_stop(&dev->command_timer);
|
||||
sub_cycles(ISA_CYCLES(8));
|
||||
#ifdef USE_NEW_DYNAREC
|
||||
sermouse_timer_on(dev, 5000.0, 0);
|
||||
#else
|
||||
sermouse_timer_on(dev, dev->transmit_period, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
sermouse_data_msystems(mouse_t *dev, int x, int y, int b)
|
||||
{
|
||||
dev->data[0] = 0x80;
|
||||
dev->data[0] |= (b & 0x01) ? 0x00 : 0x04; /* left button */
|
||||
dev->data[0] |= (b & 0x02) ? 0x00 : 0x01; /* middle button */
|
||||
dev->data[0] |= (b & 0x04) ? 0x00 : 0x02; /* right button */
|
||||
dev->data[1] = x;
|
||||
dev->data[2] = -y;
|
||||
dev->data[3] = x; /* same as byte 1 */
|
||||
dev->data[4] = -y; /* same as byte 2 */
|
||||
|
||||
return 5;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
sermouse_data_3bp(mouse_t *dev, int x, int y, int b)
|
||||
{
|
||||
dev->data[0] |= (b & 0x01) ? 0x00 : 0x04; /* left button */
|
||||
dev->data[0] |= (b & 0x04) ? 0x00 : 0x02; /* middle button */
|
||||
dev->data[0] |= (b & 0x02) ? 0x00 : 0x01; /* right button */
|
||||
dev->data[1] = x;
|
||||
dev->data[2] = -y;
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
sermouse_data_mmseries(mouse_t *dev, int x, int y, int b)
|
||||
{
|
||||
if (x < -127)
|
||||
x = -127;
|
||||
if (y < -127)
|
||||
y = -127;
|
||||
|
||||
dev->data[0] = 0x80;
|
||||
if (x >= 0)
|
||||
dev->data[0] |= 0x10;
|
||||
if (y < 0)
|
||||
dev->data[0] |= 0x08;
|
||||
dev->data[0] |= (b & 0x01) ? 0x04 : 0x00; /* left button */
|
||||
dev->data[0] |= (b & 0x04) ? 0x02 : 0x00; /* middle button */
|
||||
dev->data[0] |= (b & 0x02) ? 0x01 : 0x00; /* right button */
|
||||
dev->data[1] = abs(x);
|
||||
dev->data[2] = abs(y);
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
sermouse_data_bp1(mouse_t *dev, int x, int y, int b)
|
||||
{
|
||||
dev->data[0] = 0x80;
|
||||
dev->data[0] |= (b & 0x01) ? 0x10 : 0x00; /* left button */
|
||||
dev->data[0] |= (b & 0x04) ? 0x08 : 0x00; /* middle button */
|
||||
dev->data[0] |= (b & 0x02) ? 0x04 : 0x00; /* right button */
|
||||
dev->data[1] = (x & 0x3f);
|
||||
dev->data[2] = (x >> 6);
|
||||
dev->data[3] = (y & 0x3f);
|
||||
dev->data[4] = (y >> 6);
|
||||
|
||||
return 5;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
sermouse_data_ms(mouse_t *dev, int x, int y, int z, int b)
|
||||
{
|
||||
uint8_t len;
|
||||
|
||||
dev->data[0] = 0x40;
|
||||
dev->data[0] |= (((y >> 6) & 0x03) << 2);
|
||||
dev->data[0] |= ((x >> 6) & 0x03);
|
||||
if (b & 0x01)
|
||||
dev->data[0] |= 0x20;
|
||||
if (b & 0x02)
|
||||
dev->data[0] |= 0x10;
|
||||
dev->data[1] = x & 0x3F;
|
||||
dev->data[2] = y & 0x3F;
|
||||
if (dev->but == 3) {
|
||||
len = 3;
|
||||
if (dev->type == MOUSE_TYPE_LT3BUTTON) {
|
||||
if (b & 0x04) {
|
||||
dev->data[3] = 0x20;
|
||||
len++;
|
||||
}
|
||||
} else {
|
||||
if ((b ^ dev->oldb) & 0x04) {
|
||||
/* Microsoft 3-button mice send a fourth byte of 0x00 when the middle button
|
||||
has changed. */
|
||||
dev->data[3] = 0x00;
|
||||
len++;
|
||||
}
|
||||
}
|
||||
} else if (dev->but == 4) {
|
||||
len = 4;
|
||||
dev->data[3] = z & 0x0F;
|
||||
if (b & 0x04)
|
||||
dev->data[3] |= 0x10;
|
||||
} else
|
||||
len = 3;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
sermouse_data_hex(mouse_t *dev, int x, int y, int b)
|
||||
{
|
||||
char ret[6] = { 0, 0, 0, 0, 0, 0 };
|
||||
uint8_t i, but = 0x00;
|
||||
|
||||
but |= (b & 0x01) ? 0x04 : 0x00; /* left button */
|
||||
but |= (b & 0x04) ? 0x02 : 0x00; /* middle button */
|
||||
but |= (b & 0x02) ? 0x01 : 0x00; /* right button */
|
||||
|
||||
sprintf(ret, "%02X%02X%01X", (int8_t) y, (int8_t) x, but & 0x0f);
|
||||
|
||||
for (i = 0; i < 5; i++)
|
||||
dev->data[i] = ret[4 - i];
|
||||
|
||||
return 5;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
sermouse_report(int x, int y, int z, int b, mouse_t *dev)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
memset(dev->data, 0, 5);
|
||||
|
||||
/* If the mouse is 2-button, ignore the middle button. */
|
||||
if (dev->but == 2)
|
||||
b &= ~0x04;
|
||||
|
||||
switch (dev->format) {
|
||||
case 0:
|
||||
len = sermouse_data_msystems(dev, x, y, b);
|
||||
break;
|
||||
case 1:
|
||||
len = sermouse_data_3bp(dev, x, y, b);
|
||||
break;
|
||||
case 2:
|
||||
len = sermouse_data_hex(dev, x, y, b);
|
||||
break;
|
||||
case 3: /* Relative */
|
||||
len = sermouse_data_bp1(dev, x, y, b);
|
||||
break;
|
||||
case 5:
|
||||
len = sermouse_data_mmseries(dev, x, y, b);
|
||||
break;
|
||||
case 6: /* Absolute */
|
||||
len = sermouse_data_bp1(dev, dev->abs_x, dev->abs_y, b);
|
||||
break;
|
||||
case 7:
|
||||
len = sermouse_data_ms(dev, x, y, z, b);
|
||||
break;
|
||||
}
|
||||
|
||||
dev->data_len = len;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
sermouse_command_phase_idle(mouse_t *dev)
|
||||
{
|
||||
dev->command_pos = 0;
|
||||
dev->command_phase = PHASE_IDLE;
|
||||
dev->command_enabled = 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
sermouse_command_pos_check(mouse_t *dev, int len)
|
||||
{
|
||||
if (++dev->command_pos == len)
|
||||
sermouse_command_phase_idle(dev);
|
||||
else
|
||||
timer_on_auto(&dev->command_timer, dev->transmit_period);
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
sermouse_last_button_status(mouse_t *dev)
|
||||
{
|
||||
uint8_t ret = 0x00;
|
||||
|
||||
if (dev->oldb & 0x01)
|
||||
ret |= 0x04;
|
||||
if (dev->oldb & 0x02)
|
||||
ret |= 0x02;
|
||||
if (dev->oldb & 0x04)
|
||||
ret |= 0x01;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
sermouse_update_delta(mouse_t *dev, int *local, int *global)
|
||||
{
|
||||
int min, max;
|
||||
|
||||
if (dev->format == 3) {
|
||||
min = -2048;
|
||||
max = 2047;
|
||||
} else {
|
||||
min = -128;
|
||||
max = 127;
|
||||
}
|
||||
|
||||
if (*global > max) {
|
||||
*local = max;
|
||||
*global -= max;
|
||||
} else if (*global < min) {
|
||||
*local = min;
|
||||
*global += -min;
|
||||
} else {
|
||||
*local = *global;
|
||||
*global = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static uint8_t
|
||||
sermouse_update_data(mouse_t *dev)
|
||||
{
|
||||
uint8_t ret = 0;
|
||||
int delta_x, delta_y, delta_z;
|
||||
|
||||
/* Update the deltas and the delays. */
|
||||
sermouse_update_delta(dev, &delta_x, &dev->rel_x);
|
||||
sermouse_update_delta(dev, &delta_y, &dev->rel_y);
|
||||
sermouse_update_delta(dev, &delta_z, &dev->rel_z);
|
||||
|
||||
sermouse_report(delta_x, delta_y, delta_z, dev->oldb, dev);
|
||||
|
||||
mouse_serial_log("delta_x = %i, delta_y = %i, delta_z = %i, dev->oldb = %02X\n",
|
||||
delta_x, delta_y, delta_z, dev->oldb);
|
||||
|
||||
if (delta_x || delta_y || delta_z || (dev->oldb != dev->lastb) || !dev->on_change)
|
||||
ret = 1;
|
||||
|
||||
dev->lastb = dev->oldb;
|
||||
|
||||
mouse_serial_log("sermouse_update_data(): ret = %i\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static double
|
||||
sermouse_report_period(mouse_t *dev)
|
||||
{
|
||||
if (dev->report_period == 0)
|
||||
return dev->transmit_period;
|
||||
else
|
||||
return dev->report_period;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
sermouse_report_prepare(mouse_t *dev)
|
||||
{
|
||||
if (sermouse_update_data(dev)) {
|
||||
/* Start sending data. */
|
||||
dev->report_phase = REPORT_PHASE_TRANSMIT;
|
||||
dev->report_pos = 0;
|
||||
sermouse_timer_on(dev, dev->transmit_period, 1);
|
||||
} else {
|
||||
dev->report_phase = REPORT_PHASE_PREPARE;
|
||||
sermouse_timer_on(dev, sermouse_report_period(dev), 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
sermouse_report_timer(void *priv)
|
||||
{
|
||||
mouse_t *dev = (mouse_t *)priv;
|
||||
|
||||
if (dev->report_phase == REPORT_PHASE_PREPARE)
|
||||
sermouse_report_prepare(dev);
|
||||
else {
|
||||
/* If using the Mouse Systems format, update data because
|
||||
the last two bytes are the X and Y delta since bytes 1
|
||||
and 2 were transmitted. */
|
||||
if (!dev->format && (dev->report_pos == 3))
|
||||
sermouse_update_data(dev);
|
||||
serial_write_fifo(dev->serial, dev->data[dev->report_pos]);
|
||||
if (++dev->report_pos == dev->data_len) {
|
||||
if (!dev->report_enabled)
|
||||
sermouse_report_prepare(dev);
|
||||
else {
|
||||
sermouse_timer_on(dev, sermouse_report_period(dev), 1);
|
||||
dev->report_phase = REPORT_PHASE_PREPARE;
|
||||
}
|
||||
} else
|
||||
sermouse_timer_on(dev, dev->transmit_period, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Callback timer expired, now send our "mouse ID" to the serial port. */
|
||||
static void
|
||||
sermouse_command_timer(void *priv)
|
||||
{
|
||||
mouse_t *dev = (mouse_t *)priv;
|
||||
|
||||
switch (dev->command_phase) {
|
||||
case PHASE_ID:
|
||||
serial_write_fifo(dev->serial, dev->id[dev->command_pos]);
|
||||
sermouse_command_pos_check(dev, dev->id_len);
|
||||
if ((dev->command_phase == PHASE_IDLE) && (dev->type != MOUSE_TYPE_MSYSTEMS)) {
|
||||
/* This resets back to Microsoft-compatible mode. */
|
||||
dev->report_phase = REPORT_PHASE_PREPARE;
|
||||
sermouse_report_timer((void *) dev);
|
||||
}
|
||||
break;
|
||||
case PHASE_DATA:
|
||||
serial_write_fifo(dev->serial, dev->data[dev->command_pos]);
|
||||
sermouse_command_pos_check(dev, dev->data_len);
|
||||
break;
|
||||
case PHASE_STATUS:
|
||||
serial_write_fifo(dev->serial, dev->status);
|
||||
sermouse_command_phase_idle(dev);
|
||||
break;
|
||||
case PHASE_DIAGNOSTIC:
|
||||
if (dev->command_pos)
|
||||
serial_write_fifo(dev->serial, 0x00);
|
||||
else
|
||||
serial_write_fifo(dev->serial, sermouse_last_button_status(dev));
|
||||
sermouse_command_pos_check(dev, 3);
|
||||
break;
|
||||
case PHASE_FORMAT_AND_REVISION:
|
||||
serial_write_fifo(dev->serial, 0x10 | (dev->format << 1));
|
||||
sermouse_command_phase_idle(dev);
|
||||
break;
|
||||
case PHASE_BUTTONS:
|
||||
serial_write_fifo(dev->serial, dev->but);
|
||||
sermouse_command_phase_idle(dev);
|
||||
break;
|
||||
default:
|
||||
sermouse_command_phase_idle(dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
sermouse_poll(int x, int y, int z, int b, void *priv)
|
||||
{
|
||||
mouse_t *dev = (mouse_t *)priv;
|
||||
|
||||
if (!x && !y && !z && (b == dev->oldb)) {
|
||||
dev->oldb = b;
|
||||
return(1);
|
||||
}
|
||||
|
||||
dev->oldb = b;
|
||||
dev->abs_x += x;
|
||||
dev->abs_y += y;
|
||||
if (dev->abs_x < 0)
|
||||
dev->abs_x = 0;
|
||||
if (dev->abs_x > 4095)
|
||||
dev->abs_x = 4095;
|
||||
if (dev->abs_y < 0)
|
||||
dev->abs_y = 0;
|
||||
if (dev->abs_y > 4095)
|
||||
dev->abs_y = 4095;
|
||||
|
||||
if (dev->format == 3) {
|
||||
if (x > 2047) x = 2047;
|
||||
if (y > 2047) y = 2047;
|
||||
if (x <- 2048) x = -2048;
|
||||
if (y <- 2048) y = -2048;
|
||||
} else {
|
||||
if (x > 127) x = 127;
|
||||
if (y > 127) y = 127;
|
||||
if (x <- 128) x = -128;
|
||||
if (y <- 128) y = -128;
|
||||
}
|
||||
|
||||
dev->rel_x += x;
|
||||
dev->rel_y += y;
|
||||
dev->rel_z += z;
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ltsermouse_prompt_mode(mouse_t *dev, int prompt)
|
||||
{
|
||||
dev->prompt = prompt;
|
||||
dev->status &= 0xBF;
|
||||
if (prompt)
|
||||
dev->status |= 0x40;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ltsermouse_command_phase(mouse_t *dev, int phase)
|
||||
{
|
||||
dev->command_pos = 0;
|
||||
dev->command_phase = phase;
|
||||
timer_stop(&dev->command_timer);
|
||||
sermouse_timer_on(dev, dev->transmit_period, 0);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ltsermouse_set_report_period(mouse_t *dev, int rps)
|
||||
{
|
||||
dev->report_period = sermouse_transmit_period(dev, 9600, rps);
|
||||
timer_stop(&dev->report_timer);
|
||||
sermouse_timer_on(dev, dev->report_period, 1);
|
||||
ltsermouse_prompt_mode(dev, 0);
|
||||
dev->report_phase = REPORT_PHASE_PREPARE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ltsermouse_write(struct serial_s *serial, void *priv, uint8_t data)
|
||||
{
|
||||
mouse_t *dev = (mouse_t *)priv;
|
||||
|
||||
/* Stop reporting when we're processing a command. */
|
||||
dev->report_phase = REPORT_PHASE_PREPARE;
|
||||
|
||||
if (dev->want_data) switch (dev->want_data) {
|
||||
case 0x2A:
|
||||
dev->data_len--;
|
||||
dev->want_data = 0;
|
||||
switch (data) {
|
||||
default:
|
||||
mouse_serial_log("Serial mouse: Invalid period %02X, using 1200 bps\n", data);
|
||||
/*FALLTHROUGH*/
|
||||
case 0x6E:
|
||||
dev->transmit_period = sermouse_transmit_period(dev, 1200, -1);
|
||||
break;
|
||||
case 0x6F:
|
||||
dev->transmit_period = sermouse_transmit_period(dev, 2400, -1);
|
||||
break;
|
||||
case 0x70:
|
||||
dev->transmit_period = sermouse_transmit_period(dev, 4800, -1);
|
||||
break;
|
||||
case 0x71:
|
||||
dev->transmit_period = sermouse_transmit_period(dev, 9600, -1);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
} else switch (data) {
|
||||
case 0x2A:
|
||||
dev->want_data = data;
|
||||
dev->data_len = 1;
|
||||
break;
|
||||
case 0x44: /* Set prompt mode */
|
||||
ltsermouse_prompt_mode(dev, 1);
|
||||
break;
|
||||
case 0x50:
|
||||
if (!dev->prompt)
|
||||
ltsermouse_prompt_mode(dev, 1);
|
||||
sermouse_update_data(dev);
|
||||
ltsermouse_command_phase(dev, PHASE_DATA);
|
||||
break;
|
||||
case 0x73: /* Status */
|
||||
ltsermouse_command_phase(dev, PHASE_STATUS);
|
||||
break;
|
||||
case 0x4A: /* Report Rate Selection commands */
|
||||
ltsermouse_set_report_period(dev, 10);
|
||||
break;
|
||||
case 0x4B:
|
||||
ltsermouse_set_report_period(dev, 20);
|
||||
break;
|
||||
case 0x4C:
|
||||
ltsermouse_set_report_period(dev, 35);
|
||||
break;
|
||||
case 0x52:
|
||||
ltsermouse_set_report_period(dev, 50);
|
||||
break;
|
||||
case 0x4D:
|
||||
ltsermouse_set_report_period(dev, 70);
|
||||
break;
|
||||
case 0x51:
|
||||
ltsermouse_set_report_period(dev, 100);
|
||||
break;
|
||||
case 0x4E:
|
||||
ltsermouse_set_report_period(dev, 150);
|
||||
break;
|
||||
case 0x4F:
|
||||
ltsermouse_prompt_mode(dev, 0);
|
||||
dev->report_period = 0;
|
||||
timer_stop(&dev->report_timer);
|
||||
dev->report_phase = REPORT_PHASE_PREPARE;
|
||||
sermouse_report_timer((void *) dev);
|
||||
break;
|
||||
case 0x41:
|
||||
dev->format = 6; /* Aboslute Bit Pad One Format */
|
||||
dev->abs_x = dev->abs_y = 0;
|
||||
break;
|
||||
case 0x42:
|
||||
dev->format = 3; /* Relative Bit Pad One Format */
|
||||
break;
|
||||
case 0x53:
|
||||
dev->format = 5; /* MM Series Format */
|
||||
break;
|
||||
case 0x54:
|
||||
dev->format = 1; /* Three Byte Packed Binary Format */
|
||||
break;
|
||||
case 0x55: /* This is the Mouse Systems-compatible format */
|
||||
dev->format = 0; /* Five Byte Packed Binary Format */
|
||||
break;
|
||||
case 0x56:
|
||||
dev->format = 7; /* Microsoft Compatible Format */
|
||||
break;
|
||||
case 0x57:
|
||||
dev->format = 2; /* Hexadecimal Format */
|
||||
break;
|
||||
case 0x05:
|
||||
ltsermouse_command_phase(dev, PHASE_DIAGNOSTIC);
|
||||
break;
|
||||
case 0x66:
|
||||
ltsermouse_command_phase(dev, PHASE_FORMAT_AND_REVISION);
|
||||
break;
|
||||
case 0x6B:
|
||||
ltsermouse_command_phase(dev, PHASE_BUTTONS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
sermouse_speed_changed(void *priv)
|
||||
{
|
||||
mouse_t *dev = (mouse_t *)priv;
|
||||
|
||||
if (dev->report_enabled) {
|
||||
timer_stop(&dev->report_timer);
|
||||
if (dev->report_phase == REPORT_PHASE_TRANSMIT)
|
||||
sermouse_timer_on(dev, dev->transmit_period, 1);
|
||||
else
|
||||
sermouse_timer_on(dev, sermouse_report_period(dev), 1);
|
||||
}
|
||||
|
||||
if (dev->command_enabled) {
|
||||
timer_stop(&dev->command_timer);
|
||||
sermouse_timer_on(dev, dev->transmit_period, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
sermouse_close(void *priv)
|
||||
{
|
||||
mouse_t *dev = (mouse_t *)priv;
|
||||
|
||||
/* Detach serial port from the mouse. */
|
||||
if (dev && dev->serial && dev->serial->sd)
|
||||
memset(dev->serial->sd, 0, sizeof(serial_device_t));
|
||||
|
||||
free(dev);
|
||||
}
|
||||
|
||||
|
||||
/* Initialize the device for use by the user. */
|
||||
static void *
|
||||
sermouse_init(const device_t *info)
|
||||
{
|
||||
mouse_t *dev;
|
||||
|
||||
dev = (mouse_t *)malloc(sizeof(mouse_t));
|
||||
memset(dev, 0x00, sizeof(mouse_t));
|
||||
dev->name = info->name;
|
||||
dev->but = device_get_config_int("buttons");
|
||||
if (dev->but > 2)
|
||||
dev->flags |= FLAG_3BTN;
|
||||
|
||||
if (info->local == MOUSE_TYPE_MSYSTEMS) {
|
||||
dev->on_change = 1;
|
||||
dev->format = 0;
|
||||
dev->type = info->local;
|
||||
dev->id_len = 1;
|
||||
dev->id[0] = 'H';
|
||||
} else {
|
||||
dev->on_change = !info->local;
|
||||
dev->format = 7;
|
||||
dev->status = 0x0f;
|
||||
dev->id_len = 1;
|
||||
dev->id[0] = 'M';
|
||||
switch(dev->but) {
|
||||
case 2:
|
||||
default:
|
||||
dev->type = info->local ? MOUSE_TYPE_LOGITECH : MOUSE_TYPE_MICROSOFT;
|
||||
break;
|
||||
case 3:
|
||||
dev->type = info->local ? MOUSE_TYPE_LT3BUTTON : MOUSE_TYPE_MS3BUTTON;
|
||||
dev->id_len = 2;
|
||||
dev->id[1] = '3';
|
||||
break;
|
||||
case 4:
|
||||
dev->type = MOUSE_TYPE_MSWHEEL;
|
||||
dev->id_len = 6;
|
||||
dev->id[1] = 'Z';
|
||||
dev->id[2] = '@';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dev->transmit_period = sermouse_transmit_period(dev, 1200, -1);
|
||||
|
||||
/* Default: Continuous reporting = no delay between reports. */
|
||||
dev->report_phase = REPORT_PHASE_PREPARE;
|
||||
dev->report_period = 0;
|
||||
|
||||
/* Default: Doing nothing - command transmit timer deactivated. */
|
||||
dev->command_phase = PHASE_IDLE;
|
||||
|
||||
dev->port = device_get_config_int("port");
|
||||
|
||||
/* Attach a serial port to the mouse. */
|
||||
if (info->local)
|
||||
dev->serial = serial_attach(dev->port, sermouse_callback, ltsermouse_write, dev);
|
||||
else
|
||||
dev->serial = serial_attach(dev->port, sermouse_callback, NULL, dev);
|
||||
|
||||
mouse_serial_log("%s: port=COM%d\n", dev->name, dev->port + 1);
|
||||
|
||||
timer_add(&dev->report_timer, sermouse_report_timer, dev, 0);
|
||||
timer_add(&dev->command_timer, sermouse_command_timer, dev, 0);
|
||||
|
||||
if (info->local == MOUSE_TYPE_MSYSTEMS) {
|
||||
sermouse_timer_on(dev, dev->transmit_period, 1);
|
||||
dev->report_enabled = 1;
|
||||
}
|
||||
|
||||
/* 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 mssermouse_config[] = {
|
||||
{
|
||||
"port", "Serial Port", CONFIG_SELECTION, "", 0, {
|
||||
{
|
||||
"COM1", 0
|
||||
},
|
||||
{
|
||||
"COM2", 1
|
||||
},
|
||||
{
|
||||
""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"buttons", "Buttons", CONFIG_SELECTION, "", 2, {
|
||||
{
|
||||
"Two", 2
|
||||
},
|
||||
{
|
||||
"Three", 3
|
||||
},
|
||||
{
|
||||
"Wheel", 4
|
||||
},
|
||||
{
|
||||
""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"", "", -1
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static const device_config_t ltsermouse_config[] = {
|
||||
{
|
||||
"port", "Serial Port", CONFIG_SELECTION, "", 0, {
|
||||
{
|
||||
"COM1", 0
|
||||
},
|
||||
{
|
||||
"COM2", 1
|
||||
},
|
||||
{
|
||||
""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"buttons", "Buttons", CONFIG_SELECTION, "", 2, {
|
||||
{
|
||||
"Two", 2
|
||||
},
|
||||
{
|
||||
"Three", 3
|
||||
},
|
||||
{
|
||||
""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"", "", -1
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const device_t mouse_mssystems_device = {
|
||||
"Mouse Systems Serial Mouse",
|
||||
0,
|
||||
MOUSE_TYPE_MSYSTEMS,
|
||||
sermouse_init, sermouse_close, NULL,
|
||||
sermouse_poll, sermouse_speed_changed, NULL,
|
||||
mssermouse_config
|
||||
};
|
||||
|
||||
const device_t mouse_msserial_device = {
|
||||
"Microsoft Serial Mouse",
|
||||
0,
|
||||
0,
|
||||
sermouse_init, sermouse_close, NULL,
|
||||
sermouse_poll, sermouse_speed_changed, NULL,
|
||||
mssermouse_config
|
||||
};
|
||||
|
||||
const device_t mouse_ltserial_device = {
|
||||
"Logitech Serial Mouse",
|
||||
0,
|
||||
1,
|
||||
sermouse_init, sermouse_close, NULL,
|
||||
sermouse_poll, sermouse_speed_changed, NULL,
|
||||
ltsermouse_config
|
||||
};
|
||||
147
src/device/postcard.c
Normal file
147
src/device/postcard.c
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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 a port 80h POST diagnostic card.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Author: RichardG, <richardg867@gmail.com>
|
||||
*
|
||||
* Copyright 2020 RichardG.
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#define HAVE_STDARG_H
|
||||
#include <86box/86box.h>
|
||||
#include <86box/io.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/machine.h>
|
||||
#include <86box/plat.h>
|
||||
#include <86box/ui.h>
|
||||
#include <86box/postcard.h>
|
||||
#include "cpu.h"
|
||||
|
||||
|
||||
static uint16_t postcard_port;
|
||||
static uint8_t postcard_written;
|
||||
static uint8_t postcard_code, postcard_prev_code;
|
||||
#define UISTR_LEN 13
|
||||
static char postcard_str[UISTR_LEN]; /* UI output string */
|
||||
|
||||
|
||||
extern void ui_sb_bugui(char *__str);
|
||||
|
||||
|
||||
#ifdef ENABLE_POSTCARD_LOG
|
||||
int postcard_do_log = ENABLE_POSTCARD_LOG;
|
||||
|
||||
|
||||
static void
|
||||
postcard_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (postcard_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
int postcard_do_log = 0;
|
||||
|
||||
#define postcard_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
|
||||
static void
|
||||
postcard_setui(void)
|
||||
{
|
||||
if (!postcard_written)
|
||||
sprintf(postcard_str, "POST: -- --");
|
||||
else if (postcard_written == 1)
|
||||
sprintf(postcard_str, "POST: %02X --", postcard_code);
|
||||
else
|
||||
sprintf(postcard_str, "POST: %02X %02X", postcard_code, postcard_prev_code);
|
||||
|
||||
ui_sb_bugui(postcard_str);
|
||||
|
||||
if (postcard_do_log) {
|
||||
/* log same string sent to the UI */
|
||||
int len = strlen(postcard_str);
|
||||
postcard_str[len + 1] = '\0';
|
||||
postcard_str[len] = '\n';
|
||||
postcard_log(postcard_str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
postcard_reset(void)
|
||||
{
|
||||
postcard_written = 0;
|
||||
postcard_code = postcard_prev_code = 0x00;
|
||||
|
||||
postcard_setui();
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
postcard_write(uint16_t port, uint8_t val, void *priv)
|
||||
{
|
||||
if (postcard_written && val == postcard_code)
|
||||
return;
|
||||
|
||||
postcard_prev_code = postcard_code;
|
||||
postcard_code = val;
|
||||
if (postcard_written < 2)
|
||||
postcard_written++;
|
||||
|
||||
postcard_setui();
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
postcard_init(const device_t *info)
|
||||
{
|
||||
postcard_reset();
|
||||
|
||||
if (machines[machine].flags & MACHINE_MCA)
|
||||
postcard_port = 0x680; /* MCA machines */
|
||||
else if (strstr(machines[machine].name, " PS/2 "))
|
||||
postcard_port = 0x90; /* ISA PS/2 machines */
|
||||
else
|
||||
postcard_port = 0x80; /* AT and clone machines */
|
||||
postcard_log("POST card initializing on port %04Xh\n", postcard_port);
|
||||
|
||||
if (postcard_port) io_sethandler(postcard_port, 1,
|
||||
NULL, NULL, NULL, postcard_write, NULL, NULL, NULL);
|
||||
|
||||
return postcard_write;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
postcard_close(UNUSED(void *priv))
|
||||
{
|
||||
if (postcard_port) io_removehandler(postcard_port, 1,
|
||||
NULL, NULL, NULL, postcard_write, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
const device_t postcard_device = {
|
||||
"POST Card",
|
||||
DEVICE_ISA,
|
||||
0,
|
||||
postcard_init, postcard_close, NULL,
|
||||
NULL, NULL, NULL,
|
||||
NULL
|
||||
};
|
||||
752
src/device/serial.c
Normal file
752
src/device/serial.c
Normal file
@@ -0,0 +1,752 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* NS8250/16450/16550 UART emulation.
|
||||
*
|
||||
* Now passes all the AMIDIAG tests.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Author: Sarah Walker, <http://pcem-emulator.co.uk/>
|
||||
* Miran Grca, <mgrca8@gmail.com>
|
||||
* Fred N. van Kempen, <decwiz@yahoo.com>
|
||||
*
|
||||
* Copyright 2008-2020 Sarah Walker.
|
||||
* Copyright 2016-2020 Miran Grca.
|
||||
* Copyright 2017-2020 Fred N. van Kempen.
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#define HAVE_STDARG_H
|
||||
#include <86box/86box.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/timer.h>
|
||||
#include <86box/machine.h>
|
||||
#include <86box/io.h>
|
||||
#include <86box/pic.h>
|
||||
#include <86box/mem.h>
|
||||
#include <86box/rom.h>
|
||||
#include <86box/serial.h>
|
||||
#include <86box/mouse.h>
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
SERIAL_INT_LSR = 1,
|
||||
SERIAL_INT_RECEIVE = 2,
|
||||
SERIAL_INT_TRANSMIT = 4,
|
||||
SERIAL_INT_MSR = 8,
|
||||
SERIAL_INT_TIMEOUT = 16
|
||||
};
|
||||
|
||||
|
||||
static int next_inst = 0;
|
||||
static serial_device_t serial_devices[SERIAL_MAX];
|
||||
|
||||
|
||||
#ifdef ENABLE_SERIAL_LOG
|
||||
int serial_do_log = ENABLE_SERIAL_LOG;
|
||||
|
||||
|
||||
static void
|
||||
serial_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (serial_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define serial_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
|
||||
void
|
||||
serial_reset_port(serial_t *dev)
|
||||
{
|
||||
dev->lsr = 0x60; /* Mark that both THR/FIFO and TXSR are empty. */
|
||||
dev->iir = dev->ier = dev->lcr = dev->fcr = 0;
|
||||
dev->fifo_enabled = 0;
|
||||
dev->xmit_fifo_pos = dev->rcvr_fifo_pos = 0;
|
||||
dev->rcvr_fifo_full = 0;
|
||||
dev->baud_cycles = 0;
|
||||
memset(dev->xmit_fifo, 0, 16);
|
||||
memset(dev->rcvr_fifo, 0, 14);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
serial_transmit_period(serial_t *dev)
|
||||
{
|
||||
double ddlab;
|
||||
|
||||
ddlab = (double) dev->dlab;
|
||||
|
||||
/* Bit period based on DLAB. */
|
||||
dev->transmit_period = (16000000.0 * ddlab) / dev->clock_src;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
serial_update_ints(serial_t *dev)
|
||||
{
|
||||
int stat = 0;
|
||||
|
||||
dev->iir = 1;
|
||||
|
||||
if ((dev->ier & 4) && (dev->int_status & SERIAL_INT_LSR)) {
|
||||
/* Line status interrupt */
|
||||
stat = 1;
|
||||
dev->iir = 6;
|
||||
} else if ((dev->ier & 1) && (dev->int_status & SERIAL_INT_TIMEOUT)) {
|
||||
/* Received data available */
|
||||
stat = 1;
|
||||
dev->iir = 0x0c;
|
||||
} else if ((dev->ier & 1) && (dev->int_status & SERIAL_INT_RECEIVE)) {
|
||||
/* Received data available */
|
||||
stat = 1;
|
||||
dev->iir = 4;
|
||||
} else if ((dev->ier & 2) && (dev->int_status & SERIAL_INT_TRANSMIT)) {
|
||||
/* Transmit data empty */
|
||||
stat = 1;
|
||||
dev->iir = 2;
|
||||
} else if ((dev->ier & 8) && (dev->int_status & SERIAL_INT_MSR)) {
|
||||
/* Modem status interrupt */
|
||||
stat = 1;
|
||||
dev->iir = 0;
|
||||
}
|
||||
|
||||
if (stat && ((dev->mctrl & 8) || (dev->type == SERIAL_8250_PCJR))) {
|
||||
if (dev->type >= SERIAL_NS16450)
|
||||
picintlevel(1 << dev->irq);
|
||||
else
|
||||
picint(1 << dev->irq);
|
||||
} else
|
||||
picintc(1 << dev->irq);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
serial_clear_timeout(serial_t *dev)
|
||||
{
|
||||
/* Disable timeout timer and clear timeout condition. */
|
||||
timer_disable(&dev->timeout_timer);
|
||||
dev->int_status &= ~SERIAL_INT_TIMEOUT;
|
||||
serial_update_ints(dev);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
write_fifo(serial_t *dev, uint8_t dat)
|
||||
{
|
||||
serial_log("write_fifo(%08X, %02X, %i, %i)\n", dev, dat, (dev->type >= SERIAL_NS16550) && dev->fifo_enabled, dev->rcvr_fifo_pos & 0x0f);
|
||||
|
||||
if ((dev->type >= SERIAL_NS16550) && dev->fifo_enabled) {
|
||||
/* FIFO mode. */
|
||||
timer_disable(&dev->timeout_timer);
|
||||
/* Indicate overrun. */
|
||||
if (dev->rcvr_fifo_full)
|
||||
dev->lsr |= 0x02;
|
||||
else
|
||||
dev->rcvr_fifo[dev->rcvr_fifo_pos] = dat;
|
||||
dev->lsr &= 0xfe;
|
||||
dev->int_status &= ~SERIAL_INT_RECEIVE;
|
||||
if (dev->rcvr_fifo_pos == (dev->rcvr_fifo_len - 1)) {
|
||||
dev->lsr |= 0x01;
|
||||
dev->int_status |= SERIAL_INT_RECEIVE;
|
||||
}
|
||||
if (dev->rcvr_fifo_pos < 15)
|
||||
dev->rcvr_fifo_pos++;
|
||||
else
|
||||
dev->rcvr_fifo_full = 1;
|
||||
serial_update_ints(dev);
|
||||
timer_on_auto(&dev->timeout_timer, 4.0 * dev->bits * dev->transmit_period);
|
||||
} else {
|
||||
/* Non-FIFO mode. */
|
||||
/* Indicate overrun. */
|
||||
if (dev->lsr & 0x01)
|
||||
dev->lsr |= 0x02;
|
||||
dev->dat = dat;
|
||||
dev->lsr |= 0x01;
|
||||
dev->int_status |= SERIAL_INT_RECEIVE;
|
||||
serial_update_ints(dev);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
serial_write_fifo(serial_t *dev, uint8_t dat)
|
||||
{
|
||||
serial_log("serial_write_fifo(%08X, %02X, %i, %i)\n", dev, dat, (dev->type >= SERIAL_NS16550) && dev->fifo_enabled, dev->rcvr_fifo_pos & 0x0f);
|
||||
|
||||
if (!(dev->mctrl & 0x10))
|
||||
write_fifo(dev, dat);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
serial_transmit(serial_t *dev, uint8_t val)
|
||||
{
|
||||
if (dev->mctrl & 0x10)
|
||||
write_fifo(dev, val);
|
||||
else if (dev->sd->dev_write)
|
||||
dev->sd->dev_write(dev, dev->sd->priv, val);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
serial_move_to_txsr(serial_t *dev)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (dev->fifo_enabled) {
|
||||
dev->txsr = dev->xmit_fifo[0];
|
||||
if (dev->xmit_fifo_pos > 0) {
|
||||
/* Move the entire fifo forward by one byte. */
|
||||
for (i = 1; i < 16; i++)
|
||||
dev->xmit_fifo[i - 1] = dev->xmit_fifo[i];
|
||||
/* Decrease FIFO position. */
|
||||
dev->xmit_fifo_pos--;
|
||||
}
|
||||
} else {
|
||||
dev->txsr = dev->thr;
|
||||
dev->thr = 0;
|
||||
}
|
||||
|
||||
dev->lsr &= ~0x40;
|
||||
serial_log("serial_move_to_txsr(): FIFO %sabled, FIFO pos = %i\n", dev->fifo_enabled ? "en" : "dis", dev->xmit_fifo_pos & 0x0f);
|
||||
|
||||
if (!dev->fifo_enabled || (dev->xmit_fifo_pos == 0x0)) {
|
||||
/* Update interrupts to signal THRE and that TXSR is no longer empty. */
|
||||
dev->lsr |= 0x20;
|
||||
dev->int_status |= SERIAL_INT_TRANSMIT;
|
||||
serial_update_ints(dev);
|
||||
}
|
||||
if (dev->transmit_enabled & 2)
|
||||
dev->baud_cycles++;
|
||||
else
|
||||
dev->baud_cycles = 0; /* If not moving while transmitting, reset BAUDOUT cycle count. */
|
||||
if (!dev->fifo_enabled || (dev->xmit_fifo_pos == 0x0))
|
||||
dev->transmit_enabled &= ~1; /* Stop moving. */
|
||||
dev->transmit_enabled |= 2; /* Start transmitting. */
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
serial_process_txsr(serial_t *dev)
|
||||
{
|
||||
serial_log("serial_process_txsr(): FIFO %sabled\n", dev->fifo_enabled ? "en" : "dis");
|
||||
serial_transmit(dev, dev->txsr);
|
||||
dev->txsr = 0;
|
||||
/* Reset BAUDOUT cycle count. */
|
||||
dev->baud_cycles = 0;
|
||||
/* If FIFO is enabled and there are bytes left to transmit,
|
||||
continue with the FIFO, otherwise stop. */
|
||||
if (dev->fifo_enabled && (dev->xmit_fifo_pos != 0x0))
|
||||
dev->transmit_enabled |= 1;
|
||||
else {
|
||||
/* Both FIFO/THR and TXSR are empty. */
|
||||
/* If bit 5 is set, also set bit 6 to mark both THR and shift register as empty. */
|
||||
if (dev->lsr & 0x20)
|
||||
dev->lsr |= 0x40;
|
||||
dev->transmit_enabled &= ~2;
|
||||
}
|
||||
dev->int_status &= ~SERIAL_INT_TRANSMIT;
|
||||
serial_update_ints(dev);
|
||||
}
|
||||
|
||||
|
||||
/* Transmit_enable flags:
|
||||
Bit 0 = Do move if set;
|
||||
Bit 1 = Do transmit if set. */
|
||||
static void
|
||||
serial_transmit_timer(void *priv)
|
||||
{
|
||||
serial_t *dev = (serial_t *) priv;
|
||||
int delay = 8; /* STOP to THRE delay is 8 BAUDOUT cycles. */
|
||||
|
||||
if (dev->transmit_enabled & 3) {
|
||||
if ((dev->transmit_enabled & 1) && (dev->transmit_enabled & 2))
|
||||
delay = dev->data_bits; /* Delay by less if already transmitting. */
|
||||
|
||||
dev->baud_cycles++;
|
||||
|
||||
/* We have processed (total bits) BAUDOUT cycles, transmit the byte. */
|
||||
if ((dev->baud_cycles == dev->bits) && (dev->transmit_enabled & 2))
|
||||
serial_process_txsr(dev);
|
||||
|
||||
/* We have processed (data bits) BAUDOUT cycles. */
|
||||
if ((dev->baud_cycles == delay) && (dev->transmit_enabled & 1))
|
||||
serial_move_to_txsr(dev);
|
||||
|
||||
if (dev->transmit_enabled & 3)
|
||||
timer_on_auto(&dev->transmit_timer, dev->transmit_period);
|
||||
} else {
|
||||
dev->baud_cycles = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
serial_timeout_timer(void *priv)
|
||||
{
|
||||
serial_t *dev = (serial_t *) priv;
|
||||
|
||||
#ifdef ENABLE_SERIAL_LOG
|
||||
serial_log("serial_timeout_timer()\n");
|
||||
#endif
|
||||
|
||||
dev->lsr |= 0x01;
|
||||
dev->int_status |= SERIAL_INT_TIMEOUT;
|
||||
serial_update_ints(dev);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
serial_update_speed(serial_t *dev)
|
||||
{
|
||||
if (dev->transmit_enabled & 3)
|
||||
timer_on_auto(&dev->transmit_timer, dev->transmit_period);
|
||||
|
||||
if (timer_is_enabled(&dev->timeout_timer))
|
||||
timer_on_auto(&dev->timeout_timer, 4.0 * dev->bits * dev->transmit_period);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
serial_reset_fifo(serial_t *dev)
|
||||
{
|
||||
dev->lsr = (dev->lsr & 0xfe) | 0x60;
|
||||
dev->int_status = (dev->int_status & ~SERIAL_INT_RECEIVE) | SERIAL_INT_TRANSMIT;
|
||||
serial_update_ints(dev);
|
||||
dev->xmit_fifo_pos = dev->rcvr_fifo_pos = 0;
|
||||
dev->rcvr_fifo_full = 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
serial_set_clock_src(serial_t *dev, double clock_src)
|
||||
{
|
||||
dev->clock_src = clock_src;
|
||||
|
||||
serial_transmit_period(dev);
|
||||
serial_update_speed(dev);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
serial_write(uint16_t addr, uint8_t val, void *p)
|
||||
{
|
||||
serial_t *dev = (serial_t *)p;
|
||||
uint8_t new_msr, old;
|
||||
|
||||
serial_log("UART: Write %02X to port %02X\n", val, addr);
|
||||
|
||||
sub_cycles(ISA_CYCLES(8));
|
||||
|
||||
switch (addr & 7) {
|
||||
case 0:
|
||||
if (dev->lcr & 0x80) {
|
||||
dev->dlab = (dev->dlab & 0xff00) | val;
|
||||
serial_transmit_period(dev);
|
||||
serial_update_speed(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Indicate FIFO/THR is no longer empty. */
|
||||
dev->lsr &= 0x9f;
|
||||
dev->int_status &= ~SERIAL_INT_TRANSMIT;
|
||||
serial_update_ints(dev);
|
||||
|
||||
if ((dev->type >= SERIAL_NS16550) && dev->fifo_enabled && (dev->xmit_fifo_pos < 16)) {
|
||||
/* FIFO mode, begin transmitting. */
|
||||
timer_on_auto(&dev->transmit_timer, dev->transmit_period);
|
||||
dev->transmit_enabled |= 1; /* Start moving. */
|
||||
dev->xmit_fifo[dev->xmit_fifo_pos++] = val;
|
||||
} else {
|
||||
/* Non-FIFO mode, begin transmitting. */
|
||||
timer_on_auto(&dev->transmit_timer, dev->transmit_period);
|
||||
dev->transmit_enabled |= 1; /* Start moving. */
|
||||
dev->thr = val;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (dev->lcr & 0x80) {
|
||||
dev->dlab = (dev->dlab & 0x00ff) | (val << 8);
|
||||
serial_transmit_period(dev);
|
||||
serial_update_speed(dev);
|
||||
return;
|
||||
}
|
||||
if ((val & 2) && (dev->lsr & 0x20))
|
||||
dev->int_status |= SERIAL_INT_TRANSMIT;
|
||||
dev->ier = val & 0xf;
|
||||
serial_update_ints(dev);
|
||||
break;
|
||||
case 2:
|
||||
if (dev->type >= SERIAL_NS16550) {
|
||||
if ((val ^ dev->fcr) & 0x01)
|
||||
serial_reset_fifo(dev);
|
||||
dev->fcr = val & 0xf9;
|
||||
dev->fifo_enabled = val & 0x01;
|
||||
if (!dev->fifo_enabled) {
|
||||
memset(dev->rcvr_fifo, 0, 14);
|
||||
memset(dev->xmit_fifo, 0, 16);
|
||||
dev->xmit_fifo_pos = dev->rcvr_fifo_pos = 0;
|
||||
dev->rcvr_fifo_full = 0;
|
||||
dev->rcvr_fifo_len = 1;
|
||||
break;
|
||||
}
|
||||
if (val & 0x02) {
|
||||
memset(dev->rcvr_fifo, 0, 14);
|
||||
dev->rcvr_fifo_pos = 0;
|
||||
dev->rcvr_fifo_full = 0;
|
||||
}
|
||||
if (val & 0x04) {
|
||||
memset(dev->xmit_fifo, 0, 16);
|
||||
dev->xmit_fifo_pos = 0;
|
||||
}
|
||||
switch ((val >> 6) & 0x03) {
|
||||
case 0:
|
||||
dev->rcvr_fifo_len = 1;
|
||||
break;
|
||||
case 1:
|
||||
dev->rcvr_fifo_len = 4;
|
||||
break;
|
||||
case 2:
|
||||
dev->rcvr_fifo_len = 8;
|
||||
break;
|
||||
case 3:
|
||||
dev->rcvr_fifo_len = 14;
|
||||
break;
|
||||
}
|
||||
serial_log("FIFO now %sabled, receive FIFO length = %i\n", dev->fifo_enabled ? "en" : "dis", dev->rcvr_fifo_len);
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
old = dev->lcr;
|
||||
dev->lcr = val;
|
||||
if ((old ^ val) & 0x0f) {
|
||||
/* Data bits + start bit. */
|
||||
dev->bits = ((dev->lcr & 0x03) + 5) + 1;
|
||||
/* Stop bits. */
|
||||
dev->bits++; /* First stop bit. */
|
||||
if (dev->lcr & 0x04)
|
||||
dev->bits++; /* Second stop bit. */
|
||||
/* Parity bit. */
|
||||
if (dev->lcr & 0x08)
|
||||
dev->bits++;
|
||||
|
||||
serial_transmit_period(dev);
|
||||
serial_update_speed(dev);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if ((val & 2) && !(dev->mctrl & 2)) {
|
||||
if (dev->sd->rcr_callback)
|
||||
dev->sd->rcr_callback(dev, dev->sd->priv);
|
||||
}
|
||||
if (!(val & 8) && (dev->mctrl & 8))
|
||||
picintc(1 << dev->irq);
|
||||
if ((val ^ dev->mctrl) & 0x10)
|
||||
serial_reset_fifo(dev);
|
||||
dev->mctrl = val;
|
||||
if (val & 0x10) {
|
||||
new_msr = (val & 0x0c) << 4;
|
||||
new_msr |= (val & 0x02) ? 0x10: 0;
|
||||
new_msr |= (val & 0x01) ? 0x20: 0;
|
||||
|
||||
if ((dev->msr ^ new_msr) & 0x10)
|
||||
new_msr |= 0x01;
|
||||
if ((dev->msr ^ new_msr) & 0x20)
|
||||
new_msr |= 0x02;
|
||||
if ((dev->msr ^ new_msr) & 0x80)
|
||||
new_msr |= 0x08;
|
||||
if ((dev->msr & 0x40) && !(new_msr & 0x40))
|
||||
new_msr |= 0x04;
|
||||
|
||||
dev->msr = new_msr;
|
||||
|
||||
dev->xmit_fifo_pos = dev->rcvr_fifo_pos = 0;
|
||||
dev->rcvr_fifo_full = 0;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
dev->lsr = val;
|
||||
if (dev->lsr & 0x01)
|
||||
dev->int_status |= SERIAL_INT_RECEIVE;
|
||||
if (dev->lsr & 0x1e)
|
||||
dev->int_status |= SERIAL_INT_LSR;
|
||||
if (dev->lsr & 0x20)
|
||||
dev->int_status |= SERIAL_INT_TRANSMIT;
|
||||
serial_update_ints(dev);
|
||||
break;
|
||||
case 6:
|
||||
dev->msr = val;
|
||||
if (dev->msr & 0x0f)
|
||||
dev->int_status |= SERIAL_INT_MSR;
|
||||
serial_update_ints(dev);
|
||||
break;
|
||||
case 7:
|
||||
if (dev->type >= SERIAL_NS16450)
|
||||
dev->scratch = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
serial_read(uint16_t addr, void *p)
|
||||
{
|
||||
serial_t *dev = (serial_t *)p;
|
||||
uint8_t i, ret = 0;
|
||||
|
||||
sub_cycles(ISA_CYCLES(8));
|
||||
|
||||
switch (addr & 7) {
|
||||
case 0:
|
||||
if (dev->lcr & 0x80) {
|
||||
ret = dev->dlab & 0xff;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((dev->type >= SERIAL_NS16550) && dev->fifo_enabled) {
|
||||
/* FIFO mode. */
|
||||
|
||||
serial_clear_timeout(dev);
|
||||
|
||||
ret = dev->rcvr_fifo[0];
|
||||
dev->rcvr_fifo_full = 0;
|
||||
if (dev->rcvr_fifo_pos > 0) {
|
||||
for (i = 1; i < 16; i++)
|
||||
dev->rcvr_fifo[i - 1] = dev->rcvr_fifo[i];
|
||||
serial_log("FIFO position %i: read %02X, next %02X\n", dev->rcvr_fifo_pos, ret, dev->rcvr_fifo[0]);
|
||||
dev->rcvr_fifo_pos--;
|
||||
/* At least one byte remains to be read, start the timeout
|
||||
timer so that a timeout is indicated in case of no read. */
|
||||
timer_on_auto(&dev->timeout_timer, 4.0 * dev->bits * dev->transmit_period);
|
||||
} else {
|
||||
dev->lsr &= 0xfe;
|
||||
dev->int_status &= ~SERIAL_INT_RECEIVE;
|
||||
serial_update_ints(dev);
|
||||
}
|
||||
} else {
|
||||
ret = dev->dat;
|
||||
dev->lsr &= 0xfe;
|
||||
dev->int_status &= ~SERIAL_INT_RECEIVE;
|
||||
serial_update_ints(dev);
|
||||
}
|
||||
serial_log("Read data: %02X\n", ret);
|
||||
break;
|
||||
case 1:
|
||||
if (dev->lcr & 0x80)
|
||||
ret = (dev->dlab >> 8) & 0xff;
|
||||
else
|
||||
ret = dev->ier;
|
||||
break;
|
||||
case 2:
|
||||
ret = dev->iir;
|
||||
if ((ret & 0xe) == 2) {
|
||||
dev->int_status &= ~SERIAL_INT_TRANSMIT;
|
||||
serial_update_ints(dev);
|
||||
}
|
||||
if (dev->fcr & 1)
|
||||
ret |= 0xc0;
|
||||
break;
|
||||
case 3:
|
||||
ret = dev->lcr;
|
||||
break;
|
||||
case 4:
|
||||
ret = dev->mctrl;
|
||||
break;
|
||||
case 5:
|
||||
ret = dev->lsr;
|
||||
if (dev->lsr & 0x1f)
|
||||
dev->lsr &= ~0x1e;
|
||||
dev->int_status &= ~SERIAL_INT_LSR;
|
||||
serial_update_ints(dev);
|
||||
break;
|
||||
case 6:
|
||||
ret = dev->msr;
|
||||
dev->msr &= ~0x0f;
|
||||
dev->int_status &= ~SERIAL_INT_MSR;
|
||||
serial_update_ints(dev);
|
||||
break;
|
||||
case 7:
|
||||
ret = dev->scratch;
|
||||
break;
|
||||
}
|
||||
|
||||
serial_log("UART: Read %02X from port %02X\n", ret, addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
serial_remove(serial_t *dev)
|
||||
{
|
||||
if (!serial_enabled[dev->inst])
|
||||
return;
|
||||
|
||||
if (!dev->base_address)
|
||||
return;
|
||||
|
||||
serial_log("Removing serial port %i at %04X...\n", dev->inst, dev->base_address);
|
||||
|
||||
io_removehandler(dev->base_address, 0x0008,
|
||||
serial_read, NULL, NULL, serial_write, NULL, NULL, dev);
|
||||
dev->base_address = 0x0000;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
serial_setup(serial_t *dev, uint16_t addr, int irq)
|
||||
{
|
||||
serial_log("Adding serial port %i at %04X...\n", dev->inst, addr);
|
||||
|
||||
if (!serial_enabled[dev->inst])
|
||||
return;
|
||||
if (dev->base_address != 0x0000)
|
||||
serial_remove(dev);
|
||||
dev->base_address = addr;
|
||||
if (addr != 0x0000)
|
||||
io_sethandler(addr, 0x0008, serial_read, NULL, NULL, serial_write, NULL, NULL, dev);
|
||||
dev->irq = irq;
|
||||
}
|
||||
|
||||
|
||||
serial_t *
|
||||
serial_attach(int port,
|
||||
void (*rcr_callback)(struct serial_s *serial, void *p),
|
||||
void (*dev_write)(struct serial_s *serial, void *p, uint8_t data),
|
||||
void *priv)
|
||||
{
|
||||
serial_device_t *sd = &serial_devices[port];
|
||||
|
||||
sd->rcr_callback = rcr_callback;
|
||||
sd->dev_write = dev_write;
|
||||
sd->priv = priv;
|
||||
|
||||
return sd->serial;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
serial_speed_changed(void *priv)
|
||||
{
|
||||
serial_t *dev = (serial_t *) priv;
|
||||
|
||||
serial_update_speed(dev);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
serial_close(void *priv)
|
||||
{
|
||||
serial_t *dev = (serial_t *) priv;
|
||||
|
||||
next_inst--;
|
||||
|
||||
free(dev);
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
serial_init(const device_t *info)
|
||||
{
|
||||
serial_t *dev = (serial_t *) malloc(sizeof(serial_t));
|
||||
memset(dev, 0, sizeof(serial_t));
|
||||
|
||||
dev->inst = next_inst;
|
||||
|
||||
if (serial_enabled[next_inst]) {
|
||||
serial_log("Adding serial port %i...\n", next_inst);
|
||||
dev->type = info->local;
|
||||
memset(&(serial_devices[next_inst]), 0, sizeof(serial_device_t));
|
||||
dev->sd = &(serial_devices[next_inst]);
|
||||
dev->sd->serial = dev;
|
||||
serial_reset_port(dev);
|
||||
if (next_inst || (info->flags & DEVICE_PCJR))
|
||||
serial_setup(dev, SERIAL2_ADDR, SERIAL2_IRQ);
|
||||
else
|
||||
serial_setup(dev, SERIAL1_ADDR, SERIAL1_IRQ);
|
||||
|
||||
/* Default to 1200,N,7. */
|
||||
dev->dlab = 96;
|
||||
dev->fcr = 0x06;
|
||||
dev->clock_src = 1843200.0;
|
||||
serial_transmit_period(dev);
|
||||
timer_add(&dev->transmit_timer, serial_transmit_timer, dev, 0);
|
||||
timer_add(&dev->timeout_timer, serial_timeout_timer, dev, 0);
|
||||
}
|
||||
|
||||
next_inst++;
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
serial_set_next_inst(int ni)
|
||||
{
|
||||
next_inst = ni;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
serial_standalone_init(void) {
|
||||
if (next_inst == 0) {
|
||||
device_add_inst(&i8250_device, 1);
|
||||
device_add_inst(&i8250_device, 2);
|
||||
} else if (next_inst == 1)
|
||||
device_add_inst(&i8250_device, 2);
|
||||
};
|
||||
|
||||
|
||||
const device_t i8250_device = {
|
||||
"Intel 8250(-compatible) UART",
|
||||
0,
|
||||
SERIAL_8250,
|
||||
serial_init, serial_close, NULL,
|
||||
NULL, serial_speed_changed, NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
const device_t i8250_pcjr_device = {
|
||||
"Intel 8250(-compatible) UART for PCjr",
|
||||
DEVICE_PCJR,
|
||||
SERIAL_8250_PCJR,
|
||||
serial_init, serial_close, NULL,
|
||||
NULL, serial_speed_changed, NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
const device_t ns16450_device = {
|
||||
"National Semiconductor NS16450(-compatible) UART",
|
||||
0,
|
||||
SERIAL_NS16450,
|
||||
serial_init, serial_close, NULL,
|
||||
NULL, serial_speed_changed, NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
const device_t ns16550_device = {
|
||||
"National Semiconductor NS16550(-compatible) UART",
|
||||
0,
|
||||
SERIAL_NS16550,
|
||||
serial_init, serial_close, NULL,
|
||||
NULL, serial_speed_changed, NULL,
|
||||
NULL
|
||||
};
|
||||
396
src/device/smbus.c
Normal file
396
src/device/smbus.c
Normal file
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Implement SMBus (System Management Bus) and its operations.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: RichardG, <richardg867@gmail.com>
|
||||
*
|
||||
* Copyright 2020 RichardG.
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#define HAVE_STDARG_H
|
||||
#include <86box/86box.h>
|
||||
#include <86box/smbus.h>
|
||||
|
||||
|
||||
#define NADDRS 128 /* SMBus supports 128 addresses */
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
|
||||
typedef struct _smbus_ {
|
||||
uint8_t (*read_byte)(uint8_t addr, void *priv);
|
||||
uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv);
|
||||
uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv);
|
||||
uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv);
|
||||
|
||||
void (*write_byte)(uint8_t addr, uint8_t val, void *priv);
|
||||
void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv);
|
||||
void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv);
|
||||
void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv);
|
||||
|
||||
void *priv;
|
||||
|
||||
struct _smbus_ *prev, *next;
|
||||
} smbus_t;
|
||||
|
||||
int smbus_initialized = 0;
|
||||
smbus_t *smbus[NADDRS], *smbus_last[NADDRS];
|
||||
|
||||
|
||||
#ifdef ENABLE_SMBUS_LOG
|
||||
int smbus_do_log = ENABLE_SMBUS_LOG;
|
||||
|
||||
|
||||
static void
|
||||
smbus_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (smbus_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define smbus_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
|
||||
void
|
||||
smbus_init(void)
|
||||
{
|
||||
int c;
|
||||
smbus_t *p, *q;
|
||||
|
||||
if (!smbus_initialized) {
|
||||
for (c=0; c<NADDRS; c++)
|
||||
smbus[c] = smbus_last[c] = NULL;
|
||||
smbus_initialized = 1;
|
||||
}
|
||||
|
||||
for (c=0; c<NADDRS; c++) {
|
||||
if (smbus_last[c]) {
|
||||
/* Address c has at least one handler. */
|
||||
p = smbus_last[c];
|
||||
/* After this loop, p will have the pointer to the first handler. */
|
||||
while (p) {
|
||||
q = p->prev;
|
||||
free(p);
|
||||
p = q;
|
||||
}
|
||||
p = NULL;
|
||||
}
|
||||
|
||||
/* smbus[c] should be NULL. */
|
||||
smbus[c] = smbus_last[c] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
smbus_sethandler(uint8_t base, int size,
|
||||
uint8_t (*read_byte)(uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void (*write_byte)(uint8_t addr, uint8_t val, void *priv),
|
||||
void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv),
|
||||
void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv),
|
||||
void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void *priv)
|
||||
{
|
||||
int c;
|
||||
smbus_t *p, *q = NULL;
|
||||
|
||||
for (c = 0; c < size; c++) {
|
||||
p = smbus_last[base + c];
|
||||
q = (smbus_t *) malloc(sizeof(smbus_t));
|
||||
memset(q, 0, sizeof(smbus_t));
|
||||
if (p) {
|
||||
p->next = q;
|
||||
q->prev = p;
|
||||
} else {
|
||||
smbus[base + c] = q;
|
||||
q->prev = NULL;
|
||||
}
|
||||
|
||||
q->read_byte = read_byte;
|
||||
q->read_byte_cmd = read_byte_cmd;
|
||||
q->read_word_cmd = read_word_cmd;
|
||||
q->read_block_cmd = read_block_cmd;
|
||||
|
||||
q->write_byte = write_byte;
|
||||
q->write_byte_cmd = write_byte_cmd;
|
||||
q->write_word_cmd = write_word_cmd;
|
||||
q->write_block_cmd = write_block_cmd;
|
||||
|
||||
q->priv = priv;
|
||||
q->next = NULL;
|
||||
|
||||
smbus_last[base + c] = q;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
smbus_removehandler(uint8_t base, int size,
|
||||
uint8_t (*read_byte)(uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void (*write_byte)(uint8_t addr, uint8_t val, void *priv),
|
||||
void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv),
|
||||
void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv),
|
||||
void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void *priv)
|
||||
{
|
||||
int c;
|
||||
smbus_t *p;
|
||||
|
||||
for (c = 0; c < size; c++) {
|
||||
p = smbus[base + c];
|
||||
if (!p)
|
||||
continue;
|
||||
while(p) {
|
||||
if ((p->read_byte == read_byte) && (p->read_byte_cmd == read_byte_cmd) &&
|
||||
(p->read_word_cmd == read_word_cmd) && (p->read_block_cmd == read_block_cmd) &&
|
||||
(p->write_byte == write_byte) && (p->write_byte_cmd == write_byte_cmd) &&
|
||||
(p->write_word_cmd == write_word_cmd) && (p->write_block_cmd == write_block_cmd) &&
|
||||
(p->priv == priv)) {
|
||||
if (p->prev)
|
||||
p->prev->next = p->next;
|
||||
else
|
||||
smbus[base + c] = p->next;
|
||||
if (p->next)
|
||||
p->next->prev = p->prev;
|
||||
else
|
||||
smbus_last[base + c] = p->prev;
|
||||
free(p);
|
||||
p = NULL;
|
||||
break;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
smbus_handler(int set, uint8_t base, int size,
|
||||
uint8_t (*read_byte)(uint8_t addr, void *priv),
|
||||
uint8_t (*read_byte_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint16_t (*read_word_cmd)(uint8_t addr, uint8_t cmd, void *priv),
|
||||
uint8_t (*read_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void (*write_byte)(uint8_t addr, uint8_t val, void *priv),
|
||||
void (*write_byte_cmd)(uint8_t addr, uint8_t cmd, uint8_t val, void *priv),
|
||||
void (*write_word_cmd)(uint8_t addr, uint8_t cmd, uint16_t val, void *priv),
|
||||
void (*write_block_cmd)(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len, void *priv),
|
||||
void *priv)
|
||||
{
|
||||
if (set)
|
||||
smbus_sethandler(base, size, read_byte, read_byte_cmd, read_word_cmd, read_block_cmd, write_byte, write_byte_cmd, write_word_cmd, write_block_cmd, priv);
|
||||
else
|
||||
smbus_removehandler(base, size, read_byte, read_byte_cmd, read_word_cmd, read_block_cmd, write_byte, write_byte_cmd, write_word_cmd, write_block_cmd, priv);
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
smbus_has_device(uint8_t addr)
|
||||
{
|
||||
return(!!smbus[addr]);
|
||||
}
|
||||
|
||||
|
||||
uint8_t
|
||||
smbus_read_byte(uint8_t addr)
|
||||
{
|
||||
uint8_t ret = 0xff;
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
p = smbus[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->read_byte) {
|
||||
ret &= p->read_byte(addr, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: read_byte(%02X) = %02X\n", addr, ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
uint8_t
|
||||
smbus_read_byte_cmd(uint8_t addr, uint8_t cmd)
|
||||
{
|
||||
uint8_t ret = 0xff;
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
p = smbus[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->read_byte_cmd) {
|
||||
ret &= p->read_byte_cmd(addr, cmd, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: read_byte_cmd(%02X, %02X) = %02X\n", addr, cmd, ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
uint16_t
|
||||
smbus_read_word_cmd(uint8_t addr, uint8_t cmd)
|
||||
{
|
||||
uint16_t ret = 0xffff;
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
p = smbus[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->read_word_cmd) {
|
||||
ret &= p->read_word_cmd(addr, cmd, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: read_word_cmd(%02X, %02X) = %04X\n", addr, cmd, ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
uint8_t
|
||||
smbus_read_block_cmd(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len)
|
||||
{
|
||||
uint8_t ret = 0;
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
p = smbus[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->read_block_cmd) {
|
||||
ret = MAX(ret, p->read_block_cmd(addr, cmd, data, len, p->priv));
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: read_block_cmd(%02X, %02X) = %02X\n", addr, cmd, len);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
smbus_write_byte(uint8_t addr, uint8_t val)
|
||||
{
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
if (smbus[addr]) {
|
||||
p = smbus[addr];
|
||||
while(p) {
|
||||
if (p->write_byte) {
|
||||
p->write_byte(addr, val, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: write_byte(%02X, %02X)\n", addr, val);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
smbus_write_byte_cmd(uint8_t addr, uint8_t cmd, uint8_t val)
|
||||
{
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
if (smbus[addr]) {
|
||||
p = smbus[addr];
|
||||
while(p) {
|
||||
if (p->write_byte_cmd) {
|
||||
p->write_byte_cmd(addr, cmd, val, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: write_byte_cmd(%02X, %02X, %02X)\n", addr, cmd, val);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
smbus_write_word_cmd(uint8_t addr, uint8_t cmd, uint16_t val)
|
||||
{
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
if (smbus[addr]) {
|
||||
p = smbus[addr];
|
||||
while(p) {
|
||||
if (p->write_word_cmd) {
|
||||
p->write_word_cmd(addr, cmd, val, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: write_word_cmd(%02X, %02X, %04X)\n", addr, cmd, val);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
smbus_write_block_cmd(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t len)
|
||||
{
|
||||
smbus_t *p;
|
||||
int found = 0;
|
||||
|
||||
p = smbus[addr];
|
||||
if (p) {
|
||||
while(p) {
|
||||
if (p->write_block_cmd) {
|
||||
p->write_block_cmd(addr, cmd, data, len, p->priv);
|
||||
found++;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
smbus_log("SMBus: write_block_cmd(%02X, %02X, %02X)\n", addr, cmd, len);
|
||||
|
||||
return;
|
||||
}
|
||||
253
src/device/smbus_piix4.c
Normal file
253
src/device/smbus_piix4.c
Normal file
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* 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 a generic PIIX4-compatible SMBus host controller.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: RichardG, <richardg867@gmail.com>
|
||||
*
|
||||
* Copyright 2020 RichardG.
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#define HAVE_STDARG_H
|
||||
#include <86box/86box.h>
|
||||
#include <86box/io.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/timer.h>
|
||||
#include <86box/smbus.h>
|
||||
#include <86box/smbus_piix4.h>
|
||||
|
||||
|
||||
#ifdef ENABLE_SMBUS_PIIX4_LOG
|
||||
int smbus_piix4_do_log = ENABLE_SMBUS_PIIX4_LOG;
|
||||
|
||||
|
||||
static void
|
||||
smbus_piix4_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (smbus_piix4_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define smbus_piix4_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
|
||||
static uint8_t
|
||||
smbus_piix4_read(uint16_t addr, void *priv)
|
||||
{
|
||||
smbus_piix4_t *dev = (smbus_piix4_t *) priv;
|
||||
uint8_t ret = 0x00;
|
||||
|
||||
switch (addr - dev->io_base) {
|
||||
case 0x00:
|
||||
ret = dev->stat;
|
||||
break;
|
||||
case 0x02:
|
||||
dev->index = 0; /* reading from this resets the block data index */
|
||||
ret = dev->ctl;
|
||||
break;
|
||||
case 0x03:
|
||||
ret = dev->cmd;
|
||||
break;
|
||||
case 0x04:
|
||||
ret = dev->addr;
|
||||
break;
|
||||
case 0x05:
|
||||
ret = dev->data0;
|
||||
break;
|
||||
case 0x06:
|
||||
ret = dev->data1;
|
||||
break;
|
||||
case 0x07:
|
||||
ret = dev->data[dev->index++];
|
||||
if (dev->index >= SMBUS_PIIX4_BLOCK_DATA_SIZE)
|
||||
dev->index = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
smbus_piix4_log("SMBus PIIX4: read(%02x) = %02x\n", addr, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
smbus_piix4_write(uint16_t addr, uint8_t val, void *priv)
|
||||
{
|
||||
smbus_piix4_t *dev = (smbus_piix4_t *) priv;
|
||||
uint8_t smbus_addr, smbus_read, prev_stat;
|
||||
uint16_t temp;
|
||||
|
||||
smbus_piix4_log("SMBus PIIX4: write(%02x, %02x)\n", addr, val);
|
||||
|
||||
prev_stat = dev->next_stat;
|
||||
dev->next_stat = 0;
|
||||
switch (addr - dev->io_base) {
|
||||
case 0x00:
|
||||
/* some status bits are reset by writing 1 to them */
|
||||
for (smbus_addr = 0x02; smbus_addr <= 0x10; smbus_addr = smbus_addr << 1) {
|
||||
if (val & smbus_addr)
|
||||
dev->stat = dev->stat & ~smbus_addr;
|
||||
}
|
||||
break;
|
||||
case 0x02:
|
||||
dev->ctl = val & ~(0x40); /* START always reads 0 */
|
||||
if (val & 0x02) { /* cancel an in-progress command if KILL is set */
|
||||
/* cancel only if a command is in progress */
|
||||
if (prev_stat) {
|
||||
dev->stat = 0x10; /* raise FAILED */
|
||||
timer_disable(&dev->response_timer);
|
||||
}
|
||||
}
|
||||
if (val & 0x40) { /* dispatch command if START is set */
|
||||
smbus_addr = (dev->addr >> 1);
|
||||
if (!smbus_has_device(smbus_addr)) {
|
||||
/* raise DEV_ERR if no device is at this address */
|
||||
dev->next_stat = 0x4;
|
||||
break;
|
||||
}
|
||||
smbus_read = (dev->addr & 0x01);
|
||||
|
||||
/* decode the 3-bit command protocol */
|
||||
switch ((val >> 2) & 0x7) {
|
||||
case 0x0: /* quick R/W */
|
||||
dev->next_stat = 0x2;
|
||||
break;
|
||||
case 0x1: /* byte R/W */
|
||||
if (smbus_read)
|
||||
dev->data0 = smbus_read_byte(smbus_addr);
|
||||
else
|
||||
smbus_write_byte(smbus_addr, dev->data0);
|
||||
dev->next_stat = 0x2;
|
||||
break;
|
||||
case 0x2: /* byte data R/W */
|
||||
if (smbus_read)
|
||||
dev->data0 = smbus_read_byte_cmd(smbus_addr, dev->cmd);
|
||||
else
|
||||
smbus_write_byte_cmd(smbus_addr, dev->cmd, dev->data0);
|
||||
dev->next_stat = 0x2;
|
||||
break;
|
||||
case 0x3: /* word data R/W */
|
||||
if (smbus_read) {
|
||||
temp = smbus_read_word_cmd(smbus_addr, dev->cmd);
|
||||
dev->data0 = (temp & 0xFF);
|
||||
dev->data1 = (temp >> 8);
|
||||
} else {
|
||||
temp = (dev->data1 << 8) | dev->data0;
|
||||
smbus_write_word_cmd(smbus_addr, dev->cmd, temp);
|
||||
}
|
||||
dev->next_stat = 0x2;
|
||||
break;
|
||||
case 0x5: /* block R/W */
|
||||
if (smbus_read)
|
||||
dev->data0 = smbus_read_block_cmd(smbus_addr, dev->cmd, dev->data, SMBUS_PIIX4_BLOCK_DATA_SIZE);
|
||||
else
|
||||
smbus_write_block_cmd(smbus_addr, dev->cmd, dev->data, dev->data0);
|
||||
dev->next_stat = 0x2;
|
||||
break;
|
||||
default:
|
||||
/* other command protocols have undefined behavior, but raise DEV_ERR to be safe */
|
||||
dev->next_stat = 0x4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x03:
|
||||
dev->cmd = val;
|
||||
break;
|
||||
case 0x04:
|
||||
dev->addr = val;
|
||||
break;
|
||||
case 0x05:
|
||||
dev->data0 = val;
|
||||
break;
|
||||
case 0x06:
|
||||
dev->data1 = val;
|
||||
break;
|
||||
case 0x07:
|
||||
dev->data[dev->index++] = val;
|
||||
if (dev->index >= SMBUS_PIIX4_BLOCK_DATA_SIZE)
|
||||
dev->index = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* if a status register update was given, dispatch it after 10ms to ensure nothing breaks */
|
||||
if (dev->next_stat) {
|
||||
dev->stat = 0x1; /* raise HOST_BUSY while waiting */
|
||||
timer_disable(&dev->response_timer);
|
||||
timer_set_delay_u64(&dev->response_timer, 10 * TIMER_USEC);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
smbus_piix4_response(void *priv)
|
||||
{
|
||||
smbus_piix4_t *dev = (smbus_piix4_t *) priv;
|
||||
|
||||
/* dispatch the status register update */
|
||||
dev->stat = dev->next_stat;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
smbus_piix4_remap(smbus_piix4_t *dev, uint16_t new_io_base, uint8_t enable)
|
||||
{
|
||||
if (dev->io_base != 0x0000)
|
||||
io_removehandler(dev->io_base, 0x10, smbus_piix4_read, NULL, NULL, smbus_piix4_write, NULL, NULL, dev);
|
||||
|
||||
dev->io_base = new_io_base;
|
||||
smbus_piix4_log("SMBus PIIX4: remap to %04Xh\n", dev->io_base);
|
||||
|
||||
if (enable && (dev->io_base != 0x0000))
|
||||
io_sethandler(dev->io_base, 0x10, smbus_piix4_read, NULL, NULL, smbus_piix4_write, NULL, NULL, dev);
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
smbus_piix4_init(const device_t *info)
|
||||
{
|
||||
smbus_piix4_t *dev = (smbus_piix4_t *) malloc(sizeof(smbus_piix4_t));
|
||||
memset(dev, 0, sizeof(smbus_piix4_t));
|
||||
|
||||
smbus_init();
|
||||
timer_add(&dev->response_timer, smbus_piix4_response, dev, 0);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
smbus_piix4_close(void *priv)
|
||||
{
|
||||
smbus_piix4_t *dev = (smbus_piix4_t *) priv;
|
||||
|
||||
free(dev);
|
||||
}
|
||||
|
||||
|
||||
const device_t piix4_smbus_device = {
|
||||
"PIIX4-compatible SMBus Host Controller",
|
||||
DEVICE_AT,
|
||||
0,
|
||||
smbus_piix4_init, smbus_piix4_close, NULL,
|
||||
NULL, NULL, NULL,
|
||||
NULL
|
||||
};
|
||||
Reference in New Issue
Block a user