A slight reorganization of the source tree and fixed a warning in disk/mo.c.

This commit is contained in:
OBattler
2020-06-13 10:17:57 +02:00
parent d94db23979
commit 9c6f0d806e
37 changed files with 40 additions and 140 deletions

564
src/chipset/intel_420ex.c Normal file
View File

@@ -0,0 +1,564 @@
/*
* 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 Intel 82420EX chipset that acts as both the
* northbridge and the southbridge.
*
*
*
* Authors: Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2020 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/hdc_ide.h>
#include <86box/hdc.h>
#include <86box/machine.h>
#include <86box/chipset.h>
#define MEM_STATE_SHADOW_R 0x01
#define MEM_STATE_SHADOW_W 0x02
#define MEM_STATE_SMRAM 0x04
typedef struct
{
uint8_t id, smram_locked,
regs[256];
uint16_t timer_base,
timer_latch;
double fast_off_period;
pc_timer_t timer, fast_off_timer;
apm_t * apm;
port_92_t * port_92;
} i420ex_t;
#ifdef ENABLE_I420EX_LOG
int i420ex_do_log = ENABLE_I420EX_LOG;
static void
i420ex_log(const char *fmt, ...)
{
va_list ap;
if (i420ex_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
#define i420ex_log(fmt, ...)
#endif
static void
i420ex_map(uint32_t addr, uint32_t size, int state)
{
switch (state & 3) {
case 0:
mem_set_mem_state(addr, size, MEM_READ_EXTANY | MEM_WRITE_EXTANY);
break;
case 1:
mem_set_mem_state(addr, size, MEM_READ_INTERNAL | MEM_WRITE_EXTANY);
break;
case 2:
mem_set_mem_state(addr, size, MEM_READ_EXTANY | MEM_WRITE_INTERNAL);
break;
case 3:
mem_set_mem_state(addr, size, MEM_READ_INTERNAL | MEM_WRITE_INTERNAL);
break;
}
flushmmucache_nopc();
}
static void
i420ex_smram_map(i420ex_t *dev, int smm, uint32_t addr, uint32_t size, int is_smram)
{
mem_set_mem_state_smram(smm, addr, size, is_smram);
flushmmucache();
}
static void
i420ex_smram_handler_phase0(i420ex_t *dev)
{
/* Disable low extended SMRAM. */
i420ex_smram_map(dev, 0, 0xa0000, 0x60000, 0);
i420ex_smram_map(dev, 1, 0xa0000, 0x60000, 0);
}
static void
i420ex_smram_handler_phase1(i420ex_t *dev)
{
uint8_t *regs = (uint8_t *) dev->regs;
uint32_t base = 0x000a0000;
uint32_t size = 0x00010000;
switch (regs[0x70] & 0x07) {
case 0: case 1:
default:
base = size = 0x00000000;
break;
case 2:
base = 0x000a0000;
smram[0].host_base = 0x000a0000;
smram[0].ram_base = 0x000a0000;
break;
case 3:
base = 0x000b0000;
smram[0].host_base = 0x000b0000;
smram[0].ram_base = 0x000b0000;
break;
case 4:
base = 0x000c0000;
smram[0].host_base = 0x000c0000;
smram[0].ram_base = 0x000a0000;
break;
case 5:
base = 0x000d0000;
smram[0].host_base = 0x000d0000;
smram[0].ram_base = 0x000a0000;
break;
case 6:
base = 0x000e0000;
smram[0].host_base = 0x000e0000;
smram[0].ram_base = 0x000a0000;
break;
case 7:
base = 0x000f0000;
smram[0].host_base = 0x000f0000;
smram[0].ram_base = 0x000a0000;
break;
}
if (base != 0x00000000) {
mem_mapping_set_addr(&ram_smram_mapping[0], smram[0].host_base, 0x00010000);
mem_mapping_set_exec(&ram_smram_mapping[0], ram + smram[0].ram_base);
/* If OSS = 1 and LSS = 0, extended SMRAM is visible outside SMM. */
i420ex_smram_map(dev, 0, base, size, (regs[0x70] & 0x70) == 0x40);
/* If the register is set accordingly, disable the mapping also in SMM. */
i420ex_smram_map(dev, 1, base, size, !(regs[0x70] & 0x20));
}
}
static void
i420ex_write(int func, int addr, uint8_t val, void *priv)
{
i420ex_t *dev = (i420ex_t *) priv;
if (func > 0)
return;
if (((addr >= 0x0f) && (addr < 0x4c)) && (addr != 0x40))
return;
/* The IB (original) variant of the I420EX has no PCI IRQ steering. */
if ((addr >= 0x60) && (addr <= 0x63) && (dev->id < 0x03))
return;
switch (addr) {
case 0x05:
dev->regs[addr] = (val & 0x01);
break;
case 0x07:
dev->regs[addr] &= ~(val & 0xf0);
break;
case 0x40:
dev->regs[addr] = (val & 0x7f);
break;
case 0x44:
dev->regs[addr] = (val & 0x07);
break;
case 0x48:
dev->regs[addr] = (val & 0x3f);
#ifdef USE_420EX_IDE
ide_pri_disable();
switch (val & 0x03) {
case 0x01:
ide_set_base(0, 0x01f0);
ide_set_side(0, 0x03f6);
ide_pri_enable();
break;
case 0x02:
ide_set_base(0, 0x0170);
ide_set_side(0, 0x0376);
ide_pri_enable();
break;
}
#endif
break;
case 0x49: case 0x53:
dev->regs[addr] = (val & 0x1f);
break;
case 0x4c: case 0x51:
case 0x57:
case 0x60: case 0x61: case 0x62: case 0x63:
case 0x64:
case 0x68: case 0x69:
dev->regs[addr] = val;
if (addr == 0x4c) {
dma_alias_remove();
if (!(val & 0x80))
dma_alias_set();
}
break;
case 0x4d:
dev->regs[addr] = (dev->regs[addr] & 0xef) | (val & 0x10);
break;
case 0x4e:
dev->regs[addr] = (val & 0xf7);
break;
case 0x50:
dev->regs[addr] = (val & 0x0f);
break;
case 0x52:
dev->regs[addr] = (val & 0x7f);
break;
case 0x56:
dev->regs[addr] = (val & 0x3e);
break;
case 0x59: /* PAM0 */
if ((dev->regs[0x59] ^ val) & 0xf0) {
i420ex_map(0xf0000, 0x10000, val >> 4);
shadowbios = (val & 0x10);
}
dev->regs[0x59] = val & 0xf0;
break;
case 0x5a: /* PAM1 */
if ((dev->regs[0x5a] ^ val) & 0x0f)
i420ex_map(0xc0000, 0x04000, val & 0xf);
if ((dev->regs[0x5a] ^ val) & 0xf0)
i420ex_map(0xc4000, 0x04000, val >> 4);
dev->regs[0x5a] = val;
break;
case 0x5b: /*PAM2 */
if ((dev->regs[0x5b] ^ val) & 0x0f)
i420ex_map(0xc8000, 0x04000, val & 0xf);
if ((dev->regs[0x5b] ^ val) & 0xf0)
i420ex_map(0xcc000, 0x04000, val >> 4);
dev->regs[0x5b] = val;
break;
case 0x5c: /*PAM3 */
if ((dev->regs[0x5c] ^ val) & 0x0f)
i420ex_map(0xd0000, 0x04000, val & 0xf);
if ((dev->regs[0x5c] ^ val) & 0xf0)
i420ex_map(0xd4000, 0x04000, val >> 4);
dev->regs[0x5c] = val;
break;
case 0x5d: /* PAM4 */
if ((dev->regs[0x5d] ^ val) & 0x0f)
i420ex_map(0xd8000, 0x04000, val & 0xf);
if ((dev->regs[0x5d] ^ val) & 0xf0)
i420ex_map(0xdc000, 0x04000, val >> 4);
dev->regs[0x5d] = val;
break;
case 0x5e: /* PAM5 */
if ((dev->regs[0x5e] ^ val) & 0x0f)
i420ex_map(0xe0000, 0x04000, val & 0xf);
if ((dev->regs[0x5e] ^ val) & 0xf0)
i420ex_map(0xe4000, 0x04000, val >> 4);
dev->regs[0x5e] = val;
break;
case 0x5f: /* PAM6 */
if ((dev->regs[0x5f] ^ val) & 0x0f)
i420ex_map(0xe8000, 0x04000, val & 0xf);
if ((dev->regs[0x5f] ^ val) & 0xf0)
i420ex_map(0xec000, 0x04000, val >> 4);
dev->regs[0x5f] = val;
break;
case 0x66: case 0x67:
i420ex_log("Set IRQ routing: INT %c -> %02X\n", 0x41 + (addr & 0x01), val);
dev->regs[addr] = val & 0x8f;
if (val & 0x80)
pci_set_irq_routing(PCI_INTA + (addr & 0x01), PCI_IRQ_DISABLED);
else
pci_set_irq_routing(PCI_INTA + (addr & 0x01), val & 0xf);
break;
case 0x70: /* SMRAM */
i420ex_smram_handler_phase0(dev);
if (dev->smram_locked)
dev->regs[0x70] = (dev->regs[0x70] & 0xdf) | (val & 0x20);
else {
dev->regs[0x70] = (dev->regs[0x70] & 0x88) | (val & 0x77);
dev->smram_locked = (val & 0x10);
if (dev->smram_locked)
dev->regs[0x70] &= 0xbf;
}
i420ex_smram_handler_phase1(dev);
break;
case 0xa0:
dev->regs[addr] = val & 0x1f;
apm_set_do_smi(dev->apm, !!(val & 0x01) && !!(dev->regs[0xa2] & 0x80));
switch ((val & 0x18) >> 3) {
case 0x00:
dev->fast_off_period = PCICLK * 32768.0 * 60000.0;
break;
case 0x01:
default:
dev->fast_off_period = 0.0;
break;
case 0x02:
dev->fast_off_period = PCICLK;
break;
case 0x03:
dev->fast_off_period = PCICLK * 32768.0;
break;
}
cpu_fast_off_count = dev->regs[0xa8] + 1;
timer_disable(&dev->fast_off_timer);
if (dev->fast_off_period != 0.0)
timer_on_auto(&dev->fast_off_timer, dev->fast_off_period);
break;
case 0xa2:
dev->regs[addr] = val & 0xff;
apm_set_do_smi(dev->apm, !!(dev->regs[0xa0] & 0x01) && !!(val & 0x80));
break;
case 0xaa: case 0xac: case 0xae:
if (dev->id == 0x03)
dev->regs[addr] = val & 0xff;
break;
case 0xa4:
dev->regs[addr] = val & 0xfb;
cpu_fast_off_flags = (cpu_fast_off_flags & 0xffffff00) | dev->regs[addr];
break;
case 0xa5:
dev->regs[addr] = val;
cpu_fast_off_flags = (cpu_fast_off_flags & 0xffff00ff) | (dev->regs[addr] << 8);
break;
case 0xa7:
dev->regs[addr] = val & 0xe0;
cpu_fast_off_flags = (cpu_fast_off_flags & 0x00ffffff) | (dev->regs[addr] << 24);
break;
case 0xa8:
dev->regs[addr] = val & 0xff;
cpu_fast_off_val = val;
cpu_fast_off_count = val + 1;
timer_disable(&dev->fast_off_timer);
if (dev->fast_off_period != 0.0)
timer_on_auto(&dev->fast_off_timer, dev->fast_off_period);
break;
}
}
static uint8_t
i420ex_read(int func, int addr, void *priv)
{
i420ex_t *dev = (i420ex_t *) priv;
uint8_t ret;
ret = 0xff;
if (func == 0)
ret = dev->regs[addr];
return ret;
}
static void
i420ex_reset_hard(void *priv)
{
i420ex_t *dev = (i420ex_t *) priv;
memset(dev->regs, 0, 256);
dev->regs[0x00] = 0x86; dev->regs[0x01] = 0x80; /*Intel*/
dev->regs[0x02] = 0x86; dev->regs[0x03] = 0x04; /*82378IB (I420EX)*/
dev->regs[0x04] = 0x07;
dev->regs[0x07] = 0x02;
dev->regs[0x08] = dev->id;
dev->regs[0x4c] = 0x4d;
dev->regs[0x4e] = 0x03;
/* Bits 2:1 of register 50h are 00 is 25 MHz, and 01 if 33 MHz, 10 and 11 are reserved. */
if (cpu_busspeed >= 33333333)
dev->regs[0x50] |= 0x02;
dev->regs[0x51] = 0x80;
dev->regs[0x60] = dev->regs[0x61] = dev->regs[0x62] = dev->regs[0x63] = dev->regs[0x64] = 0x01;
dev->regs[0x66] = 0x80; dev->regs[0x67] = 0x80;
dev->regs[0x69] = 0x02;
dev->regs[0xa0] = 0x08;
dev->regs[0xa8] = 0x0f;
mem_set_mem_state(0x000a0000, 0x00060000, MEM_READ_EXTANY | MEM_WRITE_EXTANY);
mem_set_mem_state_smm(0x000a0000, 0x00060000, MEM_READ_EXTANY | MEM_WRITE_EXTANY);
pci_set_irq_routing(PCI_INTA, PCI_IRQ_DISABLED);
pci_set_irq_routing(PCI_INTB, PCI_IRQ_DISABLED);
pci_set_irq_routing(PCI_INTC, PCI_IRQ_DISABLED);
pci_set_irq_routing(PCI_INTD, PCI_IRQ_DISABLED);
}
static void
i420ex_apm_out(uint16_t port, uint8_t val, void *p)
{
i420ex_t *dev = (i420ex_t *) p;
if (dev->apm->do_smi)
dev->regs[0xaa] |= 0x80;
}
static void
i420ex_fast_off_count(void *priv)
{
i420ex_t *dev = (i420ex_t *) priv;
cpu_fast_off_count--;
if (cpu_fast_off_count == 0) {
smi_line = 1;
dev->regs[0xaa] |= 0x20;
cpu_fast_off_count = dev->regs[0xa8] + 1;
}
timer_on_auto(&dev->fast_off_timer, dev->fast_off_period);
}
static void
i420ex_reset(void *p)
{
i420ex_t *dev = (i420ex_t *) p;
int i;
for (i = 0; i < 7; i++)
i420ex_write(0, 0x59 + i, 0x00, p);
for (i = 0; i <= 4; i++)
i420ex_write(0, 0x60 + i, 0x01, p);
dev->regs[0x70] &= 0xef; /* Forcibly unlock the SMRAM register. */
dev->smram_locked = 0;
i420ex_write(0, 0x70, 0x00, p);
mem_set_mem_state(0x000a0000, 0x00060000, MEM_READ_EXTANY | MEM_WRITE_EXTANY);
mem_set_mem_state_smm(0x000a0000, 0x00060000, MEM_READ_EXTANY | MEM_WRITE_EXTANY);
i420ex_write(0, 0xa0, 0x08, p);
i420ex_write(0, 0xa2, 0x00, p);
i420ex_write(0, 0xa4, 0x00, p);
i420ex_write(0, 0xa5, 0x00, p);
i420ex_write(0, 0xa6, 0x00, p);
i420ex_write(0, 0xa7, 0x00, p);
i420ex_write(0, 0xa8, 0x0f, p);
}
static void
i420ex_close(void *p)
{
i420ex_t *dev = (i420ex_t *)p;
free(dev);
}
static void
i420ex_speed_changed(void *priv)
{
i420ex_t *dev = (i420ex_t *) priv;
int te;
te = timer_is_enabled(&dev->timer);
timer_disable(&dev->timer);
if (te)
timer_set_delay_u64(&dev->timer, ((uint64_t) dev->timer_latch) * TIMER_USEC);
if (dev->id == 0x03) {
te = timer_is_enabled(&dev->fast_off_timer);
timer_stop(&dev->fast_off_timer);
if (te)
timer_on_auto(&dev->fast_off_timer, dev->fast_off_period);
}
}
static void *
i420ex_init(const device_t *info)
{
i420ex_t *dev = (i420ex_t *) malloc(sizeof(i420ex_t));
memset(dev, 0, sizeof(i420ex_t));
pci_add_card(PCI_ADD_NORTHBRIDGE, i420ex_read, i420ex_write, dev);
dev->id = info->local;
timer_add(&dev->fast_off_timer, i420ex_fast_off_count, dev, 0);
i420ex_reset_hard(dev);
cpu_fast_off_flags = 0x00000000;
cpu_fast_off_val = dev->regs[0xa8];
cpu_fast_off_count = cpu_fast_off_val + 1;
dev->apm = device_add(&apm_pci_device);
/* APM intercept handler to update 82420EX SMI status on APM SMI. */
io_sethandler(0x00b2, 0x0001, NULL, NULL, NULL, i420ex_apm_out, NULL, NULL, dev);
dev->port_92 = device_add(&port_92_pci_device);
dma_alias_set();
#ifdef USE_420EX_IDE
device_add(&ide_pci_device);
ide_pri_disable();
#else
device_add(&ide_pci_2ch_device);
#endif
return dev;
}
const device_t i420ex_device =
{
"Intel 82420EX",
DEVICE_PCI,
0x00,
i420ex_init,
i420ex_close,
i420ex_reset,
NULL,
i420ex_speed_changed,
NULL,
NULL
};

