mirror of
https://github.com/86Box/86Box.git
synced 2026-02-28 01:44:22 -07:00
448 lines
10 KiB
C
448 lines
10 KiB
C
/*
|
|
* ITE IT8702
|
|
*
|
|
* Authors: Tiseno100,
|
|
*
|
|
* Copyright 2022 Tiseno100.
|
|
*/
|
|
|
|
// NOTE: We don't really utilize fans because our emulated board doesn't use them.
|
|
// NOTE 2: LPT & Serial are aligned on a different way due to CUSL2-C not using them properly.
|
|
|
|
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <wchar.h>
|
|
#define HAVE_STDARG_H
|
|
#include <86box/86box.h>
|
|
#include <86box/io.h>
|
|
#include <86box/timer.h>
|
|
#include <86box/device.h>
|
|
|
|
#include <86box/keyboard.h>
|
|
#include <86box/lpt.h>
|
|
#include <86box/serial.h>
|
|
#include <86box/fdd.h>
|
|
#include <86box/fdc.h>
|
|
#include <86box/fdd_common.h>
|
|
#include <86box/port_92.h>
|
|
|
|
#include <86box/sio.h>
|
|
|
|
typedef struct
|
|
{
|
|
int ldn, unlock;
|
|
|
|
uint8_t index, regs[15], sw_lock,
|
|
enable[11],
|
|
b_addr[4][11],
|
|
irq[11],
|
|
dma[11],
|
|
d_spec[15][11];
|
|
|
|
fdc_t *fdc;
|
|
serial_t *uart[2];
|
|
} it8702_t;
|
|
|
|
#ifdef ENABLE_IT8702_LOG
|
|
int it8702_do_log = ENABLE_IT8702_LOG;
|
|
static void
|
|
it8702_log(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
if (it8702_do_log) {
|
|
va_start(ap, fmt);
|
|
pclog_ex(fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
}
|
|
#else
|
|
#define it8702_log(fmt, ...)
|
|
#endif
|
|
|
|
static void it8702_reset(void *priv);
|
|
|
|
|
|
static void
|
|
it8702_unlock(uint8_t val, it8702_t *dev)
|
|
{
|
|
if(val == 0x87) { /* The unlock mechanism of the CUSL2-C doesn't work as intended so we just unlock it with 87h only for now */
|
|
it8702_log("IT8702 Unlock: Unlocking\n");
|
|
dev->unlock = 1;
|
|
}
|
|
}
|
|
|
|
static void
|
|
it8702_sw_lock(uint8_t val, it8702_t *dev)
|
|
{
|
|
if(val & 0x80)
|
|
dev->sw_lock = val;
|
|
else
|
|
dev->sw_lock = 0;
|
|
}
|
|
|
|
static void
|
|
it8702_fdc(it8702_t *dev)
|
|
{
|
|
uint16_t base = ((dev->b_addr[0][0] & 0x0f) << 8) | (dev->b_addr[1][0] & 0xf8);
|
|
int irq = dev->irq[0];
|
|
int dma = dev->dma[0];
|
|
fdc_remove(dev->fdc);
|
|
|
|
if(dev->enable[0] & 1) {
|
|
it8702_log("IT8702 FDC: Enabled with Base: 0x%x IRQ: %d and DMA: %d\n", base, irq, dma);
|
|
fdc_set_base(dev->fdc, base);
|
|
fdc_set_irq(dev->fdc, irq);
|
|
fdc_set_dma_ch(dev->fdc, dma);
|
|
|
|
if(dev->d_spec[0][0] & 1)
|
|
fdc_writeprotect(dev->fdc);
|
|
|
|
fdc_update_drvrate(dev->fdc, 0, dev->d_spec[1][0] & 3);
|
|
fdc_update_drvrate(dev->fdc, 1, (dev->d_spec[1][0] >> 2) & 3);
|
|
}
|
|
}
|
|
|
|
static void
|
|
it8702_uart(int uart, it8702_t *dev)
|
|
{
|
|
uint16_t base = ((dev->b_addr[0][2 + uart] & 0x0f) << 8) | (dev->b_addr[1][2 + uart] & 0xf8);
|
|
int irq = dev->irq[2 + uart];
|
|
serial_remove(dev->uart[uart]);
|
|
|
|
if(dev->enable[2 + uart])
|
|
{
|
|
it8702_log("IT8702 Serial %c: Enabled with Base: 0x%x IRQ: %d\n", 'A' + uart, base, irq);
|
|
serial_setup(dev->uart[uart], base, irq);
|
|
serial_set_clock_src(dev->uart[uart], 24444444 / ((dev->d_spec[0][2 + uart] >> 1) ? 12 : 13));
|
|
}
|
|
}
|
|
|
|
static void
|
|
it8702_lpt(it8702_t *dev)
|
|
{
|
|
uint16_t base = ((dev->b_addr[0][1] & 0x0f) << 8) | (dev->b_addr[1][1] & 0xf8);
|
|
int irq = dev->irq[1];
|
|
lpt1_remove();
|
|
lpt2_remove();
|
|
|
|
if(dev->enable[1] & 1) {
|
|
it8702_log("IT8702 LPT1: Enabled with Base: 0x%x IRQ: %d\n", base, irq);
|
|
lpt1_init(base);
|
|
lpt1_irq(irq);
|
|
lpt2_irq(irq);
|
|
}
|
|
}
|
|
|
|
static void
|
|
it8702_ldn(it8702_t *dev)
|
|
{
|
|
switch(dev->ldn)
|
|
{
|
|
case 0:
|
|
it8702_fdc(dev);
|
|
break;
|
|
|
|
case 1:
|
|
it8702_lpt(dev);
|
|
break;
|
|
|
|
case 2 ... 3:
|
|
it8702_uart((dev->ldn == 3), dev);
|
|
break;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
static void
|
|
it8702_write(uint16_t addr, uint8_t val, void *priv)
|
|
{
|
|
it8702_t *dev = (it8702_t *) priv;
|
|
|
|
if(addr == 0x2e) {
|
|
if(dev->unlock)
|
|
dev->index = val;
|
|
else
|
|
it8702_unlock(val, dev);
|
|
}
|
|
else if(addr == 0x2f) {
|
|
switch(dev->index)
|
|
{
|
|
/* Global Registers */
|
|
case 0x02: /* Configure Control */
|
|
if(val & 2) {
|
|
it8702_log("IT8702 Unlock: Locking\n");
|
|
dev->unlock = 0;
|
|
}
|
|
|
|
if(val & 1) {
|
|
it8702_log("IT8702: Resetting\n");
|
|
it8702_reset(dev);
|
|
}
|
|
break;
|
|
|
|
case 0x07:
|
|
dev->ldn = val;
|
|
break;
|
|
|
|
case 0x23:
|
|
if(!(dev->sw_lock & 0x80))
|
|
dev->regs[dev->index - 0x20] = val & 0xd9;
|
|
break;
|
|
|
|
case 0x24:
|
|
dev->regs[dev->index - 0x20] = val & 0x00;
|
|
break;
|
|
|
|
case 0x25 ... 0x28:
|
|
if(dev->ldn == 7)
|
|
dev->regs[dev->index - 0x20] = val;
|
|
break;
|
|
|
|
case 0x29:
|
|
if(dev->ldn == 7)
|
|
dev->regs[dev->index - 0x20] = val & 0x3f;
|
|
break;
|
|
|
|
case 0x2a:
|
|
if(dev->ldn == 7)
|
|
dev->regs[dev->index - 0x20] = val & 0x7f;
|
|
break;
|
|
|
|
case 0x2b:
|
|
if(!(dev->sw_lock & 0x80)) {
|
|
dev->regs[dev->index - 0x20] = val;
|
|
it8702_sw_lock(val, dev);
|
|
}
|
|
break;
|
|
|
|
case 0x2c:
|
|
dev->regs[dev->index - 0x20] = val & 0x1c;
|
|
break;
|
|
|
|
case 0x30:
|
|
if(dev->ldn < 11)
|
|
dev->enable[dev->ldn] = val & 1;
|
|
|
|
it8702_ldn(dev);
|
|
break;
|
|
|
|
case 0x60 ... 0x63:
|
|
if(dev->ldn < 11)
|
|
dev->b_addr[dev->index & 3][dev->ldn] = val;
|
|
|
|
it8702_ldn(dev);
|
|
break;
|
|
|
|
case 0x70:
|
|
if(dev->ldn < 11)
|
|
dev->irq[dev->ldn] = val & 0x0f;
|
|
|
|
it8702_ldn(dev);
|
|
break;
|
|
|
|
case 0x74:
|
|
if(dev->ldn < 11)
|
|
dev->dma[dev->ldn] = val & 0x0f;
|
|
|
|
it8702_ldn(dev);
|
|
break;
|
|
|
|
case 0xf0 ... 0xf0:
|
|
if(dev->ldn < 11)
|
|
dev->d_spec[dev->index & 0x0f][dev->ldn] = val;
|
|
|
|
it8702_ldn(dev);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static uint8_t
|
|
it8702_read(uint16_t addr, void *priv)
|
|
{
|
|
it8702_t *dev = (it8702_t *) priv;
|
|
|
|
if(addr == 0x2e) {
|
|
return dev->index;
|
|
} else if(addr == 0x2f) {
|
|
if(dev->index == 0x02) { /* The Configure Control register is Write Only */
|
|
return 0xff;
|
|
} else {
|
|
switch(dev->index) {
|
|
case 0x07:
|
|
return dev->ldn;
|
|
|
|
case 0x20 ... 0x2f:
|
|
return dev->regs[dev->index - 0x20];
|
|
|
|
case 0x30:
|
|
if(dev->ldn < 11)
|
|
return dev->enable[dev->ldn];
|
|
else
|
|
return 0xff;
|
|
|
|
case 0x60 ... 0x63:
|
|
if(dev->ldn < 11)
|
|
return dev->b_addr[dev->index & 3][dev->ldn];
|
|
else
|
|
return 0xff;
|
|
|
|
case 0x70:
|
|
if(dev->ldn < 11)
|
|
return dev->irq[dev->ldn];
|
|
else
|
|
return 0xff;
|
|
|
|
case 0x74:
|
|
if(dev->ldn < 11)
|
|
return dev->dma[dev->ldn];
|
|
else
|
|
return 0xff;
|
|
|
|
case 0xf0 ... 0xff:
|
|
if(dev->ldn < 11)
|
|
return dev->d_spec[dev->index & 0x0f][dev->ldn];
|
|
else
|
|
return 0xff;
|
|
|
|
default:
|
|
return 0xff;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0xff;
|
|
}
|
|
|
|
|
|
static void
|
|
it8702_reset(void *priv)
|
|
{
|
|
it8702_t *dev = (it8702_t *) priv;
|
|
|
|
dev->ldn = 0;
|
|
dev->sw_lock = 0; // Needs implementation
|
|
|
|
dev->unlock = 0; /* Lock the chip */
|
|
|
|
/* Global Super I/O Registers */
|
|
dev->regs[0x00] = 0x87;
|
|
dev->regs[0x01] = 0x02;
|
|
dev->regs[0x02] = 0x07;
|
|
|
|
/* Floppy Disk Controller */
|
|
dev->b_addr[0][0] = 0x03;
|
|
dev->b_addr[1][0] = 0xf0;
|
|
dev->irq[0] = 0x06;
|
|
dev->dma[0] = 0x02;
|
|
fdc_reset(dev->fdc);
|
|
it8702_fdc(dev);
|
|
|
|
/* LPT */
|
|
dev->b_addr[0][1] = 0x03;
|
|
dev->b_addr[1][1] = 0x78;
|
|
dev->irq[1] = 0x07;
|
|
dev->dma[1] = 0x03;
|
|
dev->d_spec[0][1] = 0x03;
|
|
it8702_lpt(dev);
|
|
|
|
/* UART Serial A */
|
|
dev->b_addr[0][2] = 0x02;
|
|
dev->b_addr[1][2] = 0xf8;
|
|
dev->irq[2] = 0x04;
|
|
dev->d_spec[1][2] = 0x50;
|
|
dev->d_spec[3][2] = 0x7f;
|
|
it8702_uart(0, dev);
|
|
|
|
/* UART Serial B */
|
|
dev->b_addr[0][3] = 0x02;
|
|
dev->b_addr[1][3] = 0x78;
|
|
dev->irq[3] = 0x04;
|
|
dev->d_spec[1][3] = 0x50;
|
|
dev->d_spec[3][3] = 0x7f;
|
|
it8702_uart(1, dev);
|
|
|
|
/* FAN Handler */
|
|
dev->b_addr[0][4] = 0x02;
|
|
dev->b_addr[1][4] = 0x90;
|
|
dev->b_addr[2][4] = 0x02;
|
|
dev->b_addr[3][4] = 0x30;
|
|
dev->irq[4] = 0x09;
|
|
|
|
/* Keyboard Controller */
|
|
dev->b_addr[3][5] = 0x64;
|
|
dev->irq[5] = 0x01;
|
|
|
|
/* PS/2 Mouse */
|
|
dev->irq[6] = 0x0c;
|
|
|
|
/* MIDI */
|
|
dev->b_addr[0][8] = 0x03;
|
|
dev->irq[8] = 0x0a;
|
|
|
|
/* Gameport */
|
|
dev->b_addr[0][9] = 0x02;
|
|
dev->b_addr[1][9] = 0x10;
|
|
|
|
/* IR */
|
|
dev->b_addr[0][10] = 0x03;
|
|
dev->b_addr[1][10] = 0x10;
|
|
dev->irq[10] = 0x0b;
|
|
}
|
|
|
|
|
|
static void
|
|
it8702_close(void *priv)
|
|
{
|
|
it8702_t *dev = (it8702_t *) priv;
|
|
|
|
free(dev);
|
|
}
|
|
|
|
|
|
static void *
|
|
it8702_init(const device_t *info)
|
|
{
|
|
it8702_t *dev = (it8702_t *) malloc(sizeof(it8702_t));
|
|
memset(dev, 0, sizeof(it8702_t));
|
|
|
|
/* FDC */
|
|
dev->fdc = device_add(&fdc_at_smc_device);
|
|
|
|
/* Keyboard Controller */
|
|
device_add(&keyboard_ps2_ami_pci_device);
|
|
|
|
/* Port 92h */
|
|
device_add(&port_92_pci_device);
|
|
|
|
/* Serial */
|
|
dev->uart[0] = device_add_inst(&ns16650_device, 1);
|
|
dev->uart[1] = device_add_inst(&ns16550_device, 2);
|
|
|
|
io_sethandler(info->local, 2, it8702_read, NULL, NULL, it8702_write, NULL, NULL, dev); /* Ports 2Eh-2Fh: ITE IT8702 */
|
|
|
|
it8702_reset(dev);
|
|
|
|
return dev;
|
|
}
|
|
|
|
const device_t it8702_device = {
|
|
.name = "ITE IT8702",
|
|
.internal_name = "it8702",
|
|
.flags = 0,
|
|
.local = 0x2e,
|
|
.init = it8702_init,
|
|
.close = it8702_close,
|
|
.reset = it8702_reset,
|
|
{ .available = NULL },
|
|
.speed_changed = NULL,
|
|
.force_redraw = NULL,
|
|
.config = NULL
|
|
};
|