More reorganization and finally merged the two makefiles.

This commit is contained in:
OBattler
2020-06-13 12:32:09 +02:00
parent 4e48943ad5
commit ca55e2a12a
35 changed files with 3645 additions and 4372 deletions

365
src/device/bugger.c Normal file
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

760
src/device/isartc.c Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

814
src/device/keyboard_xt.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
};