View File

@@ -10,8 +10,7 @@
*
*
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
* Authors: Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2019,2020 Miran Grca.
*/
@@ -1659,9 +1658,9 @@ const device_t i430fx_device =
};
const device_t i430fx_pb640_device =
const device_t i430fx_rev02_device =
{
"Intel SB82437FX-66 (PB640)",
"Intel SB82437FX-66 (Rev. 02)",
DEVICE_PCI,
0x0200 | INTEL_430FX,
i4x0_init,

1388
src/chipset/intel_piix.c Normal file

File diff suppressed because it is too large Load Diff

569
src/chipset/intel_sio.c Normal file
View File

@@ -0,0 +1,569 @@
/*
* 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 Intel System I/O PCI chip.
*
*
*
* 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>
#include <86box/chipset.h>
typedef struct
{
uint8_t id,
regs[256];
uint16_t timer_base,
timer_latch;
double fast_off_period;
pc_timer_t timer, fast_off_timer;
apm_t * apm;
port_92_t * port_92;
} sio_t;
#ifdef ENABLE_SIO_LOG
int sio_do_log = ENABLE_SIO_LOG;
static void
sio_log(const char *fmt, ...)
{
va_list ap;
if (sio_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
#define sio_log(fmt, ...)
#endif
static void
sio_timer_write(uint16_t addr, uint8_t val, void *priv)
{
sio_t *dev = (sio_t *) priv;
if (!(addr & 0x0002)) {
if (addr & 0x0001)
dev->timer_latch = (dev->timer_latch & 0xff) | (val << 8);
else
dev->timer_latch = (dev->timer_latch & 0xff00) | val;
timer_set_delay_u64(&dev->timer, ((uint64_t) dev->timer_latch) * TIMER_USEC);
}
}
static void
sio_timer_writew(uint16_t addr, uint16_t val, void *priv)
{
sio_t *dev = (sio_t *) priv;
if (!(addr & 0x0002)) {
dev->timer_latch = val;
timer_set_delay_u64(&dev->timer, ((uint64_t) dev->timer_latch) * TIMER_USEC);
}
}
static uint8_t
sio_timer_read(uint16_t addr, void *priv)
{
sio_t *dev = (sio_t *) priv;
uint16_t sio_timer_latch;
uint8_t ret = 0xff;
if (!(addr & 0x0002)) {
sub_cycles((int)(PITCONST >> 32));
sio_timer_latch = timer_get_remaining_us(&dev->timer);
if (addr & 0x0001)
ret = sio_timer_latch >> 8;
else
ret = sio_timer_latch & 0xff;
}
return ret;
}
static uint16_t
sio_timer_readw(uint16_t addr, void *priv)
{
sio_t *dev = (sio_t *) priv;
uint16_t ret = 0xffff;
if (!(addr & 0x0002)) {
sub_cycles((int)(PITCONST >> 32));
ret = timer_get_remaining_us(&dev->timer);
}
return ret;
}
static void
sio_write(int func, int addr, uint8_t val, void *priv)
{
sio_t *dev = (sio_t *) priv;
uint8_t old;
if (func > 0)
return;
if (((addr >= 0x0f) && (addr < 0x4c)) && (addr != 0x40))
return;
/* The IB (original) variant of the SIO has no PCI IRQ steering. */
if ((addr >= 0x60) && (addr <= 0x63) && (dev->id < 0x03))
return;
old = dev->regs[addr];
switch (addr) {
case 0x04: /*Command register*/
if (dev->id == 0x03)
dev->regs[addr] = (dev->regs[addr] & 0xf7) | (val & 0x08);
break;
case 0x07:
dev->regs[addr] &= ~(val & 0x38);
break;
case 0x40:
if (dev->id == 0x03) {
dev->regs[addr] = (val & 0x7f);
if (!((val ^ old) & 0x40))
return;
dma_alias_remove();
if (!(val & 0x40))
dma_alias_set();
} else
dev->regs[addr] = (val & 0x3f);
break;
case 0x41: case 0x44:
dev->regs[addr] = (val & 0x1f);
break;
case 0x42:
if (dev->id == 0x03)
dev->regs[addr] = val;
else
dev->regs[addr] = (val & 0x77);
break;
case 0x43:
if (dev->id == 0x03)
dev->regs[addr] = (val & 0x01);
break;
case 0x45: case 0x46:
case 0x47: case 0x48:
case 0x49: case 0x4a:
case 0x4b: case 0x4e:
case 0x54: case 0x55:
case 0x56:
dev->regs[addr] = val;
break;
case 0x4c: case 0x4d:
dev->regs[addr] = (val & 0x7f);
break;
case 0x4f:
dev->regs[addr] = val;
if (!((val ^ old) & 0x40))
return;
port_92_remove(dev->port_92);
if (val & 0x40)
port_92_add(dev->port_92);
break;
case 0x57:
dev->regs[addr] = val;
dma_remove_sg();
dma_set_sg_base(val);
break;
case 0x60: case 0x61: case 0x62: case 0x63:
if (dev->id == 0x03) {
sio_log("Set IRQ routing: INT %c -> %02X\n", 0x41 + (addr & 0x03), val);
dev->regs[addr] = val & 0x8f;
if (val & 0x80)
pci_set_irq_routing(PCI_INTA + (addr & 0x03), PCI_IRQ_DISABLED);
else
pci_set_irq_routing(PCI_INTA + (addr & 0x03), val & 0xf);
}
break;
case 0x80:
case 0x81:
if (addr == 0x80)
dev->regs[addr] = val & 0xfd;
else
dev->regs[addr] = val;
if (dev->timer_base & 0x01) {
io_removehandler(dev->timer_base & 0xfffc, 0x0004,
sio_timer_read, sio_timer_readw, NULL,
sio_timer_write, sio_timer_writew, NULL, dev);
}
dev->timer_base = (dev->regs[0x81] << 8) | (dev->regs[0x80] & 0xfd);
if (dev->timer_base & 0x01) {
io_sethandler(dev->timer_base & 0xfffc, 0x0004,
sio_timer_read, sio_timer_readw, NULL,
sio_timer_write, sio_timer_writew, NULL, dev);
}
break;
case 0xa0:
if (dev->id == 0x03) {
dev->regs[addr] = val & 0x1f;
apm_set_do_smi(dev->apm, !!(val & 0x01) && !!(dev->regs[0xa2] & 0x80));
switch ((val & 0x18) >> 3) {
case 0x00:
dev->fast_off_period = PCICLK * 32768.0 * 60000.0;
break;
case 0x01:
default:
dev->fast_off_period = 0.0;
break;
case 0x02:
dev->fast_off_period = PCICLK;
break;
case 0x03:
dev->fast_off_period = PCICLK * 32768.0;
break;
}
cpu_fast_off_count = dev->regs[0xa8] + 1;
timer_disable(&dev->fast_off_timer);
if (dev->fast_off_period != 0.0)
timer_on_auto(&dev->fast_off_timer, dev->fast_off_period);
}
break;
case 0xa2:
if (dev->id == 0x03) {
dev->regs[addr] = val & 0xff;
apm_set_do_smi(dev->apm, !!(dev->regs[0xa0] & 0x01) && !!(val & 0x80));
}
break;
case 0xaa: case 0xac: case 0xae:
if (dev->id == 0x03)
dev->regs[addr] = val & 0xff;
break;
case 0xa4:
if (dev->id == 0x03) {
dev->regs[addr] = val & 0xfb;
cpu_fast_off_flags = (cpu_fast_off_flags & 0xffffff00) | dev->regs[addr];
}
break;
case 0xa5:
if (dev->id == 0x03) {
dev->regs[addr] = val & 0xff;
cpu_fast_off_flags = (cpu_fast_off_flags & 0xffff00ff) | (dev->regs[addr] << 8);
}
break;
case 0xa7:
if (dev->id == 0x03) {
dev->regs[addr] = val & 0xa0;
cpu_fast_off_flags = (cpu_fast_off_flags & 0x00ffffff) | (dev->regs[addr] << 24);
}
break;
case 0xa8:
dev->regs[addr] = val & 0xff;
cpu_fast_off_val = val;
cpu_fast_off_count = val + 1;
timer_disable(&dev->fast_off_timer);
if (dev->fast_off_period != 0.0)
timer_on_auto(&dev->fast_off_timer, dev->fast_off_period);
break;
}
}
static uint8_t
sio_read(int func, int addr, void *priv)
{
sio_t *dev = (sio_t *) priv;
uint8_t ret;
ret = 0xff;
if (func == 0)
ret = dev->regs[addr];
return ret;
}
static void
sio_config_write(uint16_t addr, uint8_t val, void *priv)
{
}
static uint8_t
sio_config_read(uint16_t port, void *priv)
{
uint8_t ret = 0x00;
switch (port & 0x000f) {
case 3:
ret = 0xff;
break;
case 5:
ret = 0xd3;
switch (cpu_pci_speed) {
case 20000000:
ret |= 0x0c;
break;
case 25000000:
default:
ret |= 0x00;
break;
case 30000000:
ret |= 0x08;
break;
case 33333333:
ret |= 0x04;
break;
}
break;
}
return ret;
}
static void
sio_reset_hard(void *priv)
{
sio_t *dev = (sio_t *) priv;
memset(dev->regs, 0, 256);
dev->regs[0x00] = 0x86; dev->regs[0x01] = 0x80; /*Intel*/
dev->regs[0x02] = 0x84; dev->regs[0x03] = 0x04; /*82378IB (SIO)*/
dev->regs[0x04] = 0x07;
dev->regs[0x07] = 0x02;
dev->regs[0x08] = dev->id;
dev->regs[0x40] = 0x20; dev->regs[0x41] = 0x00;
dev->regs[0x42] = 0x04;
dev->regs[0x45] = 0x10; dev->regs[0x46] = 0x0f;
dev->regs[0x48] = 0x01;
dev->regs[0x4a] = 0x10; dev->regs[0x4b] = 0x0f;
dev->regs[0x4c] = 0x56; dev->regs[0x4d] = 0x40;
dev->regs[0x4e] = 0x07; dev->regs[0x4f] = 0x4f;
dev->regs[0x57] = 0x04;
if (dev->id == 0x03) {
dev->regs[0x60] = 0x80; dev->regs[0x61] = 0x80; dev->regs[0x62] = 0x80; dev->regs[0x63] = 0x80;
}
dev->regs[0x80] = 0x78;
if (dev->id == 0x03) {
dev->regs[0xa0] = 0x08;
dev->regs[0xa8] = 0x0f;
}
pci_set_irq_routing(PCI_INTA, PCI_IRQ_DISABLED);
pci_set_irq_routing(PCI_INTB, PCI_IRQ_DISABLED);
pci_set_irq_routing(PCI_INTC, PCI_IRQ_DISABLED);
pci_set_irq_routing(PCI_INTD, PCI_IRQ_DISABLED);
if (dev->timer_base & 0x0001) {
io_removehandler(dev->timer_base & 0xfffc, 0x0004,
sio_timer_read, sio_timer_readw, NULL,
sio_timer_write, sio_timer_writew, NULL, dev);
}
dev->timer_base = 0x0078;
}
static void
sio_apm_out(uint16_t port, uint8_t val, void *p)
{
sio_t *dev = (sio_t *) p;
if (dev->apm->do_smi)
dev->regs[0xaa] |= 0x80;
}
static void
sio_fast_off_count(void *priv)
{
sio_t *dev = (sio_t *) priv;
cpu_fast_off_count--;
if (cpu_fast_off_count == 0) {
smi_line = 1;
dev->regs[0xaa] |= 0x20;
cpu_fast_off_count = dev->regs[0xa8] + 1;
}
timer_on_auto(&dev->fast_off_timer, dev->fast_off_period);
}
static void
sio_reset(void *p)
{
sio_t *dev = (sio_t *) p;
sio_write(0, 0x57, 0x04, p);
dma_set_params(1, 0xffffffff);
if (dev->id == 0x03) {
sio_write(0, 0xa0, 0x08, p);
sio_write(0, 0xa2, 0x00, p);
sio_write(0, 0xa4, 0x00, p);
sio_write(0, 0xa5, 0x00, p);
sio_write(0, 0xa6, 0x00, p);
sio_write(0, 0xa7, 0x00, p);
sio_write(0, 0xa8, 0x0f, p);
}
}
static void
sio_close(void *p)
{
sio_t *dev = (sio_t *)p;
free(dev);
}
static void
sio_speed_changed(void *priv)
{
sio_t *dev = (sio_t *) priv;
int te;
te = timer_is_enabled(&dev->timer);
timer_disable(&dev->timer);
if (te)
timer_set_delay_u64(&dev->timer, ((uint64_t) dev->timer_latch) * TIMER_USEC);
if (dev->id == 0x03) {
te = timer_is_enabled(&dev->fast_off_timer);
timer_stop(&dev->fast_off_timer);
if (te)
timer_on_auto(&dev->fast_off_timer, dev->fast_off_period);
}
}
static void *
sio_init(const device_t *info)
{
sio_t *dev = (sio_t *) malloc(sizeof(sio_t));
memset(dev, 0, sizeof(sio_t));
pci_add_card(PCI_ADD_SOUTHBRIDGE, sio_read, sio_write, dev);
dev->id = info->local;
if (dev->id == 0x03)
timer_add(&dev->fast_off_timer, sio_fast_off_count, dev, 0);
sio_reset_hard(dev);
cpu_fast_off_flags = 0x00000000;
if (dev->id == 0x03) {
cpu_fast_off_val = dev->regs[0xa8];
cpu_fast_off_count = cpu_fast_off_val + 1;
} else
cpu_fast_off_val = cpu_fast_off_count = 0;
if (dev->id == 0x03) {
dev->apm = device_add(&apm_pci_device);
/* APM intercept handler to update 82378ZB SMI status on APM SMI. */
io_sethandler(0x00b2, 0x0001, NULL, NULL, NULL, sio_apm_out, NULL, NULL, dev);
}
dev->port_92 = device_add(&port_92_pci_device);
dma_set_sg_base(0x04);
dma_set_params(1, 0xffffffff);
dma_ext_mode_init();
dma_high_page_init();
if (dev->id == 0x03)
dma_alias_set();
io_sethandler(0x0073, 0x0001,
sio_config_read, NULL, NULL, sio_config_write, NULL, NULL, dev);
io_sethandler(0x0075, 0x0001,
sio_config_read, NULL, NULL, sio_config_write, NULL, NULL, dev);
timer_add(&dev->timer, NULL, NULL, 0);
return dev;
}
const device_t sio_device =
{
"Intel 82378IB (SIO)",
DEVICE_PCI,
0x00,
sio_init,
sio_close,
sio_reset,
NULL,
sio_speed_changed,
NULL,
NULL
};
const device_t sio_zb_device =
{
"Intel 82378ZB (SIO)",
DEVICE_PCI,
0x03,
sio_init,
sio_close,
sio_reset,
NULL,
sio_speed_changed,
NULL,
NULL
};

659
src/chipset/via_vt82c586b.c Normal file
View File

@@ -0,0 +1,659 @@
/*
* 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 VIA Apollo MVP3 southbridge
*
*
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
* Melissa Goad, <mszoopers@protonmail.com>
*
* Copyright 2008-2020 Sarah Walker.
* Copyright 2016-2020 Miran Grca.
* Copyright 2020 Melissa Goad.
*/
#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/cdrom.h>
#include "cpu.h"
#include <86box/scsi_device.h>
#include <86box/scsi_cdrom.h>
#include <86box/dma.h>
#include <86box/io.h>
#include <86box/device.h>
#include <86box/apm.h>
#include <86box/keyboard.h>
#include <86box/mem.h>
#include <86box/timer.h>
#include <86box/nvr.h>
#include <86box/pci.h>
#include <86box/pic.h>
#include <86box/port_92.h>
#include <86box/hdc.h>
#include <86box/hdc_ide.h>
#include <86box/hdc_ide_sff8038i.h>
#include <86box/zip.h>
#include <86box/machine.h>
#include <86box/chipset.h>
#define ACPI_TIMER_FREQ 3579545
#define ACPI_IO_ENABLE (1 << 7)
#define ACPI_TIMER_32BIT (1 << 3)
typedef struct
{
uint8_t pci_isa_regs[256];
uint8_t ide_regs[256];
uint8_t usb_regs[256];
uint8_t power_regs[256];
sff8038i_t * bm[2];
nvr_t * nvr;
int nvr_enabled, slot;
struct
{
uint16_t io_base;
} usb;
struct
{
uint16_t io_base;
} power;
} via_vt82c586b_t;
static void
via_vt82c586b_reset_hard(void *priv)
{
int i;
via_vt82c586b_t *via_vt82c586b = (via_vt82c586b_t *) priv;
uint16_t old_base = (via_vt82c586b->ide_regs[0x20] & 0xf0) | (via_vt82c586b->ide_regs[0x21] << 8);
sff_bus_master_reset(via_vt82c586b->bm[0], old_base);
sff_bus_master_reset(via_vt82c586b->bm[1], old_base + 8);
memset(via_vt82c586b->pci_isa_regs, 0, 256);
memset(via_vt82c586b->ide_regs, 0, 256);
memset(via_vt82c586b->usb_regs, 0, 256);
memset(via_vt82c586b->power_regs, 0, 256);
via_vt82c586b->pci_isa_regs[0x00] = 0x06; via_vt82c586b->pci_isa_regs[0x01] = 0x11; /*VIA*/
via_vt82c586b->pci_isa_regs[0x02] = 0x86; via_vt82c586b->pci_isa_regs[0x03] = 0x05; /*VT82C586B*/
via_vt82c586b->pci_isa_regs[0x04] = 0x0f;
via_vt82c586b->pci_isa_regs[0x07] = 0x02;
via_vt82c586b->pci_isa_regs[0x0a] = 0x01;
via_vt82c586b->pci_isa_regs[0x0b] = 0x06;
via_vt82c586b->pci_isa_regs[0x0e] = 0x80;
via_vt82c586b->pci_isa_regs[0x48] = 0x01;
via_vt82c586b->pci_isa_regs[0x4a] = 0x04;
via_vt82c586b->pci_isa_regs[0x4f] = 0x03;
via_vt82c586b->pci_isa_regs[0x50] = 0x24;
via_vt82c586b->pci_isa_regs[0x59] = 0x04;
dma_e = 0x00;
for (i = 0; i < 8; i++) {
dma[i].ab &= 0xffff000f;
dma[i].ac &= 0xffff000f;
}
pic_set_shadow(0);
/* IDE registers */
via_vt82c586b->ide_regs[0x00] = 0x06; via_vt82c586b->ide_regs[0x01] = 0x11; /*VIA*/
via_vt82c586b->ide_regs[0x02] = 0x71; via_vt82c586b->ide_regs[0x03] = 0x05; /*VT82C586B*/
via_vt82c586b->ide_regs[0x04] = 0x80;
via_vt82c586b->ide_regs[0x06] = 0x80; via_vt82c586b->ide_regs[0x07] = 0x02;
via_vt82c586b->ide_regs[0x09] = 0x85;
via_vt82c586b->ide_regs[0x0a] = 0x01;
via_vt82c586b->ide_regs[0x0b] = 0x01;
via_vt82c586b->ide_regs[0x10] = 0xf1; via_vt82c586b->ide_regs[0x11] = 0x01;
via_vt82c586b->ide_regs[0x14] = 0xf5; via_vt82c586b->ide_regs[0x15] = 0x03;
via_vt82c586b->ide_regs[0x18] = 0x71; via_vt82c586b->ide_regs[0x19] = 0x01;
via_vt82c586b->ide_regs[0x1c] = 0x75; via_vt82c586b->ide_regs[0x1d] = 0x03;
via_vt82c586b->ide_regs[0x20] = 0x01; via_vt82c586b->ide_regs[0x21] = 0xcc;
via_vt82c586b->ide_regs[0x3c] = 0x0e;
via_vt82c586b->ide_regs[0x40] = 0x08;
via_vt82c586b->ide_regs[0x41] = 0x02;
via_vt82c586b->ide_regs[0x42] = 0x09;
via_vt82c586b->ide_regs[0x43] = 0x3a;
via_vt82c586b->ide_regs[0x44] = 0x68;
via_vt82c586b->ide_regs[0x46] = 0xc0;
via_vt82c586b->ide_regs[0x48] = 0xa8; via_vt82c586b->ide_regs[0x49] = 0xa8;
via_vt82c586b->ide_regs[0x4a] = 0xa8; via_vt82c586b->ide_regs[0x4b] = 0xa8;
via_vt82c586b->ide_regs[0x4c] = 0xff;
via_vt82c586b->ide_regs[0x4e] = 0xff;
via_vt82c586b->ide_regs[0x4f] = 0xff;
via_vt82c586b->ide_regs[0x50] = 0x03; via_vt82c586b->ide_regs[0x51] = 0x03;
via_vt82c586b->ide_regs[0x52] = 0x03; via_vt82c586b->ide_regs[0x53] = 0x03;
via_vt82c586b->ide_regs[0x61] = 0x02;
via_vt82c586b->ide_regs[0x69] = 0x02;
via_vt82c586b->usb_regs[0x00] = 0x06; via_vt82c586b->usb_regs[0x01] = 0x11; /*VIA*/
via_vt82c586b->usb_regs[0x02] = 0x38; via_vt82c586b->usb_regs[0x03] = 0x30;
via_vt82c586b->usb_regs[0x04] = 0x00; via_vt82c586b->usb_regs[0x05] = 0x00;
via_vt82c586b->usb_regs[0x06] = 0x00; via_vt82c586b->usb_regs[0x07] = 0x02;
via_vt82c586b->usb_regs[0x0a] = 0x03;
via_vt82c586b->usb_regs[0x0b] = 0x0c;
via_vt82c586b->usb_regs[0x0d] = 0x16;
via_vt82c586b->usb_regs[0x20] = 0x01;
via_vt82c586b->usb_regs[0x21] = 0x03;
via_vt82c586b->usb_regs[0x3d] = 0x04;
via_vt82c586b->usb_regs[0x60] = 0x10;
via_vt82c586b->usb_regs[0xc1] = 0x20;
via_vt82c586b->power_regs[0x00] = 0x06; via_vt82c586b->power_regs[0x01] = 0x11; /*VIA*/
via_vt82c586b->power_regs[0x02] = 0x40; via_vt82c586b->power_regs[0x03] = 0x30;
via_vt82c586b->power_regs[0x04] = 0x00; via_vt82c586b->power_regs[0x05] = 0x00;
via_vt82c586b->power_regs[0x06] = 0x80; via_vt82c586b->power_regs[0x07] = 0x02;
via_vt82c586b->power_regs[0x08] = 0x10; /*Production version (3041)*/
via_vt82c586b->power_regs[0x48] = 0x01;
pci_set_irq_routing(PCI_INTA, PCI_IRQ_DISABLED);
pci_set_irq_routing(PCI_INTB, PCI_IRQ_DISABLED);
pci_set_irq_routing(PCI_INTC, PCI_IRQ_DISABLED);
pci_set_irq_routing(PCI_INTD, PCI_IRQ_DISABLED);
pci_set_mirq_routing(PCI_MIRQ0, PCI_IRQ_DISABLED);
pci_set_mirq_routing(PCI_MIRQ1, PCI_IRQ_DISABLED);
pci_set_mirq_routing(PCI_MIRQ2, PCI_IRQ_DISABLED);
ide_pri_disable();
ide_sec_disable();
}
static void
via_vt82c586b_ide_handlers(via_vt82c586b_t *dev)
{
uint16_t main, side;
ide_pri_disable();
ide_sec_disable();
if (dev->ide_regs[0x09] & 0x01) {
main = (dev->ide_regs[0x11] << 8) | (dev->ide_regs[0x10] & 0xf8);
side = ((dev->ide_regs[0x15] << 8) | (dev->ide_regs[0x14] & 0xfc)) + 2;
} else {
main = 0x1f0;
side = 0x3f6;
}
ide_set_base(0, main);
ide_set_side(0, side);
if (dev->ide_regs[0x09] & 0x04) {
main = (dev->ide_regs[0x19] << 8) | (dev->ide_regs[0x18] & 0xf8);
side = ((dev->ide_regs[0x1d] << 8) | (dev->ide_regs[0x1c] & 0xfc)) + 2;
} else {
main = 0x170;
side = 0x376;
}
ide_set_base(1, main);
ide_set_side(1, side);
if (dev->ide_regs[0x04] & PCI_COMMAND_IO) {
if (dev->ide_regs[0x40] & 0x02)
ide_pri_enable();
if (dev->ide_regs[0x40] & 0x01)
ide_sec_enable();
}
}
static void
via_vt82c586b_ide_irqs(via_vt82c586b_t *dev)
{
int irq_mode[2] = { 0, 0 };
if (dev->ide_regs[0x09] & 0x01)
irq_mode[0] = (dev->ide_regs[0x3d] & 0x01);
if (dev->ide_regs[0x09] & 0x04)
irq_mode[1] = (dev->ide_regs[0x3d] & 0x01);
sff_set_irq_mode(dev->bm[0], 0, irq_mode[0]);
sff_set_irq_mode(dev->bm[0], 1, irq_mode[1]);
sff_set_irq_mode(dev->bm[1], 0, irq_mode[0]);
sff_set_irq_mode(dev->bm[1], 1, irq_mode[1]);
}
static void
via_vt82c586b_bus_master_handlers(via_vt82c586b_t *dev)
{
uint16_t base = (dev->ide_regs[0x20] & 0xf0) | (dev->ide_regs[0x21] << 8);
sff_bus_master_handler(dev->bm[0], (dev->ide_regs[0x04] & 1), base);
sff_bus_master_handler(dev->bm[1], (dev->ide_regs[0x04] & 1), base + 8);
}
static uint8_t
via_vt82c586b_read(int func, int addr, void *priv)
{
via_vt82c586b_t *dev = (via_vt82c586b_t *) priv;
uint8_t ret = 0xff;
int c;
switch(func) {
case 0:
if ((addr >= 0x60) && (addr <= 0x6f)) {
c = (addr & 0x0e) >> 1;
if (addr & 0x01)
ret = (dma[c].ab & 0x0000ff00) >> 8;
else {
ret = (dma[c].ab & 0x000000f0);
ret |= (!!(dma_e & (1 << c)) << 3);
}
} else
ret = dev->pci_isa_regs[addr];
break;
case 1:
ret = dev->ide_regs[addr];
break;
case 2:
ret = dev->usb_regs[addr];
break;
case 3:
ret = dev->power_regs[addr];
break;
}
return ret;
}
static uint8_t
usb_reg_read(uint16_t addr, void *p)
{
uint8_t ret = 0xff;
switch (addr & 0x1f) {
case 0x10: case 0x11: case 0x12: case 0x13:
/* Port status */
ret = 0x00;
break;
}
return ret;
}
static void
usb_reg_write(uint16_t addr, uint8_t val, void *p)
{
}
static void
nvr_update_io_mapping(via_vt82c586b_t *dev)
{
if (dev->nvr_enabled)
nvr_at_handler(0, 0x0074, dev->nvr);
if ((dev->pci_isa_regs[0x5b] & 0x02) && (dev->pci_isa_regs[0x48] & 0x08))
nvr_at_handler(1, 0x0074, dev->nvr);
}
static void
usb_update_io_mapping(via_vt82c586b_t *dev)
{
if (dev->usb.io_base != 0x0000)
io_removehandler(dev->usb.io_base, 0x20, usb_reg_read, NULL, NULL, usb_reg_write, NULL, NULL, dev);
dev->usb.io_base = (dev->usb_regs[0x20] & ~0x1f) | (dev->usb_regs[0x21] << 8);
if ((dev->usb_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) && (dev->usb.io_base != 0x0000))
io_sethandler(dev->usb.io_base, 0x20, usb_reg_read, NULL, NULL, usb_reg_write, NULL, NULL, dev);
}
static uint8_t
power_reg_read(uint16_t addr, void *p)
{
via_vt82c586b_t *dev = (via_vt82c586b_t *) p;
uint32_t timer;
uint8_t ret = 0xff;
switch (addr & 0xff) {
case 0x08: case 0x09: case 0x0a: case 0x0b:
/* ACPI timer */
timer = (tsc * ACPI_TIMER_FREQ) / machines[machine].cpu[cpu_manufacturer].cpus[cpu_effective].rspeed;
if (!(dev->power_regs[0x41] & ACPI_TIMER_32BIT))
timer &= 0x00ffffff;
ret = (timer >> (8 * (addr & 3))) & 0xff;
break;
}
return ret;
}
static void
power_reg_write(uint16_t addr, uint8_t val, void *p)
{
}
static void
power_update_io_mapping(via_vt82c586b_t *dev)
{
if (dev->power.io_base != 0x0000)
io_removehandler(dev->power.io_base, 0x100, power_reg_read, NULL, NULL, power_reg_write, NULL, NULL, dev);
dev->power.io_base = (dev->power_regs[0x49] << 8);
if ((dev->power_regs[0x41] & ACPI_IO_ENABLE) && (dev->power.io_base != 0x0000))
io_sethandler(dev->power.io_base, 0x100, power_reg_read, NULL, NULL, power_reg_write, NULL, NULL, dev);
}
static void
via_vt82c586b_write(int func, int addr, uint8_t val, void *priv)
{
via_vt82c586b_t *dev = (via_vt82c586b_t *) priv;
int c;
if (func > 3)
return;
switch(func) {
case 0: /* PCI-ISA bridge */
/* Read-only addresses */
if ((addr < 4) || (addr == 5) || ((addr >= 8) && (addr < 0x40)) ||
(addr == 0x49) || (addr == 0x4b) || ((addr >= 0x51) && (addr < 0x54)) || ((addr >= 0x5d) && (addr < 0x60)) ||
((addr >= 0x68) && (addr < 0x6a)) || (addr >= 0x73))
return;
switch (addr) {
case 0x04:
dev->pci_isa_regs[0x04] = (val & 8) | 7;
break;
case 0x07:
dev->pci_isa_regs[0x07] &= ~(val & 0xb0);
break;
case 0x47:
if ((val & 0x81) == 0x81)
resetx86();
pic_set_shadow(!!(val & 0x10));
pci_elcr_set_enabled(!!(val & 0x20));
dev->pci_isa_regs[0x47] = val & 0xfe;
break;
case 0x48:
dev->pci_isa_regs[0x48] = val;
nvr_update_io_mapping(dev);
break;
case 0x54:
pci_set_irq_level(PCI_INTA, !(val & 8));
pci_set_irq_level(PCI_INTB, !(val & 4));
pci_set_irq_level(PCI_INTC, !(val & 2));
pci_set_irq_level(PCI_INTD, !(val & 1));
break;
case 0x55:
pci_set_irq_routing(PCI_INTD, (val & 0xf0) ? (val >> 4) : PCI_IRQ_DISABLED);
pci_set_mirq_routing(PCI_MIRQ0, (val & 0x0f) ? (val & 0x0f) : PCI_IRQ_DISABLED);
dev->pci_isa_regs[0x55] = val;
break;
case 0x56:
pci_set_irq_routing(PCI_INTA, (val & 0xf0) ? (val >> 4) : PCI_IRQ_DISABLED);
pci_set_irq_routing(PCI_INTB, (val & 0x0f) ? (val & 0x0f) : PCI_IRQ_DISABLED);
dev->pci_isa_regs[0x56] = val;
break;
case 0x57:
pci_set_irq_routing(PCI_INTC, (val & 0xf0) ? (val >> 4) : PCI_IRQ_DISABLED);
pci_set_mirq_routing(PCI_MIRQ1, (val & 0x0f) ? (val & 0x0f) : PCI_IRQ_DISABLED);
dev->pci_isa_regs[0x57] = val;
break;
case 0x58:
pci_set_mirq_routing(PCI_MIRQ2, (val & 0x0f) ? (val & 0x0f) : PCI_IRQ_DISABLED);
dev->pci_isa_regs[0x58] = val;
break;
case 0x5b:
dev->pci_isa_regs[0x5b] = val;
nvr_update_io_mapping(dev);
break;
case 0x60: case 0x62: case 0x64: case 0x66:
case 0x6a: case 0x6c: case 0x6e:
c = (addr & 0x0e) >> 1;
dma[c].ab = (dma[c].ab & 0xffffff0f) | (val & 0xf0);
dma[c].ac = (dma[c].ac & 0xffffff0f) | (val & 0xf0);
if (val & 0x08)
dma_e |= (1 << c);
else
dma_e &= ~(1 << c);
break;
case 0x61: case 0x63: case 0x65: case 0x67:
case 0x6b: case 0x6d: case 0x6f:
c = (addr & 0x0e) >> 1;
dma[c].ab = (dma[c].ab & 0xffff00ff) | (val << 8);
dma[c].ac = (dma[c].ac & 0xffff00ff) | (val << 8);
break;
case 0x70: case 0x71: case 0x72: case 0x73:
dev->pci_isa_regs[(addr - 0x44)] = val;
break;
}
break;
case 1: /* IDE regs */
/* Read-only addresses */
if ((addr < 4) || (addr == 5) || (addr == 8) || ((addr >= 0xa) && (addr < 0x0d)) ||
((addr >= 0x0e) && (addr < 0x10)) || ((addr >= 0x12) && (addr < 0x13)) ||
((addr >= 0x16) && (addr < 0x17)) || ((addr >= 0x1a) && (addr < 0x1b)) ||
((addr >= 0x1e) && (addr < 0x1f)) || ((addr >= 0x22) && (addr < 0x3c)) ||
((addr >= 0x3e) && (addr < 0x40)) || ((addr >= 0x54) && (addr < 0x60)) ||
((addr >= 0x52) && (addr < 0x68)) || (addr >= 0x62))
return;
switch (addr) {
case 0x04:
dev->ide_regs[0x04] = val & 0x85;
via_vt82c586b_ide_handlers(dev);
via_vt82c586b_bus_master_handlers(dev);
break;
case 0x07:
dev->ide_regs[0x07] &= ~(val & 0xf1);
break;
case 0x09:
dev->ide_regs[0x09] = (val & 0x05) | 0x8a;
via_vt82c586b_ide_handlers(dev);
via_vt82c586b_ide_irqs(dev);
break;
case 0x10:
dev->ide_regs[0x10] = (val & 0xf8) | 1;
via_vt82c586b_ide_handlers(dev);
break;
case 0x11:
dev->ide_regs[0x11] = val;
via_vt82c586b_ide_handlers(dev);
break;
case 0x14:
dev->ide_regs[0x14] = (val & 0xfc) | 1;
via_vt82c586b_ide_handlers(dev);
break;
case 0x15:
dev->ide_regs[0x15] = val;
via_vt82c586b_ide_handlers(dev);
break;
case 0x18:
dev->ide_regs[0x18] = (val & 0xf8) | 1;
via_vt82c586b_ide_handlers(dev);
break;
case 0x19:
dev->ide_regs[0x19] = val;
via_vt82c586b_ide_handlers(dev);
break;
case 0x1c:
dev->ide_regs[0x1c] = (val & 0xfc) | 1;
via_vt82c586b_ide_handlers(dev);
break;
case 0x1d:
dev->ide_regs[0x1d] = val;
via_vt82c586b_ide_handlers(dev);
break;
case 0x20:
dev->ide_regs[0x20] = (val & 0xf0) | 1;
via_vt82c586b_bus_master_handlers(dev);
break;
case 0x21:
dev->ide_regs[0x21] = val;
via_vt82c586b_bus_master_handlers(dev);
break;
case 0x3d:
dev->ide_regs[0x3d] = val & 0x01;
via_vt82c586b_ide_irqs(dev);
break;
case 0x40:
dev->ide_regs[0x40] = val;
via_vt82c586b_ide_handlers(dev);
break;
default:
dev->ide_regs[addr] = val;
break;
}
break;
case 2:
/* Read-only addresses */
if ((addr < 4) || (addr == 5) || (addr == 6) || ((addr >= 8) && (addr < 0xd)) ||
((addr >= 0xe) && (addr < 0x20)) || ((addr >= 0x22) && (addr < 0x3c)) ||
((addr >= 0x3e) && (addr < 0x40)) || ((addr >= 0x42) && (addr < 0x44)) ||
((addr >= 0x46) && (addr < 0xc0)) || (addr >= 0xc2))
return;
switch (addr) {
case 0x04:
dev->usb_regs[0x04] = val & 0x97;
usb_update_io_mapping(dev);
break;
case 0x07:
dev->usb_regs[0x07] &= ~(val & 0x78);
break;
case 0x20:
dev->usb_regs[0x20] = (val & ~0x1f) | 1;
usb_update_io_mapping(dev);
break;
case 0x21:
dev->usb_regs[0x21] = val;
usb_update_io_mapping(dev);
break;
default:
dev->usb_regs[addr] = val;
break;
}
break;
case 3:
/* Read-only addresses */
if ((addr < 0xd) || ((addr >= 0xe) && (addr < 0x40)) || (addr == 0x43) || (addr == 0x48) ||
((addr >= 0x4a) && (addr < 0x50)) || (addr >= 0x54))
return;
switch (addr) {
case 0x41: case 0x49:
dev->power_regs[addr] = val;
power_update_io_mapping(dev);
break;
default:
dev->power_regs[addr] = val;
break;
}
}
}
static void
*via_vt82c586b_init(const device_t *info)
{
via_vt82c586b_t *dev = (via_vt82c586b_t *) malloc(sizeof(via_vt82c586b_t));
memset(dev, 0, sizeof(via_vt82c586b_t));
dev->slot = pci_add_card(PCI_ADD_SOUTHBRIDGE, via_vt82c586b_read, via_vt82c586b_write, dev);
dev->bm[0] = device_add_inst(&sff8038i_device, 1);
sff_set_slot(dev->bm[0], dev->slot);
sff_set_irq_mode(dev->bm[0], 0, 0);
sff_set_irq_mode(dev->bm[0], 1, 0);
sff_set_irq_pin(dev->bm[0], PCI_INTA);
dev->bm[1] = device_add_inst(&sff8038i_device, 2);
sff_set_slot(dev->bm[1], dev->slot);
sff_set_irq_mode(dev->bm[1], 0, 0);
sff_set_irq_mode(dev->bm[1], 1, 0);
sff_set_irq_pin(dev->bm[1], PCI_INTA);
dev->nvr = device_add(&via_nvr_device);
via_vt82c586b_reset_hard(dev);
device_add(&port_92_pci_device);
dma_alias_set();
pci_enable_mirq(0);
pci_enable_mirq(1);
pci_enable_mirq(2);
return dev;
}
static void
via_vt82c586b_close(void *p)
{
via_vt82c586b_t *via_vt82c586b = (via_vt82c586b_t *)p;
free(via_vt82c586b);
}
const device_t via_vt82c586b_device =
{
"VIA VT82C586B",
DEVICE_PCI,
0,
via_vt82c586b_init,
via_vt82c586b_close,
NULL,
NULL,
NULL,
NULL,
NULL
};

789
src/chipset/via_vt82c596b.c Normal file
View File

@@ -0,0 +1,789 @@
/*
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.
Implementation of the VT82C596B. Based on VT82C586B + PIIX4
Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
Copyright 2020 Tiseno100 <Implemented the 596B overlay>
Copyright 2020 Sarah Walker <Main 586B code>
Copyright 2020 Miran Grca <Author>
Copyright 2020 Melissa Goad <Port to 86Box>
TODO:
- The SMBus must be checked and implemented properly
- Fix Documentation errors
*/
#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/cdrom.h>
#include "cpu.h"
#include <86box/scsi_device.h>
#include <86box/scsi_cdrom.h>
#include <86box/dma.h>
#include <86box/io.h>
#include <86box/device.h>
#include <86box/apm.h>
#include <86box/keyboard.h>
#include <86box/mem.h>
#include <86box/timer.h>
#include <86box/nvr.h>
#include <86box/pci.h>
#include <86box/pic.h>
#include <86box/port_92.h>
#include <86box/hdc.h>
#include <86box/hdc_ide.h>
#include <86box/hdc_ide_sff8038i.h>
#include <86box/zip.h>
#include <86box/machine.h>
#include <86box/chipset.h>
// As of now
#include <86box/smbus_piix4.h>
#define ACPI_TIMER_FREQ 3579545 // Probably Emulator related
#define ACPI_IO_ENABLE (1 << 7)
#define ACPI_TIMER_32BIT (1 << 3)
#if defined(DEV_BRANCH) && defined(USE_596B)
typedef struct
{
uint8_t pci_to_isa_regs[256]; // PCI-to-ISA (Same as 586B)
uint8_t ide_regs[256]; // Common VIA IDE controller
uint8_t usb_regs[256]; // Common VIA USB controller
uint8_t power_regs[256]; // VT82C596B Power Managment Device(Same as 586B + SMBus)
sff8038i_t * bm[2];
nvr_t * nvr;
int nvr_enabled, slot;
struct
{
uint16_t io_base;
}usb;
struct
{
uint16_t io_base;
}power;
smbus_piix4_t * smbus;
} via_vt82c596b_t;
static void
via_vt82c596b_reset(void *priv)
{
via_vt82c596b_t *via_vt82c596b = (via_vt82c596b_t *) priv;
uint16_t old_base = (via_vt82c596b->ide_regs[0x20] & 0xf0) | (via_vt82c596b->ide_regs[0x21] << 8);
sff_bus_master_reset(via_vt82c596b->bm[0], old_base);
sff_bus_master_reset(via_vt82c596b->bm[1], old_base + 8);
memset(via_vt82c596b->pci_to_isa_regs, 0, 256);
memset(via_vt82c596b->ide_regs, 0, 256);
memset(via_vt82c596b->usb_regs, 0, 256);
memset(via_vt82c596b->power_regs, 0, 256);
//PCI-to-ISA registers
via_vt82c596b->pci_to_isa_regs[0x00] = 0x06; //VIA
via_vt82c596b->pci_to_isa_regs[0x01] = 0x11;
via_vt82c596b->pci_to_isa_regs[0x02] = 0x96; //VT82C596B
via_vt82c596b->pci_to_isa_regs[0x03] = 0x05;
via_vt82c596b->pci_to_isa_regs[0x04] = 0x0f; //Control
via_vt82c596b->pci_to_isa_regs[0x07] = 2; //Status
via_vt82c596b->pci_to_isa_regs[0x09] = 0; //Program Interface
via_vt82c596b->pci_to_isa_regs[0x0A] = 1; //Sub-Class code
via_vt82c596b->pci_to_isa_regs[0x0B] = 6; //Class code
via_vt82c596b->pci_to_isa_regs[0x0E] = 0x80; //Header Type
via_vt82c596b->pci_to_isa_regs[0x2C] = 0x73; //Subsystem ID
via_vt82c596b->pci_to_isa_regs[0x2D] = 0x72;
via_vt82c596b->pci_to_isa_regs[0x2E] = 0x71;
via_vt82c596b->pci_to_isa_regs[0x2F] = 0x70;
via_vt82c596b->pci_to_isa_regs[0x48] = 1; //Miscellaneous control 3
via_vt82c596b->pci_to_isa_regs[0x4A] = 4;
via_vt82c596b->pci_to_isa_regs[0x4F] = 3;
via_vt82c596b->pci_to_isa_regs[0x50] = 0x24; //Reserved(?)
via_vt82c596b->pci_to_isa_regs[0x59] = 4; //PIRQ Pin Configuration
//Resetting the DMA
dma_e = 0x00;
for (int i = 0; i < 8; i++) {
dma[i].ab &= 0xffff000f;
dma[i].ac &= 0xffff000f;
}
pic_set_shadow(0);
//IDE registers
via_vt82c596b->ide_regs[0x00] = 0x06; //VIA
via_vt82c596b->ide_regs[0x01] = 0x11;
via_vt82c596b->ide_regs[0x02] = 0x71; //Common VIA IDE Controller
via_vt82c596b->ide_regs[0x03] = 0x05;
via_vt82c596b->ide_regs[0x04] = 0x80; //Command
via_vt82c596b->ide_regs[0x06] = 0x80; //Status
via_vt82c596b->ide_regs[0x07] = 0x02;
via_vt82c596b->ide_regs[0x09] = 0x85; //Programming Interface
via_vt82c596b->ide_regs[0x0A] = 0x01; //Sub class code
via_vt82c596b->ide_regs[0x0B] = 0x01; //Base class code
//Base address control commands
via_vt82c596b->ide_regs[0x10] = 0xF0;
via_vt82c596b->ide_regs[0x11] = 0x01;
via_vt82c596b->ide_regs[0x14] = 0xF4;
via_vt82c596b->ide_regs[0x15] = 0x03;
via_vt82c596b->ide_regs[0x18] = 0x70;
via_vt82c596b->ide_regs[0x19] = 0x01;
via_vt82c596b->ide_regs[0x1C] = 0x74;
via_vt82c596b->ide_regs[0x1D] = 0x03;
via_vt82c596b->ide_regs[0x20] = 0x01;
via_vt82c596b->ide_regs[0x21] = 0xCC;
////
via_vt82c596b->ide_regs[0x3C] = 0x0E; //Interrupt line
via_vt82c596b->ide_regs[0x40] = 0x08; //Chip Enable
via_vt82c596b->ide_regs[0x41] = 0x02; //IDE Configuration
via_vt82c596b->ide_regs[0x42] = 0x09; //Reserved
via_vt82c596b->ide_regs[0x43] = 0x3A; //FIFO Configuration
via_vt82c596b->ide_regs[0x44] = 0x68; //Miscellaneous Control 1
via_vt82c596b->ide_regs[0x46] = 0xC0; //Miscellaneous Control 3
via_vt82c596b->ide_regs[0x48] = 0xA8; //Driver Timing Control
via_vt82c596b->ide_regs[0x49] = 0xA8;
via_vt82c596b->ide_regs[0x4A] = 0xA8;
via_vt82c596b->ide_regs[0x4B] = 0xA8;
via_vt82c596b->ide_regs[0x4C] = 0xFF; //Address Setup Time
via_vt82c596b->ide_regs[0x4E] = 0xFF; //Sec Non-1F0 Port Access Timing
via_vt82c596b->ide_regs[0x4F] = 0xFF; //Pri Non-1F0 Port Access Timing
via_vt82c596b->ide_regs[0x50] = 0x03; //UltraDMA33 Extended Timing Control
via_vt82c596b->ide_regs[0x51] = 0x03;
via_vt82c596b->ide_regs[0x52] = 0x03;
via_vt82c596b->ide_regs[0x53] = 0x03;
via_vt82c596b->ide_regs[0x61] = 0x02; //IDE Primary Sector Size
via_vt82c596b->ide_regs[0x69] = 0x02; //IDE Secondary Sector Size
via_vt82c596b->usb_regs[0x00] = 0x06; //VIA USB Common Controller
via_vt82c596b->usb_regs[0x01] = 0x11;
via_vt82c596b->usb_regs[0x02] = 0x38;
via_vt82c596b->usb_regs[0x03] = 0x30;
//USB Registers
via_vt82c596b->usb_regs[0x04] = 0; //Control
via_vt82c596b->usb_regs[0x05] = 0;
via_vt82c596b->usb_regs[0x06] = 0;
via_vt82c596b->usb_regs[0x07] = 2; //Status
via_vt82c596b->usb_regs[0x0A] = 3; //Sub Class Code
via_vt82c596b->usb_regs[0x0B] = 0x0C; //Base Class Code
via_vt82c596b->usb_regs[0x0D] = 0x16; //Latency Timer
via_vt82c596b->usb_regs[0x20] = 1; //Base address
via_vt82c596b->usb_regs[0x21] = 3;
via_vt82c596b->usb_regs[0x3D] = 4; //Interrupt Pin
via_vt82c596b->usb_regs[0x60] = 0x10; //Serial Bus Release Number(USB 1.0)
via_vt82c596b->usb_regs[0xC1] = 0x20; //Legacy Support
//Power Management Registers
via_vt82c596b->power_regs[0x00] = 0x06; //VT82C596B Power Managment Controller
via_vt82c596b->power_regs[0x01] = 0x11;
via_vt82c596b->power_regs[0x02] = 0x50;
via_vt82c596b->power_regs[0x03] = 0x30;
via_vt82c596b->power_regs[0x04] = 0; //Control
via_vt82c596b->power_regs[0x05] = 0;
via_vt82c596b->power_regs[0x06] = 0x80; //Status
via_vt82c596b->power_regs[0x07] = 2;
via_vt82c596b->power_regs[0x48] = 1; //Power Managment IO Base
via_vt82c596b->power_regs[0x90] = 1; //SMBus IO Base
//Setting up Routing
pci_set_irq_routing(PCI_INTA, PCI_IRQ_DISABLED);
pci_set_irq_routing(PCI_INTB, PCI_IRQ_DISABLED);
pci_set_irq_routing(PCI_INTC, PCI_IRQ_DISABLED);
pci_set_irq_routing(PCI_INTD, PCI_IRQ_DISABLED);
pci_set_mirq_routing(PCI_MIRQ0, PCI_IRQ_DISABLED);
pci_set_mirq_routing(PCI_MIRQ1, PCI_IRQ_DISABLED);
pci_set_mirq_routing(PCI_MIRQ2, PCI_IRQ_DISABLED);
//Disabling the Primary & Secondary controller(?)
ide_pri_disable();
ide_sec_disable();
}
// IDE CONTROLLER
static void
ide_handlers(via_vt82c596b_t *dev)
{
uint16_t main, side;
ide_pri_disable();
ide_sec_disable();
//On Bitfield 0(0x01) Primary Channel operating mode
if (dev->ide_regs[0x09] & 0x01) {
main = (dev->ide_regs[0x11] << 8) | (dev->ide_regs[0x10] & 0xf8);
side = ((dev->ide_regs[0x15] << 8) | (dev->ide_regs[0x14] & 0xfc)) + 2;
} else {
main = 0x1f0;
side = 0x3f6;
}
ide_set_base(0, main);
ide_set_side(0, side);
//On Bitfield 2(0x04) Secondary Channel operating mode
if (dev->ide_regs[0x09] & 0x04) {
main = (dev->ide_regs[0x19] << 8) | (dev->ide_regs[0x18] & 0xf8);
side = ((dev->ide_regs[0x1d] << 8) | (dev->ide_regs[0x1c] & 0xfc)) + 2;
} else {
main = 0x170;
side = 0x376;
}
ide_set_base(1, main);
ide_set_side(1, side);
//Enable the Primary & Secondary Controllers
if (dev->ide_regs[0x04] & PCI_COMMAND_IO) {
//On Bitfield 0(0x01) Enable the Secondary Controller
//On Bitfield 1(0x02) Enable the Primary Controller
if (dev->ide_regs[0x40] & 0x02)
ide_pri_enable();
if (dev->ide_regs[0x40] & 0x01)
ide_sec_enable();
}
}
static void
ide_irqs(via_vt82c596b_t *dev)
{
int irq_mode[2] = { 0, 0 };
if (dev->ide_regs[0x09] & 0x01)
irq_mode[0] = (dev->ide_regs[0x3d] & 0x01);
if (dev->ide_regs[0x09] & 0x04)
irq_mode[1] = (dev->ide_regs[0x3d] & 0x01);
sff_set_irq_mode(dev->bm[0], 0, irq_mode[0]);
sff_set_irq_mode(dev->bm[0], 1, irq_mode[1]);
sff_set_irq_mode(dev->bm[1], 0, irq_mode[0]);
sff_set_irq_mode(dev->bm[1], 1, irq_mode[1]);
}
static void
bus_master_handlers(via_vt82c596b_t *dev)
{
uint16_t base = (dev->ide_regs[0x20] & 0xf0) | (dev->ide_regs[0x21] << 8);
//On Bitfield 0(0x01) I/O Space
sff_bus_master_handler(dev->bm[0], (dev->ide_regs[0x04] & 1), base);
sff_bus_master_handler(dev->bm[1], (dev->ide_regs[0x04] & 1), base + 8);
}
////
// USB CONTROLLER
static uint8_t
usb_reg_read(uint16_t addr, void *p)
{
uint8_t ret = 0xff;
switch (addr & 0x1f) {
case 0x10: case 0x11: case 0x12: case 0x13:
// Return 0 on port status
ret = 0x00;
break;
}
return ret;
}
static void
usb_reg_write(uint16_t addr, uint8_t val, void *p) {}
static void
usb_update_io_mapping(via_vt82c596b_t *dev)
{
if (dev->usb.io_base != 0x0000)
io_removehandler(dev->usb.io_base, 0x20, usb_reg_read, NULL, NULL, usb_reg_write, NULL, NULL, dev);
//On Bitfield 31 defaults to zero (0x20)
dev->usb.io_base = (dev->usb_regs[0x20] & ~0x1f) | (dev->usb_regs[0x21] << 8);
//0x20
//Master Interrupt Control(?)
//TODO: Find the exact meaning
if ((dev->usb_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) && (dev->usb.io_base != 0x0000))
io_sethandler(dev->usb.io_base, 0x20, usb_reg_read, NULL, NULL, usb_reg_write, NULL, NULL, dev);
}
////
// NVR
static void
nvr_update_io_mapping(via_vt82c596b_t *dev)
{
//0x74 CMOS Memory Address
//0x75 CMOS Memory Data
//Ports 74-75 may be used to access CMOS if the internal RTC is disabled.
if (dev->nvr_enabled)
nvr_at_handler(0, 0x0074, dev->nvr);
//In case of we are set on 5B bitfield 1(0x02)(RTC SRAM Access Enable) and 48 bitfield 3(0x08)
//(Extra RTC Port 74/75 Enable)
if ((dev->pci_to_isa_regs[0x5b] & 0x02) && (dev->pci_to_isa_regs[0x48] & 0x08))
nvr_at_handler(1, 0x0074, dev->nvr);
}
////
// POWER MANAGMENT
// Need excessive documentation
static uint8_t
power_reg_read(uint16_t addr, void *p)
{
via_vt82c596b_t *dev = (via_vt82c596b_t *) p;
uint32_t timer;
uint8_t ret = 0xff;
switch (addr & 0xff) {
case 0x08: case 0x09: case 0x0a: case 0x0b:
//ACPI Timer
timer = (tsc * ACPI_TIMER_FREQ) / machines[machine].cpu[cpu_manufacturer].cpus[cpu_effective].rspeed;
if (!(dev->power_regs[0x41] & ACPI_TIMER_32BIT))
timer &= 0x00ffffff;
ret = (timer >> (8 * (addr & 3))) & 0xff;
break;
}
return ret;
}
static void
power_reg_write(uint16_t addr, uint8_t val, void *p) {}
static void
power_update_io_mapping(via_vt82c596b_t *dev)
{
if (dev->power.io_base != 0x0000)
io_removehandler(dev->power.io_base, 0x100, power_reg_read, NULL, NULL, power_reg_write, NULL, NULL, dev);
dev->power.io_base = (dev->power_regs[0x49] << 8);
if ((dev->power_regs[0x41] & ACPI_IO_ENABLE) && (dev->power.io_base != 0x0000))
io_sethandler(dev->power.io_base, 0x100, power_reg_read, NULL, NULL, power_reg_write, NULL, NULL, dev);
}
static void
smbus_update_io_mapping(via_vt82c596b_t *dev)
{
smbus_piix4_remap(dev->smbus, (dev->power_regs[0x91] << 8) | (dev->power_regs[0x90] & 0xf0), (dev->power_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) && (dev->power_regs[0xD2] & 0x01));
}
////
static uint8_t
via_vt82c596b_read(int func, int addr, void *priv)
{
via_vt82c596b_t *dev = (via_vt82c596b_t *) priv;
uint8_t ret = 0xff;
int c;
switch(func) {
case 0:
//By System I/O Map, setting up the Keyboard Controller
//60-6F Keyboard Controller [0000 0000 0110] xnxn
if ((addr >= 0x60) && (addr <= 0x6f)) {
c = (addr & 0x0e) >> 1;
if (addr & 0x01) //If Enabled
ret = (dma[c].ab & 0x0000ff00) >> 8;
else {
ret = (dma[c].ab & 0x000000f0);
ret |= (!!(dma_e & (1 << c)) << 3);
}
} else
ret = dev->pci_to_isa_regs[addr];
break;
case 1:
ret = dev->ide_regs[addr];
break;
case 2:
ret = dev->usb_regs[addr];
break;
case 3:
ret = dev->power_regs[addr];
break;
}
return ret;
}
static void
via_vt82c596b_write(int func, int addr, uint8_t val, void *priv)
{
via_vt82c596b_t *dev = (via_vt82c596b_t *) priv;
int c;
//Excessive Documentation is needed!!
if (func > 3)
return;
switch(func) {
//PCI-to-ISA
case 0:
//Read-only addresses
if ((addr < 4) || (addr == 5) || ((addr >= 8) && (addr < 0x40)) ||
(addr == 0x49) || (addr == 0x4b) || ((addr >= 0x51) && (addr < 0x54)) ||
((addr >= 0x5d) && (addr < 0x60)) ||
((addr >= 0x68) && (addr < 0x6a)) || (addr >= 0x73))
return;
switch (addr) {
case 0x04:
dev->pci_to_isa_regs[0x04] = (val & 8) | 7;
break;
case 0x07:
dev->pci_to_isa_regs[0x07] &= ~(val & 0xb0);
break;
case 0x47:
if ((val & 0x81) == 0x81)
resetx86();
pic_set_shadow(!!(val & 0x10));
pci_elcr_set_enabled(!!(val & 0x20));
dev->pci_to_isa_regs[0x47] = val & 0xfe;
break;
case 0x48:
dev->pci_to_isa_regs[0x48] = val;
nvr_update_io_mapping(dev);
break;
case 0x54:
pci_set_irq_level(PCI_INTA, !(val & 8));
pci_set_irq_level(PCI_INTB, !(val & 4));
pci_set_irq_level(PCI_INTC, !(val & 2));
pci_set_irq_level(PCI_INTD, !(val & 1));
break;
case 0x55:
pci_set_irq_routing(PCI_INTD, (val & 0xf0) ? (val >> 4) : PCI_IRQ_DISABLED);
pci_set_mirq_routing(PCI_MIRQ0, (val & 0x0f) ? (val & 0x0f) : PCI_IRQ_DISABLED);
dev->pci_to_isa_regs[0x55] = val;
break;
case 0x56:
pci_set_irq_routing(PCI_INTA, (val & 0xf0) ? (val >> 4) : PCI_IRQ_DISABLED);
pci_set_irq_routing(PCI_INTB, (val & 0x0f) ? (val & 0x0f) : PCI_IRQ_DISABLED);
dev->pci_to_isa_regs[0x56] = val;
break;
case 0x57:
pci_set_irq_routing(PCI_INTC, (val & 0xf0) ? (val >> 4) : PCI_IRQ_DISABLED);
pci_set_mirq_routing(PCI_MIRQ1, (val & 0x0f) ? (val & 0x0f) : PCI_IRQ_DISABLED);
dev->pci_to_isa_regs[0x57] = val;
break;
case 0x58:
pci_set_mirq_routing(PCI_MIRQ2, (val & 0x0f) ? (val & 0x0f) : PCI_IRQ_DISABLED);
dev->pci_to_isa_regs[0x58] = val;
break;
case 0x5b:
dev->pci_to_isa_regs[0x5b] = val;
nvr_update_io_mapping(dev);
break;
case 0x60: case 0x62: case 0x64: case 0x66:
case 0x6a: case 0x6c: case 0x6e:
c = (addr & 0x0e) >> 1;
dma[c].ab = (dma[c].ab & 0xffffff0f) | (val & 0xf0);
dma[c].ac = (dma[c].ac & 0xffffff0f) | (val & 0xf0);
if (val & 0x08)
dma_e |= (1 << c);
else
dma_e &= ~(1 << c);
break;
case 0x61: case 0x63: case 0x65: case 0x67:
case 0x6b: case 0x6d: case 0x6f:
c = (addr & 0x0e) >> 1;
dma[c].ab = (dma[c].ab & 0xffff00ff) | (val << 8);
dma[c].ac = (dma[c].ac & 0xffff00ff) | (val << 8);
break;
case 0x70: case 0x71: case 0x72: case 0x73:
dev->pci_to_isa_regs[(addr - 0x44)] = val;
break;
}
break;
//IDE Controller
case 1:
//Read-only addresses
if ((addr < 4) || (addr == 5) || (addr == 8) || ((addr >= 0xa) && (addr < 0x0d)) ||
((addr >= 0x0e) && (addr < 0x10)) || ((addr >= 0x12) && (addr < 0x13)) ||
((addr >= 0x16) && (addr < 0x17)) || ((addr >= 0x1a) && (addr < 0x1b)) ||
((addr >= 0x1e) && (addr < 0x1f)) || ((addr >= 0x22) && (addr < 0x3c)) ||
((addr >= 0x3e) && (addr < 0x40)) || ((addr >= 0x54) && (addr < 0x60)) ||
((addr >= 0x52) && (addr < 0x68)) || (addr >= 0x62))
return;
switch (addr) {
case 0x04:
dev->ide_regs[0x04] = val & 0x85;
ide_handlers(dev);
bus_master_handlers(dev);
break;
case 0x07:
dev->ide_regs[0x07] &= ~(val & 0xf1);
break;
case 0x09:
dev->ide_regs[0x09] = (val & 0x05) | 0x8a;
ide_handlers(dev);
ide_irqs(dev);
break;
case 0x10:
dev->ide_regs[0x10] = (val & 0xf8) | 1;
ide_handlers(dev);
break;
case 0x11:
dev->ide_regs[0x11] = val;
ide_handlers(dev);
break;
case 0x14:
dev->ide_regs[0x14] = (val & 0xfc) | 1;
ide_handlers(dev);
break;
case 0x15:
dev->ide_regs[0x15] = val;
ide_handlers(dev);
break;
case 0x18:
dev->ide_regs[0x18] = (val & 0xf8) | 1;
ide_handlers(dev);
break;
case 0x19:
dev->ide_regs[0x19] = val;
ide_handlers(dev);
break;
case 0x1c:
dev->ide_regs[0x1c] = (val & 0xfc) | 1;
ide_handlers(dev);
break;
case 0x1d:
dev->ide_regs[0x1d] = val;
ide_handlers(dev);
break;
case 0x20:
dev->ide_regs[0x20] = (val & 0xf0) | 1;
bus_master_handlers(dev);
break;
case 0x21:
dev->ide_regs[0x21] = val;
bus_master_handlers(dev);
break;
case 0x3d:
dev->ide_regs[0x3d] = val & 0x01;
ide_irqs(dev);
break;
case 0x40:
dev->ide_regs[0x40] = val;
ide_handlers(dev);
break;
default:
dev->ide_regs[addr] = val;
break;
}
break;
//USB Controller
case 2:
//Read-only addresses
if ((addr < 4) || (addr == 5) || (addr == 6) || ((addr >= 8) && (addr < 0xd)) ||
((addr >= 0xe) && (addr < 0x20)) || ((addr >= 0x22) && (addr < 0x3c)) ||
((addr >= 0x3e) && (addr < 0x40)) || ((addr >= 0x42) && (addr < 0x44)) ||
((addr >= 0x46) && (addr < 0xc0)) || (addr >= 0xc2))
return;
switch (addr) {
case 0x04:
dev->usb_regs[0x04] = val & 0x97;
usb_update_io_mapping(dev);
break;
case 0x07:
dev->usb_regs[0x07] &= ~(val & 0x78);
break;
case 0x20:
dev->usb_regs[0x20] = (val & ~0x1f) | 1;
usb_update_io_mapping(dev);
break;
case 0x21:
dev->usb_regs[0x21] = val;
usb_update_io_mapping(dev);
break;
default:
dev->usb_regs[addr] = val;
break;
}
break;
//Power Management
case 3:
//Read-Only Addresses
if ((addr < 0xd) || ((addr >= 0xe) && (addr < 0x40)) || (addr == 0x43) || (addr == 0x48) ||
((addr >= 0x4a) && (addr < 0x50)) || (addr >= 0x54))
return;
switch (addr) {
case 0x41: case 0x49:
dev->power_regs[addr] = val;
power_update_io_mapping(dev);
smbus_update_io_mapping(dev);
break;
case 0x90:
dev->power_regs[0x90] = (val & 0xf0) | 1;
smbus_update_io_mapping(dev);
break;
case 0x91:
dev->power_regs[0x91] = val;
smbus_update_io_mapping(dev);
break;
default:
dev->power_regs[addr] = val;
break;
}
}
}
static void *
via_vt82c596b_init(const device_t *info)
{
via_vt82c596b_t *dev = (via_vt82c596b_t *) malloc(sizeof(via_vt82c596b_t));
memset(dev, 0, sizeof(via_vt82c596b_t));
dev->slot = pci_add_card(PCI_ADD_SOUTHBRIDGE, via_vt82c596b_read, via_vt82c596b_write, dev);
dev->bm[0] = device_add_inst(&sff8038i_device, 1);
sff_set_slot(dev->bm[0], dev->slot);
sff_set_irq_mode(dev->bm[0], 0, 0);
sff_set_irq_mode(dev->bm[0], 1, 0);
sff_set_irq_pin(dev->bm[0], PCI_INTA);
dev->bm[1] = device_add_inst(&sff8038i_device, 2);
sff_set_slot(dev->bm[1], dev->slot);
sff_set_irq_mode(dev->bm[1], 0, 0);
sff_set_irq_mode(dev->bm[1], 1, 0);
sff_set_irq_pin(dev->bm[1], PCI_INTA);
dev->nvr = device_add(&via_nvr_device);
via_vt82c596b_reset(dev);
dev->smbus = device_add(&piix4_smbus_device);
device_add(&port_92_pci_device);
dma_alias_set();
pci_enable_mirq(0);
pci_enable_mirq(1);
pci_enable_mirq(2);
return dev;
}
static void
via_vt82c596b_close(void *p)
{
via_vt82c596b_t *via_vt82c596b = (via_vt82c596b_t *)p;
free(via_vt82c596b);
}
const device_t via_vt82c596b_device =
{
"VIA VT82C596B",
DEVICE_PCI,
0,
via_vt82c596b_init,
via_vt82c596b_close,
NULL,
NULL,
NULL,
NULL,
NULL
};
#endif